MTG Forge Source Code

 

MTG Forge lets you play Magic: The Gathering against a computer opponent using all of the rules.

 

All source code is released under the GNU license.

 

Developers Blog: mtgrares.blogspot.com

 

The source code compiles with at least Java 1.4.  The source code will compile with Java 1.6 but you have to turn off all of the warnings since the code doesn't use generics. 

 

You can get the Java compiler from www.java.com/getjava To compile, unzip everything into the same directory and type “javac *.java”  To run it type “java Gui_NewGame  To change the message displayed in title bar like “MTG Forge - 10/15/2008” change the variable Constant.ProgramName.

 

Card and SpellAbility

The Card class represents regular, cardboard cards and the SpellAbility class represents the effect that any card does when it is played.  SpellAbility is an abstract class and the resolve() method must be implemented.  The resolve() method of Wrath of God actually destroys all the creatures.

 

CardFactory

All the code for the Magic cards is in CardFactory.java  The basic card information like name, mana cost, text is in the file “cards.txt”  CardFactory gets the card text from cards.txt and then adds the correct SpellAbility object to the Card object.  For example, CardFactory adds a SpellAbility object to Wrath of God that destroys all creatures. 

 

Input

The Input class handles all of the mouse input.  All phases are Input objects.  The Input class also handles choosing a card’s target.  The Input class also handles paying the mana cost.

 

Required Files

In order to run, MTG Forge requires the following files.  If one of these files is missing, you will probably get a weird error message.

 

cards.txt                 - holds the card’s text, name, mana cost, etc…

all-decks2              - holds all of the decks that are available

card-pictures.txt     - used to download the card pictures

 

down.gif       - used by the deck editor

up.gif           - used by the deck editor

 

common.txt            - used to create booster packs for sealed and draft

uncommon.txt

rare.txt

 

How to update MTG Forge

If you have added cards to MTG Forge and you wanted to distribute a new version, just update all of the classes in the file “run-forge.jar  Open the jar file with a program like Winzip. 

 

The file “run-forge.exe” runs the class Gui_NewGame in “run-forge.jar  The file “run-forge.exe” is just a Java wrapper that was created using JSmooth, http://jsmooth.sourceforge.net/

 

 

Below are excerpts from my blog describing how to program cards.

 

MTG Forge Overview

 

Trying to understand a whole programming project is difficult and almost impossible without copious amounts of documentation.  I’m going to try to summarize the major classes MTG Forge uses.  The Card class mimics the functions of a real card, and the Card class exists in your hand, in play, in the graveyard, and removed from the game.  A Card can hold any number of SpellAbility objects.  Each Card has at least one SpellAbility and if the Card has an ability like Elvish Piper, the Card will have two SpellAbilities added.  One SpellAbility is the default summon creature spell and the other SpellAbility is the activated ability that lets you put a creature from your hand into play. 

 

Every SpellAbility can have an optional mana cost, the only SpellAbilities that don’t have a mana cost are creatures that have tap abilities.  Instants and Sorceries also use the SpellAbility class.  SpellAbility has a resolve method that is called after it is popped off the stack.  The CardFactory class makes new cards and copies existing ones.  CardFactory also adds the SpellAbility objects to the Cards, and implements SpellAbility’s resolve method.  The resolve method for Wrath of God would destroy everything.

 

The file “cards.txt” holds simple cards as well as all the text for Instants and Sorceries.  This file saved time because I didn’t have to program simple creatures like Glory Seeker.  This file also let me specify mana abilities, so creatures like Bird of Paradise are only implemented in cards.txt, no other code is needed.  Flying, haste, fear, and vigilance are also supported by cards.txt  Some users have added their own cards, which surprised me.

 

The class that runs everything is Gui_NewGame.  It starts up the user interface and initializes everything.  Every screen is a separate class and JFrame.  These display classes all start with the abbreviation Gui and are only loosely tied together, they all read from Constant.Runtime in order to read which deck you and the computer are using.

 

I hate not throwing some code at you. J The code below constructs Serra Avenger.  AllZone holds all of the zones, in play, graveyard, etc… and can be globally accessed.  Global variables are generally bad, but they really helped get MTG Forge working.  The computer can play regular creature cards like Serra Avenger using the code below, since it just pays the mana cost and the resolve method works the same for both the human and computer player. 

 

