From 1714cac0c21ab5b58c9c9a73e8019866ab71899a Mon Sep 17 00:00:00 2001 From: paulsnoops Date: Sun, 10 Jul 2022 12:25:10 +0100 Subject: [PATCH 1/4] Rescue the Foal add Oracle: --- .../cardsfolder/upcoming/pegasus_guardian_rescue_the_foal.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/forge-gui/res/cardsfolder/upcoming/pegasus_guardian_rescue_the_foal.txt b/forge-gui/res/cardsfolder/upcoming/pegasus_guardian_rescue_the_foal.txt index 6c8dd5fbbd5..27edc434df3 100644 --- a/forge-gui/res/cardsfolder/upcoming/pegasus_guardian_rescue_the_foal.txt +++ b/forge-gui/res/cardsfolder/upcoming/pegasus_guardian_rescue_the_foal.txt @@ -17,4 +17,4 @@ Types:Instant Adventure A:SP$ ChangeZone | ValidTgts$ Creature.YouCtrl | Origin$ Battlefield | Destination$ Exile | TgtPrompt$ Select target creature you control | RememberTargets$ True | SubAbility$ DBReturn | StackDescription$ Exile {c:Targeted}, | SpellDescription$ Exile target creature you control, then return that card to the battlefield under its owner's control. SVar:DBReturn:DB$ ChangeZone | Defined$ Remembered | Origin$ Exile | Destination$ Battlefield | SubAbility$ DBCleanup | StackDescription$ then return it to the battlefield under its owner's control. SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True -Exile target creature you control, then return that card to the battlefield under its owner's control. (Then exile this card. You may cast the creature later from exile.) +Oracle:Exile target creature you control, then return that card to the battlefield under its owner's control. (Then exile this card. You may cast the creature later from exile.) From ecb54c6fce475c9bc5e3399651cb563c9e849ad5 Mon Sep 17 00:00:00 2001 From: t-w-o-s-a-t <103374130+t-w-o-s-a-t@users.noreply.github.com> Date: Sun, 10 Jul 2022 14:46:55 +0200 Subject: [PATCH 2/4] Update de-DE.properties translated lblAttackers --- forge-gui/res/languages/de-DE.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/forge-gui/res/languages/de-DE.properties b/forge-gui/res/languages/de-DE.properties index fea5db7fbc5..2dd48a9348a 100644 --- a/forge-gui/res/languages/de-DE.properties +++ b/forge-gui/res/languages/de-DE.properties @@ -1503,7 +1503,7 @@ lblAttachee=Anhang lblNumberBlockers=Anzahl Blocker lblBlocker=Blocker #TriggerAttackerBlockedOnce.java -lblAttackers=Attackers +lblAttackers=Angreifer #TriggerAttackersDeclared.java lblNumberAttackers=Anzahl Angreifer #TriggerAttackerUnblockedOnce.java From ec3f7e00ca0e1e787d531a45d7505a30f47d09a8 Mon Sep 17 00:00:00 2001 From: tool4ever Date: Sun, 10 Jul 2022 15:51:56 +0200 Subject: [PATCH 3/4] Fix AI casting suspended spells against Drannith Magistrate (#1098) * Cleanup cards * Fix AI casting suspended spells against Drannith Magistrate Co-authored-by: tool4EvEr --- .../src/main/java/forge/ai/ComputerUtil.java | 30 ++++++++++++- .../java/forge/ai/PlayerControllerAi.java | 2 +- .../main/java/forge/game/GameActionUtil.java | 43 ++++++++++++++++++ .../ability/effects/CountersPutEffect.java | 2 +- .../res/cardsfolder/d/djerus_resolve.txt | 3 +- forge-gui/res/cardsfolder/g/godtoucher.txt | 3 +- .../cardsfolder/h/harvestguard_alseids.txt | 3 +- .../res/cardsfolder/i/indestructible_aura.txt | 3 +- .../res/cardsfolder/p/piston_fist_cyclops.txt | 2 +- .../res/cardsfolder/s/shielded_passage.txt | 3 +- .../cardsfolder/w/wellgabber_apothecary.txt | 3 +- .../forge/player/HumanPlaySpellAbility.java | 44 +------------------ 12 files changed, 88 insertions(+), 53 deletions(-) diff --git a/forge-ai/src/main/java/forge/ai/ComputerUtil.java b/forge-ai/src/main/java/forge/ai/ComputerUtil.java index 83520cd8c55..b85f75da7fd 100644 --- a/forge-ai/src/main/java/forge/ai/ComputerUtil.java +++ b/forge-ai/src/main/java/forge/ai/ComputerUtil.java @@ -250,6 +250,13 @@ public class ComputerUtil { return false; final Card source = sa.getHostCard(); + + Zone fromZone = game.getZoneOf(source); + int zonePosition = 0; + if (fromZone != null) { + zonePosition = fromZone.getCards().indexOf(source); + } + if (sa.isSpell() && !source.isCopiedSpell()) { sa.setHostCard(game.getAction().moveToStack(source, sa)); } @@ -257,11 +264,18 @@ public class ComputerUtil { sa = GameActionUtil.addExtraKeywordCost(sa); final Cost cost = sa.getPayCosts(); + final CostPayment pay = new CostPayment(cost, sa); + + // do this after card got added to stack + if (!sa.checkRestrictions(ai)) { + GameActionUtil.rollbackAbility(sa, fromZone, zonePosition, pay, source); + return false; + } + if (cost == null) { ComputerUtilMana.payManaCost(ai, sa, false); game.getStack().add(sa); } else { - final CostPayment pay = new CostPayment(cost, sa); if (pay.payComputerCosts(new AiCostDecision(ai, sa, false))) { game.getStack().add(sa); } @@ -292,6 +306,13 @@ public class ComputerUtil { newSA = GameActionUtil.addExtraKeywordCost(newSA); final Card source = newSA.getHostCard(); + + Zone fromZone = game.getZoneOf(source); + int zonePosition = 0; + if (fromZone != null) { + zonePosition = fromZone.getCards().indexOf(source); + } + if (newSA.isSpell() && !source.isCopiedSpell()) { newSA.setHostCard(game.getAction().moveToStack(source, newSA)); @@ -303,6 +324,13 @@ public class ComputerUtil { } final CostPayment pay = new CostPayment(newSA.getPayCosts(), newSA); + + // do this after card got added to stack + if (!sa.checkRestrictions(ai)) { + GameActionUtil.rollbackAbility(sa, fromZone, zonePosition, pay, source); + return false; + } + pay.payComputerCosts(new AiCostDecision(ai, newSA, false)); game.getStack().add(newSA); diff --git a/forge-ai/src/main/java/forge/ai/PlayerControllerAi.java b/forge-ai/src/main/java/forge/ai/PlayerControllerAi.java index 772f0d51734..5ca41529349 100644 --- a/forge-ai/src/main/java/forge/ai/PlayerControllerAi.java +++ b/forge-ai/src/main/java/forge/ai/PlayerControllerAi.java @@ -1087,7 +1087,7 @@ public class PlayerControllerAi extends PlayerController { if (tgtSA instanceof Spell) { // Isn't it ALWAYS a spell? Spell spell = (Spell) tgtSA; // TODO if mandatory AI is only forced to use mana when it's already in the pool - if (tgtSA.checkRestrictions(brains.getPlayer()) && (brains.canPlayFromEffectAI(spell, !optional, noManaCost) == AiPlayDecision.WillPlay || !optional)) { + if (brains.canPlayFromEffectAI(spell, !optional, noManaCost) == AiPlayDecision.WillPlay || !optional) { if (noManaCost) { return ComputerUtil.playSpellAbilityWithoutPayingManaCost(player, tgtSA, getGame()); } diff --git a/forge-game/src/main/java/forge/game/GameActionUtil.java b/forge-game/src/main/java/forge/game/GameActionUtil.java index f7574571163..22f49d4fa62 100644 --- a/forge-game/src/main/java/forge/game/GameActionUtil.java +++ b/forge-game/src/main/java/forge/game/GameActionUtil.java @@ -34,6 +34,7 @@ import forge.game.ability.AbilityUtils; import forge.game.ability.ApiType; import forge.game.card.CardPlayOption.PayManaCost; import forge.game.cost.Cost; +import forge.game.cost.CostPayment; import forge.game.keyword.Keyword; import forge.game.keyword.KeywordInterface; import forge.game.player.Player; @@ -842,4 +843,46 @@ public final class GameActionUtil { c.getGame().getTriggerHandler().resetActiveTriggers(); } + public static void rollbackAbility(SpellAbility ability, final Zone fromZone, final int zonePosition, CostPayment payment, Card oldCard) { + // cancel ability during target choosing + final Game game = ability.getActivatingPlayer().getGame(); + + if (fromZone != null) { // and not a copy + oldCard.setCastSA(null); + oldCard.setCastFrom(null); + // add back to where it came from, hopefully old state + // skip GameAction + oldCard.getZone().remove(oldCard); + fromZone.add(oldCard, zonePosition >= 0 ? Integer.valueOf(zonePosition) : null); + ability.setHostCard(oldCard); + ability.setXManaCostPaid(null); + ability.setSpendPhyrexianMana(false); + if (ability.hasParam("Announce")) { + for (final String aVar : ability.getParam("Announce").split(",")) { + final String varName = aVar.trim(); + if (!varName.equals("X")) { + ability.setSVar(varName, "0"); + } + } + } + // better safe than sorry approach in case rolled back ability was copy (from addExtraKeywordCost) + for (SpellAbility sa : oldCard.getSpells()) { + sa.setHostCard(oldCard); + } + //for Chorus of the Conclave + ability.rollback(); + + oldCard.setBackSide(false); + oldCard.setState(oldCard.getFaceupCardStateName(), true); + oldCard.unanimateBestow(); + } + + ability.clearTargets(); + + ability.resetOnceResolved(); + payment.refundPayment(); + game.getStack().clearFrozen(); + game.getTriggerHandler().clearWaitingTriggers(); + } + } diff --git a/forge-game/src/main/java/forge/game/ability/effects/CountersPutEffect.java b/forge-game/src/main/java/forge/game/ability/effects/CountersPutEffect.java index bab1c1bd3e0..3dec9d6ae4e 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/CountersPutEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/CountersPutEffect.java @@ -496,7 +496,7 @@ public class CountersPutEffect extends SpellAbilityEffect { // need to unfreeze tracker game.getTracker().unfreeze(); - // check if it can recive the Tribute + // check if it can receive the Tribute if (abort) { continue; } diff --git a/forge-gui/res/cardsfolder/d/djerus_resolve.txt b/forge-gui/res/cardsfolder/d/djerus_resolve.txt index 441cab095b4..c940cf2a52e 100644 --- a/forge-gui/res/cardsfolder/d/djerus_resolve.txt +++ b/forge-gui/res/cardsfolder/d/djerus_resolve.txt @@ -2,6 +2,7 @@ Name:Djeru's Resolve ManaCost:W Types:Instant A:SP$ Untap | Cost$ W | ValidTgts$ Creature | TgtPrompt$ Select target creature | SubAbility$ DBPump | SpellDescription$ Untap target creature. Prevent all damage that would be dealt to it this turn. -SVar:DBPump:DB$ Pump | Defined$ Targeted | KW$ Prevent all damage that would be dealt to CARDNAME. +SVar:DBPump:DB$ Effect | ReplacementEffects$ RPrevent | RememberObjects$ Targeted | ExileOnMoved$ Battlefield +SVar:RPrevent:Event$ DamageDone | Prevent$ True | ValidTarget$ Card.IsRemembered | Description$ Prevent all damage that would be dealt to that creature this turn. K:Cycling:2 Oracle:Untap target creature. Prevent all damage that would be dealt to it this turn.\nCycling {2} ({2}, Discard this card: Draw a card.) diff --git a/forge-gui/res/cardsfolder/g/godtoucher.txt b/forge-gui/res/cardsfolder/g/godtoucher.txt index ffa765c3cde..1052967e2fe 100644 --- a/forge-gui/res/cardsfolder/g/godtoucher.txt +++ b/forge-gui/res/cardsfolder/g/godtoucher.txt @@ -2,7 +2,8 @@ Name:Godtoucher ManaCost:3 G Types:Creature Elf Cleric PT:2/2 -A:AB$ Pump | Cost$ 1 W T | KW$ Prevent all damage that would be dealt to CARDNAME. | ValidTgts$ Creature.powerGE5 | TgtPrompt$ Select target creature with power 5 or greater | SpellDescription$ Prevent all damage that would be dealt to target creature with power 5 or greater this turn. +A:AB$ Effect | Cost$ 1 W T | ValidTgts$ Creature.powerGE5 | TgtPrompt$ Select target creature with power 5 or greater | ReplacementEffects$ RPrevent| RememberObjects$ Targeted | ExileOnMoved$ Battlefield | SpellDescription$ Prevent all damage that would be dealt to target creature with power 5 or greater this turn. +SVar:RPrevent:Event$ DamageDone | Prevent$ True | ValidTarget$ Card.IsRemembered | Description$ Prevent all damage that would be dealt to that creature this turn. AI:RemoveDeck:All AI:RemoveDeck:Random Oracle:{1}{W}, {T}: Prevent all damage that would be dealt to target creature with power 5 or greater this turn. diff --git a/forge-gui/res/cardsfolder/h/harvestguard_alseids.txt b/forge-gui/res/cardsfolder/h/harvestguard_alseids.txt index 50e85f86af2..efb0d150036 100644 --- a/forge-gui/res/cardsfolder/h/harvestguard_alseids.txt +++ b/forge-gui/res/cardsfolder/h/harvestguard_alseids.txt @@ -3,7 +3,8 @@ ManaCost:2 W Types:Enchantment Creature Nymph PT:2/3 T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self,Enchantment.Other+YouCtrl | Execute$ TrigPump | TriggerDescription$ Constellation — Whenever CARDNAME or another enchantment enters the battlefield under your control, prevent all damage that would be dealt to target creature this turn. -SVar:TrigPump:DB$ Pump | ValidTgts$ Creature | TgtPrompt$ Select target creature | KW$ Prevent all damage that would be dealt to CARDNAME. +SVar:TrigPump:DB$ Effect | Cost$ W | ValidTgts$ Creature | ReplacementEffects$ RPrevent | RememberObjects$ Targeted | ExileOnMoved$ Battlefield +SVar:RPrevent:Event$ DamageDone | Prevent$ True | ValidTarget$ Card.IsRemembered | Description$ Prevent all damage that would be dealt to that creature this turn. SVar:PlayMain1:TRUE SVar:BuffedBy:Enchantment Oracle:Constellation — Whenever Harvestguard Alseids or another enchantment enters the battlefield under your control, prevent all damage that would be dealt to target creature this turn. diff --git a/forge-gui/res/cardsfolder/i/indestructible_aura.txt b/forge-gui/res/cardsfolder/i/indestructible_aura.txt index da743170761..fab165f32b9 100644 --- a/forge-gui/res/cardsfolder/i/indestructible_aura.txt +++ b/forge-gui/res/cardsfolder/i/indestructible_aura.txt @@ -1,6 +1,7 @@ Name:Indestructible Aura ManaCost:W Types:Instant -A:SP$ Pump | Cost$ W | KW$ Prevent all damage that would be dealt to CARDNAME. | ValidTgts$ Creature | TgtPrompt$ Select target creature | SpellDescription$ Prevent all damage that would be dealt to target creature this turn. +A:SP$ Effect | ValidTgts$ Creature | ReplacementEffects$ RPrevent | RememberObjects$ Targeted | ExileOnMoved$ Battlefield | SpellDescription$ Prevent all damage that would be dealt to target creature this turn. +SVar:RPrevent:Event$ DamageDone | Prevent$ True | ValidTarget$ Card.IsRemembered | Description$ Prevent all damage that would be dealt to that creature this turn. AI:RemoveDeck:All Oracle:Prevent all damage that would be dealt to target creature this turn. diff --git a/forge-gui/res/cardsfolder/p/piston_fist_cyclops.txt b/forge-gui/res/cardsfolder/p/piston_fist_cyclops.txt index 25f2a682d01..37fb8914942 100644 --- a/forge-gui/res/cardsfolder/p/piston_fist_cyclops.txt +++ b/forge-gui/res/cardsfolder/p/piston_fist_cyclops.txt @@ -4,7 +4,7 @@ Types:Creature Cyclops PT:4/3 K:Defender S:Mode$ CanAttackDefender | ValidCard$ Card.Self | CheckSVar$ X | Description$ As long as you've cast an instant or sorcery spell this turn, CARDNAME can attack as though it didn't have defender. -SVar:X:Count$ThisTurnCast_Instant.YouOwn,Sorcery.YouOwn +SVar:X:Count$ThisTurnCast_Instant.YouCtrl,Sorcery.YouCtrl SVar:BuffedBy:Instant,Sorcery DeckHints:Type$Instant|Sorcery Oracle:Defender\nAs long as you've cast an instant or sorcery spell this turn, Piston-Fist Cyclops can attack as though it didn't have defender. diff --git a/forge-gui/res/cardsfolder/s/shielded_passage.txt b/forge-gui/res/cardsfolder/s/shielded_passage.txt index 596241bbb38..5ab6ad87861 100644 --- a/forge-gui/res/cardsfolder/s/shielded_passage.txt +++ b/forge-gui/res/cardsfolder/s/shielded_passage.txt @@ -1,6 +1,7 @@ Name:Shielded Passage ManaCost:W Types:Instant -A:SP$ Pump | Cost$ W | KW$ Prevent all damage that would be dealt to CARDNAME. | ValidTgts$ Creature | TgtPrompt$ Select target creature | SpellDescription$ Prevent all damage that would be dealt to target creature this turn. +A:SP$ Effect | ValidTgts$ Creature | ReplacementEffects$ RPrevent | RememberObjects$ Targeted | ExileOnMoved$ Battlefield | SpellDescription$ Prevent all damage that would be dealt to target creature this turn. +SVar:RPrevent:Event$ DamageDone | Prevent$ True | ValidTarget$ Card.IsRemembered | Description$ Prevent all damage that would be dealt to that creature this turn. AI:RemoveDeck:All Oracle:Prevent all damage that would be dealt to target creature this turn. diff --git a/forge-gui/res/cardsfolder/w/wellgabber_apothecary.txt b/forge-gui/res/cardsfolder/w/wellgabber_apothecary.txt index 239f9a3e2b4..8dd9b05c732 100644 --- a/forge-gui/res/cardsfolder/w/wellgabber_apothecary.txt +++ b/forge-gui/res/cardsfolder/w/wellgabber_apothecary.txt @@ -2,5 +2,6 @@ Name:Wellgabber Apothecary ManaCost:4 W Types:Creature Merfolk Cleric PT:2/3 -A:AB$ Pump | Cost$ 1 W | KW$ Prevent all damage that would be dealt to CARDNAME. | ValidTgts$ Creature.Merfolk+tapped,Creature.Kithkin+tapped | TgtPrompt$ Select tapped Merfolk or Kithkin creature | SpellDescription$ Prevent all damage that would be dealt to target tapped Merfolk or Kithkin creature this turn. +A:SP$ Effect | Cost$ 1 W | ValidTgts$ Creature.Merfolk+tapped,Creature.Kithkin+tapped | TgtPrompt$ Select tapped Merfolk or Kithkin creature | ReplacementEffects$ RPrevent | RememberObjects$ Targeted | ExileOnMoved$ Battlefield | SpellDescription$ Prevent all damage that would be dealt to target tapped Merfolk or Kithkin creature this turn. +SVar:RPrevent:Event$ DamageDone | Prevent$ True | ValidTarget$ Card.IsRemembered | Description$ Prevent all damage that would be dealt to that creature this turn. Oracle:{1}{W}: Prevent all damage that would be dealt to target tapped Merfolk or Kithkin creature this turn. diff --git a/forge-gui/src/main/java/forge/player/HumanPlaySpellAbility.java b/forge-gui/src/main/java/forge/player/HumanPlaySpellAbility.java index 74fa086002a..9d9b4ce0ebb 100644 --- a/forge-gui/src/main/java/forge/player/HumanPlaySpellAbility.java +++ b/forge-gui/src/main/java/forge/player/HumanPlaySpellAbility.java @@ -164,7 +164,7 @@ public class HumanPlaySpellAbility { if (!prerequisitesMet) { if (!ability.isTrigger()) { - rollbackAbility(fromZone, zonePosition, payment, c); + GameActionUtil.rollbackAbility(ability, fromZone, zonePosition, payment, c); if (ability.getHostCard().isMadness()) { // if a player failed to play madness cost, move the card to graveyard Card newCard = game.getAction().moveToGraveyard(c, null); @@ -209,48 +209,6 @@ public class HumanPlaySpellAbility { return true; } - private void rollbackAbility(final Zone fromZone, final int zonePosition, CostPayment payment, Card oldCard) { - // cancel ability during target choosing - final Game game = ability.getActivatingPlayer().getGame(); - - if (fromZone != null) { // and not a copy - oldCard.setCastSA(null); - oldCard.setCastFrom(null); - // add back to where it came from, hopefully old state - // skip GameAction - oldCard.getZone().remove(oldCard); - fromZone.add(oldCard, zonePosition >= 0 ? Integer.valueOf(zonePosition) : null); - ability.setHostCard(oldCard); - ability.setXManaCostPaid(null); - ability.setSpendPhyrexianMana(false); - if (ability.hasParam("Announce")) { - for (final String aVar : ability.getParam("Announce").split(",")) { - final String varName = aVar.trim(); - if (!varName.equals("X")) { - ability.setSVar(varName, "0"); - } - } - } - // better safe than sorry approach in case rolled back ability was copy (from addExtraKeywordCost) - for (SpellAbility sa : oldCard.getSpells()) { - sa.setHostCard(oldCard); - } - //for Chorus of the Conclave - ability.rollback(); - - oldCard.setBackSide(false); - oldCard.setState(oldCard.getFaceupCardStateName(), true); - oldCard.unanimateBestow(); - } - - ability.clearTargets(); - - ability.resetOnceResolved(); - payment.refundPayment(); - game.getStack().clearFrozen(); - game.getTriggerHandler().clearWaitingTriggers(); - } - private boolean announceValuesLikeX() { if (ability.isCopied() || ability.isWrapper()) { return true; } //don't re-announce for spell copies From 5c3f75d0d9b5e739cdcef9acdef6b4a34f9db86e Mon Sep 17 00:00:00 2001 From: Northmoc <103371817+Northmoc@users.noreply.github.com> Date: Sun, 10 Jul 2022 09:52:15 -0400 Subject: [PATCH 4/4] CLB: Illithid Harvester // Plant Tadpoles and support (#1056) * illithid_harvester_plant_tadpoles.txt * SetStateEffect.setFaceDownState * tidy up the tadpoles * CardFactoryUtil.setFaceDownState * ChangeZoneEffect remove setFaceDownState * SetStateEffect remove setFaceDownState * ChangeZoneEffect.changeZonePlayerInvariant call CardFactoryUtil too --- .../ability/effects/ChangeZoneEffect.java | 78 +++---------------- .../game/ability/effects/SetStateEffect.java | 4 + .../java/forge/game/card/CardFactoryUtil.java | 34 ++++++++ .../illithid_harvester_plant_tadpoles.txt | 19 +++++ 4 files changed, 68 insertions(+), 67 deletions(-) create mode 100644 forge-gui/res/cardsfolder/upcoming/illithid_harvester_plant_tadpoles.txt 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 d675f24b9f6..7a7e633dc4a 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 @@ -1,41 +1,18 @@ package forge.game.ability.effects; -import java.util.Arrays; -import java.util.List; -import java.util.Map; - -import forge.game.*; -import org.apache.commons.lang3.ObjectUtils; -import org.apache.commons.lang3.StringUtils; - import com.google.common.base.Predicate; import com.google.common.base.Predicates; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import com.google.common.collect.Maps; - -import forge.GameCommand; -import forge.card.CardStateName; import forge.card.CardType; +import forge.game.*; import forge.game.ability.AbilityKey; import forge.game.ability.AbilityUtils; import forge.game.ability.SpellAbilityEffect; -import forge.game.card.Card; -import forge.game.card.CardCollection; -import forge.game.card.CardCollectionView; -import forge.game.card.CardLists; -import forge.game.card.CardPredicates; -import forge.game.card.CardState; -import forge.game.card.CardUtil; -import forge.game.card.CardView; -import forge.game.card.CardZoneTable; -import forge.game.card.CounterType; +import forge.game.card.*; import forge.game.event.GameEventCombatChanged; -import forge.game.player.DelayedReveal; -import forge.game.player.Player; -import forge.game.player.PlayerActionConfirmMode; -import forge.game.player.PlayerCollection; -import forge.game.player.PlayerView; +import forge.game.player.*; import forge.game.replacement.ReplacementEffect; import forge.game.replacement.ReplacementType; import forge.game.spellability.SpellAbility; @@ -43,13 +20,13 @@ import forge.game.spellability.SpellAbilityStackInstance; import forge.game.trigger.TriggerType; import forge.game.zone.Zone; import forge.game.zone.ZoneType; -import forge.util.Aggregates; -import forge.util.CardTranslation; -import forge.util.Lang; -import forge.util.Localizer; -import forge.util.MessageUtil; -import forge.util.TextUtil; +import forge.util.*; import forge.util.collect.FCollectionView; +import org.apache.commons.lang3.ObjectUtils; +import org.apache.commons.lang3.StringUtils; + +import java.util.List; +import java.util.Map; public class ChangeZoneEffect extends SpellAbilityEffect { @@ -689,7 +666,7 @@ public class ChangeZoneEffect extends SpellAbilityEffect { // need to be facedown before it hits the battlefield in case of Replacement Effects or Trigger if (sa.hasParam("FaceDown")) { gameCard.turnFaceDown(true); - setFaceDownState(gameCard, sa); + CardFactoryUtil.setFaceDownState(gameCard, sa); } movedCard = game.getAction().moveTo(gameCard.getController().getZone(destination), gameCard, sa, moveParams); @@ -1345,7 +1322,7 @@ public class ChangeZoneEffect extends SpellAbilityEffect { // need to be facedown before it hits the battlefield in case of Replacement Effects or Trigger if (sa.hasParam("FaceDown")) { c.turnFaceDown(true); - setFaceDownState(c, sa); + CardFactoryUtil.setFaceDownState(c, sa); } movedCard = game.getAction().moveToPlay(c, c.getController(), sa, moveParams); @@ -1503,39 +1480,6 @@ public class ChangeZoneEffect extends SpellAbilityEffect { && sa.getParam("WithTotalCMC") == null; } - private static void setFaceDownState(Card c, SpellAbility sa) { - final Card source = sa.getHostCard(); - CardState faceDown = c.getFaceDownState(); - - // set New Pt doesn't work because this values need to be copyable for clone effects - if (sa.hasParam("FaceDownPower")) { - faceDown.setBasePower(AbilityUtils.calculateAmount( - source, sa.getParam("FaceDownPower"), sa)); - } - if (sa.hasParam("FaceDownToughness")) { - faceDown.setBaseToughness(AbilityUtils.calculateAmount( - source, sa.getParam("FaceDownToughness"), sa)); - } - - if (sa.hasParam("FaceDownSetType")) { - faceDown.setType(new CardType(Arrays.asList(sa.getParam("FaceDownSetType").split(" & ")), false)); - } - - if (sa.hasParam("FaceDownPower") || sa.hasParam("FaceDownToughness") - || sa.hasParam("FaceDownSetType")) { - final GameCommand unanimate = new GameCommand() { - private static final long serialVersionUID = 8853789549297846163L; - - @Override - public void run() { - c.clearStates(CardStateName.FaceDown, true); - } - }; - - c.addFaceupCommand(unanimate); - } - } - /** *

