diff --git a/Mage.Sets/src/mage/cards/g/GeodeGolem.java b/Mage.Sets/src/mage/cards/g/GeodeGolem.java index 82b5c9e8ba4..bdc737f1a8b 100644 --- a/Mage.Sets/src/mage/cards/g/GeodeGolem.java +++ b/Mage.Sets/src/mage/cards/g/GeodeGolem.java @@ -4,7 +4,6 @@ import mage.ApprovingObject; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility; -import mage.abilities.costs.mana.ManaCost; import mage.abilities.effects.OneShotEffect; import mage.abilities.keyword.TrampleAbility; import mage.cards.Card; @@ -16,8 +15,6 @@ import mage.filter.FilterCard; import mage.game.Game; import mage.players.Player; import mage.target.TargetCard; -import mage.util.ManaUtil; -import mage.watchers.common.CommanderPlaysCountWatcher; import java.util.Set; import java.util.UUID; @@ -37,8 +34,7 @@ public final class GeodeGolem extends CardImpl { // Trample this.addAbility(TrampleAbility.getInstance()); - // Whenever Geode Golem deals combat damage to a player, you may - // cast your commander from the command zone without paying its mana cost. + // Whenever Geode Golem deals combat damage to a player, you may cast your commander from the command zone without paying its mana cost. this.addAbility(new DealsCombatDamageToAPlayerTriggeredAbility(new GeodeGolemEffect(), true)); } @@ -83,12 +79,11 @@ class GeodeGolemEffect extends OneShotEffect { target.setNotTarget(true); if (controller.canRespond() && controller.choose(Outcome.PlayForFree, new CardsImpl(commandersInCommandZone), target, game)) { - if (target.getFirstTarget() != null) { - selectedCommander = commandersInCommandZone.stream() - .filter(c -> c.getId().equals(target.getFirstTarget())) - .findFirst() - .orElse(null); - } + selectedCommander = commandersInCommandZone.stream() + .filter(c -> c.getId().equals(target.getFirstTarget())) + .findFirst() + .orElse(null); + } } @@ -96,27 +91,17 @@ class GeodeGolemEffect extends OneShotEffect { return false; } - // PAY - // TODO: this is broken with the commander cost reduction effect - ManaCost cost = null; - CommanderPlaysCountWatcher watcher = game.getState().getWatcher(CommanderPlaysCountWatcher.class); - int castCount = watcher.getPlaysCount(selectedCommander.getId()); - if (castCount > 0) { - cost = ManaUtil.createManaCost(castCount * 2, false); - } - - // CAST: as spell or as land - if (cost == null - || cost.pay(source, game, source, controller.getId(), false, null)) { - if (selectedCommander.getSpellAbility() != null) { // TODO: can be broken with mdf cards (one side creature, one side land)? - game.getState().setValue("PlayFromNotOwnHandZone" + selectedCommander.getId(), Boolean.TRUE); - Boolean commanderWasCast = controller.cast(controller.chooseAbilityForCast(selectedCommander, game, true), - game, true, new ApprovingObject(source, game)); - game.getState().setValue("PlayFromNotOwnHandZone" + selectedCommander.getId(), null); - return commanderWasCast; - } else { - return controller.playLand(selectedCommander, game, true); - } + // commander tax applies as additional cost + if (selectedCommander.getSpellAbility() != null) { + game.getState().setValue("PlayFromNotOwnHandZone" + selectedCommander.getId(), Boolean.TRUE); + Boolean commanderWasCast = controller.cast(controller.chooseAbilityForCast(selectedCommander, game, true), + game, true, new ApprovingObject(source, game)); + game.getState().setValue("PlayFromNotOwnHandZone" + selectedCommander.getId(), null); + return commanderWasCast; + } else { + // play commander as land is xmage feature, but mtg rules for text "cast commander" doesn't allow that + // TODO: improve lands support for "cast your commander" (allow land play from mdf cards)? + return controller.playLand(selectedCommander, game, true); } } return false; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/c18/GeodeGolemTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/c18/GeodeGolemTest.java new file mode 100644 index 00000000000..3c4cce83e0f --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/c18/GeodeGolemTest.java @@ -0,0 +1,205 @@ +package org.mage.test.cards.single.c18; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestCommanderDuelBase; + +/** + * @author JayDi85 + */ +public class GeodeGolemTest extends CardTestCommanderDuelBase { + + @Test + public void test_Normal() { + // Whenever Geode Golem deals combat damage to a player, you may + // cast your commander from the command zone without paying its mana cost. + addCard(Zone.BATTLEFIELD, playerA, "Geode Golem"); + // + addCard(Zone.COMMAND, playerA, "Grizzly Bears"); // {1}{G} + addCard(Zone.BATTLEFIELD, playerA, "Forest", 10); + // + addCustomEffect_TargetDamage(playerA, 2); + + checkCommandCardCount("before 1", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Grizzly Bears", 1); + + // turn 1 - first cast + + // attack and cast first time (free) + attack(1, playerA, "Geode Golem"); + setChoice(playerA, "Yes"); // cast commander + setChoice(playerA, "Grizzly Bears"); // commander choice + waitStackResolved(1, PhaseStep.COMBAT_DAMAGE); + checkPermanentCount("after 1", 1, PhaseStep.COMBAT_DAMAGE, playerA, "Grizzly Bears", 1); + checkPermanentTapped("after 1", 1, PhaseStep.COMBAT_DAMAGE, playerA, "Forest", true, 0); + // + // remove to command zone (0x tax) + activateAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "target damage 2", "Grizzly Bears"); + setChoice(playerA, "Yes"); // move to command zone + + // turn 3 - second cast (1x tax) + + attack(3, playerA, "Geode Golem"); + setChoice(playerA, "Yes"); // cast commander + setChoice(playerA, "Grizzly Bears"); // commander choice + waitStackResolved(3, PhaseStep.COMBAT_DAMAGE); + checkPermanentCount("after 2", 3, PhaseStep.COMBAT_DAMAGE, playerA, "Grizzly Bears", 1); + checkPermanentTapped("after 2", 3, PhaseStep.COMBAT_DAMAGE, playerA, "Forest", true, 2); // 1x tax + // + // remove to command zone + activateAbility(3, PhaseStep.POSTCOMBAT_MAIN, playerA, "target damage 2", "Grizzly Bears"); + setChoice(playerA, "Yes"); // move to command zone + + // turn 5 - third cast (2x tax) + + attack(5, playerA, "Geode Golem"); + setChoice(playerA, "Yes"); // cast commander + setChoice(playerA, "Grizzly Bears"); // commander choice + waitStackResolved(5, PhaseStep.COMBAT_DAMAGE); + checkPermanentCount("after 3", 5, PhaseStep.COMBAT_DAMAGE, playerA, "Grizzly Bears", 1); + checkPermanentTapped("after 3", 5, PhaseStep.COMBAT_DAMAGE, playerA, "Forest", true, 2 * 2); // 2x tax + + setStrictChooseMode(true); + setStopAt(5, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertPermanentCount(playerA, "Grizzly Bears", 1); + } + + @Test + public void test_MDF_SingleSide() { + // Whenever Geode Golem deals combat damage to a player, you may + // cast your commander from the command zone without paying its mana cost. + addCard(Zone.BATTLEFIELD, playerA, "Geode Golem"); + // + // Akoum Warrior {5}{R} - creature 4/5 + // Akoum Teeth - land + addCard(Zone.COMMAND, playerA, "Akoum Warrior"); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 10); + // + addCustomEffect_TargetDamage(playerA, 5); + + checkCommandCardCount("before 1", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Akoum Warrior", 1); + + // turn 1 - first cast + + // attack and cast first time (free) + attack(1, playerA, "Geode Golem"); + setChoice(playerA, "Yes"); // cast commander + setChoice(playerA, "Akoum Warrior"); // commander choice + waitStackResolved(1, PhaseStep.COMBAT_DAMAGE); + checkPermanentCount("after 1", 1, PhaseStep.COMBAT_DAMAGE, playerA, "Akoum Warrior", 1); + checkPermanentTapped("after 1", 1, PhaseStep.COMBAT_DAMAGE, playerA, "Mountain", true, 0); + // + // remove to command zone (0x tax) + activateAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "target damage 5", "Akoum Warrior"); + setChoice(playerA, "Yes"); // move to command zone + + // turn 3 - second cast (1x tax) + + attack(3, playerA, "Geode Golem"); + setChoice(playerA, "Yes"); // cast commander + setChoice(playerA, "Akoum Warrior"); // commander choice + waitStackResolved(3, PhaseStep.COMBAT_DAMAGE); + checkPermanentCount("after 2", 3, PhaseStep.COMBAT_DAMAGE, playerA, "Akoum Warrior", 1); + checkPermanentTapped("after 2", 3, PhaseStep.COMBAT_DAMAGE, playerA, "Mountain", true, 2); // 1x tax + // + // remove to command zone + activateAbility(3, PhaseStep.POSTCOMBAT_MAIN, playerA, "target damage 5", "Akoum Warrior"); + setChoice(playerA, "Yes"); // move to command zone + + // turn 5 - third cast (2x tax) + + attack(5, playerA, "Geode Golem"); + setChoice(playerA, "Yes"); // cast commander + setChoice(playerA, "Akoum Warrior"); // commander choice + waitStackResolved(5, PhaseStep.COMBAT_DAMAGE); + checkPermanentCount("after 3", 5, PhaseStep.COMBAT_DAMAGE, playerA, "Akoum Warrior", 1); + checkPermanentTapped("after 3", 5, PhaseStep.COMBAT_DAMAGE, playerA, "Mountain", true, 2 * 2); // 2x tax + + setStrictChooseMode(true); + setStopAt(5, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertPermanentCount(playerA, "Akoum Warrior", 1); + } + + @Test + public void test_MDF_BothSides() { + // Whenever Geode Golem deals combat damage to a player, you may + // cast your commander from the command zone without paying its mana cost. + addCard(Zone.BATTLEFIELD, playerA, "Geode Golem"); + // + // Birgi, God of Storytelling {2}{R} - creature 3/3 + // Harnfel, Horn of Bounty {4}{R} - artifact + addCard(Zone.COMMAND, playerA, "Birgi, God of Storytelling"); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 10); + // + addCustomEffect_TargetDamage(playerA, 3); + addCustomEffect_DestroyTarget(playerA); + + checkCommandCardCount("before 1", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Birgi, God of Storytelling", 1); + + // turn 1 - first cast, LEFT side + + // attack and cast first time (free) + attack(1, playerA, "Geode Golem"); + setChoice(playerA, "Yes"); // cast commander + setChoice(playerA, "Birgi, God of Storytelling"); // commander choice + setChoice(playerA, "Cast Birgi, God of Storytelling"); // spell choice + waitStackResolved(1, PhaseStep.COMBAT_DAMAGE); + checkPermanentCount("after 1", 1, PhaseStep.COMBAT_DAMAGE, playerA, "Birgi, God of Storytelling", 1); + checkPermanentTapped("after 1", 1, PhaseStep.COMBAT_DAMAGE, playerA, "Mountain", true, 0); + // + // remove to command zone (0x tax) + activateAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "target damage 3", "Birgi, God of Storytelling"); + setChoice(playerA, "Yes"); // move to command zone + + // turn 3 - second cast, LEFT side (1x tax) + + attack(3, playerA, "Geode Golem"); + setChoice(playerA, "Yes"); // cast commander + setChoice(playerA, "Birgi, God of Storytelling"); // commander choice + setChoice(playerA, "Cast Birgi, God of Storytelling"); // spell choice + waitStackResolved(3, PhaseStep.COMBAT_DAMAGE); + checkPermanentCount("after 2", 3, PhaseStep.COMBAT_DAMAGE, playerA, "Birgi, God of Storytelling", 1); + checkPermanentTapped("after 2", 3, PhaseStep.COMBAT_DAMAGE, playerA, "Mountain", true, 2); // 1x tax + // + // remove to command zone + activateAbility(3, PhaseStep.POSTCOMBAT_MAIN, playerA, "target damage 3", "Birgi, God of Storytelling"); + setChoice(playerA, "Yes"); // move to command zone + + // turn 5 - third cast, RIGHT side (2x tax) + + attack(5, playerA, "Geode Golem"); + setChoice(playerA, "Yes"); // cast commander + setChoice(playerA, "Birgi, God of Storytelling"); // commander choice + setChoice(playerA, "Cast Harnfel, Horn of Bounty"); // spell choice + waitStackResolved(5, PhaseStep.COMBAT_DAMAGE); + checkPermanentCount("after 3", 5, PhaseStep.COMBAT_DAMAGE, playerA, "Harnfel, Horn of Bounty", 1); + checkPermanentTapped("after 3", 5, PhaseStep.COMBAT_DAMAGE, playerA, "Mountain", true, 2 * 2); // 2x tax + // + // remove to command zone + activateAbility(5, PhaseStep.POSTCOMBAT_MAIN, playerA, "target destroy", "Harnfel, Horn of Bounty"); + setChoice(playerA, "Yes"); // move to command zone + + // turn 7 - fourth cast, RIGHT side (3x tax) + + attack(7, playerA, "Geode Golem"); + setChoice(playerA, "Yes"); // cast commander + setChoice(playerA, "Birgi, God of Storytelling"); // commander choice + setChoice(playerA, "Cast Harnfel, Horn of Bounty"); // spell choice + waitStackResolved(7, PhaseStep.COMBAT_DAMAGE); + checkPermanentCount("after 4", 7, PhaseStep.COMBAT_DAMAGE, playerA, "Harnfel, Horn of Bounty", 1); + checkPermanentTapped("after 4", 7, PhaseStep.COMBAT_DAMAGE, playerA, "Mountain", true, 2 * 3); // 3x tax + + setStrictChooseMode(true); + setStopAt(7, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertPermanentCount(playerA, "Harnfel, Horn of Bounty", 1); + } +} diff --git a/Mage/src/main/java/mage/abilities/dynamicvalue/common/CommanderPlaysCount.java b/Mage/src/main/java/mage/abilities/dynamicvalue/common/CommanderPlaysCount.java deleted file mode 100644 index 65051b3f2bd..00000000000 --- a/Mage/src/main/java/mage/abilities/dynamicvalue/common/CommanderPlaysCount.java +++ /dev/null @@ -1,52 +0,0 @@ -package mage.abilities.dynamicvalue.common; - -import mage.abilities.Ability; -import mage.abilities.dynamicvalue.DynamicValue; -import mage.abilities.effects.Effect; -import mage.game.Game; -import mage.watchers.common.CommanderPlaysCountWatcher; - -/** - * @author JayDi85 - */ -public class CommanderPlaysCount implements DynamicValue { - - private Integer multiplier; - - public CommanderPlaysCount() { - this(1); - } - - public CommanderPlaysCount(Integer multiplier) { - this.multiplier = multiplier; - } - - public CommanderPlaysCount(final CommanderPlaysCount dynamicValue) { - this.multiplier = dynamicValue.multiplier; - } - - @Override - public int calculate(Game game, Ability sourceAbility, Effect effect) { - CommanderPlaysCountWatcher watcher = game.getState().getWatcher(CommanderPlaysCountWatcher.class); - int value = 0; - if (watcher != null) { - value = watcher.getPlaysCount(sourceAbility.getSourceId()); - } - return value * multiplier; - } - - @Override - public CommanderPlaysCount copy() { - return new CommanderPlaysCount(this); - } - - @Override - public String toString() { - return "X"; - } - - @Override - public String getMessage() { - return ""; - } -} diff --git a/Mage/src/main/java/mage/cards/Card.java b/Mage/src/main/java/mage/cards/Card.java index a54fc780ace..f6cca7b1495 100644 --- a/Mage/src/main/java/mage/cards/Card.java +++ b/Mage/src/main/java/mage/cards/Card.java @@ -181,6 +181,14 @@ public interface Card extends MageObject { return getOwnerId().equals(controllerId); } + /** + * Commander tax calculation. Can be change from {2} to life life cost (see Liesa, Shroud of Dusk) + * + * @param game + * @param source + * @param abilityToModify + * @return + */ default boolean commanderCost(Game game, Ability source, Ability abilityToModify) { CommanderPlaysCountWatcher watcher = game.getState().getWatcher(CommanderPlaysCountWatcher.class); int castCount = watcher.getPlaysCount(getMainCard().getId());