The computer checks the canPlay and canPlayAI methods in SpellAbility to see if it is allowed to play that card.  This specific card does not have a canPlayAI method, a card like Hex does, because it targets 6 creatures.  The card below belongs in the human player’s deck, since he both controls and owns the card.  If this card was going into the computer’s library, the computer would be listed as the owner and controller.

 

final Card card = new Card();

card.setName("Serra Avenger");

card.setManaCost("W W");

 

card.setAttack(3);

card.setDefense(3);

 

card.addKeyword("Flying");

card.addKeyword("Vigilance");

 

card.setOwner("Human");

card.setController("Human");

 

SpellAbility spell = new SpellAbility(SpellAbility.Spell, card)

{

  public void resolve()

  {

    PlayerZone play = AllZone.getZone(Constant.Zone.Play, card.getController());

    play.add(card);

  }

  public boolean canPlay()

  {

   //Serra Avenger cannot be played for the first three turns of the game

   return 3 < AllZone.Phase.getTurn();

  }

};

card.addSpellAbility(spell);

 

 

Programming Shock

 

Many cards and abilities are similar to Shock.  In case you can’t remember, Shock deals 2 damage to target creature or player.  First, Shock has to be added to the cards.txt file.  Cards.txt holds all of the text that the card displays as well as the mana cost. 

 

Shock

R

Instant

Shock deals 2 damage to target creature or player.

 

The resolve method checks to see if the card is still in play, something that we take for granted in a real-life game.  SpellAbility only allows for 1 target to be set, either a card or a player by the setTargetPlayer(String player) and setTargetCard(Card) methods.  To get the target you call either getTargetPlayer or getTargetCard. The SpellAbiliy method setBeforePayMana(Input), lets the user actually choose the target card or player.  CardFactoryUtil has a bunch of reusable Input classes that gets a user choice.  If the spell just targeted a creature you would use CardFactoryUtil.input_targetCreature. 

 

SpellAbility canPlayAI method returns false if you do not want the computer to play this spell.   A very simple AI for Shock is below.  canPlayAI is called first, and if it returns true, then chooseTargetAI is called.  chooseTargetAI sets the target for the spell by either calling setTargetCard(Card) or setTargetPlayer(String player).  Some cards like Wrath of God do not have a chooseTargetAI method, because no targets are needed.

 

This checks to see that the human player (you) have at least 1 creature in play and sets that creature as the target.  A more sophisticated AI would try to get the best creature that Shock could kill, like a 2/2 flyer.  The AI could also see if you had low life, and could alternately target you instead of a creature.  The regular AI for Shock tries to kill a creature with flying or you if your life is low.

 

//simple AI for Shock

//should be put in a SpellAbility object

public boolean canPlayAI()

{

  CardList list = CardFactoryUtil.AI_getHumanCreature();

  return 0 < list.size();

}

public void chooseTargetAI()

{

  CardList list = CardFactoryUtil.AI_getHumanCreature();

  setTargetCard(list.get(0));

}

 

//Complete card

final Card card = new Card();

card.setName(“Shock”);

card.setManaCost("R");

card.setOwner(Constant.Player.Human);

card.setController(Constant.Player.Human);

 

final SpellAbility spell = new Spell(card)

{

  int damage = 2;

 

  public boolean canPlayAI() {return false;}

 

  public void resolve()

  {

    if(getTargetCard() != null)

    {

      if(AllZone.GameAction.isCardInPlay(getTargetCard()))

      {

        Card c = getTargetCard();

        c.addDamage(damage);

      }

    }

    else

      AllZone.GameAction.getPlayerLife(getTargetPlayer()).subtractLife(damage);

  }//resolve()

};//SpellAbility

card.addSpellAbility(spell);

spell.setBeforePayMana(CardFactoryUtil.input_targetCreaturePlayer(spell));

 

//Shock as it would be in CardFactory

if(cardName.equals("Shock"))

{

  final SpellAbility spell = new Spell(card)

  {

  //same stuff

  };//SpellAbility

 

  //this is cleared because a card is presumed to be a permanent

  //all Sorceries and Instants have to call card.clearSpellAbility()

  card.clearSpellAbility();

  card.addSpellAbility(spell);

 

  spell.setBeforePayMana(CardFactoryUtil.input_targetCreaturePlayer(spell));

}

 

 

