It is currently 26 Apr 2024, 08:51
   
Text Size

CardFactory Cleaning

Post MTG Forge Related Programming Questions Here

Moderators: timmermac, Blacksmith, KrazyTheFox, Agetian, friarsol, CCGHQ Admins

CardFactory Cleaning

Postby zerker2000 » 22 Jun 2009, 11:38

Ok, this shall be where I'll post card coding/cleanup suggestions. They will be in the format of "<cards.txt text> /n/n <suggested Factory code>", and I have three so far:
---1 Blight Sickle: At Rob's request, I shall paste a copy here:
Code: Select all
Blight Sickle
2
Artifact Equipment
Equipped creature gets +1/+0 and has Wither.
Code: Select all
  //*************** START *********** START **************************
    if (cardName.equals("Blight Sickle"))
    {
       final Ability equip = new Ability(card, "2")
       {
          public void resolve()
          {
             if (AllZone.GameAction.isCardInPlay(getTargetCard()) && CardFactoryUtil.canTarget(card, getTargetCard()) )
             {
                if (card.isEquipping())
                {
                   Card crd = card.getEquipping().get(0);
                   if (crd.equals(getTargetCard()) )
                      return;
                   
                   card.unEquipCard(crd);
                }   
                card.equipCard(getTargetCard());
             }
          }
          
          public boolean canPlay()
          {
             return AllZone.getZone(card).is(Constant.Zone.Play) &&           
                      AllZone.Phase.getActivePlayer().equals(card.getController()) &&
                      !AllZone.Phase.getPhase().equals("End of Turn") &&
                   !AllZone.Phase.getPhase().equals(Constant.Phase.Combat_Declare_Blockers_InstantAbility);
          }
          
          public boolean canPlayAI()
            {
              return getCreature().size() != 0 && !card.isEquipping();
            }
         
          
          public void chooseTargetAI()
            {
              Card target = CardFactoryUtil.AI_getBestCreature(getCreature());
              setTargetCard(target);
            }
            CardList getCreature()
            {
              CardList list = new CardList(AllZone.Computer_Play.getCards());
              list = list.filter(new CardListFilter()
              {
                public boolean addCard(Card c)
                {
                  return c.isCreature() && (!CardFactoryUtil.AI_doesCreatureAttack(c)) && CardFactoryUtil.canTarget(card, c) &&
                         (! c.getKeyword().contains("Defender"));
                }
              });
              // list.remove(card);      // if mana-only cost, allow self-target
              return list;
            }//getCreature()
          
       };//equip ability
       

       Command onEquip = new Command()
       {   

         private static final long serialVersionUID = 8130682765214560887L;

         public void execute()
           {
            if (card.isEquipping())
             {
                Card crd = card.getEquipping().get(0);
                crd.addExtrinsicKeyword("Wither");
                crd.addSemiPermanentAttackBoost(1);
             } 
           }//execute()
       };//Command
      

       Command onUnEquip = new Command()
       {   

         private static final long serialVersionUID = 5783423127748320501L;

         public void execute()
           {
            if (card.isEquipping())
             {
                Card crd = card.getEquipping().get(0);
                crd.removeExtrinsicKeyword("Wither");
                crd.addSemiPermanentAttackBoost(-1);
                   
             }
            
           }//execute()
       };//Command
      
       
       Input runtime = new Input()
       {
         private static final long serialVersionUID = -6785656229070523470L;

         public void showMessage()
             {
               //get all creatures you control
               CardList list = new CardList();
               list.addAll(AllZone.Human_Play.getCards());
               list = list.getType("Creature");
              
               stopSetNext(CardFactoryUtil.input_targetSpecific(equip, list, "Select target creature to equip", true));
             }
        };//Input
      
       equip.setBeforePayMana(runtime);
       
       equip.setDescription("Equip: 2");
       card.addSpellAbility(equip);
       
       card.setEquip(onEquip);
       card.setUnEquip(onUnEquip);

    } //*************** END ************ END **************************
