From 2eea49a8e44008bf3dca178796f9d571dc3ae565 Mon Sep 17 00:00:00 2001 From: Hans Mackowiak Date: Thu, 20 Feb 2020 07:03:24 +0100 Subject: [PATCH] Spell: add getAlternateHost for spells that would change the CardState --- .../main/java/forge/game/GameActionUtil.java | 68 +--------- .../java/forge/game/spellability/Spell.java | 123 ++++++++++++------ 2 files changed, 85 insertions(+), 106 deletions(-) diff --git a/forge-game/src/main/java/forge/game/GameActionUtil.java b/forge-game/src/main/java/forge/game/GameActionUtil.java index 353111565fb..b54c727370f 100644 --- a/forge-game/src/main/java/forge/game/GameActionUtil.java +++ b/forge-game/src/main/java/forge/game/GameActionUtil.java @@ -22,7 +22,6 @@ import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import com.google.common.collect.Sets; -import forge.card.CardStateName; import forge.card.mana.ManaCost; import forge.card.mana.ManaCostParser; import forge.game.ability.AbilityUtils; @@ -78,68 +77,9 @@ public final class GameActionUtil { if (sa.isSpell() && !source.isInZone(ZoneType.Battlefield)) { boolean lkicheck = false; - // need to be done before so it works with Vivien and Zoetic Cavern - if (source.isFaceDown() && source.isInZone(ZoneType.Exile)) { - if (!source.isLKI()) { - source = CardUtil.getLKICopy(source); - } - - source.turnFaceUp(false, false); - lkicheck = true; - } - - if (sa.isBestow() && !source.isBestowed() && !source.isInZone(ZoneType.Battlefield)) { - if (!source.isLKI()) { - source = CardUtil.getLKICopy(source); - } - - source.animateBestow(false); - lkicheck = true; - } else if (sa.isCastFaceDown()) { - // need a copy of the card to turn facedown without trigger anything - if (!source.isLKI()) { - source = CardUtil.getLKICopy(source); - } - source.turnFaceDownNoUpdate(); - lkicheck = true; - } else if (sa.isAdventure()) { - if (!source.isLKI()) { - source = CardUtil.getLKICopy(source); - } - - source.setState(CardStateName.Adventure, false); - - // need to reset CMC - source.setLKICMC(-1); - source.setLKICMC(source.getCMC()); - lkicheck = true; - } else if (source.isSplitCard() && (sa.isLeftSplit() || sa.isRightSplit())) { - if (!source.isLKI()) { - source = CardUtil.getLKICopy(source); - } - if (sa.isLeftSplit()) { - if (!source.hasState(CardStateName.LeftSplit)) { - source.addAlternateState(CardStateName.LeftSplit, false); - source.getState(CardStateName.LeftSplit).copyFrom( - sa.getHostCard().getState(CardStateName.LeftSplit), true); - } - - source.setState(CardStateName.LeftSplit, false); - } - - if (sa.isRightSplit()) { - if (!source.hasState(CardStateName.RightSplit)) { - source.addAlternateState(CardStateName.RightSplit, false); - source.getState(CardStateName.RightSplit).copyFrom( - sa.getHostCard().getState(CardStateName.RightSplit), true); - } - - source.setState(CardStateName.RightSplit, false); - } - - // need to reset CMC - source.setLKICMC(-1); - source.setLKICMC(source.getCMC()); + Card newHost = ((Spell)sa).getAlternateHost(source); + if (newHost != null) { + source = newHost; lkicheck = true; } @@ -218,6 +158,7 @@ public final class GameActionUtil { final Cost escapeCost = new Cost(k[1], true); final SpellAbility newSA = sa.copyWithDefinedCost(escapeCost); + newSA.setActivatingPlayer(activator); newSA.getMapParams().put("PrecostDesc", "Escape—"); newSA.getMapParams().put("CostDesc", escapeCost.toString()); @@ -276,6 +217,7 @@ public final class GameActionUtil { if (sa.isCycling() && activator.hasKeyword("CyclingForZero")) { // set the cost to this directly to buypass non mana cost final SpellAbility newSA = sa.copyWithDefinedCost("Discard<1/CARDNAME>"); + newSA.setActivatingPlayer(activator); newSA.setBasicSpell(false); newSA.getMapParams().put("CostDesc", ManaCostParser.parse("0")); // makes new SpellDescription diff --git a/forge-game/src/main/java/forge/game/spellability/Spell.java b/forge-game/src/main/java/forge/game/spellability/Spell.java index 87fbb0d4e84..2978d406f34 100644 --- a/forge-game/src/main/java/forge/game/spellability/Spell.java +++ b/forge-game/src/main/java/forge/game/spellability/Spell.java @@ -75,12 +75,12 @@ public abstract class Spell extends SpellAbility implements java.io.Serializable @Override public boolean canPlay() { Card card = this.getHostCard(); + if (card.isInZone(ZoneType.Battlefield)) { + return false; + } // Save the original cost and the face down info for a later check since the LKI copy will overwrite them ManaCost origCost = card.getState(card.isFaceDown() ? CardStateName.Original : card.getCurrentStateName()).getManaCost(); - boolean wasFaceDownInstant = card.isFaceDown() - && !card.getLastKnownZone().is(ZoneType.Battlefield) - && card.getState(CardStateName.Original).getType().isInstant(); Player activator = this.getActivatingPlayer(); if (activator == null) { @@ -95,61 +95,29 @@ public abstract class Spell extends SpellAbility implements java.io.Serializable return false; } - boolean isInstant = card.isInstant(); - // special case for split cards - if (card.isSplitCard()) { - CardStateName name = isLeftSplit() ? CardStateName.LeftSplit : CardStateName.RightSplit; - isInstant = card.getState(name).getType().isInstant(); - } else if (isAdventure()) { - if (card.hasState(CardStateName.Adventure)) { - isInstant = card.getState(CardStateName.Adventure).getType().isInstant(); - } - } - boolean lkicheck = false; - boolean flash = false; // do performanceMode only for cases where the activator is different than controller - if (!Spell.performanceMode && activator != null && !card.getController().equals(activator) - && !card.isInZone(ZoneType.Battlefield)) { + if (!Spell.performanceMode && activator != null && !card.getController().equals(activator)) { // always make a lki copy in this case? card = CardUtil.getLKICopy(card); card.setController(activator, 0); lkicheck = true; } - if (isBestow() && !card.isBestowed() && !card.isInZone(ZoneType.Battlefield)) { - // Rule 601.3: cast Bestow with Flash - // for the check the card does need to be animated - // otherwise the StaticAbility will not found them - if (!card.isLKI()) { - card = CardUtil.getLKICopy(card); - } - card.animateBestow(false); - lkicheck = true; - } else if (isCastFaceDown()) { - // need a copy of the card to turn facedown without trigger anything - if (!card.isLKI()) { - card = CardUtil.getLKICopy(card); - } - card.turnFaceDownNoUpdate(); - lkicheck = true; - } else if (isAdventure()) { - if (!card.isLKI()) { - card = CardUtil.getLKICopy(card); - } - - card.setState(CardStateName.Adventure, false); + Card lkiHost = getAlternateHost(card); + if (lkiHost != null) { + card = lkiHost; lkicheck = true; } - if (lkicheck) { game.getTracker().freeze(); //prevent views flickering during while updating for state-based effects game.getAction().checkStaticAbilities(false, Sets.newHashSet(card), new CardCollection(card)); } - flash = card.withFlash(activator); + boolean isInstant = card.isInstant(); + boolean flash = card.withFlash(activator); // reset static abilities if (lkicheck) { @@ -160,8 +128,7 @@ public abstract class Spell extends SpellAbility implements java.io.Serializable } if (!(isInstant || activator.canCastSorcery() || flash || getRestrictions().isInstantSpeed() - || hasSVar("IsCastFromPlayEffect") - || wasFaceDownInstant)) { + || hasSVar("IsCastFromPlayEffect"))) { return false; } @@ -235,4 +202,74 @@ public abstract class Spell extends SpellAbility implements java.io.Serializable this.castFaceDown = faceDown; } + public Card getAlternateHost(Card source) { + boolean lkicheck = false; + + // need to be done before so it works with Vivien and Zoetic Cavern + if (source.isFaceDown() && source.isInZone(ZoneType.Exile)) { + if (!source.isLKI()) { + source = CardUtil.getLKICopy(source); + } + + source.turnFaceUp(false, false); + lkicheck = true; + } + + if (isBestow() && !source.isBestowed()) { + if (!source.isLKI()) { + source = CardUtil.getLKICopy(source); + } + + source.animateBestow(false); + lkicheck = true; + } else if (isCastFaceDown()) { + // need a copy of the card to turn facedown without trigger anything + if (!source.isLKI()) { + source = CardUtil.getLKICopy(source); + } + source.turnFaceDownNoUpdate(); + lkicheck = true; + } else if (isAdventure()) { + if (!source.isLKI()) { + source = CardUtil.getLKICopy(source); + } + + source.setState(CardStateName.Adventure, false); + + // need to reset CMC + source.setLKICMC(-1); + source.setLKICMC(source.getCMC()); + lkicheck = true; + } else if (source.isSplitCard() && (isLeftSplit() || isRightSplit())) { + if (!source.isLKI()) { + source = CardUtil.getLKICopy(source); + } + if (isLeftSplit()) { + if (!source.hasState(CardStateName.LeftSplit)) { + source.addAlternateState(CardStateName.LeftSplit, false); + source.getState(CardStateName.LeftSplit).copyFrom( + getHostCard().getState(CardStateName.LeftSplit), true); + } + + source.setState(CardStateName.LeftSplit, false); + } + + if (isRightSplit()) { + if (!source.hasState(CardStateName.RightSplit)) { + source.addAlternateState(CardStateName.RightSplit, false); + source.getState(CardStateName.RightSplit).copyFrom( + getHostCard().getState(CardStateName.RightSplit), true); + } + + source.setState(CardStateName.RightSplit, false); + } + + // need to reset CMC + source.setLKICMC(-1); + source.setLKICMC(source.getCMC()); + lkicheck = true; + } + + return lkicheck ? source : null; + } }