It is currently 12 Sep 2025, 12:20
   
Text Size

Programming a card

Post MTG Forge Related Programming Questions Here

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

Re: Programming a card

Postby silly freak » 18 Oct 2009, 21:12

actually, the point of the forge.properties file is that you don't have to copy your res folder. you can change the forge.properties file to point wherever you want. my folder structure looks like
Code: Select all
forge
forge/versions
forge/versions/20091007
forge/versions/20091015
forge/versions/20091017
forge/res
the forge.properties files inside forge/versions/yyyymmdd, i change to
Code: Select all
main--transparent-properties=../../res/main.properties
this way, I can keep my quest progress with new versions and don't have to move/copy/redownload the card pictures

(okay, that's not exactly what it looks for me; i just share the pics folder, just to be save from incompatible res files. but that's only my paranoia...)
___

where's the "trust me, that will work!" switch for the compiler?
Laterna Magica - blog, forum, project, 2010/09/06 release!
silly freak
DEVELOPER
 
Posts: 598
Joined: 26 Mar 2009, 07:18
Location: Vienna, Austria
Has thanked: 93 times
Been thanked: 25 times

Re: Programming a card

Postby cyclope » 19 Oct 2009, 19:08

Thanks for your help with using Eclipse...

But as Zerker said , can i use the abdamage keyword or no for Nightscape Master ?
cyclope
 
Posts: 69
Joined: 28 Sep 2009, 18:08
Has thanked: 0 time
Been thanked: 0 time

Re: Programming a card

Postby DennisBergkamp » 19 Oct 2009, 20:12

There's currently an abDamageCP keyword, but it also targets players, so I'm not sure if that would work for Nightscape Master.
A possibility would be to create an abDamageP keyword :)
User avatar
DennisBergkamp
AI Programmer
 
Posts: 2602
Joined: 09 Sep 2008, 15:46
Has thanked: 0 time
Been thanked: 0 time

Re: Programming a card

Postby Chris H. » 19 Oct 2009, 20:26

