diff --git a/Mage.Sets/src/mage/cards/f/FlameblastDragon.java b/Mage.Sets/src/mage/cards/f/FlameblastDragon.java index 50b304da9b8..3f109f300a5 100644 --- a/Mage.Sets/src/mage/cards/f/FlameblastDragon.java +++ b/Mage.Sets/src/mage/cards/f/FlameblastDragon.java @@ -1,7 +1,5 @@ - package mage.cards.f; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.AttacksTriggeredAbility; @@ -13,20 +11,22 @@ import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Outcome; +import mage.constants.SubType; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.common.TargetAnyTarget; +import java.util.UUID; + /** * @author Loki */ public final class FlameblastDragon extends CardImpl { public FlameblastDragon(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{4}{R}{R}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{R}{R}"); this.subtype.add(SubType.DRAGON); this.power = new MageInt(5); @@ -34,6 +34,7 @@ public final class FlameblastDragon extends CardImpl { // Flying this.addAbility(FlyingAbility.getInstance()); + // Whenever Flameblast Dragon attacks, you may pay {X}{R}. If you do, Flameblast Dragon deals X damage to any target. Ability ability = new AttacksTriggeredAbility(new FlameblastDragonEffect(), false); ability.addTarget(new TargetAnyTarget()); diff --git a/Mage.Sets/src/mage/cards/r/RosheenMeanderer.java b/Mage.Sets/src/mage/cards/r/RosheenMeanderer.java index 8f27548a9c1..9186613c397 100644 --- a/Mage.Sets/src/mage/cards/r/RosheenMeanderer.java +++ b/Mage.Sets/src/mage/cards/r/RosheenMeanderer.java @@ -4,9 +4,12 @@ import mage.ConditionalMana; import mage.MageInt; import mage.Mana; import mage.abilities.Ability; -import mage.abilities.condition.Condition; +import mage.abilities.costs.Cost; +import mage.abilities.costs.mana.ManaCosts; +import mage.abilities.costs.mana.VariableManaCost; import mage.abilities.effects.mana.BasicManaEffect; import mage.abilities.mana.BasicManaAbility; +import mage.abilities.mana.conditional.ManaCondition; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; @@ -70,7 +73,7 @@ class RosheenMeandererConditionalMana extends ConditionalMana { } } -class RosheenMeandererManaCondition implements Condition { +class RosheenMeandererManaCondition extends ManaCondition { /* 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 @@ -81,7 +84,11 @@ class RosheenMeandererManaCondition implements Condition { */ @Override - public boolean apply(Game game, Ability source) { - return source.getManaCostsToPay().containsX(); + public boolean apply(Game game, Ability source, UUID originalId, Cost costToPay) { + if (costToPay instanceof ManaCosts) { + return !((ManaCosts) costToPay).getVariableCosts().isEmpty(); + } else { + return costToPay instanceof VariableManaCost; + } } } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/continuous/UnboundFlourishingTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/continuous/UnboundFlourishingTest.java index e226099d9cd..fdb5fd79498 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/continuous/UnboundFlourishingTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/continuous/UnboundFlourishingTest.java @@ -183,7 +183,7 @@ public class UnboundFlourishingTest extends CardTestPlayerBase { int xAnnouncedValue = 3; int xMultiplier = 2; VariableManaCost cost = new VariableManaCost(xInstancesCount); - cost.setAmount(xAnnouncedValue * xMultiplier, xAnnouncedValue * xInstancesCount); + cost.setAmount(xAnnouncedValue * xMultiplier, xAnnouncedValue * xInstancesCount, false); Assert.assertEquals("instances count", xInstancesCount, cost.getXInstancesCount()); Assert.assertEquals("boosted X value", xAnnouncedValue * xMultiplier, cost.getAmount()); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/mana/ManaPoolTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/mana/ManaPoolTest.java new file mode 100644 index 00000000000..3cccb1cdf06 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/mana/ManaPoolTest.java @@ -0,0 +1,377 @@ +package org.mage.test.cards.mana; + +import mage.Mana; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.dynamicvalue.common.ManacostVariableValue; +import mage.abilities.effects.common.CounterUnlessPaysEffect; +import mage.abilities.effects.common.DamageTargetEffect; +import mage.abilities.effects.mana.AddConditionalManaEffect; +import mage.abilities.mana.SimpleManaAbility; +import mage.abilities.mana.builder.common.InstantOrSorcerySpellManaBuilder; +import mage.abilities.mana.builder.common.SimpleActivatedAbilityManaBuilder; +import mage.constants.PhaseStep; +import mage.constants.Zone; +import mage.target.TargetSpell; +import mage.target.common.TargetAnyTarget; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author JayDi85 + */ +public class ManaPoolTest extends CardTestPlayerBase { + + @Test + public void test_OneMana_OneSpell() { + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1); + addCard(Zone.HAND, playerA, "Lightning Bolt"); // {R} + + // make mana + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}"); + checkManaPool("mana", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "R", 1); + + // use for spell + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", playerB); + + checkLife("after", 1, PhaseStep.END_TURN, playerB, 20 - 3); + + setStopAt(1, PhaseStep.END_TURN); + setStrictChooseMode(true); + execute(); + assertAllCommandsUsed(); + } + + @Test + public void test_MultipleMana_OneSpell() { + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3); + addCard(Zone.HAND, playerA, "Precision Bolt"); // {2}{R} + + // make mana + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}"); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}"); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}"); + checkManaPool("mana", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "R", 3); + + // use for spell + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Precision Bolt", playerB); + + checkLife("after", 1, PhaseStep.END_TURN, playerB, 20 - 3); + + setStopAt(1, PhaseStep.END_TURN); + setStrictChooseMode(true); + execute(); + assertAllCommandsUsed(); + } + + @Test + public void test_ConditionalMana_OneSpell() { + // +1: Add {R}{R}{R}. Spend this mana only to cast instant or sorcery spells. + addCard(Zone.BATTLEFIELD, playerA, "Jaya Ballard"); + addCard(Zone.HAND, playerA, "Precision Bolt"); // {2}{R} + + // make mana + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "+1: Add {R}{R}{R}"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + checkManaPool("mana", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "R", 3); + + // use for spell + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Precision Bolt", playerB); + + checkLife("after", 1, PhaseStep.END_TURN, playerB, 20 - 3); + + setStopAt(1, PhaseStep.END_TURN); + setStrictChooseMode(true); + execute(); + assertAllCommandsUsed(); + } + + @Test + public void test_ConditionalMana_MultipleSpells() { + // +1: Add {R}{R}{R}. Spend this mana only to cast instant or sorcery spells. + addCard(Zone.BATTLEFIELD, playerA, "Jaya Ballard"); + addCard(Zone.HAND, playerA, "Lightning Bolt", 2); // {R} + + // make mana + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "+1: Add {R}{R}{R}"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + checkManaPool("mana", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "R", 3); + + // use for spell + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", playerB); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", playerB); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + checkManaPool("mana", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "R", 1); + + checkLife("after", 1, PhaseStep.END_TURN, playerB, 20 - 3 * 2); + + setStopAt(1, PhaseStep.END_TURN); + setStrictChooseMode(true); + execute(); + assertAllCommandsUsed(); + } + + @Test + public void test_MultipleMana_OneXSpell() { + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 4); + addCard(Zone.HAND, playerA, "Volcanic Geyser"); // {X}{R}{R} + + // make mana + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}"); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}"); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}"); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}"); + checkManaPool("mana", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "R", 4); + + // use for spell + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Volcanic Geyser", playerB); + setChoice(playerA, "X=2"); + + checkLife("after", 1, PhaseStep.END_TURN, playerB, 20 - 2); + + setStopAt(1, PhaseStep.END_TURN); + setStrictChooseMode(true); + execute(); + assertAllCommandsUsed(); + } + + @Test + public void test_MultipleMana_MultipleXSpell() { + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 4 * 2); + addCard(Zone.HAND, playerA, "Volcanic Geyser", 2); // {X}{R}{R} + + // make mana + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}"); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}"); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}"); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}"); + // + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}"); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}"); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}"); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}"); + checkManaPool("mana", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "R", 4 * 2); + + // use for spell + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Volcanic Geyser", playerB); + setChoice(playerA, "X=2"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Volcanic Geyser", playerB); + setChoice(playerA, "X=2"); + + checkLife("after", 1, PhaseStep.END_TURN, playerB, 20 - 2 * 2); + + setStopAt(1, PhaseStep.END_TURN); + setStrictChooseMode(true); + execute(); + assertAllCommandsUsed(); + } + + @Test + public void test_ConditionalMana_OneXSpell() { + addCustomCardWithAbility("add 10", playerA, new SimpleActivatedAbility(Zone.ALL, + new AddConditionalManaEffect(Mana.RedMana(10), new InstantOrSorcerySpellManaBuilder()), + new ManaCostsImpl(""))); + addCard(Zone.HAND, playerA, "Volcanic Geyser"); // {X}{R}{R} + + // make mana + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Add {R}"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + checkManaPool("mana", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "R", 10); + + // use for spell + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Volcanic Geyser", playerB); + setChoice(playerA, "X=1"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + checkManaPool("mana", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "R", 10 - 3); + + checkLife("after", 1, PhaseStep.END_TURN, playerB, 20 - 1); + + setStopAt(1, PhaseStep.END_TURN); + setStrictChooseMode(true); + execute(); + assertAllCommandsUsed(); + } + + @Test + public void test_ConditionalMana_MultipleXSpell() { + addCustomCardWithAbility("add 10", playerA, new SimpleActivatedAbility(Zone.ALL, + new AddConditionalManaEffect(Mana.RedMana(10), new InstantOrSorcerySpellManaBuilder()), + new ManaCostsImpl(""))); + addCard(Zone.HAND, playerA, "Volcanic Geyser", 2); // {X}{R}{R} + + // make mana + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Add {R}"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + checkManaPool("mana", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "R", 10); + + // use for spell 1 + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Volcanic Geyser", playerB); + setChoice(playerA, "X=1"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + checkManaPool("mana", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "R", 10 - 3); + + // use for spell 2 + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Volcanic Geyser", playerB); + setChoice(playerA, "X=1"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + checkManaPool("mana", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "R", 10 - 3 * 2); + + checkLife("after", 1, PhaseStep.END_TURN, playerB, 20 - 1 * 2); + + setStopAt(1, PhaseStep.END_TURN); + setStrictChooseMode(true); + execute(); + assertAllCommandsUsed(); + } + + @Test + public void test_MultipleMana_OneXAbility() { + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 4); + // + Ability ability = new SimpleActivatedAbility(Zone.ALL, new DamageTargetEffect(ManacostVariableValue.instance), new ManaCostsImpl("{X}")); + ability.addTarget(new TargetAnyTarget()); + addCustomCardWithAbility("damage X", playerA, ability); + + // make mana + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}"); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}"); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}"); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}"); + checkManaPool("mana", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "R", 4); + + // use for ability + showAvaileableAbilities("before ability", 1, PhaseStep.PRECOMBAT_MAIN, playerA); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{X}:", playerB); + setChoice(playerA, "X=3"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + checkManaPool("mana", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "R", 4 - 3); + + checkLife("after", 1, PhaseStep.END_TURN, playerB, 20 - 3); + + setStopAt(1, PhaseStep.END_TURN); + setStrictChooseMode(true); + execute(); + assertAllCommandsUsed(); + } + + @Test + public void test_ConditionalMana_OneXAbility() { + addCustomCardWithAbility("add 10", playerA, new SimpleActivatedAbility(Zone.ALL, + new AddConditionalManaEffect(Mana.RedMana(10), new SimpleActivatedAbilityManaBuilder()), + new ManaCostsImpl(""))); + // + Ability ability = new SimpleActivatedAbility(Zone.ALL, new DamageTargetEffect(ManacostVariableValue.instance), new ManaCostsImpl("{X}")); + ability.addTarget(new TargetAnyTarget()); + addCustomCardWithAbility("damage X", playerA, ability); + + // make mana + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Add {R}"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + checkManaPool("mana", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "R", 10); + + // use for ability + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{X}:", playerB); + setChoice(playerA, "X=3"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + checkManaPool("mana", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "R", 10 - 3); + + checkLife("after", 1, PhaseStep.END_TURN, playerB, 20 - 3); + + setStopAt(1, PhaseStep.END_TURN); + setStrictChooseMode(true); + execute(); + assertAllCommandsUsed(); + } + + @Test + public void test_MultipleMana_OneXPart() { + addCard(Zone.HAND, playerA, "Lightning Bolt"); // {R} + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1 + 3 + 1); + // + Ability ability = new SimpleActivatedAbility(Zone.ALL, new DamageTargetEffect(ManacostVariableValue.instance), new ManaCostsImpl("")); + ability.addTarget(new TargetAnyTarget()); + addCustomCardWithAbility("damage X", playerA, ability); + // + // {X}: Counter target spell + ability = new SimpleActivatedAbility(Zone.ALL, new CounterUnlessPaysEffect(ManacostVariableValue.instance), new ManaCostsImpl("{X}")); + ability.addTarget(new TargetSpell()); + addCustomCardWithAbility("counter until pay X", playerB, ability); + addCard(Zone.BATTLEFIELD, playerB, "Island", 3); + + // make mana for spell + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}"); + checkManaPool("mana spell", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "R", 1); + // cast spell + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", playerB); + // make mana for pay X to prevent + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}"); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}"); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}"); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}"); // one must be saved in pool + checkManaPool("mana prevent", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "R", 4); + + // counter by X=3 + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerB, "{X}: Counter"); + setChoice(playerB, "X=3"); + addTarget(playerB, "Lightning Bolt"); + // pay to prevent + setChoice(playerA, "Yes"); // pay 3 to prevent counter + + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + checkManaPool("mana after", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "R", 4 - 3); + + checkLife("after", 1, PhaseStep.END_TURN, playerB, 20 - 3); + + setStopAt(1, PhaseStep.END_TURN); + setStrictChooseMode(true); + execute(); + assertAllCommandsUsed(); + } + + @Test + public void test_ConditionalMana_OneXPart() { + addCard(Zone.HAND, playerA, "Lightning Bolt"); // {R} + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1); + // + addCustomCardWithAbility("add 10", playerA, new SimpleManaAbility(Zone.ALL, + new AddConditionalManaEffect(Mana.RedMana(10), new SimpleActivatedAbilityManaBuilder()), + new ManaCostsImpl(""))); + // + Ability ability = new SimpleActivatedAbility(Zone.ALL, new DamageTargetEffect(ManacostVariableValue.instance), new ManaCostsImpl("")); + ability.addTarget(new TargetAnyTarget()); + addCustomCardWithAbility("damage X", playerA, ability); + // + // {X}: Counter target spell + ability = new SimpleActivatedAbility(Zone.ALL, new CounterUnlessPaysEffect(ManacostVariableValue.instance), new ManaCostsImpl("{X}")); + ability.addTarget(new TargetSpell()); + addCustomCardWithAbility("counter until pay X", playerB, ability); + addCard(Zone.BATTLEFIELD, playerB, "Island", 3); + + // make mana for spell + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}"); + checkManaPool("mana spell", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "R", 1); + // cast spell + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", playerB); + // make mana for pay X to prevent + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Add {R}"); + checkManaPool("mana prevent", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "R", 10); + + // counter by X=3 + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerB, "{X}: Counter"); + setChoice(playerB, "X=3"); + addTarget(playerB, "Lightning Bolt"); + // pay to prevent + setChoice(playerA, "Yes"); // pay 3 to prevent counter + + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + checkManaPool("mana after", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "R", 10 + 1 - 1 - 3); + + checkLife("after", 1, PhaseStep.END_TURN, playerB, 20 - 3); + + setStopAt(1, PhaseStep.END_TURN); + setStrictChooseMode(true); + execute(); + assertAllCommandsUsed(); + } +} 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 index 9d349587c0c..6ef6eb8977d 100644 --- 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 @@ -93,4 +93,92 @@ public class RosheenMeandererManaXTest extends CardTestPlayerBase { assertAllCommandsUsed(); } + // https://github.com/magefree/mage/issues/5206 + + // Flameblast Dragon {4}{R}{R} + // 5/5 + // Flying + // Whenever Flameblast Dragon attacks, you may pay {X}{R}. If you do, Flameblast Dragon deals X damage to any target. + + @Test + public void test_SimpleDragon() { + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 4); + addCard(Zone.BATTLEFIELD, playerA, "Flameblast Dragon"); + + // attack (-5) + attack(1, playerA, "Flameblast Dragon", playerB); + + // with extra damage (-3) + setChoice(playerA, "Yes"); + setChoice(playerA, "X=3"); + addTarget(playerA, playerB); + + checkLife("after", 1, PhaseStep.POSTCOMBAT_MAIN, playerB, 20 - 5 - 3); + + setStopAt(1, PhaseStep.END_TURN); + setStrictChooseMode(true); + execute(); + assertAllCommandsUsed(); + } + + @Test + public void test_PayXForDragonAbility() { + addCard(Zone.BATTLEFIELD, playerA, "Flameblast Dragon"); + addCard(Zone.BATTLEFIELD, playerA, "Rosheen Meanderer"); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1); + + // make 4 mana for X pay + activateAbility(1, PhaseStep.DECLARE_ATTACKERS, playerA, "{T}: Add {C}"); + checkManaPool("mana", 1, PhaseStep.DECLARE_ATTACKERS, playerA, "C", 4); + + // attack (-5) + attack(1, playerA, "Flameblast Dragon", playerB); + + // with extra damage (-3) + setChoice(playerA, "Yes"); + setChoice(playerA, "X=3"); // need to pay {3}{R} + addTarget(playerA, playerB); + + checkLife("after", 1, PhaseStep.POSTCOMBAT_MAIN, playerB, 20 - 5 - 3); + + setStopAt(1, PhaseStep.END_TURN); + setStrictChooseMode(true); + execute(); + assertAllCommandsUsed(); + } + + // Condescend {X}{U} + // Counter target spell unless its controller pays {X}. Scry 2. + + @Test + public void test_PayXForCondescendPrevent() { + addCard(Zone.HAND, playerB, "Condescend"); + addCard(Zone.BATTLEFIELD, playerB, "Island", 3); + // + addCard(Zone.BATTLEFIELD, playerA, "Rosheen Meanderer"); + // + addCard(Zone.HAND, playerA, "Lightning Bolt"); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1); + + // cast bolt + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", playerB); + // counter with condescend + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Condescend"); + setChoice(playerB, "X=2"); + addTarget(playerB, "Lightning Bolt"); + // make 4 mana for X pay + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {C}"); + checkManaPool("mana", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "C", 4); + // pay to prevent + setChoice(playerA, "Yes"); // pay 2 to prevent counter + + checkLife("after", 1, PhaseStep.POSTCOMBAT_MAIN, playerB, 20 - 3); + checkHandCardCount("after", 1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Lightning Bolt", 0); + checkHandCardCount("after", 1, PhaseStep.POSTCOMBAT_MAIN, playerB, "Condescend", 0); + + setStopAt(1, PhaseStep.END_TURN); + setStrictChooseMode(true); + execute(); + assertAllCommandsUsed(); + } } diff --git a/Mage/src/main/java/mage/abilities/AbilityImpl.java b/Mage/src/main/java/mage/abilities/AbilityImpl.java index f42d4cca2bd..fe39fd4adf4 100644 --- a/Mage/src/main/java/mage/abilities/AbilityImpl.java +++ b/Mage/src/main/java/mage/abilities/AbilityImpl.java @@ -256,7 +256,7 @@ public abstract class AbilityImpl implements Ability { VariableManaCost xCosts = new VariableManaCost(); // no x events - rules from Unbound Flourishing: // - Spells with additional costs that include X won't be affected by Unbound Flourishing. X must be in the spell's mana cost. - xCosts.setAmount(xValue, xValue); + xCosts.setAmount(xValue, xValue, false); this.getManaCostsToPay().add(xCosts); } else { this.getManaCostsToPay().clear(); @@ -525,7 +525,7 @@ public abstract class AbilityImpl implements Ability { // set the xcosts to paid // no x events - rules from Unbound Flourishing: // - Spells with additional costs that include X won't be affected by Unbound Flourishing. X must be in the spell's mana cost. - variableCost.setAmount(xValue, xValue); + variableCost.setAmount(xValue, xValue, false); ((Cost) variableCost).setPaid(); String message = controller.getLogName() + " announces a value of " + xValue + " (" + variableCost.getActionText() + ')'; announceString.append(message); diff --git a/Mage/src/main/java/mage/abilities/costs/VariableCost.java b/Mage/src/main/java/mage/abilities/costs/VariableCost.java index 5327976c532..eb2bda22500 100644 --- a/Mage/src/main/java/mage/abilities/costs/VariableCost.java +++ b/Mage/src/main/java/mage/abilities/costs/VariableCost.java @@ -17,10 +17,11 @@ public interface VariableCost { /** * Sets the variable amount * - * @param xValue - value of X - * @param xPay - total value of pays for X (X * xMultiplier * xInstancesCount) + * @param xValue - value of X + * @param xPay - total value of pays for X (X * xMultiplier * xInstancesCount) + * @param isPayed - is that was real payed or just value setup */ - void setAmount(int xValue, int xPay); + void setAmount(int xValue, int xPay, boolean isPayed); /** * returns the action text (e.g. "creature cards to exile from your hand", "life to pay") diff --git a/Mage/src/main/java/mage/abilities/costs/VariableCostImpl.java b/Mage/src/main/java/mage/abilities/costs/VariableCostImpl.java index 9d9d9804386..6d38676d0de 100644 --- a/Mage/src/main/java/mage/abilities/costs/VariableCostImpl.java +++ b/Mage/src/main/java/mage/abilities/costs/VariableCostImpl.java @@ -123,7 +123,7 @@ public abstract class VariableCostImpl implements Cost, VariableCost { } @Override - public void setAmount(int xValue, int xPay) { + public void setAmount(int xValue, int xPay, boolean isPayed) { amountPaid = xPay; } diff --git a/Mage/src/main/java/mage/abilities/costs/common/ExileFromHandCost.java b/Mage/src/main/java/mage/abilities/costs/common/ExileFromHandCost.java index fd98adf8fcc..5074791ef48 100644 --- a/Mage/src/main/java/mage/abilities/costs/common/ExileFromHandCost.java +++ b/Mage/src/main/java/mage/abilities/costs/common/ExileFromHandCost.java @@ -69,7 +69,7 @@ public class ExileFromHandCost extends CostImpl { VariableManaCost vmc = new VariableManaCost(); // no x events - rules from Unbound Flourishing: // - Spells with additional costs that include X won't be affected by Unbound Flourishing. X must be in the spell's mana cost. - vmc.setAmount(cmc, cmc); + vmc.setAmount(cmc, cmc, false); vmc.setPaid(); ability.getManaCostsToPay().add(vmc); } diff --git a/Mage/src/main/java/mage/abilities/costs/mana/GenericManaCost.java b/Mage/src/main/java/mage/abilities/costs/mana/GenericManaCost.java index bace69310a7..bfd38cb9c64 100644 --- a/Mage/src/main/java/mage/abilities/costs/mana/GenericManaCost.java +++ b/Mage/src/main/java/mage/abilities/costs/mana/GenericManaCost.java @@ -1,4 +1,3 @@ - package mage.abilities.costs.mana; import mage.Mana; @@ -42,7 +41,7 @@ public class GenericManaCost extends ManaCostImpl { @Override public void assignPayment(Game game, Ability ability, ManaPool pool, Cost costsToPay) { - this.assignGeneric(ability, game, pool, mana, costsToPay); + this.assignGeneric(ability, game, pool, mana, null, costsToPay); } @Override diff --git a/Mage/src/main/java/mage/abilities/costs/mana/ManaCostImpl.java b/Mage/src/main/java/mage/abilities/costs/mana/ManaCostImpl.java index 082f09c47db..afbab56ea0b 100644 --- a/Mage/src/main/java/mage/abilities/costs/mana/ManaCostImpl.java +++ b/Mage/src/main/java/mage/abilities/costs/mana/ManaCostImpl.java @@ -1,9 +1,5 @@ - package mage.abilities.costs.mana; -import java.util.ArrayList; -import java.util.List; -import java.util.UUID; import mage.Mana; import mage.abilities.Ability; import mage.abilities.costs.Cost; @@ -12,11 +8,16 @@ import mage.abilities.mana.ManaOptions; import mage.constants.ColoredManaSymbol; import mage.constants.ManaType; import mage.filter.Filter; +import mage.filter.FilterMana; import mage.game.Game; import mage.players.ManaPool; import mage.players.Player; import mage.util.ManaUtil; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + public abstract class ManaCostImpl extends CostImpl implements ManaCost { protected Mana payment; @@ -143,33 +144,49 @@ public abstract class ManaCostImpl extends CostImpl implements ManaCost { } } - protected boolean assignGeneric(Ability ability, Game game, ManaPool pool, int mana, Cost costToPay) { - int conditionalCount = pool.getConditionalCount(ability, game, null, costToPay); + protected boolean assignGeneric(Ability ability, Game game, ManaPool pool, int mana, FilterMana filterMana, Cost costToPay) { + int conditionalCount = pool.getConditionalCount(ability, game, filterMana, costToPay); while (mana > payment.count() && (pool.count() > 0 || conditionalCount > 0)) { - if (pool.pay(ManaType.COLORLESS, ability, sourceFilter, game, costToPay, usedManaToPay)) { + // try to use different mana to pay (conditional mana will used in pool.pay) + // filterMana can be null, uses for spells like "spend only black mana on X" + + // {C} + if ((filterMana == null || filterMana.isColorless()) && pool.pay(ManaType.COLORLESS, ability, sourceFilter, game, costToPay, usedManaToPay)) { this.payment.increaseColorless(); continue; } - if (pool.pay(ManaType.BLACK, ability, sourceFilter, game, costToPay, usedManaToPay)) { + + // {B} + if ((filterMana == null || filterMana.isBlack()) && pool.pay(ManaType.BLACK, ability, sourceFilter, game, costToPay, usedManaToPay)) { this.payment.increaseBlack(); continue; } - if (pool.pay(ManaType.BLUE, ability, sourceFilter, game, costToPay, usedManaToPay)) { + + // {U} + if ((filterMana == null || filterMana.isBlue()) && pool.pay(ManaType.BLUE, ability, sourceFilter, game, costToPay, usedManaToPay)) { this.payment.increaseBlue(); continue; } - if (pool.pay(ManaType.WHITE, ability, sourceFilter, game, costToPay, usedManaToPay)) { + + // {W} + if ((filterMana == null || filterMana.isWhite()) && pool.pay(ManaType.WHITE, ability, sourceFilter, game, costToPay, usedManaToPay)) { this.payment.increaseWhite(); continue; } - if (pool.pay(ManaType.GREEN, ability, sourceFilter, game, costToPay, usedManaToPay)) { + + // {G} + if ((filterMana == null || filterMana.isGreen()) && pool.pay(ManaType.GREEN, ability, sourceFilter, game, costToPay, usedManaToPay)) { this.payment.increaseGreen(); continue; } - if (pool.pay(ManaType.RED, ability, sourceFilter, game, costToPay, usedManaToPay)) { + + // {R} + if ((filterMana == null || filterMana.isRed()) && pool.pay(ManaType.RED, ability, sourceFilter, game, costToPay, usedManaToPay)) { this.payment.increaseRed(); continue; } + + // nothing to pay break; } return mana > payment.count(); @@ -208,14 +225,14 @@ public abstract class ManaCostImpl extends CostImpl implements ManaCost { } Player player = game.getPlayer(controllerId); if (!player.getManaPool().isForcedToPay()) { - assignPayment(game, ability, player.getManaPool(), costToPay); + assignPayment(game, ability, player.getManaPool(), costToPay != null ? costToPay : this); } game.getState().getSpecialActions().removeManaActions(); while (!isPaid()) { ManaCost unpaid = this.getUnpaid(); String promptText = ManaUtil.addSpecialManaPayAbilities(ability, game, unpaid); if (player.playMana(ability, unpaid, promptText, game)) { - assignPayment(game, ability, player.getManaPool(), costToPay); + assignPayment(game, ability, player.getManaPool(), costToPay != null ? costToPay : this); } else { return false; } diff --git a/Mage/src/main/java/mage/abilities/costs/mana/ManaCostsImpl.java b/Mage/src/main/java/mage/abilities/costs/mana/ManaCostsImpl.java index 7ef9ad7eb52..103dae1dd93 100644 --- a/Mage/src/main/java/mage/abilities/costs/mana/ManaCostsImpl.java +++ b/Mage/src/main/java/mage/abilities/costs/mana/ManaCostsImpl.java @@ -232,7 +232,7 @@ public class ManaCostsImpl extends ArrayList implements M public void setX(int xValue, int xPay) { List variableCosts = getVariableCosts(); if (!variableCosts.isEmpty()) { - variableCosts.get(0).setAmount(xValue, xPay); + variableCosts.get(0).setAmount(xValue, xPay, false); } } diff --git a/Mage/src/main/java/mage/abilities/costs/mana/MonoHybridManaCost.java b/Mage/src/main/java/mage/abilities/costs/mana/MonoHybridManaCost.java index c53d551e276..e7c15468e3c 100644 --- a/Mage/src/main/java/mage/abilities/costs/mana/MonoHybridManaCost.java +++ b/Mage/src/main/java/mage/abilities/costs/mana/MonoHybridManaCost.java @@ -1,8 +1,5 @@ - package mage.abilities.costs.mana; -import java.util.ArrayList; -import java.util.List; import mage.Mana; import mage.abilities.Ability; import mage.abilities.costs.Cost; @@ -10,6 +7,9 @@ import mage.constants.ColoredManaSymbol; import mage.game.Game; import mage.players.ManaPool; +import java.util.ArrayList; +import java.util.List; + public class MonoHybridManaCost extends ManaCostImpl { private final ColoredManaSymbol mana; @@ -45,7 +45,7 @@ public class MonoHybridManaCost extends ManaCostImpl { @Override public void assignPayment(Game game, Ability ability, ManaPool pool, Cost costToPay) { if (!assignColored(ability, game, pool, mana, costToPay)) { - assignGeneric(ability, game, pool, mana2, costToPay); + assignGeneric(ability, game, pool, mana2, null, costToPay); } } diff --git a/Mage/src/main/java/mage/abilities/costs/mana/SnowManaCost.java b/Mage/src/main/java/mage/abilities/costs/mana/SnowManaCost.java index 5b1d9dfafa4..c23881f063d 100644 --- a/Mage/src/main/java/mage/abilities/costs/mana/SnowManaCost.java +++ b/Mage/src/main/java/mage/abilities/costs/mana/SnowManaCost.java @@ -1,4 +1,3 @@ - package mage.abilities.costs.mana; import mage.Mana; @@ -36,7 +35,7 @@ public class SnowManaCost extends ManaCostImpl { @Override public void assignPayment(Game game, Ability ability, ManaPool pool, Cost costToPay) { - this.assignGeneric(ability, game, pool, 1, costToPay); + this.assignGeneric(ability, game, pool, 1, null, costToPay); } @Override diff --git a/Mage/src/main/java/mage/abilities/costs/mana/VariableManaCost.java b/Mage/src/main/java/mage/abilities/costs/mana/VariableManaCost.java index 81ed89b2f40..8aa8749c363 100644 --- a/Mage/src/main/java/mage/abilities/costs/mana/VariableManaCost.java +++ b/Mage/src/main/java/mage/abilities/costs/mana/VariableManaCost.java @@ -10,13 +10,20 @@ import mage.game.Game; import mage.players.ManaPool; /** - * @author BetaSteward_at_googlemail.com + * @author BetaSteward_at_googlemail.com, JayDi85 */ -public class VariableManaCost extends ManaCostImpl implements VariableCost { +public final class VariableManaCost extends ManaCostImpl implements VariableCost { - protected int xInstancesCount; // number of {X} + // variable mana cost usage on 2019-06-20: + // 1. as X value in spell/ability cast (announce X, set VariableManaCost as paid and add generic mana to pay instead) + // 2. as X value in direct pay (X already announced, cost is unpaid, need direct pay) + + protected int xInstancesCount; // number of {X} instances in cost like {X} or {X}{X} protected int xValue = 0; // final X value after announce and replace events - protected FilterMana filter; + protected int xPay = 0; // final/total need pay after announce and replace events (example: {X}{X}, X=3, xPay = 6) + protected boolean wasAnnounced = false; + + protected FilterMana filter; // mana filter that can be used for that cost protected int minX = 0; protected int maxX = Integer.MAX_VALUE; @@ -34,6 +41,8 @@ public class VariableManaCost extends ManaCostImpl implements VariableCost { super(manaCost); this.xInstancesCount = manaCost.xInstancesCount; this.xValue = manaCost.xValue; + this.xPay = manaCost.xPay; + this.wasAnnounced = manaCost.wasAnnounced; if (manaCost.filter != null) { this.filter = manaCost.filter.copy(); } @@ -48,9 +57,8 @@ public class VariableManaCost extends ManaCostImpl implements VariableCost { @Override public void assignPayment(Game game, Ability ability, ManaPool pool, Cost costToPay) { - payment.add(pool.getMana(filter)); - payment.add(pool.getAllConditionalMana(ability, game, filter)); - pool.payX(ability, game, filter); + // X mana cost always pays as generic mana + this.assignGeneric(ability, game, pool, xPay, filter, costToPay); } @Override @@ -66,6 +74,14 @@ public class VariableManaCost extends ManaCostImpl implements VariableCost { } } + @Override + public boolean isPaid() { + if (!wasAnnounced) return false; + if (paid) return true; + + return this.isColorlessPaid(xPay); + } + @Override public VariableManaCost getUnpaid() { return this; @@ -74,19 +90,23 @@ public class VariableManaCost extends ManaCostImpl implements VariableCost { @Override public int getAmount() { // must return X value - //return payment.count() / multiplier; return this.xValue; } @Override - public void setAmount(int xValue, int xPay) { + public void setAmount(int xValue, int xPay, boolean isPayed) { + // xPay is total pay value (X * instances) this.xValue = xValue; - payment.setGeneric(xPay); + this.xPay = xPay; + if (isPayed) { + payment.setGeneric(xPay); + } + this.wasAnnounced = true; } @Override public boolean testPay(Mana testMana) { - return true; + return true; // TODO: need rework to generic mana style? } @Override @@ -121,27 +141,27 @@ public class VariableManaCost extends ManaCostImpl implements VariableCost { @Override public int announceXValue(Ability source, Game game) { - throw new UnsupportedOperationException("Not supported."); //To change body of generated methods, choose Tools | Templates. + throw new UnsupportedOperationException("Not supported."); } @Override public Cost getFixedCostsFromAnnouncedValue(int xValue) { - throw new UnsupportedOperationException("Not supported."); //To change body of generated methods, choose Tools | Templates. + throw new UnsupportedOperationException("Not supported."); } @Override public String getActionText() { - throw new UnsupportedOperationException("Not supported."); //To change body of generated methods, choose Tools | Templates. + throw new UnsupportedOperationException("Not supported."); } @Override public int getMinValue(Ability source, Game game) { - throw new UnsupportedOperationException("Not supported."); //To change body of generated methods, choose Tools | Templates. + throw new UnsupportedOperationException("Not supported."); } @Override public int getMaxValue(Ability source, Game game) { - throw new UnsupportedOperationException("Not supported."); //To change body of generated methods, choose Tools | Templates. + throw new UnsupportedOperationException("Not supported."); } public FilterMana getFilter() { diff --git a/Mage/src/main/java/mage/abilities/effects/common/CounterUnlessPaysEffect.java b/Mage/src/main/java/mage/abilities/effects/common/CounterUnlessPaysEffect.java index 8a896dd8f70..bb1cbd939fd 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/CounterUnlessPaysEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/CounterUnlessPaysEffect.java @@ -1,10 +1,8 @@ - package mage.abilities.effects.common; import mage.abilities.Ability; import mage.abilities.Mode; import mage.abilities.costs.Cost; -import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.costs.mana.ManaCost; import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.effects.OneShotEffect; @@ -12,9 +10,9 @@ import mage.constants.Outcome; import mage.game.Game; import mage.game.stack.StackObject; import mage.players.Player; +import mage.util.ManaUtil; /** - * * @author BetaSteward_at_googlemail.com */ public class CounterUnlessPaysEffect extends OneShotEffect { @@ -54,23 +52,27 @@ public class CounterUnlessPaysEffect extends OneShotEffect { Player player = game.getPlayer(spell.getControllerId()); if (player != null) { Cost costToPay; + String costValueMessage; if (cost != null) { costToPay = cost.copy(); + costValueMessage = costToPay.getText(); } else { - costToPay = new GenericManaCost(genericMana.calculate(game, source, this)); + costValueMessage = "{" + genericMana.calculate(game, source, this) + "}"; + costToPay = ManaUtil.createManaCost(genericMana, game, source, this); } String message; if (costToPay instanceof ManaCost) { - message = "Would you like to pay " + costToPay.getText() + " to prevent counter effect?"; + message = "Would you like to pay " + costValueMessage + " to prevent counter effect?"; } else { - message = costToPay.getText() + " to prevent counter effect?"; + message = costValueMessage + " to prevent counter effect?"; } + costToPay.clearPaid(); if (!(player.chooseUse(Outcome.Benefit, message, source, game) && costToPay.pay(source, game, spell.getSourceId(), spell.getControllerId(), false, null))) { - game.informPlayers(player.getLogName() + " chooses not to pay " + costToPay.getText() + " to prevent the counter effect"); + game.informPlayers(player.getLogName() + " chooses not to pay " + costValueMessage + " to prevent the counter effect"); return game.getStack().counter(spell.getId(), source.getSourceId(), game); } - game.informPlayers(player.getLogName() + " chooses to pay " + costToPay.getText() + " to prevent the counter effect"); + game.informPlayers(player.getLogName() + " chooses to pay " + costValueMessage + " to prevent the counter effect"); return true; } } diff --git a/Mage/src/main/java/mage/abilities/mana/builder/common/InstantOrSorcerySpellManaBuilder.java b/Mage/src/main/java/mage/abilities/mana/builder/common/InstantOrSorcerySpellManaBuilder.java index 6f6f61f23f7..62b2fbf0035 100644 --- a/Mage/src/main/java/mage/abilities/mana/builder/common/InstantOrSorcerySpellManaBuilder.java +++ b/Mage/src/main/java/mage/abilities/mana/builder/common/InstantOrSorcerySpellManaBuilder.java @@ -1,11 +1,5 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package mage.abilities.mana.builder.common; -import java.util.UUID; import mage.ConditionalMana; import mage.MageObject; import mage.Mana; @@ -17,8 +11,9 @@ import mage.abilities.mana.builder.ConditionalManaBuilder; import mage.abilities.mana.conditional.ManaCondition; import mage.game.Game; +import java.util.UUID; + /** - * * @author LevelX2 */ public class InstantOrSorcerySpellManaBuilder extends ConditionalManaBuilder { @@ -49,9 +44,7 @@ class InstantOrSorceryCastManaCondition extends ManaCondition implements Conditi public boolean apply(Game game, Ability source) { if (source instanceof SpellAbility) { MageObject object = game.getObject(source.getSourceId()); - if (object != null && (object.isInstant() || object.isSorcery())) { - return true; - } + return object != null && (object.isInstant() || object.isSorcery()); } return false; } diff --git a/Mage/src/main/java/mage/abilities/mana/builder/common/SimpleActivatedAbilityManaBuilder.java b/Mage/src/main/java/mage/abilities/mana/builder/common/SimpleActivatedAbilityManaBuilder.java new file mode 100644 index 00000000000..86e7b3bbe48 --- /dev/null +++ b/Mage/src/main/java/mage/abilities/mana/builder/common/SimpleActivatedAbilityManaBuilder.java @@ -0,0 +1,53 @@ +package mage.abilities.mana.builder.common; + +import mage.ConditionalMana; +import mage.Mana; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.condition.Condition; +import mage.abilities.costs.Cost; +import mage.abilities.mana.builder.ConditionalManaBuilder; +import mage.abilities.mana.conditional.ManaCondition; +import mage.game.Game; + +import java.util.UUID; + +/** + * testing class + * + * @author JayDi85 + */ +public class SimpleActivatedAbilityManaBuilder extends ConditionalManaBuilder { + + @Override + public ConditionalMana build(Object... options) { + return new SimpleActivatedAbilityConditionalMana(this.mana); + } + + @Override + public String getRule() { + return "Spend this mana only to activate simple abilities"; + } +} + +class SimpleActivatedAbilityConditionalMana extends ConditionalMana { + + public SimpleActivatedAbilityConditionalMana(Mana mana) { + super(mana); + staticText = "Spend this mana only to activate simple abilities"; + addCondition(new SimpleActivatedAbilityManaCondition()); + } +} + +class SimpleActivatedAbilityManaCondition extends ManaCondition implements Condition { + + @Override + public boolean apply(Game game, Ability source) { + return source instanceof SimpleActivatedAbility; + } + + @Override + public boolean apply(Game game, Ability source, UUID originalId, Cost costsToPay) { + return apply(game, source); + } +} diff --git a/Mage/src/main/java/mage/players/ManaPool.java b/Mage/src/main/java/mage/players/ManaPool.java index ee51d6c7036..2c5df2da273 100644 --- a/Mage/src/main/java/mage/players/ManaPool.java +++ b/Mage/src/main/java/mage/players/ManaPool.java @@ -1,12 +1,5 @@ package mage.players; -import java.io.Serializable; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Set; -import java.util.UUID; import mage.ConditionalMana; import mage.Mana; import mage.abilities.Ability; @@ -22,8 +15,10 @@ import mage.game.events.GameEvent.EventType; import mage.game.events.ManaEvent; import mage.game.stack.Spell; +import java.io.Serializable; +import java.util.*; + /** - * * @author BetaSteward_at_googlemail.com */ public class ManaPool implements Serializable { @@ -84,25 +79,24 @@ public class ManaPool implements Serializable { } /** - * - * @param manaType the mana type that should be paid + * @param manaType the mana type that should be paid * @param ability * @param filter * @param game - * @param costToPay complete costs to pay (needed to check conditional mana) + * @param costToPay complete costs to pay (needed to check conditional mana) * @param usedManaToPay the information about what mana was paid * @return */ public boolean pay(ManaType manaType, Ability ability, Filter filter, Game game, Cost costToPay, Mana usedManaToPay) { - if (!isAutoPayment() + if (!isAutoPayment() && manaType != unlockedManaType) { // if manual payment and the needed mana type was not unlocked, nothing will be paid return false; } ManaType possibleAsThoughPoolManaType = null; - if (isAutoPayment() - && isAutoPaymentRestricted() - && !wasManaAddedBeyondStock() + if (isAutoPayment() + && isAutoPaymentRestricted() + && !wasManaAddedBeyondStock() && manaType != unlockedManaType) { // if automatic restricted payment and there is already mana in the pool // and the needed mana type was not unlocked, nothing will be paid @@ -112,7 +106,7 @@ public class ManaPool implements Serializable { possibleAsThoughPoolManaType = game.getContinuousEffects().asThoughMana(manaType, checkItem, ability.getSourceId(), ability, ability.getControllerId(), game); } // Check if it's possible to use mana as thought for the unlocked manatype in the mana pool for this ability - if (possibleAsThoughPoolManaType == null + if (possibleAsThoughPoolManaType == null || possibleAsThoughPoolManaType != unlockedManaType) { return false; // if it's not possible return } @@ -123,21 +117,21 @@ public class ManaPool implements Serializable { lockManaType(); // pay only one mana if mana payment is set to manually return true; } - + for (ManaPoolItem mana : manaItems) { if (filter != null) { if (!filter.match(mana.getSourceObject(), game)) { // Prevent that cost reduction by convoke is filtered out - if (!(mana.getSourceObject() instanceof Spell) + if (!(mana.getSourceObject() instanceof Spell) || ability.getSourceId().equals(mana.getSourceId())) { continue; } } } - if (possibleAsThoughPoolManaType == null - && manaType != unlockedManaType - && isAutoPayment() - && isAutoPaymentRestricted() + if (possibleAsThoughPoolManaType == null + && manaType != unlockedManaType + && isAutoPayment() + && isAutoPaymentRestricted() && mana.count() == mana.getStock()) { // no mana added beyond the stock so don't auto pay this continue; @@ -174,7 +168,7 @@ public class ManaPool implements Serializable { if (mana.isConditional() && mana.getConditionalMana().get(manaType) > 0 && mana.getConditionalMana().apply(ability, game, mana.getSourceId(), costToPay)) { - if (filter == null + if (filter == null || filter.match(mana.getSourceObject(), game)) { return mana.getConditionalMana().get(manaType); } @@ -184,7 +178,7 @@ public class ManaPool implements Serializable { } public int getConditionalCount(Ability ability, Game game, FilterMana filter, Cost costToPay) { - if (ability == null + if (ability == null || getConditionalMana().isEmpty()) { return 0; } @@ -222,7 +216,7 @@ public class ManaPool implements Serializable { for (ManaType manaType : ManaType.values()) { if (!doNotEmptyManaTypes.contains(manaType)) { if (item.get(manaType) > 0) { - if (item.getDuration() != Duration.EndOfTurn + if (item.getDuration() != Duration.EndOfTurn || game.getPhase().getType() == TurnPhase.END) { if (game.replaceEvent(new GameEvent(GameEvent.EventType.EMPTY_MANA_POOL, playerId, null, playerId))) { int amount = item.get(manaType); @@ -236,7 +230,7 @@ public class ManaPool implements Serializable { } if (conditionalItem != null) { if (conditionalItem.get(manaType) > 0) { - if (item.getDuration() != Duration.EndOfTurn + if (item.getDuration() != Duration.EndOfTurn || game.getPhase().getType() == TurnPhase.END) { if (game.replaceEvent(new GameEvent(GameEvent.EventType.EMPTY_MANA_POOL, playerId, null, playerId))) { int amount = conditionalItem.get(manaType); @@ -258,86 +252,6 @@ public class ManaPool implements Serializable { return total; } - private int payX(Ability ability, Game game) { - int total = 0; - Iterator it = manaItems.iterator(); - while (it.hasNext()) { - ManaPoolItem item = it.next(); - if (item.isConditional()) { - ConditionalMana cm = item.getConditionalMana(); - if (cm.apply(ability, game, cm.getManaProducerId(), null)) { - total += item.count(); - it.remove(); - } - } else { - total += item.count(); - it.remove(); - } - } - return total; - } - - /** - * remove all mana from pool that applies and that matches filter - * - * @param ability - * @param game - * @param filter - * @return - */ - public int payX(Ability ability, Game game, FilterMana filter) { - if (filter == null) { - return payX(ability, game); - } - int total = 0; - Iterator it = manaItems.iterator(); - while (it.hasNext()) { - ManaPoolItem item = it.next(); - if (item.isConditional()) { - ConditionalMana c = item.getConditionalMana(); - if (c.apply(ability, game, c.getManaProducerId(), null)) { - int count = c.count(filter); - if (count > 0) { - total += count; - c.removeAll(filter); - if (c.count() == 0) { - it.remove(); - } - } - } - } else { - if (filter.isBlack()) { - total += item.getBlack(); - item.removeBlack(); - } - if (filter.isBlue()) { - total += item.getBlue(); - item.removeBlue(); - } - if (filter.isWhite()) { - total += item.getWhite(); - item.removeWhite(); - } - if (filter.isRed()) { - total += item.getRed(); - item.removeRed(); - } - if (filter.isGreen()) { - total += item.getGreen(); - item.removeGreen(); - } - if (filter.isGeneric()) { - total += item.getColorless(); - item.removeColorless(); - } - if (item.count() == 0) { - it.remove(); - } - } - } - return total; - } - public Mana getMana() { Mana m = new Mana(); for (ManaPoolItem item : manaItems) { @@ -376,12 +290,6 @@ public class ManaPool implements Serializable { return m; } - public Mana getAllConditionalMana(Ability ability, Game game, FilterMana filter) { - Mana m = new Mana(); - m.setGeneric(getConditionalCount(ability, game, filter, null)); - return m; - } - public void addMana(Mana manaToAdd, Game game, Ability source) { addMana(manaToAdd, game, source, false); } @@ -392,7 +300,7 @@ public class ManaPool implements Serializable { if (!game.replaceEvent(new ManaEvent(EventType.ADD_MANA, source.getId(), source.getSourceId(), playerId, mana))) { if (mana instanceof ConditionalMana) { ManaPoolItem item = new ManaPoolItem((ConditionalMana) mana, source.getSourceObject(game), - ((ConditionalMana) mana).getManaProducerOriginalId() != null + ((ConditionalMana) mana).getManaProducerOriginalId() != null ? ((ConditionalMana) mana).getManaProducerOriginalId() : source.getOriginalId()); if (emptyOnTurnsEnd) { item.setDuration(Duration.EndOfTurn); @@ -524,12 +432,12 @@ public class ManaPool implements Serializable { public UUID getPlayerId() { return playerId; } - + public void storeMana() { poolBookmark.clear(); poolBookmark.addAll(getManaItems()); } - + public List getPoolBookmark() { List itemsCopy = new ArrayList<>(); for (ManaPoolItem manaItem : poolBookmark) { @@ -537,7 +445,7 @@ public class ManaPool implements Serializable { } return itemsCopy; } - + public void restoreMana(List manaList) { manaItems.clear(); if (!manaList.isEmpty()) { diff --git a/Mage/src/main/java/mage/util/ManaUtil.java b/Mage/src/main/java/mage/util/ManaUtil.java index 8e49f964403..6496c88bc6a 100644 --- a/Mage/src/main/java/mage/util/ManaUtil.java +++ b/Mage/src/main/java/mage/util/ManaUtil.java @@ -4,9 +4,10 @@ import mage.MageObject; import mage.Mana; import mage.ManaSymbol; import mage.abilities.Ability; -import mage.abilities.costs.mana.AlternateManaPaymentAbility; -import mage.abilities.costs.mana.ManaCost; -import mage.abilities.costs.mana.ManaSymbols; +import mage.abilities.costs.mana.*; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.ManacostVariableValue; +import mage.abilities.effects.Effect; import mage.abilities.mana.*; import mage.cards.Card; import mage.choices.Choice; @@ -494,4 +495,21 @@ public final class ManaUtil { destColors.setGreen(true); } } + + public static ManaCost createManaCost(int manaValue) { + return new GenericManaCost(manaValue); + } + + public static ManaCost createManaCost(DynamicValue manaValue, Game game, Ability sourceAbility, Effect effect) { + int costValue = manaValue.calculate(game, sourceAbility, effect); + if (manaValue instanceof ManacostVariableValue) { + // variable (X must be final value after all events and effects) + VariableManaCost xCost = new VariableManaCost(); + xCost.setAmount(costValue, costValue, false); + return xCost; + } else { + // static/generic + return new GenericManaCost(costValue); + } + } }