From c7feb44214d73b73dbfb8a1a29e3de91ecbc4fe3 Mon Sep 17 00:00:00 2001 From: TRT <> Date: Wed, 4 Jan 2023 16:13:26 +0100 Subject: [PATCH 01/13] Fix "can't happen" applied with other replacements --- .../src/main/java/forge/game/replacement/ReplacementLayer.java | 1 + forge-gui/res/cardsfolder/g/grafdiggers_cage.txt | 2 +- forge-gui/res/cardsfolder/k/kunoros_hound_of_athreos.txt | 2 +- forge-gui/res/cardsfolder/w/weathered_runestone.txt | 2 +- forge-gui/res/cardsfolder/w/worms_of_the_earth.txt | 2 +- 5 files changed, 5 insertions(+), 4 deletions(-) diff --git a/forge-game/src/main/java/forge/game/replacement/ReplacementLayer.java b/forge-game/src/main/java/forge/game/replacement/ReplacementLayer.java index ca71573f4b8..38ab284bcd9 100644 --- a/forge-game/src/main/java/forge/game/replacement/ReplacementLayer.java +++ b/forge-game/src/main/java/forge/game/replacement/ReplacementLayer.java @@ -6,6 +6,7 @@ package forge.game.replacement; * */ public enum ReplacementLayer { + CantHappen, // 614.17 Control, // 616.1b Copy, // 616.1c Transform, // 616.1d diff --git a/forge-gui/res/cardsfolder/g/grafdiggers_cage.txt b/forge-gui/res/cardsfolder/g/grafdiggers_cage.txt index f77d045954e..72f0491a6f8 100644 --- a/forge-gui/res/cardsfolder/g/grafdiggers_cage.txt +++ b/forge-gui/res/cardsfolder/g/grafdiggers_cage.txt @@ -1,7 +1,7 @@ Name:Grafdigger's Cage ManaCost:1 Types:Artifact -R:Event$ Moved | ActiveZones$ Battlefield | Origin$ Graveyard,Library | Destination$ Battlefield | ValidLKI$ Creature.Other | Prevent$ True | Description$ Creature cards in graveyards and libraries can't enter the battlefield. +R:Event$ Moved | ActiveZones$ Battlefield | Origin$ Graveyard,Library | Destination$ Battlefield | ValidLKI$ Creature.Other | Prevent$ True | Layer$ CantHappen | Description$ Creature cards in graveyards and libraries can't enter the battlefield. S:Mode$ CantBeCast | Origin$ Graveyard,Library | Description$ Players can't cast spells from graveyards or libraries. SVar:NonStackingEffect:True AI:RemoveDeck:Random diff --git a/forge-gui/res/cardsfolder/k/kunoros_hound_of_athreos.txt b/forge-gui/res/cardsfolder/k/kunoros_hound_of_athreos.txt index d13c61e88b4..4924dd4516d 100644 --- a/forge-gui/res/cardsfolder/k/kunoros_hound_of_athreos.txt +++ b/forge-gui/res/cardsfolder/k/kunoros_hound_of_athreos.txt @@ -5,7 +5,7 @@ PT:3/3 K:Vigilance K:Menace K:Lifelink -R:Event$ Moved | ActiveZones$ Battlefield | Origin$ Graveyard | Destination$ Battlefield | ValidLKI$ Creature.Other | Prevent$ True | Description$ Creature cards in graveyards can't enter the battlefield. +R:Event$ Moved | ActiveZones$ Battlefield | Origin$ Graveyard | Destination$ Battlefield | ValidLKI$ Creature.Other | Prevent$ True | Layer$ CantHappen | Description$ Creature cards in graveyards can't enter the battlefield. S:Mode$ CantBeCast | Origin$ Graveyard | Description$ Players can't cast spells from graveyards. SVar:NonStackingEffect:True Oracle:Vigilance, menace, lifelink\nCreature cards in graveyards can't enter the battlefield.\nPlayers can't cast spells from graveyards. diff --git a/forge-gui/res/cardsfolder/w/weathered_runestone.txt b/forge-gui/res/cardsfolder/w/weathered_runestone.txt index 824a1b4f92e..68f39c73052 100644 --- a/forge-gui/res/cardsfolder/w/weathered_runestone.txt +++ b/forge-gui/res/cardsfolder/w/weathered_runestone.txt @@ -1,7 +1,7 @@ Name:Weathered Runestone ManaCost:2 Types:Artifact -R:Event$ Moved | ActiveZones$ Battlefield | Origin$ Graveyard,Library | Destination$ Battlefield | ValidLKI$ Permanent.nonland | Prevent$ True | Description$ Nonland permanent cards in graveyards and libraries can't enter the battlefield. +R:Event$ Moved | ActiveZones$ Battlefield | Origin$ Graveyard,Library | Destination$ Battlefield | ValidLKI$ Permanent.nonland | Prevent$ True | Layer$ CantHappen | Description$ Nonland permanent cards in graveyards and libraries can't enter the battlefield. S:Mode$ CantBeCast | Origin$ Graveyard,Library | Description$ Players can't cast spells from graveyards or libraries. SVar:NonStackingEffect:True AI:RemoveDeck:Random diff --git a/forge-gui/res/cardsfolder/w/worms_of_the_earth.txt b/forge-gui/res/cardsfolder/w/worms_of_the_earth.txt index b758fbfa3d0..b1432871eaa 100644 --- a/forge-gui/res/cardsfolder/w/worms_of_the_earth.txt +++ b/forge-gui/res/cardsfolder/w/worms_of_the_earth.txt @@ -2,7 +2,7 @@ Name:Worms of the Earth ManaCost:2 B B B Types:Enchantment S:Mode$ CantPlayLand | Description$ Players can't play lands. -R:Event$ Moved | ActiveZones$ Battlefield | Destination$ Battlefield | ValidCard$ Land | Prevent$ True | Description$ Lands can't enter the battlefield. +R:Event$ Moved | ActiveZones$ Battlefield | Destination$ Battlefield | ValidCard$ Land | Prevent$ True | Layer$ CantHappen | Description$ Lands can't enter the battlefield. T:Mode$ Phase | Phase$ Upkeep | TriggerZones$ Battlefield | Execute$ RepeatAbility | TriggerDescription$ At the beginning of each upkeep, any player may sacrifice two lands or have CARDNAME deal 5 damage to that player. If a player does either, destroy CARDNAME. SVar:RepeatAbility:DB$ RepeatEach | RepeatPlayers$ Player | RepeatSubAbility$ DBChoose SVar:DBChoose:DB$ GenericChoice | Defined$ Player.IsRemembered | Choices$ SacTwoLands,DealDmg | AILogic$ PayUnlessCost From 0250c9ca0b1178783375d4666cd3f4684802243b Mon Sep 17 00:00:00 2001 From: TRT <> Date: Wed, 4 Jan 2023 16:14:34 +0100 Subject: [PATCH 02/13] Fix missing LKI for Startled Awake --- .../ability/effects/ChangeZoneEffect.java | 26 +++++++++++-------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/forge-game/src/main/java/forge/game/ability/effects/ChangeZoneEffect.java b/forge-game/src/main/java/forge/game/ability/effects/ChangeZoneEffect.java index 9a8ca6c6a6b..2deac9438ad 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/ChangeZoneEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/ChangeZoneEffect.java @@ -575,6 +575,17 @@ public class ChangeZoneEffect extends SpellAbilityEffect { movedCard = game.getAction().moveToLibrary(gameCard, libraryPosition, sa); } else { if (destination.equals(ZoneType.Battlefield)) { + Map moveParams = AbilityKey.newMap(); + moveParams.put(AbilityKey.LastStateBattlefield, lastStateBattlefield); + moveParams.put(AbilityKey.LastStateGraveyard, lastStateGraveyard); + if (sa.isReplacementAbility()) { + ReplacementEffect re = sa.getReplacementEffect(); + moveParams.put(AbilityKey.ReplacementEffect, re); + if (ReplacementType.Moved.equals(re.getMode()) && sa.getReplacingObject(AbilityKey.CardLKI) != null) { + moveParams.put(AbilityKey.CardLKI, sa.getReplacingObject(AbilityKey.CardLKI)); + } + } + if (sa.hasParam("Tapped") || sa.isNinjutsu()) { gameCard.setTapped(true); } @@ -583,6 +594,10 @@ public class ChangeZoneEffect extends SpellAbilityEffect { } if (sa.hasParam("Transformed")) { if (gameCard.isDoubleFaced()) { + // need LKI before Animate does apply + if (!moveParams.containsKey(AbilityKey.CardLKI)) { + moveParams.put(AbilityKey.CardLKI, CardUtil.getLKICopy(gameCard)); + } gameCard.changeCardState("Transform", null, sa); } else { // If it can't Transform, don't change zones. @@ -650,17 +665,6 @@ public class ChangeZoneEffect extends SpellAbilityEffect { } } - Map moveParams = AbilityKey.newMap(); - moveParams.put(AbilityKey.LastStateBattlefield, lastStateBattlefield); - moveParams.put(AbilityKey.LastStateGraveyard, lastStateGraveyard); - if (sa.isReplacementAbility()) { - ReplacementEffect re = sa.getReplacementEffect(); - moveParams.put(AbilityKey.ReplacementEffect, re); - if (ReplacementType.Moved.equals(re.getMode()) && sa.getReplacingObject(AbilityKey.CardLKI) != null) { - moveParams.put(AbilityKey.CardLKI, sa.getReplacingObject(AbilityKey.CardLKI)); - } - } - if (sa.hasAdditionalAbility("AnimateSubAbility")) { // need LKI before Animate does apply if (!moveParams.containsKey(AbilityKey.CardLKI)) { From 414fb3fcef252704f245d1c2782b70b91400d0db Mon Sep 17 00:00:00 2001 From: TRT <> Date: Wed, 4 Jan 2023 16:15:16 +0100 Subject: [PATCH 03/13] Improve cleanup when card can't ETB --- .../src/main/java/forge/game/GameAction.java | 23 +++++++++++++++---- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/forge-game/src/main/java/forge/game/GameAction.java b/forge-game/src/main/java/forge/game/GameAction.java index 91c4f25e68c..b671c2a823d 100644 --- a/forge-game/src/main/java/forge/game/GameAction.java +++ b/forge-game/src/main/java/forge/game/GameAction.java @@ -365,7 +365,22 @@ public class GameAction { copied.getOwner().removeInboundToken(copied); if (repres == ReplacementResult.Prevented) { - if (game.getStack().isResolving(c) && !zoneTo.is(ZoneType.Graveyard)) { + c.clearEtbCounters(); + if (cause != null) { + if (cause.hasParam("AnimateSubAbility")) { + c.removeChangedCardKeywords(game.getTimestamp(), 0); + c.removeChangedCardTraits(game.getTimestamp(), 0); + c.removeChangedCardTypes(game.getTimestamp(), 0); + c.removeChangedName(game.getTimestamp(), 0); + } + if (cause.hasParam("Transformed") || cause.hasParam("FaceDown")) { + c.setBackSide(false); + c.changeToState(CardStateName.Original); + } + unattachCardLeavingBattlefield(c); + } + + if (c.isInZone(ZoneType.Stack) && !zoneTo.is(ZoneType.Graveyard)) { return moveToGraveyard(c, cause, params); } @@ -373,10 +388,8 @@ public class GameAction { copied.clearDelved(); copied.clearConvoked(); copied.clearExploited(); - } - - // was replaced with another Zone Change - if (toBattlefield && !c.isInPlay()) { + } else if (toBattlefield && !c.isInPlay()) { + // was replaced with another Zone Change if (c.removeChangedState()) { c.updateStateForView(); } From 96037f9388242200d42767c1e708591215e7f648 Mon Sep 17 00:00:00 2001 From: tool4EvEr Date: Wed, 4 Jan 2023 20:29:55 +0100 Subject: [PATCH 04/13] Fix cards --- forge-gui/res/cardsfolder/g/greasefang_okiba_boss.txt | 5 +++-- forge-gui/res/cardsfolder/o/olivia_crimson_bride.txt | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/forge-gui/res/cardsfolder/g/greasefang_okiba_boss.txt b/forge-gui/res/cardsfolder/g/greasefang_okiba_boss.txt index a5204791aeb..2a083be643c 100644 --- a/forge-gui/res/cardsfolder/g/greasefang_okiba_boss.txt +++ b/forge-gui/res/cardsfolder/g/greasefang_okiba_boss.txt @@ -3,8 +3,9 @@ ManaCost:1 W B Types:Legendary Creature Rat Pilot PT:4/3 T:Mode$ Phase | Phase$ BeginCombat | ValidPlayer$ You | TriggerZones$ Battlefield | Execute$ TrigReturn | TriggerDescription$ At the beginning of combat on your turn, return target Vehicle card from your graveyard to the battlefield. It gains haste. Return it to its owner's hand at the beginning of your next end step. -SVar:TrigReturn:DB$ ChangeZone | Origin$ Graveyard | Destination$ Battlefield | TgtPrompt$ Select target Vehicle card in your graveyard | ValidTgts$ Vehicle.YouOwn | AnimateSubAbility$ Animate -SVar:Animate:DB$ Animate | Keywords$ Haste | Defined$ Remembered | Duration$ Permanent | AtEOT$ Hand +SVar:TrigReturn:DB$ ChangeZone | Origin$ Graveyard | Destination$ Battlefield | TgtPrompt$ Select target Vehicle card in your graveyard | ValidTgts$ Vehicle.YouOwn | SubAbility$ Animate | RememberChanged$ True | AtEOT$ Hand +SVar:Animate:DB$ Animate | Keywords$ Haste | Defined$ Remembered | Duration$ Permanent | SubAbility$ DBCleanup +SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True DeckHas:Ability$Graveyard DeckNeeds:Type$Vehicle Oracle:At the beginning of combat on your turn, return target Vehicle card from your graveyard to the battlefield. It gains haste. Return it to its owner's hand at the beginning of your next end step. diff --git a/forge-gui/res/cardsfolder/o/olivia_crimson_bride.txt b/forge-gui/res/cardsfolder/o/olivia_crimson_bride.txt index d3d21e4db38..6badaf7a978 100644 --- a/forge-gui/res/cardsfolder/o/olivia_crimson_bride.txt +++ b/forge-gui/res/cardsfolder/o/olivia_crimson_bride.txt @@ -5,8 +5,9 @@ PT:3/4 K:Flying K:Haste T:Mode$ Attacks | ValidCard$ Card.Self | Execute$ TrigChange | TriggerZones$ Battlefield | TriggerDescription$ Whenever CARDNAME attacks, return target creature card from your graveyard to the battlefield tapped and attacking. It gains "When you don't control a legendary Vampire, exile this creature." -SVar:TrigChange:DB$ ChangeZone | Origin$ Graveyard | Destination$ Battlefield | ValidTgts$ Creature.YouOwn | Tapped$ True | Attacking$ True | AnimateSubAbility$ DBAnimate -SVar:DBAnimate:DB$ Animate | Defined$ Remembered | Duration$ Permanent | Triggers$ TrigOlivia +SVar:TrigChange:DB$ ChangeZone | Origin$ Graveyard | Destination$ Battlefield | ValidTgts$ Creature.YouOwn | Tapped$ True | Attacking$ True | RememberChanged$ True | SubAbility$ DBAnimate +SVar:DBAnimate:DB$ Animate | Defined$ Remembered | Duration$ Permanent | Triggers$ TrigOlivia | SubAbility$ DBCleanup +SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True SVar:TrigOlivia:Mode$ Always | TriggerZones$ Battlefield | IsPresent$ Vampire.YouCtrl+Legendary | PresentCompare$ EQ0 | Execute$ TrigExile | TriggerDescription$ When you don't control a legendary Vampire, exile this creature. SVar:TrigExile:DB$ ChangeZone | Defined$ Self | Origin$ Battlefield | Destination$ Exile SVar:HasAttackEffect:TRUE From 58a917e667cca2fc05a1fd5067011e04cde4866e Mon Sep 17 00:00:00 2001 From: tool4EvEr Date: Wed, 4 Jan 2023 20:33:35 +0100 Subject: [PATCH 05/13] Store timestamp to be safe --- .../src/main/java/forge/game/GameAction.java | 12 +++++--- .../ability/effects/AnimateAllEffect.java | 28 +++++++++---------- .../ability/effects/ChangeZoneAllEffect.java | 4 ++- .../ability/effects/ChangeZoneEffect.java | 12 ++++++-- .../forge/game/ability/effects/DigEffect.java | 8 +++--- 5 files changed, 39 insertions(+), 25 deletions(-) diff --git a/forge-game/src/main/java/forge/game/GameAction.java b/forge-game/src/main/java/forge/game/GameAction.java index b671c2a823d..678fa0f9f20 100644 --- a/forge-game/src/main/java/forge/game/GameAction.java +++ b/forge-game/src/main/java/forge/game/GameAction.java @@ -368,10 +368,14 @@ public class GameAction { c.clearEtbCounters(); if (cause != null) { if (cause.hasParam("AnimateSubAbility")) { - c.removeChangedCardKeywords(game.getTimestamp(), 0); - c.removeChangedCardTraits(game.getTimestamp(), 0); - c.removeChangedCardTypes(game.getTimestamp(), 0); - c.removeChangedName(game.getTimestamp(), 0); + long unanimateTimestamp = Long.valueOf(cause.getAdditionalAbility("AnimateSubAbility").getSVar("unanimateTimestamp")); + c.removeChangedCardKeywords(unanimateTimestamp, 0); + c.removeChangedCardTypes(unanimateTimestamp, 0); + c.removeChangedName(unanimateTimestamp, 0); + c.removeNewPT(unanimateTimestamp, 0); + if (c.removeChangedCardTraits(unanimateTimestamp, 0)) { + c.updateStateForView(); + } } if (cause.hasParam("Transformed") || cause.hasParam("FaceDown")) { c.setBackSide(false); diff --git a/forge-game/src/main/java/forge/game/ability/effects/AnimateAllEffect.java b/forge-game/src/main/java/forge/game/ability/effects/AnimateAllEffect.java index 0276fb32bc1..357331bd1d0 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/AnimateAllEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/AnimateAllEffect.java @@ -136,10 +136,10 @@ public class AnimateAllEffect extends AnimateEffectBase { CardCollectionView list; - if (!sa.usesTargeting() && !sa.hasParam("Defined")) { - list = game.getCardsIn(ZoneType.Battlefield); - } else { + if (sa.usesTargeting() || sa.hasParam("Defined")) { list = getTargetPlayers(sa).getCardsIn(ZoneType.Battlefield); + } else { + list = game.getCardsIn(ZoneType.Battlefield); } list = CardLists.getValidCards(list, valid, sa.getActivatingPlayer(), host, sa); @@ -155,18 +155,18 @@ public class AnimateAllEffect extends AnimateEffectBase { game.fireEvent(new GameEventCardStatsChanged(c)); - final GameCommand unanimate = new GameCommand() { - private static final long serialVersionUID = -5861759814760561373L; - - @Override - public void run() { - doUnanimate(c, timestamp); - - game.fireEvent(new GameEventCardStatsChanged(c)); - } - }; - if (!permanent) { + final GameCommand unanimate = new GameCommand() { + private static final long serialVersionUID = -5861759814760561373L; + + @Override + public void run() { + doUnanimate(c, timestamp); + + game.fireEvent(new GameEventCardStatsChanged(c)); + } + }; + addUntilCommand(sa, unanimate); } } diff --git a/forge-game/src/main/java/forge/game/ability/effects/ChangeZoneAllEffect.java b/forge-game/src/main/java/forge/game/ability/effects/ChangeZoneAllEffect.java index 528ac2bbbc2..4436dfafd7f 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/ChangeZoneAllEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/ChangeZoneAllEffect.java @@ -171,9 +171,11 @@ public class ChangeZoneAllEffect extends SpellAbilityEffect { // need LKI before Animate does apply moveParams.put(AbilityKey.CardLKI, CardUtil.getLKICopy(c)); + final SpellAbility animate = sa.getAdditionalAbility("AnimateSubAbility"); source.addRemembered(c); - AbilityUtils.resolve(sa.getAdditionalAbility("AnimateSubAbility")); + AbilityUtils.resolve(animate); source.removeRemembered(c); + animate.setSVar("unanimateTimestamp", String.valueOf(game.getTimestamp())); } if (sa.hasParam("Tapped")) { c.setTapped(true); diff --git a/forge-game/src/main/java/forge/game/ability/effects/ChangeZoneEffect.java b/forge-game/src/main/java/forge/game/ability/effects/ChangeZoneEffect.java index 2deac9438ad..e03ef947792 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/ChangeZoneEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/ChangeZoneEffect.java @@ -671,9 +671,11 @@ public class ChangeZoneEffect extends SpellAbilityEffect { moveParams.put(AbilityKey.CardLKI, CardUtil.getLKICopy(gameCard)); } + final SpellAbility animate = sa.getAdditionalAbility("AnimateSubAbility"); hostCard.addRemembered(gameCard); - AbilityUtils.resolve(sa.getAdditionalAbility("AnimateSubAbility")); + AbilityUtils.resolve(animate); hostCard.removeRemembered(gameCard); + animate.setSVar("unanimateTimestamp", String.valueOf(game.getTimestamp())); } // need to be facedown before it hits the battlefield in case of Replacement Effects or Trigger @@ -1314,9 +1316,11 @@ public class ChangeZoneEffect extends SpellAbilityEffect { // need LKI before Animate does apply moveParams.put(AbilityKey.CardLKI, CardUtil.getLKICopy(c)); + final SpellAbility animate = sa.getAdditionalAbility("AnimateSubAbility"); source.addRemembered(c); - AbilityUtils.resolve(sa.getAdditionalAbility("AnimateSubAbility")); + AbilityUtils.resolve(animate); source.removeRemembered(c); + animate.setSVar("unanimateTimestamp", String.valueOf(game.getTimestamp())); } if (sa.hasParam("GainControl")) { final String g = sa.getParam("GainControl"); @@ -1335,6 +1339,10 @@ public class ChangeZoneEffect extends SpellAbilityEffect { } if (sa.hasParam("Transformed")) { if (c.isDoubleFaced()) { + // need LKI before Animate does apply + if (!moveParams.containsKey(AbilityKey.CardLKI)) { + moveParams.put(AbilityKey.CardLKI, CardUtil.getLKICopy(c)); + } c.changeCardState("Transform", null, sa); } else { // If it can't Transform, don't change zones. diff --git a/forge-game/src/main/java/forge/game/ability/effects/DigEffect.java b/forge-game/src/main/java/forge/game/ability/effects/DigEffect.java index 415ea98a006..e6a92d4cb74 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/DigEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/DigEffect.java @@ -417,13 +417,13 @@ public class DigEffect extends SpellAbilityEffect { } if (sa.hasAdditionalAbility("AnimateSubAbility")) { // need LKI before Animate does apply - if (!moveParams.containsKey(AbilityKey.CardLKI)) { - moveParams.put(AbilityKey.CardLKI, CardUtil.getLKICopy(c)); - } + moveParams.put(AbilityKey.CardLKI, CardUtil.getLKICopy(c)); + final SpellAbility animate = sa.getAdditionalAbility("AnimateSubAbility"); host.addRemembered(c); - AbilityUtils.resolve(sa.getAdditionalAbility("AnimateSubAbility")); + AbilityUtils.resolve(animate); host.removeRemembered(c); + animate.setSVar("unanimateTimestamp", String.valueOf(game.getTimestamp())); } c = game.getAction().moveTo(zone, c, sa, moveParams); if (destZone1.equals(ZoneType.Battlefield)) { From a51582237a187dc1ba92e6cb19ef296b5d96a064 Mon Sep 17 00:00:00 2001 From: tool4EvEr Date: Wed, 4 Jan 2023 20:49:06 +0100 Subject: [PATCH 06/13] Stonehewer Giant fix when it can't ETB --- .../main/java/forge/game/ability/effects/ChangeZoneEffect.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/forge-game/src/main/java/forge/game/ability/effects/ChangeZoneEffect.java b/forge-game/src/main/java/forge/game/ability/effects/ChangeZoneEffect.java index e03ef947792..6679367396b 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/ChangeZoneEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/ChangeZoneEffect.java @@ -1402,7 +1402,7 @@ public class ChangeZoneEffect extends SpellAbilityEffect { movedCard.setTimestamp(ts); - if (sa.hasParam("AttachAfter") && movedCard.isAttachment()) { + if (sa.hasParam("AttachAfter") && movedCard.isAttachment() && movedCard.isInPlay()) { CardCollection list = AbilityUtils.getDefinedCards(source, sa.getParam("AttachAfter"), sa); if (list.isEmpty()) { list = CardLists.getValidCards(game.getCardsIn(ZoneType.Battlefield), sa.getParam("AttachAfter"), c.getController(), c, sa); From e8704e3c5e3b049ab252363622d034e83201f378 Mon Sep 17 00:00:00 2001 From: tool4EvEr Date: Wed, 4 Jan 2023 22:16:02 +0100 Subject: [PATCH 07/13] Fix auras losing all previous traits when they can't ETB --- .../src/main/java/forge/game/GameAction.java | 28 +++++++++++-------- .../cardsfolder/s/swords_to_plowshares.txt | 3 +- 2 files changed, 18 insertions(+), 13 deletions(-) diff --git a/forge-game/src/main/java/forge/game/GameAction.java b/forge-game/src/main/java/forge/game/GameAction.java index 678fa0f9f20..098e5ced755 100644 --- a/forge-game/src/main/java/forge/game/GameAction.java +++ b/forge-game/src/main/java/forge/game/GameAction.java @@ -164,8 +164,8 @@ public class GameAction { } if (!found) { c.clearControllers(); - if (c.removeChangedState()) { - c.updateStateForView(); + if (cause != null) { + unanimateOnAbortedChange(cause, c); } return c; } @@ -367,16 +367,7 @@ public class GameAction { if (repres == ReplacementResult.Prevented) { c.clearEtbCounters(); if (cause != null) { - if (cause.hasParam("AnimateSubAbility")) { - long unanimateTimestamp = Long.valueOf(cause.getAdditionalAbility("AnimateSubAbility").getSVar("unanimateTimestamp")); - c.removeChangedCardKeywords(unanimateTimestamp, 0); - c.removeChangedCardTypes(unanimateTimestamp, 0); - c.removeChangedName(unanimateTimestamp, 0); - c.removeNewPT(unanimateTimestamp, 0); - if (c.removeChangedCardTraits(unanimateTimestamp, 0)) { - c.updateStateForView(); - } - } + unanimateOnAbortedChange(cause, c); if (cause.hasParam("Transformed") || cause.hasParam("FaceDown")) { c.setBackSide(false); c.changeToState(CardStateName.Original); @@ -2577,4 +2568,17 @@ public class GameAction { } return false; } + + private static void unanimateOnAbortedChange(final SpellAbility cause, final Card c) { + if (cause.hasParam("AnimateSubAbility")) { + long unanimateTimestamp = Long.valueOf(cause.getAdditionalAbility("AnimateSubAbility").getSVar("unanimateTimestamp")); + c.removeChangedCardKeywords(unanimateTimestamp, 0); + c.removeChangedCardTypes(unanimateTimestamp, 0); + c.removeChangedName(unanimateTimestamp, 0); + c.removeNewPT(unanimateTimestamp, 0); + if (c.removeChangedCardTraits(unanimateTimestamp, 0)) { + c.updateStateForView(); + } + } + } } diff --git a/forge-gui/res/cardsfolder/s/swords_to_plowshares.txt b/forge-gui/res/cardsfolder/s/swords_to_plowshares.txt index e75a18cde30..7ffd4a071eb 100644 --- a/forge-gui/res/cardsfolder/s/swords_to_plowshares.txt +++ b/forge-gui/res/cardsfolder/s/swords_to_plowshares.txt @@ -2,6 +2,7 @@ Name:Swords to Plowshares ManaCost:W Types:Instant A:SP$ ChangeZone | ValidTgts$ Creature | Origin$ Battlefield | Destination$ Exile | RememberLKI$ True | SubAbility$ DBGainLife | SpellDescription$ Exile target creature. -SVar:DBGainLife:DB$ GainLife | Defined$ RememberedController | LifeAmount$ X | SpellDescription$ Its controller gains life equal to its power. +SVar:DBGainLife:DB$ GainLife | Defined$ RememberedController | LifeAmount$ X | SubAbility$ DBCleanup | SpellDescription$ Its controller gains life equal to its power. +SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True SVar:X:RememberedLKI$CardPower Oracle:Exile target creature. Its controller gains life equal to its power. From 8986a025e7f3fba43908d3c6d0b15c4f6c66ea82 Mon Sep 17 00:00:00 2001 From: tool4EvEr Date: Wed, 4 Jan 2023 22:24:33 +0100 Subject: [PATCH 08/13] Also remove controller --- forge-game/src/main/java/forge/game/GameAction.java | 1 + 1 file changed, 1 insertion(+) diff --git a/forge-game/src/main/java/forge/game/GameAction.java b/forge-game/src/main/java/forge/game/GameAction.java index 098e5ced755..df67f6bf4c6 100644 --- a/forge-game/src/main/java/forge/game/GameAction.java +++ b/forge-game/src/main/java/forge/game/GameAction.java @@ -366,6 +366,7 @@ public class GameAction { if (repres == ReplacementResult.Prevented) { c.clearEtbCounters(); + c.clearControllers(); if (cause != null) { unanimateOnAbortedChange(cause, c); if (cause.hasParam("Transformed") || cause.hasParam("FaceDown")) { From 838fa0cc1f2f82436ad144277b7ae7cc3866dbc5 Mon Sep 17 00:00:00 2001 From: invalidCards <842080+invalidCards@users.noreply.github.com> Date: Wed, 4 Jan 2023 22:34:45 +0100 Subject: [PATCH 09/13] Add SCD precons --- .../Chaos Incarnate [SCD] [2022].dck | 78 +++++++++++++++++++ .../Draconic Destruction [SCD] [2022].dck | 77 ++++++++++++++++++ .../First Flight [SCD] [2022].dck | 77 ++++++++++++++++++ .../Grave Danger [SCD] [2022].dck | 76 ++++++++++++++++++ .../Token Triumph [SCD] [2022].dck | 78 +++++++++++++++++++ 5 files changed, 386 insertions(+) create mode 100644 forge-gui/res/quest/commanderprecons/Chaos Incarnate [SCD] [2022].dck create mode 100644 forge-gui/res/quest/commanderprecons/Draconic Destruction [SCD] [2022].dck create mode 100644 forge-gui/res/quest/commanderprecons/First Flight [SCD] [2022].dck create mode 100644 forge-gui/res/quest/commanderprecons/Grave Danger [SCD] [2022].dck create mode 100644 forge-gui/res/quest/commanderprecons/Token Triumph [SCD] [2022].dck diff --git a/forge-gui/res/quest/commanderprecons/Chaos Incarnate [SCD] [2022].dck b/forge-gui/res/quest/commanderprecons/Chaos Incarnate [SCD] [2022].dck new file mode 100644 index 00000000000..002e211ad06 --- /dev/null +++ b/forge-gui/res/quest/commanderprecons/Chaos Incarnate [SCD] [2022].dck @@ -0,0 +1,78 @@ +[metadata] +name=Chaos Incarnate [SCD] [2022] +[Commander] +1 Kardur, Doomscourge|SCD|1 +[Main] +1 Abrade|SCD|1 +1 Akoum Refuge|SCD|1 +1 Ambition's Cost|SCD|1 +1 Arcane Signet|SCD|1 +1 Archfiend of Depravity|SCD|1 +1 Blasphemous Act|SCD|1 +1 Bloodfell Caves|SCD|1 +1 Bloodgift Demon|SCD|1 +1 Brash Taunter|SCD|1 +1 Breath of Malfegor|SCD|1 +1 Burnished Hart|SCD|1 +1 Chaos Warp|SCD|1 +1 Cinder Barrens|SCD|1 +1 Combustible Gearhulk|SCD|1 +1 Command Tower|SCD|1 +1 Commander's Sphere|SCD|1 +1 Coveted Jewel|SCD|1 +1 Deadly Tempest|SCD|1 +1 Dictate of the Twin Gods|SCD|1 +1 Dredge the Mire|SCD|1 +1 Explosion of Riches|SCD|1 +1 Feed the Swarm|SCD|1 +1 Fiery Confluence|SCD|1 +1 Foreboding Ruins|SCD|1 +1 Geode Rager|SCD|1 +1 Guttersnipe|SCD|1 +1 Hate Mirage|SCD|1 +1 Indulgent Tormentor|SCD|1 +1 Kaervek the Merciless|SCD|1 +1 Kazuul, Tyrant of the Cliffs|SCD|1 +1 Lightning Greaves|SCD|1 +1 Magmatic Force|SCD|1 +1 Mana Geyser|SCD|1 +1 Molten Slagheap|SCD|1 +14 Mountain|SCD|1 +1 Myriad Landscape|SCD|1 +1 Nihil Spellbomb|SCD|1 +1 Ob Nixilis Reignited|SCD|1 +1 Profane Command|SCD|1 +1 Rakdos Charm|SCD|1 +1 Rakdos Signet|SCD|1 +1 Rakshasa Debaser|SCD|1 +1 Read the Bones|SCD|1 +1 Reign of the Pit|SCD|1 +1 Sangromancer|SCD|1 +1 Scythe Specter|SCD|1 +1 Sepulchral Primordial|SCD|1 +1 Sign in Blood|SCD|1 +1 Smoldering Marsh|SCD|1 +1 Sol Ring|SCD|1 +1 Solemn Simulacrum|SCD|1 +1 Soul Shatter|SCD|1 +1 Spiteful Visions|SCD|1 +1 Stensia Bloodhall|SCD|1 +1 Stormfist Crusader|SCD|1 +1 Sunbird's Invocation|SCD|1 +15 Swamp|SCD|1 +1 Syphon Mind|SCD|1 +1 Talisman of Indulgence|SCD|1 +1 Tectonic Giant|SCD|1 +1 Temple of Malice|SCD|1 +1 Terminate|SCD|1 +1 Theater of Horrors|SCD|1 +1 Thermo-Alchemist|SCD|1 +1 Titan Hunter|SCD|1 +1 Unlicensed Disintegration|SCD|1 +1 Urborg Volcano|SCD|1 +1 Vampire Nighthawk|SCD|1 +1 Wayfarer's Bauble|SCD|1 +1 Wild Ricochet|SCD|1 +1 Wildfire Devils|SCD|1 +1 Worn Powerstone|SCD|1 +[Sideboard] diff --git a/forge-gui/res/quest/commanderprecons/Draconic Destruction [SCD] [2022].dck b/forge-gui/res/quest/commanderprecons/Draconic Destruction [SCD] [2022].dck new file mode 100644 index 00000000000..b74f90379c1 --- /dev/null +++ b/forge-gui/res/quest/commanderprecons/Draconic Destruction [SCD] [2022].dck @@ -0,0 +1,77 @@ +[metadata] +name=Draconic Destruction [SCD] [2022] +[Commander] +1 Atarka, World Render|SCD|1 +[Main] +1 Akoum Hellkite|SCD|1 +1 Arcane Signet|SCD|1 +1 Atarka Monument|SCD|1 +1 Beast Within|SCD|1 +1 Blossoming Defense|SCD|1 +1 Chain Reaction|SCD|1 +1 Cinder Glade|SCD|1 +1 Clan Defiance|SCD|1 +1 Command Tower|SCD|1 +1 Commander's Sphere|SCD|1 +1 Crucible of Fire|SCD|1 +1 Cultivate|SCD|1 +1 Demanding Dragon|SCD|1 +1 Draconic Disciple|SCD|1 +1 Dragon Mage|SCD|1 +1 Dragon Tempest|SCD|1 +1 Dragon's Hoard|SCD|1 +1 Dragonkin Berserker|SCD|1 +1 Dragonlord's Servant|SCD|1 +1 Dragonmaster Outcast|SCD|1 +1 Dragonspeaker Shaman|SCD|1 +1 Drakuseth, Maw of Flames|SCD|1 +1 Dream Pillager|SCD|1 +1 Drumhunter|SCD|1 +1 Elemental Bond|SCD|1 +1 Fires of Yavimaya|SCD|1 +1 Flameblast Dragon|SCD|1 +1 Foe-Razer Regent|SCD|1 +12 Forest|SCD|1 +1 Frontier Siege|SCD|1 +1 Furnace Whelp|SCD|1 +1 Game Trail|SCD|1 +1 Garruk's Uprising|SCD|1 +1 Harbinger of the Hunt|SCD|1 +1 Harmonize|SCD|1 +1 Haven of the Spirit Dragon|SCD|1 +1 Hoard-Smelter Dragon|SCD|1 +1 Hunter's Insight|SCD|1 +1 Hunter's Prowess|SCD|1 +1 Kazandu Refuge|SCD|1 +1 Loaming Shaman|SCD|1 +1 Magmaquake|SCD|1 +1 Mordant Dragon|SCD|1 +18 Mountain|SCD|1 +1 Path of Ancestry|SCD|1 +1 Primal Might|SCD|1 +1 Provoke the Trolls|SCD|1 +1 Rapacious Dragon|SCD|1 +1 Return to Nature|SCD|1 +1 Rugged Highlands|SCD|1 +1 Runehorn Hellkite|SCD|1 +1 Sakura-Tribe Elder|SCD|1 +1 Sarkhan, the Dragonspeaker|SCD|1 +1 Savage Ventmaw|SCD|1 +1 Scourge of Valkas|SCD|1 +1 Shamanic Revelation|SCD|1 +1 Shivan Oasis|SCD|1 +1 Sol Ring|SCD|1 +1 Spit Flame|SCD|1 +1 Steel Hellkite|SCD|1 +1 Sweltering Suns|SCD|1 +1 Swiftfoot Boots|SCD|1 +1 Talisman of Impulse|SCD|1 +1 Temple of Abandon|SCD|1 +1 Thunderbreak Regent|SCD|1 +1 Thundermaw Hellkite|SCD|1 +1 Timber Gorge|SCD|1 +1 Tyrant's Familiar|SCD|1 +1 Unleash Fury|SCD|1 +1 Vandalblast|SCD|1 +1 Verix Bladewing|SCD|1 +[Sideboard] diff --git a/forge-gui/res/quest/commanderprecons/First Flight [SCD] [2022].dck b/forge-gui/res/quest/commanderprecons/First Flight [SCD] [2022].dck new file mode 100644 index 00000000000..04aae9fa3b6 --- /dev/null +++ b/forge-gui/res/quest/commanderprecons/First Flight [SCD] [2022].dck @@ -0,0 +1,77 @@ +[metadata] +name=First Flight [SCD] [2022] +[Commander] +1 Isperia, Supreme Judge|SCD|1 +[Main] +1 Absorb|SCD|1 +1 Aetherize|SCD|1 +1 Angler Turtle|SCD|1 +1 Arcane Signet|SCD|1 +1 Archon of Redemption|SCD|1 +1 Aven Gagglemaster|SCD|1 +1 Azorius Signet|SCD|1 +1 Banishing Light|SCD|1 +1 Bident of Thassa|SCD|1 +1 Cartographer's Hawk|SCD|1 +1 Cleansing Nova|SCD|1 +1 Cloudblazer|SCD|1 +1 Coastal Tower|SCD|1 +1 Command Tower|SCD|1 +1 Commander's Sphere|SCD|1 +1 Condemn|SCD|1 +1 Counterspell|SCD|1 +1 Crush Contraband|SCD|1 +1 Diluvian Primordial|SCD|1 +1 Disenchant|SCD|1 +1 Emeria Angel|SCD|1 +1 Empyrean Eagle|SCD|1 +1 Ever-Watching Threshold|SCD|1 +1 Faerie Formation|SCD|1 +1 Favorable Winds|SCD|1 +1 Generous Gift|SCD|1 +1 Gideon Jura|SCD|1 +1 Gravitational Shift|SCD|1 +1 Hanged Executioner|SCD|1 +1 Hedron Archive|SCD|1 +1 Inspired Sphinx|SCD|1 +15 Island|SCD|1 +1 Jubilant Skybonder|SCD|1 +1 Kangee's Lieutenant|SCD|1 +1 Kangee, Sky Warden|SCD|1 +1 Meandering River|SCD|1 +1 Migratory Route|SCD|1 +1 Moorland Haunt|SCD|1 +1 Negate|SCD|1 +1 Pilgrim's Eye|SCD|1 +15 Plains|SCD|1 +1 Port Town|SCD|1 +1 Prairie Stream|SCD|1 +1 Rally of Wings|SCD|1 +1 Remorseful Cleric|SCD|1 +1 Sejiri Refuge|SCD|1 +1 Sephara, Sky's Blade|SCD|1 +1 Sharding Sphinx|SCD|1 +1 Sky Diamond|SCD|1 +1 Skycat Sovereign|SCD|1 +1 Skyscanner|SCD|1 +1 Sol Ring|SCD|1 +1 Soul Snare|SCD|1 +1 Sphinx of Enlightenment|SCD|1 +1 Sphinx's Revelation|SCD|1 +1 Staggering Insight|SCD|1 +1 Steel-Plume Marshal|SCD|1 +1 Storm Herd|SCD|1 +1 Swords to Plowshares|SCD|1 +1 Talisman of Progress|SCD|1 +1 Temple of Enlightenment|SCD|1 +1 Thought Vessel +1 Thunderclap Wyvern|SCD|1 +1 Tide Skimmer|SCD|1 +1 Time Wipe|SCD|1 +1 Tranquil Cove|SCD|1 +1 True Conviction|SCD|1 +1 Vow of Duty|SCD|1 +1 Warden of Evos Isle|SCD|1 +1 Windreader Sphinx|SCD|1 +1 Winged Words|SCD|1 +[Sideboard] diff --git a/forge-gui/res/quest/commanderprecons/Grave Danger [SCD] [2022].dck b/forge-gui/res/quest/commanderprecons/Grave Danger [SCD] [2022].dck new file mode 100644 index 00000000000..2651ec5b0d4 --- /dev/null +++ b/forge-gui/res/quest/commanderprecons/Grave Danger [SCD] [2022].dck @@ -0,0 +1,76 @@ +[metadata] +name=Grave Danger [SCD] [2022] +[Commander] +1 Gisa and Geralf|SCD|1 +[Main] +1 Arcane Signet|SCD|1 +1 Army of the Damned|SCD|1 +1 Cemetery Reaper|SCD|1 +1 Champion of the Perished|SCD|1 +1 Choked Estuary|SCD|1 +1 Command Tower|SCD|1 +1 Commander's Sphere|SCD|1 +1 Crippling Fear|SCD|1 +1 Cruel Revival|SCD|1 +1 Curse of Disturbance|SCD|1 +1 Deep Analysis|SCD|1 +1 Dimir Signet|SCD|1 +1 Diregraf Captain|SCD|1 +1 Dismal Backwater|SCD|1 +1 Distant Melody|SCD|1 +1 Enter the God-Eternals|SCD|1 +1 Eternal Skylord|SCD|1 +1 Feed the Swarm|SCD|1 +1 Fleshbag Marauder|SCD|1 +1 Geralf's Mindcrusher|SCD|1 +1 Gleaming Overseer|SCD|1 +1 Gravespawn Sovereign|SCD|1 +1 Gray Merchant of Asphodel|SCD|1 +1 Grimoire of the Dead|SCD|1 +1 Havengul Lich|SCD|1 +1 Heraldic Banner|SCD|1 +13 Island|SCD|1 +1 Josu Vess, Lich Knight|SCD|1 +1 Jwar Isle Refuge|SCD|1 +1 Laboratory Drudge|SCD|1 +1 Lazotep Plating|SCD|1 +1 Lazotep Reaver|SCD|1 +1 Liliana's Devotee|SCD|1 +1 Liliana's Mastery|SCD|1 +1 Liliana's Standard Bearer|SCD|1 +1 Liliana, Untouched by Death|SCD|1 +1 Lord of the Accursed|SCD|1 +1 Lotleth Giant|SCD|1 +1 Loyal Subordinate|SCD|1 +1 Midnight Reaper|SCD|1 +1 Mire Triton|SCD|1 +1 Murder|SCD|1 +1 Necromantic Selection|SCD|1 +1 Necrotic Hex|SCD|1 +1 Open the Graves|SCD|1 +1 Overseer of the Damned|SCD|1 +1 Pilfered Plans|SCD|1 +1 Salt Marsh|SCD|1 +1 Scourge of Nel Toth|SCD|1 +1 Sinister Sabotage|SCD|1 +1 Sol Ring|SCD|1 +1 Spark Reaper|SCD|1 +1 Submerged Boneyard|SCD|1 +1 Sunken Hollow|SCD|1 +18 Swamp|SCD|1 +1 Syphon Flesh|SCD|1 +1 Talisman of Dominance|SCD|1 +1 Temple of Deceit|SCD|1 +1 Unbreathing Horde|SCD|1 +1 Undead Augur|SCD|1 +1 Undermine|SCD|1 +1 Unstable Obelisk|SCD|1 +1 Vampiric Rites|SCD|1 +1 Vela the Night-Clad|SCD|1 +1 Vengeful Dead|SCD|1 +1 Victimize|SCD|1 +1 Vizier of the Scorpion|SCD|1 +1 Wayfarer's Bauble|SCD|1 +1 Withered Wretch|SCD|1 +1 Zombie Apocalypse|SCD|1 +[Sideboard] diff --git a/forge-gui/res/quest/commanderprecons/Token Triumph [SCD] [2022].dck b/forge-gui/res/quest/commanderprecons/Token Triumph [SCD] [2022].dck new file mode 100644 index 00000000000..660ec3d15ca --- /dev/null +++ b/forge-gui/res/quest/commanderprecons/Token Triumph [SCD] [2022].dck @@ -0,0 +1,78 @@ +[metadata] +name=Token Triumph [SCD] [2022] +[Commander] +1 Emmara, Soul of the Accord|SCD|1 +[Main] +1 Ajani, Caller of the Pride|SCD|1 +1 Arcane Signet|SCD|1 +1 Aura Mutation|SCD|1 +1 Avacyn's Pilgrim|SCD|1 +1 Blossoming Sands|SCD|1 +1 Camaraderie|SCD|1 +1 Canopy Vista|SCD|1 +1 Champion of Lambholt|SCD|1 +1 Citanul Hierophants|SCD|1 +1 Citywide Bust|SCD|1 +1 Collective Blessing|SCD|1 +1 Collective Unconscious|SCD|1 +1 Command Tower|SCD|1 +1 Commander's Insignia|SCD|1 +1 Commander's Sphere|SCD|1 +1 Conclave Tribunal|SCD|1 +1 Curse of Bounty|SCD|1 +1 Dauntless Escort|SCD|1 +1 Dawn of Hope|SCD|1 +1 Devouring Light|SCD|1 +1 Dictate of Heliod|SCD|1 +1 Elfhame Palace|SCD|1 +1 Eternal Witness|SCD|1 +1 Farhaven Elf|SCD|1 +1 Felidar Retreat|SCD|1 +15 Forest|SCD|1 +1 Fortified Village|SCD|1 +1 Graypelt Refuge|SCD|1 +1 Great Oak Guardian|SCD|1 +1 Harmonize|SCD|1 +1 Harvest Season|SCD|1 +1 Holdout Settlement|SCD|1 +1 Hornet Nest|SCD|1 +1 Hornet Queen|SCD|1 +1 Hour of Reckoning|SCD|1 +1 Idol of Oblivion|SCD|1 +1 Jade Mage|SCD|1 +1 Jaspera Sentinel|SCD|1 +1 Karametra's Favor|SCD|1 +1 Leafkin Druid|SCD|1 +1 Loyal Guardian|SCD|1 +1 Maja, Bretagard Protector|SCD|1 +1 March of the Multitudes|SCD|1 +1 Mentor of the Meek|SCD|1 +1 Nissa's Expedition|SCD|1 +1 Nullmage Shepherd|SCD|1 +1 Overrun|SCD|1 +1 Overwhelming Instinct|SCD|1 +1 Path to Exile|SCD|1 +14 Plains|SCD|1 +1 Presence of Gond|SCD|1 +1 Reclamation Sage|SCD|1 +1 Rishkar, Peema Renegade|SCD|1 +1 Rootborn Defenses|SCD|1 +1 Scatter the Seeds|SCD|1 +1 Scavenging Ooze|SCD|1 +1 Selesnya Evangel|SCD|1 +1 Selesnya Guildmage|SCD|1 +1 Slate of Ancestry|SCD|1 +1 Sol Ring|SCD|1 +1 Sporemound|SCD|1 +1 Sylvan Reclamation|SCD|1 +1 Talisman of Unity|SCD|1 +1 Temple of Plenty|SCD|1 +1 Thunderfoot Baloth|SCD|1 +1 Tranquil Expanse|SCD|1 +1 Trostani Discordant|SCD|1 +1 Valor in Akros|SCD|1 +1 Verdant Force|SCD|1 +1 Vitu-Ghazi, the City-Tree|SCD|1 +1 Voice of Many|SCD|1 +1 White Sun's Zenith|SCD|1 +[Sideboard] From 50966d3f7a1ecfc20322e5c212dd9b18b0eb8952 Mon Sep 17 00:00:00 2001 From: tool4EvEr Date: Wed, 4 Jan 2023 22:43:17 +0100 Subject: [PATCH 10/13] Morph fix --- forge-gui/src/main/java/forge/player/HumanPlay.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/forge-gui/src/main/java/forge/player/HumanPlay.java b/forge-gui/src/main/java/forge/player/HumanPlay.java index 9b5cc2c5dc0..07cd9c0b126 100644 --- a/forge-gui/src/main/java/forge/player/HumanPlay.java +++ b/forge-gui/src/main/java/forge/player/HumanPlay.java @@ -114,9 +114,11 @@ public class HumanPlay { final HumanPlaySpellAbility req = new HumanPlaySpellAbility(controller, sa); if (!req.playAbility(true, false, false)) { - if (flippedToCast && !castFaceDown) { + Card rollback = p.getGame().getCardState(sa.getHostCard()); + if (castFaceDown) { + rollback.setFaceDown(false); + } else if (flippedToCast) { // need to get the changed card if able - Card rollback = p.getGame().getCardState(sa.getHostCard()); rollback.turnFaceDown(true); //need to set correct imagekey when forcing facedown rollback.setImageKey(ImageKeys.getTokenKey(isforetold ? ImageKeys.FORETELL_IMAGE : ImageKeys.HIDDEN_CARD)); From 58eb11ec56d724282e3ef3893e2bce094b1bb690 Mon Sep 17 00:00:00 2001 From: Anthony Calosa Date: Thu, 5 Jan 2023 12:25:39 +0800 Subject: [PATCH 11/13] ValidCard$ Self to ValidCard$ Card.Self fixes Moonshae Pixie --- forge-gui/res/cardsfolder/b/bow_to_my_command.txt | 2 +- forge-gui/res/cardsfolder/m/moonshae_pixie.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/forge-gui/res/cardsfolder/b/bow_to_my_command.txt b/forge-gui/res/cardsfolder/b/bow_to_my_command.txt index d6eba81a67d..11145dd97f4 100644 --- a/forge-gui/res/cardsfolder/b/bow_to_my_command.txt +++ b/forge-gui/res/cardsfolder/b/bow_to_my_command.txt @@ -16,7 +16,7 @@ SVar:RepeatOpp:DB$ RepeatEach | RepeatSubAbility$ ChooseCardsToTap | RepeatPlaye SVar:ChooseCardsToTap:DB$ ChooseCard | Defined$ Opponent | MinAmount$ 0 | Amount$ NumCreatures | Choices$ Creature.untapped+RememberedPlayerCtrl | ChoiceTitle$ Choose any number of untapped creatures you control | ChoiceZone$ Battlefield | RememberChosen$ True | AILogic$ BowToMyCommand | SubAbility$ TapChosenCards SVar:TapChosenCards:DB$ Tap | Defined$ Remembered | SubAbility$ AbandonSelf | ConditionCheckSVar$ TappedCreaturePower | ConditionSVarCompare$ GE8 SVar:AbandonSelf:DB$ Abandon | SubAbility$ DBCleanup | ConditionCheckSVar$ TappedCreaturePower | ConditionSVarCompare$ GE8 -T:Mode$ Abandoned | ValidCard$ Self | Execute$ DBCleanup +T:Mode$ Abandoned | ValidCard$ Card.Self | Execute$ DBCleanup SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True | ClearChosenCard$ True SVar:NumCreatures:Count$Valid Creature.RememberedPlayerCtrl SVar:TappedCreaturePower:Count$SumPower_Card.IsRemembered diff --git a/forge-gui/res/cardsfolder/m/moonshae_pixie.txt b/forge-gui/res/cardsfolder/m/moonshae_pixie.txt index f2f45c2d8ca..48bccc005b6 100644 --- a/forge-gui/res/cardsfolder/m/moonshae_pixie.txt +++ b/forge-gui/res/cardsfolder/m/moonshae_pixie.txt @@ -3,7 +3,7 @@ ManaCost:3 U Types:Creature Faerie PT:2/2 K:Flying -T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Self | Execute$ TrigDraw | TriggerDescription$ When CARDNAME enters the battlefield, draw cards equal to the number of opponents who were dealt combat damage this turn. +T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigDraw | TriggerDescription$ When CARDNAME enters the battlefield, draw cards equal to the number of opponents who were dealt combat damage this turn. SVar:TrigDraw:DB$ Draw | NumCards$ X SVar:X:PlayerCountRegisteredOpponents$HasPropertywasDealtCombatDamageThisTurn AlternateMode:Adventure From b1df2d10081d4939d538a48d3e8c05b8adba9937 Mon Sep 17 00:00:00 2001 From: TRT <> Date: Thu, 5 Jan 2023 16:10:50 +0100 Subject: [PATCH 12/13] Fix DiscardedThisTurn --- forge-ai/src/main/java/forge/ai/ComputerUtil.java | 9 +++++---- forge-ai/src/main/java/forge/ai/ComputerUtilMana.java | 9 +++++---- .../src/main/java/forge/ai/ability/ManaEffectAi.java | 1 + forge-ai/src/main/java/forge/ai/ability/ManifestAi.java | 2 +- .../forge/game/ability/effects/ChangeZoneAllEffect.java | 3 +-- .../forge/game/ability/effects/ChangeZoneEffect.java | 2 +- forge-game/src/main/java/forge/game/card/Card.java | 6 +++--- .../src/main/java/forge/game/card/CardProperty.java | 3 +-- forge-game/src/main/java/forge/game/player/Player.java | 3 +++ .../java/forge/game/spellability/AbilityManaPart.java | 4 ++-- .../main/java/forge/player/HumanPlaySpellAbility.java | 2 +- 11 files changed, 24 insertions(+), 20 deletions(-) diff --git a/forge-ai/src/main/java/forge/ai/ComputerUtil.java b/forge-ai/src/main/java/forge/ai/ComputerUtil.java index bd12ad6c7e0..8ab5228fc26 100644 --- a/forge-ai/src/main/java/forge/ai/ComputerUtil.java +++ b/forge-ai/src/main/java/forge/ai/ComputerUtil.java @@ -1063,6 +1063,10 @@ public class ComputerUtil { return true; } + if (cardState.hasKeyword(Keyword.EXALTED) || cardState.hasKeyword(Keyword.EXTORT)) { + return true; + } + if (cardState.hasKeyword(Keyword.RIOT) && ChooseGenericEffectAi.preferHasteForRiot(sa, ai)) { // Planning to choose Haste for Riot, so do this in Main 1 return true; @@ -1070,6 +1074,7 @@ public class ComputerUtil { // if we have non-persistent mana in our pool, would be good to try to use it and not waste it if (ai.getManaPool().willManaBeLostAtEndOfPhase()) { + // TODO should check if some will be kept and skip those boolean canUseToPayCost = false; for (byte color : ManaAtom.MANATYPES) { // tries to reuse any amount of colorless if cost only has generic @@ -1089,10 +1094,6 @@ public class ComputerUtil { return true; } - if (cardState.hasKeyword(Keyword.EXALTED) || cardState.hasKeyword(Keyword.EXTORT)) { - return true; - } - //cast equipments in Main1 when there are creatures to equip and no other unequipped equipment if (card.isEquipment()) { boolean playNow = false; diff --git a/forge-ai/src/main/java/forge/ai/ComputerUtilMana.java b/forge-ai/src/main/java/forge/ai/ComputerUtilMana.java index f08f5a69332..87cc9c397b7 100644 --- a/forge-ai/src/main/java/forge/ai/ComputerUtilMana.java +++ b/forge-ai/src/main/java/forge/ai/ComputerUtilMana.java @@ -93,6 +93,7 @@ public class ComputerUtilMana { ability.setActivatingPlayer(card.getController(), true); if (ability.isManaAbility()) { score += ability.calculateScoreForManaAbility(); + // TODO check TriggersWhenSpent } else if (!ability.isTrigger() && ability.isPossible()) { score += 13; //add 13 for any non-mana activated abilities @@ -393,9 +394,9 @@ public class ComputerUtilMana { String manaProduced = toPay.isSnow() && hostCard.isSnow() ? "S" : GameActionUtil.generatedTotalMana(saPayment); //String originalProduced = manaProduced; - final Map repParams = AbilityKey.mapFromPlayer(ai); + final Map repParams = AbilityKey.mapFromAffected(hostCard); repParams.put(AbilityKey.Mana, manaProduced); - repParams.put(AbilityKey.Affected, hostCard); + repParams.put(AbilityKey.Activator, ai); repParams.put(AbilityKey.AbilityMana, saPayment); // RootAbility // TODO Damping Sphere might replace later? @@ -1614,9 +1615,9 @@ public class ComputerUtilMana { // setup produce mana replacement effects String origin = mp.getOrigProduced(); - final Map repParams = AbilityKey.mapFromPlayer(ai); + final Map repParams = AbilityKey.mapFromAffected(sourceCard); repParams.put(AbilityKey.Mana, origin); - repParams.put(AbilityKey.Affected, sourceCard); + repParams.put(AbilityKey.Activator, ai); repParams.put(AbilityKey.AbilityMana, m); // RootAbility List reList = game.getReplacementHandler().getReplacementList(ReplacementType.ProduceMana, repParams, ReplacementLayer.Other); diff --git a/forge-ai/src/main/java/forge/ai/ability/ManaEffectAi.java b/forge-ai/src/main/java/forge/ai/ability/ManaEffectAi.java index 22c5a093338..eb8104e47db 100644 --- a/forge-ai/src/main/java/forge/ai/ability/ManaEffectAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/ManaEffectAi.java @@ -266,6 +266,7 @@ public class ManaEffectAi extends SpellAbilityAi { ManaPool mp = ai.getManaPool(); Mana test = null; if (mp.isEmpty()) { + // TODO use color from ability test = new Mana((byte) ManaAtom.COLORLESS, source, null); mp.addMana(test, false); } diff --git a/forge-ai/src/main/java/forge/ai/ability/ManifestAi.java b/forge-ai/src/main/java/forge/ai/ability/ManifestAi.java index b807069b859..891fabb44ba 100644 --- a/forge-ai/src/main/java/forge/ai/ability/ManifestAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/ManifestAi.java @@ -101,7 +101,7 @@ public class ManifestAi extends SpellAbilityAi { repParams.put(AbilityKey.Origin, card.getZone().getZoneType()); repParams.put(AbilityKey.Destination, ZoneType.Battlefield); repParams.put(AbilityKey.Source, sa.getHostCard()); - List list = game.getReplacementHandler().getReplacementList(ReplacementType.Moved, repParams, ReplacementLayer.Other); + List list = game.getReplacementHandler().getReplacementList(ReplacementType.Moved, repParams, ReplacementLayer.CantHappen); if (!list.isEmpty()) { return false; } diff --git a/forge-game/src/main/java/forge/game/ability/effects/ChangeZoneAllEffect.java b/forge-game/src/main/java/forge/game/ability/effects/ChangeZoneAllEffect.java index 4436dfafd7f..9468d9a3cd4 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/ChangeZoneAllEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/ChangeZoneAllEffect.java @@ -94,8 +94,7 @@ public class ChangeZoneAllEffect extends SpellAbilityEffect { if (!libCards.isEmpty()) { sa.getActivatingPlayer().getController().reveal(libCards, ZoneType.Library, libCards.get(0).getOwner()); } - final Map runParams = AbilityKey.newMap(); - runParams.put(AbilityKey.Player, sa.getActivatingPlayer()); + final Map runParams = AbilityKey.mapFromPlayer(sa.getActivatingPlayer()); runParams.put(AbilityKey.Target, tgtPlayers); game.getTriggerHandler().runTrigger(TriggerType.SearchedLibrary, runParams, false); } diff --git a/forge-game/src/main/java/forge/game/ability/effects/ChangeZoneEffect.java b/forge-game/src/main/java/forge/game/ability/effects/ChangeZoneEffect.java index 89e84303122..68d6f40bac1 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/ChangeZoneEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/ChangeZoneEffect.java @@ -782,7 +782,7 @@ public class ChangeZoneEffect extends SpellAbilityEffect { } if (sa.hasParam("TrackDiscarded")) { - movedCard.setMadnessWithoutCast(true); + movedCard.setDiscarded(true); } } } 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 696cea94a1a..ad4f65c5634 100644 --- a/forge-game/src/main/java/forge/game/card/Card.java +++ b/forge-game/src/main/java/forge/game/card/Card.java @@ -226,7 +226,7 @@ public class Card extends GameEntity implements Comparable, IHasSVars { private boolean tributed = false; private boolean embalmed = false; private boolean eternalized = false; - private boolean madnessWithoutCast = false; + private boolean discarded = false; private boolean flipped = false; private boolean facedown = false; @@ -5801,8 +5801,8 @@ public class Card extends GameEntity implements Comparable, IHasSVars { } return getCastSA().isMadness(); } - public boolean getMadnessWithoutCast() { return madnessWithoutCast; } - public void setMadnessWithoutCast(boolean state) { madnessWithoutCast = state; } + public boolean wasDiscarded() { return discarded; } + public void setDiscarded(boolean state) { discarded = state; } public final boolean isMonstrous() { return monstrous; 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 1cd1130174a..2d13ad41bb2 100644 --- a/forge-game/src/main/java/forge/game/card/CardProperty.java +++ b/forge-game/src/main/java/forge/game/card/CardProperty.java @@ -984,8 +984,7 @@ public class CardProperty { return false; } - List cards = CardUtil.getThisTurnEntered(ZoneType.Graveyard, ZoneType.Hand, "Card", source, spellAbility); - if (!cards.contains(card) && !card.getMadnessWithoutCast()) { + if (!card.wasDiscarded()) { return false; } } else if (property.startsWith("ControlledByPlayerInTheDirection")) { 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 50191ded7fd..747fb5dfc9b 100644 --- a/forge-game/src/main/java/forge/game/player/Player.java +++ b/forge-game/src/main/java/forge/game/player/Player.java @@ -1444,6 +1444,9 @@ public class Player extends GameEntity implements Comparable { newCard = game.getAction().moveToGraveyard(c, sa, params); // Play the Discard sound } + + newCard.setDiscarded(true); + if (table != null) { table.put(origin, newCard.getZone().getZoneType(), newCard); } 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 b95f6119b3a..c5966bf587c 100644 --- a/forge-game/src/main/java/forge/game/spellability/AbilityManaPart.java +++ b/forge-game/src/main/java/forge/game/spellability/AbilityManaPart.java @@ -664,9 +664,9 @@ public class AbilityManaPart implements java.io.Serializable { // check for produce mana replacement effects - they mess this up, so just use the mana ability final Card source = am.getHostCard(); final Player activator = am.getActivatingPlayer(); - final Map repParams = AbilityKey.mapFromPlayer(activator); + final Map repParams = AbilityKey.mapFromAffected(source); repParams.put(AbilityKey.Mana, getOrigProduced()); - repParams.put(AbilityKey.Affected, source); + repParams.put(AbilityKey.Activator, activator); repParams.put(AbilityKey.AbilityMana, am.getRootAbility()); if (!source.getGame().getReplacementHandler().getReplacementList(ReplacementType.ProduceMana, repParams, ReplacementLayer.Other).isEmpty()) { diff --git a/forge-gui/src/main/java/forge/player/HumanPlaySpellAbility.java b/forge-gui/src/main/java/forge/player/HumanPlaySpellAbility.java index 43994257c45..f5010b2f039 100644 --- a/forge-gui/src/main/java/forge/player/HumanPlaySpellAbility.java +++ b/forge-gui/src/main/java/forge/player/HumanPlaySpellAbility.java @@ -173,7 +173,7 @@ public class HumanPlaySpellAbility { if (ability.getHostCard().isMadness()) { // if a player failed to play madness cost, move the card to graveyard Card newCard = game.getAction().moveToGraveyard(c, null); - newCard.setMadnessWithoutCast(true); + newCard.setDiscarded(true); } } else { payment.refundPayment(); From ef14fb934a2c0ac8a9091d2b3fbed5ea08eb0518 Mon Sep 17 00:00:00 2001 From: sky81 Date: Fri, 6 Jan 2023 09:07:04 +0200 Subject: [PATCH 13/13] Do not clear POI changes after reload --- forge-gui-mobile/src/forge/adventure/world/WorldSave.java | 1 - 1 file changed, 1 deletion(-) diff --git a/forge-gui-mobile/src/forge/adventure/world/WorldSave.java b/forge-gui-mobile/src/forge/adventure/world/WorldSave.java index aa787f54634..a8957a458ab 100644 --- a/forge-gui-mobile/src/forge/adventure/world/WorldSave.java +++ b/forge-gui-mobile/src/forge/adventure/world/WorldSave.java @@ -79,7 +79,6 @@ public class WorldSave { System.err.println("Generating New World"); currentSave.world.generateNew(0); } - currentSave.pointOfInterestChanges.clear(); currentSave.onLoadList.emit();