From 1f752ff21005aefc2064670ee4a0e4e001765a96 Mon Sep 17 00:00:00 2001 From: Chris H Date: Mon, 29 Apr 2019 20:41:36 -0400 Subject: [PATCH 1/2] Add Different colors to combo mana --- .../src/main/java/forge/card/ColorSet.java | 1 + .../game/ability/effects/ManaEffect.java | 210 +++++++++--------- .../game/spellability/AbilityManaPart.java | 38 +--- .../res/cardsfolder/f/firemind_vessel.txt | 7 + .../res/cardsfolder/i/interplanar_beacon.txt | 9 + .../res/cardsfolder/upcoming/guild_globe.txt | 3 +- 6 files changed, 131 insertions(+), 137 deletions(-) create mode 100644 forge-gui/res/cardsfolder/f/firemind_vessel.txt create mode 100644 forge-gui/res/cardsfolder/i/interplanar_beacon.txt diff --git a/forge-core/src/main/java/forge/card/ColorSet.java b/forge-core/src/main/java/forge/card/ColorSet.java index 9458c3ded66..bb27f0bc88d 100644 --- a/forge-core/src/main/java/forge/card/ColorSet.java +++ b/forge-core/src/main/java/forge/card/ColorSet.java @@ -41,6 +41,7 @@ public final class ColorSet implements Comparable, Iterable, Ser private static final long serialVersionUID = 794691267379929080L; private final byte myColor; + public byte getMyColor() { return myColor; } private final float orderWeight; private static final ColorSet[] cache = new ColorSet[32]; diff --git a/forge-game/src/main/java/forge/game/ability/effects/ManaEffect.java b/forge-game/src/main/java/forge/game/ability/effects/ManaEffect.java index 7c598f302f0..6e6d352e7ff 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/ManaEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/ManaEffect.java @@ -58,120 +58,132 @@ public class ManaEffect extends SpellAbilityEffect { if (abMana.isComboMana()) { for (Player p : tgtPlayers) { int amount = sa.hasParam("Amount") ? AbilityUtils.calculateAmount(card, sa.getParam("Amount"), sa) : 1; - if (tgt == null || p.canBeTargetedBy(sa)) { - Player activator = sa.getActivatingPlayer(); - String express = abMana.getExpressChoice(); - String[] colorsProduced = abMana.getComboColors().split(" "); - - final StringBuilder choiceString = new StringBuilder(); - ColorSet colorOptions = null; - String[] colorsNeeded = express.isEmpty() ? null : express.split(" "); - if (!abMana.isAnyMana()) { - colorOptions = ColorSet.fromNames(colorsProduced); - } else { - colorOptions = ColorSet.fromNames(MagicColor.Constant.ONLY_COLORS); - } - final ColorSet fullOptions = colorOptions; - for (int nMana = 1; nMana <= amount; nMana++) { - String choice = ""; - if (colorsNeeded != null && colorsNeeded.length >= nMana) { // select from express choices if possible - colorOptions = ColorSet - .fromMask(fullOptions.getColor() & ManaAtom.fromName(colorsNeeded[nMana - 1])); - } - if (colorOptions.isColorless() && colorsProduced.length > 0) { - // If we just need generic mana, no reason to ask the controller for a choice, - // just use the first possible color. - choice = colorsProduced[0]; - } else { - byte chosenColor = activator.getController().chooseColor("Select Mana to Produce", sa, colorOptions); - if (chosenColor == 0) - throw new RuntimeException("ManaEffect::resolve() /*combo mana*/ - " + activator + " color mana choice is empty for " + card.getName()); - choice = MagicColor.toShortString(chosenColor); - } - - if (nMana != 1) { - choiceString.append(" "); - } - choiceString.append(choice); - } - - if (choiceString.toString().isEmpty() && "Combo ColorIdentity".equals(abMana.getOrigProduced())) { - // No mana could be produced here (non-EDH match?), so cut short - return; - } - - game.action.nofityOfValue(sa, card, activator + " picked " + choiceString, activator); - abMana.setExpressChoice(choiceString.toString()); + if (tgt != null && !p.canBeTargetedBy(sa)) { + // Illegal target. Skip. + continue; } + + Player activator = sa.getActivatingPlayer(); + String express = abMana.getExpressChoice(); + String[] colorsProduced = abMana.getComboColors().split(" "); + + final StringBuilder choiceString = new StringBuilder(); + ColorSet colorOptions = null; + String[] colorsNeeded = express.isEmpty() ? null : express.split(" "); + if (!abMana.isAnyMana()) { + colorOptions = ColorSet.fromNames(colorsProduced); + } else { + colorOptions = ColorSet.fromNames(MagicColor.Constant.ONLY_COLORS); + } + boolean differentChoice = abMana.getOrigProduced().contains("Different"); + ColorSet fullOptions = colorOptions; + for (int nMana = 0; nMana < amount; nMana++) { + String choice = ""; + if (colorsNeeded != null && colorsNeeded.length > nMana) { // select from express choices if possible + colorOptions = ColorSet + .fromMask(fullOptions.getColor() & ManaAtom.fromName(colorsNeeded[nMana])); + } + if (colorOptions.isColorless() && colorsProduced.length > 0) { + // If we just need generic mana, no reason to ask the controller for a choice, + // just use the first possible color. + choice = colorsProduced[differentChoice ? nMana : 0]; + } else { + byte chosenColor = activator.getController().chooseColor("Select Mana to Produce", sa, colorOptions); + if (chosenColor == 0) + throw new RuntimeException("ManaEffect::resolve() /*combo mana*/ - " + activator + " color mana choice is empty for " + card.getName()); + + fullOptions = ColorSet.fromMask(fullOptions.getMyColor() - chosenColor); + choice = MagicColor.toShortString(chosenColor); + } + + if (nMana > 0) { + choiceString.append(" "); + } + choiceString.append(choice); + } + + if (choiceString.toString().isEmpty() && "Combo ColorIdentity".equals(abMana.getOrigProduced())) { + // No mana could be produced here (non-EDH match?), so cut short + return; + } + + game.action.nofityOfValue(sa, card, activator + " picked " + choiceString, activator); + abMana.setExpressChoice(choiceString.toString()); } } else if (abMana.isAnyMana()) { for (Player p : tgtPlayers) { - if (tgt == null || p.canBeTargetedBy(sa)) { - Player act = sa.getActivatingPlayer(); - // AI color choice is set in ComputerUtils so only human players need to make a choice - - String colorsNeeded = abMana.getExpressChoice(); - String choice = ""; - - ColorSet colorMenu = null; - byte mask = 0; - //loop through colors to make menu - for (int nChar = 0; nChar < colorsNeeded.length(); nChar++) { - mask |= MagicColor.fromName(colorsNeeded.charAt(nChar)); - } - colorMenu = mask == 0 ? ColorSet.ALL_COLORS : ColorSet.fromMask(mask); - byte val = p.getController().chooseColor("Select Mana to Produce", sa, colorMenu); - if (0 == val) { - throw new RuntimeException("ManaEffect::resolve() /*any mana*/ - " + act + " color mana choice is empty for " + card.getName()); - } - choice = MagicColor.toShortString(val); - - game.action.nofityOfValue(sa, card, act + " picked " + choice, act); - abMana.setExpressChoice(choice); + if (tgt != null && !p.canBeTargetedBy(sa)) { + // Illegal target. Skip. + continue; } + + Player act = sa.getActivatingPlayer(); + // AI color choice is set in ComputerUtils so only human players need to make a choice + + String colorsNeeded = abMana.getExpressChoice(); + String choice = ""; + + ColorSet colorMenu = null; + byte mask = 0; + //loop through colors to make menu + for (int nChar = 0; nChar < colorsNeeded.length(); nChar++) { + mask |= MagicColor.fromName(colorsNeeded.charAt(nChar)); + } + colorMenu = mask == 0 ? ColorSet.ALL_COLORS : ColorSet.fromMask(mask); + byte val = p.getController().chooseColor("Select Mana to Produce", sa, colorMenu); + if (0 == val) { + throw new RuntimeException("ManaEffect::resolve() /*any mana*/ - " + act + " color mana choice is empty for " + card.getName()); + } + choice = MagicColor.toShortString(val); + + game.action.nofityOfValue(sa, card, act + " picked " + choice, act); + abMana.setExpressChoice(choice); } } else if (abMana.isSpecialMana()) { for (Player p : tgtPlayers) { - if (tgt == null || p.canBeTargetedBy(sa)) { - String type = abMana.getOrigProduced().split("Special ")[1]; + if (tgt != null && !p.canBeTargetedBy(sa)) { + // Illegal target. Skip. + continue; + } - if (type.equals("EnchantedManaCost")) { - Card enchanted = card.getEnchantingCard(); - if (enchanted == null ) + String type = abMana.getOrigProduced().split("Special ")[1]; + + if (type.equals("EnchantedManaCost")) { + Card enchanted = card.getEnchantingCard(); + if (enchanted == null ) + continue; + + StringBuilder sb = new StringBuilder(); + int generic = enchanted.getManaCost().getGenericCost(); + if( generic > 0 ) + sb.append(generic); + + for (ManaCostShard s : enchanted.getManaCost()) { + ColorSet cs = ColorSet.fromMask(s.getColorMask()); + if(cs.isColorless()) continue; - - StringBuilder sb = new StringBuilder(); - int generic = enchanted.getManaCost().getGenericCost(); - if( generic > 0 ) - sb.append(generic); - - for (ManaCostShard s : enchanted.getManaCost()) { - ColorSet cs = ColorSet.fromMask(s.getColorMask()); - if(cs.isColorless()) - continue; - sb.append(' '); - if (cs.isMonoColor()) - sb.append(MagicColor.toShortString(s.getColorMask())); - else /* (cs.isMulticolor()) */ { - byte chosenColor = sa.getActivatingPlayer().getController().chooseColor("Choose a single color from " + s.toString(), sa, cs); - sb.append(MagicColor.toShortString(chosenColor)); - } + sb.append(' '); + if (cs.isMonoColor()) + sb.append(MagicColor.toShortString(s.getColorMask())); + else /* (cs.isMulticolor()) */ { + byte chosenColor = sa.getActivatingPlayer().getController().chooseColor("Choose a single color from " + s.toString(), sa, cs); + sb.append(MagicColor.toShortString(chosenColor)); } - abMana.setExpressChoice(sb.toString().trim()); - } else if (type.equals("LastNotedType")) { - Mana manaType = (Mana) Iterables.getFirst(card.getRemembered(), null); - if (manaType == null) { - return; - } - String cs = manaType.toString(); - abMana.setExpressChoice(cs); } + abMana.setExpressChoice(sb.toString().trim()); + } else if (type.equals("LastNotedType")) { + Mana manaType = (Mana) Iterables.getFirst(card.getRemembered(), null); + if (manaType == null) { + return; + } + String cs = manaType.toString(); + abMana.setExpressChoice(cs); + } - if (abMana.getExpressChoice().isEmpty()) { - System.out.println("AbilityFactoryMana::manaResolve() - special mana effect is empty for " + sa.getHostCard().getName()); - } + if (abMana.getExpressChoice().isEmpty()) { + System.out.println("AbilityFactoryMana::manaResolve() - special mana effect is empty for " + sa.getHostCard().getName()); } } } diff --git a/forge-game/src/main/java/forge/game/spellability/AbilityManaPart.java b/forge-game/src/main/java/forge/game/spellability/AbilityManaPart.java index 91b181a7a6b..9d2d71e1f38 100644 --- a/forge-game/src/main/java/forge/game/spellability/AbilityManaPart.java +++ b/forge-game/src/main/java/forge/game/spellability/AbilityManaPart.java @@ -431,58 +431,22 @@ public class AbilityManaPart implements java.io.Serializable { return this.lastManaProduced; } - /** - *

- * isSnow. - *

- * - * @return a boolean. - */ public final boolean isSnow() { return this.getSourceCard().isSnow(); } - /** - *

- * isAnyMana. - *

- * - * @return a boolean. - */ public boolean isAnyMana() { return this.getOrigProduced().contains("Any"); } - /** - *

- * isComboMana. - *

- * - * @return a boolean. - */ public boolean isComboMana() { return this.getOrigProduced().contains("Combo"); } - /** - *

- * isSpecialMana. - *

- * - * @return a boolean. - */ public boolean isSpecialMana() { return this.getOrigProduced().contains("Special"); } - /** - *

- * canProduce. - *

- * - * @param s - * a {@link java.lang.String} object. - * @return a boolean. - */ + public final boolean canProduce(final String s) { return canProduce(s, null); } diff --git a/forge-gui/res/cardsfolder/f/firemind_vessel.txt b/forge-gui/res/cardsfolder/f/firemind_vessel.txt new file mode 100644 index 00000000000..d9a1397fe1c --- /dev/null +++ b/forge-gui/res/cardsfolder/f/firemind_vessel.txt @@ -0,0 +1,7 @@ +Name:Firemind Vessel +ManaCost:4 +Types:Artifact +K:CARDNAME enters the battlefield tapped. +A:AB$ Mana | Cost$ T | Amount$ 2 | Produced$ Combo AnyDifferent | SpellDescription$ Add two mana of different colors. +AI:RemoveDeck:All +Oracle:Firemind Vessel enters the battlefield tapped.\n{T}: Add two mana of different colors. \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/i/interplanar_beacon.txt b/forge-gui/res/cardsfolder/i/interplanar_beacon.txt new file mode 100644 index 00000000000..7e3b5c73f05 --- /dev/null +++ b/forge-gui/res/cardsfolder/i/interplanar_beacon.txt @@ -0,0 +1,9 @@ +Name:Interplanar Beacon +ManaCost: +Types:Land +T:Mode$ SpellCast | ValidCard$ Planeswalker | ValidActivatingPlayer$ You | TriggerZones$ Battlefield | Execute$ TrigGainLife | TriggerDescription$ Whenever you cast a planeswalker spell, you gain 1 life. +SVar:TrigGainLife:DB$GainLife | Defined$ You | LifeAmount$ 1 +A:AB$ Mana | Cost$ T | Produced$ C | SpellDescription$ Add {C}. +A:AB$ Mana | Cost$ 1 T | Produced$ Combo AnyDifferent | Amount$ 2 | RestrictValid$ Planeswalker | SpellDescription$ Add two mana of different colors. Spend this mana only to cast planeswalker spells. +AI:RemoveDeck:All +Oracle:Whenever you cast a planeswalker spell, you gain 1 life.\n{T}: Add {C}.\n{1}, {T}: Add two mana of different colors. Spend this mana only to cast planeswalker spells. diff --git a/forge-gui/res/cardsfolder/upcoming/guild_globe.txt b/forge-gui/res/cardsfolder/upcoming/guild_globe.txt index 23fae4068c8..8b5dc931b59 100644 --- a/forge-gui/res/cardsfolder/upcoming/guild_globe.txt +++ b/forge-gui/res/cardsfolder/upcoming/guild_globe.txt @@ -3,5 +3,6 @@ ManaCost:2 Types:Artifact T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigDraw | TriggerDescription$ When CARDNAME enters the battlefield, draw a card. SVar:TrigDraw:DB$Draw | Defined$ You | NumCards$ 1 -A:AB$ Mana | Cost$ 2 T Sac<1/CARDNAME> | Produced$ Any | Amount$ 2 | SpellDescription$ Add two mana of different colors. +A:AB$ Mana | Cost$ 2 T Sac<1/CARDNAME> | Produced$ Combo AnyDifferent | Amount$ 2 | SpellDescription$ Add two mana of different colors. +AI:RemoveDeck:All Oracle:When Guild Globe enters the battlefield, draw a card.\n{2}, {T}, Sacrifice Guild Globe: Add two mana of different colors. From ea3cf1a7a82bf413150b83e709f1e920d76c8c64 Mon Sep 17 00:00:00 2001 From: Chris H Date: Tue, 30 Apr 2019 20:56:06 -0400 Subject: [PATCH 2/2] Add Spell to mana restriction --- forge-gui/res/cardsfolder/i/interplanar_beacon.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/forge-gui/res/cardsfolder/i/interplanar_beacon.txt b/forge-gui/res/cardsfolder/i/interplanar_beacon.txt index 7e3b5c73f05..c9d3e3ad802 100644 --- a/forge-gui/res/cardsfolder/i/interplanar_beacon.txt +++ b/forge-gui/res/cardsfolder/i/interplanar_beacon.txt @@ -4,6 +4,6 @@ Types:Land T:Mode$ SpellCast | ValidCard$ Planeswalker | ValidActivatingPlayer$ You | TriggerZones$ Battlefield | Execute$ TrigGainLife | TriggerDescription$ Whenever you cast a planeswalker spell, you gain 1 life. SVar:TrigGainLife:DB$GainLife | Defined$ You | LifeAmount$ 1 A:AB$ Mana | Cost$ T | Produced$ C | SpellDescription$ Add {C}. -A:AB$ Mana | Cost$ 1 T | Produced$ Combo AnyDifferent | Amount$ 2 | RestrictValid$ Planeswalker | SpellDescription$ Add two mana of different colors. Spend this mana only to cast planeswalker spells. +A:AB$ Mana | Cost$ 1 T | Produced$ Combo AnyDifferent | Amount$ 2 | RestrictValid$ Spell.Planeswalker | SpellDescription$ Add two mana of different colors. Spend this mana only to cast planeswalker spells. AI:RemoveDeck:All Oracle:Whenever you cast a planeswalker spell, you gain 1 life.\n{T}: Add {C}.\n{1}, {T}: Add two mana of different colors. Spend this mana only to cast planeswalker spells.