diff --git a/Mage.Sets/src/mage/cards/k/KangeeAerieKeeper.java b/Mage.Sets/src/mage/cards/k/KangeeAerieKeeper.java index cdf9e48aaf3..79b0923feb1 100644 --- a/Mage.Sets/src/mage/cards/k/KangeeAerieKeeper.java +++ b/Mage.Sets/src/mage/cards/k/KangeeAerieKeeper.java @@ -1,22 +1,17 @@ - package mage.cards.k; -import java.util.UUID; import mage.MageInt; -import mage.abilities.Ability; import mage.abilities.TriggeredAbility; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.condition.common.KickedCondition; import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; -import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.dynamicvalue.common.CountersSourceCount; -import mage.abilities.effects.Effect; +import mage.abilities.dynamicvalue.common.GetKickerXValue; import mage.abilities.effects.common.continuous.BoostAllEffect; import mage.abilities.effects.common.counter.AddCountersSourceEffect; import mage.abilities.keyword.FlyingAbility; import mage.abilities.keyword.KickerAbility; -import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; @@ -24,10 +19,10 @@ import mage.counters.CounterType; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.mageobject.SubtypePredicate; import mage.filter.predicate.permanent.AnotherPredicate; -import mage.game.Game; + +import java.util.UUID; /** - * * @author emerald000 */ public final class KangeeAerieKeeper extends CardImpl { @@ -55,7 +50,7 @@ public final class KangeeAerieKeeper extends CardImpl { this.addAbility(FlyingAbility.getInstance()); // When Kangee, Aerie Keeper enters the battlefield, if it was kicked, put X feather counters on it. - TriggeredAbility ability = new EntersBattlefieldTriggeredAbility(new AddCountersSourceEffect(CounterType.FEATHER.createInstance(), new KangeeAerieKeeperGetKickerXValue(), true)); + TriggeredAbility ability = new EntersBattlefieldTriggeredAbility(new AddCountersSourceEffect(CounterType.FEATHER.createInstance(), GetKickerXValue.instance, true)); this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, KickedCondition.instance, "When {this} enters the battlefield, if it was kicked, put X feather counters on it.")); // Other Bird creatures get +1/+1 for each feather counter on Kangee, Aerie Keeper. @@ -71,38 +66,3 @@ public final class KangeeAerieKeeper extends CardImpl { return new KangeeAerieKeeper(this); } } - -class KangeeAerieKeeperGetKickerXValue implements DynamicValue { - - public KangeeAerieKeeperGetKickerXValue() { - } - - @Override - public int calculate(Game game, Ability source, Effect effect) { - int count = 0; - Card card = game.getCard(source.getSourceId()); - if (card != null) { - for (Ability ability : card.getAbilities()) { - if (ability instanceof KickerAbility) { - count += ((KickerAbility) ability).getXManaValue(); - } - } - } - return count; - } - - @Override - public KangeeAerieKeeperGetKickerXValue copy() { - return new KangeeAerieKeeperGetKickerXValue(); - } - - @Override - public String toString() { - return "X"; - } - - @Override - public String getMessage() { - return "X"; - } -} diff --git a/Mage.Sets/src/mage/cards/r/RosheenMeanderer.java b/Mage.Sets/src/mage/cards/r/RosheenMeanderer.java index 50db07f325e..8f27548a9c1 100644 --- a/Mage.Sets/src/mage/cards/r/RosheenMeanderer.java +++ b/Mage.Sets/src/mage/cards/r/RosheenMeanderer.java @@ -1,10 +1,7 @@ - package mage.cards.r; -import java.util.UUID; import mage.ConditionalMana; import mage.MageInt; -import mage.MageObject; import mage.Mana; import mage.abilities.Ability; import mage.abilities.condition.Condition; @@ -12,20 +9,20 @@ import mage.abilities.effects.mana.BasicManaEffect; import mage.abilities.mana.BasicManaAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.AbilityType; import mage.constants.CardType; import mage.constants.SubType; import mage.constants.SuperType; import mage.game.Game; +import java.util.UUID; + /** - * * @author jeffwadsworth */ public final class RosheenMeanderer extends CardImpl { public RosheenMeanderer(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{R/G}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{R/G}"); addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.GIANT); this.subtype.add(SubType.SHAMAN); @@ -75,15 +72,16 @@ class RosheenMeandererConditionalMana extends ConditionalMana { class RosheenMeandererManaCondition implements Condition { + /* + A “cost that contains {X}” may be a spell’s total cost, an activated ability’s cost, a suspend cost, or a cost you’re + asked to pay as part of the resolution of a spell or ability (such as Condescend). A spell’s total cost includes either + its mana cost (printed in the upper right corner) or its alternative cost (such as flashback), as well as any additional + costs (such as kicker). If it’s something you can spend mana on, it’s a cost. If that cost includes the {X} symbol in it, + you can spend mana generated by Rosheen on that cost. (2017-11-17) + */ + @Override public boolean apply(Game game, Ability source) { - if (AbilityType.SPELL == source.getAbilityType()) { - MageObject object = game.getObject(source.getSourceId()); - return object != null - && object.getManaCost().getText().contains("X"); - - } else { - return source.getManaCosts().getText().contains("X"); - } + return source.getManaCostsToPay().containsX(); } } diff --git a/Mage.Sets/src/mage/cards/v/VerdelothTheAncient.java b/Mage.Sets/src/mage/cards/v/VerdelothTheAncient.java index 33099a79cdc..5f84ad9888e 100644 --- a/Mage.Sets/src/mage/cards/v/VerdelothTheAncient.java +++ b/Mage.Sets/src/mage/cards/v/VerdelothTheAncient.java @@ -1,19 +1,14 @@ - package mage.cards.v; -import java.util.UUID; import mage.MageInt; -import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.condition.common.KickedCondition; import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; -import mage.abilities.dynamicvalue.DynamicValue; -import mage.abilities.effects.Effect; +import mage.abilities.dynamicvalue.common.GetKickerXValue; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.effects.common.continuous.BoostAllEffect; import mage.abilities.keyword.KickerAbility; -import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; @@ -21,17 +16,17 @@ import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.Predicates; import mage.filter.predicate.mageobject.SubtypePredicate; import mage.filter.predicate.permanent.PermanentIdPredicate; -import mage.game.Game; import mage.game.permanent.token.SaprolingToken; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class VerdelothTheAncient extends CardImpl { public VerdelothTheAncient(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{4}{G}{G}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{G}{G}"); addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.TREEFOLK); @@ -40,22 +35,22 @@ public final class VerdelothTheAncient extends CardImpl { // Kicker {X} this.addAbility(new KickerAbility("{X}")); - + // Saproling creatures and other Treefolk creatures get +1/+1. FilterCreaturePermanent filter = new FilterCreaturePermanent("Saproling creatures and other Treefolk creatures"); filter.add(Predicates.or( Predicates.and(new SubtypePredicate(SubType.TREEFOLK), Predicates.not(new PermanentIdPredicate(this.getId()))), new SubtypePredicate(SubType.SAPROLING)) - ); + ); filter.add(Predicates.not(new PermanentIdPredicate(this.getId()))); - - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostAllEffect(1,1, Duration.WhileOnBattlefield, filter, false))); - + + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostAllEffect(1, 1, Duration.WhileOnBattlefield, filter, false))); + // When Verdeloth the Ancient enters the battlefield, if it was kicked, create X 1/1 green Saproling creature tokens. - EntersBattlefieldTriggeredAbility ability = new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new SaprolingToken(), new GetKickerXValue()), false); + EntersBattlefieldTriggeredAbility ability = new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new SaprolingToken(), GetKickerXValue.instance), false); this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, KickedCondition.instance, "When {this} enters the battlefield, if it was kicked, create X 1/1 green Saproling creature tokens.")); - + } public VerdelothTheAncient(final VerdelothTheAncient card) { @@ -66,39 +61,4 @@ public final class VerdelothTheAncient extends CardImpl { public VerdelothTheAncient copy() { return new VerdelothTheAncient(this); } -} - -class GetKickerXValue implements DynamicValue { - - public GetKickerXValue() { - } - - @Override - public int calculate(Game game, Ability source, Effect effect) { - int count = 0; - Card card = game.getCard(source.getSourceId()); - if (card != null) { - for (Ability ability: card.getAbilities()) { - if (ability instanceof KickerAbility) { - count += ((KickerAbility) ability).getXManaValue(); - } - } - } - return count; - } - - @Override - public GetKickerXValue copy() { - return new GetKickerXValue(); - } - - @Override - public String toString() { - return "X"; - } - - @Override - public String getMessage() { - return "X"; - } } \ No newline at end of file diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/mana/RosheenMeandererManaXTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/mana/RosheenMeandererManaXTest.java new file mode 100644 index 00000000000..9d349587c0c --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/mana/RosheenMeandererManaXTest.java @@ -0,0 +1,96 @@ +package org.mage.test.cards.mana; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author JayDi85 + */ +public class RosheenMeandererManaXTest extends CardTestPlayerBase { + + // https://github.com/magefree/mage/issues/3538 + + // Rosheen Meanderer {3}{R/G} + // {T}: Add {C}{C}{C}{C}. Spend this mana only on costs that contain {X}. + + // Verdeloth the Ancient {4}{G}{G} + // Kicker {X} (You may pay an additional {X} as you cast this spell.) + // Saproling creatures and other Treefolk creatures get +1/+1. + // When Verdeloth the Ancient enters the battlefield, if it was kicked, create X 1/1 green Saproling creature tokens. + + @Test + public void test_SimpleKicker() { + addCard(Zone.BATTLEFIELD, playerA, "Forest", 6 + 2); + addCard(Zone.HAND, playerA, "Verdeloth the Ancient"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Verdeloth the Ancient"); + setChoice(playerA, "Yes"); // use kicker + setChoice(playerA, "X=2"); + + checkPermanentCount("after", 1, PhaseStep.END_TURN, playerA, "Verdeloth the Ancient", 1); + checkPermanentCount("after", 1, PhaseStep.END_TURN, playerA, "Saproling", 2); + + setStopAt(1, PhaseStep.END_TURN); + setStrictChooseMode(true); + execute(); + assertAllCommandsUsed(); + } + + @Test + public void test_KickerWithXMana() { + addCard(Zone.BATTLEFIELD, playerA, "Forest", 6 + 2 - 4); + addCard(Zone.HAND, playerA, "Verdeloth the Ancient"); + // + addCard(Zone.BATTLEFIELD, playerA, "Rosheen Meanderer"); + + // make 4 mana for X pay + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {C}"); + checkManaPool("mana", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "C", 4); + + // cast kicker X and use extra 4 mana + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Verdeloth the Ancient"); + setChoice(playerA, "Yes"); // use kicker + setChoice(playerA, "X=2"); + + checkPermanentCount("after", 1, PhaseStep.END_TURN, playerA, "Verdeloth the Ancient", 1); + checkPermanentCount("after", 1, PhaseStep.END_TURN, playerA, "Saproling", 2); + + setStopAt(1, PhaseStep.END_TURN); + setStrictChooseMode(true); + execute(); + assertAllCommandsUsed(); + } + + @Test + public void test_KickerWithXZero() { + + // You can spend mana generated by Rosheen on a cost that includes {X} even if you’ve chosen an X of 0, + // or if the card specifies that you can spend only colored mana on X. (You’ll have to spend Rosheen’s mana on + // a different part of that cost, of course.) (2017-11-17) + + addCard(Zone.BATTLEFIELD, playerA, "Forest", 6 - 4); + addCard(Zone.HAND, playerA, "Verdeloth the Ancient"); + // + addCard(Zone.BATTLEFIELD, playerA, "Rosheen Meanderer"); + + // make 4 mana for X pay + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {C}"); + checkManaPool("mana", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "C", 4); + + // cast kicker X and use extra 4 mana + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Verdeloth the Ancient"); + setChoice(playerA, "Yes"); // use kicker + setChoice(playerA, "X=0"); + + checkPermanentCount("after", 1, PhaseStep.END_TURN, playerA, "Verdeloth the Ancient", 1); + checkPermanentCount("after", 1, PhaseStep.END_TURN, playerA, "Saproling", 0); + + setStopAt(1, PhaseStep.END_TURN); + setStrictChooseMode(true); + execute(); + assertAllCommandsUsed(); + } + +} diff --git a/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java b/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java index f5db6d66195..29ef18a65c0 100644 --- a/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java +++ b/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java @@ -1129,7 +1129,9 @@ public class TestPlayer implements Player { } private void assertManaPoolInner(PlayerAction action, Player player, ManaType manaType, Integer amount) { - Integer current = player.getManaPool().get(manaType); + Integer normal = player.getManaPool().getMana().get(manaType); + Integer conditional = player.getManaPool().getConditionalMana().stream().mapToInt(a -> a.get(manaType)).sum(); // calcs FULL conditional mana, not real conditions + Integer current = normal + conditional; Assert.assertEquals(action.getActionName() + " - mana pool must contain [" + amount.toString() + " " + manaType.toString() + "], but found [" + current.toString() + "]", amount, current); } diff --git a/Mage/src/main/java/mage/abilities/dynamicvalue/common/GetKickerXValue.java b/Mage/src/main/java/mage/abilities/dynamicvalue/common/GetKickerXValue.java new file mode 100644 index 00000000000..66d94f89bbf --- /dev/null +++ b/Mage/src/main/java/mage/abilities/dynamicvalue/common/GetKickerXValue.java @@ -0,0 +1,73 @@ +package mage.abilities.dynamicvalue.common; + +import mage.abilities.Ability; +import mage.abilities.costs.OptionalAdditionalCost; +import mage.abilities.costs.OptionalAdditionalCostImpl; +import mage.abilities.costs.VariableCost; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.effects.Effect; +import mage.abilities.keyword.KickerAbility; +import mage.game.Game; +import mage.game.stack.Spell; + +import java.util.List; + +/** + * @author JayDi85 + */ +public enum GetKickerXValue implements DynamicValue { + instance; + + @Override + public int calculate(Game game, Ability source, Effect effect) { + // calcs only kicker with X values + + // kicker adds additional costs to spell ability + // only one X value per card possible + // kicker can be calls multiple times (use getKickedCounter) + + int finalValue = 0; + Spell spell = game.getSpellOrLKIStack(source.getSourceId()); + if (spell != null && spell.getSpellAbility() != null) { + int xValue = spell.getSpellAbility().getManaCostsToPay().getX(); + for (Ability ability : spell.getAbilities()) { + if (ability instanceof KickerAbility) { + + // search that kicker used X value + KickerAbility kickerAbility = (KickerAbility) ability; + boolean haveVarCost = false; + for (OptionalAdditionalCost cost : kickerAbility.getKickerCosts()) { + List varCosts = ((OptionalAdditionalCostImpl) cost).getVariableCosts(); + if (!varCosts.isEmpty()) { + haveVarCost = true; + break; + } + } + + if (haveVarCost) { + int kickedCount = ((KickerAbility) ability).getKickedCounter(game, source); + if (kickedCount > 0) { + finalValue += kickedCount * xValue; + } + } + } + } + } + return finalValue; + } + + @Override + public GetKickerXValue copy() { + return GetKickerXValue.instance; + } + + @Override + public String toString() { + return "X"; + } + + @Override + public String getMessage() { + return ""; + } +} diff --git a/Mage/src/main/java/mage/abilities/keyword/KickerAbility.java b/Mage/src/main/java/mage/abilities/keyword/KickerAbility.java index a3ea9163adc..9d42b876e53 100644 --- a/Mage/src/main/java/mage/abilities/keyword/KickerAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/KickerAbility.java @@ -1,15 +1,10 @@ package mage.abilities.keyword; -import java.util.*; -import java.util.concurrent.ConcurrentHashMap; import mage.abilities.Ability; import mage.abilities.SpellAbility; import mage.abilities.StaticAbility; import mage.abilities.costs.*; -import mage.abilities.costs.mana.GenericManaCost; -import mage.abilities.costs.mana.ManaCost; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.costs.mana.VariableManaCost; import mage.constants.AbilityType; import mage.constants.Outcome; import mage.constants.Zone; @@ -17,6 +12,12 @@ import mage.game.Game; import mage.game.events.GameEvent; import mage.players.Player; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + /** * 20121001 702.31. Kicker 702.31a Kicker is a static ability that functions * while the spell with kicker is on the stack. "Kicker [cost]" means "You may @@ -57,7 +58,6 @@ public class KickerAbility extends StaticAbility implements OptionalAdditionalSo protected String keywordText; protected String reminderText; protected List kickerCosts = new LinkedList<>(); - private int xManaValue = 0; public KickerAbility(String manaString) { this(KICKER_KEYWORD, KICKER_REMINDER_MANA); @@ -79,10 +79,11 @@ public class KickerAbility extends StaticAbility implements OptionalAdditionalSo public KickerAbility(final KickerAbility ability) { super(ability); - this.kickerCosts.addAll(ability.kickerCosts); + for (OptionalAdditionalCost cost : ability.kickerCosts) { + this.kickerCosts.add((OptionalAdditionalCost) cost.copy()); + } this.keywordText = ability.keywordText; this.reminderText = ability.reminderText; - this.xManaValue = ability.xManaValue; this.activations.putAll(ability.activations); } @@ -108,7 +109,7 @@ public class KickerAbility extends StaticAbility implements OptionalAdditionalSo cost.reset(); } String key = getActivationKey(source, "", game); - for (Iterator iterator = activations.keySet().iterator(); iterator.hasNext();) { + for (Iterator iterator = activations.keySet().iterator(); iterator.hasNext(); ) { String activationKey = iterator.next(); if (activationKey.startsWith(key) && activations.get(activationKey) > 0) { activations.put(key, 0); @@ -116,10 +117,6 @@ public class KickerAbility extends StaticAbility implements OptionalAdditionalSo } } - public int getXManaValue() { - return xManaValue; - } - public int getKickedCounter(Game game, Ability source) { String key = getActivationKey(source, "", game); return activations.getOrDefault(key, 0); @@ -167,7 +164,7 @@ public class KickerAbility extends StaticAbility implements OptionalAdditionalSo if (zcc > 0 && (source.getAbilityType() == AbilityType.TRIGGERED)) { --zcc; } - return String.valueOf(zcc) + ((kickerCosts.size() > 1) ? costText : ""); + return zcc + ((kickerCosts.size() > 1) ? costText : ""); } @Override @@ -182,16 +179,16 @@ public class KickerAbility extends StaticAbility implements OptionalAdditionalSo String times = ""; if (kickerCost.isRepeatable()) { int activatedCount = getKickedCounter(game, ability); - times = Integer.toString(activatedCount + 1) + (activatedCount == 0 ? " time " : " times "); + times = (activatedCount + 1) + (activatedCount == 0 ? " time " : " times "); } if (kickerCost.canPay(ability, sourceId, controllerId, game) && player.chooseUse(Outcome.Benefit, "Pay " + times + kickerCost.getText(false) + " ?", ability, game)) { this.activateKicker(kickerCost, ability, game); if (kickerCost instanceof Costs) { - for (Iterator itKickerCost = ((Costs) kickerCost).iterator(); itKickerCost.hasNext();) { + for (Iterator itKickerCost = ((Costs) kickerCost).iterator(); itKickerCost.hasNext(); ) { Object kickerCostObject = itKickerCost.next(); if ((kickerCostObject instanceof Costs) || (kickerCostObject instanceof CostsImpl)) { - for (@SuppressWarnings("unchecked") Iterator itDetails = ((Costs) kickerCostObject).iterator(); itDetails.hasNext();) { + for (@SuppressWarnings("unchecked") Iterator itDetails = ((Costs) kickerCostObject).iterator(); itDetails.hasNext(); ) { addKickerCostsToAbility(itDetails.next(), ability, game); } } else { @@ -199,7 +196,7 @@ public class KickerAbility extends StaticAbility implements OptionalAdditionalSo } } } else { - addKickerCostsToAbility((Cost) kickerCost, ability, game); + addKickerCostsToAbility(kickerCost, ability, game); } again = kickerCost.isRepeatable(); } else { @@ -212,26 +209,9 @@ public class KickerAbility extends StaticAbility implements OptionalAdditionalSo } private void addKickerCostsToAbility(Cost cost, Ability ability, Game game) { + // can contains multiple costs from multikicker ability if (cost instanceof ManaCostsImpl) { - @SuppressWarnings("unchecked") - List varCosts = ((ManaCostsImpl) cost).getVariableCosts(); - if (!varCosts.isEmpty()) { - // use only first variable cost - xManaValue = game.getPlayer(this.controllerId).announceXMana(varCosts.get(0).getMinX(), Integer.MAX_VALUE, "Announce kicker value for " + varCosts.get(0).getText(), game, this); - // kicker variable X costs handled internally as multikicker with {1} cost (no multikicker on card) - if (!game.isSimulation()) { - game.informPlayers(game.getPlayer(this.controllerId).getLogName() + " announced a value of " + xManaValue + " for " + " kicker X "); - } - ability.getManaCostsToPay().add(new GenericManaCost(xManaValue)); - ManaCostsImpl kickerManaCosts = (ManaCostsImpl) cost; - for (ManaCost manaCost : kickerManaCosts) { - if (!(manaCost instanceof VariableManaCost)) { - ability.getManaCostsToPay().add(manaCost.copy()); - } - } - } else { - ability.getManaCostsToPay().add((ManaCostsImpl) cost.copy()); - } + ability.getManaCostsToPay().add((ManaCostsImpl) cost.copy()); } else { ability.getCosts().add(cost.copy()); }