diff --git a/Mage.Tests/src/test/java/org/mage/test/commander/duel/MythUnboundTest.java b/Mage.Tests/src/test/java/org/mage/test/commander/duel/MythUnboundTest.java index 0388fbde92c..b08652f2bb8 100644 --- a/Mage.Tests/src/test/java/org/mage/test/commander/duel/MythUnboundTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/commander/duel/MythUnboundTest.java @@ -1,6 +1,5 @@ package org.mage.test.commander.duel; -import java.io.FileNotFoundException; import mage.constants.PhaseStep; import mage.constants.Zone; import mage.game.Game; @@ -8,8 +7,9 @@ import mage.game.GameException; import org.junit.Test; import org.mage.test.serverside.base.CardTestCommanderDuelBase; +import java.io.FileNotFoundException; + /** - * * @author LevelX2 */ public class MythUnboundTest extends CardTestCommanderDuelBase { @@ -31,21 +31,28 @@ public class MythUnboundTest extends CardTestCommanderDuelBase { // Whenever your commander is put into the command zone from anywhere, draw a card. addCard(Zone.BATTLEFIELD, playerA, "Myth Unbound", 1); // Enchantment {2}{G} - addCard(Zone.BATTLEFIELD, playerB, "Mountain", 2); + addCard(Zone.BATTLEFIELD, playerB, "Mountain", 1); addCard(Zone.HAND, playerB, "Lightning Bolt", 1); + // cast 1 (G = 1 mana) castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Oviya Pashiri, Sage Lifecrafter"); + // destroy castSpell(1, PhaseStep.BEGIN_COMBAT, playerB, "Lightning Bolt", "Oviya Pashiri, Sage Lifecrafter"); + setChoice(playerA, "Yes"); + // cast 2 (G + 2 - 1 = 2 mana) castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Oviya Pashiri, Sage Lifecrafter"); + setStopAt(1, PhaseStep.END_TURN); + setStrictChooseMode(true); execute(); + assertAllCommandsUsed(); assertPermanentCount(playerA, "Myth Unbound", 1); assertGraveyardCount(playerB, "Lightning Bolt", 1); assertPermanentCount(playerA, "Oviya Pashiri, Sage Lifecrafter", 1); assertHandCount(playerA, 1); - assertTappedCount("Forest", false, 1); + assertTappedCount("Forest", false, 4 - 3); // 1 for first, 2 for second cast } } diff --git a/Mage/src/main/java/mage/abilities/common/CastCommanderAbility.java b/Mage/src/main/java/mage/abilities/common/CastCommanderAbility.java index 40aafbd62bd..397c4dfd158 100644 --- a/Mage/src/main/java/mage/abilities/common/CastCommanderAbility.java +++ b/Mage/src/main/java/mage/abilities/common/CastCommanderAbility.java @@ -1,7 +1,6 @@ package mage.abilities.common; import mage.abilities.SpellAbility; -import mage.abilities.costs.common.CommanderAdditionalCost; import mage.cards.Card; import mage.constants.SpellAbilityType; import mage.constants.Zone; @@ -18,9 +17,6 @@ public class CastCommanderAbility extends SpellAbility { this.getEffects().addAll(card.getSpellAbility().getEffects().copy()); this.getTargets().addAll(card.getSpellAbility().getTargets().copy()); this.timing = card.getSpellAbility().getTiming(); - - // extra cost - this.addCost(new CommanderAdditionalCost()); } else { throw new IllegalStateException("Cast commander ability must be used with spell ability only: " + card.getName()); } diff --git a/Mage/src/main/java/mage/abilities/common/PlayLandAsCommanderAbility.java b/Mage/src/main/java/mage/abilities/common/PlayLandAsCommanderAbility.java index a3ca4505c33..72f32931d38 100644 --- a/Mage/src/main/java/mage/abilities/common/PlayLandAsCommanderAbility.java +++ b/Mage/src/main/java/mage/abilities/common/PlayLandAsCommanderAbility.java @@ -1,7 +1,6 @@ package mage.abilities.common; import mage.abilities.PlayLandAbility; -import mage.abilities.costs.common.CommanderAdditionalCost; import mage.constants.Zone; /** @@ -12,9 +11,6 @@ public class PlayLandAsCommanderAbility extends PlayLandAbility { public PlayLandAsCommanderAbility(PlayLandAbility originalAbility) { super(originalAbility); zone = Zone.COMMAND; - - // extra cost - this.addCost(new CommanderAdditionalCost()); } private PlayLandAsCommanderAbility(PlayLandAsCommanderAbility ability) { diff --git a/Mage/src/main/java/mage/abilities/costs/common/CommanderAdditionalCost.java b/Mage/src/main/java/mage/abilities/costs/common/CommanderAdditionalCost.java deleted file mode 100644 index 08a4160e484..00000000000 --- a/Mage/src/main/java/mage/abilities/costs/common/CommanderAdditionalCost.java +++ /dev/null @@ -1,34 +0,0 @@ -package mage.abilities.costs.common; - -import mage.abilities.Ability; -import mage.abilities.dynamicvalue.common.CommanderPlaysCount; -import mage.game.Game; - -/** - * @author JayDi85 - */ -public class CommanderAdditionalCost extends DynamicValueGenericManaCost { - - /* - 903.8. A player may cast a commander they own from the command zone. A commander cast from the - command zone costs an additional {2} for each previous time the player casting it has cast it from - the command zone that game. This additional cost is informally known as the “commander tax.” - */ - - public CommanderAdditionalCost() { - super(new CommanderPlaysCount(2), "{2} for each previous time the player casting it has cast it from the command zone"); - } - - public CommanderAdditionalCost(final CommanderAdditionalCost cost) { - super(cost); - } - - @Override - public CommanderAdditionalCost copy() { - return new CommanderAdditionalCost(this); - } - - public boolean isEmptyPay(Ability ability, Game game) { - return amount.calculate(game, ability, null) == 0; - } -} \ No newline at end of file diff --git a/Mage/src/main/java/mage/abilities/effects/common/cost/CommanderCostModification.java b/Mage/src/main/java/mage/abilities/effects/common/cost/CommanderCostModification.java new file mode 100644 index 00000000000..26658c7345e --- /dev/null +++ b/Mage/src/main/java/mage/abilities/effects/common/cost/CommanderCostModification.java @@ -0,0 +1,57 @@ +package mage.abilities.effects.common.cost; + +import mage.abilities.Ability; +import mage.abilities.common.CastCommanderAbility; +import mage.abilities.common.PlayLandAsCommanderAbility; +import mage.abilities.costs.mana.GenericManaCost; +import mage.constants.CostModificationType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.game.Game; +import mage.watchers.common.CommanderPlaysCountWatcher; + +import java.util.UUID; + +/** + * @author Plopman + */ + /* + 903.8. A player may cast a commander they own from the command zone. A commander cast from the + command zone costs an additional {2} for each previous time the player casting it has cast it from + the command zone that game. This additional cost is informally known as the “commander tax.” + */ +public class CommanderCostModification extends CostModificationEffectImpl { + + private final UUID commanderId; + + public CommanderCostModification(UUID commanderId) { + super(Duration.Custom, Outcome.Neutral, CostModificationType.INCREASE_COST); + this.commanderId = commanderId; + } + + public CommanderCostModification(final CommanderCostModification effect) { + super(effect); + this.commanderId = effect.commanderId; + } + + @Override + public boolean apply(Game game, Ability source, Ability abilityToModify) { + CommanderPlaysCountWatcher watcher = game.getState().getWatcher(CommanderPlaysCountWatcher.class); + int castCount = watcher.getPlaysCount(commanderId); + if (castCount > 0) { + abilityToModify.getManaCostsToPay().add(new GenericManaCost(2 * castCount)); + } + return true; + } + + @Override + public boolean applies(Ability abilityToModify, Ability source, Game game) { + return commanderId.equals(abilityToModify.getSourceId()) + && (abilityToModify instanceof CastCommanderAbility || abilityToModify instanceof PlayLandAsCommanderAbility); + } + + @Override + public CommanderCostModification copy() { + return new CommanderCostModification(this); + } +} diff --git a/Mage/src/main/java/mage/game/GameCommanderImpl.java b/Mage/src/main/java/mage/game/GameCommanderImpl.java index 715295c6453..eb5094f735b 100644 --- a/Mage/src/main/java/mage/game/GameCommanderImpl.java +++ b/Mage/src/main/java/mage/game/GameCommanderImpl.java @@ -4,6 +4,7 @@ import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.common.InfoEffect; import mage.abilities.effects.common.continuous.CommanderReplacementEffect; +import mage.abilities.effects.common.cost.CommanderCostModification; import mage.cards.Card; import mage.constants.MultiplayerAttackOption; import mage.constants.PhaseStep; @@ -78,6 +79,7 @@ public abstract class GameCommanderImpl extends GameImpl { commander.moveToZone(Zone.COMMAND, null, this, true); commander.getAbilities().setControllerId(player.getId()); ability.addEffect(new CommanderReplacementEffect(commander.getId(), alsoHand, alsoLibrary)); + ability.addEffect(new CommanderCostModification(commander.getId())); CommanderInfoWatcher watcher = new CommanderInfoWatcher(commander.getId(), checkCommanderDamage); getState().addWatcher(watcher); watcher.addCardInfoToCommander(this); diff --git a/Mage/src/main/java/mage/game/GameTinyLeadersImpl.java b/Mage/src/main/java/mage/game/GameTinyLeadersImpl.java index f43c71e92b0..f89c542c844 100644 --- a/Mage/src/main/java/mage/game/GameTinyLeadersImpl.java +++ b/Mage/src/main/java/mage/game/GameTinyLeadersImpl.java @@ -5,6 +5,7 @@ import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.common.InfoEffect; import mage.abilities.effects.common.continuous.CommanderReplacementEffect; +import mage.abilities.effects.common.cost.CommanderCostModification; import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -58,6 +59,7 @@ public abstract class GameTinyLeadersImpl extends GameImpl { commander.moveToZone(Zone.COMMAND, null, this, true); Ability ability = new SimpleStaticAbility(Zone.COMMAND, new InfoEffect("Commander effects")); ability.addEffect(new CommanderReplacementEffect(commander.getId(), alsoHand, alsoLibrary)); + ability.addEffect(new CommanderCostModification(commander.getId())); // Commander rule #4 was removed Jan. 18, 2016 // ability.addEffect(new CommanderManaReplacementEffect(player.getId(), CardUtil.getColorIdentity(commander))); CommanderInfoWatcher watcher = new CommanderInfoWatcher(commander.getId(), false); diff --git a/Mage/src/main/java/mage/players/PlayerImpl.java b/Mage/src/main/java/mage/players/PlayerImpl.java index a131973ae78..c39eff82b06 100644 --- a/Mage/src/main/java/mage/players/PlayerImpl.java +++ b/Mage/src/main/java/mage/players/PlayerImpl.java @@ -12,7 +12,6 @@ import mage.abilities.common.PlayLandAsCommanderAbility; import mage.abilities.common.WhileSearchingPlayFromLibraryAbility; import mage.abilities.common.delayed.AtTheEndOfTurnStepPostDelayedTriggeredAbility; import mage.abilities.costs.*; -import mage.abilities.costs.common.CommanderAdditionalCost; import mage.abilities.costs.mana.ManaCost; import mage.abilities.costs.mana.ManaCosts; import mage.abilities.costs.mana.ManaCostsImpl; @@ -1147,8 +1146,6 @@ public abstract class PlayerImpl implements Player, Serializable { } } - // warning, if you change code here then fix it in activateAbility too (play commander as land) - //20091005 - 305.1 if (!game.replaceEvent(GameEvent.getEvent(GameEvent.EventType.PLAY_LAND, card.getId(), card.getId(), playerId, activationStatus.getPermittingObject()))) { // int bookmark = game.bookmarkState(); @@ -1260,12 +1257,8 @@ public abstract class PlayerImpl implements Player, Serializable { Card card = game.getCard(ability.getSourceId()); if (ability instanceof PlayLandAsCommanderAbility) { - // LAND as commander: - // * first time - play without cost as land - // * second+ times -- cast as spell with cost and stack - - // code from playLand + // LAND as commander: play land with cost, but without stack ActivationStatus activationStatus = ability.canActivate(this.playerId, game); if (!activationStatus.canActivate() || !this.canPlayLand()) { return false; @@ -1274,42 +1267,21 @@ public abstract class PlayerImpl implements Player, Serializable { return false; } - // workaround to find out empty pay in commander land - boolean isEmptyPay = true; - Costs costs = ability.getCosts().copy(); - for (Cost cost : costs) { - if (!(cost instanceof CommanderAdditionalCost) || !((CommanderAdditionalCost) cost).isEmptyPay(ability, game)) { - isEmptyPay = false; - } - } - - if (isEmptyPay) { - // play as land + // as copy, tries to applie cost effects and pays + Ability activatingAbility = ability.copy(); + if (activatingAbility.activate(game, false)) { result = playLand(card, game, false); } else { - // cast as spell with cost, but with all land's restrictions and events like Damping Engine - // look at code in playLand - if (!game.replaceEvent(GameEvent.getEvent(GameEvent.EventType.PLAY_LAND, card.getId(), card.getId(), playerId, activationStatus.getPermittingObject()))) { - game.fireEvent(GameEvent.getEvent(GameEvent.EventType.PLAY_LAND, card.getId(), card.getId(), playerId, activationStatus.getPermittingObject())); - - SpellAbility spellAbility = new SpellAbility(null, card.getName(), game.getState().getZone(card.getId())); - spellAbility.addCost(costs); - spellAbility.setControllerId(this.getId()); - spellAbility.setSourceId(card.getId()); - result = cast(spellAbility, game, false, activationStatus.getPermittingObject()); - - if (result) { - landsPlayed++; - game.fireEvent(GameEvent.getEvent(GameEvent.EventType.LAND_PLAYED, card.getId(), card.getId(), playerId, activationStatus.getPermittingObject())); - } - } else { - result = false; - } + result = false; } + } else if (ability instanceof PlayLandAbility) { + // LAND as normal card: without cost and stack result = playLand(card, game, false); + } else { + // ABILITY ActivationStatus activationStatus = ability.canActivate(this.playerId, game); if (!activationStatus.canActivate()) {