diff --git a/Mage.Client/src/main/java/mage/client/cards/Cards.java b/Mage.Client/src/main/java/mage/client/cards/Cards.java index d79ed7c1e9..cff58b5614 100644 --- a/Mage.Client/src/main/java/mage/client/cards/Cards.java +++ b/Mage.Client/src/main/java/mage/client/cards/Cards.java @@ -183,6 +183,7 @@ if (card instanceof StackAbilityView) { // replace ability by original card CardView tmp = ((StackAbilityView) card).getSourceCard(); + // sync settings tmp.overrideRules(card.getRules()); tmp.setChoosable(card.isChoosable()); tmp.setPlayableStats(card.getPlayableStats().copy()); @@ -191,6 +192,9 @@ tmp.overrideTargets(card.getTargets()); tmp.overrideId(card.getId()); tmp.setAbilityType(card.getAbilityType()); + // sync card icons + tmp.getCardIcons().clear(); + tmp.getCardIcons().addAll(card.getCardIcons()); card = tmp; } else { card.setAbilityType(null); diff --git a/Mage.Common/src/main/java/mage/view/StackAbilityView.java b/Mage.Common/src/main/java/mage/view/StackAbilityView.java index c9de06d7f6..74142d11d9 100644 --- a/Mage.Common/src/main/java/mage/view/StackAbilityView.java +++ b/Mage.Common/src/main/java/mage/view/StackAbilityView.java @@ -3,9 +3,11 @@ package mage.view; import mage.MageObject; import mage.abilities.Mode; import mage.abilities.Modes; +import mage.abilities.dynamicvalue.common.ManacostVariableValue; import mage.abilities.effects.Effect; import mage.abilities.hint.Hint; import mage.abilities.hint.HintUtils; +import mage.abilities.icon.other.VariableCostCardIcon; import mage.cards.Card; import mage.constants.AbilityType; import mage.constants.CardType; @@ -28,7 +30,7 @@ public class StackAbilityView extends CardView { private static final long serialVersionUID = 1L; // in GUI: that's view will be replaced by sourceCard, so don't forget to sync settings like - // selectable, chooseable, etc. Search by getSourceCard + // selectable, chooseable, card icons etc. Search by getSourceCard private final CardView sourceCard; public StackAbilityView(Game game, StackAbility ability, String sourceName, CardView sourceCard) { @@ -73,6 +75,13 @@ public class StackAbilityView extends CardView { this.counters = sourceCard.getCounters(); updateTargets(game, ability); + + // card icons (warning, it must be synced in gui dialogs with replaced card, see comments at the start of the file) + // cost x + if (ability.getManaCostsToPay().containsX()) { + int costX = ManacostVariableValue.REGULAR.calculate(game, ability, null); + this.cardIcons.add(new VariableCostCardIcon(costX)); + } } private void updateTargets(Game game, StackAbility ability) { @@ -108,7 +117,7 @@ public class StackAbilityView extends CardView { } } if (!names.isEmpty()) { - getRules().add("Related objects: " + names.toString() + ""); + getRules().add("Related objects: " + names + ""); } // show for modal ability, which mode was choosen diff --git a/Mage.Tests/src/test/java/org/mage/test/serverside/CardIconsTest.java b/Mage.Tests/src/test/java/org/mage/test/serverside/CardIconsTest.java index 37b7d33efa..dae5855e90 100644 --- a/Mage.Tests/src/test/java/org/mage/test/serverside/CardIconsTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/serverside/CardIconsTest.java @@ -152,4 +152,73 @@ public class CardIconsTest extends CardTestPlayerBase { execute(); assertAllCommandsUsed(); } + + @Test + public void test_CostX_Abilities() { + // X icon must be visible only for activated ability, not spell cast + + // {X}{R}, {tap}, Sacrifice Cinder Elemental: Cinder Elemental deals X damage to any target. + addCard(Zone.HAND, playerA, "Cinder Elemental", 1); // {3}{R} + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 4); + + // hand (not visible) + runCode("card icons in hand", 1, PhaseStep.PRECOMBAT_MAIN, playerA, (info, player, game) -> { + GameView gameView = getGameView(player); + Assert.assertEquals("must have 1 card in hand", 1, gameView.getHand().values().size()); + CardView cardView = gameView.getHand().values().stream().findFirst().get(); + Assert.assertEquals("must have non x cost card icons in hand", 0, cardView.getCardIcons().size()); + }); + + // spell cast + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cinder Elemental"); + + // stack (spell cast - not visible) + runCode("card icons on stack (spell cast - not visible)", 1, PhaseStep.PRECOMBAT_MAIN, playerA, (info, player, game) -> { + GameView gameView = getGameView(player); + Assert.assertEquals("must have 1 card in stack", 1, gameView.getStack().values().size()); + CardView cardView = gameView.getStack().values().stream().findFirst().get(); + Assert.assertEquals("must have not x cost card icons in stack", 0, cardView.getCardIcons().size()); + }); + + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + checkPermanentCount("after cast", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cinder Elemental", 1); + + // battlefield (card, not visible) + runCode("card icons in battlefield (card)", 1, PhaseStep.PRECOMBAT_MAIN, playerA, (info, player, game) -> { + GameView gameView = getGameView(player); + PlayerView playerView = gameView.getPlayers().get(0); + Assert.assertEquals("player", player.getName(), playerView.getName()); + CardView cardView = playerView.getBattlefield().values().stream().filter(p -> p.getName().equals("Cinder Elemental")).findFirst().orElse(null); + Assert.assertNotNull("must have Cinder Elemental in battlefield", cardView); + Assert.assertEquals("must have not x cost card icons in battlefield (card)", 0, cardView.getCardIcons().size()); + }); + + // ACTIVATE ABILITY (x must be visible in stack, but not visible after resolve) + activateAbility(3, PhaseStep.PRECOMBAT_MAIN, playerA, "{X}{R}"); + setChoice(playerA, "X=2"); + addTarget(playerA, playerB); + + // stack (ability activated - visible) + runCode("card icons on stack (ability activated - visible)", 3, PhaseStep.PRECOMBAT_MAIN, playerA, (info, player, game) -> { + GameView gameView = getGameView(player); + Assert.assertEquals("ability activated - must have 1 card in stack", 1, gameView.getStack().values().size()); + CardView cardView = gameView.getStack().values().stream().findFirst().get(); + Assert.assertEquals("ability activated - must have x cost card icons in stack", 1, cardView.getCardIcons().size()); + }); + + // battlefield (ability activated, not visible) + runCode("card icons in battlefield (ability activated)", 1, PhaseStep.PRECOMBAT_MAIN, playerA, (info, player, game) -> { + GameView gameView = getGameView(player); + PlayerView playerView = gameView.getPlayers().get(0); + Assert.assertEquals("player", player.getName(), playerView.getName()); + CardView cardView = playerView.getBattlefield().values().stream().filter(p -> p.getName().equals("Cinder Elemental")).findFirst().orElse(null); + Assert.assertNotNull("ability activated - must have Cinder Elemental in battlefield", cardView); + Assert.assertEquals("ability activated - must have not x cost card icons in battlefield", 0, cardView.getCardIcons().size()); + }); + + setStrictChooseMode(true); + setStopAt(3, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + } }