diff --git a/forge-game/src/main/java/forge/game/ability/AbilityKey.java b/forge-game/src/main/java/forge/game/ability/AbilityKey.java index 3771456ae85..906aec341a0 100644 --- a/forge-game/src/main/java/forge/game/ability/AbilityKey.java +++ b/forge-game/src/main/java/forge/game/ability/AbilityKey.java @@ -93,7 +93,6 @@ public enum AbilityKey { Mana("Mana"), MergedCards("MergedCards"), Mode("Mode"), - Modifier("Modifier"), MonstrosityAmount("MonstrosityAmount"), NaturalResult("NaturalResult"), NewCard("NewCard"), diff --git a/forge-game/src/main/java/forge/game/ability/AbilityUtils.java b/forge-game/src/main/java/forge/game/ability/AbilityUtils.java index 12fcf1a6b42..46da1e35f55 100644 --- a/forge-game/src/main/java/forge/game/ability/AbilityUtils.java +++ b/forge-game/src/main/java/forge/game/ability/AbilityUtils.java @@ -2220,7 +2220,7 @@ public class AbilityUtils { // Count$IfCastInOwnMainPhase.. // 7/10 if (sq[0].contains("IfCastInOwnMainPhase")) { final PhaseHandler cPhase = game.getPhaseHandler(); - final boolean isMyMain = cPhase.getPhase().isMain() && cPhase.isPlayerTurn(player) && c.getCastFrom() != null; + final boolean isMyMain = cPhase.getPhase().isMain() && cPhase.isPlayerTurn(player) && c.wasCast(); return doXMath(Integer.parseInt(sq[isMyMain ? 1 : 2]), expr, c, ctb); } @@ -2480,7 +2480,6 @@ public class AbilityUtils { // But these aren't really things you count so they'll show up in properties most likely } - //Count$TypesSharedWith [defined] if (sq[0].startsWith("TypesSharedWith")) { Set thisTypes = Sets.newHashSet(c.getType().getCoreTypes()); diff --git a/forge-game/src/main/java/forge/game/ability/SpellAbilityEffect.java b/forge-game/src/main/java/forge/game/ability/SpellAbilityEffect.java index 9ad783e9419..e1ae0b4b4a4 100644 --- a/forge-game/src/main/java/forge/game/ability/SpellAbilityEffect.java +++ b/forge-game/src/main/java/forge/game/ability/SpellAbilityEffect.java @@ -1016,8 +1016,9 @@ public abstract class SpellAbilityEffect { return true; } - public static Player getNewChooser(final SpellAbility sa, final Player activator, final Player loser) { + public static Player getNewChooser(final SpellAbility sa, final Player loser) { // CR 800.4g + final Player activator = sa.getActivatingPlayer(); final PlayerCollection options; if (loser.isOpponentOf(activator)) { options = activator.getOpponents(); diff --git a/forge-game/src/main/java/forge/game/ability/effects/ChooseCardEffect.java b/forge-game/src/main/java/forge/game/ability/effects/ChooseCardEffect.java index 8195a0aa64d..19ee339ae2b 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/ChooseCardEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/ChooseCardEffect.java @@ -103,7 +103,7 @@ public class ChooseCardEffect extends SpellAbilityEffect { CardCollectionView pChoices = choices; CardCollection chosen = new CardCollection(); if (!p.isInGame()) { - p = getNewChooser(sa, activator, p); + p = getNewChooser(sa, p); } if (sa.hasParam("ControlledByPlayer")) { final String param = sa.getParam("ControlledByPlayer"); diff --git a/forge-game/src/main/java/forge/game/ability/effects/ChooseColorEffect.java b/forge-game/src/main/java/forge/game/ability/effects/ChooseColorEffect.java index 289a4bcfa80..8a2e14400c2 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/ChooseColorEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/ChooseColorEffect.java @@ -1,9 +1,12 @@ package forge.game.ability.effects; +import forge.card.ColorSet; import forge.card.MagicColor; import forge.deck.DeckRecognizer; +import forge.game.ability.AbilityUtils; import forge.game.ability.SpellAbilityEffect; import forge.game.card.Card; +import forge.game.card.CardUtil; import forge.game.player.Player; import forge.game.spellability.SpellAbility; import forge.util.Aggregates; @@ -41,6 +44,13 @@ public class ChooseColorEffect extends SpellAbilityEffect { String[] restrictedChoices = sa.getParam("Choices").split(","); colorChoices = Arrays.asList(restrictedChoices); } + if (sa.hasParam("ColorsFrom")) { + ColorSet cs = CardUtil.getColorsFromCards(AbilityUtils.getDefinedCards(card, sa.getParam("ColorsFrom"), sa)); + if (cs.isColorless()) { + return; + } + colorChoices = cs.stream().map(Object::toString).collect(Collectors.toCollection(ArrayList::new)); + } if (sa.hasParam("Exclude")) { for (String s : sa.getParam("Exclude").split(",")) { colorChoices.remove(s); @@ -49,24 +59,22 @@ public class ChooseColorEffect extends SpellAbilityEffect { for (Player p : getTargetPlayers(sa)) { if (!p.isInGame()) { - p = getNewChooser(sa, sa.getActivatingPlayer(), p); + p = getNewChooser(sa, p); } List chosenColors = new ArrayList<>(); - int cntMin = sa.hasParam("TwoColors") ? 2 : 1; + int cntMin = sa.hasParam("UpTo") ? 0 : sa.hasParam("TwoColors") ? 2 : 1; int cntMax = sa.hasParam("TwoColors") ? 2 : sa.hasParam("OrColors") ? colorChoices.size() : 1; String prompt = null; if (cntMax == 1) { prompt = Localizer.getInstance().getMessage("lblChooseAColor"); - } else { - if (cntMax > cntMin) { - if (cntMax >= MagicColor.NUMBER_OR_COLORS) { - prompt = Localizer.getInstance().getMessage("lblAtLastChooseNumColors", Lang.getNumeral(cntMin)); - } else { - prompt = Localizer.getInstance().getMessage("lblChooseSpecifiedRangeColors", Lang.getNumeral(cntMin), Lang.getNumeral(cntMax)); - } + } else if (cntMax > cntMin) { + if (cntMax >= MagicColor.NUMBER_OR_COLORS) { + prompt = Localizer.getInstance().getMessage("lblAtLastChooseNumColors", Lang.getNumeral(cntMin)); } else { - prompt = Localizer.getInstance().getMessage("lblChooseNColors", Lang.getNumeral(cntMax)); + prompt = Localizer.getInstance().getMessage("lblChooseSpecifiedRangeColors", Lang.getNumeral(cntMin), Lang.getNumeral(cntMax)); } + } else { + prompt = Localizer.getInstance().getMessage("lblChooseNColors", Lang.getNumeral(cntMax)); } Player noNotify = p; if (sa.hasParam("Random")) { diff --git a/forge-game/src/main/java/forge/game/ability/effects/ChooseGenericEffect.java b/forge-game/src/main/java/forge/game/ability/effects/ChooseGenericEffect.java index 4441ce160d6..606ff0cb7ce 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/ChooseGenericEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/ChooseGenericEffect.java @@ -62,7 +62,7 @@ public class ChooseGenericEffect extends SpellAbilityEffect { for (Player p : getDefinedPlayersOrTargeted(sa)) { if (!p.isInGame()) { - p = getNewChooser(sa, sa.getActivatingPlayer(), p); + p = getNewChooser(sa, p); } // determine if any of the choices are not valid @@ -116,7 +116,6 @@ public class ChooseGenericEffect extends SpellAbilityEffect { } else if (secretly) { if (record.length() > 0) record.append("\r\n"); record.append(Localizer.getInstance().getMessage("lblPlayerChooseValue", p, chosenValue)); - } if (sa.hasParam("SetChosenMode")) { sa.getHostCard().setChosenMode(chosenValue); diff --git a/forge-game/src/main/java/forge/game/ability/effects/EndTurnEffect.java b/forge-game/src/main/java/forge/game/ability/effects/EndTurnEffect.java index 1abcb9944f5..3aac8c89112 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/EndTurnEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/EndTurnEffect.java @@ -22,7 +22,7 @@ public class EndTurnEffect extends SpellAbilityEffect { final List enders = getDefinedPlayersOrTargeted(sa, "Defined"); Player ender = enders.isEmpty() ? sa.getActivatingPlayer() : enders.get(0); if (!ender.isInGame()) { - ender = getNewChooser(sa, sa.getActivatingPlayer(), ender); + ender = getNewChooser(sa, ender); } if (sa.hasParam("Optional") && !ender.getController().confirmAction(sa, null, Localizer.getInstance().getMessage("lblDoYouWantEndTurn"), null)) { 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 9428ba88667..b5b99aaa103 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 @@ -60,18 +60,21 @@ public class ManaEffect extends SpellAbilityEffect { if (abMana.isComboMana()) { int amount = sa.hasParam("Amount") ? AbilityUtils.calculateAmount(card, sa.getParam("Amount"), sa) : 1; - if(amount <= 0) + if (amount <= 0) continue; - String express = abMana.getExpressChoice(); - String[] colorsProduced = abMana.getComboColors(sa).split(" "); - - final StringBuilder choiceString = new StringBuilder(); - final StringBuilder choiceSymbols = new StringBuilder(); + String combo = abMana.getComboColors(sa); + if (combo.isBlank()) { + return; + } + String[] colorsProduced = combo.split(" "); ColorSet colorOptions = ColorSet.fromNames(colorsProduced); + String express = abMana.getExpressChoice(); String[] colorsNeeded = express.isEmpty() ? null : express.split(" "); boolean differentChoice = abMana.getOrigProduced().contains("Different"); ColorSet fullOptions = colorOptions; + final StringBuilder choiceString = new StringBuilder(); + final StringBuilder choiceSymbols = new StringBuilder(); // Use specifyManaCombo if possible if (colorsNeeded == null && amount > 1 && !sa.hasParam("TwoEach")) { Map choices = chooser.getController().specifyManaCombo(sa, colorOptions, amount, differentChoice); diff --git a/forge-game/src/main/java/forge/game/ability/effects/RollDiceEffect.java b/forge-game/src/main/java/forge/game/ability/effects/RollDiceEffect.java index 18766519796..44c58aac69c 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/RollDiceEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/RollDiceEffect.java @@ -319,7 +319,6 @@ public class RollDiceEffect extends SpellAbilityEffect { for (DieRollResult roll : resultsList) { final Map runParams = AbilityKey.mapFromPlayer(player); runParams.put(AbilityKey.Sides, sides); - runParams.put(AbilityKey.Modifier, modifier); runParams.put(AbilityKey.Result, roll.getModifiedValue()); runParams.put(AbilityKey.NaturalResult, roll.getNaturalValue()); runParams.put(AbilityKey.RolledToVisitAttractions, toVisitAttractions); diff --git a/forge-game/src/main/java/forge/game/card/Card.java b/forge-game/src/main/java/forge/game/card/Card.java index b374fde88bc..fe917410625 100644 --- a/forge-game/src/main/java/forge/game/card/Card.java +++ b/forge-game/src/main/java/forge/game/card/Card.java @@ -2625,8 +2625,7 @@ public class Card extends GameEntity implements Comparable, IHasSVars, ITr || keyword.startsWith("Graft") || keyword.startsWith("Fading") || keyword.startsWith("Vanishing:") || keyword.startsWith("Afterlife") || keyword.startsWith("Hideaway") || keyword.startsWith("Toxic") || keyword.startsWith("Afflict") || keyword.startsWith ("Poisonous") || keyword.startsWith("Rampage") - || keyword.startsWith("Renown") || keyword.startsWith("Annihilator") || keyword.startsWith("Devour") - || keyword.startsWith("Mobilize")) { + || keyword.startsWith("Renown") || keyword.startsWith("Annihilator") || keyword.startsWith("Devour")) { final String[] k = keyword.split(":"); sbLong.append(k[0]).append(" ").append(k[1]).append(" (").append(inst.getReminderText()).append(")"); } else if (keyword.startsWith("Crew")) { @@ -2667,14 +2666,13 @@ public class Card extends GameEntity implements Comparable, IHasSVars, ITr final String[] k = keyword.split(":"); String desc = k.length > 2 ? k[2] : CardType.getPluralType(k[1]); sbLong.append(k[0]).append(" ").append(desc).append(" (").append(inst.getReminderText()).append(")"); - } else if (keyword.equals("Convoke") || keyword.equals("Dethrone")|| keyword.equals("Fear") - || keyword.equals("Melee") || keyword.equals("Improvise")|| keyword.equals("Shroud") - || keyword.equals("Banding") || keyword.equals("Intimidate")|| keyword.equals("Evolve") - || keyword.equals("Exalted") || keyword.equals("Extort")|| keyword.equals("Flanking") - || keyword.equals("Horsemanship") || keyword.equals("Infect")|| keyword.equals("Persist") - || keyword.equals("Phasing") || keyword.equals("Shadow")|| keyword.equals("Skulk") - || keyword.equals("Undying") || keyword.equals("Wither") - || keyword.equals("Bargain") + } else if (keyword.equals("Convoke") || keyword.equals("Dethrone") || keyword.equals("Fear") + || keyword.equals("Melee") || keyword.equals("Improvise") || keyword.equals("Shroud") + || keyword.equals("Banding") || keyword.equals("Intimidate") || keyword.equals("Evolve") + || keyword.equals("Exalted") || keyword.equals("Extort") || keyword.equals("Flanking") + || keyword.equals("Horsemanship") || keyword.equals("Infect") || keyword.equals("Persist") + || keyword.equals("Phasing") || keyword.equals("Shadow") || keyword.equals("Skulk") + || keyword.equals("Undying") || keyword.equals("Wither") || keyword.equals("Bargain") || keyword.equals("Mentor") || keyword.equals("Training")) { if (sb.length() != 0) { sb.append("\r\n"); @@ -2738,7 +2736,7 @@ public class Card extends GameEntity implements Comparable, IHasSVars, ITr || keyword.startsWith("Class") || keyword.startsWith("Blitz") || keyword.startsWith("Specialize") || keyword.equals("Ravenous") || keyword.equals("For Mirrodin") || keyword.startsWith("Craft") - || keyword.startsWith("Landwalk") || keyword.startsWith("Visit")) { + || keyword.startsWith("Landwalk") || keyword.startsWith("Visit") || keyword.startsWith("Mobilize")) { // keyword parsing takes care of adding a proper description } else if (keyword.equals("Read ahead")) { sb.append(Localizer.getInstance().getMessage("lblReadAhead")).append(" (").append(Localizer.getInstance().getMessage("lblReadAheadDesc")); @@ -4817,29 +4815,6 @@ public class Card extends GameEntity implements Comparable, IHasSVars, ITr perpetual.add(p); } - @SuppressWarnings("unchecked") - public final void executePerpetual(Map p) { - final String category = (String) p.get("Category"); - if (category.equals("NewPT")) { - addNewPT((Integer) p.get("Power"), (Integer) p.get("Toughness"), (long) - p.get("Timestamp"), (long) 0); - } else if (category.equals("PTBoost")) { - addPTBoost((Integer) p.get("Power"), (Integer) p.get("Toughness"), (long) - p.get("Timestamp"), (long) 0); - } else if (category.equals("Keywords")) { - boolean removeAll = p.containsKey("RemoveAll") && (boolean) p.get("RemoveAll") == true; - addChangedCardKeywords((List) p.get("AddKeywords"), (List) p.get("RemoveKeywords"), - removeAll, (long) p.get("Timestamp"), null); - } else if (category.equals("Types")) { - addChangedCardTypes((CardType) p.get("AddTypes"), (CardType) p.get("RemoveTypes"), - false, (Set) p.get("RemoveXTypes"), - (long) p.get("Timestamp"), (long) 0, true, false); - } else if (category.equals("Colors")) { - addColor((ColorSet) p.get("Colors"), !(boolean) p.get("Overwrite"), (long) p.get("Timestamp"), - (long) 0, false); - } - } - public final void removePerpetual(final long timestamp) { Map toRemove = Maps.newHashMap(); for (Map p : perpetual) { @@ -4852,14 +4827,14 @@ public class Card extends GameEntity implements Comparable, IHasSVars, ITr } public final void setPerpetual(final Card oldCard) { - final List> perp = oldCard.getPerpetual(); - perpetual = perp; - for (Map p : perp) { - if (p.get("Category").equals("Abilities")) { + perpetual = oldCard.getPerpetual(); + for (Map p : perpetual) { + final String category = (String) p.get("Category"); + if (category.equals("Abilities")) { long timestamp = (long) p.get("Timestamp"); CardTraitChanges ctc = oldCard.getChangedCardTraits().get(timestamp, (long) 0).copy(this, false); addChangedCardTraits(ctc, timestamp, (long) 0); - } else if (p.get("Category").equals("Incorporate")) { + } else if (category.equals("Incorporate")) { long ts = (long) p.get("Timestamp"); final ManaCost cCMC = oldCard.changedCardManaCost.get(ts, (long) 0); addChangedManaCost(cCMC, ts, (long) 0); @@ -4868,7 +4843,24 @@ public class Card extends GameEntity implements Comparable, IHasSVars, ITr if (getFirstSpellAbility() != null) { getFirstSpellAbility().getPayCosts().add(new Cost((String) p.get("Incorporate"), false)); } - } else executePerpetual(p); + } else if (category.equals("NewPT")) { + addNewPT((Integer) p.get("Power"), (Integer) p.get("Toughness"), (long) + p.get("Timestamp"), (long) 0); + } else if (category.equals("PTBoost")) { + addPTBoost((Integer) p.get("Power"), (Integer) p.get("Toughness"), (long) + p.get("Timestamp"), (long) 0); + } else if (category.equals("Keywords")) { + boolean removeAll = p.containsKey("RemoveAll") && (boolean) p.get("RemoveAll") == true; + addChangedCardKeywords((List) p.get("AddKeywords"), (List) p.get("RemoveKeywords"), + removeAll, (long) p.get("Timestamp"), null); + } else if (category.equals("Types")) { + addChangedCardTypes((CardType) p.get("AddTypes"), (CardType) p.get("RemoveTypes"), + false, (Set) p.get("RemoveXTypes"), + (long) p.get("Timestamp"), (long) 0, true, false); + } else if (category.equals("Colors")) { + addColor((ColorSet) p.get("Colors"), !(boolean) p.get("Overwrite"), (long) p.get("Timestamp"), + (long) 0, false); + } } } diff --git a/forge-game/src/main/java/forge/game/card/CardFactoryUtil.java b/forge-game/src/main/java/forge/game/card/CardFactoryUtil.java index 185a2cf43f8..ac57a232cfe 100644 --- a/forge-game/src/main/java/forge/game/card/CardFactoryUtil.java +++ b/forge-game/src/main/java/forge/game/card/CardFactoryUtil.java @@ -1529,7 +1529,7 @@ public class CardFactoryUtil { final String[] k = keyword.split(":"); final String n = k[1]; - final String trigStr = "Mode$ Attacks | ValidCard$ Card.Self | Secondary$ True" + final String trigStr = "Mode$ Attacks | ValidCard$ Card.Self" + " | TriggerDescription$ Mobilize " + n + " (" + inst.getReminderText() + ")"; final String effect = "DB$ Token | TokenAmount$ " + n + " | TokenScript$ r_1_1_warrior" diff --git a/forge-game/src/main/java/forge/game/card/CardProperty.java b/forge-game/src/main/java/forge/game/card/CardProperty.java index 05b2fa73508..cbf7d338f3c 100644 --- a/forge-game/src/main/java/forge/game/card/CardProperty.java +++ b/forge-game/src/main/java/forge/game/card/CardProperty.java @@ -504,18 +504,6 @@ public class CardProperty { return false; } } - } else if (property.startsWith("NotEnchantedBy")) { - if (property.substring(14).equals("Targeted")) { - for (final Card c : AbilityUtils.getDefinedCards(source, "Targeted", spellAbility)) { - if (card.isEnchantedBy(c)) { - return false; - } - } - } else { - if (card.isEnchantedBy(source)) { - return false; - } - } } else if (property.startsWith("Enchanted")) { if (!source.equals(card.getEntityAttachedTo())) { return false; @@ -540,15 +528,6 @@ public class CardProperty { return false; } } - } else if (property.substring(16).equals("AllRemembered")) { - for (final Object rem : source.getRemembered()) { - if (rem instanceof Card) { - final Card c = (Card) rem; - if (!card.canBeAttached(c, null)) { - return false; - } - } - } } else { if (!card.canBeAttached(source, null)) { return false; @@ -556,12 +535,7 @@ public class CardProperty { } } else if (property.startsWith("EquippedBy") || property.startsWith("AttachedBy")) { String prop = property.substring(10); - if (prop.equals("Enchanted")) { - if (source.getEnchantingCard() == null || - !card.hasCardAttachment(source.getEnchantingCard())) { - return false; - } - } else if (!StringUtils.isBlank(prop)) { + if (!StringUtils.isBlank(prop)) { boolean found = false; for (final Card c : AbilityUtils.getDefinedCards(source, prop, spellAbility)) { if (card.hasCardAttachment(c)) { 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 b256fb90ac2..0c4911d3ad0 100644 --- a/forge-game/src/main/java/forge/game/spellability/AbilityManaPart.java +++ b/forge-game/src/main/java/forge/game/spellability/AbilityManaPart.java @@ -17,6 +17,8 @@ */ package forge.game.spellability; +import com.google.common.base.Function; +import com.google.common.collect.Iterators; import com.google.common.collect.Lists; import forge.card.ColorSet; import forge.card.GamePieceType; @@ -48,6 +50,7 @@ import forge.game.zone.ZoneType; import forge.util.TextUtil; import org.apache.commons.lang3.StringUtils; +import java.util.Iterator; import java.util.List; import java.util.Map; @@ -513,7 +516,7 @@ public class AbilityManaPart implements java.io.Serializable { } String produced = this.getOrigProduced(); if (produced.contains("Chosen")) { - produced = produced.replace("Chosen", this.getChosenColor(sa)); + produced = produced.replace("Chosen", getChosenColor(sa, sa.getHostCard().getChosenColors())); } return produced; } @@ -649,11 +652,18 @@ public class AbilityManaPart implements java.io.Serializable { } // replace Chosen for Combo colors if (origProduced.contains("Chosen")) { - origProduced = origProduced.replace("Chosen", getChosenColor(sa)); + origProduced = origProduced.replace("Chosen", getChosenColor(sa, sa.getHostCard().getChosenColors())); } // replace Chosen for Spire colors if (origProduced.contains("ColorID")) { - origProduced = origProduced.replace("ColorID", getChosenColorID(sa)); + Iterator colors = Iterators.transform(sa.getHostCard().getMarkedColors().iterator(), + new Function<>() { + @Override + public String apply(Byte b) { + return MagicColor.toLongString(b); + } + }); + origProduced = origProduced.replace("ColorID", getChosenColor(sa, () -> colors)); } if (origProduced.contains("NotedColors")) { // Should only be used for Paliano, the High City @@ -698,14 +708,14 @@ public class AbilityManaPart implements java.io.Serializable { return sb.length() == 0 ? "" : sb.substring(0, sb.length() - 1); } - public String getChosenColorID(SpellAbility sa) { + public String getChosenColor(SpellAbility sa, Iterable colors) { if (sa == null) { return ""; } Card card = sa.getHostCard(); - if (card != null && card.hasMarkedColor()) { + if (card != null) { StringBuilder values = new StringBuilder(); - for (byte c : card.getMarkedColors()) { + for (String c : colors) { values.append(MagicColor.toShortString(c)).append(" "); } return values.toString(); @@ -713,17 +723,6 @@ public class AbilityManaPart implements java.io.Serializable { return ""; } - public String getChosenColor(SpellAbility sa) { - if (sa == null) { - return ""; - } - Card card = sa.getHostCard(); - if (card != null && card.hasChosenColor()) { - return MagicColor.toShortString(card.getChosenColor()); - } - return ""; - } - public Card getSourceCard() { return sourceCard; } diff --git a/forge-game/src/main/java/forge/game/staticability/StaticAbilityCantAttackBlock.java b/forge-game/src/main/java/forge/game/staticability/StaticAbilityCantAttackBlock.java index acebb977d10..239a964632b 100644 --- a/forge-game/src/main/java/forge/game/staticability/StaticAbilityCantAttackBlock.java +++ b/forge-game/src/main/java/forge/game/staticability/StaticAbilityCantAttackBlock.java @@ -175,7 +175,6 @@ public class StaticAbilityCantAttackBlock { return true; } - public static boolean canBlockTapped(final Card card) { final Game game = card.getGame(); for (final Card ca : game.getCardsIn(ZoneType.STATIC_ABILITIES_SOURCE_ZONES)) { @@ -192,7 +191,6 @@ public class StaticAbilityCantAttackBlock { return false; } - private static boolean applyBlockTapped(final StaticAbility stAb, final Card card) { if (!stAb.matchesValidParam("ValidCard", card)) { return false; diff --git a/forge-game/src/main/java/forge/game/trigger/WrappedAbility.java b/forge-game/src/main/java/forge/game/trigger/WrappedAbility.java index cfa37c30b53..c2d41033abe 100644 --- a/forge-game/src/main/java/forge/game/trigger/WrappedAbility.java +++ b/forge-game/src/main/java/forge/game/trigger/WrappedAbility.java @@ -450,7 +450,7 @@ public class WrappedAbility extends Ability { if (decider != null) { if (!decider.isInGame()) { - decider = SpellAbilityEffect.getNewChooser(sa, getActivatingPlayer(), decider); + decider = SpellAbilityEffect.getNewChooser(sa, decider); } if (!decider.getController().confirmTrigger(this)) { return; diff --git a/forge-gui/res/cardsfolder/a/athreos_god_of_passage.txt b/forge-gui/res/cardsfolder/a/athreos_god_of_passage.txt index 259d4856d53..f2a47027662 100644 --- a/forge-gui/res/cardsfolder/a/athreos_god_of_passage.txt +++ b/forge-gui/res/cardsfolder/a/athreos_god_of_passage.txt @@ -6,6 +6,6 @@ K:Indestructible S:Mode$ Continuous | Affected$ Card.Self | RemoveType$ Creature | CheckSVar$ X | SVarCompare$ LT7 | Description$ As long as your devotion to white and black is less than seven, NICKNAME isn't a creature. SVar:X:Count$DevotionDual.White.Black T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | ValidCard$ Creature.YouOwn+Other | TriggerZones$ Battlefield | Execute$ TrigReturn | TriggerDescription$ Whenever another creature you own dies, return it to your hand unless target opponent pays 3 life. -SVar:TrigReturn:DB$ ChangeZone | Defined$ TriggeredNewCardLKICopy | Origin$ Graveyard | Destination$ Hand | UnlessCost$ PayLife<3> | UnlessPayer$ Targeted | ValidTgts$ Opponent | IsCurse$ True | Hidden$ True +SVar:TrigReturn:DB$ ChangeZone | ThisDefinedAndTgts$ TriggeredNewCardLKICopy | Origin$ Graveyard | Destination$ Hand | UnlessCost$ PayLife<3> | UnlessPayer$ Targeted | ValidTgts$ Opponent | IsCurse$ True SVar:BuffedBy:Permanent.White,Permanent.Black Oracle:Indestructible\nAs long as your devotion to white and black is less than seven, Athreos isn't a creature.\nWhenever another creature you own dies, return it to your hand unless target opponent pays 3 life. diff --git a/forge-gui/res/cardsfolder/b/bravado.txt b/forge-gui/res/cardsfolder/b/bravado.txt index f2ca7f84893..481954d4f2e 100644 --- a/forge-gui/res/cardsfolder/b/bravado.txt +++ b/forge-gui/res/cardsfolder/b/bravado.txt @@ -4,6 +4,6 @@ Types:Enchantment Aura K:Enchant:Creature SVar:AttachAILogic:Pump S:Mode$ Continuous | Affected$ Creature.EnchantedBy | AddPower$ X | AddToughness$ X | Description$ Enchanted creature gets +1/+1 for each other creature you control. -SVar:X:Count$Valid Creature.NotEnchantedBy+YouCtrl +SVar:X:Count$Valid Creature.!EnchantedBy+YouCtrl SVar:BuffedBy:Creature Oracle:Enchant creature\nEnchanted creature gets +1/+1 for each other creature you control. diff --git a/forge-gui/res/cardsfolder/c/conclaves_blessing.txt b/forge-gui/res/cardsfolder/c/conclaves_blessing.txt index 9d1e5586bc7..326ff4b06e0 100644 --- a/forge-gui/res/cardsfolder/c/conclaves_blessing.txt +++ b/forge-gui/res/cardsfolder/c/conclaves_blessing.txt @@ -5,5 +5,5 @@ K:Convoke K:Enchant:Creature SVar:AttachAILogic:Pump S:Mode$ Continuous | Affected$ Card.AttachedBy | AddToughness$ X | Description$ Enchanted creature gets +0/+2 for each other creature you control. -SVar:X:Count$Valid Creature.NotEnchantedBy+YouCtrl/Twice +SVar:X:Count$Valid Creature.!EnchantedBy+YouCtrl/Twice Oracle:Convoke (Your creatures can help cast this spell. Each creature you tap while casting this spell pays for {1} or one mana of that creature's color.)\nEnchant creature\nEnchanted creature gets +0/+2 for each other creature you control. diff --git a/forge-gui/res/cardsfolder/d/due_diligence.txt b/forge-gui/res/cardsfolder/d/due_diligence.txt index a1e35f61dab..5ab1c67d078 100644 --- a/forge-gui/res/cardsfolder/d/due_diligence.txt +++ b/forge-gui/res/cardsfolder/d/due_diligence.txt @@ -4,6 +4,6 @@ Types:Enchantment Aura K:Enchant:Creature SVar:AttachAILogic:Pump T:Mode$ ChangesZone | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigPump | TriggerZones$ Battlefield | TriggerDescription$ When CARDNAME enters, target creature you control other than enchanted creature gets +2/+2 and gains vigilance until end of turn. -SVar:TrigPump:DB$ Pump | ValidTgts$ Creature.YouCtrl+NotEnchantedBy | KW$ Vigilance | TgtPrompt$ Select target creature you control other than enchanted creature | NumAtt$ +2 | NumDef$ +2 +SVar:TrigPump:DB$ Pump | ValidTgts$ Creature.YouCtrl+!EnchantedBy | KW$ Vigilance | TgtPrompt$ Select target creature you control other than enchanted creature | NumAtt$ +2 | NumDef$ +2 S:Mode$ Continuous | Affected$ Creature.EnchantedBy | AddPower$ 2 | AddToughness$ 2 | AddKeyword$ Vigilance | Description$ Enchanted creature gets +2/+2 and has vigilance. Oracle:Enchant creature\nWhen Due Diligence enters, target creature you control other than enchanted creature gets +2/+2 and gains vigilance until end of turn.\nEnchanted creature gets +2/+2 and has vigilance. diff --git a/forge-gui/res/cardsfolder/g/gleam_of_authority.txt b/forge-gui/res/cardsfolder/g/gleam_of_authority.txt index c9bc5405f25..ca513a1d0ce 100644 --- a/forge-gui/res/cardsfolder/g/gleam_of_authority.txt +++ b/forge-gui/res/cardsfolder/g/gleam_of_authority.txt @@ -5,5 +5,5 @@ K:Enchant:Creature SVar:AttachAILogic:Pump S:Mode$ Continuous | Affected$ Creature.EnchantedBy | AddKeyword$ Vigilance | AddAbility$ ABBolster | AddPower$ X | AddToughness$ X | Description$ Enchanted creature gets +1/+1 for each +1/+1 counter on other creatures you control. Enchanted creature has vigilance and "{W}, {T}: Bolster 1." SVar:ABBolster:AB$ PutCounter | Cost$ W T | Bolster$ True | CounterNum$ 1 | CounterType$ P1P1 | SpellDescription$ Bolster 1. -SVar:X:Count$Valid Creature.YouCtrl+NotEnchantedBy$CardCounters.P1P1 +SVar:X:Count$Valid Creature.YouCtrl+!EnchantedBy$CardCounters.P1P1 Oracle:Enchant creature\nEnchanted creature gets +1/+1 for each +1/+1 counter on other creatures you control.\nEnchanted creature has vigilance and "{W}, {T}: Bolster 1." (To bolster 1, choose a creature with the least toughness among creatures you control and put a +1/+1 counter on it.) diff --git a/forge-gui/res/cardsfolder/k/kjeldoran_pride.txt b/forge-gui/res/cardsfolder/k/kjeldoran_pride.txt index 4dc7123b267..7fca75f176e 100644 --- a/forge-gui/res/cardsfolder/k/kjeldoran_pride.txt +++ b/forge-gui/res/cardsfolder/k/kjeldoran_pride.txt @@ -4,6 +4,6 @@ Types:Enchantment Aura K:Enchant:Creature SVar:AttachAILogic:Pump S:Mode$ Continuous | Affected$ Creature.EnchantedBy | AddPower$ 1 | AddToughness$ 2 | Description$ Enchanted creature gets +1/+2. -A:AB$ Attach | Cost$ 2 U | ValidTgts$ Creature.NotEnchantedBy | TgtPrompt$ Select target creature other than enchanted creature | AILogic$ Pump | SpellDescription$ Attach CARDNAME to target creature other than enchanted creature. +A:AB$ Attach | Cost$ 2 U | ValidTgts$ Creature.!EnchantedBy | TgtPrompt$ Select target creature other than enchanted creature | AILogic$ Pump | SpellDescription$ Attach CARDNAME to target creature other than enchanted creature. AI:RemoveDeck:All Oracle:Enchant creature\nEnchanted creature gets +1/+2.\n{2}{U}: Attach Kjeldoran Pride to target creature other than enchanted creature. diff --git a/forge-gui/res/cardsfolder/l/lost_legacy.txt b/forge-gui/res/cardsfolder/l/lost_legacy.txt index 5d96b1b2455..bc368dd644d 100644 --- a/forge-gui/res/cardsfolder/l/lost_legacy.txt +++ b/forge-gui/res/cardsfolder/l/lost_legacy.txt @@ -2,7 +2,7 @@ Name:Lost Legacy ManaCost:1 B B Types:Sorcery A:SP$ NameCard | Defined$ You | ValidCards$ Card.nonLand+nonArtifact | ValidDescription$ nonartifact, nonland | SubAbility$ ExileHand | SpellDescription$ Choose a nonartifact, nonland card name. Search target player's graveyard, hand, and library for any number of cards with that name and exile them. That player shuffles, then draws a card for each card exiled from their hand this way. | StackDescription$ SpellDescription -SVar:ExileHand:DB$ ChangeZone | ValidTgts$ Player | TgtPrompt$ Select target player | RememberTargets$ True | Origin$ Hand | Destination$ Exile | DefinedPlayer$ TargetedPlayer | ChangeType$ Card.NamedCard | ChangeNum$ NumInHand | Chooser$ You | SubAbility$ ExileLib | RememberChanged$ True | StackDescription$ None +SVar:ExileHand:DB$ ChangeZone | ValidTgts$ Player | TgtPrompt$ Select target player | Origin$ Hand | Destination$ Exile | DefinedPlayer$ TargetedPlayer | ChangeType$ Card.NamedCard | ChangeNum$ NumInHand | Chooser$ You | SubAbility$ ExileLib | RememberChanged$ True | StackDescription$ None SVar:ExileLib:DB$ ChangeZone | Origin$ Library | Destination$ Exile | DefinedPlayer$ TargetedPlayer | ChangeType$ Card.NamedCard | ChangeNum$ NumInLib | Chooser$ You | Shuffle$ True | SubAbility$ ExileYard | StackDescription$ None SVar:ExileYard:DB$ ChangeZone | Origin$ Graveyard | Destination$ Exile | DefinedPlayer$ TargetedPlayer | ChangeType$ Card.NamedCard | ChangeNum$ NumInYard | Chooser$ You | Hidden$ True | SubAbility$ Draw | StackDescription$ None SVar:Draw:DB$ Draw | Defined$ TargetedPlayer | NumCards$ X | SubAbility$ DBCleanup | StackDescription$ None diff --git a/forge-gui/res/cardsfolder/o/one_with_the_kami.txt b/forge-gui/res/cardsfolder/o/one_with_the_kami.txt index 74f30456cb2..a3e6cc988d0 100644 --- a/forge-gui/res/cardsfolder/o/one_with_the_kami.txt +++ b/forge-gui/res/cardsfolder/o/one_with_the_kami.txt @@ -4,7 +4,7 @@ Types:Enchantment Aura K:Flash K:Enchant:Creature.YouCtrl:creature you control SVar:AttachAILogic:Pump -T:Mode$ ChangesZone | ValidCard$ Creature.EnchantedBy,Creature.YouCtrl+modified+NotEnchantedBy | Origin$ Battlefield | Destination$ Graveyard | Execute$ TrigToken | TriggerDescription$ Whenever enchanted creature or another modified creature you control dies, create X 1/1 colorless Spirit creature tokens, where X is that creature's power. (Equipment, Auras you control, and counters are modifications.) +T:Mode$ ChangesZone | ValidCard$ Creature.EnchantedBy,Creature.YouCtrl+modified+!EnchantedBy | Origin$ Battlefield | Destination$ Graveyard | Execute$ TrigToken | TriggerDescription$ Whenever enchanted creature or another modified creature you control dies, create X 1/1 colorless Spirit creature tokens, where X is that creature's power. (Equipment, Auras you control, and counters are modifications.) SVar:TrigToken:DB$ Token | TokenScript$ c_1_1_spirit | TokenAmount$ X SVar:X:TriggeredCard$CardPower DeckHas:Ability$Token & Type$Spirit diff --git a/forge-gui/res/cardsfolder/r/rumbling_ruin.txt b/forge-gui/res/cardsfolder/r/rumbling_ruin.txt index 31cc355a7d9..d40090c0a97 100644 --- a/forge-gui/res/cardsfolder/r/rumbling_ruin.txt +++ b/forge-gui/res/cardsfolder/r/rumbling_ruin.txt @@ -2,10 +2,9 @@ Name:Rumbling Ruin ManaCost:5 R Types:Creature Elemental PT:6/6 -T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigStore | TriggerDescription$ When CARDNAME enters, count the number of +1/+1 counters on creatures you control. Creatures your opponents control with power less than or equal to that number can't block this turn. -SVar:TrigStore:DB$ StoreSVar | SVar$ X | Type$ CountSVar | Expression$ Y | SubAbility$ TrigEffect -SVar:TrigEffect:DB$ Effect | StaticAbilities$ KWPump | SpellDescription$ Creatures your opponents control with power less than or equal to that number can't block this turn. -SVar:KWPump:Mode$ Continuous | AffectedZone$ Battlefield | Affected$ Creature.OppCtrl+powerLEX | AddHiddenKeyword$ CARDNAME can't block. | Description$ Creatures your opponents control with power less than or equal to that number can't block this turn. -SVar:X:Number$0 +T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigEffect | TriggerDescription$ When CARDNAME enters, count the number of +1/+1 counters on creatures you control. Creatures your opponents control with power less than or equal to that number can't block this turn. +SVar:TrigEffect:DB$ Effect | StaticAbilities$ KWPump | SetChosenNumber$ Y | SpellDescription$ Creatures your opponents control with power less than or equal to that number can't block this turn. +SVar:KWPump:Mode$ CantBlock | ValidCard$ Creature.OppCtrl+powerLEX | Description$ Creatures your opponents control with power less than or equal to that number can't block this turn. +SVar:X:Count$ChosenNumber SVar:Y:Count$Valid Creature.YouCtrl$CardCounters.P1P1 Oracle:When Rumbling Ruin enters, count the number of +1/+1 counters on creatures you control. Creatures your opponents control with power less than or equal to that number can't block this turn. diff --git a/forge-gui/res/cardsfolder/s/sigil_of_sleep.txt b/forge-gui/res/cardsfolder/s/sigil_of_sleep.txt index 4174d739e02..c26d6f1df6a 100644 --- a/forge-gui/res/cardsfolder/s/sigil_of_sleep.txt +++ b/forge-gui/res/cardsfolder/s/sigil_of_sleep.txt @@ -3,6 +3,6 @@ ManaCost:U Types:Enchantment Aura K:Enchant:Creature SVar:AttachAILogic:Curiosity -T:Mode$ DamageDone | ValidSource$ Creature.AttachedBy | ValidTarget$ Player | Execute$ TrigReturn | TriggerDescription$ Whenever enchanted creature deals damage to a player, return target creature that player controls to its owner's hand. +T:Mode$ DamageDone | ValidSource$ Creature.AttachedBy | ValidTarget$ Player | Execute$ TrigReturn | TriggerZones$ Battlefield | TriggerDescription$ Whenever enchanted creature deals damage to a player, return target creature that player controls to its owner's hand. SVar:TrigReturn:DB$ ChangeZone | ValidTgts$ Creature | TargetsWithDefinedController$ TriggeredTarget | TgtPrompt$ Select target creature your opponent controls | IsCurse$ True | Origin$ Battlefield | Destination$ Hand Oracle:Enchant creature\nWhenever enchanted creature deals damage to a player, return target creature that player controls to its owner's hand. diff --git a/forge-gui/res/cardsfolder/u/unnatural_hunger.txt b/forge-gui/res/cardsfolder/u/unnatural_hunger.txt index 057899a639c..b3a9e1936ed 100644 --- a/forge-gui/res/cardsfolder/u/unnatural_hunger.txt +++ b/forge-gui/res/cardsfolder/u/unnatural_hunger.txt @@ -5,6 +5,6 @@ K:Enchant:Creature SVar:AttachAITgts:Card.powerGE3 SVar:AttachAILogic:Curse T:Mode$ Phase | Phase$ Upkeep | ValidPlayer$ Player.EnchantedController | TriggerZones$ Battlefield | Execute$ TrigDamage | TriggerDescription$ At the beginning of the upkeep of enchanted creature's controller, CARDNAME deals damage equal to that creature's power to that player unless they sacrifice another creature. -SVar:TrigDamage:DB$ DealDamage | Defined$ TriggeredPlayer | NumDmg$ X | UnlessCost$ Sac<1/Creature.NotEnchantedBy> | UnlessPayer$ EnchantedController +SVar:TrigDamage:DB$ DealDamage | Defined$ TriggeredPlayer | NumDmg$ X | UnlessCost$ Sac<1/Creature.!EnchantedBy> | UnlessPayer$ EnchantedController SVar:X:Enchanted$CardPower Oracle:Enchant creature\nAt the beginning of the upkeep of enchanted creature's controller, Unnatural Hunger deals damage equal to that creature's power to that player unless they sacrifice another creature. diff --git a/forge-gui/res/cardsfolder/v/veterans_voice.txt b/forge-gui/res/cardsfolder/v/veterans_voice.txt index 119bbe14ed0..a7f03e5137f 100644 --- a/forge-gui/res/cardsfolder/v/veterans_voice.txt +++ b/forge-gui/res/cardsfolder/v/veterans_voice.txt @@ -3,7 +3,7 @@ ManaCost:R Types:Enchantment Aura K:Enchant:Creature.YouCtrl:creature you control SVar:AttachAILogic:Pump -A:AB$ Pump | Cost$ tapXType<1/Creature.EnchantedBy/Enchanted Creature> | ValidTgts$ Creature.NotEnchantedBy | TgtPrompt$ Select target creature other than the creature tapped | NumAtt$ +2 | NumDef$ +1 | CostDesc$ Tap enchanted creature: | SpellDescription$ Target creature other than the creature tapped this way gets +2/+1 until end of turn. Activate only if enchanted creature is untapped. +A:AB$ Pump | Cost$ tapXType<1/Creature.EnchantedBy/Enchanted Creature> | ValidTgts$ Creature.!EnchantedBy | TgtPrompt$ Select target creature other than the creature tapped | NumAtt$ +2 | NumDef$ +1 | CostDesc$ Tap enchanted creature: | SpellDescription$ Target creature other than the creature tapped this way gets +2/+1 until end of turn. Activate only if enchanted creature is untapped. AI:RemoveDeck:All SVar:NonStackingAttachEffect:True Oracle:Enchant creature you control\nTap enchanted creature: Target creature other than the creature tapped this way gets +2/+1 until end of turn. Activate only if enchanted creature is untapped. diff --git a/forge-gui/src/main/java/forge/gui/card/CardScriptParser.java b/forge-gui/src/main/java/forge/gui/card/CardScriptParser.java index e2507afe970..6f80e3165b9 100644 --- a/forge-gui/src/main/java/forge/gui/card/CardScriptParser.java +++ b/forge-gui/src/main/java/forge/gui/card/CardScriptParser.java @@ -449,8 +449,7 @@ public final class CardScriptParser { "AttachedBy", "Attached", "NameNotEnchantingEnchantedPlayer", "Enchanted", "CanEnchantRemembered", "CanEnchantSource", "CanBeEnchantedBy", "CanBeEnchantedByTargeted", - "CanBeEnchantedByAllRemembered", "EquippedBy", - "EquippedByTargeted", "EquippedByEnchanted", "FortifiedBy", + "EquippedBy", "EquippedByTargeted", "EquippedByEnchanted", "FortifiedBy", "CanBeEquippedBy", "Equipped", "Fortified", "HauntedBy", "notTributed", "madness", "Paired", "PairedWith", "Above", "DirectlyAbove", "TopGraveyardCreature", @@ -488,7 +487,7 @@ public final class CardScriptParser { private static final Set VALID_EXCLUSIVE_STARTSWITH = ImmutableSortedSet .of("named", "notnamed", "OwnedBy", "ControlledBy", "ControllerControls", "AttachedTo", "EnchantedBy", - "NotEnchantedBy", "TopGraveyard", "SharesColorWith", + "TopGraveyard", "SharesColorWith", "MostProminentColor", "notSharesColorWith", "sharesCreatureTypeWith", "sharesCardTypeWith", "sharesLandTypeWith", "sharesNameWith", "doesNotShareNameWith",