mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-20 12:48:00 +00:00
Spell: add getAlternateHost for spells that would change the CardState
This commit is contained in:
@@ -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
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user