---2 Akki Drillmaster: Now that we have TgtKPump, we don't need him to take up space in Card Factory
Code: Select all
Akki Drillmaster
2 R
Creature Goblin Shaman
no text
2/2
>TgtKPump T:Haste
Code: Select all
---3 Sensei's Divining Top: I though the response to "Cancel" while changing card order strange, and when I looked at the first ability's code, It looked like it could be compressed easily:
Code: Select all
//***Keep as is:
Sensei's Divining Top
1
Artifact
no text
Code: Select all
    else if(cardName.equals("Sensei's Divining Top"))
    {
      //ability2: Draw card, and put divining top on top of library
      final SpellAbility ability2 = new Ability_Tap(card, "0")
      {
      private static final long serialVersionUID = -2523015092351744208L;

      public void resolve()
        {
          String player = card.getController();
          String owner = card.getOwner();
         
          PlayerZone play =  AllZone.getZone(Constant.Zone.Play, player);
          PlayerZone lib =  AllZone.getZone(Constant.Zone.Library, owner);
             
          AllZone.GameAction.drawCard(player);
          play.remove(card);
          lib.add(card,0); //move divining top to top of library
          card.untap();
     
        }

        public boolean canPlayAI()
        {
          return false;
        }

        public boolean canPlay()
        {
           if (AllZone.getZone(card).is(Constant.Zone.Play))
               return true;
            else
               return false;
        }//canPlay()
      };//SpellAbility ability2

      ability2.setBeforePayMana(new Input()
      {
      private static final long serialVersionUID = -4773496833654414458L;
      @SuppressWarnings("unused") // check
      int check = -1;
         public void showMessage()
         {
             AllZone.Stack.push(ability2);
             stop();
         }//showMessage()
      });

     

      //ability (rearrange top 3 cards) :
      final SpellAbility ability1 = new Ability(card, "1")
      {
        public void resolve()
        {
           
           String player = card.getController();
           PlayerZone lib =  AllZone.getZone(Constant.Zone.Library, player);
           
           if (lib.size() < 3)
              return;
           
           CardList topThree = new CardList();
           
           //show top 3 cards:
           topThree.add(lib.get(0));
           topThree.add(lib.get(1));
           topThree.add(lib.get(2));
           
>           for (int i=1;i<=3;i++){
>           String Title = "Put on top: ";
>           if (i==2) Title = "Put second from top: ";
>            if (i==3) Title = "Put third from top: ";
>           Object o = AllZone.Display.getChoiceOptional(Title, topThree.toArray());
>           if(o == null) break;
>           Card c_1 = (Card)o;
>           topThree.remove(c_1);
>           lib.remove(c_1);
>           lib.add(c_1,i-1);
>           }
        }
        public boolean canPlayAI()
        {
           return false;
         
        }
        public boolean canPlay()
        {
          if (AllZone.getZone(card).is(Constant.Zone.Play))
             return true;
          else
             return false;
        }//canPlay()
      };//SpellAbility ability1


      ability1.setDescription("1: Look at the top three cards of your library, then put them back in any order.");
      ability1.setStackDescription("Sensei's Divining Top - rearrange top 3 cards");
      card.addSpellAbility(ability1);
      ability1.setBeforePayMana(new Input_PayManaCost(ability1));

      ability2.setDescription("tap: Draw a card, then put Sensei's Divining Top on top of its owner's library.");
      ability2.setStackDescription("Sensei's Divining Top - draw a card, then put back on owner's library");
      ability2.setBeforePayMana(new Input_NoCost_TapAbility((Ability_Tap) ability2));
      card.addSpellAbility(ability2);
     

    }
Ok, that's it for now, though I do remember seeing a couple other cards with redundant abilities at some point.
O forest, hold thy wand'ring son
Though fears assail the door.
O foliage, cloak thy ravaged one
In vestments cut for war.


--Eladamri, the Seed of Freyalise
zerker2000
Programmer
 
Posts: 569
Joined: 09 May 2009, 21:40
Location: South Pasadena, CA
Has thanked: 0 time
Been thanked: 0 time

Re: CardFactory Cleaning

Postby DennisBergkamp » 22 Jun 2009, 17:16

Cool, I'll make these changes! You seem to be picking up things pretty quickly, Zerker :)

