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/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/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-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/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/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.) 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.) 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/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 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