mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-17 19:28: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");
|
return sa.hasParam("Equip");
|
||||||
} else if (property.equals("Boast")) {
|
} else if (property.equals("Boast")) {
|
||||||
return sa.isBoast();
|
return sa.isBoast();
|
||||||
|
} else if (property.equals("Foretelling")) {
|
||||||
|
return sa.isForetelling();
|
||||||
|
} else if (property.equals("Foretold")) {
|
||||||
|
return sa.isForetold();
|
||||||
} else if (property.equals("MayPlaySource")) {
|
} else if (property.equals("MayPlaySource")) {
|
||||||
StaticAbility m = sa.getMayPlay();
|
StaticAbility m = sa.getMayPlay();
|
||||||
if (m == null) {
|
if (m == null) {
|
||||||
|
|||||||
@@ -73,6 +73,8 @@ public class GameAction {
|
|||||||
// Reset Activations per Turn
|
// Reset Activations per Turn
|
||||||
for (final Card card : game.getCardsInGame()) {
|
for (final Card card : game.getCardsInGame()) {
|
||||||
card.resetActivationsPerTurn();
|
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
|
// Don't copy Tokens, copy only cards leaving the battlefield
|
||||||
// and returning to hand (to recreate their spell ability information)
|
// and returning to hand (to recreate their spell ability information)
|
||||||
if (suppress || toBattlefield || zoneTo.is(ZoneType.Stack)) {
|
if (suppress || toBattlefield) {
|
||||||
copied = c;
|
copied = c;
|
||||||
|
|
||||||
if (lastKnownInfo == null) {
|
if (lastKnownInfo == null) {
|
||||||
@@ -193,10 +195,6 @@ public class GameAction {
|
|||||||
lastKnownInfo = CardUtil.getLKICopy(c);
|
lastKnownInfo = CardUtil.getLKICopy(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (wasFacedown) {
|
|
||||||
c.forceTurnFaceUp();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!c.isToken()) {
|
if (!c.isToken()) {
|
||||||
copied = CardFactory.copyCard(c, false);
|
copied = CardFactory.copyCard(c, false);
|
||||||
|
|
||||||
|
|||||||
@@ -204,8 +204,51 @@ public final class GameActionUtil {
|
|||||||
flashback.setPayCosts(new Cost(k[1], false));
|
flashback.setPayCosts(new Cost(k[1], false));
|
||||||
}
|
}
|
||||||
alternatives.add(flashback);
|
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
|
// reset static abilities
|
||||||
|
|||||||
@@ -701,6 +701,13 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
|
|||||||
if (sa.hasParam("ExileFaceDown")) {
|
if (sa.hasParam("ExileFaceDown")) {
|
||||||
movedCard.turnFaceDown(true);
|
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")) {
|
if (sa.hasParam("TrackDiscarded")) {
|
||||||
movedCard.setMadnessWithoutCast(true);
|
movedCard.setMadnessWithoutCast(true);
|
||||||
@@ -1240,6 +1247,13 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
|
|||||||
if (sa.hasParam("ExileFaceDown")) {
|
if (sa.hasParam("ExileFaceDown")) {
|
||||||
movedCard.turnFaceDown(true);
|
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 {
|
else {
|
||||||
movedCard = game.getAction().moveTo(destination, c, 0, cause, moveParams);
|
movedCard = game.getAction().moveTo(destination, c, 0, cause, moveParams);
|
||||||
|
|||||||
@@ -177,6 +177,10 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|
|||||||
|
|
||||||
private boolean manifested = false;
|
private boolean manifested = false;
|
||||||
|
|
||||||
|
private boolean foretold = false;
|
||||||
|
private boolean foretoldThisTurn = false;
|
||||||
|
private boolean foretoldByEffect = false;
|
||||||
|
|
||||||
private long bestowTimestamp = -1;
|
private long bestowTimestamp = -1;
|
||||||
private long transformedTimestamp = 0;
|
private long transformedTimestamp = 0;
|
||||||
private boolean tributed = false;
|
private boolean tributed = false;
|
||||||
@@ -1694,7 +1698,7 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|
|||||||
} else {
|
} else {
|
||||||
sbLong.append(parts[0]).append(" ").append(ManaCostParser.parse(parts[1])).append("\r\n");
|
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(":");
|
String[] k = keyword.split(":");
|
||||||
sbLong.append(k[0]);
|
sbLong.append(k[0]);
|
||||||
if (k.length > 1) {
|
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("Changeling") || keyword.equals("Delve")
|
||||||
|| keyword.equals("Split second") || keyword.equals("Sunburst")
|
|| keyword.equals("Split second") || keyword.equals("Sunburst")
|
||||||
|| keyword.equals("Suspend") // for the ones without amounnt
|
|| keyword.equals("Suspend") // for the ones without amounnt
|
||||||
|
|| keyword.equals("Foretell") // for the ones without cost
|
||||||
|| keyword.equals("Hideaway") || keyword.equals("Ascend")
|
|| keyword.equals("Hideaway") || keyword.equals("Ascend")
|
||||||
|| keyword.equals("Totem armor") || keyword.equals("Battle cry")
|
|| keyword.equals("Totem armor") || keyword.equals("Battle cry")
|
||||||
|| keyword.equals("Devoid") || keyword.equals("Riot")){
|
|| keyword.equals("Devoid") || keyword.equals("Riot")){
|
||||||
@@ -2236,7 +2241,7 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|
|||||||
sbBefore.append("\r\n");
|
sbBefore.append("\r\n");
|
||||||
} else if (keyword.startsWith("Entwine") || keyword.startsWith("Madness")
|
} else if (keyword.startsWith("Entwine") || keyword.startsWith("Madness")
|
||||||
|| keyword.startsWith("Miracle") || keyword.startsWith("Recover")
|
|| keyword.startsWith("Miracle") || keyword.startsWith("Recover")
|
||||||
|| keyword.startsWith("Escape")) {
|
|| keyword.startsWith("Escape") || keyword.startsWith("Foretell:")) {
|
||||||
final String[] k = keyword.split(":");
|
final String[] k = keyword.split(":");
|
||||||
final Cost cost = new Cost(k[1], false);
|
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() {
|
public final void animateBestow() {
|
||||||
animateBestow(true);
|
animateBestow(true);
|
||||||
}
|
}
|
||||||
@@ -6567,11 +6608,6 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|
|||||||
return numberGameActivations.containsKey(original) ? numberGameActivations.get(original) : 0;
|
return numberGameActivations.containsKey(original) ? numberGameActivations.get(original) : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void resetTurnActivations() {
|
|
||||||
numberTurnActivations.clear();
|
|
||||||
numberTurnActivationsStatic.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<String> getChosenModesTurn(SpellAbility ability) {
|
public List<String> getChosenModesTurn(SpellAbility ability) {
|
||||||
SpellAbility original = null;
|
SpellAbility original = null;
|
||||||
SpellAbility root = ability.getRootAbility();
|
SpellAbility root = ability.getRootAbility();
|
||||||
|
|||||||
@@ -88,7 +88,7 @@ public class CardFactory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
out.setZone(in.getZone());
|
out.setZone(in.getZone());
|
||||||
out.setState(in.getCurrentStateName(), true);
|
out.setState(in.getFaceupCardStateName(), true);
|
||||||
out.setBackSide(in.isBackSide());
|
out.setBackSide(in.isBackSide());
|
||||||
|
|
||||||
// this's necessary for forge.game.GameAction.unattachCardLeavingBattlefield(Card)
|
// 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.Sentry;
|
||||||
import io.sentry.event.BreadcrumbBuilder;
|
import io.sentry.event.BreadcrumbBuilder;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>
|
* <p>
|
||||||
* CardFactoryUtil class.
|
* CardFactoryUtil class.
|
||||||
@@ -1404,6 +1403,13 @@ public class CardFactoryUtil {
|
|||||||
return doXMath(StringUtils.isNumeric(v) ? Integer.parseInt(v) : xCount(c, c.getSVar(v)), m, c);
|
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>
|
// Count$Presence_<Type>.<True>.<False>
|
||||||
if (sq[0].startsWith("Presence")) {
|
if (sq[0].startsWith("Presence")) {
|
||||||
final String type = sq[0].split("_")[1];
|
final String type = sq[0].split("_")[1];
|
||||||
@@ -1449,7 +1455,6 @@ public class CardFactoryUtil {
|
|||||||
return forge.util.MyRandom.getRandom().nextInt(1+max-min) + min;
|
return forge.util.MyRandom.getRandom().nextInt(1+max-min) + min;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Count$Domain
|
// Count$Domain
|
||||||
if (sq[0].startsWith("Domain")) {
|
if (sq[0].startsWith("Domain")) {
|
||||||
int n = 0;
|
int n = 0;
|
||||||
@@ -1997,7 +2002,6 @@ public class CardFactoryUtil {
|
|||||||
final Set<String> hexproofkw = Sets.newHashSet();
|
final Set<String> hexproofkw = Sets.newHashSet();
|
||||||
final Set<String> allkw = Sets.newHashSet();
|
final Set<String> allkw = Sets.newHashSet();
|
||||||
|
|
||||||
|
|
||||||
for (Card c : CardLists.getValidCards(cardlist, restrictions, p, host, null)) {
|
for (Card c : CardLists.getValidCards(cardlist, restrictions, p, host, null)) {
|
||||||
for (KeywordInterface inst : c.getKeywords()) {
|
for (KeywordInterface inst : c.getKeywords()) {
|
||||||
final String k = inst.getOriginal();
|
final String k = inst.getOriginal();
|
||||||
@@ -2155,7 +2159,6 @@ public class CardFactoryUtil {
|
|||||||
return re;
|
return re;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static ReplacementEffect makeEtbCounter(final String kw, final Card card, final boolean intrinsic)
|
public static ReplacementEffect makeEtbCounter(final String kw, final Card card, final boolean intrinsic)
|
||||||
{
|
{
|
||||||
String parse = kw;
|
String parse = kw;
|
||||||
@@ -4095,6 +4098,60 @@ public class CardFactoryUtil {
|
|||||||
newSA.setAlternativeCost(AlternativeCost.Evoke);
|
newSA.setAlternativeCost(AlternativeCost.Evoke);
|
||||||
newSA.setIntrinsic(intrinsic);
|
newSA.setIntrinsic(intrinsic);
|
||||||
inst.addSpellAbility(newSA);
|
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")) {
|
} else if (keyword.startsWith("Fortify")) {
|
||||||
String[] k = keyword.split(":");
|
String[] k = keyword.split(":");
|
||||||
// Get cost string
|
// Get cost string
|
||||||
|
|||||||
@@ -1670,36 +1670,40 @@ public class CardProperty {
|
|||||||
if (property.equals("pseudokicked")) {
|
if (property.equals("pseudokicked")) {
|
||||||
if (!card.isOptionalCostPaid(OptionalCost.Generic)) return false;
|
if (!card.isOptionalCostPaid(OptionalCost.Generic)) return false;
|
||||||
}
|
}
|
||||||
} else if (property.startsWith("surged")) {
|
} else if (property.equals("surged")) {
|
||||||
if (card.getCastSA() == null) {
|
if (card.getCastSA() == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return card.getCastSA().isSurged();
|
return card.getCastSA().isSurged();
|
||||||
} else if (property.startsWith("dashed")) {
|
} else if (property.equals("dashed")) {
|
||||||
if (card.getCastSA() == null) {
|
if (card.getCastSA() == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return card.getCastSA().isDash();
|
return card.getCastSA().isDash();
|
||||||
} else if (property.startsWith("escaped")) {
|
} else if (property.equals("escaped")) {
|
||||||
if (card.getCastSA() == null) {
|
if (card.getCastSA() == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return card.getCastSA().isEscape();
|
return card.getCastSA().isEscape();
|
||||||
} else if (property.startsWith("evoked")) {
|
} else if (property.equals("evoked")) {
|
||||||
if (card.getCastSA() == null) {
|
if (card.getCastSA() == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return card.getCastSA().isEvoke();
|
return card.getCastSA().isEvoke();
|
||||||
} else if (property.startsWith("prowled")) {
|
} else if (property.equals("prowled")) {
|
||||||
if (card.getCastSA() == null) {
|
if (card.getCastSA() == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return card.getCastSA().isProwl();
|
return card.getCastSA().isProwl();
|
||||||
} else if (property.startsWith("spectacle")) {
|
} else if (property.equals("spectacle")) {
|
||||||
if (card.getCastSA() == null) {
|
if (card.getCastSA() == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return card.getCastSA().isSpectacle();
|
return card.getCastSA().isSpectacle();
|
||||||
|
} else if (property.equals("foretold")) {
|
||||||
|
if (!card.isForetold()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
} else if (property.equals("HasDevoured")) {
|
} else if (property.equals("HasDevoured")) {
|
||||||
if (card.getDevouredCards().isEmpty()) {
|
if (card.getDevouredCards().isEmpty()) {
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -278,6 +278,10 @@ public final class CardUtil {
|
|||||||
|
|
||||||
newCopy.copyChangedTextFrom(in);
|
newCopy.copyChangedTextFrom(in);
|
||||||
|
|
||||||
|
newCopy.setForetold(in.isForetold());
|
||||||
|
newCopy.setForetoldThisTurn(in.isForetoldThisTurn());
|
||||||
|
newCopy.setForetoldByEffect(in.isForetoldByEffect());
|
||||||
|
|
||||||
newCopy.setMeldedWith(getLKICopy(in.getMeldedWith(), cachedMap));
|
newCopy.setMeldedWith(getLKICopy(in.getMeldedWith(), cachedMap));
|
||||||
|
|
||||||
newCopy.setTimestamp(in.getTimestamp());
|
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."),
|
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."),
|
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."),
|
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."),
|
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."),
|
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."),
|
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.ability.AbilityKey;
|
||||||
import forge.game.card.Card;
|
import forge.game.card.Card;
|
||||||
import forge.game.card.CardCollection;
|
import forge.game.card.CardCollection;
|
||||||
import forge.game.card.CardCollectionView;
|
|
||||||
import forge.game.card.CardLists;
|
import forge.game.card.CardLists;
|
||||||
import forge.game.card.CardPredicates.Presets;
|
import forge.game.card.CardPredicates.Presets;
|
||||||
import forge.game.card.CardZoneTable;
|
import forge.game.card.CardZoneTable;
|
||||||
@@ -175,8 +174,7 @@ public class PhaseHandler implements java.io.Serializable {
|
|||||||
game.fireEvent(new GameEventTurnBegan(playerTurn, turn));
|
game.fireEvent(new GameEventTurnBegan(playerTurn, turn));
|
||||||
|
|
||||||
// Tokens starting game in play should suffer from Sum. Sickness
|
// Tokens starting game in play should suffer from Sum. Sickness
|
||||||
final CardCollectionView list = playerTurn.getCardsIncludePhasingIn(ZoneType.Battlefield);
|
for (final Card c : playerTurn.getCardsIncludePhasingIn(ZoneType.Battlefield)) {
|
||||||
for (final Card c : list) {
|
|
||||||
if (playerTurn.getTurn() > 0 || !c.isStartsGameInPlay()) {
|
if (playerTurn.getTurn() > 0 || !c.isStartsGameInPlay()) {
|
||||||
c.setSickness(false);
|
c.setSickness(false);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -111,6 +111,7 @@ public class Player extends GameEntity implements Comparable<Player> {
|
|||||||
private int numDrawnThisDrawStep = 0;
|
private int numDrawnThisDrawStep = 0;
|
||||||
private int numDiscardedThisTurn = 0;
|
private int numDiscardedThisTurn = 0;
|
||||||
private int numTokenCreatedThisTurn = 0;
|
private int numTokenCreatedThisTurn = 0;
|
||||||
|
private int numForetoldThisTurn = 0;
|
||||||
private int numCardsInHandStartedThisTurnWith = 0;
|
private int numCardsInHandStartedThisTurnWith = 0;
|
||||||
private final Map<String, FCollection<String>> notes = Maps.newHashMap();
|
private final Map<String, FCollection<String>> notes = Maps.newHashMap();
|
||||||
|
|
||||||
@@ -1666,6 +1667,22 @@ public class Player extends GameEntity implements Comparable<Player> {
|
|||||||
numTokenCreatedThisTurn = 0;
|
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() {
|
public final int getNumDiscardedThisTurn() {
|
||||||
return numDiscardedThisTurn;
|
return numDiscardedThisTurn;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ public enum AlternativeCost {
|
|||||||
Escape,
|
Escape,
|
||||||
Evoke,
|
Evoke,
|
||||||
Flashback,
|
Flashback,
|
||||||
|
Foretold,
|
||||||
Madness,
|
Madness,
|
||||||
Offering,
|
Offering,
|
||||||
Outlast, // ActivatedAbility
|
Outlast, // ActivatedAbility
|
||||||
|
|||||||
@@ -823,6 +823,14 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
|
|||||||
return this.isAlternativeCost(AlternativeCost.Flashback);
|
return this.isAlternativeCost(AlternativeCost.Flashback);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isForetelling() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
public boolean isForetold() {
|
||||||
|
return this.isAlternativeCost(AlternativeCost.Foretold);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the aftermath
|
* @return the aftermath
|
||||||
*/
|
*/
|
||||||
@@ -1780,6 +1788,11 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (incR[0].equals("Static")) {
|
||||||
|
if (!(root instanceof AbilityStatic)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
else { //not a spell/ability type
|
else { //not a spell/ability type
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -118,6 +118,10 @@ public class SpellAbilityCondition extends SpellAbilityVariables {
|
|||||||
this.optionalCostPaid = true;
|
this.optionalCostPaid = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (value.equals("Foretold")) {
|
||||||
|
this.foretold = true;
|
||||||
|
}
|
||||||
|
|
||||||
if (params.containsKey("ConditionOptionalPaid")) {
|
if (params.containsKey("ConditionOptionalPaid")) {
|
||||||
this.optionalBoolean = Boolean.parseBoolean(params.get("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.kicked2 && !sa.isOptionalCostPaid(OptionalCost.Kicker2)) return false;
|
||||||
if (this.altCostPaid && !sa.isOptionalCostPaid(OptionalCost.AltCost)) return false;
|
if (this.altCostPaid && !sa.isOptionalCostPaid(OptionalCost.AltCost)) return false;
|
||||||
if (this.surgeCostPaid && !sa.isSurged()) 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;
|
||||||
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 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 optionalBoolean = true; // Just in case you need to check if something wasn't kicked, etc
|
||||||
protected boolean surgeCostPaid = false;
|
protected boolean surgeCostPaid = false;
|
||||||
|
protected boolean foretold = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the allTargetsLegal
|
* @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),
|
Fight(TriggerFight.class),
|
||||||
FightOnce(TriggerFightOnce.class),
|
FightOnce(TriggerFightOnce.class),
|
||||||
FlippedCoin(TriggerFlippedCoin.class),
|
FlippedCoin(TriggerFlippedCoin.class),
|
||||||
|
Foretell(TriggerForetell.class),
|
||||||
Immediate(TriggerImmediate.class),
|
Immediate(TriggerImmediate.class),
|
||||||
Investigated(TriggerInvestigated.class),
|
Investigated(TriggerInvestigated.class),
|
||||||
LandPlayed(TriggerLandPlayed.class),
|
LandPlayed(TriggerLandPlayed.class),
|
||||||
|
|||||||
@@ -52,7 +52,6 @@ import forge.game.keyword.Keyword;
|
|||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
import forge.game.spellability.AbilityStatic;
|
import forge.game.spellability.AbilityStatic;
|
||||||
import forge.game.spellability.OptionalCost;
|
import forge.game.spellability.OptionalCost;
|
||||||
import forge.game.spellability.Spell;
|
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
import forge.game.spellability.SpellAbilityStackInstance;
|
import forge.game.spellability.SpellAbilityStackInstance;
|
||||||
import forge.game.spellability.TargetChoices;
|
import forge.game.spellability.TargetChoices;
|
||||||
@@ -241,8 +240,7 @@ public class MagicStack /* extends MyObservable */ implements Iterable<SpellAbil
|
|||||||
|
|
||||||
if (sp.isSpell()) {
|
if (sp.isSpell()) {
|
||||||
source.setController(activator, 0);
|
source.setController(activator, 0);
|
||||||
final Spell spell = (Spell) sp;
|
if (sp.isCastFaceDown()) {
|
||||||
if (spell.isCastFaceDown()) {
|
|
||||||
// Need to override for double faced cards
|
// Need to override for double faced cards
|
||||||
source.turnFaceDown(true);
|
source.turnFaceDown(true);
|
||||||
} else if (source.isFaceDown()) {
|
} else if (source.isFaceDown()) {
|
||||||
|
|||||||
@@ -1847,7 +1847,7 @@ public class GameSimulatorTest extends SimulationTestCase {
|
|||||||
sim.simulateSpellAbility(gideonSA);
|
sim.simulateSpellAbility(gideonSA);
|
||||||
sim.simulateSpellAbility(sparkDoubleSA);
|
sim.simulateSpellAbility(sparkDoubleSA);
|
||||||
|
|
||||||
Card simSpark = (Card)sim.getGameCopier().find(sparkDouble);
|
Card simSpark = sim.getSimulatedGameState().findById(sparkDouble.getId());
|
||||||
|
|
||||||
assertNotNull(simSpark);
|
assertNotNull(simSpark);
|
||||||
assertTrue(simSpark.isInZone(ZoneType.Battlefield));
|
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();
|
boolean castFaceDown = sa.isCastFaceDown();
|
||||||
|
|
||||||
sa.setActivatingPlayer(p);
|
sa.setActivatingPlayer(p);
|
||||||
boolean flippedToCast = sa instanceof Spell && source.isFaceDown();
|
boolean flippedToCast = sa.isSpell() && source.isFaceDown();
|
||||||
|
|
||||||
source.setSplitStateToPlayAbility(sa);
|
source.setSplitStateToPlayAbility(sa);
|
||||||
sa = chooseOptionalAdditionalCosts(p, sa);
|
sa = chooseOptionalAdditionalCosts(p, sa);
|
||||||
@@ -98,7 +98,12 @@ public class HumanPlay {
|
|||||||
final HumanPlaySpellAbility req = new HumanPlaySpellAbility(controller, sa);
|
final HumanPlaySpellAbility req = new HumanPlaySpellAbility(controller, sa);
|
||||||
if (!req.playAbility(true, false, false)) {
|
if (!req.playAbility(true, false, false)) {
|
||||||
if (flippedToCast && !castFaceDown) {
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ package forge.player;
|
|||||||
|
|
||||||
import com.google.common.collect.Iterables;
|
import com.google.common.collect.Iterables;
|
||||||
import com.google.common.collect.Maps;
|
import com.google.common.collect.Maps;
|
||||||
import forge.card.CardStateName;
|
|
||||||
import forge.card.CardType;
|
import forge.card.CardType;
|
||||||
import forge.card.MagicColor;
|
import forge.card.MagicColor;
|
||||||
import forge.game.Game;
|
import forge.game.Game;
|
||||||
@@ -64,7 +63,6 @@ public class HumanPlaySpellAbility {
|
|||||||
|
|
||||||
// used to rollback
|
// used to rollback
|
||||||
Zone fromZone = null;
|
Zone fromZone = null;
|
||||||
CardStateName fromState = null;
|
|
||||||
int zonePosition = 0;
|
int zonePosition = 0;
|
||||||
final ManaPool manapool = human.getManaPool();
|
final ManaPool manapool = human.getManaPool();
|
||||||
|
|
||||||
@@ -80,15 +78,9 @@ public class HumanPlaySpellAbility {
|
|||||||
|
|
||||||
if (ability.isSpell() && !c.isCopiedSpell()) {
|
if (ability.isSpell() && !c.isCopiedSpell()) {
|
||||||
fromZone = game.getZoneOf(c);
|
fromZone = game.getZoneOf(c);
|
||||||
fromState = c.getCurrentStateName();
|
|
||||||
if (fromZone != null) {
|
if (fromZone != null) {
|
||||||
zonePosition = fromZone.getCards().indexOf(c);
|
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));
|
ability.setHostCard(game.getAction().moveToStack(c, ability));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -155,7 +147,7 @@ public class HumanPlaySpellAbility {
|
|||||||
|
|
||||||
if (!prerequisitesMet) {
|
if (!prerequisitesMet) {
|
||||||
if (!ability.isTrigger()) {
|
if (!ability.isTrigger()) {
|
||||||
rollbackAbility(fromZone, zonePosition, payment);
|
rollbackAbility(fromZone, zonePosition, payment, c);
|
||||||
if (ability.getHostCard().isMadness()) {
|
if (ability.getHostCard().isMadness()) {
|
||||||
// if a player failed to play madness cost, move the card to graveyard
|
// if a player failed to play madness cost, move the card to graveyard
|
||||||
Card newCard = game.getAction().moveToGraveyard(c, null);
|
Card newCard = game.getAction().moveToGraveyard(c, null);
|
||||||
@@ -200,15 +192,17 @@ public class HumanPlaySpellAbility {
|
|||||||
return true;
|
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
|
// cancel ability during target choosing
|
||||||
final Game game = ability.getActivatingPlayer().getGame();
|
final Game game = ability.getActivatingPlayer().getGame();
|
||||||
|
|
||||||
if (fromZone != null) { // and not a copy
|
if (fromZone != null) { // and not a copy
|
||||||
ability.getHostCard().setCastSA(null);
|
oldCard.setCastSA(null);
|
||||||
ability.getHostCard().setCastFrom(null);
|
oldCard.setCastFrom(null);
|
||||||
// add back to where it came from
|
// add back to where it came from, hopefully old state
|
||||||
game.getAction().moveTo(fromZone, ability.getHostCard(), zonePosition >= 0 ? Integer.valueOf(zonePosition) : null, null);
|
// skip GameAction
|
||||||
|
oldCard.getZone().remove(oldCard);
|
||||||
|
fromZone.add(oldCard, zonePosition >= 0 ? Integer.valueOf(zonePosition) : null);
|
||||||
}
|
}
|
||||||
|
|
||||||
ability.clearTargets();
|
ability.clearTargets();
|
||||||
|
|||||||
Reference in New Issue
Block a user