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

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

View file

@ -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 spells total cost, an activated abilitys cost, a suspend cost, or a cost youre
@ -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;
}
}
}

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

View file

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

View file

@ -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")

View file

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

View file

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

View file

@ -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

View file

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

View file

@ -232,7 +232,7 @@ public class ManaCostsImpl<T extends ManaCost> extends ArrayList<T> implements M
public void setX(int xValue, int xPay) {
List<VariableCost> variableCosts = getVariableCosts();
if (!variableCosts.isEmpty()) {
variableCosts.get(0).setAmount(xValue, xPay);
variableCosts.get(0).setAmount(xValue, xPay, false);
}
}

View file

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

View file

@ -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

View file

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

View file

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

View file

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

View file

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

View file

@ -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<ManaPoolItem> 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<ManaPoolItem> 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<ManaPoolItem> getPoolBookmark() {
List<ManaPoolItem> itemsCopy = new ArrayList<>();
for (ManaPoolItem manaItem : poolBookmark) {
@ -537,7 +445,7 @@ public class ManaPool implements Serializable {
}
return itemsCopy;
}
public void restoreMana(List<ManaPoolItem> manaList) {
manaItems.clear();
if (!manaList.isEmpty()) {

View file

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