diff --git a/forge-ai/src/main/java/forge/ai/ComputerUtil.java b/forge-ai/src/main/java/forge/ai/ComputerUtil.java index 932a3090b31..3ef898ea51f 100644 --- a/forge-ai/src/main/java/forge/ai/ComputerUtil.java +++ b/forge-ai/src/main/java/forge/ai/ComputerUtil.java @@ -1293,7 +1293,6 @@ public class ComputerUtil { && !sa.hasParam("ActivationPhases"); } - //returns true if it's better to wait until blockers are declared). public static boolean castSpellInMain1(final Player ai, final SpellAbility sa) { final Card source = sa.getHostCard(); final SpellAbility sub = sa.getSubAbility(); @@ -1315,6 +1314,7 @@ public class ComputerUtil { return true; } } + final CardCollectionView buffed = ai.getCardsIn(ZoneType.Battlefield); boolean checkThreshold = sa.isSpell() && !ai.hasThreshold() && !source.isInZone(ZoneType.Graveyard); for (Card buffedCard : buffed) { @@ -2716,12 +2716,11 @@ public class ComputerUtil { return ComputerUtilCard.getBestCreatureAI(killables); } - public static int predictDamageFromSpell(final SpellAbility sa, final Player targetPlayer) { + public static int predictDamageFromSpell(SpellAbility ab, final Player targetPlayer) { int damage = -1; // returns -1 if the spell does not deal damage - final Card card = sa.getHostCard(); + final Card card = ab.getHostCard(); - SpellAbility ab = sa; - while (ab != null) { + while (ab != null && targetPlayer.canLoseLife()) { if (ab.getApi() == ApiType.DealDamage) { if (damage == -1) { damage = 0; } // found a damage-dealing spell if (!ab.hasParam("NumDmg")) { diff --git a/forge-ai/src/main/java/forge/ai/ComputerUtilCard.java b/forge-ai/src/main/java/forge/ai/ComputerUtilCard.java index 9127805e45d..308137f97e3 100644 --- a/forge-ai/src/main/java/forge/ai/ComputerUtilCard.java +++ b/forge-ai/src/main/java/forge/ai/ComputerUtilCard.java @@ -823,7 +823,7 @@ public class ComputerUtilCard { } // Changeling are all creature types, they are not interesting for // counting creature types - if (c.hasStartOfKeyword(Keyword.CHANGELING.toString())) { + if (c.getType().hasAllCreatureTypes()) { continue; } // ignore cards that does enter the battlefield as clones 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 d08668ddfd1..275aa4ea28c 100644 --- a/forge-game/src/main/java/forge/game/ability/AbilityUtils.java +++ b/forge-game/src/main/java/forge/game/ability/AbilityUtils.java @@ -1197,7 +1197,7 @@ public class AbilityUtils { } else if (defined.startsWith("Non")) { players.addAll(game.getPlayersInTurnOrder()); - players.removeAll((FCollectionView)getDefinedPlayers(card, defined.substring(3), sa)); + players.removeAll(getDefinedPlayers(card, defined.substring(3), sa)); } else if (defined.equals("EnchantedPlayer")) { final Object o = sa.getHostCard().getEntityAttachedTo(); @@ -2699,8 +2699,7 @@ public class AbilityUtils { } if (sq[0].startsWith("OppTypesInGrave")) { final PlayerCollection opponents = player.getOpponents(); - CardCollection oppCards = new CardCollection(); - oppCards.addAll(opponents.getCardsIn(ZoneType.Graveyard)); + CardCollection oppCards = opponents.getCardsIn(ZoneType.Graveyard); return doXMath(getCardTypesFromList(oppCards), expr, c, ctb); } @@ -3053,7 +3052,7 @@ public class AbilityUtils { newWord = "" + originalWord + " " + newWord; } // use word boundaries and keep negations - return text.replaceAll((isDescriptive ? "(?)" : "") + "\\b(non)?" + originalWord, "$1" + newWord); + return text.replaceAll((isDescriptive ? "(?)" : "") + "(?, IHasSVars { // Enumeration for CMC request types public enum SplitCMCMode { CurrentSideCMC, - CombinedCMC, LeftSplitCMC, RightSplitCMC } @@ -2419,7 +2418,7 @@ public class Card extends GameEntity implements Comparable, IHasSVars { return CardTranslation.translateMultipleDescriptionText(sb.toString(), getName()); } - private String kickerDesc (String keyword, String remText) { + private String kickerDesc(String keyword, String remText) { final StringBuilder sbx = new StringBuilder(); final String[] n = keyword.split(":"); final Cost cost = new Cost(n[1], false); @@ -2467,7 +2466,6 @@ public class Card extends GameEntity implements Comparable, IHasSVars { public String getAbilityText() { return getAbilityText(currentState); } - public String getAbilityText(final CardState state) { final String linebreak = "\r\n\r\n"; boolean useGrayTag = true; @@ -2574,7 +2572,7 @@ public class Card extends GameEntity implements Comparable, IHasSVars { boolean hasMeldEffect = hasSVar("Meld") || Iterables.any(state.getNonManaAbilities(), SpellAbilityPredicates.isApi(ApiType.Meld)); String meld = this.getRules().getMeldWith(); - if (meld != "" && (!hasMeldEffect)) { + if (meld != "" && !hasMeldEffect) { sb.append("\r\n"); sb.append("(Melds with ").append(meld).append(".)"); sb.append("\r\n"); @@ -2694,8 +2692,8 @@ public class Card extends GameEntity implements Comparable, IHasSVars { final Card host = stAb.getHostCard(); String currentName = host.getName(); - String desc1 = TextUtil.fastReplace(stAb.toString(), "CARDNAME", currentName); - String desc = TextUtil.fastReplace(desc1, "NICKNAME", currentName.split(" ")[0].replace(",", "")); + String desc = TextUtil.fastReplace(stAb.toString(), "CARDNAME", currentName); + desc = TextUtil.fastReplace(desc, "NICKNAME", Lang.getInstance().getNickName(currentName)); if (host.getEffectSource() != null) { desc = TextUtil.fastReplace(desc, "EFFECTSOURCE", host.getEffectSource().getName()); } @@ -5916,7 +5914,6 @@ public class Card extends GameEntity implements Comparable, IHasSVars { public final SpellAbility getTokenSpawningAbility() { return tokenSpawningAbility; } - public void setTokenSpawningAbility(SpellAbility sa) { tokenSpawningAbility = sa; } @@ -6606,7 +6603,6 @@ public class Card extends GameEntity implements Comparable, IHasSVars { if (isSplitCard()) { switch(mode) { case CurrentSideCMC: - // TODO: test if this returns combined CMC for the full face (then get rid of CombinedCMC mode?) requestedCMC = getManaCost().getCMC() + xPaid; break; case LeftSplitCMC: @@ -6615,11 +6611,6 @@ public class Card extends GameEntity implements Comparable, IHasSVars { case RightSplitCMC: requestedCMC = getState(CardStateName.RightSplit).getManaCost().getCMC() + xPaid; break; - case CombinedCMC: - requestedCMC += getState(CardStateName.LeftSplit).getManaCost().getCMC(); - requestedCMC += getState(CardStateName.RightSplit).getManaCost().getCMC(); - requestedCMC += xPaid; - break; default: System.out.println(TextUtil.concatWithSpace("Illegal Split Card CMC mode", mode.toString(),"passed to getCMC!")); break; diff --git a/forge-game/src/main/java/forge/game/card/CardFactory.java b/forge-game/src/main/java/forge/game/card/CardFactory.java index eda2d0236a0..c2d706600e7 100644 --- a/forge-game/src/main/java/forge/game/card/CardFactory.java +++ b/forge-game/src/main/java/forge/game/card/CardFactory.java @@ -781,6 +781,7 @@ public class CardFactory { if (sa.hasParam("KeepName")) { state.setName(originalState.getName()); } else if (newName != null) { + // convert NICKNAME descriptions? state.setName(newName); } diff --git a/forge-game/src/main/java/forge/game/trigger/TriggerHandler.java b/forge-game/src/main/java/forge/game/trigger/TriggerHandler.java index 594c1001b90..ad3ca9af8db 100644 --- a/forge-game/src/main/java/forge/game/trigger/TriggerHandler.java +++ b/forge-game/src/main/java/forge/game/trigger/TriggerHandler.java @@ -575,11 +575,8 @@ public class TriggerHandler { regtrig.triggerRun(); - if (regtrig.hasParam("OneOff")) { - if (regtrig.getHostCard().isImmutable()) { - Player p = regtrig.getHostCard().getController(); - p.getZone(ZoneType.Command).remove(regtrig.getHostCard()); - } + if (regtrig.hasParam("OneOff") && host.isImmutable()) { + host.getController().getZone(ZoneType.Command).remove(host); } } diff --git a/forge-gui-desktop/src/test/java/forge/ai/simulation/GameSimulationTest.java b/forge-gui-desktop/src/test/java/forge/ai/simulation/GameSimulationTest.java index 10f021f7a5d..9c8074a0cb9 100644 --- a/forge-gui-desktop/src/test/java/forge/ai/simulation/GameSimulationTest.java +++ b/forge-gui-desktop/src/test/java/forge/ai/simulation/GameSimulationTest.java @@ -1785,8 +1785,6 @@ public class GameSimulationTest extends SimulationTest { AssertJUnit.assertEquals(clonedOutLaw.getName(), hillGiantName); - AssertJUnit.assertTrue(clonedOutLaw.isTransformable()); - score = sim.simulateSpellAbility(moonmistSA).value; AssertJUnit.assertTrue(score > 0); diff --git a/forge-gui/res/cardsfolder/b/baeloth_barrityl_entertainer.txt b/forge-gui/res/cardsfolder/b/baeloth_barrityl_entertainer.txt index dd1f6b55181..47dff98faba 100644 --- a/forge-gui/res/cardsfolder/b/baeloth_barrityl_entertainer.txt +++ b/forge-gui/res/cardsfolder/b/baeloth_barrityl_entertainer.txt @@ -3,7 +3,7 @@ ManaCost:4 R Types:Legendary Creature Elf Shaman PT:2/5 K:Choose a Background -S:Mode$ Continuous | Affected$ Creature.powerLTY+OppCtrl | Goad$ True | Description$ Creatures your opponents control with power less than CARDNAME power are goaded. (They attack each combat if able and attack a player other than you if able.) +S:Mode$ Continuous | Affected$ Creature.powerLTY+OppCtrl | Goad$ True | Description$ Creatures your opponents control with power less than NICKNAME's power are goaded. (They attack each combat if able and attack a player other than you if able.) SVar:Y:Count$CardPower T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | ValidCard$ Creature.attacking+IsGoaded,Creature.blocking+IsGoaded | Execute$ TrigToken | TriggerZones$ Battlefield | TriggerDescription$ Whenever a goaded attacking or blocking creature dies, you create a Treasure token. (It's an artifact with "{T}, Sacrifice this artifact: Add one mana of any color.") SVar:TrigToken:DB$ Token | TokenAmount$ 1 | TokenScript$ c_a_treasure_sac | TokenOwner$ You diff --git a/forge-gui/res/cardsfolder/d/deaths_approach.txt b/forge-gui/res/cardsfolder/d/deaths_approach.txt index 95f10e8f077..64062572c76 100644 --- a/forge-gui/res/cardsfolder/d/deaths_approach.txt +++ b/forge-gui/res/cardsfolder/d/deaths_approach.txt @@ -4,5 +4,5 @@ Types:Enchantment Aura K:Enchant creature A:SP$ Attach | Cost$ B | ValidTgts$ Creature | AILogic$ Curse S:Mode$ Continuous | Affected$ Creature.EnchantedBy | AddPower$ -X | AddToughness$ -X | Description$ Enchanted creature gets -X/-X, where X is the number of creature cards in its controller's graveyard. -SVar:X:Count$TypeInEnchantedYard.Creature +SVar:X:Count$ValidGraveyard Creature.OwnedBy EnchantedController Oracle:Enchant creature\nEnchanted creature gets -X/-X, where X is the number of creature cards in its controller's graveyard. diff --git a/forge-gui/res/cardsfolder/g/gideon_the_oathsworn.txt b/forge-gui/res/cardsfolder/g/gideon_the_oathsworn.txt index a4a40104208..6cf76614c8f 100644 --- a/forge-gui/res/cardsfolder/g/gideon_the_oathsworn.txt +++ b/forge-gui/res/cardsfolder/g/gideon_the_oathsworn.txt @@ -2,13 +2,12 @@ Name:Gideon, the Oathsworn ManaCost:4 W W Types:Legendary Planeswalker Gideon Loyalty:4 -T:Mode$ AttackersDeclared | Execute$ TrigCounter | CheckSVar$ NonGideonAttackers | SVarCompare$ GE2 | NoResolvingCheck$ True | TriggerZones$ Battlefield | AttackingPlayer$ You | TriggerDescription$ Whenever you attack with two or more non-Gideon creatures, put a +1/+1 counter on each of those creatures. +T:Mode$ AttackersDeclared | Execute$ TrigCounter | IsPresent$ Creature.nonGideon+YouCtrl+attacking | PresentCompare$ GE2 | NoResolvingCheck$ True | TriggerZones$ Battlefield | AttackingPlayer$ You | TriggerDescription$ Whenever you attack with two or more non-Gideon creatures, put a +1/+1 counter on each of those creatures. SVar:TrigCounter:DB$ PutCounterAll | ValidCards$ Creature.nonGideon+YouCtrl+attacking | CounterType$ P1P1 | CounterNum$ 1 A:AB$ Animate | Cost$ AddCounter<2/LOYALTY> | Defined$ Self | Power$ 5 | Toughness$ 5 | Types$ Creature,Soldier | SubAbility$ GideonPrevent | Planeswalker$ True | SpellDescription$ Until end of turn, CARDNAME becomes a 5/5 white Soldier creature that's still a planeswalker. Prevent all damage that would be dealt to him this turn. SVar:GideonPrevent:DB$ Effect | ReplacementEffects$ RPrevent | Duration$ UntilHostLeavesPlayOrEOT SVar:RPrevent:Event$ DamageDone | Prevent$ True | ActiveZones$ Command | ValidTarget$ Card.EffectSource | Description$ Prevent all damage that would be dealt to EFFECTSOURCE. A:AB$ ChangeZone | Cost$ SubCounter<9/LOYALTY> | Planeswalker$ True | Ultimate$ True | Defined$ Self | Origin$ Battlefield | Destination$ Exile | SubAbility$ ExileOppCreatures | SpellDescription$ Exile CARDNAME and each creature your opponents control. SVar:ExileOppCreatures:DB$ ChangeZoneAll | Origin$ Battlefield | Destination$ Exile | ChangeType$ Creature.OppCtrl -SVar:NonGideonAttackers:Count$Valid Creature.nonGideon+YouCtrl+attacking DeckHas:Ability$Counters Oracle:Whenever you attack with two or more non-Gideon creatures, put a +1/+1 counter on each of those creatures.\n[+2]: Until end of turn, Gideon, the Oathsworn becomes a 5/5 white Soldier creature that's still a planeswalker. Prevent all damage that would be dealt to him this turn. (He can't attack if he was cast this turn.)\n[-9]: Exile Gideon, the Oathsworn and each creature your opponents control. diff --git a/forge-gui/res/cardsfolder/h/havengul_lich.txt b/forge-gui/res/cardsfolder/h/havengul_lich.txt index ad565fb928e..ded5b41cc73 100644 --- a/forge-gui/res/cardsfolder/h/havengul_lich.txt +++ b/forge-gui/res/cardsfolder/h/havengul_lich.txt @@ -4,7 +4,7 @@ Types:Creature Zombie Wizard PT:4/4 A:AB$ Effect | Name$ Havengul Lich Delayed Trigger | Cost$ 1 | ValidTgts$ Creature | TgtZone$ Graveyard | TgtPrompt$ Select target creature card | StaticAbilities$ STPlay | Triggers$ DTCast | RememberObjects$ Targeted | ExileOnMoved$ Graveyard | SpellDescription$ You may cast target creature card in a graveyard this turn. When you cast it this turn, CARDNAME gains all activated abilities of that card until end of turn. SVar:STPlay:Mode$ Continuous | MayPlay$ True | EffectZone$ Command | Affected$ Card.IsRemembered | AffectedZone$ Graveyard | Description$ Until end of turn, you may cast a creature card in a graveyard. -SVar:DTCast:Mode$ SpellCast | ValidCard$ Card.IsRemembered | Execute$ StealAbs | TriggerDescription$ When you cast that card this turn, Havengul Lich gains all activated abilities of that card until end of turn. +SVar:DTCast:Mode$ SpellCast | ValidCard$ Card.IsRemembered+nonLand | Execute$ StealAbs | TriggerDescription$ When you cast that card this turn, Havengul Lich gains all activated abilities of that card until end of turn. SVar:StealAbs:DB$ Effect | RememberObjects$ TriggeredCard | StaticAbilities$ STSteal | Duration$ UntilHostLeavesPlayOrEOT SVar:STSteal:Mode$ Continuous | Affected$ Card.EffectSource | EffectZone$ Command | GainsAbilitiesOfDefined$ RememberedLKI | Description$ Havengul Lich gains all activated abilities of that card until end of turn. AI:RemoveDeck:All diff --git a/forge-gui/res/cardsfolder/k/kolaghan_warmonger.txt b/forge-gui/res/cardsfolder/k/kolaghan_warmonger.txt index 4f45eece45b..84ceb415eeb 100644 --- a/forge-gui/res/cardsfolder/k/kolaghan_warmonger.txt +++ b/forge-gui/res/cardsfolder/k/kolaghan_warmonger.txt @@ -4,7 +4,7 @@ Types:Creature Ogre Warrior PT:3/2 K:Haste T:Mode$ Attacks | ValidCard$ Card.Self | Execute$ TrigDig | TriggerDescription$ Whenever CARDNAME attacks, look at the top six cards of your library. You may reveal a Dragon card from among them and put it into your hand. Put the rest on the bottom of your library in a random order. -SVar:TrigDig:DB$ Dig | DigNum$ 6 | ChangeNum$ 1 | Optional$ True | ChangeValid$ Card.Dragon | RestRandomOrder$ True +SVar:TrigDig:DB$ Dig | DigNum$ 6 | ChangeNum$ 1 | Optional$ True | ChangeValid$ Card.Dragon | RestRandomOrder$ True | ForceRevealToController$ True DeckNeeds:Type$Dragon SVar:HasAttackEffect:TRUE Oracle:Whenever Kolaghan Warmonger attacks, look at the top six cards of your library. You may reveal a Dragon card from among them and put it into your hand. Put the rest on the bottom of your library in a random order. diff --git a/forge-gui/res/cardsfolder/r/righteous_authority.txt b/forge-gui/res/cardsfolder/r/righteous_authority.txt index c059eefa718..2cb66262fe1 100644 --- a/forge-gui/res/cardsfolder/r/righteous_authority.txt +++ b/forge-gui/res/cardsfolder/r/righteous_authority.txt @@ -4,7 +4,7 @@ Types:Enchantment Aura K:Enchant creature A:SP$ Attach | Cost$ 3 W U | ValidTgts$ Creature | AILogic$ Pump S:Mode$ Continuous | Affected$ Creature.EnchantedBy | AddPower$ X | AddToughness$ X | Description$ Enchanted creature gets +1/+1 for each card in its controller's hand. -SVar:X:Count$InEnchantedHand +SVar:X:Count$ValidHand Card.OwnedBy EnchantedController T:Mode$ Phase | Phase$ Draw | ValidPlayer$ Player.EnchantedController | TriggerZones$ Battlefield | Execute$ TrigDraw | TriggerDescription$ At the beginning of the draw step of enchanted creature's controller, that player draws an additional card. SVar:TrigDraw:DB$ Draw | NumCards$ 1 | Defined$ TriggeredPlayer Oracle:Enchant creature\nEnchanted creature gets +1/+1 for each card in its controller's hand.\nAt the beginning of the draw step of enchanted creature's controller, that player draws an additional card. diff --git a/forge-gui/res/cardsfolder/s/sengir_connoisseur.txt b/forge-gui/res/cardsfolder/s/sengir_connoisseur.txt index cbf247fa8aa..721939cdd3e 100644 --- a/forge-gui/res/cardsfolder/s/sengir_connoisseur.txt +++ b/forge-gui/res/cardsfolder/s/sengir_connoisseur.txt @@ -3,7 +3,7 @@ ManaCost:3 B B Types:Creature Vampire PT:3/3 K:Flying -T:Mode$ ChangesZoneAll | Origin$ Battlefield | Destination$ Graveyard | ValidCard$ Creature.Other | TriggerZones$ Battlefield | ActivationLimit$ 1 | Execute$ TrigPutCounter | TriggerDescription$ Whenever one or more other creatures die, put a +1/+1 counter on CARDNAME. This ability triggers only once each turn. +T:Mode$ ChangesZoneAll | Origin$ Battlefield | Destination$ Graveyard | ValidCards$ Creature.Other | TriggerZones$ Battlefield | ActivationLimit$ 1 | Execute$ TrigPutCounter | TriggerDescription$ Whenever one or more other creatures die, put a +1/+1 counter on CARDNAME. This ability triggers only once each turn. SVar:TrigPutCounter:DB$ PutCounter | Defined$ Self | CounterType$ P1P1 | CounterNum$ 1 DeckHas:Ability$Counters Oracle:Flying\nWhenever one or more other creatures die, put a +1/+1 counter on Sengir Connoisseur. This ability triggers only once each turn. diff --git a/forge-gui/res/tokenscripts/wb_1_1_cleric_deathpact.txt b/forge-gui/res/tokenscripts/wb_1_1_cleric_deathpact.txt index f5908e06035..af16be80df3 100644 --- a/forge-gui/res/tokenscripts/wb_1_1_cleric_deathpact.txt +++ b/forge-gui/res/tokenscripts/wb_1_1_cleric_deathpact.txt @@ -3,5 +3,5 @@ ManaCost:no cost Types:Creature Cleric Colors:white,black PT:1/1 -A:AB$ ChangeZone | Cost$ 3 W B B T Sac<1/CARDNAME> | ChangeType$ Card.namedDeathpact Angel+YouOwn | ChangeNum$ 1 | Origin$ Graveyard | Destination$ Battlefield | Hidden$ True | SpellDescription$ Return a card named Deathpact Angel from your graveyard to the battlefield. +A:AB$ ChangeZone | Cost$ 3 W B B T Sac<1/CARDNAME> | ChangeType$ Card.namedDeathpact Angel+YouOwn | Mandatory$ True | ChangeNum$ 1 | Origin$ Graveyard | Destination$ Battlefield | Hidden$ True | SpellDescription$ Return a card named Deathpact Angel from your graveyard to the battlefield. Oracle:{3}{W}{B}{B}, {T}, Sacrifice this creature: Return a card named Deathpact Angel from your graveyard to the battlefield. diff --git a/forge-gui/src/main/java/forge/gamemodes/limited/BoosterDraftAI.java b/forge-gui/src/main/java/forge/gamemodes/limited/BoosterDraftAI.java index eda3ad645f1..e876c72ea06 100644 --- a/forge-gui/src/main/java/forge/gamemodes/limited/BoosterDraftAI.java +++ b/forge-gui/src/main/java/forge/gamemodes/limited/BoosterDraftAI.java @@ -90,7 +90,7 @@ public class BoosterDraftAI { out[i] = new BoosterDeckBuilder(this.decks.get(i), this.playerColors.get(i)).buildDeck(); } return out; - } // getDecks() + } public BoosterDraftAI() { // Initialize deck array and playerColors list @@ -98,7 +98,7 @@ public class BoosterDraftAI { this.decks.add(new ArrayList<>()); this.playerColors.add(new DeckColors()); } - } // BoosterDraftAI() + } public IBoosterDraft getBd() { return this.bd; @@ -107,4 +107,4 @@ public class BoosterDraftAI { this.bd = bd0; } -} // BoosterDraftAI() +} diff --git a/forge-gui/src/main/java/forge/gamemodes/limited/LimitedPlayerAI.java b/forge-gui/src/main/java/forge/gamemodes/limited/LimitedPlayerAI.java index 02fd2775812..51f8bc0355f 100644 --- a/forge-gui/src/main/java/forge/gamemodes/limited/LimitedPlayerAI.java +++ b/forge-gui/src/main/java/forge/gamemodes/limited/LimitedPlayerAI.java @@ -15,7 +15,6 @@ public class LimitedPlayerAI extends LimitedPlayer { public LimitedPlayerAI(int seatingOrder) { super(seatingOrder); deckCols = new DeckColors(); - } @Override