Programming Giant Growth

 

People seemed to have really enjoyed my entry on “Programming Shock” so I’ll cover another common card.  If you look at the code, you will see that it works for both Giant Growth and its Planar Chaos brother Brute Force.  Remember at the top of the CardFactory getCard method the mana cost and name of the card is already set.

 

Giant Growth and other effects stay around “until end of turn.”  End of turn is commonly abbreviated EOT.  In order to program EOT effects some bit of code has to be executed later.

 

MTG Forge source code has an interface called Command, which is a software Design Pattern.  The interface for Command is very simple.

 

public interface Command

{   

    public void execute();   

}

 

Command is a generic object that is used to execute arbitrary code at a later time.  So when Giant Growth resolves it will give the target creature +3/+3 and make a Command object that resets the creatures stats to normal.  AllZone.EndOfTurn.addUntil(Command)

executes all until EOT Commands.

 

There are really two types of EOT effects, until EOT and at EOT.  Giant Growth says until EOT and Ball Lightning says at EOT.  So Giant Growth would use AllZone.EndOfTurn.addUntil(Command) and Ball Lightning would use AllZone.EndOfTurn.addAt(Command)  This division doesn’t matter in the current version of MTG Forge, since you cannot stop at EOT.  Technically “At EOT” effects are done first, then you would get priority to play cards/abilities, and then “Until Effects” wear off.

 

Unfortunately the resolve method for Giant Growth is a little messy because a Command object has to be constructed.  Command has to be inside the resolve method in case Giant Growth is played, and then returned to your hand, and then played again.

 

This is very Java specific and a little complicated.  The array “target” is used because the Command object needs a final variable.  Target simulates a variable that can be changed, while fulfilling the syntax that Java needs to make the Command object.

 

The AI just makes sure that the computer is attacking with at least one creature and plays Giant Growth on that creature.  Currently the computer cannot play any spells/abilities during combat, so the computer will never attack with a smaller creature and then play Giant Growth.  The computer will only play Giant Growth during his main phase.

 

if(cardName.equals("Giant Growth") || cardName.equals("Brute Force"))

{

SpellAbility spell = new Spell(card)

{

public void resolve()

{

  final Card[] target = new Card[1];

  final Command untilEOT = new Command()

  {

    public void execute()

    {

      if(AllZone.GameAction.isCardInPlay(target[0]))

      {

        target[0].setAttack (target[0].getAttack() - 3);

        target[0].setDefense(target[0].getDefense()- 3);

      }

    }

  };

 

  target[0] = getTargetCard();

  if(AllZone.GameAction.isCardInPlay(target[0]))

  {

    target[0].setAttack (target[0].getAttack() + 3);

    target[0].setDefense(target[0].getDefense()+ 3);

 

    AllZone.EndOfTurn.addUntil(untilEOT);

  }

}//resolve()

};

 

spell.setBeforePayMana(CardFactoryUtil.input_targetCreature(spell));

 

//Instants and Sorceries should clear because cards are presumed to be permanents

card.clearSpellAbility();

card.addSpellAbility(spell);

}

 

//AI removed for clarity, should be put in SpellAbility

public boolean canPlayAI()

{

return getAttacker() != null;

}

public void chooseTargetAI()

{

setTargetCard(getAttacker());

}

public Card getAttacker()

{

//target creature that is going to attack

Combat c = ComputerUtil.getAttackers();

Card[] att = c.getAttackers();

if(att.length != 0)

  return att[0];

else

  return null;

}

 

 

Programming Magic Cards 1

 

Many people are interested in how I program cards in MTG Forge.  Obviously some of the cards are simple and some are complicated.  Wrath of God is very simple while Kiki-Jiki, Mirror Breaker is very complicated.  The article “MTG Forge Overview” showed you Serra Angel which is a typical creature.  Today I’m going to show you how I programmed tap abilities with and without mana costs as well as how to draw a card and gain life.

 

