Thursday, July 12, 2007

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

No comments: