diff --git a/forge-ai/src/main/java/forge/ai/ComputerUtilMana.java b/forge-ai/src/main/java/forge/ai/ComputerUtilMana.java index 877a75af6a9..d05d10c0111 100644 --- a/forge-ai/src/main/java/forge/ai/ComputerUtilMana.java +++ b/forge-ai/src/main/java/forge/ai/ComputerUtilMana.java @@ -341,7 +341,7 @@ public class ComputerUtilMana { continue; } - if (canPayShardWithSpellAbility(toPay, ai, paymentChoice, sa, checkCosts)) { + if (canPayShardWithSpellAbility(toPay, ai, paymentChoice, sa, checkCosts, cost.getXManaCostPaidByColor())) { return paymentChoice; } } @@ -547,7 +547,7 @@ public class ComputerUtilMana { } // get a mana of this type from floating, bail if none available - final Mana mana = getMana(ai, part, sa, cost.getSourceRestriction(), (byte) -1); + final Mana mana = getMana(ai, part, sa, cost.getSourceRestriction(), (byte) -1, cost.getXManaCostPaidByColor()); if (mana != null) { if (ai.getManaPool().tryPayCostWithMana(sa, cost, mana, false)) { manaSpentToPay.add(0, mana); @@ -936,7 +936,7 @@ public class ComputerUtilMana { } // get a mana of this type from floating, bail if none available - final Mana mana = getMana(ai, part, sa, cost.getSourceRestriction(), hasConverge ? cost.getColorsPaid() : -1); + final Mana mana = getMana(ai, part, sa, cost.getSourceRestriction(), hasConverge ? cost.getColorsPaid() : -1, cost.getXManaCostPaidByColor()); if (mana != null) { if (ai.getManaPool().tryPayCostWithMana(sa, cost, mana, test)) { manaSpentToPay.add(0, mana); @@ -965,8 +965,10 @@ public class ComputerUtilMana { * a {@link forge.game.spellability.SpellAbility} object. * @return a {@link forge.game.mana.Mana} object. */ - private static Mana getMana(final Player ai, final ManaCostShard shard, final SpellAbility saBeingPaidFor, String restriction, final byte colorsPaid) { - final List> weightedOptions = selectManaToPayFor(ai.getManaPool(), shard, saBeingPaidFor, restriction, colorsPaid); + private static Mana getMana(final Player ai, final ManaCostShard shard, final SpellAbility saBeingPaidFor, + String restriction, final byte colorsPaid, Map xManaCostPaidByColor) { + final List> weightedOptions = selectManaToPayFor(ai.getManaPool(), shard, + saBeingPaidFor, restriction, colorsPaid, xManaCostPaidByColor); // Exclude border case if (weightedOptions.isEmpty()) { @@ -1015,9 +1017,13 @@ public class ComputerUtilMana { } private static List> selectManaToPayFor(final ManaPool manapool, final ManaCostShard shard, - final SpellAbility saBeingPaidFor, String restriction, final byte colorsPaid) { + final SpellAbility saBeingPaidFor, String restriction, final byte colorsPaid, Map xManaCostPaidByColor) { final List> weightedOptions = new ArrayList<>(); for (final Mana thisMana : manapool) { + if (shard == ManaCostShard.COLORED_X && !ManaCostBeingPaid.canColoredXShardBePaidByColor(MagicColor.toShortString(thisMana.getColor()), xManaCostPaidByColor)) { + continue; + } + if (!manapool.canPayForShardWithColor(shard, thisMana.getColor())) { continue; } @@ -1093,7 +1099,7 @@ public class ComputerUtilMana { } } - private static boolean canPayShardWithSpellAbility(ManaCostShard toPay, Player ai, SpellAbility ma, SpellAbility sa, boolean checkCosts) { + private static boolean canPayShardWithSpellAbility(ManaCostShard toPay, Player ai, SpellAbility ma, SpellAbility sa, boolean checkCosts, Map xManaCostPaidByColor) { final Card sourceCard = ma.getHostCard(); if (isManaSourceReserved(ai, sourceCard, sa)) { @@ -1131,6 +1137,10 @@ public class ComputerUtilMana { if (m.isComboMana()) { for (String s : m.getComboColors().split(" ")) { + if (toPay == ManaCostShard.COLORED_X && !ManaCostBeingPaid.canColoredXShardBePaidByColor(s, xManaCostPaidByColor)) { + continue; + } + if ("Any".equals(s) || ai.getManaPool().canPayForShardWithColor(toPay, ManaAtom.fromName(s))) return true; } @@ -1141,6 +1151,9 @@ public class ComputerUtilMana { Set reflected = CardUtil.getReflectableManaColors(ma); for (byte c : MagicColor.WUBRG) { + if (toPay == ManaCostShard.COLORED_X && !ManaCostBeingPaid.canColoredXShardBePaidByColor(MagicColor.toShortString(c), xManaCostPaidByColor)) { + continue; + } if (ai.getManaPool().canPayForShardWithColor(toPay, c) && reflected.contains(MagicColor.toLongString(c))) { m.setExpressChoice(MagicColor.toShortString(c)); return true; @@ -1148,6 +1161,16 @@ public class ComputerUtilMana { } return false; } + + if (toPay == ManaCostShard.COLORED_X) { + for (String s : m.mana().split(" ")) { + if (ManaCostBeingPaid.canColoredXShardBePaidByColor(s, xManaCostPaidByColor)) { + return true; + } + } + return false; + } + return true; } @@ -1434,17 +1457,26 @@ public class ComputerUtilMana { // Tack xMana Payments into mana here if X is a set value if (cost.getXcounter() > 0 || extraMana > 0) { int manaToAdd = 0; + int xCounter = cost.getXcounter(); if (test && extraMana > 0) { - final int multiplicator = Math.max(cost.getXcounter(), 1); + final int multiplicator = Math.max(xCounter, 1); manaToAdd = extraMana * multiplicator; } else { - manaToAdd = AbilityUtils.calculateAmount(card, "X", sa) * cost.getXcounter(); + manaToAdd = AbilityUtils.calculateAmount(card, "X", sa) * xCounter; } - cost.increaseShard(ManaCostShard.parseNonGeneric(sa.getParamOrDefault("XColor", "1")), manaToAdd); + String xColor = sa.getParamOrDefault("XColor", "1"); + if (card.hasKeyword("Spend only colored mana on X. No more than one mana of each color may be spent this way.")) { + xColor = "WUBRGX"; + } + if (xCounter > 0) { + cost.setXManaCostPaid(manaToAdd / xCounter, xColor); + } else { + cost.increaseShard(ManaCostShard.parseNonGeneric(xColor), manaToAdd); + } if (!test) { - sa.setXManaCostPaid(manaToAdd / cost.getXcounter()); + sa.setXManaCostPaid(manaToAdd / xCounter); } } diff --git a/forge-core/src/main/java/forge/card/mana/ManaCostShard.java b/forge-core/src/main/java/forge/card/mana/ManaCostShard.java index d4533f1abc5..a0141250fc0 100644 --- a/forge-core/src/main/java/forge/card/mana/ManaCostShard.java +++ b/forge-core/src/main/java/forge/card/mana/ManaCostShard.java @@ -64,7 +64,10 @@ public enum ManaCostShard { PR(ManaAtom.RED | ManaAtom.OR_2_LIFE, "P/R", "PR"), PG(ManaAtom.GREEN | ManaAtom.OR_2_LIFE, "P/G", "PG"), - X(ManaAtom.IS_X, "X"); + X(ManaAtom.IS_X, "X"), + + // Colored only X, each color can be used to pay for this only once (for Emblazoned Golem) + COLORED_X(ManaAtom.WHITE | ManaAtom.BLUE | ManaAtom.BLACK | ManaAtom.RED | ManaAtom.GREEN | ManaAtom.IS_X, "1"); private final int shard; @@ -297,7 +300,7 @@ public enum ManaCostShard { } public boolean isColor(byte colorCode) { - return (colorCode & this.shard) > 0; + return (colorCode & this.shard) > 0; } public boolean canBePaidWithManaOfColor(byte colorCode) { diff --git a/forge-game/src/main/java/forge/game/mana/ManaCostBeingPaid.java b/forge-game/src/main/java/forge/game/mana/ManaCostBeingPaid.java index 488645a784d..889aa5baa82 100644 --- a/forge-game/src/main/java/forge/game/mana/ManaCostBeingPaid.java +++ b/forge-game/src/main/java/forge/game/mana/ManaCostBeingPaid.java @@ -240,7 +240,7 @@ public class ManaCostBeingPaid { public final boolean isNeeded(final Mana paid, final ManaPool pool) { for (ManaCostShard shard : unpaidShards.keySet()) { - if (canBePaidWith(shard, paid, pool)) { + if (canBePaidWith(shard, paid, pool, xManaCostPaidByColor)) { return true; } } @@ -260,7 +260,7 @@ public class ManaCostBeingPaid { shard = ManaCostShard.GENERIC; } else { - shard = ManaCostShard.valueOf(ManaAtom.fromName(xColor)); + shard = ManaCostShard.parseNonGeneric(xColor); } increaseShard(shard, xCost, true); } @@ -441,6 +441,10 @@ public class ManaCostBeingPaid { Predicate predCanBePaid = new Predicate() { @Override public boolean apply(ManaCostShard ms) { + // Check Colored X and see if the color is already used + if (ms == ManaCostShard.COLORED_X && !canColoredXShardBePaidByColor(MagicColor.toShortString(colorMask), xManaCostPaidByColor)) { + return false; + } return pool.canPayForShardWithColor(ms, colorMask); } }; @@ -465,7 +469,7 @@ public class ManaCostBeingPaid { Predicate predCanBePaid = new Predicate() { @Override public boolean apply(ManaCostShard ms) { - return canBePaidWith(ms, mana, pool); + return canBePaidWith(ms, mana, pool, xManaCostPaidByColor); } }; @@ -552,7 +556,14 @@ public class ManaCostBeingPaid { return 5; } - private static boolean canBePaidWith(final ManaCostShard shard, final Mana mana, final ManaPool pool) { + public static boolean canColoredXShardBePaidByColor(String color, Map xManaCostPaidByColor) { + if (xManaCostPaidByColor != null && xManaCostPaidByColor.get(color) != null) { + return false; + } + return true; + } + + private static boolean canBePaidWith(final ManaCostShard shard, final Mana mana, final ManaPool pool, Map xManaCostPaidByColor) { if (shard.isSnow() && !mana.isSnow()) { return false; } @@ -565,6 +576,11 @@ public class ManaCostBeingPaid { return true; } + // Check Colored X and see if the color is already used + if (shard == ManaCostShard.COLORED_X && !canColoredXShardBePaidByColor(MagicColor.toShortString(mana.getColor()), xManaCostPaidByColor)) { + return false; + } + byte color = mana.getColor(); return pool.canPayForShardWithColor(shard, color); } diff --git a/forge-gui/res/cardsfolder/e/emblazoned_golem.txt b/forge-gui/res/cardsfolder/e/emblazoned_golem.txt new file mode 100644 index 00000000000..16c4155ec10 --- /dev/null +++ b/forge-gui/res/cardsfolder/e/emblazoned_golem.txt @@ -0,0 +1,11 @@ +Name:Emblazoned Golem +ManaCost:2 +Types:Artifact Creature Golem +PT:1/2 +K:Kicker:X +K:Spend only colored mana on X. No more than one mana of each color may be spent this way. +K:etbCounter:P1P1:X:CheckSVar$ WasKicked:If CARDNAME was kicked, it enters the battlefield with X +1/+1 counters on it. +SVar:X:Count$xPaid +SVar:WasKicked:Count$Kicked.1.0 +DeckHas:Ability$Counters +Oracle:Kicker {X} (You may pay an additional {X} as you cast this spell.)\nSpend only colored mana on X. No more than one mana of each color may be spent this way.\nIf Emblazoned Golem was kicked, it enters the battlefield with X +1/+1 counters on it. diff --git a/forge-gui/src/main/java/forge/player/HumanPlay.java b/forge-gui/src/main/java/forge/player/HumanPlay.java index d116656f420..f8f2f87b0a5 100644 --- a/forge-gui/src/main/java/forge/player/HumanPlay.java +++ b/forge-gui/src/main/java/forge/player/HumanPlay.java @@ -708,13 +708,17 @@ public class HumanPlay { ManaCostBeingPaid toPay = new ManaCostBeingPaid(realCost, mc.getRestiction()); String xInCard = source.getSVar("X"); + String xColor = ability.getParam("XColor"); + if (source.hasKeyword("Spend only colored mana on X. No more than one mana of each color may be spent this way.")) { + xColor = "WUBRGX"; + } if (mc.getAmountOfX() > 0 && !"Count$xPaid".equals(xInCard)) { // announce X will overwrite whatever was in card script int xPaid = AbilityUtils.calculateAmount(source, "X", ability); - toPay.setXManaCostPaid(xPaid, ability.getParam("XColor")); + toPay.setXManaCostPaid(xPaid, xColor); ability.setXManaCostPaid(xPaid); } else if (ability.getXManaCostPaid() != null) { //ensure pre-announced X value retained - toPay.setXManaCostPaid(ability.getXManaCostPaid(), ability.getParam("XColor")); + toPay.setXManaCostPaid(ability.getXManaCostPaid(), xColor); } int timesMultikicked = source.getKickerMagnitude(); diff --git a/forge-gui/src/main/java/forge/player/PlayerControllerHuman.java b/forge-gui/src/main/java/forge/player/PlayerControllerHuman.java index f36bf4a921d..92128f2890b 100644 --- a/forge-gui/src/main/java/forge/player/PlayerControllerHuman.java +++ b/forge-gui/src/main/java/forge/player/PlayerControllerHuman.java @@ -396,6 +396,9 @@ public class PlayerControllerHuman extends PlayerController implements IGameCont canChooseZero = false; } } + if (ability.getHostCard().hasKeyword("Spend only colored mana on X. No more than one mana of each color may be spent this way.")) { + max = 5; + } } final int min = canChooseZero ? 0 : 1;