The code showed below is for “Super Card.”  It is an artifact that lets you tap to draw a card or tap and pay 1 to gain a life.  Super Card has 3 SpellAbilities added to the card: 1. so it comes into play as a permanent, 2. gain 1 life, and 3. draw a card.  As you may or not remember, the SpellAbility class is used for all spells and abilities, hence its name.  The life gaining effects costs 1 mana of any color while the draw card ability only taps the card.  The resolve method of SpellAbility does the main function of any card or ability.  The card’s controller is the one who gains life.   If your opponent steals it during the game the effect will still work correctly and you will still own the card, but your opponent would control it.  If it is put into a graveyard, it will go into its owner’s graveyard.

 

The setDescription method of SpellAbility sets the text that is show text box of the card on screen.  The setStackDescription, also a part of SpellAbility, is the text that the stack displays.  These two abilities require a different argument for the setBeforePayMana function.  The setBeforePayMana function optionally lets a user do some action before choosing any targets and before paying for mana, it is the first thing that is checked when a card is clicked on.  The setBeforePayMana function makes the user pay for the mana cost when gaining life, and just taps the card when drawing another card.  You can put anything into the setBeforePayMana method and I added “tap abilities” late in development.  setBeforePayMana accepts Input objects.  Input objects process all user input through the mouse and allows the user choose targets.  This way of doing abilities and tap abilities is not every elegant but it works.

 

AllZone is a global (static) object that can be accessed from anywhere in the program and GameAction does some logical actions like drawing a card, gaining life, discarding, etc...  When this card is clicked on the user is given the choice of playing either of the two abilities.

 

Card card = new Card();

card.setName("Super Card");

card.setOwner(“Human”);

card.setController(“Human”);

card.setManaCost(“1”);

card.addType(“Artifact”);

card.addSpellAbility(new Spell_Permanent(card));

 

final SpellAbility a = new Ability_Tap(card, "1")

{

  public void resolve()

  {

    AllZone.GameAction.getPlayerLife(card.getController()).addLife(1);

    setStackDescription(card.getController() +" gains 1 life.");

  }

};

a.setDescription("1, tap: You gain 1 life.");

a.setBeforePayMana(new Input_PayManaCost(a));

card.addSpellAbility(a);

 

 

final SpellAbility b = new Ability_Tap(card, "0")

{

  public void resolve()

  {

    AllZone.GameAction.drawCard(card.getController());

    setStackDescription(card.getController() +" draws a card");

  }

};

b.setDescription("tap: You draw a card.");

b.setBeforePayMana(new Input_NoCost_TapAbility((Ability_Tap) b)); 

card.addSpellAbility(b);

 

 

 

Programming Life Gain

 

I wanted to cover some of the basic abilities that cards do like gaining life, losing life, drawing a card, etc…  AllZone is a static global variable that can be accessed from anywhere.  GameAction does some high level actions like drawing cards and destroying a creature.  The code snippets that I will be going over usually will go in the resolve method of SpellAbility objects.  These are some common actions that are done throughout the game. 

 

This is how you gain life.

 

PlayerLife p = AllZone.GameAction.getPlayerLife(Constant.Player.Computer);

p.addLife(2);

 

Usually I wouldn’t hardcode the player (human or computer) so the code would look like this.

 

PlayerLife p = AllZone.GameAction.getPlayerLife(card.getController());

p.addLife(2);

 

Subtracting life is similar.

 

PlayerLife p = AllZone.GameAction.getPlayerLife(Constant.Player.Computer);

p.subtractLife(4);

 

Drawing a card looks like this.  If you wanted to draw multiple cards, you would use a for loop.  AllZone.GameAction.drawCard(Constant.Player.Human);

 

Whenever a card is put into a graveyard, AllZone.GameAction.destroy(Card) is used. 

AllZone.GameAction.destroy(Card);

 

Cards like Strangling Soot will have a SpellAbility.resolve that looks like this.

AllZone.GameAction.destroy(spell.getTargetCard());

 

Terror is similar but says that the card cannot be regenerated.  AllZone.GameAction.destroyNoRegeneration(Card);

 

This discards a card at random from a player.

AllZone.GameAction.discardRandom(Constant.Player.Computer);

 

Regular discard is done this way.  This assumes that the chosen card is already picked and this method just moves the card from a player’s hand to the graveyard.

AllZone.GameAction.discard(Card);

 

Sacrificing a card is similar to discarding, the chosen card is already picked.  This would be called from the Input class, since it handles all the user input from the mouse.  The Input class lets the user choose a card and then the code below is executed.

AllZone.GameAction.sacrifice(Card);

 

