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

View File

@@ -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;
}
}