cyclope wrote:can i use the abdamage keyword or no for Nightscape Master ?
`
Permanents which use the abDamageCP keyword will ask you to "Select target Creature, Player, or Planeswalker" as part of it's activation and targeting system. You can look at the abDamageCP keyword code. You may be able to copy and modify a portion of the code to use with the Nightscape Master code.

Unfortunately, the keyword by itself will not give you quite the ability that you are looking for ... it is close.
User avatar
Chris H.
Forge Moderator
 
Posts: 6320
Joined: 04 Nov 2008, 12:11
Location: Mac OS X Yosemite
Has thanked: 644 times
Been thanked: 643 times

Re: Programming a card

Postby Rob Cashwalker » 20 Oct 2009, 18:49

abDamageC, abDamageP and spDamageP represented too few cards that I knew how to do back then, many of them rely on variable information, which I didn't figure out how to program until spDraw and spDamageTgt.

spDamageTgt represents a way to code a single effect that may target either Creatures only, Players only or Creatures or Players (I know it asks the human for planeswalker targets, but I forgot about the AI). Previous keywords were separate because they had different AI due to the difference in targets or other details.

Basically, look for the sequence "CP" in the keyword string (k[0]) ELSE (important) look for "C" ELSE look for "P".
Set a final boolean to represent each specific case, and set up the AI to choose accordingly.
Players should be higher priority than creatures if their life would drop below a threshold of like 10 or so after the damage.
Walkers probably need to drop below 2 or 3, or half their printed loyalty, or (something else?).
Notice that the AI is broken up into smaller decision functions, each of which is used depending on the variant of damage spell.

Having overcome the slight AI differences, I'd like to go back over the keywords to make them work in a uniform fashion, which could eliminate a few thousand lines of code in just the keyword handling section, not to mention any explicit code for similar cards that needed it. Adding support for the variables and drawbacks also opens up all the cards that were ignored previously because of their difficulty in implementation.
I don't think we should go as far as unifying abilities and spells, because spells have their SpellAbilities cleared and permanents don't, but that might not be difficult to revise later as well.
Just consolidating the various forms of Pump into "abPump[Tgt] {cost}:{Boosts}" would be huge. ([Tgt] is optional if present, it's targeted, if not, it's a self-pump. If there is only one element in the Boosts parameter, then it's a keyword-only pump, if there are two elements, it's a PT-only pump, if there are three elements it's a PTK pump. Simple enough? This is how the spPumpTgt works, but without the support for variables)
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: Programming a card

Postby silly freak » 20 Oct 2009, 18:59

I like eliminating redundant code - no, slightly wrong - I like redundant code getting deleted ^^
no, really, this is a good Idea. having a more uniform syntax for the different keywords makes it easier to add cards. currently, you can compare it with if every class was written in a different language. I hope you succeed

as for the planeswalkers, I'd say that a major targeting criterion is to drop the walker below his ultimate's cost
___

where's the "trust me, that will work!" switch for the compiler?
Laterna Magica - blog, forum, project, 2010/09/06 release!
silly freak
DEVELOPER
 
Posts: 598
Joined: 26 Mar 2009, 07:18
Location: Vienna, Austria
Has thanked: 93 times
Been thanked: 25 times

Re: Programming a card

Postby cyclope » 22 Oct 2009, 18:26

OK... if i really understand, i keep the code for the second ability of Nightscape Master...
Could anybody tell me if the code for this second ability is correct...
cyclope
 
Posts: 69
Joined: 28 Sep 2009, 18:08
Has thanked: 0 time
Been thanked: 0 time

Re: Programming a card

Postby cyclope » 23 Oct 2009, 19:16

I 've coded some more enchantments:
Reflexes
Code: Select all
//*************** START *********** START **************************
if(cardName.equals("Reflexes"))
{
  final SpellAbility spell = new Spell(card)
  {

   public boolean canPlayAI()
    {
      CardList list = new CardList(AllZone.Computer_Play.getCards());
      list = list.getType("Creature");

      if(list.isEmpty())
       return false;

      //else
      CardListUtil.sortAttack(list);
      CardListUtil.sortFlying(list);

      for (int i=0;i<list.size();i++) {
         if (CardFactoryUtil.canTarget(card, list.get(i)))
         {
            setTargetCard(list.get(i));
            return true;
         }
      }
      return false;
    }//canPlayAI()
    public void resolve()
    {
      PlayerZone play = AllZone.getZone(Constant.Zone.Play, card.getController());
      play.add(card);
     
      Card c = getTargetCard();
     
      if(AllZone.GameAction.isCardInPlay(c)  && CardFactoryUtil.canTarget(card, c) )
      {
         card.enchantCard(c);
         System.out.println("Enchanted: " +getTargetCard());
      }
    }//resolve()
  };//SpellAbility
  card.clearSpellAbility();
  card.addSpellAbility(spell);

  Command onEnchant = new Command()
  {   

   public void execute()
      {
         if (card.isEnchanting())
         {
            Card crd = card.getEnchanting().get(0);
            crd.addExtrinsicKeyword("First Strike");
         }
      }//execute()
  };//Command


  Command onUnEnchant = new Command()
  {   

   public void execute()
      {
         if (card.isEnchanting())
         {
            Card crd = card.getEnchanting().get(0);
            crd.removeExtrinsicKeyword("First Strike");
         }
     
      }//execute()
   };//Command
   
   Command onLeavesPlay = new Command()
   {


   public void execute()
      {
         if (card.isEnchanting())
         {
            Card crd = card.getEnchanting().get(0);
            card.unEnchantCard(crd);
         }
      }
   };

  card.setEnchant(onEnchant);
  card.setUnEnchant(onUnEnchant);
  card.setLeavesPlay(onLeavesPlay);

  spell.setBeforePayMana(CardFactoryUtil.input_targetCreature(spell));
}//*************** END ************ END **************************

in card.txt:
Reflexes
R
Enchantment Aura
Enchanted creature has first strike.
Enchant creature
Shield of Duty and Reason
Code: Select all
//*************** START *********** START **************************
if(cardName.equals("Shield of Duty and Reason"))
{
  final SpellAbility spell = new Spell(card)
  {

   public boolean canPlayAI()
    {
      CardList list = new CardList(AllZone.Computer_Play.getCards());
      list = list.getType("Creature");

      if(list.isEmpty())
       return false;

      //else
      CardListUtil.sortAttack(list);
      CardListUtil.sortFlying(list);

      for (int i=0;i<list.size();i++) {
         if (CardFactoryUtil.canTarget(card, list.get(i)))
         {
            setTargetCard(list.get(i));
            return true;
         }
      }
      return false;
    }//canPlayAI()
    public void resolve()
    {
      PlayerZone play = AllZone.getZone(Constant.Zone.Play, card.getController());
      play.add(card);
     
      Card c = getTargetCard();
     
      if(AllZone.GameAction.isCardInPlay(c)  && CardFactoryUtil.canTarget(card, c) )
      {
         card.enchantCard(c);
         System.out.println("Enchanted: " +getTargetCard());
      }
    }//resolve()
  };//SpellAbility
  card.clearSpellAbility();
  card.addSpellAbility(spell);

  Command onEnchant = new Command()
  {   

   public void execute()
      {
         if (card.isEnchanting())
         {
            Card crd = card.getEnchanting().get(0);
            crd.addExtrinsicKeyword("Protection from green");
      crd.addExtrinsicKeyword("Protection from blue");
         }
      }//execute()
  };//Command


  Command onUnEnchant = new Command()
  {   

   public void execute()
      {
         if (card.isEnchanting())
         {
            Card crd = card.getEnchanting().get(0);
            crd.removeExtrinsicKeyword("Protection from green");
      crd.removeExtrinsicKeyword("Protection from blue");
         }
     
      }//execute()
   };//Command
   
   Command onLeavesPlay = new Command()
   {


   public void execute()
      {
         if (card.isEnchanting())
         {
            Card crd = card.getEnchanting().get(0);
            card.unEnchantCard(crd);
         }
      }
   };

  card.setEnchant(onEnchant);
  card.setUnEnchant(onUnEnchant);
  card.setLeavesPlay(onLeavesPlay);

  spell.setBeforePayMana(CardFactoryUtil.input_targetCreature(spell));
}//*************** END ************ END **************************

in card.txt:
Shield of Duty and Reason
W
Enchantment Aura
Enchanted creature has first protection from green and from blue.
Enchant creature
Scavenged Weaponry
Code: Select all
Scavenged Weaponry

//*************** START *********** START **************************
if(cardName.equals("Scavenged Weaponry"))
{
  final SpellAbility spell = new Spell(card)
  {

   public boolean canPlayAI()
    {
      CardList list = new CardList(AllZone.Computer_Play.getCards());
      list = list.getType("Creature");

      if(list.isEmpty())
       return false;

      //else
      CardListUtil.sortAttack(list);
      CardListUtil.sortFlying(list);

      for (int i=0;i<list.size();i++) {
         if (CardFactoryUtil.canTarget(card, list.get(i)))
         {
            setTargetCard(list.get(i));
            return true;
         }
      }
      return false;
    }//canPlayAI()
    public void resolve()
    {
      PlayerZone play = AllZone.getZone(Constant.Zone.Play, card.getController());
      play.add(card);
     
      Card c = getTargetCard();
     
      if(AllZone.GameAction.isCardInPlay(c)  && CardFactoryUtil.canTarget(card, c) )
      {
         card.enchantCard(c);
         System.out.println("Enchanted: " +getTargetCard());
      }
    }//resolve()
  };//SpellAbility
  card.clearSpellAbility();
  card.addSpellAbility(spell);

  Command onEnchant = new Command()
  {   

   public void execute()
      {
         if (card.isEnchanting())
         {
            Card crd = card.getEnchanting().get(0);
      crd.addSemiPermanentAttackBoost(1);
           crd.addSemiPermanentDefenseBoost(1);
           
         }
      }//execute()
  };//Command


  Command onUnEnchant = new Command()
  {   

   public void execute()
      {
         if (card.isEnchanting())
         {
            Card crd = card.getEnchanting().get(0);
        crd.addSemiPermanentAttackBoost(-1);
           crd.addSemiPermanentDefenseBoost(-1);
           
         }
     
      }//execute()
   };//Command
   
   Command onLeavesPlay = new Command()
   {


   public void execute()
      {
         if (card.isEnchanting())
         {
            Card crd = card.getEnchanting().get(0);
            card.unEnchantCard(crd);
         }
      }
   };

  card.setEnchant(onEnchant);
  card.setUnEnchant(onUnEnchant);
  card.setLeavesPlay(onLeavesPlay);

  spell.setBeforePayMana(CardFactoryUtil.input_targetCreature(spell));
}//*************** END ************ END **************************

in card.txt:
Scavenged Weaponry
2 B
Enchantment Aura
Enchanted creature gets +1/+1.
Enchant creature
Cantrip
Maniacal Rage
Code: Select all
//*************** START *********** START **************************
if(cardName.equals("Maniacal Rage"))
{
  final SpellAbility spell = new Spell(card)
  {

   public boolean canPlayAI()
    {
      CardList list = new CardList(AllZone.Computer_Play.getCards());
      list = list.getType("Creature");

      if(list.isEmpty())
       return false;

      //else
      CardListUtil.sortAttack(list);
      CardListUtil.sortFlying(list);

      for (int i=0;i<list.size();i++) {
         if (CardFactoryUtil.canTarget(card, list.get(i)))
         {
            setTargetCard(list.get(i));
            return true;
         }
      }
      return false;
    }//canPlayAI()
    public void resolve()
    {
      PlayerZone play = AllZone.getZone(Constant.Zone.Play, card.getController());
      play.add(card);
     
      Card c = getTargetCard();
     
      if(AllZone.GameAction.isCardInPlay(c)  && CardFactoryUtil.canTarget(card, c) )
      {
         card.enchantCard(c);
         System.out.println("Enchanted: " +getTargetCard());
      }
    }//resolve()
  };//SpellAbility
  card.clearSpellAbility();
  card.addSpellAbility(spell);

  Command onEnchant = new Command()
  {   

   public void execute()
      {
         if (card.isEnchanting())
         {
            Card crd = card.getEnchanting().get(0);
      crd.addSemiPermanentAttackBoost(2);
           crd.addSemiPermanentDefenseBoost(2);
      crd.addExtrinsicKeyword("This creature cannot block");
           
         }
      }//execute()
  };//Command


  Command onUnEnchant = new Command()
  {   

   public void execute()
      {
         if (card.isEnchanting())
         {
            Card crd = card.getEnchanting().get(0);
        crd.addSemiPermanentAttackBoost(-2);
           crd.addSemiPermanentDefenseBoost(-2);
      crd.removeExtrinsicKeyword("This creature cannot block");          

         }
     
      }//execute()
   };//Command
   
   Command onLeavesPlay = new Command()
   {


   public void execute()
      {
         if (card.isEnchanting())
         {
            Card crd = card.getEnchanting().get(0);
            card.unEnchantCard(crd);
         }
      }
   };

  card.setEnchant(onEnchant);
  card.setUnEnchant(onUnEnchant);
  card.setLeavesPlay(onLeavesPlay);

  spell.setBeforePayMana(CardFactoryUtil.input_targetCreature(spell));
}//*************** END ************ END **************************

in card.txt:
Maniacal Rage
1 R
Enchantment Aura
Enchanted creature gets +2/+2 and this creature cannot block.
Enchant creature
Magefire Wings
Code: Select all
//*************** START *********** START **************************
if(cardName.equals("Magefire Wings"))
{
  final SpellAbility spell = new Spell(card)
  {

   public boolean canPlayAI()
    {
      CardList list = new CardList(AllZone.Computer_Play.getCards());
      list = list.getType("Creature");

      if(list.isEmpty())
       return false;

      //else
      CardListUtil.sortAttack(list);
      CardListUtil.sortFlying(list);

      for (int i=0;i<list.size();i++) {
         if (CardFactoryUtil.canTarget(card, list.get(i)))
         {
            setTargetCard(list.get(i));
            return true;
         }
      }
      return false;
    }//canPlayAI()
    public void resolve()
    {
      PlayerZone play = AllZone.getZone(Constant.Zone.Play, card.getController());
      play.add(card);
     
      Card c = getTargetCard();
     
      if(AllZone.GameAction.isCardInPlay(c)  && CardFactoryUtil.canTarget(card, c) )
      {
         card.enchantCard(c);
         System.out.println("Enchanted: " +getTargetCard());
      }
    }//resolve()
  };//SpellAbility
  card.clearSpellAbility();
  card.addSpellAbility(spell);

  Command onEnchant = new Command()
  {   

   public void execute()
      {
         if (card.isEnchanting())
         {
            Card crd = card.getEnchanting().get(0);
      crd.addSemiPermanentAttackBoost(2);
      crd.addExtrinsicKeyword("Flying");
           
         }
      }//execute()
  };//Command


  Command onUnEnchant = new Command()
  {   

   public void execute()
      {
         if (card.isEnchanting())
         {
            Card crd = card.getEnchanting().get(0);
        crd.addSemiPermanentAttackBoost(-2);
      crd.removeExtrinsicKeyword("Flying");          

         }
     
      }//execute()
   };//Command
   
   Command onLeavesPlay = new Command()
   {


   public void execute()
      {
         if (card.isEnchanting())
         {
            Card crd = card.getEnchanting().get(0);
            card.unEnchantCard(crd);
         }
      }
   };

  card.setEnchant(onEnchant);
  card.setUnEnchant(onUnEnchant);
  card.setLeavesPlay(onLeavesPlay);

  spell.setBeforePayMana(CardFactoryUtil.input_targetCreature(spell));
}//*************** END ************ END **************************

in card.txt:
Magefire Wings
U R
Enchantment Aura
Enchanted creature gets +2/+0 and has flying.
Enchant creature
and I've coded the sorcery Sacred Nectar:
Code: Select all
//*************** START *********** START **************************
    if(cardName.equals("Sacred Nectar")
    {
      SpellAbility spell = new Spell(card)
      {
      

      public boolean canPlay()
        {
          setStackDescription(card.getName() +" - " +card.getController() +" gains 4 life.");
          return super.canPlay();
        }

        public void resolve()
        {
          PlayerLife life = AllZone.GameAction.getPlayerLife(card.getController());
          life.addLife(4);
        }
      };
      spell.setDescription("You gain 4 life.");

      card.clearSpellAbility();
      card.addSpellAbility(spell);
     
    }//*************** END ************ END **************************

in cards.txt:
Sacred Nectar
1 W
Sorcery
You gain 4 life.
I hope i haven't made too much errors and i hope you'll enjoy these...
cyclope
 
Posts: 69
Joined: 28 Sep 2009, 18:08
Has thanked: 0 time
Been thanked: 0 time

Re: Programming a card

Postby Chris H. » 23 Oct 2009, 19:48

Thank you, Cyclope. It is nice to have some more spells added to Forge.
User avatar
Chris H.
Forge Moderator
 
Posts: 6320
Joined: 04 Nov 2008, 12:11
Location: Mac OS X Yosemite
Has thanked: 644 times
Been thanked: 643 times

Re: Programming a card

Postby cyclope » 25 Oct 2009, 13:49

I read the file GameActionUtil.java and i think we could add these cards:

Levitation:
Code: Select all
In GameActionUtil.java:

- in public static void executeCardStateEffects() add : Levitation.execute();

- after }// executeCardStateEffects() add:

public static Command Levitation = new Command()
   {
      private static final long serialVersionUID = ;
      
      CardList gloriousAnthemList = new CardList();

      public void execute()
      {
         String keyword = "Flying";

         CardList list = gloriousAnthemList;
         Card c;
         // reset all cards in list - aka "old" cards
         for (int i = 0; i < list.size(); i++)
         {
            c = list.get(i);
            c.removeExtrinsicKeyword(keyword);
         }

         list.clear();
         PlayerZone[] zone = getZone("Levitation");

         for (int outer = 0; outer < zone.length; outer++)
         {
            CardList creature = new CardList(zone[outer].getCards());
            creature = creature.getType("Creature");

            for (int i = 0; i < creature.size(); i++)
            {
               c = creature.get(i);
               if (!c.getKeyword().contains(keyword))
               {
                  c.addExtrinsicKeyword(keyword);
                  gloriousAnthemList.add(c);
               }
            }// for inner
         }// for outer
      }// execute()
   };

and in" public static HashMap<String, Command> commands = new HashMap<String, Command>();
   static {" add: commands.put("Levitation", Levitation);

and finally in StateBasedEffects.java, after "public void initStateBasedEffectsList()":

cardToEffectsList.put("Levitation", new String[] {"Levitation"});

and in cards.txt:
Levitation
2 U U
Enchantment
All creatures you control gain flying.
Knighthood:
Code: Select all
In GameActionUtil.java:

- in public static void executeCardStateEffects() add : Knighthood.execute();

- after }// executeCardStateEffects() add:

public static Command Levitation = new Command()
   {
      private static final long serialVersionUID = ;
      
      CardList gloriousAnthemList = new CardList();

      public void execute()
      {
         String keyword = "First Strike";

         CardList list = gloriousAnthemList;
         Card c;
         // reset all cards in list - aka "old" cards
         for (int i = 0; i < list.size(); i++)
         {
            c = list.get(i);
            c.removeExtrinsicKeyword(keyword);
         }

         list.clear();
         PlayerZone[] zone = getZone("Knighthood");

         for (int outer = 0; outer < zone.length; outer++)
         {
            CardList creature = new CardList(zone[outer].getCards());
            creature = creature.getType("Creature");

            for (int i = 0; i < creature.size(); i++)
            {
               c = creature.get(i);
               if (!c.getKeyword().contains(keyword))
               {
                  c.addExtrinsicKeyword(keyword);
                  gloriousAnthemList.add(c);
               }
            }// for inner
         }// for outer
      }// execute()
   };

and in" public static HashMap<String, Command> commands = new HashMap<String, Command>();
   static {" add: commands.put("Knighthood", Knighthood);

and finally in StateBasedEffects.java, after "public void initStateBasedEffectsList()":

cardToEffectsList.put("Knighthood", new String[] {"Knighthood"});

and in cards.txt:
Knighthood
2 W
Enchantment
All creatures you control gain first strike.

Boartusk Liege
Code: Select all
In GameActionUtil.java:

- in public static void executeCardStateEffects() add : Boartusk_Liege.execute();

- after }// executeCardStateEffects() add:

public static Command Boartusk_Liege = new Command()
   {
      private static final long serialVersionUID = ;
      
      CardList gloriousAnthemList = new CardList();

      public void execute()
      {
         CardList list = gloriousAnthemList;
         Card c;
         // reset all cards in list - aka "old" cards
         for (int i = 0; i < list.size(); i++)
         {
            c = list.get(i);
            if (CardUtil.getColors(c).contains(Constant.Color.Green)
                  && !c.getName().equals("Boartusk Liege"))
            {
               c.addSemiPermanentAttackBoost(-1);
               c.addSemiPermanentDefenseBoost(-1);
            }
            if (CardUtil.getColors(c).contains(Constant.Color.Red)
                  && !c.getName().equals("Boartusk Liege"))
            {
               c.addSemiPermanentAttackBoost(-1);
               c.addSemiPermanentDefenseBoost(-1);
            }
         }

         // add +1/+1 to cards
         list.clear();
         PlayerZone[] zone = getZone("Boartusk Liege");

         // for each zone found add +1/+1 to each card
         for (int outer = 0; outer < zone.length; outer++)
         {
            CardList creature = new CardList(zone[outer].getCards());
            creature = creature.getType("Creature");

            for (int i = 0; i < creature.size(); i++)
            {
               c = creature.get(i);
               if (CardUtil.getColors(c).contains(Constant.Color.Green)
                     && !c.getName().equals("Boartusk Liege"))
               {
                  c.addSemiPermanentAttackBoost(1);
                  c.addSemiPermanentDefenseBoost(1);
               }
               if (CardUtil.getColors(c).contains(Constant.Color.Red)
                     && !c.getName().equals("Boartusk Liege"))
               {
                  c.addSemiPermanentAttackBoost(1);
                  c.addSemiPermanentDefenseBoost(1);
               }

               gloriousAnthemList.add(c);
            }// for inner
         }// for outer
      }// execute()
   };// Boartusk_Liege

and in" public static HashMap<String, Command> commands = new HashMap<String, Command>();
   static {" add: commands.put("Boartusk_Liege", Boartusk_Liege);

and finally in StateBasedEffects.java, after "public void initStateBasedEffectsList()":

cardToEffectsList.put("Boartusk Liege", new String[] {"Boartusk Liege"});

and in cards.txt:
Boartusk Liege
1 RG RG RG
Creature Goblin Knight
Other green creatures you control get +1/+1. Other red creatures you control get +1/+1.
3/4
Trample
Glen Elendra Liege
Code: Select all
In GameActionUtil.java:

- in public static void executeCardStateEffects() add : Glen_Elendra.execute();

- after }// executeCardStateEffects() add:

public static Command Glen_Elendra = new Command()
   {
      private static final long serialVersionUID = ;
      
      CardList gloriousAnthemList = new CardList();

      public void execute()
      {
         CardList list = gloriousAnthemList;
         Card c;
         // reset all cards in list - aka "old" cards
         for (int i = 0; i < list.size(); i++)
         {
            c = list.get(i);
            if (CardUtil.getColors(c).contains(Constant.Color.Blue)
                  && !c.getName().equals("Glen Elendra Liege"))
            {
               c.addSemiPermanentAttackBoost(-1);
               c.addSemiPermanentDefenseBoost(-1);
            }
            if (CardUtil.getColors(c).contains(Constant.Color.Black)
                  && !c.getName().equals("Glen Elendra Liege"))
            {
               c.addSemiPermanentAttackBoost(-1);
               c.addSemiPermanentDefenseBoost(-1);
            }
         }

         // add +1/+1 to cards
         list.clear();
         PlayerZone[] zone = getZone("Glen Elendra Liege");

         // for each zone found add +1/+1 to each card
         for (int outer = 0; outer < zone.length; outer++)
         {
            CardList creature = new CardList(zone[outer].getCards());
            creature = creature.getType("Creature");

            for (int i = 0; i < creature.size(); i++)
            {
               c = creature.get(i);
               if (CardUtil.getColors(c).contains(Constant.Color.Blue)
                     && !c.getName().equals("Glen Elendra Liege"))
               {
                  c.addSemiPermanentAttackBoost(1);
                  c.addSemiPermanentDefenseBoost(1);
               }
               if (CardUtil.getColors(c).contains(Constant.Color.Black)
                     && !c.getName().equals("Glen Elendra Liege"))
               {
                  c.addSemiPermanentAttackBoost(1);
                  c.addSemiPermanentDefenseBoost(1);
               }

               gloriousAnthemList.add(c);
            }// for inner
         }// for outer
      }// execute()
   };// Glen_Elendra

and in" public static HashMap<String, Command> commands = new HashMap<String, Command>();
   static {" add: commands.put("Glen_Elendra", Glen_Elendra);

and finally in StateBasedEffects.java, after "public void initStateBasedEffectsList()":

cardToEffectsList.put("Glen Elendra Liege", new String[] {"Glen Elendra Liege"});

and in cards.txt:
Glen Elendra Liege
1 UB UB UB
Creature Goblin Knight
Other blue creatures you control get +1/+1. Other black creatures you control get +1/+1.
2/3
Flying
Thistledown Liege
Code: Select all
In GameActionUtil.java:

- in public static void executeCardStateEffects() add : Thistledown_Liege.execute();

- after }// executeCardStateEffects() add:

public static Command Thistledown_Liege= new Command()
   {
      private static final long serialVersionUID = ;
      
      CardList gloriousAnthemList = new CardList();

      public void execute()
      {
         CardList list = gloriousAnthemList;
         Card c;
         // reset all cards in list - aka "old" cards
         for (int i = 0; i < list.size(); i++)
         {
            c = list.get(i);
            if (CardUtil.getColors(c).contains(Constant.Color.Blue)
                  && !c.getName().equals("Thistledown Liege"))
            {
               c.addSemiPermanentAttackBoost(-1);
               c.addSemiPermanentDefenseBoost(-1);
            }
            if (CardUtil.getColors(c).contains(Constant.Color.White)
                  && !c.getName().equals("Thistledown Liege"))
            {
               c.addSemiPermanentAttackBoost(-1);
               c.addSemiPermanentDefenseBoost(-1);
            }
         }

         // add +1/+1 to cards
         list.clear();
         PlayerZone[] zone = getZone("Thistledown Liege");

         // for each zone found add +1/+1 to each card
         for (int outer = 0; outer < zone.length; outer++)
         {
            CardList creature = new CardList(zone[outer].getCards());
            creature = creature.getType("Creature");

            for (int i = 0; i < creature.size(); i++)
            {
               c = creature.get(i);
               if (CardUtil.getColors(c).contains(Constant.Color.Blue)
                     && !c.getName().equals("Thistledown Liege"))
               {
                  c.addSemiPermanentAttackBoost(1);
                  c.addSemiPermanentDefenseBoost(1);
               }
               if (CardUtil.getColors(c).contains(Constant.Color.White)
                     && !c.getName().equals("Thistledown Liege"))
               {
                  c.addSemiPermanentAttackBoost(1);
                  c.addSemiPermanentDefenseBoost(1);
               }

               gloriousAnthemList.add(c);
            }// for inner
         }// for outer
      }// execute()
   };// Thistledown_Liege

and in" public static HashMap<String, Command> commands = new HashMap<String, Command>();
   static {" add: commands.put("Thistledown_Liege", Thistledown_Liege);

and finally in StateBasedEffects.java, after "public void initStateBasedEffectsList()":

cardToEffectsList.put("Thistledown Liege", new String[] {"Thistledown Liege"});

and in cards.txt:
Thistledown Liege
1 UB UB UB
Creature Goblin Knight
Other blue creatures you control get +1/+1. Other white creatures you control get +1/+1.
1/3
Flash

Could anybody tells me if it's right or wrong or if i have forgotten something...
Thanks for your help.
cyclope
 
Posts: 69
Joined: 28 Sep 2009, 18:08
Has thanked: 0 time
Been thanked: 0 time

Re: Programming a card

Postby Marek14 » 25 Oct 2009, 16:45

How about Creakwood Liege? It could be cobbled from your code and Bringer of the Green Dawn. Mindwrack Liege might also be possible, by modifying Elvish Piper.

Ashenmoor Liege, Wilt-Leaf Liege and Murkfiend Liege have more complex rules.

As for Balefire Liege and Deathbringer Liege, they should be possible (since there's Fable of Wolf and Owl, Quirion Dryad, and Merrow Levitator). With those two, the whole cycle of Hatchlings might be easy to do as well:

Belligerent Hatchling
Noxious Hatchling
Shrewd Hatchling (maybe not this one)
Sturdy Hatchling
Voracious Hatchling

Going through cards.txt, I see that there is large amount of cards implemented that trigger on you playing a certain type of spell. Further possibilities are:

Eidolon cycle: Aurora Eidolon (damage prevention, so probably not doable right now), Enigma Eidolon, Entropic Eidolon, Sandstorm Eidolon, Verdant Eidolon

Spiritcraft cards: Baku Altar, Blademane Baku, Bounteous Kirin, Briarknit Kami, Budoka Pupil (flip), Callow Jushi (flip), Celestial Kirin, Cloudhoof Kirin, Cunning Bandit (flip), Dreamcatcher, Earthshaker, Elder Pine of Jukai (soulshift), Faithful Squire (flip), Fiddlehead Kami, Guardian of Solitude, Haru-Onna, Hikari, Twilight Guardian (blink), Hired Muscle (flip), Horizon Seed, Infernal Kirin, Innocence Kami, Jade Idol, Kami of Fire's Roar, Kami of Tattered Shoji, Kami of the Hunt, Kami of the Painted Road, Kami of the Waning Moon, Kemuri-Onna, Kiri-Onna, Kodama of the South Tree, Kyoki, Sanity's Eclipse, Loam Dweller, Nikko-Onna, Orbweaver Kumo, Ore Gorger, Oyobi, Who Split the Heavens, Petalmane Baku, Quillmane Baku, Scaled Hulk, Sire of the Storm, Skullmane Baku, Skyfire Kirin, Soilshaper, Soul of Magma, Tallowisp, Teller of Tales, Thief of Hope (soulshift), Waxmane Baku, Yuki-Onna

Other cards from Merrow Levitator cycle: Ballynock Trapper, Cinder Pyromancer, Merrow Bonegnawer, Nettle Sentinel

Mimic cycle: Battlegate Mimic, Nightsky Mimic, Riverfall Mimic, Shorecrasher Mimic. Woodlurker Mimic

Duo cycle: Emberstrike Duo, Gravelgill Duo, Safehold Duo, Tattermunge Duo. Thistledown Duo

Battlewand Oak
Blood Funnel
Celestial Ancient
Circu, Dimir Lobotomist
Cloven Casting (spell copying)
Contemplation
Customs Depot
Door of Destinies
Dreamspoiler Witches
Dwarven Patrol
Endrek Sahr, Master Breeder (state trigger)
Equilibrium
Eyes of the Watcher (scry)
Faerie Tauntings
Furious Assault
Gelectrode
Glen Elendra Pranksters
Glimpse of Nature (probably hard)
Gloryscale Viashino
Halcyon Glaze
Inspired Sprite
Kurgadon
Leering Emblem
Leonin Battlemage
Leyline of Lightning (leyline ability)
Loyal Gyrfalcon
Lys Alana Bowmaster
Lys Alana Huntmaster
Manaplasm
Masked Admirers
Merrow Reejerey
Mindmoil
Mirari (spell copying)
Mishra, Artificer Prodigy
Momir Vig, Simic Visionary
Mountain Titan (hard)
Onslaught
Pristine Angel
Pyromancer Ascension (spell copying)
Quest for the Holy Relic
Spellweaver Volute (spell copying)
Thorntooth Witch
Tibor and Lumia
Tidespout Tyrant
Veilstone Amulet (beware, it doesn't give untargetability, it sets up a rule)
Voidmage Husher (ability countering)
Wee Dragonauts
Witch-Maw Nephilim
Marek14
Tester
 
Posts: 2773
Joined: 07 Jun 2008, 07:54
Has thanked: 0 time
Been thanked: 303 times

Re: Programming a card

Postby DennisBergkamp » 25 Oct 2009, 16:50

Cyclope,

Hmm, Knighthood and Levitation should work fine... however, the Lieges are much trickier than that, since they pump "Other" creatures of the same colors.
Your code might've worked if it was for a Legendary creature, but since there could be multiple Lieges in play, it's trickier.
User avatar
DennisBergkamp
AI Programmer
 
Posts: 2602
Joined: 09 Sep 2008, 15:46
Has thanked: 0 time
Been thanked: 0 time

Re: Programming a card

Postby cyclope » 25 Oct 2009, 19:38

Ok Dennis , i haven't thought about that it could have in play more than one of these Liege...
Have you got any idea to implement these cards ?

I also try to code Stormscape Apprentice:
Code: Select all
//*************** START *********** START **************************
if (cardName.equals("Stormscape Apprentice")
    {
       final SpellAbility ability = new Ability_Tap(card, "W")
        {

         private static final long serialVersionUID = ;
         public void resolve()
           {
             Card c = getTargetCard();
             c.tap();
           }
           public boolean canPlayAI() {return false;}
        };//SpellAbility

   final SpellAbility ability2 = new Ability_Tap(card, "B")
        {

         private static final long serialVersionUID = ;
         public void resolve()
         {
          String opponent = AllZone.GameAction.getOpponent(card.getController());
          AllZone.GameAction.getPlayerLife(opponent).subtractLife(1);
        }
        public boolean canPlayAI()
        {
          //computer should play ability if this creature doesn't attack
          Combat c = ComputerUtil.getAttackers();
          CardList list = new CardList(c.getAttackers());

          //could this creature attack?, if attacks, do not use ability
          return (! list.contains(card));
        }
        };//SpellAbility


        card.addSpellAbility(ability);
        ability.setDescription("W, tap: Tap target creature.");
        ability.setBeforePayMana(CardFactoryUtil.input_targetCreature(ability));
card.addSpellAbility(ability2);
      ability2.setDescription("B, tap: Target player loses 1 life.");
      ability2.setStackDescription(card.getName() + " - Opponent loses 1 life.");

     }//*************** END ************ END **************************
and Stormscape Master:
Code: Select all
//*************** START *********** START **************************
if (cardName.equals("Stormscape Master")
    {
   
   final SpellAbility ability = new Ability_Tap(card, "B B")
        {

         private static final long serialVersionUID = ;
         public void resolve()
         {
          String opponent = AllZone.GameAction.getOpponent(card.getController());
          AllZone.GameAction.getPlayerLife(opponent).subtractLife(2);
   AllZone.GameAction.getPlayerLife(card.getController()).addLife(2);
        }
        public boolean canPlayAI()
        {
          //computer should play ability if this creature doesn't attack
          Combat c = ComputerUtil.getAttackers();
          CardList list = new CardList(c.getAttackers());

          //could this creature attack?, if attacks, do not use ability
          return (! list.contains(card));
        }
        };//SpellAbility


        card.addSpellAbility(ability);
              ability.setDescription("B B, tap: Target player loses 2 life and you gain 2 life");
      ability2.setStackDescription(card.getName() + " - Opponent loses 2 life."+card.getName()+card.getController()+" gains 2 life");

     }//*************** END ************ END **************************

and in cards.txt:
Stormscape Master
2 U U
Creature Human Wizard
WW, tap : target creature gains protection from color of your choice until end of turn. BB, tap: target player loses 2 life and you gain 2 life.
2/2
TgtKpump W W T:Protection from white
TgtKpump W W T:Protection from red
TgtKpump W W T:Protection from black
TgtKpump W W T:Protection from green
TgtKpump W W T:Protection from blue
Did I succeeded or not ? (I'm not sure of the last lines...)
Last edited by cyclope on 25 Oct 2009, 20:28, edited 1 time in total.
cyclope
 
Posts: 69
Joined: 28 Sep 2009, 18:08
Has thanked: 0 time
Been thanked: 0 time

Re: Programming a card

Postby DennisBergkamp » 25 Oct 2009, 20:18

Yeah I think I know how to implement them, hopefully without bugs.

Stormscape Apprentice looks good :)
User avatar
DennisBergkamp
AI Programmer
 
Posts: 2602
Joined: 09 Sep 2008, 15:46
Has thanked: 0 time
Been thanked: 0 time

Re: Programming a card

Postby DennisBergkamp » 25 Oct 2009, 20:43

Oh, and actually I don't really "execute" these effects anymore with "Levitation.execute()", instead add it to the big list at the bottom of GameActionUtil:

Code: Select all
commands.put("Levitation", Levitation);
and then in StateBasedEffects.java:

Code: Select all
cardToEffectsList.put("Levitation",new String[] {"Levitation"});
User avatar
DennisBergkamp
AI Programmer
 
Posts: 2602
Joined: 09 Sep 2008, 15:46
Has thanked: 0 time
Been thanked: 0 time

PreviousNext

Return to Developer's Corner

Who is online

Users browsing this forum: No registered users and 30 guests

Main Menu

User Menu

Our Partners


Who is online

In total there are 30 users online :: 0 registered, 0 hidden and 30 guests (based on users active over the past 10 minutes)
Most users ever online was 7967 on 09 Sep 2025, 23:08

Users browsing this forum: No registered users and 30 guests

Login Form