Shuffling your library is a common occurrence.

AllZone.GameAction.shuffle(card.getController());

 

Cards like Shock just add damage to a card.  GameAction.checkStateEffects moves the card to the graveyard if it is needed.

Card c = spell.getTargetCard();

c.addDamage(2);

 

 

 

 

Input Explained

 

Some people have commented that the Input class is giving them problems and I understand where they are coming from.  All of this code was never meant to be understood by anyone else, lol.  Anyways, the Input class handles all the user input, which is from a mouse, and is used to set targets for cards.  The targets may be either a player or a card.  SpellAbility setTargetPlayer(String player) and setTargetCard(Card) actually sets the target that the resolve will use.  There is corresponding getTargetPlay and getTargetCard commands.  This limits cards to a single target.  Hex has multiple targets, but it is an exception.  Multiple targets can be programmed but the code is messy. 

 

The method SpellAbility setBeforePayMana(Input) is used to set the Input that will handle getting a card’s target.  The method could have been named better. 

 

CardFactoryUtil returns common Inputs that are useful. 

 

To target a player, CardFactoryUtil.input_targetPlayer(SpellAbility spell).  Hopefully Ancestral Recall is straightforward and easy to understand.  The computer always sets himself as the target with the method setChooseTargetAI and canPlayAI makes sure the computer doesn’t have to discard any cards.

 

if(cardName.equals("Ancestral Recall"))

