Page 1 of 1

CardFactory Cleaning

PostPosted: 22 Jun 2009, 11:38
by zerker2000
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.

Re: CardFactory Cleaning

PostPosted: 22 Jun 2009, 17:16
by DennisBergkamp
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.

Bombs Away!

PostPosted: 23 Jun 2009, 09:42
by zerker2000
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.

Re: CardFactory Cleaning

PostPosted: 23 Jun 2009, 11:58
by Rob Cashwalker
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.

Re: CardFactory Cleaning

PostPosted: 23 Jun 2009, 12:13
by zerker2000
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

Re: CardFactory Cleaning

PostPosted: 25 Jun 2009, 07:26
by zerker2000
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?

Re: CardFactory Cleaning

PostPosted: 25 Jun 2009, 11:57
by Rob Cashwalker
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.

Re: CardFactory Cleaning

PostPosted: 26 Jun 2009, 06:08
by zerker2000
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?

Re: CardFactory Cleaning

PostPosted: 26 Jun 2009, 10:19
by Hellfish
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

Re: CardFactory Cleaning

PostPosted: 26 Jun 2009, 11:59
by zerker2000
Erm no, Counters is an enum in Card.java, and c.addCounters and c.getCounters refer to an int<->Counters hashTable.

Re: CardFactory Cleaning

PostPosted: 26 Jun 2009, 12:21
by Hellfish
Ah... Heh.. Carry on. :oops: