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();
+ }
}