mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-17 11:18:01 +00:00
Foretell: add Keyword
This commit is contained in:
committed by
Michael Kamensky
parent
9f4c694c21
commit
4cbeca4cb3
@@ -153,6 +153,10 @@ public class ForgeScript {
|
||||
return sa.hasParam("Equip");
|
||||
} else if (property.equals("Boast")) {
|
||||
return sa.isBoast();
|
||||
} else if (property.equals("Foretelling")) {
|
||||
return sa.isForetelling();
|
||||
} else if (property.equals("Foretold")) {
|
||||
return sa.isForetold();
|
||||
} else if (property.equals("MayPlaySource")) {
|
||||
StaticAbility m = sa.getMayPlay();
|
||||
if (m == null) {
|
||||
|
||||
@@ -73,6 +73,8 @@ public class GameAction {
|
||||
// Reset Activations per Turn
|
||||
for (final Card card : game.getCardsInGame()) {
|
||||
card.resetActivationsPerTurn();
|
||||
// need to reset this in exile
|
||||
card.resetForetoldThisTurn();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -168,7 +170,7 @@ public class GameAction {
|
||||
|
||||
// Don't copy Tokens, copy only cards leaving the battlefield
|
||||
// and returning to hand (to recreate their spell ability information)
|
||||
if (suppress || toBattlefield || zoneTo.is(ZoneType.Stack)) {
|
||||
if (suppress || toBattlefield) {
|
||||
copied = c;
|
||||
|
||||
if (lastKnownInfo == null) {
|
||||
@@ -193,10 +195,6 @@ public class GameAction {
|
||||
lastKnownInfo = CardUtil.getLKICopy(c);
|
||||
}
|
||||
|
||||
if (wasFacedown) {
|
||||
c.forceTurnFaceUp();
|
||||
}
|
||||
|
||||
if (!c.isToken()) {
|
||||
copied = CardFactory.copyCard(c, false);
|
||||
|
||||
|
||||
@@ -6,12 +6,12 @@
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
@@ -54,7 +54,7 @@ import java.util.List;
|
||||
* <p>
|
||||
* GameActionUtil class.
|
||||
* </p>
|
||||
*
|
||||
*
|
||||
* @author Forge
|
||||
* @version $Id$
|
||||
*/
|
||||
@@ -68,7 +68,7 @@ public final class GameActionUtil {
|
||||
* <p>
|
||||
* Find the alternative costs to a {@link SpellAbility}.
|
||||
* </p>
|
||||
*
|
||||
*
|
||||
* @param sa
|
||||
* a {@link SpellAbility}.
|
||||
* @param activator
|
||||
@@ -204,8 +204,51 @@ public final class GameActionUtil {
|
||||
flashback.setPayCosts(new Cost(k[1], false));
|
||||
}
|
||||
alternatives.add(flashback);
|
||||
} else if (keyword.startsWith("Foretell")) {
|
||||
// Fortell cast only from Exile
|
||||
if (!source.isInZone(ZoneType.Exile) || !source.isForetold() || source.isForetoldThisTurn()) {
|
||||
continue;
|
||||
}
|
||||
// skip this part for fortell by external source
|
||||
if (keyword.equals("Foretell")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
final SpellAbility foretold = sa.copy(activator);
|
||||
foretold.setAlternativeCost(AlternativeCost.Foretold);
|
||||
foretold.getRestrictions().setZone(ZoneType.Exile);
|
||||
|
||||
// Stack Description only for Permanent or it might crash
|
||||
if (source.isPermanent()) {
|
||||
final StringBuilder sbStack = new StringBuilder();
|
||||
sbStack.append(sa.getStackDescription()).append(" (Foretold)");
|
||||
foretold.setStackDescription(sbStack.toString());
|
||||
}
|
||||
|
||||
final String[] k = keyword.split(":");
|
||||
foretold.setPayCosts(new Cost(k[1], false));
|
||||
|
||||
alternatives.add(foretold);
|
||||
}
|
||||
}
|
||||
|
||||
// foretell by external source
|
||||
if (source.isForetoldByEffect() && source.isInZone(ZoneType.Exile) && source.isForetold() && !source.isForetoldThisTurn() && !source.getManaCost().isNoCost()) {
|
||||
// Its foretell cost is equal to its mana cost reduced by {2}.
|
||||
final SpellAbility foretold = sa.copy(activator);
|
||||
foretold.putParam("ReduceCost", "2");
|
||||
foretold.setAlternativeCost(AlternativeCost.Foretold);
|
||||
foretold.getRestrictions().setZone(ZoneType.Exile);
|
||||
|
||||
// Stack Description only for Permanent or it might crash
|
||||
if (source.isPermanent()) {
|
||||
final StringBuilder sbStack = new StringBuilder();
|
||||
sbStack.append(sa.getStackDescription()).append(" (Foretold)");
|
||||
foretold.setStackDescription(sbStack.toString());
|
||||
}
|
||||
|
||||
alternatives.add(foretold);
|
||||
}
|
||||
}
|
||||
|
||||
// reset static abilities
|
||||
@@ -295,12 +338,12 @@ public final class GameActionUtil {
|
||||
final Cost cost = new Cost(k[1], false);
|
||||
costs.add(new OptionalCostValue(OptionalCost.Flash, cost));
|
||||
}
|
||||
|
||||
|
||||
// Surge while having OptionalCost is none of them
|
||||
}
|
||||
return costs;
|
||||
}
|
||||
|
||||
|
||||
public static SpellAbility addOptionalCosts(final SpellAbility sa, List<OptionalCostValue> list) {
|
||||
if (sa == null || list.isEmpty()) {
|
||||
return sa;
|
||||
@@ -309,7 +352,7 @@ public final class GameActionUtil {
|
||||
for (OptionalCostValue v : list) {
|
||||
result.getPayCosts().add(v.getCost());
|
||||
result.addOptionalCost(v.getType());
|
||||
|
||||
|
||||
// add some extra logic, try to move it to other parts
|
||||
switch (v.getType()) {
|
||||
case Retrace:
|
||||
@@ -325,7 +368,7 @@ public final class GameActionUtil {
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
public static List<SpellAbility> getAdditionalCostSpell(final SpellAbility sa) {
|
||||
final List<SpellAbility> abilities = Lists.newArrayList(sa);
|
||||
if (!sa.isSpell()) {
|
||||
@@ -358,14 +401,14 @@ public final class GameActionUtil {
|
||||
if (newSA2.canPlay()) {
|
||||
newAbilities.add(newSA2);
|
||||
}
|
||||
|
||||
|
||||
abilities.clear();
|
||||
abilities.addAll(newAbilities);
|
||||
}
|
||||
}
|
||||
return abilities;
|
||||
}
|
||||
|
||||
|
||||
public static SpellAbility addExtraKeywordCost(final SpellAbility sa) {
|
||||
if (!sa.isSpell() || sa.isCopied()) {
|
||||
return sa;
|
||||
|
||||
@@ -701,6 +701,13 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
|
||||
if (sa.hasParam("ExileFaceDown")) {
|
||||
movedCard.turnFaceDown(true);
|
||||
}
|
||||
if (sa.hasParam("Foretold")) {
|
||||
movedCard.setForetold(true);
|
||||
movedCard.setForetoldThisTurn(true);
|
||||
movedCard.setForetoldByEffect(true);
|
||||
// look at the exiled card
|
||||
movedCard.addMayLookTemp(sa.getActivatingPlayer());
|
||||
}
|
||||
|
||||
if (sa.hasParam("TrackDiscarded")) {
|
||||
movedCard.setMadnessWithoutCast(true);
|
||||
@@ -1240,6 +1247,13 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
|
||||
if (sa.hasParam("ExileFaceDown")) {
|
||||
movedCard.turnFaceDown(true);
|
||||
}
|
||||
if (sa.hasParam("Foretold")) {
|
||||
movedCard.setForetold(true);
|
||||
movedCard.setForetoldThisTurn(true);
|
||||
movedCard.setForetoldByEffect(true);
|
||||
// look at the exiled card
|
||||
movedCard.addMayLookTemp(sa.getActivatingPlayer());
|
||||
}
|
||||
}
|
||||
else {
|
||||
movedCard = game.getAction().moveTo(destination, c, 0, cause, moveParams);
|
||||
|
||||
@@ -154,7 +154,7 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|
||||
private boolean hasdealtDamagetoAny = false;
|
||||
|
||||
private boolean isCommander = false;
|
||||
private boolean canMoveToCommandZone = false;
|
||||
private boolean canMoveToCommandZone = false;
|
||||
|
||||
private boolean startsGameInPlay = false;
|
||||
private boolean drawnThisTurn = false;
|
||||
@@ -177,6 +177,10 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|
||||
|
||||
private boolean manifested = false;
|
||||
|
||||
private boolean foretold = false;
|
||||
private boolean foretoldThisTurn = false;
|
||||
private boolean foretoldByEffect = false;
|
||||
|
||||
private long bestowTimestamp = -1;
|
||||
private long transformedTimestamp = 0;
|
||||
private boolean tributed = false;
|
||||
@@ -1694,7 +1698,7 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|
||||
} else {
|
||||
sbLong.append(parts[0]).append(" ").append(ManaCostParser.parse(parts[1])).append("\r\n");
|
||||
}
|
||||
} else if (keyword.startsWith("Morph") || keyword.startsWith("Megamorph") || keyword.startsWith("Escape")) {
|
||||
} else if (keyword.startsWith("Morph") || keyword.startsWith("Megamorph") || keyword.startsWith("Escape") || keyword.startsWith("Foretell:")) {
|
||||
String[] k = keyword.split(":");
|
||||
sbLong.append(k[0]);
|
||||
if (k.length > 1) {
|
||||
@@ -1789,6 +1793,7 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|
||||
|| keyword.equals("Changeling") || keyword.equals("Delve")
|
||||
|| keyword.equals("Split second") || keyword.equals("Sunburst")
|
||||
|| keyword.equals("Suspend") // for the ones without amounnt
|
||||
|| keyword.equals("Foretell") // for the ones without cost
|
||||
|| keyword.equals("Hideaway") || keyword.equals("Ascend")
|
||||
|| keyword.equals("Totem armor") || keyword.equals("Battle cry")
|
||||
|| keyword.equals("Devoid") || keyword.equals("Riot")){
|
||||
@@ -2236,7 +2241,7 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|
||||
sbBefore.append("\r\n");
|
||||
} else if (keyword.startsWith("Entwine") || keyword.startsWith("Madness")
|
||||
|| keyword.startsWith("Miracle") || keyword.startsWith("Recover")
|
||||
|| keyword.startsWith("Escape")) {
|
||||
|| keyword.startsWith("Escape") || keyword.startsWith("Foretell:")) {
|
||||
final String[] k = keyword.split(":");
|
||||
final Cost cost = new Cost(k[1], false);
|
||||
|
||||
@@ -5319,6 +5324,42 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|
||||
}
|
||||
}
|
||||
|
||||
public final boolean isForetold() {
|
||||
// in exile and foretold
|
||||
if (this.isInZone(ZoneType.Exile)) {
|
||||
return this.foretold;
|
||||
}
|
||||
// cast as foretold, currently only spells
|
||||
if (this.getCastSA() != null) {
|
||||
return this.getCastSA().isForetold();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public final void setForetold(final boolean foretold) {
|
||||
this.foretold = foretold;
|
||||
}
|
||||
|
||||
public boolean isForetoldByEffect() {
|
||||
return foretoldByEffect;
|
||||
}
|
||||
|
||||
public void setForetoldByEffect(final boolean val) {
|
||||
this.foretoldByEffect = val;
|
||||
}
|
||||
|
||||
public boolean isForetoldThisTurn() {
|
||||
return foretoldThisTurn;
|
||||
}
|
||||
|
||||
public final void setForetoldThisTurn(final boolean foretoldThisTurn) {
|
||||
this.foretoldThisTurn = foretoldThisTurn;
|
||||
}
|
||||
|
||||
public void resetForetoldThisTurn() {
|
||||
foretoldThisTurn = false;
|
||||
}
|
||||
|
||||
public final void animateBestow() {
|
||||
animateBestow(true);
|
||||
}
|
||||
@@ -6567,11 +6608,6 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|
||||
return numberGameActivations.containsKey(original) ? numberGameActivations.get(original) : 0;
|
||||
}
|
||||
|
||||
public void resetTurnActivations() {
|
||||
numberTurnActivations.clear();
|
||||
numberTurnActivationsStatic.clear();
|
||||
}
|
||||
|
||||
public List<String> getChosenModesTurn(SpellAbility ability) {
|
||||
SpellAbility original = null;
|
||||
SpellAbility root = ability.getRootAbility();
|
||||
|
||||
@@ -88,7 +88,7 @@ public class CardFactory {
|
||||
}
|
||||
|
||||
out.setZone(in.getZone());
|
||||
out.setState(in.getCurrentStateName(), true);
|
||||
out.setState(in.getFaceupCardStateName(), true);
|
||||
out.setBackSide(in.isBackSide());
|
||||
|
||||
// this's necessary for forge.game.GameAction.unattachCardLeavingBattlefield(Card)
|
||||
|
||||
@@ -66,7 +66,6 @@ import java.util.Map.Entry;
|
||||
import io.sentry.Sentry;
|
||||
import io.sentry.event.BreadcrumbBuilder;
|
||||
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* CardFactoryUtil class.
|
||||
@@ -1404,6 +1403,13 @@ public class CardFactoryUtil {
|
||||
return doXMath(StringUtils.isNumeric(v) ? Integer.parseInt(v) : xCount(c, c.getSVar(v)), m, c);
|
||||
}
|
||||
|
||||
// Count$Foretold.<True>.<False>
|
||||
if (sq[0].startsWith("Foretold")) {
|
||||
String v = c.isForetold() ? sq[1] : sq[2];
|
||||
// TODO move this to AbilityUtils
|
||||
return doXMath(StringUtils.isNumeric(v) ? Integer.parseInt(v) : xCount(c, c.getSVar(v)), m, c);
|
||||
}
|
||||
|
||||
// Count$Presence_<Type>.<True>.<False>
|
||||
if (sq[0].startsWith("Presence")) {
|
||||
final String type = sq[0].split("_")[1];
|
||||
@@ -1449,7 +1455,6 @@ public class CardFactoryUtil {
|
||||
return forge.util.MyRandom.getRandom().nextInt(1+max-min) + min;
|
||||
}
|
||||
|
||||
|
||||
// Count$Domain
|
||||
if (sq[0].startsWith("Domain")) {
|
||||
int n = 0;
|
||||
@@ -1997,7 +2002,6 @@ public class CardFactoryUtil {
|
||||
final Set<String> hexproofkw = Sets.newHashSet();
|
||||
final Set<String> allkw = Sets.newHashSet();
|
||||
|
||||
|
||||
for (Card c : CardLists.getValidCards(cardlist, restrictions, p, host, null)) {
|
||||
for (KeywordInterface inst : c.getKeywords()) {
|
||||
final String k = inst.getOriginal();
|
||||
@@ -2155,7 +2159,6 @@ public class CardFactoryUtil {
|
||||
return re;
|
||||
}
|
||||
|
||||
|
||||
public static ReplacementEffect makeEtbCounter(final String kw, final Card card, final boolean intrinsic)
|
||||
{
|
||||
String parse = kw;
|
||||
@@ -4095,6 +4098,60 @@ public class CardFactoryUtil {
|
||||
newSA.setAlternativeCost(AlternativeCost.Evoke);
|
||||
newSA.setIntrinsic(intrinsic);
|
||||
inst.addSpellAbility(newSA);
|
||||
} else if (keyword.startsWith("Foretell")) {
|
||||
|
||||
final SpellAbility foretell = new AbilityStatic(card, new Cost(ManaCost.TWO, false), null) {
|
||||
@Override
|
||||
public boolean canPlay() {
|
||||
if (!getRestrictions().canPlay(getHostCard(), this)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Player activator = this.getActivatingPlayer();
|
||||
final Game game = activator.getGame();
|
||||
|
||||
if (!activator.hasKeyword("Foretell on any player’s turn") && !game.getPhaseHandler().isPlayerTurn(activator)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isForetelling() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resolve() {
|
||||
final Game game = getHostCard().getGame();
|
||||
final Card c = game.getAction().exile(getHostCard(), this);
|
||||
c.setForetold(true);
|
||||
c.setForetoldThisTurn(true);
|
||||
c.turnFaceDown(true);
|
||||
// look at the exiled card
|
||||
c.addMayLookTemp(getActivatingPlayer());
|
||||
|
||||
// only done when the card is foretold by the static ability
|
||||
getActivatingPlayer().addForetoldThisTurn();
|
||||
|
||||
if (!isIntrinsic()) {
|
||||
// because it doesn't work other wise
|
||||
c.setForetoldByEffect(true);
|
||||
}
|
||||
String sb = TextUtil.concatWithSpace(getActivatingPlayer().toString(),"has foretold.");
|
||||
game.getGameLog().add(GameLogEntryType.STACK_RESOLVE, sb);
|
||||
}
|
||||
};
|
||||
final StringBuilder sbDesc = new StringBuilder();
|
||||
sbDesc.append("Foretell (").append(inst.getReminderText()).append(")");
|
||||
foretell.setDescription(sbDesc.toString());
|
||||
foretell.putParam("Secondary", "True");
|
||||
|
||||
foretell.getRestrictions().setZone(ZoneType.Hand);
|
||||
foretell.setIntrinsic(intrinsic);
|
||||
inst.addSpellAbility(foretell);
|
||||
|
||||
} else if (keyword.startsWith("Fortify")) {
|
||||
String[] k = keyword.split(":");
|
||||
// Get cost string
|
||||
|
||||
@@ -1670,36 +1670,40 @@ public class CardProperty {
|
||||
if (property.equals("pseudokicked")) {
|
||||
if (!card.isOptionalCostPaid(OptionalCost.Generic)) return false;
|
||||
}
|
||||
} else if (property.startsWith("surged")) {
|
||||
} else if (property.equals("surged")) {
|
||||
if (card.getCastSA() == null) {
|
||||
return false;
|
||||
}
|
||||
return card.getCastSA().isSurged();
|
||||
} else if (property.startsWith("dashed")) {
|
||||
} else if (property.equals("dashed")) {
|
||||
if (card.getCastSA() == null) {
|
||||
return false;
|
||||
}
|
||||
return card.getCastSA().isDash();
|
||||
} else if (property.startsWith("escaped")) {
|
||||
} else if (property.equals("escaped")) {
|
||||
if (card.getCastSA() == null) {
|
||||
return false;
|
||||
}
|
||||
return card.getCastSA().isEscape();
|
||||
} else if (property.startsWith("evoked")) {
|
||||
} else if (property.equals("evoked")) {
|
||||
if (card.getCastSA() == null) {
|
||||
return false;
|
||||
}
|
||||
return card.getCastSA().isEvoke();
|
||||
} else if (property.startsWith("prowled")) {
|
||||
} else if (property.equals("prowled")) {
|
||||
if (card.getCastSA() == null) {
|
||||
return false;
|
||||
}
|
||||
return card.getCastSA().isProwl();
|
||||
} else if (property.startsWith("spectacle")) {
|
||||
} else if (property.equals("spectacle")) {
|
||||
if (card.getCastSA() == null) {
|
||||
return false;
|
||||
}
|
||||
return card.getCastSA().isSpectacle();
|
||||
} else if (property.equals("foretold")) {
|
||||
if (!card.isForetold()) {
|
||||
return false;
|
||||
}
|
||||
} else if (property.equals("HasDevoured")) {
|
||||
if (card.getDevouredCards().isEmpty()) {
|
||||
return false;
|
||||
|
||||
@@ -278,6 +278,10 @@ public final class CardUtil {
|
||||
|
||||
newCopy.copyChangedTextFrom(in);
|
||||
|
||||
newCopy.setForetold(in.isForetold());
|
||||
newCopy.setForetoldThisTurn(in.isForetoldThisTurn());
|
||||
newCopy.setForetoldByEffect(in.isForetoldByEffect());
|
||||
|
||||
newCopy.setMeldedWith(getLKICopy(in.getMeldedWith(), cachedMap));
|
||||
|
||||
newCopy.setTimestamp(in.getTimestamp());
|
||||
|
||||
@@ -70,6 +70,7 @@ public enum Keyword {
|
||||
FLASH("Flash", SimpleKeyword.class, true, "You may cast this spell any time you could cast an instant."),
|
||||
FLASHBACK("Flashback", KeywordWithCost.class, false, "You may cast this card from your graveyard by paying %s rather than paying its mana cost. If you do, exile it as it resolves."),
|
||||
FLYING("Flying", SimpleKeyword.class, true, "This creature can't be blocked except by creatures with flying or reach."),
|
||||
FORETELL("Foretell", KeywordWithCost.class, false, "During your turn, you may pay {2} and exile this card from your hand face down. Cast it on a later turn for its foretell cost."),
|
||||
FORTIFY("Fortify", KeywordWithCost.class, false, "%s: Attach to target land you control. Fortify only as a sorcery."),
|
||||
FRENZY("Frenzy", KeywordWithAmount.class, false, "Whenever this creature attacks and isn't blocked, it gets +%d/+0 until end of turn."),
|
||||
GRAFT("Graft", KeywordWithAmount.class, false, "This permanent enters the battlefield with {%d:+1/+1 counter} on it. Whenever another creature enters the battlefield, you may move a +1/+1 counter from this permanent onto it."),
|
||||
|
||||
@@ -26,7 +26,6 @@ import forge.game.*;
|
||||
import forge.game.ability.AbilityKey;
|
||||
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.Presets;
|
||||
import forge.game.card.CardZoneTable;
|
||||
@@ -175,8 +174,7 @@ public class PhaseHandler implements java.io.Serializable {
|
||||
game.fireEvent(new GameEventTurnBegan(playerTurn, turn));
|
||||
|
||||
// Tokens starting game in play should suffer from Sum. Sickness
|
||||
final CardCollectionView list = playerTurn.getCardsIncludePhasingIn(ZoneType.Battlefield);
|
||||
for (final Card c : list) {
|
||||
for (final Card c : playerTurn.getCardsIncludePhasingIn(ZoneType.Battlefield)) {
|
||||
if (playerTurn.getTurn() > 0 || !c.isStartsGameInPlay()) {
|
||||
c.setSickness(false);
|
||||
}
|
||||
|
||||
@@ -111,6 +111,7 @@ public class Player extends GameEntity implements Comparable<Player> {
|
||||
private int numDrawnThisDrawStep = 0;
|
||||
private int numDiscardedThisTurn = 0;
|
||||
private int numTokenCreatedThisTurn = 0;
|
||||
private int numForetoldThisTurn = 0;
|
||||
private int numCardsInHandStartedThisTurnWith = 0;
|
||||
private final Map<String, FCollection<String>> notes = Maps.newHashMap();
|
||||
|
||||
@@ -1666,6 +1667,22 @@ public class Player extends GameEntity implements Comparable<Player> {
|
||||
numTokenCreatedThisTurn = 0;
|
||||
}
|
||||
|
||||
public final int getNumForetoldThisTurn() {
|
||||
return numForetoldThisTurn;
|
||||
}
|
||||
|
||||
public final void addForetoldThisTurn() {
|
||||
numForetoldThisTurn++;
|
||||
final Map<AbilityKey, Object> runParams = AbilityKey.newMap();
|
||||
runParams.put(AbilityKey.Player, this);
|
||||
runParams.put(AbilityKey.Num, numForetoldThisTurn);
|
||||
game.getTriggerHandler().runTrigger(TriggerType.Foretell, runParams, false);
|
||||
}
|
||||
|
||||
public final void resetNumForetoldThisTurn() {
|
||||
numForetoldThisTurn = 0;
|
||||
}
|
||||
|
||||
public final int getNumDiscardedThisTurn() {
|
||||
return numDiscardedThisTurn;
|
||||
}
|
||||
@@ -2581,7 +2598,7 @@ public class Player extends GameEntity implements Comparable<Player> {
|
||||
|
||||
controlledBy.remove(timestamp);
|
||||
getView().updateMindSlaveMaster(this);
|
||||
|
||||
|
||||
if (event) {
|
||||
game.fireEvent(new GameEventPlayerControl(this, oldLobbyPlayer, oldController, getLobbyPlayer(), getController()));
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ public enum AlternativeCost {
|
||||
Escape,
|
||||
Evoke,
|
||||
Flashback,
|
||||
Foretold,
|
||||
Madness,
|
||||
Offering,
|
||||
Outlast, // ActivatedAbility
|
||||
|
||||
@@ -466,23 +466,23 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
|
||||
public final void clearManaPaid() {
|
||||
payingMana.clear();
|
||||
}
|
||||
|
||||
|
||||
public final void applyPayingManaEffects() {
|
||||
Card host = getHostCard();
|
||||
|
||||
|
||||
for (Mana mana : getPayingMana()) {
|
||||
if (mana.triggersWhenSpent()) {
|
||||
mana.getManaAbility().addTriggersWhenSpent(this, host);
|
||||
}
|
||||
|
||||
|
||||
if (mana.addsCounters(this)) {
|
||||
mana.getManaAbility().createETBCounters(host, getActivatingPlayer());
|
||||
}
|
||||
|
||||
|
||||
if (mana.addsNoCounterMagic(this) && host != null) {
|
||||
host.setCanCounter(false);
|
||||
}
|
||||
|
||||
|
||||
if (isSpell() && host != null) {
|
||||
if (mana.addsKeywords(this) && mana.addsKeywordsType()
|
||||
&& host.getType().hasStringType(mana.getManaAbility().getAddsKeywordsType())) {
|
||||
@@ -823,6 +823,14 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
|
||||
return this.isAlternativeCost(AlternativeCost.Flashback);
|
||||
}
|
||||
|
||||
public boolean isForetelling() {
|
||||
return false;
|
||||
}
|
||||
public boolean isForetold() {
|
||||
return this.isAlternativeCost(AlternativeCost.Foretold);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return the aftermath
|
||||
*/
|
||||
@@ -1780,6 +1788,11 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (incR[0].equals("Static")) {
|
||||
if (!(root instanceof AbilityStatic)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else { //not a spell/ability type
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -118,6 +118,10 @@ public class SpellAbilityCondition extends SpellAbilityVariables {
|
||||
this.optionalCostPaid = true;
|
||||
}
|
||||
|
||||
if (value.equals("Foretold")) {
|
||||
this.foretold = true;
|
||||
}
|
||||
|
||||
if (params.containsKey("ConditionOptionalPaid")) {
|
||||
this.optionalBoolean = Boolean.parseBoolean(params.get("ConditionOptionalPaid"));
|
||||
}
|
||||
@@ -250,6 +254,7 @@ public class SpellAbilityCondition extends SpellAbilityVariables {
|
||||
if (this.kicked2 && !sa.isOptionalCostPaid(OptionalCost.Kicker2)) return false;
|
||||
if (this.altCostPaid && !sa.isOptionalCostPaid(OptionalCost.AltCost)) return false;
|
||||
if (this.surgeCostPaid && !sa.isSurged()) return false;
|
||||
if (this.foretold && !sa.isForetold()) return false;
|
||||
|
||||
if (this.optionalCostPaid && this.optionalBoolean && !sa.isOptionalCostPaid(OptionalCost.Generic)) return false;
|
||||
if (this.optionalCostPaid && !this.optionalBoolean && sa.isOptionalCostPaid(OptionalCost.Generic)) return false;
|
||||
|
||||
@@ -405,6 +405,7 @@ public class SpellAbilityVariables implements Cloneable {
|
||||
protected boolean optionalCostPaid = false; // Undergrowth other Pseudo-kickers
|
||||
protected boolean optionalBoolean = true; // Just in case you need to check if something wasn't kicked, etc
|
||||
protected boolean surgeCostPaid = false;
|
||||
protected boolean foretold = false;
|
||||
|
||||
/**
|
||||
* @return the allTargetsLegal
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Forge: Play Magic: the Gathering.
|
||||
* Copyright (C) 2011 Forge Team
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package forge.game.trigger;
|
||||
|
||||
import forge.game.ability.AbilityKey;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
import forge.util.Localizer;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author Forge
|
||||
*/
|
||||
public class TriggerForetell extends Trigger {
|
||||
|
||||
/**
|
||||
*
|
||||
* @param params
|
||||
* a {@link java.util.HashMap} object.
|
||||
* @param host
|
||||
* a {@link forge.game.card.Card} object.
|
||||
* @param intrinsic
|
||||
* the intrinsic
|
||||
*/
|
||||
public TriggerForetell(final Map<String, String> params, final Card host, final boolean intrinsic) {
|
||||
super(params, host, intrinsic);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getImportantStackObjects(SpellAbility sa) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(Localizer.getInstance().getMessage("lblPlayer")).append(": ").append(sa.getTriggeringObject(AbilityKey.Player));
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public final void setTriggeringObjects(final SpellAbility sa, Map<AbilityKey, Object> runParams) {
|
||||
sa.setTriggeringObjectsFrom(runParams, AbilityKey.Player);
|
||||
}
|
||||
|
||||
/** {@inheritDoc}
|
||||
* @param runParams*/
|
||||
@Override
|
||||
public final boolean performTest(final Map<AbilityKey, Object> runParams) {
|
||||
Player p = (Player) runParams.get(AbilityKey.Player);
|
||||
if (hasParam("ValidPlayer")) {
|
||||
if (!matchesValid(p, getParam("ValidPlayer").split(","), getHostCard())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (hasParam("OnlyFirst")) {
|
||||
if ((int) runParams.get(AbilityKey.Num) != 1) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -64,6 +64,7 @@ public enum TriggerType {
|
||||
Fight(TriggerFight.class),
|
||||
FightOnce(TriggerFightOnce.class),
|
||||
FlippedCoin(TriggerFlippedCoin.class),
|
||||
Foretell(TriggerForetell.class),
|
||||
Immediate(TriggerImmediate.class),
|
||||
Investigated(TriggerInvestigated.class),
|
||||
LandPlayed(TriggerLandPlayed.class),
|
||||
|
||||
@@ -52,7 +52,6 @@ import forge.game.keyword.Keyword;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.spellability.AbilityStatic;
|
||||
import forge.game.spellability.OptionalCost;
|
||||
import forge.game.spellability.Spell;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
import forge.game.spellability.SpellAbilityStackInstance;
|
||||
import forge.game.spellability.TargetChoices;
|
||||
@@ -241,8 +240,7 @@ public class MagicStack /* extends MyObservable */ implements Iterable<SpellAbil
|
||||
|
||||
if (sp.isSpell()) {
|
||||
source.setController(activator, 0);
|
||||
final Spell spell = (Spell) sp;
|
||||
if (spell.isCastFaceDown()) {
|
||||
if (sp.isCastFaceDown()) {
|
||||
// Need to override for double faced cards
|
||||
source.turnFaceDown(true);
|
||||
} else if (source.isFaceDown()) {
|
||||
|
||||
@@ -1847,7 +1847,7 @@ public class GameSimulatorTest extends SimulationTestCase {
|
||||
sim.simulateSpellAbility(gideonSA);
|
||||
sim.simulateSpellAbility(sparkDoubleSA);
|
||||
|
||||
Card simSpark = (Card)sim.getGameCopier().find(sparkDouble);
|
||||
Card simSpark = sim.getSimulatedGameState().findById(sparkDouble.getId());
|
||||
|
||||
assertNotNull(simSpark);
|
||||
assertTrue(simSpark.isInZone(ZoneType.Battlefield));
|
||||
|
||||
11
forge-gui/res/cardsfolder/upcoming/cosmos_charger.txt
Normal file
11
forge-gui/res/cardsfolder/upcoming/cosmos_charger.txt
Normal file
@@ -0,0 +1,11 @@
|
||||
Name:Cosmos Charger
|
||||
ManaCost:3 U
|
||||
Types:Creature Horse Spirit
|
||||
PT:3/3
|
||||
K:Flash
|
||||
K:Flying
|
||||
S:Mode$ ReduceCost | ValidSpell$ Static.Foretelling | Activator$ You | Amount$ 1 | Description$ Foretelling cards from your hand costs {1} less and can be done on any player’s turn.
|
||||
S:Mode$ Continuous | Affected$ You | AddKeyword$ Foretell on any player’s turn | Secondary$ True | Description$ Foretelling cards from your hand on any player’s turn.
|
||||
K:Foretell:2 U
|
||||
Oracle:Flash\nFlying\nForetelling cards from your hand costs {1} less and can be done on any player’s turn.\nForetell {2}{U} (During your turn, you may pay {2} and exile this card from your hand face down. Cast it on a later turn for its foretell cost.)
|
||||
|
||||
8
forge-gui/res/cardsfolder/upcoming/dream_devourer.txt
Normal file
8
forge-gui/res/cardsfolder/upcoming/dream_devourer.txt
Normal file
@@ -0,0 +1,8 @@
|
||||
Name:Dream Devourer
|
||||
ManaCost:1 B
|
||||
Types:Creature Demon Cleric
|
||||
PT:0/3
|
||||
S:Mode$ Continuous | Affected$ Card.nonLand+YouOwn+withoutForetell | AffectedZone$ Hand | AddKeyword$ Foretell | Description$ Each nonland card in your hand without foretell has foretell. Its foretell cost is equal to its mana cost reduced by {2}. (During your turn, you may pay {2} and exile it from your hand face down. Cast it on a later turn for its foretell cost.)
|
||||
T:Mode$ Foretell | ValidPlayer$ You | Execute$ TrigPump | TriggerZones$ Battlefield | TriggerDescription$ Whenever you foretell a card, CARDNAME gets +2/+0 until end of turn.
|
||||
SVar:TrigPump:DB$ Pump | Defined$ Self | NumAtt$ 2
|
||||
Oracle:Each nonland card in your hand without foretell has foretell. Its foretell cost is equal to its mana cost reduced by {2}. (During your turn, you may pay {2} and exile it from your hand face down. Cast it on a later turn for its foretell cost.)\n Whenever you foretell a card, Dream Devourer gets +2/+0 until end of turn.
|
||||
11
forge-gui/res/cardsfolder/upcoming/ethereal_valkyrie.txt
Normal file
11
forge-gui/res/cardsfolder/upcoming/ethereal_valkyrie.txt
Normal file
@@ -0,0 +1,11 @@
|
||||
Name:Ethereal Valkyrie
|
||||
ManaCost:4 U W
|
||||
Types:Creature Spirit Angel
|
||||
PT:4/4
|
||||
K:Flying
|
||||
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ DBDraw | TriggerDescription$ Whenever CARDNAME enters the battlefield or attacks, draw a card, then exile a card from your hand face down. It becomes foretold. Its foretell cost is its mana cost reduced by {2}. (On a later turn, you may cast it for its foretell cost, even if this creature has left the battlefield.)
|
||||
T:Mode$ Attacks | ValidCard$ Card.Self | Execute$ DBDraw | Secondary$ True | TriggerDescription$ Whenever CARDNAME enters the battlefield or attacks, draw a card, then exile a card from your hand face down. It becomes foretold. Its foretell cost is its mana cost reduced by {2}. (On a later turn, you may cast it for its foretell cost, even if this creature has left the battlefield.)
|
||||
SVar:DBDraw:DB$ Draw | NumCards$ 1 | SubAbility$ DBExile
|
||||
SVar:DBExile:DB$ ChangeZone | Origin$ Hand | Destination$ Exile | ChangeType$ Card | ChangeNum$ 1 | ExileFaceDown$ True | Mandatory$ True | Foretold$ True
|
||||
Oracle:Whenever Ethereal Valkyrie enters the battlefield or attacks, draw a card, then exile a card from your hand face down. It becomes foretold. Its foretell cost is its mana cost reduced by {2}. (On a later turn, you may cast it for its foretell cost, even if this creature has left the battlefield.)
|
||||
|
||||
8
forge-gui/res/cardsfolder/upcoming/karfell_harbinger.txt
Normal file
8
forge-gui/res/cardsfolder/upcoming/karfell_harbinger.txt
Normal file
@@ -0,0 +1,8 @@
|
||||
Name:Karfell Harbinger
|
||||
ManaCost:1 U
|
||||
Types:Creature Zombie Wizard
|
||||
PT:1/3
|
||||
A:AB$ Mana | Cost$ T | Produced$ U | RestrictValid$ Static.Foretelling,Spell.Instant,Spell.Sorcery | SpellDescription$ Add {U}. Spend this mana only to foretell a card from your hand or cast an instant or sorcery spell.
|
||||
DeckHints:Type$Instant|Sorcery
|
||||
Oracle:{T}: Add {U}. Spend this mana only to foretell a card from your hand or cast an instant or sorcery spell.
|
||||
|
||||
10
forge-gui/res/cardsfolder/upcoming/niko_defies_destiny.txt
Normal file
10
forge-gui/res/cardsfolder/upcoming/niko_defies_destiny.txt
Normal file
@@ -0,0 +1,10 @@
|
||||
Name:Niko Defies Destiny
|
||||
ManaCost:1 W U
|
||||
Types:Enchantment Saga
|
||||
K:Saga:3:DBGainLife,DBMana,DBChangeZone
|
||||
SVar:DBGainLife:DB$ GainLife | LifeAmount$ X | References$ X | SpellDescription$ You gain 2 life for each foretold card you own in exile.
|
||||
SVar:X:Count$ValidExile Card.foretold/Times.2
|
||||
SVar:DBMana:DB$ Mana | Produced$ W U | RestrictValid$ Static.Foretelling,Card.withForetell | SpellDescription$ Add {W}{U}. Spend this mana only to foretell cards or cast spells that have foretell.
|
||||
SVar:DBChangeZone:DB$ChangeZone | Origin$ Graveyard | Destination$ Hand | ValidTgts$ Card.YouCtrl+withForetell | SpellDescription$ Return target card with foretell from your graveyard to your hand.
|
||||
DeckHas:Ability$Graveyard & Ability$GainLife
|
||||
Oracle:(As this Saga enters and after your draw step, add a lore counter. Sacrifice after III.)\nI — You gain 2 life for each foretold card you own in exile.\nII — Add {W}{U}. Spend this mana only to foretell cards or cast spells that have foretell.\nIII — Return target card with foretell from your graveyard to your hand.
|
||||
8
forge-gui/res/cardsfolder/upcoming/poison_the_cup.txt
Normal file
8
forge-gui/res/cardsfolder/upcoming/poison_the_cup.txt
Normal file
@@ -0,0 +1,8 @@
|
||||
Name:Poison the Cup
|
||||
ManaCost:1 B B
|
||||
Types:Instant
|
||||
A:SP$ Destroy | Cost$ 1 B B | ValidTgts$ Creature | TgtPrompt$ Select target creature | SubAbility$ DBScry | SpellDescription$ Destroy target creature. If this spell was foretold, scry 2.
|
||||
SVar:DBScry:DB$ Scry | ScryNum$ 2 | Condition$ Foretold
|
||||
K:Foretell:1 B
|
||||
Oracle:Destroy target creature. If this spell was foretold, scry 2.\nForetell {1}{B} (During your turn, you may pay {2} and exile this card from your hand face down. Cast it on a later turn for its foretell cost.)
|
||||
|
||||
6
forge-gui/res/cardsfolder/upcoming/scorn_effigy.txt
Normal file
6
forge-gui/res/cardsfolder/upcoming/scorn_effigy.txt
Normal file
@@ -0,0 +1,6 @@
|
||||
Name:Scorn Effigy
|
||||
ManaCost:3
|
||||
Types:Artifact Creature Scarecrow
|
||||
PT:2/3
|
||||
K:Foretell:0
|
||||
Oracle:Foretell {0} (During your turn, you may pay {2} and exile this card from your hand face down. Cast it on a later turn for its foretell cost.)
|
||||
@@ -0,0 +1,9 @@
|
||||
Name:Starnheim Unleashed
|
||||
ManaCost:2 W W
|
||||
Types:Sorcery
|
||||
A:SP$ Token | Cost$ 2 W W | TokenAmount$ Y | TokenScript$ w_4_4_angel_warrior_flying_vigilance | References$ X,Y | SpellDescription$ Create a 4/4 white Angel Warrior creature token with flying and vigilance. If this spell was foretold, create X of those tokens instead.
|
||||
SVar:Y:Count$Foretold.X.1
|
||||
SVar:X:Count$xPaid
|
||||
K:Foretell:X X W
|
||||
DeckHas:Ability$Token
|
||||
Oracle:Create a 4/4 white Angel Warrior creature token with flying and vigilance. If this spell was foretold, create X of those tokens instead.\nForetell {X}{X}{W} (During your turn, you may pay {2} and exile this card from your hand face down. Cast it on a later turn for its foretell cost.)
|
||||
@@ -0,0 +1,8 @@
|
||||
Name:Angel Warrior
|
||||
ManaCost:no cost
|
||||
Types:Creature Angel Warrior
|
||||
Colors:white
|
||||
PT:4/4
|
||||
K:Flying
|
||||
K:Vigilance
|
||||
Oracle:Flying\nVigilance
|
||||
@@ -65,7 +65,7 @@ public class HumanPlay {
|
||||
boolean castFaceDown = sa.isCastFaceDown();
|
||||
|
||||
sa.setActivatingPlayer(p);
|
||||
boolean flippedToCast = sa instanceof Spell && source.isFaceDown();
|
||||
boolean flippedToCast = sa.isSpell() && source.isFaceDown();
|
||||
|
||||
source.setSplitStateToPlayAbility(sa);
|
||||
sa = chooseOptionalAdditionalCosts(p, sa);
|
||||
@@ -98,7 +98,12 @@ public class HumanPlay {
|
||||
final HumanPlaySpellAbility req = new HumanPlaySpellAbility(controller, sa);
|
||||
if (!req.playAbility(true, false, false)) {
|
||||
if (flippedToCast && !castFaceDown) {
|
||||
source.turnFaceDown(true);
|
||||
// need to get the changed card if able
|
||||
Card rollback = p.getGame().getCardState(sa.getHostCard());
|
||||
rollback.turnFaceDown(true);
|
||||
if (rollback.isInZone(ZoneType.Exile)) {
|
||||
rollback.addMayLookTemp(p);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -19,7 +19,6 @@ package forge.player;
|
||||
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Maps;
|
||||
import forge.card.CardStateName;
|
||||
import forge.card.CardType;
|
||||
import forge.card.MagicColor;
|
||||
import forge.game.Game;
|
||||
@@ -64,7 +63,6 @@ public class HumanPlaySpellAbility {
|
||||
|
||||
// used to rollback
|
||||
Zone fromZone = null;
|
||||
CardStateName fromState = null;
|
||||
int zonePosition = 0;
|
||||
final ManaPool manapool = human.getManaPool();
|
||||
|
||||
@@ -80,15 +78,9 @@ public class HumanPlaySpellAbility {
|
||||
|
||||
if (ability.isSpell() && !c.isCopiedSpell()) {
|
||||
fromZone = game.getZoneOf(c);
|
||||
fromState = c.getCurrentStateName();
|
||||
if (fromZone != null) {
|
||||
zonePosition = fromZone.getCards().indexOf(c);
|
||||
}
|
||||
// This is should happen earlier, before the Modal spell is chosen
|
||||
// Turn face-down card face up (except case of morph spell)
|
||||
if (ability.isSpell() && !ability.isCastFaceDown() && fromState == CardStateName.FaceDown) {
|
||||
c.turnFaceUp(null);
|
||||
}
|
||||
ability.setHostCard(game.getAction().moveToStack(c, ability));
|
||||
}
|
||||
|
||||
@@ -155,7 +147,7 @@ public class HumanPlaySpellAbility {
|
||||
|
||||
if (!prerequisitesMet) {
|
||||
if (!ability.isTrigger()) {
|
||||
rollbackAbility(fromZone, zonePosition, payment);
|
||||
rollbackAbility(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);
|
||||
@@ -200,15 +192,17 @@ public class HumanPlaySpellAbility {
|
||||
return true;
|
||||
}
|
||||
|
||||
private void rollbackAbility(final Zone fromZone, final int zonePosition, CostPayment payment) {
|
||||
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
|
||||
ability.getHostCard().setCastSA(null);
|
||||
ability.getHostCard().setCastFrom(null);
|
||||
// add back to where it came from
|
||||
game.getAction().moveTo(fromZone, ability.getHostCard(), zonePosition >= 0 ? Integer.valueOf(zonePosition) : null, null);
|
||||
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.clearTargets();
|
||||
|
||||
Reference in New Issue
Block a user