Yes, I'm sure a lot of the cards' code can be compacted, or abilities turned into keywords (Rob's been doing a lot of those).
Anyway, if you're looking for redundant code, look at some of the Planeswalkers, I think there's a lot of bloated code there.
User avatar
DennisBergkamp
AI Programmer
 
Posts: 2602
Joined: 09 Sep 2008, 15:46
Has thanked: 0 time
Been thanked: 0 time

Bombs Away!

Postby zerker2000 » 23 Jun 2009, 09:42

Ok, part two.
Make sure to paste the following into somewhere you can see what I'm actually doing(and hopefully catch any mistakes I made, though the basic stuff was caught by eclipse):
--Cards.txt--
Code: Select all
AEther Spellbomb
1
Artifact
no text
1, Sacrifice AEther Spellbomb: Draw a card.

Lifespark Spellbomb
1
Artifact
no text
1, Sacrifice Lifespark Spellbomb: Draw a card.

Pyrite Spellbomb
1
Artifact
no text
1, Sacrifice Pyrite Spellbomb: Draw a card.

Necrogen Spellbomb
1
Artifact
no text
1, Sacrifice Necrogen Spellbomb: Draw a card.

Sunbeam Spellbomb
1
Artifact
no text
1, Sacrifice Sunbeam Spellbomb: Draw a card.
--Card FactoryUtil--
Code: Select all
public static SpellAbility ability_Spellbomb(final Card sourceCard){
  final SpellAbility ability = new Ability(sourceCard, "1")
  {
    public boolean canPlay()
    {
            return AllZone.GameAction.isCardInPlay(sourceCard)&&!AllZone.Stack.getSourceCards().contains(sourceCard);//in play and not already activated(Sac cost problems)
    }
    public boolean canPlayAI() {return (AllZone.Computer_Hand.size() < 4)&&(AllZone.Computer_Library.size()>0)&&MyRandom.random.nextBoolean();}

    public void resolve()
    {
      AllZone.GameAction.drawCard(sourceCard.getController());
      AllZone.GameAction.sacrifice(getSourceCard());
    }
  };
  ability.setDescription("1, Sacrifice "+sourceCard.getName()+": Draw a card.");
  ability.setStackDescription(sourceCard.getName() +" - Draw a card.");
  return ability;
--Card Factory--
Code: Select all
   if(cardName.equals("AEther Spellbomb"))
    {
       
      final Ability ability = new Ability(card, "U")
      {
       public boolean canPlay()
         {
            return AllZone.GameAction.isCardInPlay(card)&&!AllZone.Stack.getSourceCards().contains(card);
       }
       public boolean canPlayAI()
        {
          setTargetCard(CardFactoryUtil.AI_getBestCreature(new CardList(AllZone.Human_Play.getCards())));
         return ((AllZone.Computer_Hand.size() > 2)&&(getTargetCard() != null)) ;
        }
       public void resolve()
       {
          final Card[] target = new Card[1];
          target[0] = getTargetCard();
          PlayerZone hand = AllZone.getZone(Constant.Zone.Hand, target[0].getController());

          if(AllZone.GameAction.isCardInPlay(target[0])  && CardFactoryUtil.canTarget(card, target[0]) )
          {
             AllZone.GameAction.moveTo(hand ,target[0]);
          }
          AllZone.GameAction.sacrifice(getSourceCard());
       }//resolve()
      };//SpellAbility
      ability.setDescription("U, Sacrifice AEther Spellbomb: Return target creature to its owner's hand.");
      card.addSpellAbility(ability);
      ability.setBeforePayMana(CardFactoryUtil.input_targetCreature(ability));
    }
   
    if(cardName.equals("Lifespark Spellbomb"))
    {
    final SpellAbility ability = new Ability_Activated(card, "G")
    {
      private static final long serialVersionUID = -5744842090293912606L;
      public boolean canPlay()
       {
          return AllZone.GameAction.isCardInPlay(card)&&!AllZone.Stack.getSourceCards().contains(card);
         }
      public boolean canPlayAI()
        {
         CardList land = new CardList(AllZone.Computer_Play.getCards());
         land = land.getType("Land");
         CardList basic = land.getType("Basic");
         if (basic.size() < 3) return false;
         Card[] basic_1 = basic.toArray();
         for(Card var : basic_1)
            if (var.isTapped()) basic.remove(var);
          basic.shuffle();
          setTargetCard(basic.get(1));
          return false;
        }//canPlayAI() 
     public void resolve()
        {
          //in case ability is played twice
          final int[] oldAttack = new int[1];
          final int[] oldDefense = new int[1];

          final Card card[] = new Card[1];
          card[0] = getTargetCard();

          oldAttack[0]  = card[0].getBaseAttack();
          oldDefense[0] = card[0].getBaseDefense();

          card[0].setBaseAttack(3);
          card[0].setBaseDefense(3);
          card[0].addType("Creature");

          //EOT
          final Command untilEOT = new Command()
          {
            private static final long serialVersionUID = 7236360479349324099L;

         public void execute()
            {
              card[0].setBaseAttack(oldAttack[0]);
              card[0].setBaseDefense(oldDefense[0]);

              card[0].removeType("Creature");
            }
          };

          AllZone.EndOfTurn.addUntil(untilEOT);
          AllZone.GameAction.sacrifice(getSourceCard());
        }//resolve()
      };//SpellAbility
      card.addSpellAbility(ability);
      ability.setDescription("G, Sacrifice Lifespark Spellbomb: Target land becomes a 3/3 Creature until end of turn. It is still a land.");
      ability.setBeforePayMana(CardFactoryUtil.input_targetType(ability, "Land"));
    }
   
   
    if(cardName.equals("Pyrite Spellbomb"))
    {

    final SpellAbility ability = new Ability_Activated(card, "R")
    {   
      /**
       *
       */
      private static final long serialVersionUID = 1L;

     public boolean canPlay()
      {
          return AllZone.GameAction.isCardInPlay(card)&&!AllZone.Stack.getSourceCards().contains(card);
      }
      public boolean canPlayAI()
      {
         Random r = new Random();
         if (r.nextFloat() <= Math.pow(.6667, card.getAbilityUsed()))
            return true;
         else
            return false;
      }
     
      public void chooseTargetAI()
      {
        CardList list = CardFactoryUtil.AI_getHumanCreature(2, card, true);
        list.shuffle();

        if(list.isEmpty() || AllZone.Human_Life.getLife() < 5 + 2)
          setTargetPlayer(Constant.Player.Human);
        else
          setTargetCard(list.get(0));
      }//chooseTargetAI
      public void resolve()
      {
        if(getTargetCard() != null)
        {
          if(AllZone.GameAction.isCardInPlay(getTargetCard())  && CardFactoryUtil.canTarget(card, getTargetCard()) )
            getTargetCard().addDamage(2);
        }
        else
          AllZone.GameAction.getPlayerLife(getTargetPlayer()).subtractLife(2);
        AllZone.GameAction.sacrifice(getSourceCard());
      }//resolve()
     };//Ability_Activated
     
      ability.setBeforePayMana(CardFactoryUtil.input_targetCreaturePlayer(ability, true));
      ability.setDescription("R, Sacrifice Pyrite Spellbomb: Pyrite Spellbomb deals 2 damage to target creature or player.");
      card.addSpellAbility(ability);
    }
   
    if(cardName.equals("Sunbeam Spellbomb"))
    {
      final Ability ability = new Ability(card, "W")
      {
       public boolean canPlay()
         {
            return AllZone.GameAction.isCardInPlay(card)&&!AllZone.Stack.getSourceCards().contains(card);
       }
       public boolean canPlayAI()
        {
          return (AllZone.GameAction.getPlayerLife(Constant.Player.Computer).getLife() < 7);
        }
        public void resolve()
        {
          AllZone.GameAction.getPlayerLife(card.getController()).addLife(5);
          AllZone.GameAction.sacrifice(getSourceCard());
        }//resolve()
      };//SpellAbility
      ability.setStackDescription("You gain 5 life");
      ability.setDescription("W, Sacrifice Sunbeam Spellbomb: You gain 5 life.");
      card.addSpellAbility(ability);
    }
   
    if(cardName.equals("Necrogen Spellbomb"))
    {
      final Ability ability = new Ability(card, "B")
      {
       public boolean canPlay()
         {
            return AllZone.GameAction.isCardInPlay(card)&&!AllZone.Stack.getSourceCards().contains(card);
       }
       public boolean canPlayAI()
        {
          setTargetPlayer(Constant.Player.Human);
          return (MyRandom.random.nextBoolean()&&AllZone.Human_Hand.size()>0);
        }
        public void resolve()
        {
          String s = getTargetPlayer();
          setStackDescription("Necrogen Spellbomb - " +s +" discards a card");
          if(Constant.Player.Computer.equals(getTargetPlayer()))
            AllZone.GameAction.discardRandom(getTargetPlayer());
          else
            AllZone.InputControl.setInput(CardFactoryUtil.input_discard());
          AllZone.GameAction.sacrifice(getSourceCard());
        }//resolve()
      };//SpellAbility
      ability.setDescription("B, Sacrifice Necrogen Spellbomb: Target player discards a card");
      ability.setBeforePayMana(CardFactoryUtil.input_targetPlayer(ability));
      card.addSpellAbility(ability);
    }
... pretty basic, i.e. still 90% copy-pasted, but the result is fun to use (and completely within MTG rules). These are definently the kind of thing you'd combo with e.g. Summoning Station.
O forest, hold thy wand'ring son
Though fears assail the door.
O foliage, cloak thy ravaged one
In vestments cut for war.


--Eladamri, the Seed of Freyalise
zerker2000
Programmer
 
Posts: 569
Joined: 09 May 2009, 21:40
Location: South Pasadena, CA
Has thanked: 0 time
Been thanked: 0 time

Re: CardFactory Cleaning

Postby Rob Cashwalker » 23 Jun 2009, 11:58

Two things -

One, this should be in its own post since it's new cards, not cleanup of CardFactory.

Second, I don't see any way for the Card Draw ability to be added to each of the cards. Typically when keywording an ability, you need to have a handler. One part of the handler returns the index in the keyword list, (should____) and the other (if (should_____ != -1)) usually deletes the keyword text and builds an ActivatedAbility object to do the action.
The way you've got it, you don't even need the keyword handler, but you do need to add both abilities to the card.
The Force will be with you, Always.
User avatar
Rob Cashwalker
Programmer
 
Posts: 2167
Joined: 09 Sep 2008, 15:09
Location: New York
Has thanked: 5 times
Been thanked: 40 times

Re: CardFactory Cleaning

Postby zerker2000 » 23 Jun 2009, 12:13

oh oops sorry missed a piece :oops: (I copied the saproling one):
Code: Select all
private final int shouldSpellbomb(Card c) {
      ArrayList<String> a = c.getKeyword();
      for (int i = 0; i < a.size(); i++)
         if (a.get(i).toString().startsWith("1, Sacrifice"))//
            if(a.get(i).equals("1, Sacrifice "+c.getName()+": Draw a card.")) return i;
      return -1;
   }
Code: Select all
if (shouldSpellbomb(card) != -1)
    {
      int n = shouldSpellbomb(card);
      if (n != -1)
      {
       String parse = card.getKeyword().get(n).toString();
        card.removeIntrinsicKeyword(parse);
       
        card.addSpellAbility(CardFactoryUtil.ability_Spellbomb(card));
      }
    }//Spore Saproling
... And no I did not just put this in, I actually tested all of them :P
O forest, hold thy wand'ring son
Though fears assail the door.
O foliage, cloak thy ravaged one
In vestments cut for war.


--Eladamri, the Seed of Freyalise
zerker2000
Programmer
 
Posts: 569
Joined: 09 May 2009, 21:40
Location: South Pasadena, CA
Has thanked: 0 time
Been thanked: 0 time

Re: CardFactory Cleaning

Postby zerker2000 » 25 Jun 2009, 07:26

I found a bunch of cards that had keyword pumping abilities that were implemented though "if(carrdName==<name>)":
Greater Forgeling should have PTPump 1 R:+3/-3 in cards.txt instead of a Cfactory name=.
Manta Riders and Killer Whale need KPump U: Flying put into cards.txt, and their ifcardname='es taken out
Goblin Ballon Brigade has the same problem, except with cost R
Sacromite myr needs to get KPump 2:Flying, and get the first ability and stuff affiliated with it taken out

Also, while talking of pumps, would it be possible to have a spKpump keyword similar to tgtKpump?
O forest, hold thy wand'ring son
Though fears assail the door.
O foliage, cloak thy ravaged one
In vestments cut for war.


--Eladamri, the Seed of Freyalise
zerker2000
Programmer
 
Posts: 569
Joined: 09 May 2009, 21:40
Location: South Pasadena, CA
Has thanked: 0 time
Been thanked: 0 time

Re: CardFactory Cleaning

Postby Rob Cashwalker » 25 Jun 2009, 11:57

I've pointed those out before....

Yeah, spPTPump, spKPump and spPTKPump are on my list.... I'm just about finished doing TgtPTPump.

On the topic of cleaning CardFactory, you should take a look at possible ways to split CardFactory into smaller, easier to maintain mini-factories. We have a thread here that shows what we've tried so far.
The Force will be with you, Always.
User avatar
Rob Cashwalker
Programmer
 
Posts: 2167
Joined: 09 Sep 2008, 15:09
Location: New York
Has thanked: 5 times
Been thanked: 40 times

Re: CardFactory Cleaning

Postby zerker2000 » 26 Jun 2009, 06:08

Found the following in gilder bairn:
Code: Select all
//badly written, hacky... TODO: should improve this
looked at the code that followed ... agreed completely :P. My suggested a1.resolve for Gilder Bairn:
Code: Select all
public void resolve()
          {
           Card c = getTargetCard();
           card.untap();
           
           if (c.sumAllCounters() == 0)
              return;
           else if (AllZone.GameAction.isCardInPlay(c) && CardFactoryUtil.canTarget(card, c))
              for(Counters c_1 : Counters.values())
                 if (c.getCounters(c_1) != 0)
                     c.addCounter(c_1, c.getCounters(c_1));
          }
Any mistakes?
O forest, hold thy wand'ring son
Though fears assail the door.
O foliage, cloak thy ravaged one
In vestments cut for war.


--Eladamri, the Seed of Freyalise
zerker2000
Programmer
 
Posts: 569
Joined: 09 May 2009, 21:40
Location: South Pasadena, CA
Has thanked: 0 time
Been thanked: 0 time

Re: CardFactory Cleaning

Postby Hellfish » 26 Jun 2009, 10:19

Admittedly, my Java is rusty, and I can't seem to find the Counters class in the source but won't that result in an infinite loop?

Say
  • there is one counter on the target
  • the loop goes through once
  • there are now two counters
  • the loop condition is checked, getting the newly placed token returned
  • the loop goes through again and again, going through the new tokens as well
So now you're
Screaming for the blood of the cookie monster
Evil puppet demon of obesity
Time to change the tune of his fearful ballad
C is for "Lettuce," that's good enough for me
User avatar
Hellfish
Programmer
 
Posts: 1297
Joined: 07 Jun 2009, 10:41
Location: South of the Pumphouse
Has thanked: 110 times
Been thanked: 169 times

Re: CardFactory Cleaning

Postby zerker2000 » 26 Jun 2009, 11:59

Erm no, Counters is an enum in Card.java, and c.addCounters and c.getCounters refer to an int<->Counters hashTable.
O forest, hold thy wand'ring son
Though fears assail the door.
O foliage, cloak thy ravaged one
In vestments cut for war.


--Eladamri, the Seed of Freyalise
zerker2000
Programmer
 
Posts: 569
Joined: 09 May 2009, 21:40
Location: South Pasadena, CA
Has thanked: 0 time
Been thanked: 0 time

Re: CardFactory Cleaning

Postby Hellfish » 26 Jun 2009, 12:21

Ah... Heh.. Carry on. :oops:
So now you're
Screaming for the blood of the cookie monster
Evil puppet demon of obesity
Time to change the tune of his fearful ballad
C is for "Lettuce," that's good enough for me
User avatar
Hellfish
Programmer
 
Posts: 1297
Joined: 07 Jun 2009, 10:41
Location: South of the Pumphouse
Has thanked: 110 times
Been thanked: 169 times


Return to Developer's Corner

Who is online

Users browsing this forum: No registered users and 107 guests


Who is online

In total there are 107 users online :: 0 registered, 0 hidden and 107 guests (based on users active over the past 10 minutes)
Most users ever online was 4143 on 23 Jan 2024, 08:21

Users browsing this forum: No registered users and 107 guests

Login Form