diff --git a/forge-game/src/main/java/forge/game/ForgeScript.java b/forge-game/src/main/java/forge/game/ForgeScript.java index ee2aa04bcb0..bf35e289bad 100644 --- a/forge-game/src/main/java/forge/game/ForgeScript.java +++ b/forge-game/src/main/java/forge/game/ForgeScript.java @@ -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) { diff --git a/forge-game/src/main/java/forge/game/GameAction.java b/forge-game/src/main/java/forge/game/GameAction.java index 21bf17450b8..7237d41a4af 100644 --- a/forge-game/src/main/java/forge/game/GameAction.java +++ b/forge-game/src/main/java/forge/game/GameAction.java @@ -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); diff --git a/forge-game/src/main/java/forge/game/GameActionUtil.java b/forge-game/src/main/java/forge/game/GameActionUtil.java index 510d52d3d15..45deabfc9c1 100644 --- a/forge-game/src/main/java/forge/game/GameActionUtil.java +++ b/forge-game/src/main/java/forge/game/GameActionUtil.java @@ -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 . */ @@ -54,7 +54,7 @@ import java.util.List; *

* GameActionUtil class. *

- * + * * @author Forge * @version $Id$ */ @@ -68,7 +68,7 @@ public final class GameActionUtil { *

* Find the alternative costs to a {@link SpellAbility}. *

- * + * * @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 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 getAdditionalCostSpell(final SpellAbility sa) { final List 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; diff --git a/forge-game/src/main/java/forge/game/ability/effects/ChangeZoneEffect.java b/forge-game/src/main/java/forge/game/ability/effects/ChangeZoneEffect.java index 9173d117ba1..8e5f57b1d92 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/ChangeZoneEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/ChangeZoneEffect.java @@ -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); diff --git a/forge-game/src/main/java/forge/game/card/Card.java b/forge-game/src/main/java/forge/game/card/Card.java index 0eccdec5c6d..e1b2ca62fbd 100644 --- a/forge-game/src/main/java/forge/game/card/Card.java +++ b/forge-game/src/main/java/forge/game/card/Card.java @@ -154,7 +154,7 @@ public class Card extends GameEntity implements Comparable, 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, 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, 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, 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, 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, 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, IHasSVars { return numberGameActivations.containsKey(original) ? numberGameActivations.get(original) : 0; } - public void resetTurnActivations() { - numberTurnActivations.clear(); - numberTurnActivationsStatic.clear(); - } - public List getChosenModesTurn(SpellAbility ability) { SpellAbility original = null; SpellAbility root = ability.getRootAbility(); diff --git a/forge-game/src/main/java/forge/game/card/CardFactory.java b/forge-game/src/main/java/forge/game/card/CardFactory.java index 5dbfabc2740..dce30d6d5b7 100644 --- a/forge-game/src/main/java/forge/game/card/CardFactory.java +++ b/forge-game/src/main/java/forge/game/card/CardFactory.java @@ -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) diff --git a/forge-game/src/main/java/forge/game/card/CardFactoryUtil.java b/forge-game/src/main/java/forge/game/card/CardFactoryUtil.java index f6f4655d747..70da4cfd33d 100644 --- a/forge-game/src/main/java/forge/game/card/CardFactoryUtil.java +++ b/forge-game/src/main/java/forge/game/card/CardFactoryUtil.java @@ -66,7 +66,6 @@ import java.util.Map.Entry; import io.sentry.Sentry; import io.sentry.event.BreadcrumbBuilder; - /** *

* 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.. + 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_.. 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 hexproofkw = Sets.newHashSet(); final Set 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 diff --git a/forge-game/src/main/java/forge/game/card/CardProperty.java b/forge-game/src/main/java/forge/game/card/CardProperty.java index 57cc898e053..2da6fca758d 100644 --- a/forge-game/src/main/java/forge/game/card/CardProperty.java +++ b/forge-game/src/main/java/forge/game/card/CardProperty.java @@ -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; diff --git a/forge-game/src/main/java/forge/game/card/CardUtil.java b/forge-game/src/main/java/forge/game/card/CardUtil.java index ce15796ef90..e1726689621 100644 --- a/forge-game/src/main/java/forge/game/card/CardUtil.java +++ b/forge-game/src/main/java/forge/game/card/CardUtil.java @@ -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()); diff --git a/forge-game/src/main/java/forge/game/keyword/Keyword.java b/forge-game/src/main/java/forge/game/keyword/Keyword.java index b244e3c5ba8..96a11fb2ef4 100644 --- a/forge-game/src/main/java/forge/game/keyword/Keyword.java +++ b/forge-game/src/main/java/forge/game/keyword/Keyword.java @@ -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."), diff --git a/forge-game/src/main/java/forge/game/phase/PhaseHandler.java b/forge-game/src/main/java/forge/game/phase/PhaseHandler.java index bc2f165b584..782bce143af 100644 --- a/forge-game/src/main/java/forge/game/phase/PhaseHandler.java +++ b/forge-game/src/main/java/forge/game/phase/PhaseHandler.java @@ -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); } diff --git a/forge-game/src/main/java/forge/game/player/Player.java b/forge-game/src/main/java/forge/game/player/Player.java index 877158b6a3f..1ee63399db8 100644 --- a/forge-game/src/main/java/forge/game/player/Player.java +++ b/forge-game/src/main/java/forge/game/player/Player.java @@ -111,6 +111,7 @@ public class Player extends GameEntity implements Comparable { private int numDrawnThisDrawStep = 0; private int numDiscardedThisTurn = 0; private int numTokenCreatedThisTurn = 0; + private int numForetoldThisTurn = 0; private int numCardsInHandStartedThisTurnWith = 0; private final Map> notes = Maps.newHashMap(); @@ -1666,6 +1667,22 @@ public class Player extends GameEntity implements Comparable { numTokenCreatedThisTurn = 0; } + public final int getNumForetoldThisTurn() { + return numForetoldThisTurn; + } + + public final void addForetoldThisTurn() { + numForetoldThisTurn++; + final Map 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 { controlledBy.remove(timestamp); getView().updateMindSlaveMaster(this); - + if (event) { game.fireEvent(new GameEventPlayerControl(this, oldLobbyPlayer, oldController, getLobbyPlayer(), getController())); } diff --git a/forge-game/src/main/java/forge/game/spellability/AlternativeCost.java b/forge-game/src/main/java/forge/game/spellability/AlternativeCost.java index bb8029e89aa..7f23a7fdbc6 100644 --- a/forge-game/src/main/java/forge/game/spellability/AlternativeCost.java +++ b/forge-game/src/main/java/forge/game/spellability/AlternativeCost.java @@ -9,6 +9,7 @@ public enum AlternativeCost { Escape, Evoke, Flashback, + Foretold, Madness, Offering, Outlast, // ActivatedAbility diff --git a/forge-game/src/main/java/forge/game/spellability/SpellAbility.java b/forge-game/src/main/java/forge/game/spellability/SpellAbility.java index 344345b9dd5..bc63cb582f7 100644 --- a/forge-game/src/main/java/forge/game/spellability/SpellAbility.java +++ b/forge-game/src/main/java/forge/game/spellability/SpellAbility.java @@ -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; } diff --git a/forge-game/src/main/java/forge/game/spellability/SpellAbilityCondition.java b/forge-game/src/main/java/forge/game/spellability/SpellAbilityCondition.java index 687d58ffdad..416e3a05f65 100644 --- a/forge-game/src/main/java/forge/game/spellability/SpellAbilityCondition.java +++ b/forge-game/src/main/java/forge/game/spellability/SpellAbilityCondition.java @@ -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; diff --git a/forge-game/src/main/java/forge/game/spellability/SpellAbilityVariables.java b/forge-game/src/main/java/forge/game/spellability/SpellAbilityVariables.java index 69a4fd24d49..00120c5b815 100644 --- a/forge-game/src/main/java/forge/game/spellability/SpellAbilityVariables.java +++ b/forge-game/src/main/java/forge/game/spellability/SpellAbilityVariables.java @@ -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 diff --git a/forge-game/src/main/java/forge/game/trigger/TriggerForetell.java b/forge-game/src/main/java/forge/game/trigger/TriggerForetell.java new file mode 100644 index 00000000000..b051315ffff --- /dev/null +++ b/forge-game/src/main/java/forge/game/trigger/TriggerForetell.java @@ -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 . + */ +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 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 runParams) { + sa.setTriggeringObjectsFrom(runParams, AbilityKey.Player); + } + + /** {@inheritDoc} + * @param runParams*/ + @Override + public final boolean performTest(final Map 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; + } + +} diff --git a/forge-game/src/main/java/forge/game/trigger/TriggerType.java b/forge-game/src/main/java/forge/game/trigger/TriggerType.java index d31de582d13..bd3175bf094 100644 --- a/forge-game/src/main/java/forge/game/trigger/TriggerType.java +++ b/forge-game/src/main/java/forge/game/trigger/TriggerType.java @@ -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), diff --git a/forge-game/src/main/java/forge/game/zone/MagicStack.java b/forge-game/src/main/java/forge/game/zone/MagicStack.java index 213755d6d02..ea9308f0895 100644 --- a/forge-game/src/main/java/forge/game/zone/MagicStack.java +++ b/forge-game/src/main/java/forge/game/zone/MagicStack.java @@ -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= 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();