diff --git a/forge-ai/src/main/java/forge/ai/CreatureEvaluator.java b/forge-ai/src/main/java/forge/ai/CreatureEvaluator.java index a6b04973743..437ca66ac1e 100644 --- a/forge-ai/src/main/java/forge/ai/CreatureEvaluator.java +++ b/forge-ai/src/main/java/forge/ai/CreatureEvaluator.java @@ -165,7 +165,7 @@ public class CreatureEvaluator implements Function { value -= subValue(30, "cupkeep"); } else if (c.hasStartOfKeyword("At the beginning of your upkeep, sacrifice CARDNAME unless you pay")) { value -= subValue(20, "sac-unless"); - } else if (c.hasStartOfKeyword("Echo") && c.cameUnderControlSinceLastUpkeep()) { + } else if (c.hasStartOfKeyword("(Echo unpaid)")) { value -= subValue(10, "echo-unpaid"); } diff --git a/forge-game/src/main/java/forge/game/GameAction.java b/forge-game/src/main/java/forge/game/GameAction.java index e53c34b310a..0f64b4a5bce 100644 --- a/forge-game/src/main/java/forge/game/GameAction.java +++ b/forge-game/src/main/java/forge/game/GameAction.java @@ -471,6 +471,9 @@ public class GameAction { oldBattlefield.remove(c); newBattlefield.add(c); c.setSickness(true); + if (c.hasStartOfKeyword("Echo")) { + c.addExtrinsicKeyword("(Echo unpaid)"); + } if (game.getPhaseHandler().inCombat()) { game.getCombat().removeFromCombat(c); } diff --git a/forge-game/src/main/java/forge/game/card/Card.java b/forge-game/src/main/java/forge/game/card/Card.java index f23948c2da2..cdece9fc7e2 100644 --- a/forge-game/src/main/java/forge/game/card/Card.java +++ b/forge-game/src/main/java/forge/game/card/Card.java @@ -138,7 +138,6 @@ public class Card extends GameEntity implements Comparable { private boolean drawnThisTurn = false; private boolean becameTargetThisTurn = false; private boolean startedTheTurnUntapped = false; - private boolean underControlSinceLastUpkeep = true; // for Echo private boolean tapped = false; private boolean sickness = true; // summoning sickness private boolean token = false; @@ -207,6 +206,7 @@ public class Card extends GameEntity implements Comparable { private NavigableMap tempControllers = new TreeMap<>(); private String originalText = "", text = ""; + private String echoCost = ""; private Cost miracleCost = null; private String chosenType = ""; private List chosenColors; @@ -1101,6 +1101,14 @@ public class Card extends GameEntity implements Comparable { turnInZone = turn; } + public final void setEchoCost(final String s) { + echoCost = s; + } + + public final String getEchoCost() { + return echoCost; + } + public final void setManaCost(final ManaCost s) { currentState.setManaCost(s); } @@ -2150,14 +2158,6 @@ public class Card extends GameEntity implements Comparable { public void setStartedTheTurnUntapped(boolean untapped) { startedTheTurnUntapped = untapped; } - - public boolean cameUnderControlSinceLastUpkeep() { - return underControlSinceLastUpkeep; - } - - public void setUnderControlSinceLastUpkeep(boolean underControlSinceLastUpkeep) { - this.underControlSinceLastUpkeep = underControlSinceLastUpkeep; - } public final Player getOwner() { return owner; @@ -4616,10 +4616,6 @@ public class Card extends GameEntity implements Comparable { if (!hasStartedTheTurnUntapped()) { return false; } - } else if (property.startsWith("cameUnderControlSinceLastUpkeep")) { - if (!cameUnderControlSinceLastUpkeep()) { - return false; - } } else if (property.equals("attackedOrBlockedSinceYourLastUpkeep")) { if (!getDamageHistory().hasAttackedSinceLastUpkeepOf(sourceController) && !getDamageHistory().hasBlockedSinceLastUpkeepOf(sourceController)) { diff --git a/forge-game/src/main/java/forge/game/card/CardFactoryUtil.java b/forge-game/src/main/java/forge/game/card/CardFactoryUtil.java index e9335aa8322..f6d2e7cd08c 100644 --- a/forge-game/src/main/java/forge/game/card/CardFactoryUtil.java +++ b/forge-game/src/main/java/forge/game/card/CardFactoryUtil.java @@ -2306,18 +2306,19 @@ public class CardFactoryUtil { else if (keyword.startsWith("Echo")) { final String[] k = keyword.split(":"); final String manacost = k[1]; - - String upkeepTrig = "Mode$ Phase | Phase$ Upkeep | ValidPlayer$ You | TriggerZones$ Battlefield " + - " | Execute$ TrigUpkeepEcho | IsPresent$ Card.Self+cameUnderControlSinceLastUpkeep | Secondary$ True | " + - "TriggerDescription$ Echo: At the beginning of your upkeep, if CARDNAME came under your control since the " + - "beginning of your last upkeep, sacrifice it unless you pay the Echo cost"; - String ref = "X".equals(manacost) ? " | References$ X" : ""; - card.setSVar("TrigUpkeepEcho", "AB$ Sacrifice | Cost$ 0 | SacValid$ Self | " - + "Echo$ " + manacost + ref); + card.setEchoCost(manacost); - final Trigger parsedUpkeepTrig = TriggerHandler.parseTrigger(upkeepTrig, card, true); - card.addTrigger(parsedUpkeepTrig); + final GameCommand intoPlay = new GameCommand() { + + private static final long serialVersionUID = -7913835645603984242L; + + @Override + public void run() { + card.addExtrinsicKeyword("(Echo unpaid)"); + } + }; + card.addComesIntoPlayCommand(intoPlay); } else if (keyword.startsWith("Suspend")) { card.removeIntrinsicKeyword(keyword); diff --git a/forge-game/src/main/java/forge/game/phase/PhaseHandler.java b/forge-game/src/main/java/forge/game/phase/PhaseHandler.java index a2d142229c3..1c9fc903851 100644 --- a/forge-game/src/main/java/forge/game/phase/PhaseHandler.java +++ b/forge-game/src/main/java/forge/game/phase/PhaseHandler.java @@ -419,7 +419,6 @@ public class PhaseHandler implements java.io.Serializable { c.getDamageHistory().setNotAttackedSinceLastUpkeepOf(playerTurn); c.getDamageHistory().setNotBlockedSinceLastUpkeepOf(playerTurn); c.getDamageHistory().setNotBeenBlockedSinceLastUpkeepOf(playerTurn); - c.setUnderControlSinceLastUpkeep(true); } game.getUpkeep().executeUntilEndOfPhase(playerTurn); game.getUpkeep().registerUntilEndCommand(playerTurn); diff --git a/forge-game/src/main/java/forge/game/phase/Upkeep.java b/forge-game/src/main/java/forge/game/phase/Upkeep.java index 96e571e6977..306e03c0ea5 100644 --- a/forge-game/src/main/java/forge/game/phase/Upkeep.java +++ b/forge-game/src/main/java/forge/game/phase/Upkeep.java @@ -17,12 +17,15 @@ */ package forge.game.phase; +import com.google.common.base.Predicate; + import forge.card.mana.ManaCost; import forge.game.Game; import forge.game.ability.AbilityFactory; import forge.game.card.Card; import forge.game.card.CardCollectionView; import forge.game.card.CardFactoryUtil; +import forge.game.card.CardLists; import forge.game.card.CounterType; import forge.game.cost.Cost; import forge.game.player.Player; @@ -69,10 +72,42 @@ public class Upkeep extends Phase { game.getStack().freezeStack(); Upkeep.upkeepUpkeepCost(game); // sacrifice unless upkeep cost is paid + Upkeep.upkeepEcho(game); game.getStack().unfreezeStack(); } + private static void upkeepEcho(final Game game) { + CardCollectionView list = game.getPhaseHandler().getPlayerTurn().getCardsIn(ZoneType.Battlefield); + list = CardLists.filter(list, new Predicate() { + @Override + public boolean apply(final Card c) { + return c.hasStartOfKeyword("(Echo unpaid)"); + } + }); + + for (int i = 0; i < list.size(); i++) { + final Card c = list.get(i); + if (c.hasStartOfKeyword("(Echo unpaid)")) { + final StringBuilder sb = new StringBuilder(); + sb.append("Echo for ").append(c).append("\n"); + String ref = "X".equals(c.getEchoCost()) ? " | References$ X" : ""; + String effect = "AB$ Sacrifice | Cost$ 0 | SacValid$ Self | " + + "Echo$ " + c.getEchoCost() + ref; + + SpellAbility sacAbility = AbilityFactory.getAbility(effect, c); + sacAbility.setTrigger(true); + sacAbility.setActivatingPlayer(c.getController()); + sacAbility.setStackDescription(sb.toString()); + sacAbility.setDescription(sb.toString()); + + game.getStack().addSimultaneousStackEntry(sacAbility); + + c.removeAllExtrinsicKeyword("(Echo unpaid)"); + } + } + } + private static void upkeepUpkeepCost(final Game game) { final CardCollectionView list = game.getPhaseHandler().getPlayerTurn().getCardsIn(ZoneType.Battlefield); diff --git a/forge-gui-desktop/src/test/java/forge/ai/simulation/GameSimulatorTest.java b/forge-gui-desktop/src/test/java/forge/ai/simulation/GameSimulatorTest.java index 64b08c73306..93198b3b547 100644 --- a/forge-gui-desktop/src/test/java/forge/ai/simulation/GameSimulatorTest.java +++ b/forge-gui-desktop/src/test/java/forge/ai/simulation/GameSimulatorTest.java @@ -242,6 +242,30 @@ public class GameSimulatorTest extends TestCase { assertEquals(16, simGame.getPlayers().get(0).getLife()); } + public void testEchoCostState() { + Game game = initAndCreateGame(); + Player p = game.getPlayers().get(1); + + String c1Name = "Acridian"; + String c2Name = "Goblin Patrol"; + Card c1 = addCard(c1Name, p); + Card c2 = addCard(c2Name, p); + + game.getPhaseHandler().devModeSet(PhaseType.MAIN2, p); + game.getAction().checkStateEffects(true); + + assertTrue(c1.hasStartOfKeyword("(Echo unpaid)")); + assertTrue(c2.hasStartOfKeyword("(Echo unpaid)")); + c2.removeAllExtrinsicKeyword("(Echo unpaid)"); + + GameSimulator sim = createSimulator(game, p); + Game simGame = sim.getSimulatedGameState(); + Card c1Copy = findCardWithName(simGame, c1Name); + assertTrue(c1Copy.hasStartOfKeyword("(Echo unpaid)")); + Card c2Copy = findCardWithName(simGame, c2Name); + assertFalse(c2Copy.hasStartOfKeyword("(Echo unpaid)")); + } + public void testSimulateUnmorph() { Game game = initAndCreateGame(); Player p = game.getPlayers().get(1);