- Rewrote the card controller system.

- Converted Control Magic auras to continuous static abilities.
This commit is contained in:
Sloth
2013-03-16 23:28:18 +00:00
parent 8dc331ed14
commit f22b7aee0f
46 changed files with 146 additions and 273 deletions

View File

@@ -199,7 +199,9 @@ public class Card extends GameEntity implements Comparable<Card> {
private String colorsPaid = "";
private Player owner = null;
private ArrayList<Object> controllerObjects = new ArrayList<Object>();
private Player controller = null;
private long controllerTimestamp = 0;
private TreeMap<Long, Player> tempControllers = new TreeMap<Long, Player>();
// private String rarity = "";
private String text = "";
@@ -3522,6 +3524,13 @@ public class Card extends GameEntity implements Comparable<Card> {
public final void addChangeControllerCommand(final Command c) {
this.changeControllerCommandList.add(c);
}
public final void runChangeControllerCommands() {
for (final Command c : this.changeControllerCommandList) {
c.execute();
}
}
/**
* <p>
@@ -3578,98 +3587,41 @@ public class Card extends GameEntity implements Comparable<Card> {
* @return a {@link forge.game.player.Player} object.
*/
public final Player getController() {
if (this.controllerObjects.size() == 0) {
return this.owner;
if (!this.tempControllers.isEmpty()) {
final long lastTimestamp = this.tempControllers.lastKey();
if (lastTimestamp > this.controllerTimestamp) {
return this.tempControllers.lastEntry().getValue();
}
}
final Object topController = this.controllerObjects.get(this.controllerObjects.size() - 1);
if (topController instanceof Player) {
return (Player) topController;
if (this.controller != null) {
return this.controller;
}
return ((Card) topController).getController();
return this.owner;
}
/**
*
* TODO Write javadoc for this method.
*
* @param controllerObject
* an Object
*/
public final void addController(final Object controllerObject) {
final Object prevController = this.controllerObjects.size() == 0 ? this.owner : this.controllerObjects
.get(this.controllerObjects.size() - 1);
if (!controllerObject.equals(prevController)) {
if (controllerObject instanceof Player) {
for (int i = 0; i < this.controllerObjects.size(); i++) {
if (this.controllerObjects.get(i) instanceof Player) {
this.controllerObjects.remove(i);
}
}
}
this.controllerObjects.add(controllerObject);
if ((Singletons.getModel().getGame().getAction() != null) && (prevController != null)) {
Singletons.getModel().getGame().getAction().controllerChangeZoneCorrection(this);
}
if (prevController != null) {
for (final Command c : this.changeControllerCommandList) {
c.execute();
}
}
this.updateObservers();
}
public final void addTempController(final Player player, final long tstamp) {
this.tempControllers.put(tstamp, player);
}
/**
*
* TODO Write javadoc for this method.
*
* @param controllerObject
* a Object
*/
public final void removeController(final Object controllerObject) {
final Object currentController = this.getController();
this.controllerObjects.remove(controllerObject);
if (!currentController.equals(this.getController())) {
Singletons.getModel().getGame().getAction().controllerChangeZoneCorrection(this);
for (final Command c : this.changeControllerCommandList) {
c.execute();
}
this.updateObservers();
}
public final void removeTempController(final long tstamp) {
this.tempControllers.remove(tstamp);
}
public final void clearTempControllers() {
this.tempControllers.clear();
}
/**
*
* TODO Write javadoc for this method.
*/
public final void clearControllers() {
this.controllerObjects.clear();
clearTempControllers();
this.controller = null;
}
/**
*
* TODO Write javadoc for this method.
*
* @return an ArrayList<Object>
*/
public final ArrayList<Object> getControllerObjects() {
return this.controllerObjects;
}
/**
*
* TODO Write javadoc for this method.
*
* @param in
* an Object
*/
public final void setControllerObjects(final ArrayList<Object> in) {
this.controllerObjects = in;
public final void setController(final Player player, final long tstamp) {
clearTempControllers();
this.controller = player;
this.controllerTimestamp = tstamp;
}
/**
@@ -3682,23 +3634,8 @@ public class Card extends GameEntity implements Comparable<Card> {
*/
public final void setOwner(final Player player) {
this.owner = player;
//this.updateObservers();
}
/**
* <p>
* Setter for the field <code>controller</code>.
* </p>
*
* @return the equipped by
*/
/*
* public void setController(Player player) { boolean sameController =
* controller == null ? false : controller.isPlayer(player); controller =
* player; if (null != controller && !sameController) { for (Command var :
* changeControllerCommandList) var.execute(); } this.updateObservers(); }
*/
/**
* <p>
* Getter for the field <code>equippedBy</code>.

View File

@@ -330,6 +330,7 @@ public final class CardUtil {
newCopy.setUniqueNumber(in.getUniqueNumber());
newCopy.setCurSetCode(in.getCurSetCode());
newCopy.setOwner(in.getOwner());
newCopy.setController(in.getController(), 0);
newCopy.setFlipCard(in.isFlipCard());
newCopy.setDoubleFaced(in.isDoubleFaced());
newCopy.getCharacteristics().copy(in.getState(in.getCurState()));
@@ -342,7 +343,6 @@ public final class CardUtil {
sa.setSourceCard(in);
}
newCopy.setControllerObjects(in.getControllerObjects());
newCopy.setCounters(in.getCounters());
newCopy.setExtrinsicKeyword(in.getExtrinsicKeyword());
newCopy.setColor(in.getColor());

View File

@@ -184,6 +184,11 @@ public class StaticEffects {
// modify the affected card
for (int i = 0; i < affectedCards.size(); i++) {
final Card affectedCard = affectedCards.get(i);
// Gain control
if (params.containsKey("GainControl")) {
affectedCard.removeTempController(se.getTimestamp());
}
// remove set P/T
if (!params.containsKey("CharacteristicDefining") && setPT) {

View File

@@ -1254,7 +1254,7 @@ public class ChangeZoneAi extends SpellAbilityAi {
c.setTapped(true);
}
if (sa.hasParam("GainControl")) {
c.addController(sa.getSourceCard());
c.setController(sa.getActivatingPlayer(), Singletons.getModel().getGame().getNextTimestamp());
}
if (sa.hasParam("AttachedTo")) {

View File

@@ -29,7 +29,7 @@ public class AttachEffect extends SpellAbilityEffect {
// The Spell_Permanent (Auras) version of this AF needs to
// move the card into play before Attaching
sa.getSourceCard().addController(sa.getActivatingPlayer());
sa.getSourceCard().setController(sa.getActivatingPlayer(), 0);
final Card c = Singletons.getModel().getGame().getAction().moveTo(sa.getActivatingPlayer().getZone(ZoneType.Battlefield), sa.getSourceCard());
sa.setSourceCard(c);
}
@@ -92,8 +92,7 @@ public class AttachEffect extends SpellAbilityEffect {
// Spellweaver Volute, Dance of the Dead, Animate Dead
// Although honestly, I'm not sure if the three of those could
// handle being scripted
final boolean gainControl = "GainControl".equals(sa.getParam("AILogic"));
handleAura(card, c, gainControl);
handleAura(card, c);
} else if (card.isEquipment()) {
card.equipCard(c);
// else if (card.isFortification())
@@ -105,7 +104,7 @@ public class AttachEffect extends SpellAbilityEffect {
// Curse cards
final Player p = (Player) o;
if (card.isAura()) {
handleAura(card, p, false);
handleAura(card, p);
}
}
}
@@ -120,7 +119,7 @@ public class AttachEffect extends SpellAbilityEffect {
* @param gainControl
* the gain control
*/
public static void handleAura(final Card card, final GameEntity tgt, final boolean gainControl) {
public static void handleAura(final Card card, final GameEntity tgt) {
if (card.isEnchanting()) {
// If this Card is already Enchanting something
// Need to unenchant it, then clear out the commands
@@ -132,72 +131,6 @@ public class AttachEffect extends SpellAbilityEffect {
card.clearTriggers(); // not sure if cleartriggers is needed?
}
if (gainControl) {
// Handle GainControl Auras
final Player[] pl = new Player[1];
if (tgt instanceof Card) {
pl[0] = ((Card) tgt).getController();
} else {
pl[0] = (Player) tgt;
}
final Command onEnchant = new Command() {
private static final long serialVersionUID = -2519887209491512000L;
@Override
public void execute() {
final Card crd = card.getEnchantingCard();
if (crd == null) {
return;
}
pl[0] = crd.getController();
crd.addController(card);
} // execute()
}; // Command
final Command onUnEnchant = new Command() {
private static final long serialVersionUID = 3426441132121179288L;
@Override
public void execute() {
final Card crd = card.getEnchantingCard();
if (crd == null) {
return;
}
if (crd.isInPlay()) {
crd.removeController(card);
}
} // execute()
}; // Command
final Command onChangesControl = new Command() {
/** automatically generated serialVersionUID. */
private static final long serialVersionUID = -65903786170234039L;
@Override
public void execute() {
final Card crd = card.getEnchantingCard();
if (crd == null) {
return;
}
crd.removeController(card); // This looks odd, but will
// simply refresh controller
crd.addController(card);
} // execute()
}; // Command
// Add Enchant Commands for Control changers
card.addEnchantCommand(onEnchant);
card.addUnEnchantCommand(onUnEnchant);
card.addChangeControllerCommand(onChangesControl);
}
final Command onLeavesPlay = new Command() {
private static final long serialVersionUID = -639204333673364477L;
@@ -251,7 +184,6 @@ public class AttachEffect extends SpellAbilityEffect {
}
aura.setActivatingPlayer(source.getController());
final Target tgt = aura.getTarget();
final boolean gainControl = "GainControl".equals(aura.getParam("AILogic"));
if (source.getController().isHuman()) {
if (tgt.canTgtPlayer()) {
@@ -265,8 +197,7 @@ public class AttachEffect extends SpellAbilityEffect {
final Player p = GuiChoose.one(source + " - Select a player to attach to.", players);
if (p != null) {
handleAura(source, p, false);
//source.enchantEntity((Player) o);
handleAura(source, p);
return true;
}
} else {
@@ -278,7 +209,7 @@ public class AttachEffect extends SpellAbilityEffect {
final Object o = GuiChoose.one(source + " - Select a card to attach to.", list);
if (o instanceof Card) {
handleAura(source, (Card) o, gainControl);
handleAura(source, (Card) o);
//source.enchantEntity((Card) o);
return true;
}
@@ -289,11 +220,11 @@ public class AttachEffect extends SpellAbilityEffect {
final Object o = aura.getTarget().getTargets().get(0);
if (o instanceof Card) {
//source.enchantEntity((Card) o);
handleAura(source, (Card) o, gainControl);
handleAura(source, (Card) o);
return true;
} else if (o instanceof Player) {
//source.enchantEntity((Player) o);
handleAura(source, (Player) o, false);
handleAura(source, (Player) o);
return true;
}
}

View File

@@ -81,7 +81,7 @@ public class ChangeZoneAllEffect extends SpellAbilityEffect {
}
if (sa.hasParam("GainControl")) {
c.addController(sa.getSourceCard());
c.setController(sa.getActivatingPlayer(), Singletons.getModel().getGame().getNextTimestamp());
Singletons.getModel().getGame().getAction().moveToPlay(c, sa.getActivatingPlayer());
} else {
final Card movedCard = Singletons.getModel().getGame().getAction().moveTo(destination, c, libraryPos);

View File

@@ -385,7 +385,7 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
tgtC.setTapped(true);
}
if (sa.hasParam("GainControl")) {
tgtC.addController(sa.getSourceCard());
tgtC.setController(sa.getActivatingPlayer(), Singletons.getModel().getGame().getNextTimestamp());
}
if (sa.hasParam("AttachedTo")) {
List<Card> list = AbilityUtils.getDefinedCards(hostCard,
@@ -669,7 +669,7 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
c.setTapped(true);
}
if (sa.hasParam("GainControl")) {
c.addController(sa.getSourceCard());
c.setController(sa.getActivatingPlayer(), Singletons.getModel().getGame().getNextTimestamp());
}
if (sa.hasParam("AttachedTo")) {

View File

@@ -3,6 +3,7 @@ package forge.card.ability.effects;
import java.util.ArrayList;
import forge.Card;
import forge.Singletons;
import forge.card.ability.AbilityUtils;
import forge.card.ability.SpellAbilityEffect;
import forge.card.spellability.SpellAbility;
@@ -56,9 +57,10 @@ public class ControlExchangeEffect extends SpellAbilityEffect {
return;
}
Player player2 = object2.getController();
object2.addController(object1.getController());
object1.addController(player2);
final Player player2 = object2.getController();
final long tStamp = Singletons.getModel().getGame().getNextTimestamp();
object2.setController(object1.getController(), tStamp);
object1.setController(player2, tStamp);
}
}

View File

@@ -6,7 +6,6 @@ import java.util.List;
import forge.Card;
import forge.Command;
import forge.GameEntity;
import forge.Singletons;
import forge.card.ability.AbilityUtils;
import forge.card.ability.SpellAbilityEffect;
@@ -52,14 +51,12 @@ public class ControlGainEffect extends SpellAbilityEffect {
}
private void doLoseControl(final Card c, final Card host, final boolean tapOnLose,
final List<String> addedKeywords, final GameEntity newController) {
final List<String> addedKeywords, final long tStamp) {
if (null == c) {
return;
}
if (c.isInPlay()) {
c.removeController(newController);
// Singletons.getModel().getGameAction().changeController(new ArrayList<Card>(c),
// c.getController(), originalController);
c.removeTempController(tStamp);
if (tapOnLose) {
c.tap();
@@ -105,14 +102,10 @@ public class ControlGainEffect extends SpellAbilityEffect {
controllers = tgt.getTargetPlayers();
}
GameEntity newController;
Player newController;
if (controllers.size() == 0) {
if (sa.isSpell()) {
newController = sa.getActivatingPlayer();
} else {
newController = source;
}
newController = sa.getActivatingPlayer();
} else {
newController = controllers.get(0);
}
@@ -124,66 +117,62 @@ public class ControlGainEffect extends SpellAbilityEffect {
return;
}
final int size = tgtCards.size();
for (int j = 0; j < size; j++) {
final Card tgtC = tgtCards.get(j);
final Player originalController = tgtC.getController();
for (Card tgtC : tgtCards) {
if (!tgtC.equals(sa.getSourceCard()) && !sa.getSourceCard().getGainControlTargets().contains(tgtC)) {
sa.getSourceCard().addGainControlTarget(tgtC);
}
if (tgtC.isInPlay()) {
if (!tgtC.isInPlay()) {
return;
}
if (!tgtC.equals(newController)) {
tgtC.addController(newController);
}
// Singletons.getModel().getGameAction().changeController(new ArrayList<Card>(tgtC),
// tgtC.getController(), newController.get(0));
long tStamp = Singletons.getModel().getGame().getNextTimestamp();
if (lose != null) {
tgtC.addTempController(newController, tStamp);
} else {
tgtC.setController(newController, tStamp);
}
if (bUntap) {
tgtC.untap();
}
if (bUntap) {
tgtC.untap();
}
if (null != kws) {
for (final String kw : kws) {
tgtC.addExtrinsicKeyword(kw);
}
if (null != kws) {
for (final String kw : kws) {
tgtC.addExtrinsicKeyword(kw);
}
}
// end copied
final Card hostCard = sa.getSourceCard();
if (lose != null) {
if (lose.contains("LeavesPlay")) {
sa.getSourceCard().addLeavesPlayCommand(this.getLoseControlCommand(tgtC, originalController, newController, bTapOnLose, hostCard, kws));
sa.getSourceCard().addLeavesPlayCommand(this.getLoseControlCommand(tgtC, tStamp, bTapOnLose, source, kws));
}
if (lose.contains("Untap")) {
sa.getSourceCard().addUntapCommand(this.getLoseControlCommand(tgtC, originalController, newController, bTapOnLose, hostCard, kws));
sa.getSourceCard().addUntapCommand(this.getLoseControlCommand(tgtC, tStamp, bTapOnLose, source, kws));
}
if (lose.contains("LoseControl")) {
sa.getSourceCard().addChangeControllerCommand(this.getLoseControlCommand(tgtC, originalController, newController, bTapOnLose, hostCard, kws));
sa.getSourceCard().addChangeControllerCommand(this.getLoseControlCommand(tgtC, tStamp, bTapOnLose, source, kws));
}
if (lose.contains("EOT")) {
Singletons.getModel().getGame().getEndOfTurn().addAt(this.getLoseControlCommand(tgtC, originalController, newController, bTapOnLose, hostCard, kws));
Singletons.getModel().getGame().getEndOfTurn().addAt(this.getLoseControlCommand(tgtC, tStamp, bTapOnLose, source, kws));
}
}
if (destroyOn != null) {
if (destroyOn.contains("LeavesPlay")) {
sa.getSourceCard().addLeavesPlayCommand(this.getDestroyCommand(tgtC, hostCard, bNoRegen));
sa.getSourceCard().addLeavesPlayCommand(this.getDestroyCommand(tgtC, source, bNoRegen));
}
if (destroyOn.contains("Untap")) {
sa.getSourceCard().addUntapCommand(this.getDestroyCommand(tgtC, hostCard, bNoRegen));
sa.getSourceCard().addUntapCommand(this.getDestroyCommand(tgtC, source, bNoRegen));
}
if (destroyOn.contains("LoseControl")) {
sa.getSourceCard().addChangeControllerCommand(this.getDestroyCommand(tgtC, hostCard, bNoRegen));
sa.getSourceCard().addChangeControllerCommand(this.getDestroyCommand(tgtC, source, bNoRegen));
}
}
sa.getSourceCard().clearGainControlReleaseCommands();
sa.getSourceCard().addGainControlReleaseCommand(this.getLoseControlCommand(tgtC, originalController, newController, bTapOnLose, hostCard, kws));
sa.getSourceCard().addGainControlReleaseCommand(this.getLoseControlCommand(tgtC, tStamp, bTapOnLose, source, kws));
} // end foreach target
}
@@ -239,13 +228,13 @@ public class ControlGainEffect extends SpellAbilityEffect {
* a {@link forge.game.player.Player} object.
* @return a {@link forge.Command} object.
*/
private Command getLoseControlCommand(final Card c, final Player originalController, final GameEntity newController,
private Command getLoseControlCommand(final Card c, final long tStamp,
final boolean bTapOnLose, final Card hostCard, final List<String> kws) {
final Command loseControl = new Command() {
private static final long serialVersionUID = 878543373519872418L;
@Override
public void execute() { doLoseControl(c, hostCard, bTapOnLose, kws, newController); } // execute()
public void execute() { doLoseControl(c, hostCard, bTapOnLose, kws, tStamp); }
};
return loseControl;

View File

@@ -86,7 +86,7 @@ public class CopyPermanentEffect extends SpellAbilityEffect {
copy = CardFactory.getCard(CardDb.getCard(c), sa.getActivatingPlayer());
// when copying something stolen:
copy.addController(controller);
copy.setController(controller, 0);
copy.setToken(true);
copy.setCopiedToken(true);
@@ -97,7 +97,7 @@ public class CopyPermanentEffect extends SpellAbilityEffect {
copy.setImageFilename(c.getImageFilename());
copy.setOwner(controller);
copy.addController(controller);
copy.setController(controller, 0);
copy.setManaCost(c.getManaCost());
copy.setColor(c.getColor());

View File

@@ -148,11 +148,11 @@ public class CounterEffect extends SpellAbilityEffect {
if (tgtSA instanceof SpellPermanent) {
Card c = tgtSA.getSourceCard();
System.out.println(c + " is SpellPermanent");
c.addController(srcSA.getActivatingPlayer());
c.setController(srcSA.getActivatingPlayer(), 0);
Singletons.getModel().getGame().getAction().moveToPlay(c, srcSA.getActivatingPlayer());
} else {
Card c = Singletons.getModel().getGame().getAction().moveToPlay(tgtSA.getSourceCard(), srcSA.getActivatingPlayer());
c.addController(srcSA.getActivatingPlayer());
c.setController(srcSA.getActivatingPlayer(), 0);
}
} else if (destination.equals("BottomOfLibrary")) {
Singletons.getModel().getGame().getAction().moveToBottomOfLibrary(tgtSA.getSourceCard());

View File

@@ -131,7 +131,7 @@ public class DigUntilEffect extends SpellAbilityEffect {
while (itr.hasNext()) {
final Card c = itr.next();
if (sa.hasParam("GainControl") && foundDest.equals(ZoneType.Battlefield)) {
c.addController(sa.getSourceCard());
c.setController(sa.getActivatingPlayer(), Singletons.getModel().getGame().getNextTimestamp());
Singletons.getModel().getGame().getAction().moveTo(c.getController().getZone(foundDest), c);
} else if (sa.hasParam("NoMoveFound") && foundDest.equals(ZoneType.Library)) {
//Don't do anything

View File

@@ -17,7 +17,7 @@ public class PermanentCreatureEfect extends SpellAbilityEffect {
*/
@Override
public void resolve(SpellAbility sa) {
sa.getSourceCard().addController(sa.getActivatingPlayer());
sa.getSourceCard().setController(sa.getActivatingPlayer(), 0);
final Card c = Singletons.getModel().getGame().getAction().moveTo(sa.getActivatingPlayer().getZone(ZoneType.Battlefield), sa.getSourceCard());
sa.setSourceCard(c);
}

View File

@@ -17,7 +17,7 @@ public class PermanentNoncreatureEffect extends SpellAbilityEffect {
*/
@Override
public void resolve(SpellAbility sa) {
sa.getSourceCard().addController(sa.getActivatingPlayer());
sa.getSourceCard().setController(sa.getActivatingPlayer(), 0);
final Card c = Singletons.getModel().getGame().getAction().moveTo(sa.getActivatingPlayer().getZone(ZoneType.Battlefield), sa.getSourceCard());
sa.setSourceCard(c);
}

View File

@@ -575,7 +575,7 @@ public class SpellPermanent extends Spell {
@Override
public void resolve() {
Card c = this.getSourceCard();
c.addController(this.getActivatingPlayer());
c.setController(this.getActivatingPlayer(), 0);
Singletons.getModel().getGame().getAction().moveTo(ZoneType.Battlefield, c);
}
}

View File

@@ -137,6 +137,10 @@ public class StaticAbility {
return 0;
}
if (this.params.containsKey("GainControl")) {
return 2;
}
if (this.params.containsKey("AddType") || this.params.containsKey("RemoveType")
|| this.params.containsKey("RemoveCardType") || this.params.containsKey("RemoveSubType")
|| this.params.containsKey("RemoveSuperType")) {

View File

@@ -318,6 +318,11 @@ public class StaticAbilityContinuous {
// start modifying the cards
for (int i = 0; i < affectedCards.size(); i++) {
final Card affectedCard = affectedCards.get(i);
// Gain control
if (params.containsKey("GainControl")) {
affectedCard.addTempController(hostCard.getController(), hostCard.getTimestamp());
}
// set P/T
if (params.containsKey("CharacteristicDefining")) {

View File

@@ -975,9 +975,18 @@ public class GameAction {
final HashMap<String, Object> runParams = new HashMap<String, Object>();
game.getTriggerHandler().runTrigger(TriggerType.Always, runParams, false);
for (Player p : game.getPlayers()) {
for (Card c : p.getCardsIn(ZoneType.Battlefield)) {
if (!c.getController().equals(p)) {
controllerChangeZoneCorrection(c);
c.runChangeControllerCommands();
checkAgain = true;
}
}
}
for (Card c : game.getCardsIn(ZoneType.Battlefield)) {
if (c.isEquipped()) {
final List<Card> equipments = new ArrayList<Card>(c.getEquippedBy());
for (final Card equipment : equipments) {
@@ -988,7 +997,6 @@ public class GameAction {
}
} // if isEquipped()
if (c.isEquipping()) {
final Card equippedCreature = c.getEquipping().get(0);
if (!equippedCreature.isCreature() || !equippedCreature.isInPlay()) {
@@ -1089,7 +1097,7 @@ public class GameAction {
checkAgain = true;
}
} // while it.hasNext()
}
if (game.getTriggerHandler().runWaitingTriggers(true)) {
checkAgain = true;

View File

@@ -550,8 +550,7 @@ public class PhaseHandler extends MyObservable implements java.io.Serializable {
if (game.getType() == GameType.Planechase) {
Card p = game.getActivePlane();
if (p != null) {
p.clearControllers();
p.addController(next);
p.setController(next, 0);
game.getAction().controllerChangeZoneCorrection(p);
}
}

View File

@@ -1850,7 +1850,7 @@ public abstract class Player extends GameEntity implements Comparable<Player> {
*/
public final void playLand(final Card land) {
if (this.canPlayLand(land)) {
land.addController(this);
land.setController(this, 0);
game.getAction().moveTo(this.getZone(ZoneType.Battlefield), land);
CardFactoryUtil.playLandEffects(land);
this.numLandsPlayed++;