Spell: add getAlternateHost for spells that would change the CardState

This commit is contained in:
Hans Mackowiak
2020-02-20 07:03:24 +01:00
parent a7d8a62a69
commit 2eea49a8e4
2 changed files with 85 additions and 106 deletions

View File

@@ -22,7 +22,6 @@ import com.google.common.collect.Iterables;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import com.google.common.collect.Sets; import com.google.common.collect.Sets;
import forge.card.CardStateName;
import forge.card.mana.ManaCost; import forge.card.mana.ManaCost;
import forge.card.mana.ManaCostParser; import forge.card.mana.ManaCostParser;
import forge.game.ability.AbilityUtils; import forge.game.ability.AbilityUtils;
@@ -78,68 +77,9 @@ public final class GameActionUtil {
if (sa.isSpell() && !source.isInZone(ZoneType.Battlefield)) { if (sa.isSpell() && !source.isInZone(ZoneType.Battlefield)) {
boolean lkicheck = false; boolean lkicheck = false;
// need to be done before so it works with Vivien and Zoetic Cavern Card newHost = ((Spell)sa).getAlternateHost(source);
if (source.isFaceDown() && source.isInZone(ZoneType.Exile)) { if (newHost != null) {
if (!source.isLKI()) { source = newHost;
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());
lkicheck = true; lkicheck = true;
} }
@@ -218,6 +158,7 @@ public final class GameActionUtil {
final Cost escapeCost = new Cost(k[1], true); final Cost escapeCost = new Cost(k[1], true);
final SpellAbility newSA = sa.copyWithDefinedCost(escapeCost); final SpellAbility newSA = sa.copyWithDefinedCost(escapeCost);
newSA.setActivatingPlayer(activator);
newSA.getMapParams().put("PrecostDesc", "Escape—"); newSA.getMapParams().put("PrecostDesc", "Escape—");
newSA.getMapParams().put("CostDesc", escapeCost.toString()); newSA.getMapParams().put("CostDesc", escapeCost.toString());
@@ -276,6 +217,7 @@ public final class GameActionUtil {
if (sa.isCycling() && activator.hasKeyword("CyclingForZero")) { if (sa.isCycling() && activator.hasKeyword("CyclingForZero")) {
// set the cost to this directly to buypass non mana cost // set the cost to this directly to buypass non mana cost
final SpellAbility newSA = sa.copyWithDefinedCost("Discard<1/CARDNAME>"); final SpellAbility newSA = sa.copyWithDefinedCost("Discard<1/CARDNAME>");
newSA.setActivatingPlayer(activator);
newSA.setBasicSpell(false); newSA.setBasicSpell(false);
newSA.getMapParams().put("CostDesc", ManaCostParser.parse("0")); newSA.getMapParams().put("CostDesc", ManaCostParser.parse("0"));
// makes new SpellDescription // makes new SpellDescription

View File

@@ -75,12 +75,12 @@ public abstract class Spell extends SpellAbility implements java.io.Serializable
@Override @Override
public boolean canPlay() { public boolean canPlay() {
Card card = this.getHostCard(); 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 // 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(); 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(); Player activator = this.getActivatingPlayer();
if (activator == null) { if (activator == null) {
@@ -95,61 +95,29 @@ public abstract class Spell extends SpellAbility implements java.io.Serializable
return false; 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 lkicheck = false;
boolean flash = false;
// do performanceMode only for cases where the activator is different than controller // do performanceMode only for cases where the activator is different than controller
if (!Spell.performanceMode && activator != null && !card.getController().equals(activator) if (!Spell.performanceMode && activator != null && !card.getController().equals(activator)) {
&& !card.isInZone(ZoneType.Battlefield)) {
// always make a lki copy in this case? // always make a lki copy in this case?
card = CardUtil.getLKICopy(card); card = CardUtil.getLKICopy(card);
card.setController(activator, 0); card.setController(activator, 0);
lkicheck = true; lkicheck = true;
} }
if (isBestow() && !card.isBestowed() && !card.isInZone(ZoneType.Battlefield)) { Card lkiHost = getAlternateHost(card);
// Rule 601.3: cast Bestow with Flash if (lkiHost != null) {
// for the check the card does need to be animated card = lkiHost;
// 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);
lkicheck = true; lkicheck = true;
} }
if (lkicheck) { if (lkicheck) {
game.getTracker().freeze(); //prevent views flickering during while updating for state-based effects game.getTracker().freeze(); //prevent views flickering during while updating for state-based effects
game.getAction().checkStaticAbilities(false, Sets.newHashSet(card), new CardCollection(card)); 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 // reset static abilities
if (lkicheck) { if (lkicheck) {
@@ -160,8 +128,7 @@ public abstract class Spell extends SpellAbility implements java.io.Serializable
} }
if (!(isInstant || activator.canCastSorcery() || flash || getRestrictions().isInstantSpeed() if (!(isInstant || activator.canCastSorcery() || flash || getRestrictions().isInstantSpeed()
|| hasSVar("IsCastFromPlayEffect") || hasSVar("IsCastFromPlayEffect"))) {
|| wasFaceDownInstant)) {
return false; return false;
} }
@@ -235,4 +202,74 @@ public abstract class Spell extends SpellAbility implements java.io.Serializable
this.castFaceDown = faceDown; 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;
}
} }