From 0a773c30bce04fb09ae0ed46e2630942859f06ac Mon Sep 17 00:00:00 2001 From: TRT <> Date: Tue, 6 Aug 2024 21:07:21 +0200 Subject: [PATCH 1/3] Fix Uphill Battle --- .../src/main/java/forge/ai/GameState.java | 2 +- .../java/forge/game/card/CardFactoryUtil.java | 6 ++--- .../java/forge/game/card/CardProperty.java | 24 ------------------- forge-gui/res/cardsfolder/c/clay_golem.txt | 2 +- .../res/cardsfolder/g/gilt_leaf_winnower.txt | 2 +- .../cardsfolder/s/stonebinders_familiar.txt | 2 +- .../res/cardsfolder/t/the_war_doctor.txt | 2 +- forge-gui/res/cardsfolder/u/uphill_battle.txt | 2 +- .../java/forge/gui/card/CardScriptParser.java | 6 ++--- 9 files changed, 12 insertions(+), 36 deletions(-) diff --git a/forge-ai/src/main/java/forge/ai/GameState.java b/forge-ai/src/main/java/forge/ai/GameState.java index 60f4a6c076c..4a064923623 100644 --- a/forge-ai/src/main/java/forge/ai/GameState.java +++ b/forge-ai/src/main/java/forge/ai/GameState.java @@ -1315,7 +1315,7 @@ public abstract class GameState { c.setBackSide(true); } else if (info.startsWith("OnAdventure")) { - String abAdventure = "DB$ Effect | RememberObjects$ Self | StaticAbilities$ Play | ForgetOnMoved$ Exile | Duration$ Permanent | ConditionDefined$ Self | ConditionPresent$ Card.nonCopiedSpell"; + String abAdventure = "DB$ Effect | RememberObjects$ Self | StaticAbilities$ Play | ForgetOnMoved$ Exile | Duration$ Permanent | ConditionDefined$ Self | ConditionPresent$ Card.!copiedSpell"; SpellAbility saAdventure = AbilityFactory.getAbility(abAdventure, c); StringBuilder sbPlay = new StringBuilder(); sbPlay.append("Mode$ Continuous | MayPlay$ True | EffectZone$ Command | Affected$ Card.IsRemembered+nonAdventure"); 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 ba9d8171bf2..2717e25838d 100644 --- a/forge-game/src/main/java/forge/game/card/CardFactoryUtil.java +++ b/forge-game/src/main/java/forge/game/card/CardFactoryUtil.java @@ -1697,7 +1697,7 @@ public class CardFactoryUtil { final String[] k = keyword.split(":"); String renownTrig = "Mode$ DamageDone | ValidSource$ Card.Self | ValidTarget$ Player" - + " | IsPresent$ Card.Self+IsNotRenowned | CombatDamage$ True | Secondary$ True" + + " | IsPresent$ Card.Self+!IsRenowned | CombatDamage$ True | Secondary$ True" + " | TriggerDescription$ Renown " + k[1] +" (" + inst.getReminderText() + ")"; final String effect = "DB$ PutCounter | Defined$ Self | " @@ -3226,7 +3226,7 @@ public class CardFactoryUtil { String desc = "Monstrosity " + magnitude; String effect = "AB$ PutCounter | Cost$ " + manacost + " | ConditionPresent$ " - + "Card.Self+IsNotMonstrous | Monstrosity$ True | CounterNum$ " + magnitude + + "Card.Self+!IsMonstrous | Monstrosity$ True | CounterNum$ " + magnitude + " | CounterType$ P1P1 | StackDescription$ SpellDescription"; if (reduceCost != null) { effect += "| ReduceCost$ " + reduceCost; @@ -4146,7 +4146,7 @@ public class CardFactoryUtil { SpellAbility saExile = AbilityFactory.getAbility(abExile, card); - String abEffect = "DB$ Effect | RememberObjects$ Self | StaticAbilities$ Play | ForgetOnMoved$ Exile | Duration$ Permanent | ConditionDefined$ Self | ConditionPresent$ Card.nonCopiedSpell+nonToken"; + String abEffect = "DB$ Effect | RememberObjects$ Self | StaticAbilities$ Play | ForgetOnMoved$ Exile | Duration$ Permanent | ConditionDefined$ Self | ConditionPresent$ Card.!copiedSpell+nonToken"; AbilitySub saEffect = (AbilitySub)AbilityFactory.getAbility(abEffect, card); StringBuilder sbPlay = new StringBuilder(); 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 4136be56abe..a13bf509890 100644 --- a/forge-game/src/main/java/forge/game/card/CardProperty.java +++ b/forge-game/src/main/java/forge/game/card/CardProperty.java @@ -1457,10 +1457,6 @@ public class CardProperty { if (!card.isCopiedSpell()) { return false; } - } else if (property.startsWith("nonCopiedSpell")) { - if (card.isCopiedSpell()) { - return false; - } } else if (property.startsWith("hasXCost")) { ManaCost cost = card.getManaCost(); if (cost == null || cost.countX() <= 0) { @@ -1482,10 +1478,6 @@ public class CardProperty { if (!source.getExploited().contains(card)) { return false; } - } else if (property.startsWith("unequalPT")) { - if (card.getNetPower() == card.getNetToughness()) { - return false; - } } else if (property.startsWith("equalPT")) { if (card.getNetPower() != card.getNetToughness()) { return false; @@ -1919,18 +1911,10 @@ public class CardProperty { if (card.getDevouredCards().isEmpty()) { return false; } - } else if (property.equals("HasNotDevoured")) { - if (!card.getDevouredCards().isEmpty()) { - return false; - } } else if (property.equals("IsMonstrous")) { if (!card.isMonstrous()) { return false; } - } else if (property.equals("IsNotMonstrous")) { - if (card.isMonstrous()) { - return false; - } } else if (property.equals("IsUnearthed")) { if (!card.isUnearthed()) { return false; @@ -1939,10 +1923,6 @@ public class CardProperty { if (!card.isRenowned()) { return false; } - } else if (property.equals("IsNotRenowned")) { - if (card.isRenowned()) { - return false; - } } else if (property.equals("IsSolved")) { if (!card.isSolved()) { return false; @@ -1965,10 +1945,6 @@ public class CardProperty { if (!card.isSuspected()) { return false; } - } else if (property.equals("IsUnsuspected")) { - if (card.isSuspected()) { - return false; - } } else if (property.equals("IsRemembered")) { if (!source.isRemembered(card)) { return false; diff --git a/forge-gui/res/cardsfolder/c/clay_golem.txt b/forge-gui/res/cardsfolder/c/clay_golem.txt index b86ed443012..b4dd1565c05 100644 --- a/forge-gui/res/cardsfolder/c/clay_golem.txt +++ b/forge-gui/res/cardsfolder/c/clay_golem.txt @@ -2,7 +2,7 @@ Name:Clay Golem ManaCost:4 Types:Artifact Creature Golem PT:4/4 -A:AB$ PutCounter | Cost$ 6 RollDice<1/8/X> | ConditionPresent$ Card.Self+IsNotMonstrous | Monstrosity$ True | CounterNum$ X | CounterType$ P1P1 | AILogic$ FromDiceRoll | StackDescription$ SpellDescription | SpellDescription$ Monstrosity X, where X is the result. (If this creature isn't monstrous, put X +1/+1 counters on it and it becomes monstrous.) +A:AB$ PutCounter | Cost$ 6 RollDice<1/8/X> | ConditionPresent$ Card.Self+!IsMonstrous | Monstrosity$ True | CounterNum$ X | CounterType$ P1P1 | AILogic$ FromDiceRoll | StackDescription$ SpellDescription | SpellDescription$ Monstrosity X, where X is the result. (If this creature isn't monstrous, put X +1/+1 counters on it and it becomes monstrous.) T:Mode$ BecomeMonstrous | ValidCard$ Card.Self | TriggerZones$ Battlefield | Execute$ TrigDestroy | TriggerDescription$ Berserk — When CARDNAME becomes monstrous, destroy target permanent. SVar:TrigDestroy:DB$ Destroy | ValidTgts$ Permanent DeckHas:Ability$Counters diff --git a/forge-gui/res/cardsfolder/g/gilt_leaf_winnower.txt b/forge-gui/res/cardsfolder/g/gilt_leaf_winnower.txt index 83fc617e060..56b6f0e2dcc 100644 --- a/forge-gui/res/cardsfolder/g/gilt_leaf_winnower.txt +++ b/forge-gui/res/cardsfolder/g/gilt_leaf_winnower.txt @@ -4,5 +4,5 @@ Types:Creature Elf Warrior PT:4/3 K:Menace T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigDestroy | OptionalDecider$ You | TriggerDescription$ When CARDNAME enters, you may destroy target non-Elf creature whose power and toughness aren't equal. -SVar:TrigDestroy:DB$ Destroy | ValidTgts$ Creature.nonElf+unequalPT | TgtPrompt$ Select target non-Elf creature whose power and toughness aren't equal. +SVar:TrigDestroy:DB$ Destroy | ValidTgts$ Creature.nonElf+!equalPT | TgtPrompt$ Select target non-Elf creature whose power and toughness aren't equal. Oracle:Menace (This creature can't be blocked except by two or more creatures.)\nWhen Gilt-Leaf Winnower enters, you may destroy target non-Elf creature whose power and toughness aren't equal. diff --git a/forge-gui/res/cardsfolder/s/stonebinders_familiar.txt b/forge-gui/res/cardsfolder/s/stonebinders_familiar.txt index a7676b36dfa..a666b0e1308 100644 --- a/forge-gui/res/cardsfolder/s/stonebinders_familiar.txt +++ b/forge-gui/res/cardsfolder/s/stonebinders_familiar.txt @@ -2,7 +2,7 @@ Name:Stonebinder's Familiar ManaCost:W Types:Creature Spirit Dog PT:1/1 -T:Mode$ ChangesZoneAll | ValidCards$ Card.nonToken+nonCopiedSpell | Destination$ Exile | TriggerZones$ Battlefield | Execute$ TrigPutcounter | PlayerTurn$ True | ActivationLimit$ 1 | TriggerDescription$ Whenever one or more cards are put into exile during your turn, put a +1/+1 counter on CARDNAME. This ability triggers only once each turn. +T:Mode$ ChangesZoneAll | ValidCards$ Card.nonToken+!copiedSpell | Destination$ Exile | TriggerZones$ Battlefield | Execute$ TrigPutcounter | PlayerTurn$ True | ActivationLimit$ 1 | TriggerDescription$ Whenever one or more cards are put into exile during your turn, put a +1/+1 counter on CARDNAME. This ability triggers only once each turn. SVar:TrigPutcounter:DB$ PutCounter | CounterType$ P1P1 | Defined$ Self | CounterNum$ 1 DeckHas:Ability$Counters Oracle:Whenever one or more cards are put into exile during your turn, put a +1/+1 counter on Stonebinder's Familiar. This ability triggers only once each turn. diff --git a/forge-gui/res/cardsfolder/t/the_war_doctor.txt b/forge-gui/res/cardsfolder/t/the_war_doctor.txt index 238146a4cdf..60ce8c7d6b7 100644 --- a/forge-gui/res/cardsfolder/t/the_war_doctor.txt +++ b/forge-gui/res/cardsfolder/t/the_war_doctor.txt @@ -3,7 +3,7 @@ ManaCost:2 R W Types:Legendary Creature Time Lord Doctor PT:3/5 T:Mode$ PhaseOutAll | ValidCards$ Permanent.phasedOutOther | TriggerZones$ Battlefield | Execute$ TrigPutCounter | TriggerDescription$ Whenever one or more other permanents phase out and whenever one or more other cards are put into exile from anywhere, put a time counter on CARDNAME. -T:Mode$ ChangesZoneAll | ValidCards$ Card.Other+nonToken+nonCopiedSpell | Origin$ Any | Destination$ Exile | TriggerZones$ Battlefield | Execute$ TrigPutCounter | Secondary$ True | TriggerDescription$ Whenever one or more other permanents phase out and whenever one or more other cards are put into exile from anywhere, put a time counter on CARDNAME. +T:Mode$ ChangesZoneAll | ValidCards$ Card.Other+nonToken+!copiedSpell | Origin$ Any | Destination$ Exile | TriggerZones$ Battlefield | Execute$ TrigPutCounter | Secondary$ True | TriggerDescription$ Whenever one or more other permanents phase out and whenever one or more other cards are put into exile from anywhere, put a time counter on CARDNAME. SVar:TrigPutCounter:DB$ PutCounter | CounterType$ TIME T:Mode$ Attacks | ValidCard$ Card.Self | Execute$ TrigDamage | TriggerDescription$ Whenever CARDNAME attacks, it deals damage equal to the number of time counters on it to any target. If a creature dealt damage this way would die this turn, exile it instead. SVar:TrigDamage:DB$ DealDamage | ValidTgts$ Any | DamageSource$ TriggeredAttackerLKICopy | NumDmg$ Count$CardCounters.TIME | ReplaceDyingDefined$ Targeted.Creature diff --git a/forge-gui/res/cardsfolder/u/uphill_battle.txt b/forge-gui/res/cardsfolder/u/uphill_battle.txt index 30ac005d17b..5f53f5eb8f2 100644 --- a/forge-gui/res/cardsfolder/u/uphill_battle.txt +++ b/forge-gui/res/cardsfolder/u/uphill_battle.txt @@ -1,6 +1,6 @@ Name:Uphill Battle ManaCost:2 R Types:Enchantment -R:Event$ Moved | ValidCard$ Creature.OppCtrl | Destination$ Battlefield | ReplaceWith$ ETBTapped | ReplacementResult$ Updated | ActiveZones$ Battlefield | Description$ Creatures played by your opponents enter tapped. +R:Event$ Moved | ValidCard$ Creature | ValidCause$ LandAbility.OppCtrl,Spell.wasCast+Permanent+CastSa Spell.OppCtrl | Destination$ Battlefield | ReplaceWith$ ETBTapped | ReplacementResult$ Updated | ActiveZones$ Battlefield | Description$ Creatures played by your opponents enter tapped. SVar:ETBTapped:DB$ Tap | ETB$ True | Defined$ ReplacedCard Oracle:Creatures played by your opponents enter tapped. 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 475afe87dd5..560e3f7d437 100644 --- a/forge-gui/src/main/java/forge/gui/card/CardScriptParser.java +++ b/forge-gui/src/main/java/forge/gui/card/CardScriptParser.java @@ -486,13 +486,13 @@ public final class CardScriptParser { "blockedBySourceThisTurn", "isBlockedByRemembered", "blockedRemembered", "blockedByRemembered", "unblocked", "attackersBandedWith", "kicked", "kicked1", "kicked2", "evoked", - "HasDevoured", "HasNotDevoured", "IsMonstrous", "IsNotMonstrous", + "HasDevoured", "IsMonstrous", "CostsPhyrexianMana", "IsRemembered", "IsNotRemembered", "IsImprinted", "IsNotImprinted", "hasActivatedAbilityWithTapCost", "hasActivatedAbility", "hasManaAbility", "hasNonManaActivatedAbility", "NoAbilities", "HasCounters", "wasNotCast", "ChosenType", "IsNotChosenType", "IsCommander", - "IsNotCommander","IsRenowned", "IsNotRenowned"); + "IsNotCommander", "IsRenowned"); private static final Set VALID_EXCLUSIVE_STARTSWITH = ImmutableSortedSet .of("named", "notnamed", "OwnedBy", "ControlledBy", "ControllerControls", "AttachedTo", "EnchantedBy", @@ -504,7 +504,7 @@ public final class CardScriptParser { "ThisTurnEntered", "sharesTypeWith", "hasKeyword", "with", "greatestPowerControlledBy", "greatestCMCControlledBy", "power", "toughness", "cmc", "totalPT", "counters", "non", - "RememberMap", "wasCastFrom", "wasNotCastFrom", "set", + "RememberMap", "wasCastFrom", "set", "inZone", "HasSVar"); private static boolean isValidExclusive(String valid) { From 22aa5d8e08d8951b27fd97ab7661027cb5c349c2 Mon Sep 17 00:00:00 2001 From: TRT <> Date: Wed, 7 Aug 2024 08:26:13 +0200 Subject: [PATCH 2/3] Fix Garth + Uphill Battle --- forge-game/src/main/java/forge/game/GameAction.java | 1 - forge-game/src/main/java/forge/game/GameActionUtil.java | 2 +- .../src/main/java/forge/game/ability/effects/PlayEffect.java | 1 + 3 files changed, 2 insertions(+), 2 deletions(-) diff --git a/forge-game/src/main/java/forge/game/GameAction.java b/forge-game/src/main/java/forge/game/GameAction.java index 37a568c317d..b050687210b 100644 --- a/forge-game/src/main/java/forge/game/GameAction.java +++ b/forge-game/src/main/java/forge/game/GameAction.java @@ -842,7 +842,6 @@ public class GameAction { } if (zoneTo.is(ZoneType.Stack)) { - // zoneFrom maybe null if the spell is cast from "Ouside the game", ex. ability of Garth One-Eye c.setCastFrom(zoneFrom); if (cause != null && cause.isSpell() && c.equals(cause.getHostCard())) { c.setCastSA(cause); diff --git a/forge-game/src/main/java/forge/game/GameActionUtil.java b/forge-game/src/main/java/forge/game/GameActionUtil.java index 7cc468c6667..9178d3a1182 100644 --- a/forge-game/src/main/java/forge/game/GameActionUtil.java +++ b/forge-game/src/main/java/forge/game/GameActionUtil.java @@ -877,7 +877,7 @@ public final class GameActionUtil { return; } - if (fromZone != null) { // and not a copy + if (fromZone != null && !fromZone.is(ZoneType.None)) { // and not a copy // might have been an alternative lki host oldCard = ability.getCardState().getCard(); diff --git a/forge-game/src/main/java/forge/game/ability/effects/PlayEffect.java b/forge-game/src/main/java/forge/game/ability/effects/PlayEffect.java index 22642d9b1b3..a9b19e2f3b1 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/PlayEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/PlayEffect.java @@ -169,6 +169,7 @@ public class PlayEffect extends SpellAbilityEffect { // so it gets added to stack card.setCopiedPermanent(card); card.setToken(true); + card.setZone(controller.getZone(ZoneType.None)); tgtCards = new CardCollection(card); } else { tgtCards = new CardCollection(); From 9fe6b06487d117f114fdd7bc2f4f32d71d453073 Mon Sep 17 00:00:00 2001 From: TRT <> Date: Fri, 9 Aug 2024 22:28:14 +0200 Subject: [PATCH 3/3] Clean up --- .../java/forge/game/ability/effects/PhasesEffect.java | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/forge-game/src/main/java/forge/game/ability/effects/PhasesEffect.java b/forge-game/src/main/java/forge/game/ability/effects/PhasesEffect.java index cce889e8c4c..0c16580a079 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/PhasesEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/PhasesEffect.java @@ -21,12 +21,6 @@ import forge.util.Localizer; public class PhasesEffect extends SpellAbilityEffect { - // ****************************************** - // ************** Phases ******************** - // ****************************************** - // Phases generally Phase Out. Time and Tide is the only card that can force - // Phased Out cards in. - /* (non-Javadoc) * @see forge.card.abilityfactory.SpellEffect#resolve(java.util.Map, forge.card.spellability.SpellAbility) */ @@ -56,10 +50,8 @@ public class PhasesEffect extends SpellAbilityEffect { tgtCards = game.getCardsIn(ZoneType.Battlefield); } tgtCards = AbilityUtils.filterListByType(tgtCards, sa.getParam("AllValid"), sa); - } else if (sa.hasParam("Defined")) { - tgtCards = AbilityUtils.getDefinedCards(source, sa.getParam("Defined"), sa); } else { - tgtCards = getTargetCards(sa); + tgtCards = getDefinedCardsOrTargeted(sa); } if (sa.hasParam("AnyNumber")) { tgtCards = activator.getController().chooseCardsForEffect(tgtCards, sa,