{

SpellAbility spell = new Spell(card)

{

public void resolve()

{

  AllZone.GameAction.drawCard(getTargetPlayer());

  AllZone.GameAction.drawCard(getTargetPlayer());

  AllZone.GameAction.drawCard(getTargetPlayer());

}

public boolean canPlayAI()

{

  return AllZone.Computer_Hand.getCards().length <= 5;

}

};

spell.setChooseTargetAI(CardFactoryUtil.AI_targetComputer());

 

spell.setBeforePayMana(CardFactoryUtil.input_targetPlayer(spell));

card.clearSpellAbility();

card.addSpellAbility(spell);

 

To target a creature, CardFactoryUtil. input_targetCreature(SpellAbility spell)  Swords to Plowshares removes a creature from play and gives it’s controller some life.

 

if(cardName.equals("Swords to Plowshares"))

{

SpellAbility spell = new Spell(card)

{

public void resolve()

{

if(AllZone.GameAction.isCardInPlay(getTargetCard()))

{

  //add life

  String player = getTargetCard().getController();

  PlayerLife life = AllZone.GameAction.getPlayerLife(player);

  life.addLife(getTargetCard().getAttack());

 

  //remove card from play

  PlayerZone zone = AllZone.getZone(getTargetCard());

  zone.remove(getTargetCard());

}

}//resolve()

public boolean canPlayAI()

{

CardList creature = new CardList(AllZone.Human_Play.getCards());

creature = creature.getType("Creature");

return creature.size() != 0 && (AllZone.Phase.getTurn() > 4);

}

public void chooseTargetAI()

{

CardList play = new CardList(AllZone.Human_Play.getCards());

Card target = CardFactoryUtil.AI_getBestCreature(play);

setTargetCard(target);

}

};//SpellAbility

spell.setBeforePayMana(CardFactoryUtil.input_targetCreature(spell));

 

card.clearSpellAbility();

card.addSpellAbility(spell);

}

 

 

To target anything in the “type” line of a card like Creature, Land, Goblin, or Forest.  CardFactoryUtil.input_targetType(SpellAbility spell, String cardType)

Type can also be “All” which would let the user target any permanent.

 

if(cardName.equals("Stone Rain"))

{

final SpellAbility spell = new Spell(card)

{

public void resolve()

{

  Card c = getTargetCard();

  if(AllZone.GameAction.isCardInPlay(c))

    AllZone.GameAction.destroy(c);

}

 

};//Spell

card.clearSpellAbility();

card.addSpellAbility(spell);

 

spell.setChooseTargetAI(CardFactoryUtil.AI_targetType("Land", AllZone.Human_Play));

 

spell.setBeforePayMana(CardFactoryUtil.input_targetType(spell, "Land"));

}

 

Stone Rain also configures the AI by the method

CardFactoryUtil.AI_targetType(String type, PlayerZone zone)

 

Type is like Forest, Creature, Land, or All.  Common PlayerZones are

 

AllZone.Human_Play – Shock

AllZone.Computer_Play – Giant Growth

AllZone.Computer_Graveyard – Gravedigger

 

AllZone is just a global object that holds all of the “zones”, like in play, graveyard, etc…

 

The human player can only target cards in play, so cards like Gravedigger show a window so a card from the graveyard can be selected.  Dark Banishing has a custom Input that targets a non-black creature.  After a creature is chosen in the selectCard method, the user has to pay the mana cost which stops the current Input.  The SpellAbility of Dark Banishing was omitted for clarity.

 

Input target = new Input()

{

public void showMessage()

{

AllZone.Display.showMessage("Select target non-black creature for " +spell.getSourceCard());

 

ButtonUtil.enableOnlyCancel();

}

public void selectButtonCancel() {stop();}

 

public void selectCard(Card card, PlayerZone zone)

{

if((!CardUtil.getColors(card).contains(Constant.Color.Black))

&& card.isCreature()                                         

&& zone.is(Constant.Zone.Play))

{

  spell.setTargetCard(card);

  stopSetNext(new Input_PayManaCost(spell));

}//if

}//selectCard()

};//Input

 

spell.setBeforePayMana(target);

 

Static Abilities

 

Today, I’m going to talk about the hardest cards to program.  Cards like Glorious Anthem caused me a lot of headaches.

 

This is a definition of static abilities from the rules manual.

 

You don’t play and resolve static abilities like the other two ability types. When a permanent with a static ability comes into play, the ability’s effect simply “turns on.” It stays on as long as the permanent stays in play.  (Static abilities create continuous effects.)  Most enchantments have static abilities. For example, Telepathy reads, “Your opponents play with their hands revealed.” Once Telepathy is in play, you don’t have to pay a cost to have your opponent reveal his or her hand. Your opponent’s hand is just kept face up on the table until Telepathy leaves play.

 

I call static ability, state abilities since they depend on the current “state” or condition of the game.  State abilities include Glorious Anthem, Levitation, Soul Warden, and Castle Raptors.  Glorious Anthem gives your creatures +1/+1, so it changes the state of the game.  Castle Raptors gets a bonus if it is untapped.

 

In MTG Forge static effects are processed after you play a land, the computer plays a land, or a spell/ability is resolved.  The code for Glorious Anthem is a little messy since it keeps track of old creatures that were already in play.  Glorious Anthem first takes away the +1/+1 from all old cards and then gives the bonus for each copy of Glorious Anthem in play.  The code loops through all copies of Glorious Anthem in case you and your opponent both have it in play.  This code also works correctly if you have multiple Anthems in play.

 

The code below was taken from the class GameActionUtil.  It has a method called executeCardStateEffects that executes all of the effects.  Doing state effects this way has the drawback of separating code from the Card and SpellAbility classes which I would prefer not to.  Platinum Angel has a very cool and hard to program state effect that denies your opponent from winning the game.  Check it out in the upcoming 10th edition core set.

 

GameAction also implements some state effects in the method checkStateEffects.  Other MTG Forge state effects include sending a creature to the graveyard if it has sufficient damage.  Destroying legendary creatures if two are into play and checking to see if any player’s life is 0.  The cards that have a state effect in MTG Forge are Baru, Fist of Krosa, Soul Warden, Essence Warden, Nightmare, Korlash, Heir to Blackblade, Glorious Anthem, Gaea's Anthem, Wonder, Anger, Valor, Veteran Armorer, Radiant, Archangel, and Castle Raptors.

 

static Command Glorious_Anthem = new Command()

{

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);

    c.setAttack(c.getAttack() - 1);

    c.setDefense(c.getDefense() - 1);

  }

 

  //add +1/+1 to cards

  list.clear();

  PlayerZone[] zone = getZone("Glorious Anthem");

 

  //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);

      c.setAttack(c.getAttack() + 1);

      c.setDefense(c.getDefense() + 1);

 

      gloriousAnthemList.add(c);

    }//for inner

  }//for outer

}//execute()

};//Glorious Anthem

 

//returns all PlayerZones that has at least 1 Glorious Anthem

//if Computer has 2 Glorious Anthems, AllZone.Computer_Play will be returned twice

PlayerZone[] getZone(String cardName)

//method body removed on purpose