diff --git a/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/SimulatedPlayer2.java b/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/SimulatedPlayer2.java index 53048a737b3..4ee2440d336 100644 --- a/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/SimulatedPlayer2.java +++ b/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/SimulatedPlayer2.java @@ -129,11 +129,12 @@ public class SimulatedPlayer2 extends ComputerPlayer { } } if (variableManaCost != null) { - int multiplier = variableManaCost.getMultiplier(); + int xInstancesCount = variableManaCost.getXInstancesCount(); for (int mana = variableManaCost.getMinX(); mana <= numAvailable; mana++) { - if (mana % multiplier == 0) { // use only values dependant from multiplier - int xAmount = mana / multiplier; + if (mana % xInstancesCount == 0) { // use only values dependant from multiplier + // find possible X value to pay + int xAnnounceValue = mana / xInstancesCount; Ability newAbility = ability.copy(); VariableManaCost varCost = null; for (ManaCost cost : newAbility.getManaCostsToPay()) { @@ -142,13 +143,13 @@ public class SimulatedPlayer2 extends ComputerPlayer { break; // only one VariableManCost per spell (or is it possible to have more?) } } - // add the specific value for x + // find real X value after replace events int xMultiplier = 1; if (newAbility instanceof AbilityImpl) { xMultiplier = ((AbilityImpl) newAbility).handleManaXMultiplier(game, xMultiplier); } - newAbility.getManaCostsToPay().add(new ManaCostsImpl(new StringBuilder("{").append(xAmount).append('}').toString())); - newAbility.getManaCostsToPay().setX(xAmount, xMultiplier); + newAbility.getManaCostsToPay().add(new ManaCostsImpl(new StringBuilder("{").append(xAnnounceValue).append('}').toString())); + newAbility.getManaCostsToPay().setX(xAnnounceValue * xMultiplier, xAnnounceValue * xInstancesCount); if (varCost != null) { varCost.setPaid(); } 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 6532f03dfbd..e226099d9cd 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 @@ -1,8 +1,10 @@ package org.mage.test.cards.continuous; +import mage.abilities.costs.mana.VariableManaCost; import mage.constants.PhaseStep; import mage.constants.Zone; import mage.counters.CounterType; +import org.junit.Assert; import org.junit.Test; import org.mage.test.serverside.base.CardTestPlayerBase; @@ -167,4 +169,42 @@ public class UnboundFlourishingTest extends CardTestPlayerBase { assertAllCommandsUsed(); } + @Test + public void test_VariableManaCost() { + // VariableManaCost contains: + // - number of {X} instances (1, 2, 3) + // - final X value after all replace events + // - total number of pays + // getAmount() must return final X value + // setAmount() must setup final X value + + // test example: {X}{X}, X=3, double X effect from replace event + int xInstancesCount = 2; + int xAnnouncedValue = 3; + int xMultiplier = 2; + VariableManaCost cost = new VariableManaCost(xInstancesCount); + cost.setAmount(xAnnouncedValue * xMultiplier, xAnnouncedValue * xInstancesCount); + + Assert.assertEquals("instances count", xInstancesCount, cost.getXInstancesCount()); + Assert.assertEquals("boosted X value", xAnnouncedValue * xMultiplier, cost.getAmount()); + } + + + @Test + public void test_MultipleXInstances() { + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 2); + // Chalice of the Void enters the battlefield with X charge counters on it. + // Whenever a player casts a spell with converted mana cost equal to the number of charge counters on Chalice of the Void, counter that spell. + addCard(Zone.HAND, playerA, "Chalice of the Void", 1); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Chalice of the Void"); + setChoice(playerA, "X=1"); + checkPermanentCounters("after", 1, PhaseStep.BEGIN_COMBAT, playerA, "Chalice of the Void", CounterType.CHARGE, 1); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + assertAllCommandsUsed(); + } + } \ No newline at end of file diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/ChaliceOfTheVoidTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/ChaliceOfTheVoidTest.java index 2d23dde3b09..9440682efd5 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/ChaliceOfTheVoidTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/ChaliceOfTheVoidTest.java @@ -1,4 +1,3 @@ - package org.mage.test.cards.single; import mage.constants.PhaseStep; @@ -7,7 +6,6 @@ import org.junit.Test; import org.mage.test.serverside.base.CardTestPlayerBase; /** - * * @author LevelX2 */ public class ChaliceOfTheVoidTest extends CardTestPlayerBase { @@ -19,6 +17,7 @@ public class ChaliceOfTheVoidTest extends CardTestPlayerBase { * cmc should be 2 in this case, it shouldnt be countered. * http://boardgames.stackexchange.com/questions/7327/what-is-the-converted-mana-cost-of-a-spell-with-x-when-cast-with-the-miracle-m */ + @Test public void testX1CountsFor2CMC() { addCard(Zone.BATTLEFIELD, playerA, "Swamp", 4); @@ -31,8 +30,10 @@ public class ChaliceOfTheVoidTest extends CardTestPlayerBase { castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Chalice of the Void"); setChoice(playerA, "X=1"); + setStrictChooseMode(true); setStopAt(1, PhaseStep.BEGIN_COMBAT); execute(); + assertAllCommandsUsed(); assertPermanentCount(playerA, "Chalice of the Void", 2); diff --git a/Mage/src/main/java/mage/abilities/AbilityImpl.java b/Mage/src/main/java/mage/abilities/AbilityImpl.java index 485f90e2361..e5bc20104f8 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); + xCosts.setAmount(xValue, xValue); this.getManaCostsToPay().add(xCosts); } else { this.getManaCostsToPay().clear(); @@ -505,7 +505,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); // + variableCost.setAmount(xValue, xValue); ((Cost) variableCost).setPaid(); String message = controller.getLogName() + " announces a value of " + xValue + " (" + variableCost.getActionText() + ')'; announceString.append(message); @@ -574,7 +574,7 @@ public abstract class AbilityImpl implements Ability { if (!noMana) { xValue = controller.announceXMana(variableManaCost.getMinX(), variableManaCost.getMaxX(), xValueMultiplier, "Announce the value for " + variableManaCost.getText(), game, this); - int amountMana = xValue * variableManaCost.getMultiplier(); + int amountMana = xValue * variableManaCost.getXInstancesCount(); StringBuilder manaString = threadLocalBuilder.get(); if (variableManaCost.getFilter() == null || variableManaCost.getFilter().isGeneric()) { manaString.append('{').append(amountMana).append('}'); @@ -603,7 +603,7 @@ public abstract class AbilityImpl implements Ability { } } manaCostsToPay.add(new ManaCostsImpl(manaString.toString())); - manaCostsToPay.setX(xValue, xValueMultiplier); + manaCostsToPay.setX(xValue * xValueMultiplier, amountMana); } variableManaCost.setPaid(); } diff --git a/Mage/src/main/java/mage/abilities/SpellAbility.java b/Mage/src/main/java/mage/abilities/SpellAbility.java index d96699bf3bc..21b8a963334 100644 --- a/Mage/src/main/java/mage/abilities/SpellAbility.java +++ b/Mage/src/main/java/mage/abilities/SpellAbility.java @@ -162,13 +162,15 @@ public class SpellAbility extends ActivatedAbilityImpl { return 0; } + // mana cost instances for (ManaCost manaCost : card.getManaCost()) { if (manaCost instanceof VariableManaCost) { - xMultiplier = ((VariableManaCost) manaCost).getMultiplier(); + xMultiplier = ((VariableManaCost) manaCost).getXInstancesCount(); break; } } + // mana cost final X value boolean hasNonManaXCost = false; for (Cost cost : getCosts()) { if (cost instanceof VariableCost) { diff --git a/Mage/src/main/java/mage/abilities/costs/VariableCost.java b/Mage/src/main/java/mage/abilities/costs/VariableCost.java index 460168d5f8d..5327976c532 100644 --- a/Mage/src/main/java/mage/abilities/costs/VariableCost.java +++ b/Mage/src/main/java/mage/abilities/costs/VariableCost.java @@ -1,27 +1,26 @@ - - package mage.abilities.costs; import mage.abilities.Ability; import mage.game.Game; /** - * * @author BetaSteward_at_googlemail.com */ public interface VariableCost { /** * Returns the variable amount if already set - * + * * @return */ int getAmount(); + /** * Sets the variable amount * - * @param amount + * @param xValue - value of X + * @param xPay - total value of pays for X (X * xMultiplier * xInstancesCount) */ - void setAmount(int amount); + void setAmount(int xValue, int xPay); /** * returns the action text (e.g. "creature cards to exile from your hand", "life to pay") @@ -29,6 +28,7 @@ public interface VariableCost { * @return */ String getActionText(); + /** * Return a min value to announce * @@ -37,6 +37,7 @@ public interface VariableCost { * @return */ int getMinValue(Ability source, Game game); + /** * Returns a max value to announce * @@ -45,13 +46,16 @@ public interface VariableCost { * @return */ int getMaxValue(Ability source, Game game); + /** * Asks the controller to announce the variable value + * * @param source * @param game * @return */ int announceXValue(Ability source, Game game); + /** * Returns a fixed cost with the announced variable value * diff --git a/Mage/src/main/java/mage/abilities/costs/VariableCostImpl.java b/Mage/src/main/java/mage/abilities/costs/VariableCostImpl.java index 42ce7400076..9d9d9804386 100644 --- a/Mage/src/main/java/mage/abilities/costs/VariableCostImpl.java +++ b/Mage/src/main/java/mage/abilities/costs/VariableCostImpl.java @@ -1,7 +1,5 @@ - package mage.abilities.costs; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.mana.ManaAbility; import mage.game.Game; @@ -10,8 +8,9 @@ import mage.players.Player; import mage.target.Target; import mage.target.Targets; +import java.util.UUID; + /** - * * @author LevelX2 */ public abstract class VariableCostImpl implements Cost, VariableCost { @@ -29,10 +28,9 @@ public abstract class VariableCostImpl implements Cost, VariableCost { } /** - * - * @param xText string for the defined value + * @param xText string for the defined value * @param actionText what happens with the value (e.g. "to tap", "to exile - * from your graveyard") + * from your graveyard") */ public VariableCostImpl(String xText, String actionText) { id = UUID.randomUUID(); @@ -125,8 +123,8 @@ public abstract class VariableCostImpl implements Cost, VariableCost { } @Override - public void setAmount(int amount) { - amountPaid = amount; + public void setAmount(int xValue, int xPay) { + amountPaid = xPay; } @Override 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 f1193ab635e..fd98adf8fcc 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); + vmc.setAmount(cmc, cmc); vmc.setPaid(); ability.getManaCostsToPay().add(vmc); } diff --git a/Mage/src/main/java/mage/abilities/costs/mana/ManaCosts.java b/Mage/src/main/java/mage/abilities/costs/mana/ManaCosts.java index 23fe48b3854..6aea5f0101f 100644 --- a/Mage/src/main/java/mage/abilities/costs/mana/ManaCosts.java +++ b/Mage/src/main/java/mage/abilities/costs/mana/ManaCosts.java @@ -24,10 +24,10 @@ public interface ManaCosts extends List, ManaCost { int getX(); /** - * @param xValue announced X value - * @param xMultiplier special X multiplier to change announced X value without pay increase, see Unbound Flourishing + * @param xValue final X value -- announced X * xMultiplier, where xMultiplier can be changed by replace events like Unbound Flourishing) + * @param xPay real number of pay amount (x * xMultiplier * xInstances, where xInstances is number of {X} in pay like 1, 2, 3) */ - void setX(int xValue, int xMultiplier); + void setX(int xValue, int xPay); void load(String mana); 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 241e764b83d..7ef9ad7eb52 100644 --- a/Mage/src/main/java/mage/abilities/costs/mana/ManaCostsImpl.java +++ b/Mage/src/main/java/mage/abilities/costs/mana/ManaCostsImpl.java @@ -229,10 +229,10 @@ public class ManaCostsImpl extends ArrayList implements M } @Override - public void setX(int xValue, int xMultiplier) { + public void setX(int xValue, int xPay) { List variableCosts = getVariableCosts(); if (!variableCosts.isEmpty()) { - variableCosts.get(0).setAmount(xValue * xMultiplier); + variableCosts.get(0).setAmount(xValue, xPay); } } @@ -344,7 +344,12 @@ public class ManaCostsImpl extends ArrayList implements M if (player != null) { game.undo(playerId); this.clearPaid(); - this.setX(referenceCosts.getX(), 1); // TODO: checks Word of Command with Unbound Flourishing's X multiplier + + // TODO: checks Word of Command with Unbound Flourishing's X multiplier + // TODO: checks Word of Command with {X}{X} cards + int xValue = referenceCosts.getX(); + this.setX(xValue, xValue); + player.getManaPool().restoreMana(pool.getPoolBookmark()); game.bookmarkState(); } 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 bc0bb4466e0..81ed89b2f40 100644 --- a/Mage/src/main/java/mage/abilities/costs/mana/VariableManaCost.java +++ b/Mage/src/main/java/mage/abilities/costs/mana/VariableManaCost.java @@ -1,4 +1,3 @@ - package mage.abilities.costs.mana; import mage.Mana; @@ -11,12 +10,12 @@ import mage.game.Game; import mage.players.ManaPool; /** - * * @author BetaSteward_at_googlemail.com */ public class VariableManaCost extends ManaCostImpl implements VariableCost { - protected int multiplier; + protected int xInstancesCount; // number of {X} + protected int xValue = 0; // final X value after announce and replace events protected FilterMana filter; protected int minX = 0; protected int maxX = Integer.MAX_VALUE; @@ -25,15 +24,16 @@ public class VariableManaCost extends ManaCostImpl implements VariableCost { this(1); } - public VariableManaCost(int multiplier) { - this.multiplier = multiplier; + public VariableManaCost(int xInstancesCount) { + this.xInstancesCount = xInstancesCount; this.cost = new Mana(); options.add(new Mana()); } public VariableManaCost(final VariableManaCost manaCost) { super(manaCost); - this.multiplier = manaCost.multiplier; + this.xInstancesCount = manaCost.xInstancesCount; + this.xValue = manaCost.xValue; if (manaCost.filter != null) { this.filter = manaCost.filter.copy(); } @@ -55,9 +55,9 @@ public class VariableManaCost extends ManaCostImpl implements VariableCost { @Override public String getText() { - if (multiplier > 1) { - StringBuilder symbol = new StringBuilder(multiplier); - for (int i = 0; i < multiplier; i++) { + if (xInstancesCount > 1) { + StringBuilder symbol = new StringBuilder(xInstancesCount); + for (int i = 0; i < xInstancesCount; i++) { symbol.append("{X}"); } return symbol.toString(); @@ -73,12 +73,15 @@ public class VariableManaCost extends ManaCostImpl implements VariableCost { @Override public int getAmount() { - return payment.count() / multiplier; + // must return X value + //return payment.count() / multiplier; + return this.xValue; } @Override - public void setAmount(int amount) { - payment.setGeneric(amount); + public void setAmount(int xValue, int xPay) { + this.xValue = xValue; + payment.setGeneric(xPay); } @Override @@ -91,8 +94,8 @@ public class VariableManaCost extends ManaCostImpl implements VariableCost { return new VariableManaCost(this); } - public int getMultiplier() { - return multiplier; + public int getXInstancesCount() { + return this.xInstancesCount; } public int getMinX() {