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 7a5583272a1..28a9186be0f 100644 --- a/forge-game/src/main/java/forge/game/ability/AbilityUtils.java +++ b/forge-game/src/main/java/forge/game/ability/AbilityUtils.java @@ -1,6 +1,7 @@ package forge.game.ability; import java.util.ArrayList; +import java.util.Arrays; import java.util.EnumSet; import java.util.HashMap; import java.util.HashSet; @@ -131,7 +132,7 @@ public class AbilityUtils { } else if (defined.equals("EffectSource")) { if (hostCard.isEmblem() || hostCard.getType().hasSubtype("Effect")) { - c = AbilityUtils.findEffectRoot(hostCard); + c = findEffectRoot(hostCard); } } else if (defined.equals("Equipped")) { @@ -510,13 +511,13 @@ public class AbilityUtils { } // modify amount string for text changes - calcX[1] = AbilityUtils.applyAbilityTextChangeEffects(calcX[1], ability); + calcX[1] = applyAbilityTextChangeEffects(calcX[1], ability); Integer val = null; if (calcX[0].startsWith("Count")) { - val = AbilityUtils.xCount(card, calcX[1], ability); + val = xCount(card, calcX[1], ability); } else if (calcX[0].startsWith("Number")) { - val = AbilityUtils.xCount(card, svarval, ability); + val = xCount(card, svarval, ability); } else if (calcX[0].startsWith("SVar")) { final String[] l = calcX[1].split("/"); final String m = CardFactoryUtil.extractOperators(calcX[1]); @@ -683,21 +684,15 @@ public class AbilityUtils { val = objectXCount(objects, calcX[1], card, ability); } else if (calcX[0].startsWith("TargetedController")) { - final List players = new ArrayList<>(); + final PlayerCollection players = new PlayerCollection(); final CardCollection list = getDefinedCards(card, "Targeted", sa); - final List sas = AbilityUtils.getDefinedSpellAbilities(card, "Targeted", sa); + final List sas = getDefinedSpellAbilities(card, "Targeted", sa); for (final Card c : list) { - final Player p = c.getController(); - if (!players.contains(p)) { - players.add(p); - } + players.add(c.getController()); } for (final SpellAbility s : sas) { - final Player p = s.getHostCard().getController(); - if (!players.contains(p)) { - players.add(p); - } + players.add(s.getHostCard().getController()); } val = playerXCount(players, calcX[1], card, ability); } @@ -1052,6 +1047,14 @@ public class AbilityUtils { players.add(((SpellAbility)sa).getActivatingPlayer()); } } + else if (defined.equals("ThisTargetedController")) { + for (final Card c : getDefinedCards(card, "ThisTargetedCard", sa)) { + players.add(c.getController()); + } + for (final SpellAbility s : getDefinedSpellAbilities(card, "ThisTargeted", sa)) { + players.add(s.getActivatingPlayer()); + } + } else if (defined.equals("ParentTargetedController")) { for (final Card c : getDefinedCards(card, "ParentTarget", sa)) { players.add(c.getController()); @@ -1304,22 +1307,29 @@ public class AbilityUtils { else if (defined.equals("Parent") && sa instanceof SpellAbility) { s = ((SpellAbility)sa).getRootAbility(); } - else if (defined.equals("Targeted") && sa instanceof SpellAbility) { - for (TargetChoices tc : ((SpellAbility)sa).getAllTargetChoices()) { - for (SpellAbility targetSpell : tc.getTargetSpells()) { - SpellAbilityStackInstance stackInstance = game.getStack().getInstanceFromSpellAbility(targetSpell); - if (stackInstance != null) { - SpellAbility instanceSA = stackInstance.getSpellAbility(true); - if (instanceSA != null) { - sas.add(instanceSA); - } - } - else { - sas.add(targetSpell); - } + else if (defined.equals("Remembered")) { + for (final Object o : card.getRemembered()) { + if (o instanceof Card) { + final Card rem = (Card) o; + sas.addAll(game.getCardState(rem).getSpellAbilities()); + } else if (o instanceof SpellAbility) { + sas.add((SpellAbility) o); } } } + else if (defined.equals("Imprinted")) { + for (final Card imp : card.getImprintedCards()) { + sas.addAll(imp.getSpellAbilities()); + } + } + else if (defined.equals("EffectSource")) { + if (card.getEffectSourceAbility() != null) { + sas.add(card.getEffectSourceAbility().getRootAbility()); + } + } + else if (defined.equals("SourceFirstSpell")) { + sas.add(card.getFirstSpellAbility()); + } else if (defined.startsWith("Triggered") && sa instanceof SpellAbility) { final SpellAbility root = ((SpellAbility)sa).getRootAbility(); @@ -1345,29 +1355,23 @@ public class AbilityUtils { s = ((SpellAbilityStackInstance) o).getSpellAbility(true); } } - else if (defined.equals("Remembered")) { - for (final Object o : card.getRemembered()) { - if (o instanceof Card) { - final Card rem = (Card) o; - sas.addAll(game.getCardState(rem).getSpellAbilities()); - } else if (o instanceof SpellAbility) { - sas.add((SpellAbility) o); + else if (defined.endsWith("Targeted") && sa instanceof SpellAbility) { + final List targets = defined.startsWith("This") ? Arrays.asList(((SpellAbility)sa).getTargets()) : ((SpellAbility)sa).getAllTargetChoices(); + for (TargetChoices tc : targets) { + for (SpellAbility targetSpell : tc.getTargetSpells()) { + SpellAbilityStackInstance stackInstance = game.getStack().getInstanceFromSpellAbility(targetSpell); + if (stackInstance != null) { + SpellAbility instanceSA = stackInstance.getSpellAbility(true); + if (instanceSA != null) { + sas.add(instanceSA); + } + } + else { + sas.add(targetSpell); + } } } } - else if (defined.equals("Imprinted")) { - for (final Card imp : card.getImprintedCards()) { - sas.addAll(imp.getSpellAbilities()); - } - } - else if (defined.equals("EffectSource")) { - if (card.getEffectSourceAbility() != null) { - sas.add(card.getEffectSourceAbility().getRootAbility()); - } - } - else if (defined.equals("SourceFirstSpell")) { - sas.add(card.getFirstSpellAbility()); - } if (s != null) { sas.add(s); @@ -1503,7 +1507,7 @@ public class AbilityUtils { cost = new Cost(new ManaCost(new ManaCostParser(String.valueOf(source.getChosenNumber()))), true); } else if (unlessCost.startsWith("DefinedCost")) { - CardCollection definedCards = AbilityUtils.getDefinedCards(sa.getHostCard(), unlessCost.split("_")[1], sa); + CardCollection definedCards = getDefinedCards(sa.getHostCard(), unlessCost.split("_")[1], sa); if (definedCards.isEmpty()) { sa.resolve(); resolveSubAbilities(sa, game); @@ -1523,7 +1527,7 @@ public class AbilityUtils { cost = new Cost(newCost.toManaCost(), true); } else if (unlessCost.startsWith("DefinedSACost")) { - FCollection definedSAs = AbilityUtils.getDefinedSpellAbilities(sa.getHostCard(), unlessCost.split("_")[1], sa); + FCollection definedSAs = getDefinedSpellAbilities(sa.getHostCard(), unlessCost.split("_")[1], sa); if (definedSAs.isEmpty()) { sa.resolve(); resolveSubAbilities(sa, game); @@ -1654,7 +1658,7 @@ public class AbilityUtils { * @return a int. */ public static int xCount(final Card c, final String s, final CardTraitBase ctb) { - final String s2 = AbilityUtils.applyAbilityTextChangeEffects(s, ctb); + final String s2 = applyAbilityTextChangeEffects(s, ctb); final String[] l = s2.split("/"); final String expr = CardFactoryUtil.extractOperators(s2); final Player player = ctb == null ? null : ctb instanceof SpellAbility ? ((SpellAbility)ctb).getActivatingPlayer() : ctb.getHostCard().getController(); @@ -1770,7 +1774,7 @@ public class AbilityUtils { //Count$HasNumChosenColors. if (sq[0].contains("HasNumChosenColors")) { int sum = 0; - for (Card card : AbilityUtils.getDefinedCards(c, sq[1], sa)) { + for (Card card : getDefinedCards(c, sq[1], sa)) { sum += CardUtil.getColors(card).getSharedColors(ColorSet.fromNames(c.getChosenColors())).countColors(); } return sum; @@ -1876,7 +1880,7 @@ public class AbilityUtils { // Not optimal but since xCount doesn't take SAs, we need to replicate while we have it // Probably would be best if xCount took an optional SA to use in these circumstances if (sq[0].contains("TargetedLifeTotal")) { - for (Player tgtP : AbilityUtils.getDefinedPlayers(c, "TargetedPlayer", ctb)) { + for (Player tgtP : getDefinedPlayers(c, "TargetedPlayer", ctb)) { return doXMath(tgtP.getLife(), expr, c, ctb); } } @@ -1907,7 +1911,7 @@ public class AbilityUtils { } // end ctb != null if (sq[0].contains("OppsAtLifeTotal")) { - final int lifeTotal = AbilityUtils.calculateAmount(c, sq[1], ctb); + final int lifeTotal = calculateAmount(c, sq[1], ctb); int number = 0; for (final Player opp : player.getOpponents()) { if (opp.getLife() == lifeTotal) { @@ -1920,7 +1924,7 @@ public class AbilityUtils { //Count$SearchedLibrary. if (sq[0].contains("SearchedLibrary")) { int sum = 0; - for (Player p : AbilityUtils.getDefinedPlayers(c, sq[1], ctb)) { + for (Player p : getDefinedPlayers(c, sq[1], ctb)) { sum += p.getLibrarySearched(); } return doXMath(sum, expr, c, ctb); @@ -2021,7 +2025,7 @@ public class AbilityUtils { if (sq[0].startsWith("DamageDoneByPlayerThisTurn")) { int sum = 0; - for (Player p : AbilityUtils.getDefinedPlayers(c, sq[1], ctb)) { + for (Player p : getDefinedPlayers(c, sq[1], ctb)) { sum += c.getReceivedDamageByPlayerThisTurn(p); } return doXMath(sum, expr, c, ctb); @@ -2047,8 +2051,8 @@ public class AbilityUtils { final String type = sq[0].split("_")[1]; boolean found = false; if (c.getCastFrom() != null && c.getCastSA() != null) { - int revealed = AbilityUtils.calculateAmount(c, "Revealed$Valid " + type, c.getCastSA()); - int ctrl = AbilityUtils.calculateAmount(c, "Count$Valid " + type + ".inZoneBattlefield+YouCtrl", c.getCastSA()); + int revealed = calculateAmount(c, "Revealed$Valid " + type, c.getCastSA()); + int ctrl = calculateAmount(c, "Count$Valid " + type + ".inZoneBattlefield+YouCtrl", c.getCastSA()); if (revealed + ctrl >= 1) { found = true; } @@ -2078,7 +2082,7 @@ public class AbilityUtils { if (sq[0].startsWith("AttachedTo")) { final String[] k = l[0].split(" "); int sum = 0; - for (Card card : AbilityUtils.getDefinedCards(c, k[1], ctb)) { + for (Card card : getDefinedCards(c, k[1], ctb)) { // Hateful Eidolon: the script uses LKI so that the attached cards have to be defined // This card needs the spellability ("Auras You control", you refers to the activating player) // CardFactoryUtils.xCount doesn't have the sa parameter, SVar:X:TriggeredCard$Valid cannot handle this @@ -2448,7 +2452,7 @@ public class AbilityUtils { // Count$UrzaLands.. if (sq[0].startsWith("UrzaLands")) { - return doXMath(AbilityUtils.calculateAmount(c, sq[player.hasUrzaLands() ? 1 : 2], ctb), expr, c, ctb); + return doXMath(calculateAmount(c, sq[player.hasUrzaLands() ? 1 : 2], ctb), expr, c, ctb); } ///////////////// @@ -2540,8 +2544,8 @@ public class AbilityUtils { //Count$Random.. if (sq[0].equals("Random")) { - int min = AbilityUtils.calculateAmount(c, sq[1], ctb); - int max = AbilityUtils.calculateAmount(c, sq[2], ctb); + int min = calculateAmount(c, sq[1], ctb); + int max = calculateAmount(c, sq[2], ctb); return MyRandom.getRandom().nextInt(1+max-min) + min; } @@ -3048,7 +3052,7 @@ public class AbilityUtils { secondaryNum = Integer.parseInt(s[1]); } } catch (final Exception e) { - secondaryNum = AbilityUtils.calculateAmount(c, s[1], ctb); + secondaryNum = calculateAmount(c, s[1], ctb); } if (s[0].contains("Plus")) { @@ -3498,7 +3502,7 @@ public class AbilityUtils { if (string.startsWith("FilterControlledBy")) { final String pString = filter[0].substring(18); - FCollectionView controllers = AbilityUtils.getDefinedPlayers(source, pString, ctb); + FCollectionView controllers = getDefinedPlayers(source, pString, ctb); filteredList = CardLists.filterControlledByAsList(filteredList, controllers); filteredString = TextUtil.fastReplace(filteredString, pString, ""); filteredString = TextUtil.fastReplace(filteredString, "FilterControlledBy_", ""); @@ -3603,14 +3607,14 @@ public class AbilityUtils { // Count$InTargetedHand (targeted player's cards in hand) if (sq[0].contains("InTargetedHand")) { - for (Player tgtP : AbilityUtils.getDefinedPlayers(c, "TargetedPlayer", ctb)) { + for (Player tgtP : getDefinedPlayers(c, "TargetedPlayer", ctb)) { someCards.addAll(tgtP.getCardsIn(ZoneType.Hand)); } } // Count$InTargetedHand (targeted player's cards in hand) if (sq[0].contains("InTargetedLibrary")) { - for (Player tgtP : AbilityUtils.getDefinedPlayers(c, "TargetedPlayer", ctb)) { + for (Player tgtP : getDefinedPlayers(c, "TargetedPlayer", ctb)) { someCards.addAll(tgtP.getCardsIn(ZoneType.Library)); } } diff --git a/forge-game/src/main/java/forge/game/ability/effects/LifeLoseEffect.java b/forge-game/src/main/java/forge/game/ability/effects/LifeLoseEffect.java index a73d03c59ca..cf52cbdf88f 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/LifeLoseEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/LifeLoseEffect.java @@ -15,7 +15,6 @@ public class LifeLoseEffect extends SpellAbilityEffect { */ @Override protected String getStackDescription(SpellAbility sa) { - final StringBuilder sb = new StringBuilder(); final int amount = AbilityUtils.calculateAmount(sa.getHostCard(), sa.getParam("LifeAmount"), sa); diff --git a/forge-game/src/main/java/forge/game/player/Player.java b/forge-game/src/main/java/forge/game/player/Player.java index 664b2a68d2d..1cdd6832f38 100644 --- a/forge-game/src/main/java/forge/game/player/Player.java +++ b/forge-game/src/main/java/forge/game/player/Player.java @@ -2203,8 +2203,7 @@ public class Player extends GameEntity implements Comparable { if (incR.length > 1) { final String excR = incR[1]; - final String[] exR = excR.split("\\+"); // Exclusive Restrictions - // are ... + final String[] exR = excR.split("\\+"); // Exclusive Restrictions are ... for (int j = 0; j < exR.length; j++) { if (!hasProperty(exR[j], sourceController, source, spellAbility)) { return false; diff --git a/forge-gui/res/cardsfolder/r/ram_through.txt b/forge-gui/res/cardsfolder/r/ram_through.txt index 7f819088274..01f38a09451 100644 --- a/forge-gui/res/cardsfolder/r/ram_through.txt +++ b/forge-gui/res/cardsfolder/r/ram_through.txt @@ -2,6 +2,6 @@ Name:Ram Through ManaCost:1 G Types:Instant A:SP$ Pump | Cost$ 1 G | ValidTgts$ Creature.YouCtrl | AILogic$ PowerDmg | TgtPrompt$ Select target creature you control | SubAbility$ SoulsDamage | StackDescription$ SpellDescription | SpellDescription$ Target creature you control deals damage equal to its power to target creature you don't control. If the creature you control has trample, excess damage is dealt to that creature's controller instead. -SVar:SoulsDamage:DB$ DealDamage | ValidTgts$ Creature.YouDontCtrl | AILogic$ PowerDmg | TgtPrompt$ Select target creature you don't control | NumDmg$ X | DamageSource$ ParentTarget | ExcessDamage$ TargetedController | ExcessDamageCondition$ Card.withTrample | StackDescription$ None +SVar:SoulsDamage:DB$ DealDamage | ValidTgts$ Creature.YouDontCtrl | AILogic$ PowerDmg | TgtPrompt$ Select target creature you don't control | NumDmg$ X | DamageSource$ ParentTarget | ExcessDamage$ ThisTargetedController | ExcessDamageCondition$ Card.withTrample | StackDescription$ None SVar:X:ParentTargeted$CardPower Oracle:Target creature you control deals damage equal to its power to target creature you don't control. If the creature you control has trample, excess damage is dealt to that creature's controller instead. diff --git a/forge-gui/res/cardsfolder/w/witherbloom_command.txt b/forge-gui/res/cardsfolder/w/witherbloom_command.txt index 907cfe5c585..ae5c2e91c59 100644 --- a/forge-gui/res/cardsfolder/w/witherbloom_command.txt +++ b/forge-gui/res/cardsfolder/w/witherbloom_command.txt @@ -10,4 +10,3 @@ SVar:DBLoseLife:DB$ LoseLife | ValidTgts$ Player.Opponent | TgtPrompt$ Select ta SVar:DBGainLife:DB$ GainLife | LifeAmount$ 2 DeckHas:Ability$LifeGain & Ability$Mill Oracle:Choose two —\n• Target player mills three cards, then you return a land card from your graveyard to your hand.\n• Destroy target noncreature, nonland permanent with mana value 2 or less.\n• Target creature gets -3/-1 until end of turn.\n• Target opponent loses 2 life and you gain 2 life. -