Improved and fixed X mana cost and pays, mana pool:

* Pay X abilities - fixed that it spends all available mana pool instead only needed;
 * Pay X abilities - added support of interactions with other X effects like Rosheen Meanderer's mana usage for "pay X to prevent";
 * Rosheen Meanderer - fixed that it can't use mana for "you may pay X" like Flameblast Dragon's effect (#5206);
 * Devs: added support to use VariableManaCost to pay X in code (without generic's workaround, use ManaUtil.createManaCost to generate cost to pay);
This commit is contained in:
Oleg Agafonov 2019-06-20 21:18:01 +04:00
parent 500fc935e4
commit 437861ec20
20 changed files with 675 additions and 192 deletions

View file

@ -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());

View file

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

View file

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