* removeFromStack. diff --git a/forge-game/src/main/java/forge/game/ability/effects/SetStateEffect.java b/forge-game/src/main/java/forge/game/ability/effects/SetStateEffect.java index 39c3d3eb71a..820e1ae2a91 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/SetStateEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/SetStateEffect.java @@ -162,6 +162,10 @@ public class SetStateEffect extends SpellAbilityEffect { hasTransformed = gameCard.turnFaceUp(true, true, sa); } else { hasTransformed = gameCard.changeCardState(mode, sa.getParam("NewState"), sa); + if (gameCard.isFaceDown() && (sa.hasParam("FaceDownPower") || sa.hasParam("FaceDownToughness") + || sa.hasParam("FaceDownSetType"))) { + CardFactoryUtil.setFaceDownState(gameCard, sa); + } } if (hasTransformed) { if (sa.isMorphUp()) { 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 3f454c027cf..fe14a850e82 100644 --- a/forge-game/src/main/java/forge/game/card/CardFactoryUtil.java +++ b/forge-game/src/main/java/forge/game/card/CardFactoryUtil.java @@ -25,6 +25,7 @@ import java.util.Map; import java.util.Map.Entry; import java.util.Set; +import forge.GameCommand; import forge.game.event.GameEventCardForetold; import forge.game.trigger.TriggerType; import org.apache.commons.lang3.StringUtils; @@ -3748,4 +3749,37 @@ public class CardFactoryUtil { re.setOverridingAbility(saExile); card.addReplacementEffect(re); } + + public static void setFaceDownState(Card c, SpellAbility sa) { + final Card source = sa.getHostCard(); + CardState faceDown = c.getFaceDownState(); + + // set New Pt doesn't work because this values need to be copyable for clone effects + if (sa.hasParam("FaceDownPower")) { + faceDown.setBasePower(AbilityUtils.calculateAmount( + source, sa.getParam("FaceDownPower"), sa)); + } + if (sa.hasParam("FaceDownToughness")) { + faceDown.setBaseToughness(AbilityUtils.calculateAmount( + source, sa.getParam("FaceDownToughness"), sa)); + } + + if (sa.hasParam("FaceDownSetType")) { + faceDown.setType(new CardType(Arrays.asList(sa.getParam("FaceDownSetType").split(" & ")), false)); + } + + if (sa.hasParam("FaceDownPower") || sa.hasParam("FaceDownToughness") + || sa.hasParam("FaceDownSetType")) { + final GameCommand unanimate = new GameCommand() { + private static final long serialVersionUID = 8853789549297846163L; + + @Override + public void run() { + c.clearStates(CardStateName.FaceDown, true); + } + }; + + c.addFaceupCommand(unanimate); + } + } } diff --git a/forge-gui/res/cardsfolder/upcoming/illithid_harvester_plant_tadpoles.txt b/forge-gui/res/cardsfolder/upcoming/illithid_harvester_plant_tadpoles.txt new file mode 100644 index 00000000000..e350f2df221 --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/illithid_harvester_plant_tadpoles.txt @@ -0,0 +1,19 @@ +Name:Illithid Harvester +ManaCost:4 U +Types:Creature Horror +PT:4/4 +T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigTurnFaceDown | TriggerDescription$ Ceremorphosis — When CARDNAME enters the battlefield, turn any number of target tapped nontoken creatures face down. They're 2/2 Horror creatures. +SVar:TrigTurnFaceDown:DB$ SetState | ValidTgts$ Creature.tapped+nonToken | TgtPrompt$ Select any number of target tapped nontoken creatures | TargetMin$ 0 | TargetMax$ X | Mode$ TurnFace | FaceDownPower$ 2 | FaceDownToughness$ 2 | FaceDownSetType$ Horror & Creature +SVar:X:Count$Valid Creature.tapped+nonToken +AlternateMode:Adventure +Oracle:Ceremorphosis — When Illithid Harvester enters the battlefield, turn any number of target tapped nontoken creatures face down. They're 2/2 Horror creatures. + +ALTERNATE + +Name:Plant Tadpoles +ManaCost:X U U +Types:Sorcery Adventure +A:SP$ Tap | ValidTgts$ Creature | TgtPrompt$ Select X target creatures | TargetMin$ X | TargetMax$ X | AlwaysRemember$ True | SubAbility$ DBPump | SpellDescription$ Tap X target creatures. +SVar:DBPump:DB$ Pump | Defined$ Targeted | KW$ HIDDEN This card doesn't untap during your next untap step. | Duration$ Permanent | StackDescription$ SpellDescription | SpellDescription$ They don't untap during their controllers' next untap steps. +SVar:X:Count$xPaid +Oracle:Tap X target creatures. They don't untap during their controllers' next untap steps. (Then exile this card. You may cast the creature later from exile.)