diff --git a/forge-ai/src/main/java/forge/ai/ability/ControlExchangeAi.java b/forge-ai/src/main/java/forge/ai/ability/ControlExchangeAi.java index f21b327b3fd..e93f30ff733 100644 --- a/forge-ai/src/main/java/forge/ai/ability/ControlExchangeAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/ControlExchangeAi.java @@ -81,7 +81,12 @@ public class ControlExchangeAi extends SpellAbilityAi { final TargetRestrictions tgt = sa.getTargetRestrictions(); - CardCollection list = CardLists.getValidCards(aiPlayer.getGame().getCardsIn(ZoneType.Battlefield), + // for TrigTwoTargets logic, only get the opponents' cards for the first target + CardCollectionView unfilteredList = "TrigTwoTargets".equals(sa.getParam("AILogic")) ? + aiPlayer.getOpponents().getCardsIn(ZoneType.Battlefield) : + aiPlayer.getGame().getCardsIn(ZoneType.Battlefield); + + CardCollection list = CardLists.getValidCards(unfilteredList, tgt.getValidTgts(), aiPlayer, sa.getHostCard(), sa); // only select the cards that can be targeted @@ -106,7 +111,51 @@ public class ControlExchangeAi extends SpellAbilityAi { // add best Target sa.getTargets().add(best); + + // second target needed (the AI's own worst) + if ("TrigTwoTargets".equals(sa.getParam("AILogic"))) { + return doTrigTwoTargetsLogic(aiPlayer, sa, best); + } + return true; } - + + private boolean doTrigTwoTargetsLogic(Player ai, SpellAbility sa, Card bestFirstTgt) { + final TargetRestrictions tgt = sa.getTargetRestrictions(); + final int creatureThreshold = 100; // TODO: make this configurable from the AI profile + final int nonCreatureThreshold = 2; + + CardCollection list = CardLists.getValidCards(ai.getCardsIn(ZoneType.Battlefield), + tgt.getValidTgts(), ai, sa.getHostCard(), sa); + + // only select the cards that can be targeted + list = CardLists.getTargetableCards(list, sa); + + if (list.isEmpty()) { + return false; + } + + Card aiWorst = ComputerUtilCard.getWorstAI(list); + if (aiWorst == null) { + return false; + } + + if (aiWorst != null && aiWorst != bestFirstTgt) { + if (bestFirstTgt.isCreature() && aiWorst.isCreature()) { + if ((ComputerUtilCard.evaluateCreature(bestFirstTgt) > ComputerUtilCard.evaluateCreature(aiWorst) + creatureThreshold) || sa.isMandatory()) { + sa.getTargets().add(aiWorst); + return true; + } + } else { + // TODO: compare non-creatures by CMC - can be improved, at least shouldn't give control of things like the Power Nine + if ((bestFirstTgt.getCMC() > aiWorst.getCMC() + nonCreatureThreshold) || sa.isMandatory()) { + sa.getTargets().add(aiWorst); + return true; + } + } + } + + sa.clearTargets(); + return false; + } } 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/Game.java b/forge-game/src/main/java/forge/game/Game.java index 1a4376377d6..58d844b2911 100644 --- a/forge-game/src/main/java/forge/game/Game.java +++ b/forge-game/src/main/java/forge/game/Game.java @@ -109,6 +109,7 @@ public class Game { private final Match match; private GameStage age = GameStage.BeforeMulligan; private GameOutcome outcome; + private Game maingame = null; private final GameView view; private final Tracker tracker = new Tracker(); @@ -219,7 +220,11 @@ public class Game { changeZoneLKIInfo.clear(); } - public Game(Iterable players0, GameRules rules0, Match match0) { /* no more zones to map here */ + public Game(Iterable players0, GameRules rules0, Match match0) { + this(players0, rules0, match0, -1); + } + + public Game(Iterable players0, GameRules rules0, Match match0, int startingLife) { /* no more zones to map here */ rules = rules0; match = match0; this.id = nextId(); @@ -243,7 +248,11 @@ public class Game { allPlayers.add(pl); ingamePlayers.add(pl); - pl.setStartingLife(psc.getStartingLife()); + if (startingLife != -1) { + pl.setStartingLife(startingLife); + } else { + pl.setStartingLife(psc.getStartingLife()); + } pl.setMaxHandSize(psc.getStartingHand()); pl.setStartingHandSize(psc.getStartingHand()); @@ -430,6 +439,14 @@ public class Game { return outcome; } + public final Game getMaingame() { + return maingame; + } + + public void setMaingame(final Game maingame0) { + maingame = maingame0; + } + public ReplacementHandler getReplacementHandler() { return replacementHandler; } @@ -452,12 +469,16 @@ public class Game { result.setTurnsPlayed(getPhaseHandler().getTurn()); outcome = result; - match.addGamePlayed(this); + if (maingame == null) { + match.addGamePlayed(this); + } view.updateGameOver(this); // The log shall listen to events and generate text internally - fireEvent(new GameEventGameOutcome(result, match.getOutcomes())); + if (maingame == null) { + fireEvent(new GameEventGameOutcome(result, match.getOutcomes())); + } } public Zone getZoneOf(final Card card) { @@ -492,6 +513,14 @@ public class Game { return cards; } + public CardCollectionView getCardsInOwnedBy(final Iterable zones, Player p) { + CardCollection cards = new CardCollection(); + for (final ZoneType z : zones) { + cards.addAll(getCardsIncludePhasingIn(z)); + } + return CardLists.filter(cards, CardPredicates.isOwner(p)); + } + public boolean isCardExiled(final Card c) { return getCardsIn(ZoneType.Exile).contains(c); } diff --git a/forge-game/src/main/java/forge/game/GameAction.java b/forge-game/src/main/java/forge/game/GameAction.java index 21bf17450b8..8bba12fde2f 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); @@ -518,6 +516,18 @@ public class GameAction { c = changeZone(zoneFrom, zoneTo, c, position, cause, params); + // Move card in maingame if take card from subgame + // 720.4a + if (zoneFrom != null && zoneFrom.is(ZoneType.Sideboard) && game.getMaingame() != null) { + Card maingameCard = c.getOwner().getMappingMaingameCard(c); + if (maingameCard != null) { + if (maingameCard.getZone().is(ZoneType.Stack)) { + game.getMaingame().getStack().remove(maingameCard); + } + game.getMaingame().getAction().moveTo(ZoneType.Subgame, maingameCard, null); + } + } + if (zoneFrom == null) { c.setCastFrom(null); c.setCastSA(null); 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/AbilityFactory.java b/forge-game/src/main/java/forge/game/ability/AbilityFactory.java index f45d73068dd..b64e55edf59 100644 --- a/forge-game/src/main/java/forge/game/ability/AbilityFactory.java +++ b/forge-game/src/main/java/forge/game/ability/AbilityFactory.java @@ -352,6 +352,9 @@ public final class AbilityFactory { if (mapParams.containsKey("TargetsWithSameCreatureType")) { abTgt.setWithSameCreatureType(true); } + if (mapParams.containsKey("TargetsWithSameCardType")) { + abTgt.setWithSameCardType(true); + } if (mapParams.containsKey("TargetsWithSameController")) { abTgt.setSameController(true); } diff --git a/forge-game/src/main/java/forge/game/ability/ApiType.java b/forge-game/src/main/java/forge/game/ability/ApiType.java index 365afb52c11..f21cb010fc6 100644 --- a/forge-game/src/main/java/forge/game/ability/ApiType.java +++ b/forge-game/src/main/java/forge/game/ability/ApiType.java @@ -153,6 +153,7 @@ public enum ApiType { SkipTurn (SkipTurnEffect.class), StoreSVar (StoreSVarEffect.class), StoreMap (StoreMapEffect.class), + Subgame (SubgameEffect.class), Surveil (SurveilEffect.class), SwitchBlock (SwitchBlockEffect.class), Tap (TapEffect.class), diff --git a/forge-game/src/main/java/forge/game/ability/SpellAbilityEffect.java b/forge-game/src/main/java/forge/game/ability/SpellAbilityEffect.java index 26278a445f5..2dfe7b90b0e 100644 --- a/forge-game/src/main/java/forge/game/ability/SpellAbilityEffect.java +++ b/forge-game/src/main/java/forge/game/ability/SpellAbilityEffect.java @@ -332,8 +332,7 @@ public abstract class SpellAbilityEffect { } } - protected static void addForgetOnMovedTrigger(final Card card, final String zone) { - String trig = "Mode$ ChangesZone | ValidCard$ Card.IsRemembered | Origin$ " + zone + " | Destination$ Any | TriggerZones$ Command | Static$ True"; + protected static SpellAbility getForgetSpellAbility(final Card card) { String forgetEffect = "DB$ Pump | ForgetObjects$ TriggeredCard"; String exileEffect = "DB$ ChangeZone | Defined$ Self | Origin$ Command | Destination$ Exile" + " | ConditionDefined$ Remembered | ConditionPresent$ Card | ConditionCompare$ EQ0"; @@ -341,11 +340,23 @@ public abstract class SpellAbilityEffect { SpellAbility saForget = AbilityFactory.getAbility(forgetEffect, card); AbilitySub saExile = (AbilitySub) AbilityFactory.getAbility(exileEffect, card); saForget.setSubAbility(saExile); + return saForget; + } + + protected static void addForgetOnMovedTrigger(final Card card, final String zone) { + String trig = "Mode$ ChangesZone | ValidCard$ Card.IsRemembered | Origin$ " + zone + " | ExcludedDestinations$ Stack | Destination$ Any | TriggerZones$ Command | Static$ True"; final Trigger parsedTrigger = TriggerHandler.parseTrigger(trig, card, true); - parsedTrigger.setOverridingAbility(saForget); - final Trigger addedTrigger = card.addTrigger(parsedTrigger); - addedTrigger.setIntrinsic(true); + parsedTrigger.setOverridingAbility(getForgetSpellAbility(card)); + card.addTrigger(parsedTrigger); + } + + protected static void addForgetOnCastTrigger(final Card card) { + String trig = "Mode$ SpellCast | ValidCard$ Card.IsRemembered | TriggerZones$ Command | Static$ True"; + + final Trigger parsedTrigger = TriggerHandler.parseTrigger(trig, card, true); + parsedTrigger.setOverridingAbility(getForgetSpellAbility(card)); + card.addTrigger(parsedTrigger); } protected static void addExileOnMovedTrigger(final Card card, final String zone) { @@ -368,35 +379,18 @@ public abstract class SpellAbilityEffect { protected static void addForgetOnPhasedInTrigger(final Card card) { String trig = "Mode$ PhaseIn | ValidCard$ Card.IsRemembered | TriggerZones$ Command | Static$ True"; - String forgetEffect = "DB$ Pump | ForgetObjects$ TriggeredCard"; - String exileEffect = "DB$ ChangeZone | Defined$ Self | Origin$ Command | Destination$ Exile" - + " | ConditionDefined$ Remembered | ConditionPresent$ Card | ConditionCompare$ EQ0"; - - SpellAbility saForget = AbilityFactory.getAbility(forgetEffect, card); - AbilitySub saExile = (AbilitySub) AbilityFactory.getAbility(exileEffect, card); - saForget.setSubAbility(saExile); final Trigger parsedTrigger = TriggerHandler.parseTrigger(trig, card, true); - parsedTrigger.setOverridingAbility(saForget); - final Trigger addedTrigger = card.addTrigger(parsedTrigger); - addedTrigger.setIntrinsic(true); + parsedTrigger.setOverridingAbility(getForgetSpellAbility(card)); + card.addTrigger(parsedTrigger); } protected static void addForgetCounterTrigger(final Card card, final String counterType) { String trig = "Mode$ CounterRemoved | TriggerZones$ Command | ValidCard$ Card.IsRemembered | CounterType$ " + counterType + " | NewCounterAmount$ 0 | Static$ True"; - String forgetEffect = "DB$ Pump | ForgetObjects$ TriggeredCard"; - String exileEffect = "DB$ ChangeZone | Defined$ Self | Origin$ Command | Destination$ Exile" - + " | ConditionDefined$ Remembered | ConditionPresent$ Card | ConditionCompare$ EQ0"; - - SpellAbility saForget = AbilityFactory.getAbility(forgetEffect, card); - AbilitySub saExile = (AbilitySub) AbilityFactory.getAbility(exileEffect, card); - saForget.setSubAbility(saExile); - final Trigger parsedTrigger = TriggerHandler.parseTrigger(trig, card, true); - parsedTrigger.setOverridingAbility(saForget); - final Trigger addedTrigger = card.addTrigger(parsedTrigger); - addedTrigger.setIntrinsic(true); + parsedTrigger.setOverridingAbility(getForgetSpellAbility(card)); + card.addTrigger(parsedTrigger); } protected static void addLeaveBattlefieldReplacement(final Card card, final SpellAbility sa, final String zone) { 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/ability/effects/ControlExchangeEffect.java b/forge-game/src/main/java/forge/game/ability/effects/ControlExchangeEffect.java index 0e2ea5a6ce6..7a2daa09be0 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/ControlExchangeEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/ControlExchangeEffect.java @@ -1,14 +1,16 @@ package forge.game.ability.effects; import com.google.common.collect.Lists; + +import forge.game.Game; import forge.game.ability.AbilityUtils; import forge.game.ability.SpellAbilityEffect; import forge.game.card.Card; import forge.game.player.Player; import forge.game.spellability.SpellAbility; -import forge.game.spellability.TargetRestrictions; +import forge.util.CardTranslation; +import forge.util.Localizer; -import java.util.ArrayList; import java.util.List; @@ -21,21 +23,27 @@ public class ControlExchangeEffect extends SpellAbilityEffect { protected String getStackDescription(SpellAbility sa) { Card object1 = null; Card object2 = null; - final TargetRestrictions tgt = sa.getTargetRestrictions(); - List tgts = tgt == null ? new ArrayList<>() : Lists.newArrayList(sa.getTargets().getTargetCards()); - if (tgts.size() > 0) { - object1 = tgts.get(0); + List tgts = null; + if (sa.usesTargeting()) { + tgts = Lists.newArrayList(sa.getTargets().getTargetCards()); + if (tgts.size() > 0) { + object1 = tgts.get(0); + } } if (sa.hasParam("Defined")) { List cards = AbilityUtils.getDefinedCards(sa.getHostCard(), sa.getParam("Defined"), sa); object2 = cards.isEmpty() ? null : cards.get(0); - if (cards.size() > 1 && sa.hasParam("BothDefined")) { + if (cards.size() > 1 && !sa.usesTargeting()) { object1 = cards.get(1); } } else if (tgts.size() > 1) { object2 = tgts.get(1); } + if (object1 == null || object2 == null) { + return ""; + } + return object1 + " exchanges controller with " + object2; } @@ -44,17 +52,22 @@ public class ControlExchangeEffect extends SpellAbilityEffect { */ @Override public void resolve(SpellAbility sa) { + Card host = sa.getHostCard(); + Game game = host.getGame(); Card object1 = null; Card object2 = null; - final TargetRestrictions tgt = sa.getTargetRestrictions(); - List tgts = tgt == null ? new ArrayList<>() : Lists.newArrayList(sa.getTargets().getTargetCards()); - if (tgts.size() > 0) { - object1 = tgts.get(0); + + List tgts = null; + if (sa.usesTargeting()) { + tgts = Lists.newArrayList(sa.getTargets().getTargetCards()); + if (tgts.size() > 0) { + object1 = tgts.get(0); + } } if (sa.hasParam("Defined")) { - final List cards = AbilityUtils.getDefinedCards(sa.getHostCard(), sa.getParam("Defined"), sa); + final List cards = AbilityUtils.getDefinedCards(host, sa.getParam("Defined"), sa); object2 = cards.isEmpty() ? null : cards.get(0); - if (cards.size() > 1 && sa.hasParam("BothDefined")) { + if (cards.size() > 1 && !sa.usesTargeting()) { object1 = cards.get(1); } } else if (tgts.size() > 1) { @@ -73,7 +86,16 @@ public class ControlExchangeEffect extends SpellAbilityEffect { return; } - final long tStamp = sa.getActivatingPlayer().getGame().getNextTimestamp(); + if (sa.hasParam("Optional")) { + if (!sa.getActivatingPlayer().getController().confirmAction(sa, null, + Localizer.getInstance().getMessage("lblExchangeControl", + CardTranslation.getTranslatedName(object1.getName()), + CardTranslation.getTranslatedName(object2.getName())))) { + return; + } + } + + final long tStamp = game.getNextTimestamp(); object2.setController(player1, tStamp); object1.setController(player2, tStamp); if (sa.hasParam("RememberExchanged")) { diff --git a/forge-game/src/main/java/forge/game/ability/effects/CopyPermanentEffect.java b/forge-game/src/main/java/forge/game/ability/effects/CopyPermanentEffect.java index c83bca2d61d..16979981f74 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/CopyPermanentEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/CopyPermanentEffect.java @@ -13,9 +13,11 @@ import forge.game.card.Card; import forge.game.card.CardCollectionView; import forge.game.card.CardFactory; import forge.game.card.CardLists; +import forge.game.card.CardPredicates; import forge.game.card.CardZoneTable; import forge.game.event.GameEventCombatChanged; import forge.game.player.Player; +import forge.game.player.PlayerActionConfirmMode; import forge.game.spellability.SpellAbility; import forge.game.zone.ZoneType; import forge.item.PaperCard; @@ -141,12 +143,25 @@ public class CopyPermanentEffect extends TokenEffectBase { CardCollectionView choices = game.getCardsIn(ZoneType.Battlefield); choices = CardLists.getValidCards(choices, sa.getParam("Choices"), activator, host); if (!choices.isEmpty()) { - String title = sa.hasParam("ChoiceTitle") ? sa.getParam("ChoiceTitle") : Localizer.getInstance().getMessage("lblChooseaCard") +" "; + String title = sa.hasParam("ChoiceTitle") ? sa.getParam("ChoiceTitle") : Localizer.getInstance().getMessage("lblChooseaCard"); - Card choosen = chooser.getController().chooseSingleEntityForEffect(choices, sa, title, false, null); + if (sa.hasParam("WithDifferentNames")) { + // any Number of choices with different names + while (!choices.isEmpty()) { + Card choosen = chooser.getController().chooseSingleEntityForEffect(choices, sa, title, true, null); - if (choosen != null) { - tgtCards.add(choosen); + if (choosen != null) { + tgtCards.add(choosen); + choices = CardLists.filter(choices, Predicates.not(CardPredicates.sharesNameWith(choosen))); + } else if (chooser.getController().confirmAction(sa, PlayerActionConfirmMode.OptionalChoose, Localizer.getInstance().getMessage("lblCancelChooseConfirm"))) { + break; + } + } + } else { + Card choosen = chooser.getController().chooseSingleEntityForEffect(choices, sa, title, false, null); + if (choosen != null) { + tgtCards.add(choosen); + } } } } else { @@ -208,3 +223,5 @@ public class CopyPermanentEffect extends TokenEffectBase { return copy; } } + + diff --git a/forge-game/src/main/java/forge/game/ability/effects/EffectEffect.java b/forge-game/src/main/java/forge/game/ability/effects/EffectEffect.java index 74b43ea292e..14e56102c42 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/EffectEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/EffectEffect.java @@ -215,6 +215,7 @@ public class EffectEffect extends SpellAbilityEffect { } if (sa.hasParam("ForgetOnMoved")) { addForgetOnMovedTrigger(eff, sa.getParam("ForgetOnMoved")); + addForgetOnCastTrigger(eff); } else if (sa.hasParam("ExileOnMoved")) { addExileOnMovedTrigger(eff, sa.getParam("ExileOnMoved")); } diff --git a/forge-game/src/main/java/forge/game/ability/effects/SubgameEffect.java b/forge-game/src/main/java/forge/game/ability/effects/SubgameEffect.java new file mode 100644 index 00000000000..486c43a2c26 --- /dev/null +++ b/forge-game/src/main/java/forge/game/ability/effects/SubgameEffect.java @@ -0,0 +1,254 @@ +package forge.game.ability.effects; + +import java.util.*; + +import com.google.common.collect.Lists; + +import forge.card.MagicColor; +import forge.game.Game; +import forge.game.GameOutcome; +import forge.game.ability.ApiType; +import forge.game.ability.SpellAbilityEffect; +import forge.game.card.Card; +import forge.game.card.CardCollectionView; +import forge.game.event.GameEventSubgameStart; +import forge.game.event.GameEventSubgameEnd; +import forge.game.player.Player; +import forge.game.player.PlayerController; +import forge.game.player.RegisteredPlayer; +import forge.game.spellability.SpellAbility; +import forge.game.zone.PlayerZone; +import forge.game.zone.ZoneType; +import forge.item.PaperCard; +import forge.util.CardTranslation; +import forge.util.Lang; +import forge.util.Localizer; +import forge.util.collect.FCollectionView; + +public class SubgameEffect extends SpellAbilityEffect { + + private final Game createSubGame(Game maingame, int startingLife) { + List players = Lists.newArrayList(); + + // Add remaining players to subgame + for (Player p : maingame.getPlayers()) { + players.add(p.getRegisteredPlayer()); + } + + return new Game(players, maingame.getRules(), maingame.getMatch(), startingLife); + } + + private final void setCardsInZone(Player player, final ZoneType zoneType, final CardCollectionView oldCards, boolean addMapping) { + PlayerZone zone = player.getZone(zoneType); + List newCards = Lists.newArrayList(); + for (final Card card : oldCards) { + if (card.isToken() || card.isCopiedSpell()) continue; + Card newCard = Card.fromPaperCard(card.getPaperCard(), player); + newCards.add(newCard); + if (addMapping) { + // Build mapping between maingame cards and subgame cards, + // so when subgame pick a card from maingame (like Wish effects), + // The maingame card will also be moved. + // (Will be move to Subgame zone, which will be added back to libary after subgame ends.) + player.addMaingameCardMapping(newCard, card); + } + } + zone.setCards(newCards); + } + + private final void initVariantsZonesSubgame(final Game subgame, final Player maingamePlayer, final Player player) { + PlayerZone com = player.getZone(ZoneType.Command); + RegisteredPlayer registeredPlayer = player.getRegisteredPlayer(); + + // Vanguard + if (registeredPlayer.getVanguardAvatars() != null) { + for(PaperCard avatar:registeredPlayer.getVanguardAvatars()) { + com.add(Card.fromPaperCard(avatar, player)); + } + } + + // Commander + List commanders = Lists.newArrayList(); + final CardCollectionView commandCards = maingamePlayer.getCardsIn(ZoneType.Command); + for (final Card card : commandCards) { + if (card.isCommander()) { + Card cmd = Card.fromPaperCard(card.getPaperCard(), player); + if (cmd.hasKeyword("If CARDNAME is your commander, choose a color before the game begins.")) { + List colorChoices = new ArrayList<>(MagicColor.Constant.ONLY_COLORS); + String prompt = Localizer.getInstance().getMessage("lblChooseAColorFor", cmd.getName()); + List chosenColors; + SpellAbility cmdColorsa = new SpellAbility.EmptySa(ApiType.ChooseColor, cmd, player); + chosenColors = player.getController().chooseColors(prompt,cmdColorsa, 1, 1, colorChoices); + cmd.setChosenColors(chosenColors); + subgame.getAction().nofityOfValue(cmdColorsa, cmd, Localizer.getInstance().getMessage("lblPlayerPickedChosen", player.getName(), Lang.joinHomogenous(chosenColors)), player); + } + cmd.setCommander(true); + com.add(cmd); + commanders.add(cmd); + com.add(Player.createCommanderEffect(subgame, cmd)); + } + } + if (!commanders.isEmpty()) { + player.setCommanders(commanders); + } + + // Conspiracies + // 720.2 doesn't mention Conspiracy cards so I guess they don't move + } + + private void prepareAllZonesSubgame(final Game maingame, final Game subgame) { + final FCollectionView players = subgame.getPlayers(); + final FCollectionView maingamePlayers = maingame.getPlayers(); + final List outsideZones = Arrays.asList(ZoneType.Hand, ZoneType.Battlefield, + ZoneType.Graveyard, ZoneType.Exile, ZoneType.Stack, ZoneType.Sideboard, ZoneType.Ante); + + for (int i = 0; i < players.size(); i++) { + final Player player = players.get(i); + final Player maingamePlayer = maingamePlayers.get(i); + + // Library + setCardsInZone(player, ZoneType.Library, maingamePlayer.getCardsIn(ZoneType.Library), false); + + // Sideboard + // 720.4 + final CardCollectionView outsideCards = maingame.getCardsInOwnedBy(outsideZones, maingamePlayer); + if (!outsideCards.isEmpty()) { + setCardsInZone(player, ZoneType.Sideboard, outsideCards, true); + + // Assign Companion + PlayerController person = player.getController(); + Card companion = player.assignCompanion(subgame, person); + // Create an effect that lets you cast your companion from your sideboard + if (companion != null) { + PlayerZone commandZone = player.getZone(ZoneType.Command); + companion = subgame.getAction().moveTo(ZoneType.Command, companion, null); + commandZone.add(Player.createCompanionEffect(subgame, companion)); + + player.updateZoneForView(commandZone); + } + } + + // Schemes + setCardsInZone(player, ZoneType.SchemeDeck, maingamePlayer.getCardsIn(ZoneType.SchemeDeck), false); + + // Planes + setCardsInZone(player, ZoneType.PlanarDeck, maingamePlayer.getCardsIn(ZoneType.PlanarDeck), false); + + // Vanguard and Commanders + initVariantsZonesSubgame(subgame, maingamePlayer, player); + + player.shuffle(null); + player.getZone(ZoneType.SchemeDeck).shuffle(); + player.getZone(ZoneType.PlanarDeck).shuffle(); + } + } + + @Override + public void resolve(SpellAbility sa) { + final Card hostCard = sa.getHostCard(); + final Game maingame = hostCard.getGame(); + + int startingLife = -1; + if (sa.hasParam("StartingLife")) { + startingLife = Integer.parseInt(sa.getParam("StartingLife")); + } + Game subgame = createSubGame(maingame, startingLife); + subgame.setMaingame(maingame); + + String startMessage = Localizer.getInstance().getMessage("lblSubgameStart", + CardTranslation.getTranslatedName(hostCard.getName())); + maingame.fireEvent(new GameEventSubgameStart(subgame, startMessage)); + + prepareAllZonesSubgame(maingame, subgame); + subgame.getAction().startGame(null, null); + subgame.clearCaches(); + + // Find out winners and losers + final GameOutcome outcome = subgame.getOutcome(); + List winPlayers = Lists.newArrayList(); + List notWinPlayers = Lists.newArrayList(); + StringBuilder sbWinners = new StringBuilder(); + StringBuilder sbLosers = new StringBuilder(); + for (Player p : maingame.getPlayers()) { + if (outcome.isWinner(p.getRegisteredPlayer())) { + if (!winPlayers.isEmpty()) { + sbWinners.append(", "); + } + sbWinners.append(p.getName()); + winPlayers.add(p); + } else { + if (!notWinPlayers.isEmpty()) { + sbLosers.append(", "); + } + sbLosers.append(p.getName()); + notWinPlayers.add(p); + } + } + + if (sa.hasParam("RememberPlayers")) { + final String param = sa.getParam("RememberPlayers"); + if (param.equals("Win")) { + for (Player p : winPlayers) { + hostCard.addRemembered(p); + } + } else if (param.equals("NotWin")) { + for (Player p : notWinPlayers) { + hostCard.addRemembered(p); + } + } + } + + + String endMessage = outcome.isDraw() ? Localizer.getInstance().getMessage("lblSubgameEndDraw") : + Localizer.getInstance().getMessage("lblSubgameEnd", sbWinners.toString(), sbLosers.toString()); + maingame.fireEvent(new GameEventSubgameEnd(maingame, endMessage)); + + // Setup maingame library + final FCollectionView subgamePlayers = subgame.getRegisteredPlayers(); + final FCollectionView players = maingame.getPlayers(); + for (int i = 0; i < players.size(); i++) { + final Player subgamePlayer = subgamePlayers.get(i); + final Player player = players.get(i); + + // All cards moved to Subgame Zone will be put into library when subgame ends. + // 720.5 + final CardCollectionView movedCards = player.getCardsIn(ZoneType.Subgame); + PlayerZone library = player.getZone(ZoneType.Library); + for (final Card card : movedCards) { + library.add(card); + } + player.getZone(ZoneType.Subgame).removeAllCards(true); + + // Move commander if it is no longer in subgame's commander zone + // 720.5c + List subgameCommanders = Lists.newArrayList(); + List movedCommanders = Lists.newArrayList(); + for (final Card card : subgamePlayer.getCardsIn(ZoneType.Command)) { + if (card.isCommander()) { + subgameCommanders.add(card); + } + } + for (final Card card : player.getCardsIn(ZoneType.Command)) { + if (card.isCommander()) { + boolean isInSubgameCommand = false; + for (final Card subCard : subgameCommanders) { + if (card.getName().equals(subCard.getName())) { + isInSubgameCommand = true; + } + } + if (!isInSubgameCommand) { + movedCommanders.add(card); + } + } + } + for (final Card card : movedCommanders) { + maingame.getAction().moveTo(ZoneType.Library, card, null); + } + + player.shuffle(sa); + player.getZone(ZoneType.SchemeDeck).shuffle(); + player.getZone(ZoneType.PlanarDeck).shuffle(); + } + } + +} 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/cost/CostRemoveCounter.java b/forge-game/src/main/java/forge/game/cost/CostRemoveCounter.java index 21ed56b4b61..9b012c916a6 100644 --- a/forge-game/src/main/java/forge/game/cost/CostRemoveCounter.java +++ b/forge-game/src/main/java/forge/game/cost/CostRemoveCounter.java @@ -19,7 +19,6 @@ package forge.game.cost; import com.google.common.collect.Lists; -import forge.game.ability.AbilityUtils; import forge.game.card.Card; import forge.game.card.CardLists; import forge.game.card.CounterEnumType; diff --git a/forge-game/src/main/java/forge/game/event/GameEventSubgameEnd.java b/forge-game/src/main/java/forge/game/event/GameEventSubgameEnd.java new file mode 100644 index 00000000000..5fcaf2a9256 --- /dev/null +++ b/forge-game/src/main/java/forge/game/event/GameEventSubgameEnd.java @@ -0,0 +1,18 @@ +package forge.game.event; + +import forge.game.Game; + +public class GameEventSubgameEnd extends GameEvent { + public final Game maingame; + public final String message; + + public GameEventSubgameEnd(Game game, String message0) { + maingame = game; + message = message0; + } + + @Override + public T visit(IGameEventVisitor visitor) { + return visitor.visit(this); + } +} diff --git a/forge-game/src/main/java/forge/game/event/GameEventSubgameStart.java b/forge-game/src/main/java/forge/game/event/GameEventSubgameStart.java new file mode 100644 index 00000000000..8d9f733a52b --- /dev/null +++ b/forge-game/src/main/java/forge/game/event/GameEventSubgameStart.java @@ -0,0 +1,18 @@ +package forge.game.event; + +import forge.game.Game; + +public class GameEventSubgameStart extends GameEvent { + public final Game subgame; + public final String message; + + public GameEventSubgameStart(Game subgame0, String message0) { + subgame = subgame0; + message = message0; + } + + @Override + public T visit(IGameEventVisitor visitor) { + return visitor.visit(this); + } +} diff --git a/forge-game/src/main/java/forge/game/event/IGameEventVisitor.java b/forge-game/src/main/java/forge/game/event/IGameEventVisitor.java index 2ded38b9cc4..0041e7545fc 100644 --- a/forge-game/src/main/java/forge/game/event/IGameEventVisitor.java +++ b/forge-game/src/main/java/forge/game/event/IGameEventVisitor.java @@ -44,6 +44,8 @@ public interface IGameEventVisitor { T visit(GameEventSpellAbilityCast gameEventSpellAbilityCast); T visit(GameEventSpellResolved event); T visit(GameEventSpellRemovedFromStack event); + T visit(GameEventSubgameStart event); + T visit(GameEventSubgameEnd event); T visit(GameEventSurveil event); T visit(GameEventTokenCreated event); T visit(GameEventTurnBegan gameEventTurnBegan); @@ -92,6 +94,8 @@ public interface IGameEventVisitor { public T visit(GameEventSpellResolved event) { return null; } public T visit(GameEventSpellAbilityCast event) { return null; } public T visit(GameEventSpellRemovedFromStack event) { return null; } + public T visit(GameEventSubgameStart event) { return null; } + public T visit(GameEventSubgameEnd event) { return null; } public T visit(GameEventSurveil event) { return null; } public T visit(GameEventTokenCreated event) { return null; } public T visit(GameEventTurnBegan event) { return null; } 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..9c0995b0496 100644 --- a/forge-game/src/main/java/forge/game/player/Player.java +++ b/forge-game/src/main/java/forge/game/player/Player.java @@ -77,7 +77,7 @@ import java.util.concurrent.ConcurrentSkipListMap; public class Player extends GameEntity implements Comparable { public static final List ALL_ZONES = Collections.unmodifiableList(Arrays.asList(ZoneType.Battlefield, ZoneType.Library, ZoneType.Graveyard, ZoneType.Hand, ZoneType.Exile, ZoneType.Command, ZoneType.Ante, - ZoneType.Sideboard, ZoneType.PlanarDeck, ZoneType.SchemeDeck)); + ZoneType.Sideboard, ZoneType.PlanarDeck, ZoneType.SchemeDeck, ZoneType.Subgame)); private final Map commanderDamage = Maps.newHashMap(); @@ -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(); @@ -140,6 +141,7 @@ public class Player extends GameEntity implements Comparable { private final Map zones = Maps.newEnumMap(ZoneType.class); private final Map adjustLandPlays = Maps.newHashMap(); private final Set adjustLandPlaysInfinite = Sets.newHashSet(); + private Map maingameCardsMap = Maps.newHashMap();; private CardCollection currentPlanes = new CardCollection(); private Set prowl = Sets.newHashSet(); @@ -1666,6 +1668,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; } @@ -1905,6 +1923,14 @@ public class Player extends GameEntity implements Comparable { return !adjustLandPlaysInfinite.isEmpty(); } + public final void addMaingameCardMapping(Card subgameCard, Card maingameCard) { + maingameCardsMap.put(subgameCard, maingameCard); + } + + public final Card getMappingMaingameCard(Card subgameCard) { + return maingameCardsMap.get(subgameCard); + } + public final ManaPool getManaPool() { return manaPool; } @@ -2581,7 +2607,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/replacement/ReplaceAddCounter.java b/forge-game/src/main/java/forge/game/replacement/ReplaceAddCounter.java index 27a388c2160..55cd031279b 100644 --- a/forge-game/src/main/java/forge/game/replacement/ReplaceAddCounter.java +++ b/forge-game/src/main/java/forge/game/replacement/ReplaceAddCounter.java @@ -45,7 +45,7 @@ public class ReplaceAddCounter extends ReplacementEffect { if (!(o instanceof Card)) { return false; } - if (!matchesValid(o, getParam("ValidCard").split(","), this.getHostCard())) { + if (!matchesValid(o, getParam("ValidCard").split(","), getHostCard())) { return false; } } else if (hasParam("ValidPlayer")) { @@ -53,7 +53,17 @@ public class ReplaceAddCounter extends ReplacementEffect { if (!(o instanceof Player)) { return false; } - if (!matchesValid(o, getParam("ValidPlayer").split(","), this.getHostCard())) { + if (!matchesValid(o, getParam("ValidPlayer").split(","), getHostCard())) { + return false; + } + } else if (hasParam("ValidObject")) { + if (!matchesValid(runParams.get(AbilityKey.Affected), getParam("ValidObject").split(","), getHostCard())) { + return false; + } + } + + if (hasParam("ValidSource")) { + if (!matchesValid(runParams.get(AbilityKey.Source), getParam("ValidSource").split(","), getHostCard())) { return false; } } @@ -81,6 +91,7 @@ public class ReplaceAddCounter extends ReplacementEffect { } else if (o instanceof Player) { sa.setReplacingObject(AbilityKey.Player, o); } + sa.setReplacingObject(AbilityKey.Object, o); } } 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..23d12ed06c5 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 */ @@ -1048,29 +1056,26 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit final Card c = (Card) entity; CardCollection pl = AbilityUtils.getDefinedCards(getHostCard(), getParam("TargetsWithSharedCardType"), this); for (final Card crd : pl) { - if (!c.sharesCardTypeWith(crd)) { - return false; + // one of those types + if (hasParam("TargetsWithSharedTypes")) { + boolean flag = false; + for (final String type : getParam("TargetsWithSharedTypes").split(",")) { + if (c.getType().hasStringType(type) && crd.getType().hasStringType(type)) { + flag = true; + break; + } + } + if (!flag) { + return false; + } + } else { + if (!c.sharesCardTypeWith(crd)) { + return false; + } } } } - if (hasParam("TargetsWithSharedTypes") && entity instanceof Card) { - final Card c = (Card) entity; - final SpellAbility parent = getParentTargetingCard(); - final Card parentTargeted = parent != null ? parent.getTargetCard() : null; - if (parentTargeted == null) { - return false; - } - boolean flag = false; - for (final String type : getParam("TargetsWithSharedTypes").split(",")) { - if (c.getType().hasStringType(type) && parentTargeted.getType().hasStringType(type)) { - flag = true; - break; - } - } - if (!flag) { - return false; - } - } + if (hasParam("TargetsWithControllerProperty") && entity instanceof Card) { final String prop = getParam("TargetsWithControllerProperty"); final Card c = (Card) entity; @@ -1164,6 +1169,16 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit } } + if (tr.isWithSameCardType()) { + if (entity instanceof Card) { + for (final Card c : targetChosen.getTargetCards()) { + if (entity != c && !c.sharesCardTypeWith((Card) entity)) { + return false; + } + } + } + } + String[] validTgt = tr.getValidTgts(); if (entity instanceof GameEntity) { GameEntity e = (GameEntity)entity; @@ -1780,6 +1795,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/spellability/TargetRestrictions.java b/forge-game/src/main/java/forge/game/spellability/TargetRestrictions.java index d917a4058f9..eef0d88b541 100644 --- a/forge-game/src/main/java/forge/game/spellability/TargetRestrictions.java +++ b/forge-game/src/main/java/forge/game/spellability/TargetRestrictions.java @@ -65,6 +65,7 @@ public class TargetRestrictions { private boolean sameController = false; private boolean withoutSameCreatureType = false; private boolean withSameCreatureType = false; + private boolean withSameCardType = false; private boolean singleTarget = false; private boolean randomTarget = false; @@ -108,6 +109,7 @@ public class TargetRestrictions { this.sameController = target.isSameController(); this.withoutSameCreatureType = target.isWithoutSameCreatureType(); this.withSameCreatureType = target.isWithSameCreatureType(); + this.withSameCardType = target.isWithSameCardType(); this.singleTarget = target.isSingleTarget(); this.randomTarget = target.isRandomTarget(); } @@ -622,6 +624,20 @@ public class TargetRestrictions { this.withSameCreatureType = b; } + /** + * @return the withSameCardType + */ + public boolean isWithSameCardType() { + return withSameCardType; + } + + /** + * @param b the withSameCardType to set + */ + public void setWithSameCardType(boolean b) { + this.withSameCardType = b; + } + /** *

* copy. 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 STATIC_ABILITIES_SOURCE_ZONES = Arrays.asList(Battlefield, Graveyard, Exile, Command/*, Hand*/); diff --git a/forge-gui-desktop/src/test/java/forge/ai/simulation/GameSimulatorTest.java b/forge-gui-desktop/src/test/java/forge/ai/simulation/GameSimulatorTest.java index 208edb3ad7f..4344f303dea 100644 --- a/forge-gui-desktop/src/test/java/forge/ai/simulation/GameSimulatorTest.java +++ b/forge-gui-desktop/src/test/java/forge/ai/simulation/GameSimulatorTest.java @@ -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)); diff --git a/forge-gui-mobile/src/forge/Forge.java b/forge-gui-mobile/src/forge/Forge.java index e667a157c97..2b46062fd46 100644 --- a/forge-gui-mobile/src/forge/Forge.java +++ b/forge-gui-mobile/src/forge/Forge.java @@ -202,6 +202,12 @@ public class Forge implements ApplicationListener { ImageCache.preloadCache(filteredkeys); } + public static void openHomeScreen(boolean openNewGameMenu) { + openScreen(HomeScreen.instance); + if(openNewGameMenu) + HomeScreen.instance.openNewGamMenu(); + } + private void afterDbLoaded() { stopContinuousRendering(); //save power consumption by disabling continuous rendering once assets loaded @@ -210,7 +216,7 @@ public class Forge implements ApplicationListener { SoundSystem.instance.setBackgroundMusic(MusicPlaylist.MENUS); //start background music destroyThis = false; //Allow back() Gdx.input.setCatchKey(Keys.MENU, true); - openScreen(HomeScreen.instance); + openHomeScreen(false); splashScreen = null; boolean isLandscapeMode = isLandscapeMode(); diff --git a/forge-gui-mobile/src/forge/screens/home/HomeScreen.java b/forge-gui-mobile/src/forge/screens/home/HomeScreen.java index d24656c29eb..1ba7a46b3ff 100644 --- a/forge-gui-mobile/src/forge/screens/home/HomeScreen.java +++ b/forge-gui-mobile/src/forge/screens/home/HomeScreen.java @@ -135,6 +135,10 @@ public class HomeScreen extends FScreen { QuestWorld = questWorld; } + public void openNewGamMenu(){ + NewGameMenu.getPreferredScreen().open(); + } + public boolean getQuestCommanderMode() { return QuestCommander; } diff --git a/forge-gui-mobile/src/forge/screens/match/winlose/ControlWinLose.java b/forge-gui-mobile/src/forge/screens/match/winlose/ControlWinLose.java index 9efa454a3d5..c05f8d4cd5a 100644 --- a/forge-gui-mobile/src/forge/screens/match/winlose/ControlWinLose.java +++ b/forge-gui-mobile/src/forge/screens/match/winlose/ControlWinLose.java @@ -77,14 +77,22 @@ public class ControlWinLose { /** Action performed when "quit" button is pressed in default win/lose UI. */ public void actionOnQuit() { + boolean openHomeScreen = false; // Reset other stuff saveOptions(); - try { MatchController.getHostedMatch().endCurrentGame(); + try { + if(MatchController.getHostedMatch().subGameCount > 0) { + openHomeScreen = true; + MatchController.getHostedMatch().subGameCount--; + } + MatchController.getHostedMatch().endCurrentGame(); } catch (NullPointerException e) {} view.hide(); if(humancount == 0) { Forge.back(); } + if (openHomeScreen) + Forge.openHomeScreen(true); } /** diff --git a/forge-gui/res/cardsfolder/c/cliffside_market.txt b/forge-gui/res/cardsfolder/c/cliffside_market.txt index 00db712d6b2..c6a038093a8 100644 --- a/forge-gui/res/cardsfolder/c/cliffside_market.txt +++ b/forge-gui/res/cardsfolder/c/cliffside_market.txt @@ -5,9 +5,7 @@ T:Mode$ PlaneswalkedTo | ValidCard$ Card.Self | Execute$ TrigLife | OptionalDeci T:Mode$ Phase | Phase$ Upkeep | ValidPlayer$ You | Execute$ TrigLife | TriggerZones$ Command | Secondary$ True | OptionalDecider$ You | TriggerDescription$ When you planeswalk to CARDNAME or at the beginning of your upkeep, you may exchange life totals with target player. SVar:TrigLife:DB$ ExchangeLife | Optional$ True | ValidTgts$ Player | TgtPrompt$ Select target player to exchange life totals with T:Mode$ PlanarDice | Result$ Chaos | TriggerZones$ Command | Execute$ RolledChaos | TriggerDescription$ Whenever you roll {CHAOS}, exchange control of two target permanents that share a card type. -SVar:RolledChaos:DB$ Pump | ValidTgts$ Permanent | TgtPrompt$ Select target permanent | SubAbility$ DBExchange -SVar:DBExchange:DB$ ExchangeControl | Defined$ ParentTarget | ValidTgts$ Permanent | TgtPrompt$ Select target permanent card | TargetsWithSharedTypes$ Creature,Artifact,Enchantment,Planeswalker,Land,Tribal | TargetUnique$ True +SVar:RolledChaos:DB$ ExchangeControl | TargetMin$ 2 | TargetMax$ 2 | ValidTgts$ Permanent | TgtPrompt$ Select target permanents that share a permanent type | TargetsWithSameCardType$ True AI:RemoveDeck:All AI:RemoveDeck:Random -SVar:Picture:http://www.wizards.com/global/images/magic/general/cliffside_market.jpg Oracle:When you planeswalk to Cliffside Market or at the beginning of your upkeep, you may exchange life totals with target player.\nWhenever you roll {CHAOS}, exchange control of two target permanents that share a card type. \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/c/confusion_in_the_ranks.txt b/forge-gui/res/cardsfolder/c/confusion_in_the_ranks.txt index e6f94222468..7a025cdcf7c 100644 --- a/forge-gui/res/cardsfolder/c/confusion_in_the_ranks.txt +++ b/forge-gui/res/cardsfolder/c/confusion_in_the_ranks.txt @@ -4,5 +4,4 @@ Types:Enchantment T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Artifact,Creature,Enchantment | TriggerZones$ Battlefield | Execute$ TrigExchangeControl | TriggerDescription$ Whenever an artifact, creature, or enchantment enters the battlefield, its controller chooses target permanent another player controls that shares a card type with it. Exchange control of those permanents. SVar:TrigExchangeControl:DB$ ExchangeControl | Defined$ TriggeredCard | TargetingPlayer$ TriggeredCardController | TargetsWithDefinedController$ NonTriggeredCardController | ValidTgts$ Permanent | TargetsWithSharedCardType$ TriggeredCard AI:RemoveDeck:Random -SVar:Picture:http://www.wizards.com/global/images/magic/general/confusion_in_the_ranks.jpg Oracle:Whenever an artifact, creature, or enchantment enters the battlefield, its controller chooses target permanent another player controls that shares a card type with it. Exchange control of those permanents. diff --git a/forge-gui/res/cardsfolder/d/daring_thief.txt b/forge-gui/res/cardsfolder/d/daring_thief.txt index 875fae5c210..b76e71babdd 100644 --- a/forge-gui/res/cardsfolder/d/daring_thief.txt +++ b/forge-gui/res/cardsfolder/d/daring_thief.txt @@ -4,8 +4,7 @@ Types:Creature Human Rogue PT:2/3 T:Mode$ Untaps | ValidCard$ Card.Self | TriggerZones$ Battlefield | Execute$ TrigExchangeControl | OptionalDecider$ You | TriggerDescription$ Inspired — Whenever CARDNAME becomes untapped, you may exchange control of target nonland permanent you control and target permanent an opponent controls that shares a card type with it. SVar:TrigExchangeControl:DB$ Pump | ValidTgts$ Permanent.YouCtrl+nonLand | TgtPrompt$ Select target nonland permanent you control | SubAbility$ DBExchange -SVar:DBExchange:DB$ ExchangeControl | Defined$ ParentTarget | ValidTgts$ Permanent.OppCtrl | TgtPrompt$ Select target permanent an opponent controls that shares a card type with it | TargetsWithSharedTypes$ Creature,Artifact,Enchantment,Planeswalker,Tribal +SVar:DBExchange:DB$ ExchangeControl | Defined$ ParentTarget | ValidTgts$ Permanent.OppCtrl | TgtPrompt$ Select target permanent an opponent controls that shares a card type with it | TargetsWithSharedCardType$ ParentTarget AI:RemoveDeck:All AI:RemoveDeck:Random -SVar:Picture:http://www.wizards.com/global/images/magic/general/daring_thief.jpg Oracle:Inspired — Whenever Daring Thief becomes untapped, you may exchange control of target nonland permanent you control and target permanent an opponent controls that shares a card type with it. diff --git a/forge-gui/res/cardsfolder/g/gauntlets_of_chaos.txt b/forge-gui/res/cardsfolder/g/gauntlets_of_chaos.txt index 6d5d5f43151..991a6868a74 100644 --- a/forge-gui/res/cardsfolder/g/gauntlets_of_chaos.txt +++ b/forge-gui/res/cardsfolder/g/gauntlets_of_chaos.txt @@ -2,9 +2,8 @@ Name:Gauntlets of Chaos ManaCost:5 Types:Artifact A:AB$ Pump | Cost$ 5 Sac<1/CARDNAME> | ValidTgts$ Artifact.YouCtrl,Creature.YouCtrl,Land.YouCtrl | TgtPrompt$ target artifact, creature, or land you control | StackDescription$ None | SubAbility$ DBExchange | SpellDescription$ Exchange control of target artifact, creature, or land you control and target permanent an opponent controls that shares one of those types with it. If those permanents are exchanged this way, destroy all Auras attached to them. -SVar:DBExchange:DB$ ExchangeControl | Defined$ ParentTarget | ValidTgts$ Permanent.OppCtrl | TgtPrompt$ Select target permanent an opponent controls that shares one of those types | TargetsWithSharedTypes$ Artifact,Creature,Land | RememberExchanged$ True | SubAbility$ DBDestroyAll +SVar:DBExchange:DB$ ExchangeControl | Defined$ ParentTarget | ValidTgts$ Permanent.OppCtrl | TgtPrompt$ Select target permanent an opponent controls that shares one of those types | TargetsWithSharedCardType$ ParentTarget | TargetsWithSharedTypes$ Artifact,Creature,Land | RememberExchanged$ True | SubAbility$ DBDestroyAll SVar:DBDestroyAll:DB$ DestroyAll | ValidCards$ Aura.AttachedTo Card.IsRemembered | SubAbility$ DBCleanup SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True AI:RemoveDeck:All -SVar:Picture:http://www.wizards.com/global/images/magic/general/gauntlets_of_chaos.jpg Oracle:{5}, Sacrifice Gauntlets of Chaos: Exchange control of target artifact, creature, or land you control and target permanent an opponent controls that shares one of those types with it. If those permanents are exchanged this way, destroy all Auras attached to them. diff --git a/forge-gui/res/cardsfolder/g/gonti_lord_of_luxury.txt b/forge-gui/res/cardsfolder/g/gonti_lord_of_luxury.txt index ea780b4b4f6..59d59eba74e 100644 --- a/forge-gui/res/cardsfolder/g/gonti_lord_of_luxury.txt +++ b/forge-gui/res/cardsfolder/g/gonti_lord_of_luxury.txt @@ -5,12 +5,9 @@ PT:2/3 K:Deathtouch T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigDig | TriggerDescription$ When CARDNAME enters the battlefield, look at the top four cards of target opponent's library, exile one of them face down, then put the rest on the bottom of that library in a random order. For as long as that card remains exiled, you may look at it, you may cast it, and you may spend mana as though it were mana of any type to cast that spell. SVar:TrigDig:DB$Dig | ValidTgts$ Opponent | DigNum$ 4 | ChangeNum$ 1 | DestinationZone$ Exile | DestinationZone2$ Library | LibraryPosition$ -1 | RestRandomOrder$ True | ExileFaceDown$ True | ChangeValid$ Card | RememberChanged$ True | SubAbility$ DBEffect | RememberChanged$ True -SVar:DBEffect:DB$ Effect | RememberObjects$ Remembered | StaticAbilities$ STPlay1,STPlay2 | Duration$ Permanent | Triggers$ TrigCleanup | SVars$ DBExileSelf | SubAbility$ DBCleanup +SVar:DBEffect:DB$ Effect | RememberObjects$ Remembered | StaticAbilities$ STPlay1,STPlay2 | Duration$ Permanent | ForgetOnMoved$ Exile | SubAbility$ DBCleanup SVar:STPlay1:Mode$ Continuous | MayLookAt$ You | EffectZone$ Command | Affected$ Card.IsRemembered | AffectedZone$ Exile | Description$ You may look at the card, you may cast it, and you may spend mana as though it were mana of any type to cast that spell. SVar:STPlay2:Mode$ Continuous | MayPlay$ True | MayPlayIgnoreType$ True | EffectZone$ Command | Affected$ Card.IsRemembered+nonLand | AffectedZone$ Exile | Secondary$ True | Description$ You may look at the card, you may cast it, and you may spend mana as though it were mana of any type to cast that spell. -SVar:TrigCleanup:Mode$ ChangesZone | ValidCard$ Card.IsRemembered | Origin$ Exile | Destination$ Any | TriggerZones$ Command | Execute$ DBExileSelf | Static$ True -SVar:DBExileSelf:DB$ ChangeZone | Defined$ Self | Origin$ Command | Destination$ Exile -SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True +SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True SVar:PlayMain1:TRUE -SVar:Picture:http://www.wizards.com/global/images/magic/general/gonti_lord_of_luxury.jpg Oracle:Deathtouch\nWhen Gonti, Lord of Luxury enters the battlefield, look at the top four cards of target opponent's library, exile one of them face down, then put the rest on the bottom of that library in a random order. You may look at and cast that card for as long as it remains exiled, and you may spend mana as though it were mana of any type to cast that spell. diff --git a/forge-gui/res/cardsfolder/g/gusthas_scepter.txt b/forge-gui/res/cardsfolder/g/gusthas_scepter.txt index 77f2badf6a4..d76b9217648 100644 --- a/forge-gui/res/cardsfolder/g/gusthas_scepter.txt +++ b/forge-gui/res/cardsfolder/g/gusthas_scepter.txt @@ -2,10 +2,8 @@ Name:Gustha's Scepter ManaCost:0 Types:Artifact A:AB$ ChangeZone | Cost$ T | ChangeType$ Card | ChangeNum$ 1 | Origin$ Hand | Destination$ Exile | ExileFaceDown$ True | RememberChanged$ True | Mandatory$ True | SubAbility$ DBEffect | SpellDescription$ Exile a card from your hand face down. You may look at it for as long as it remains exiled. -SVar:DBEffect:DB$ Effect | RememberObjects$ Remembered | StaticAbilities$ STLook | Duration$ Permanent | Triggers$ TrigCleanup | SVars$ DBExileSelf | SubAbility$ DBCleanup +SVar:DBEffect:DB$ Effect | RememberObjects$ Remembered | StaticAbilities$ STLook | Duration$ Permanent | ForgetOnMoved$ Exile | SubAbility$ DBCleanup SVar:STLook:Mode$ Continuous | MayLookAt$ You | EffectZone$ Command | Affected$ Card.IsRemembered | AffectedZone$ Exile | Description$ You may look at it for as long as it remains exiled. -SVar:TrigCleanup:Mode$ ChangesZone | ValidCard$ Card.IsRemembered | Origin$ Exile | Destination$ Any | TriggerZones$ Command | Execute$ DBExileSelf | Static$ True -SVar:DBExileSelf:DB$ ChangeZone | Defined$ Self | Origin$ Command | Destination$ Exile A:AB$ ChooseCard | Cost$ T | Defined$ You | Amount$ 1 | Mandatory$ True | AILogic$ AtLeast1 | ChoiceTitle$ Choose a card you own to put into your hand | Choices$ Card.IsRemembered+YouOwn+ExiledWithSource | ChoiceZone$ Exile | SubAbility$ MoveChosen | SpellDescription$ Return a card you own exiled with CARDNAME to your hand. SVar:MoveChosen:DB$ ChangeZone | Origin$ Exile | Destination$ Hand | Defined$ ChosenCard T:Mode$ ChangesZone | Origin$ Exile | Destination$ Any | Static$ True | ValidCard$ Card.IsRemembered+ExiledWithSource | Execute$ DBForget @@ -15,5 +13,4 @@ T:Mode$ ChangesController | ValidCard$ Card.Self | TriggerZones$ Battlefield | E SVar:DBChangeZoneAll:DB$ ChangeZoneAll | ChangeType$ Card.IsRemembered | Origin$ Exile | Destination$ Graveyard | SubAbility$ DBCleanup SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True AI:RemoveDeck:All -SVar:Picture:http://www.wizards.com/global/images/magic/general/gusthas_scepter.jpg Oracle:{T}: Exile a card from your hand face down. You may look at it for as long as it remains exiled.\n{T}: Return a card you own exiled with Gustha's Scepter to your hand.\nWhen you lose control of Gustha's Scepter, put all cards exiled with Gustha's Scepter into their owner's graveyard. diff --git a/forge-gui/res/cardsfolder/j/juxtapose.txt b/forge-gui/res/cardsfolder/j/juxtapose.txt index 07faf62f196..c71022b24c0 100644 --- a/forge-gui/res/cardsfolder/j/juxtapose.txt +++ b/forge-gui/res/cardsfolder/j/juxtapose.txt @@ -3,16 +3,15 @@ ManaCost:3 U Types:Sorcery A:SP$ ChooseCard | Cost$ 3 U | ValidTgts$ Player | Choices$ Creature.cmcEQY | TargetControls$ True | References$ Y | Mandatory$ True | AILogic$ WorstCard | RememberChosen$ True | SubAbility$ DBChooseCreatureYou | SpellDescription$ You and target player exchange control of the creature you each control with the highest converted mana cost. Then exchange control of artifacts the same way. If two or more permanents a player controls are tied for highest cost, their controller chooses one of them. SVar:DBChooseCreatureYou:DB$ ChooseCard | Choices$ Creature.YouCtrl+cmcEQX | References$ X | Mandatory$ True | RememberChosen$ True | SubAbility$ DBExchangeCreature -SVar:DBExchangeCreature:DB$ ExchangeControl | BothDefined$ True | Defined$ Remembered | SubAbility$ DBCleanCreature +SVar:DBExchangeCreature:DB$ ExchangeControl | Defined$ Remembered | SubAbility$ DBCleanCreature SVar:DBCleanCreature:DB$ Cleanup | ClearRemembered$ True | SubAbility$ DBChooseArtifactYou SVar:DBChooseArtifactYou:DB$ ChooseCard | Choices$ Artifact.YouCtrl+cmcEQZ | References$ Z | Mandatory$ True | RememberChosen$ True | SubAbility$ DBChooseArtifactOpp SVar:DBChooseArtifactOpp:DB$ ChooseCard | Defined$ ParentTarget | Choices$ Artifact.cmcEQW | TargetControls$ True | References$ W | Mandatory$ True | AILogic$ WorstCard | RememberChosen$ True | SubAbility$ DBExchangeArtifact -SVar:DBExchangeArtifact:DB$ ExchangeControl | BothDefined$ True | Defined$ Remembered | SubAbility$ DBCleanup +SVar:DBExchangeArtifact:DB$ ExchangeControl | Defined$ Remembered | SubAbility$ DBCleanup SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True SVar:X:Count$HighestCMC_Creature.YouCtrl+inZoneBattlefield SVar:Y:Count$HighestCMC_Creature.TargetedPlayerCtrl+inZoneBattlefield SVar:Z:Count$HighestCMC_Artifact.YouCtrl+inZoneBattlefield SVar:W:Count$HighestCMC_Artifact.TargetedPlayerCtrl+inZoneBattlefield AI:RemoveDeck:All -SVar:Picture:http://www.wizards.com/global/images/magic/general/juxtapose.jpg Oracle:You and target player exchange control of the creature you each control with the highest converted mana cost. Then exchange control of artifacts the same way. If two or more permanents a player controls are tied for highest cost, their controller chooses one of them. diff --git a/forge-gui/res/cardsfolder/k/karona_false_god_avatar.txt b/forge-gui/res/cardsfolder/k/karona_false_god_avatar.txt index 799a9dbe070..5bb48983663 100644 --- a/forge-gui/res/cardsfolder/k/karona_false_god_avatar.txt +++ b/forge-gui/res/cardsfolder/k/karona_false_god_avatar.txt @@ -5,7 +5,7 @@ HandLifeModifier:-1/+8 T:Mode$ Phase | Phase$ Upkeep | ValidPlayer$ You | TriggerZones$ Command | Execute$ TrigExchangeChoose | TriggerDescription$ At the beginning of your upkeep, exchange control of a permanent you control chosen at random and a permanent target opponent controls chosen at random. SVar:TrigExchangeChoose:DB$ ChooseCard | ValidTgts$ Opponent | Choices$ Permanent.TargetedPlayerCtrl | AtRandom$ True | Amount$ 1 | RememberChosen$ True | SubAbility$ ChooseYou SVar:ChooseYou:DB$ ChooseCard | Choices$ Permanent.YouCtrl | Amount$ 1 | AtRandom$ True | RememberChosen$ True | SubAbility$ DBExchange -SVar:DBExchange:DB$ ExchangeControl | Defined$ Remembered | BothDefined$ True | SubAbility$ DBCleanup +SVar:DBExchange:DB$ ExchangeControl | Defined$ Remembered | SubAbility$ DBCleanup SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True SVar:Picture:https://downloads.cardforge.org/images/cards/VAN/Karona, False God Avatar.full.jpg Oracle:Hand -1, life +8\nAt the beginning of your upkeep, exchange control of a permanent you control chosen at random and a permanent target opponent controls chosen at random. diff --git a/forge-gui/res/cardsfolder/k/kheru_spellsnatcher.txt b/forge-gui/res/cardsfolder/k/kheru_spellsnatcher.txt index 8cb0d53762c..e4863d113e8 100644 --- a/forge-gui/res/cardsfolder/k/kheru_spellsnatcher.txt +++ b/forge-gui/res/cardsfolder/k/kheru_spellsnatcher.txt @@ -5,10 +5,8 @@ PT:3/3 K:Morph:4 U U T:Mode$ TurnFaceUp | ValidCard$ Card.Self | Execute$ TrigCounter | TriggerZones$ Battlefield | TriggerDescription$ When CARDNAME is turned face up, counter target spell. If that spell is countered this way, exile it instead of putting it into its owner's graveyard. You may cast that card without paying its mana cost as long as it remains exiled. SVar:TrigCounter:DB$ Counter | TargetType$ Spell | TgtPrompt$ Select target spell | ValidTgts$ Card | RememberCountered$ True | ForgetOtherTargets$ True | Destination$ Exile | SubAbility$ DBEffect -SVar:DBEffect:DB$ Effect | RememberObjects$ Remembered | StaticAbilities$ STPlay | Duration$ Permanent | Triggers$ TrigCleanup | SVars$ DBCleanup | References$ PlayOpp,PlayYou,TrigCleanup,DBCleanup +SVar:DBEffect:DB$ Effect | RememberObjects$ Remembered | StaticAbilities$ STPlay | Duration$ Permanent | ForgetOnMoved$ Exile | SubAbility$ DBCleanup SVar:STPlay:Mode$ Continuous | MayPlay$ True | MayPlayWithoutManaCost$ True | EffectZone$ Command | Affected$ Card.IsRemembered | AffectedZone$ Exile | Description$ You may cast cards without paying their mana cost as long as they remain exiled. -SVar:TrigCleanup:Mode$ ChangesZone | ValidCard$ Card.IsRemembered | Origin$ Exile | Destination$ Any | TriggerZones$ Command | Execute$ DBCleanup | Static$ True -SVar:DBCleanup:DB$ ChangeZone | Defined$ Self | Origin$ Command | Destination$ Exile +SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True AI:RemoveDeck:All -SVar:Picture:http://www.wizards.com/global/images/magic/general/kheru_spellsnatcher.jpg Oracle:Morph {4}{U}{U} (You may cast this card face down as a 2/2 creature for {3}. Turn it face up any time for its morph cost.)\nWhen Kheru Spellsnatcher is turned face up, counter target spell. If that spell is countered this way, exile it instead of putting it into its owner's graveyard. You may cast that card without paying its mana cost for as long as it remains exiled. diff --git a/forge-gui/res/cardsfolder/k/knacksaw_clique.txt b/forge-gui/res/cardsfolder/k/knacksaw_clique.txt index bc698d776ba..e8dd1a61820 100644 --- a/forge-gui/res/cardsfolder/k/knacksaw_clique.txt +++ b/forge-gui/res/cardsfolder/k/knacksaw_clique.txt @@ -4,10 +4,8 @@ Types:Creature Faerie Rogue PT:1/4 K:Flying A:AB$ Dig | Cost$ 1 U Q | ValidTgts$ Opponent | TgtPrompt$ Select target opponent | DigNum$ 1 | ChangeNum$ All | DestinationZone$ Exile | RememberChanged$ True | SubAbility$ DBEffect | SpellDescription$ Target opponent exiles the top card of their library. Until end of turn, you may play that card. -SVar:DBEffect:DB$ Effect | Duration$ EndOfTurn | RememberObjects$ Remembered | StaticAbilities$ STPlay | Triggers$ TrigCleanup | SVars$ DBExileSelf | SubAbility$ DBCleanup +SVar:DBEffect:DB$ Effect | Duration$ EndOfTurn | RememberObjects$ Remembered | StaticAbilities$ STPlay | ForgetOnMoved$ Exile | SubAbility$ DBCleanup SVar:STPlay:Mode$ Continuous | MayPlay$ True | EffectZone$ Command | Affected$ Card.IsRemembered | AffectedZone$ Exile | Description$ You may play a card this turn. -SVar:TrigCleanup:Mode$ ChangesZone | ValidCard$ Card.IsRemembered | Origin$ Exile | Destination$ Any | TriggerZones$ Command | Execute$ DBExileSelf | Static$ True -SVar:DBExileSelf:DB$ ChangeZone | Defined$ Self | Origin$ Command | Destination$ Exile SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True AI:RemoveDeck:All Oracle:Flying\n{1}{U}, {Q}: Target opponent exiles the top card of their library. Until end of turn, you may play that card. ({Q} is the untap symbol.) diff --git a/forge-gui/res/cardsfolder/l/legerdemain.txt b/forge-gui/res/cardsfolder/l/legerdemain.txt index aac8a000596..273619bb132 100644 --- a/forge-gui/res/cardsfolder/l/legerdemain.txt +++ b/forge-gui/res/cardsfolder/l/legerdemain.txt @@ -2,7 +2,7 @@ Name:Legerdemain ManaCost:2 U U Types:Sorcery A:SP$ Pump | Cost$ 2 U U | ValidTgts$ Artifact,Creature | TgtPrompt$ target artifact or creature | StackDescription$ None | SubAbility$ DBExchange | SpellDescription$ Exchange control of target artifact or creature and another target permanent that shares one of those types with it. (This effect lasts indefinitely.) -SVar:DBExchange:DB$ ExchangeControl | Defined$ ParentTarget | ValidTgts$ Permanent | TgtPrompt$ Select target permanent that shares one of those types | TargetsWithSharedTypes$ Artifact,Creature | TargetUnique$ True +SVar:DBExchange:DB$ ExchangeControl | Defined$ ParentTarget | ValidTgts$ Permanent | TgtPrompt$ Select target permanent that shares one of those types | TargetsWithSharedCardType$ ParentTarget | TargetsWithSharedTypes$ Artifact,Creature | TargetUnique$ True AI:RemoveDeck:All SVar:Picture:http://www.wizards.com/global/images/magic/general/legerdemain.jpg Oracle:Exchange control of target artifact or creature and another target permanent that shares one of those types with it. (This effect lasts indefinitely.) diff --git a/forge-gui/res/cardsfolder/o/ornate_kanzashi.txt b/forge-gui/res/cardsfolder/o/ornate_kanzashi.txt index ef107a78fa2..b98f65a8f97 100644 --- a/forge-gui/res/cardsfolder/o/ornate_kanzashi.txt +++ b/forge-gui/res/cardsfolder/o/ornate_kanzashi.txt @@ -2,10 +2,8 @@ Name:Ornate Kanzashi ManaCost:5 Types:Artifact A:AB$ Dig | Cost$ 2 T | ValidTgts$ Opponent | TgtPrompt$ Select target opponent | DigNum$ 1 | ChangeNum$ All | DestinationZone$ Exile | RememberChanged$ True | SubAbility$ DBEffect | SpellDescription$ Target opponent exiles the top card of their library. You may play that card this turn. -SVar:DBEffect:DB$ Effect | Duration$ EndOfTurn | RememberObjects$ Remembered | StaticAbilities$ STPlay | Triggers$ TrigCleanup | SVars$ DBExileSelf | SubAbility$ DBCleanup +SVar:DBEffect:DB$ Effect | Duration$ EndOfTurn | RememberObjects$ Remembered | StaticAbilities$ STPlay | ForgetOnMoved$ Exile | SubAbility$ DBCleanup SVar:STPlay:Mode$ Continuous | MayPlay$ True | EffectZone$ Command | Affected$ Card.IsRemembered | AffectedZone$ Exile | Description$ You may play a card this turn. -SVar:TrigCleanup:Mode$ ChangesZone | ValidCard$ Card.IsRemembered | Origin$ Exile | Destination$ Any | TriggerZones$ Command | Execute$ DBExileSelf | Static$ True -SVar:DBExileSelf:DB$ ChangeZone | Defined$ Self | Origin$ Command | Destination$ Exile SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True AI:RemoveDeck:All Oracle:{2}, {T}: Target opponent exiles the top card of their library. You may play that card this turn. diff --git a/forge-gui/res/cardsfolder/p/praetors_grasp.txt b/forge-gui/res/cardsfolder/p/praetors_grasp.txt index fafb23cd890..285b44d6739 100644 --- a/forge-gui/res/cardsfolder/p/praetors_grasp.txt +++ b/forge-gui/res/cardsfolder/p/praetors_grasp.txt @@ -2,11 +2,8 @@ Name:Praetor's Grasp ManaCost:1 B B Types:Sorcery A:SP$ ChangeZone | Cost$ 1 B B | Origin$ Library | Destination$ Exile | ExileFaceDown$ True | ValidTgts$ Opponent | ChangeType$ Card | ChangeNum$ 1 | IsCurse$ True | RememberChanged$ True | SubAbility$ DBEffect | StackDescription$ SpellDescription | SpellDescription$ Search target opponent's library for a card and exile it face down. Then that player shuffles their library. You may look at and play that card for as long as it remains exiled. -SVar:DBEffect:DB$ Effect | RememberObjects$ Remembered | StaticAbilities$ STPlay | Duration$ Permanent | Triggers$ TrigCleanup | SVars$ DBExileSelf | SubAbility$ DBCleanup +SVar:DBEffect:DB$ Effect | RememberObjects$ Remembered | StaticAbilities$ STPlay | Duration$ Permanent | ForgetOnMoved$ Exile | SubAbility$ DBCleanup SVar:STPlay:Mode$ Continuous | MayLookAt$ You | MayPlay$ True | EffectZone$ Command | Affected$ Card.IsRemembered | AffectedZone$ Exile | Description$ You may look at and play a card as long as it remains exiled. -SVar:TrigCleanup:Mode$ ChangesZone | ValidCard$ Card.IsRemembered | Origin$ Exile | Destination$ Any | TriggerZones$ Command | Execute$ DBExileSelf | Static$ True -SVar:DBExileSelf:DB$ ChangeZone | Defined$ Self | Origin$ Command | Destination$ Exile SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True AI:RemoveDeck:All -SVar:Picture:http://www.wizards.com/global/images/magic/general/praetors_grasp.jpg Oracle:Search target opponent's library for a card and exile it face down. Then that player shuffles their library. You may look at and play that card for as long as it remains exiled. diff --git a/forge-gui/res/cardsfolder/r/release_to_the_wind.txt b/forge-gui/res/cardsfolder/r/release_to_the_wind.txt index 018c7935c76..612e9fc3f94 100644 --- a/forge-gui/res/cardsfolder/r/release_to_the_wind.txt +++ b/forge-gui/res/cardsfolder/r/release_to_the_wind.txt @@ -2,11 +2,8 @@ Name:Release to the Wind ManaCost:2 U Types:Instant A:SP$ ChangeZone | Cost$ 2 U | ValidTgts$ Permanent.nonLand | TgtPrompt$ Select target nonland permanent | Origin$ Battlefield | SubAbility$ DBEffect | Destination$ Exile | SpellDescription$ Exile target nonland permanent. For as long as that card remains exiled, its owner may cast it without paying its mana cost. -SVar:DBEffect:DB$ Effect | RememberObjects$ ParentTarget | EffectOwner$ TargetedOwner | StaticAbilities$ STPlay1,STPlay2 | Duration$ Permanent | Triggers$ TrigCleanup | SVars$ DBExileSelf | SubAbility$ DBCleanup +SVar:DBEffect:DB$ Effect | RememberObjects$ ParentTarget | EffectOwner$ TargetedOwner | StaticAbilities$ STPlay1,STPlay2 | Duration$ Permanent | ForgetOnMoved$ Exile | SubAbility$ DBCleanup SVar:STPlay1:Mode$ Continuous | MayLookAt$ You | EffectZone$ Command | Affected$ Card.IsRemembered | AffectedZone$ Exile | Description$ For as long as that card remains exiled, its owner may cast it without paying its mana cost. SVar:STPlay2:Mode$ Continuous | MayPlay$ True | MayPlayWithoutManaCost$ True | EffectZone$ Command | Affected$ Card.IsRemembered | AffectedZone$ Exile | Secondary$ True -SVar:TrigCleanup:Mode$ ChangesZone | ValidCard$ Card.IsRemembered | Origin$ Exile | Destination$ Any | TriggerZones$ Command | Execute$ DBExileSelf | Static$ True -SVar:DBExileSelf:DB$ ChangeZone | Defined$ Self | Origin$ Command | Destination$ Exile SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True -SVar:Picture:http://www.wizards.com/global/images/magic/general/release_to_the_wind.jpg Oracle:Exile target nonland permanent. For as long as that card remains exiled, its owner may cast it without paying its mana cost. \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/r/role_reversal.txt b/forge-gui/res/cardsfolder/r/role_reversal.txt index beb8f76cfa1..077734bd24a 100644 --- a/forge-gui/res/cardsfolder/r/role_reversal.txt +++ b/forge-gui/res/cardsfolder/r/role_reversal.txt @@ -1,8 +1,7 @@ Name:Role Reversal ManaCost:U U R Types:Sorcery -A:SP$ Pump | Cost$ U U R | ValidTgts$ Permanent | TgtPrompt$ Select target permanent | SubAbility$ DBExchange | StackDescription$ None | SpellDescription$ Exchange control of two target permanents that share a permanent type. -SVar:DBExchange:DB$ ExchangeControl | Defined$ ParentTarget | ValidTgts$ Permanent | TgtPrompt$ Select target permanent shares a card type with it | TargetsWithSharedTypes$ Creature,Artifact,Enchantment,Planeswalker,Land | TargetUnique$ True +A:SP$ ExchangeControl | Cost$ U U R | TargetMin$ 2 | TargetMax$ 2 | ValidTgts$ Permanent | TgtPrompt$ Select target permanents that share a permanent type | TargetsWithSameCardType$ True | SpellDescription$ Exchange control of two target permanents that share a permanent type. AI:RemoveDeck:All AI:RemoveDeck:Random Oracle:Exchange control of two target permanents that share a permanent type. diff --git a/forge-gui/res/cardsfolder/s/shahrazad.txt b/forge-gui/res/cardsfolder/s/shahrazad.txt new file mode 100644 index 00000000000..f6de4101e23 --- /dev/null +++ b/forge-gui/res/cardsfolder/s/shahrazad.txt @@ -0,0 +1,10 @@ +Name:Shahrazad +ManaCost:W W +Types:Sorcery +A:SP$ Subgame | RememberPlayers$ NotWin | SubAbility$ DBRepeatEachPlayer | SpellDescription$ Players play a Magic subgame, using their libraries as their decks. Each player who doesn't win the subgame loses half their life, rounded up. | StackDescription$ SpellDescription +SVar:DBRepeatEachPlayer:DB$ RepeatEach | RepeatPlayers$ Remembered | ClearRememberedBeforeLoop$ True | RepeatSubAbility$ DBLoseLife | SpellDescription$ Each player who doesn't win the subgame loses half their life, rounded up. | StackDescription$ SpellDescription +SVar:DBLoseLife:DB$ LoseLife | LifeAmount$ X | References$ X | Defined$ Player.IsRemembered +SVar:X:PlayerCountRemembered$LifeTotal/HalfUp +AI:RemoveDeck:All +AI:RemoveDeck:Random +Oracle:Players play a Magic subgame, using their libraries as their decks. Each player who doesn't win the subgame loses half their life, rounded up. diff --git a/forge-gui/res/cardsfolder/s/shared_fate.txt b/forge-gui/res/cardsfolder/s/shared_fate.txt index 362805bf251..27dac7eac6d 100644 --- a/forge-gui/res/cardsfolder/s/shared_fate.txt +++ b/forge-gui/res/cardsfolder/s/shared_fate.txt @@ -5,13 +5,11 @@ Text:If a player would draw a card, that player exiles the top card of an oppone T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigEffects | Static$ True #Create an effect for each player. The effect contains both Shared Fate's abilities. SVar:TrigEffects:DB$ RepeatEach | RepeatPlayers$ Player | RepeatSubAbility$ DBEffect -SVar:DBEffect:DB$ Effect | EffectOwner$ Remembered | StaticAbilities$ STPlay | Triggers$ TrigCleanup | ReplacementEffects$ RDraw | SVars$ DBChooseOpp,DBExile,DBCleanup | Duration$ UntilHostLeavesPlay +SVar:DBEffect:DB$ Effect | EffectOwner$ Remembered | StaticAbilities$ STPlay | ReplacementEffects$ RDraw | SVars$ DBChooseOpp,DBExile | Duration$ UntilHostLeavesPlay | ForgetOnMoved$ Exile SVar:RDraw:Event$ Draw | ActiveZones$ Command | ValidPlayer$ You | ReplaceWith$ DBChooseOpp | Description$ If you would draw a card, exile the top card of an opponent's library face down instead. SVar:DBChooseOpp:DB$ ChoosePlayer | ChoiceTitle$ Choose an opponent whose top library card to exile | Choices$ Player.Opponent | AILogic$ Curse | SubAbility$ DBExile SVar:DBExile:DB$ Dig | DigNum$ 1 | ChangeNum$ All | DestinationZone$ Exile | ExileFaceDown$ True | Defined$ Player.Chosen | RememberChanged$ True SVar:STPlay:Mode$ Continuous | MayLookAt$ You | MayPlay$ True | EffectZone$ Command | Affected$ Card.IsRemembered | AffectedZone$ Exile | Description$ You may look at and play cards exiled with Shared Fate. -SVar:TrigCleanup:Mode$ ChangesZone | ValidCard$ Card.IsRemembered | Origin$ Exile | Destination$ Any | TriggerZones$ Command | Execute$ DBCleanup | Static$ True -SVar:DBCleanup:DB$ Cleanup | ForgetDefined$ TriggeredCard AI:RemoveDeck:All AI:RemoveDeck:Random Oracle:If a player would draw a card, that player exiles the top card of an opponent's library face down instead.\nEach player may look at and play cards they exiled with Shared Fate. diff --git a/forge-gui/res/cardsfolder/s/shifting_loyalties.txt b/forge-gui/res/cardsfolder/s/shifting_loyalties.txt index 4e45b9a0191..ca71f08f973 100644 --- a/forge-gui/res/cardsfolder/s/shifting_loyalties.txt +++ b/forge-gui/res/cardsfolder/s/shifting_loyalties.txt @@ -1,9 +1,7 @@ Name:Shifting Loyalties ManaCost:5 U Types:Sorcery -A:SP$ Pump | Cost$ 5 U | ValidTgts$ Permanent | TgtPrompt$ Select target permanent | SubAbility$ DBExchange | StackDescription$ None | SpellDescription$ Exchange control of two target permanents that share a card type. -SVar:DBExchange:DB$ ExchangeControl | Defined$ ParentTarget | ValidTgts$ Permanent | TgtPrompt$ Select target permanent shares a card type with it | TargetsWithSharedTypes$ Creature,Artifact,Enchantment,Planeswalker,Land,Tribal | TargetUnique$ True +A:SP$ ExchangeControl | Cost$ 5 U | TargetMin$ 2 | TargetMax$ 2 | ValidTgts$ Permanent | TgtPrompt$ Select target permanents that share a permanent type | TargetsWithSameCardType$ True | SpellDescription$ Exchange control of two target permanents that share a card type. AI:RemoveDeck:All AI:RemoveDeck:Random -SVar:Picture:http://www.wizards.com/global/images/magic/general/shifting_loyalties.jpg Oracle:Exchange control of two target permanents that share a card type. (Artifact, creature, enchantment, land, and planeswalker are card types.) diff --git a/forge-gui/res/cardsfolder/s/spelljack.txt b/forge-gui/res/cardsfolder/s/spelljack.txt index 36f5789db15..ba205295ca0 100644 --- a/forge-gui/res/cardsfolder/s/spelljack.txt +++ b/forge-gui/res/cardsfolder/s/spelljack.txt @@ -2,9 +2,7 @@ Name:Spelljack ManaCost:3 U U U Types:Instant A:SP$ Counter | Cost$ 3 U U U | TargetType$ Spell | TgtPrompt$ Select target spell | ValidTgts$ Card | RememberCountered$ True | ForgetOtherTargets$ True | Destination$ Exile | SubAbility$ DBEffect | SpellDescription$ Counter target spell. If that spell is countered this way, exile it instead of putting it into its owner's graveyard. You may play it without paying its mana cost for as long as it remains exiled. (If it has X in its mana cost, X is 0.) -SVar:DBEffect:DB$ Effect | Name$ Spelljack Effect | RememberObjects$ Remembered | StaticAbilities$ Play | Duration$ Permanent | Triggers$ TrigCleanup | SVars$ DBCleanup | References$ Play,TrigCleanup,DBCleanup +SVar:DBEffect:DB$ Effect | Name$ Spelljack Effect | RememberObjects$ Remembered | StaticAbilities$ Play | Duration$ Permanent | ForgetOnMoved$ Exile | SubAbility$ DBCleanup SVar:Play:Mode$ Continuous | MayPlay$ True | MayPlayWithoutManaCost$ True | EffectZone$ Command | Affected$ Card.IsRemembered | AffectedZone$ Exile | Description$ You may play cards exiled with Spelljack. -SVar:TrigCleanup:Mode$ ChangesZone | ValidCard$ Card.IsRemembered | Origin$ Exile | Destination$ Any | TriggerZones$ Command | Execute$ DBCleanup | Static$ True -SVar:DBCleanup:DB$ ChangeZone | Defined$ Self | Origin$ Command | Destination$ Exile -SVar:Picture:http://www.wizards.com/global/images/magic/general/spelljack.jpg +SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True Oracle:Counter target spell. If that spell is countered this way, exile it instead of putting it into its owner's graveyard. You may play it without paying its mana cost for as long as it remains exiled. (If it has X in its mana cost, X is 0.) diff --git a/forge-gui/res/cardsfolder/upcoming/alrunds_epiphany.txt b/forge-gui/res/cardsfolder/upcoming/alrunds_epiphany.txt new file mode 100644 index 00000000000..73a2d05ab1e --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/alrunds_epiphany.txt @@ -0,0 +1,9 @@ +Name:Alrund's Epiphany +ManaCost:5 U U +Types:Sorcery +A:SP$ Token | Cost$ 5 U U | LegacyImage$ u 1 1 bird flying khm | TokenAmount$ 2 | TokenScript$ u_1_1_bird_flying | TokenOwner$ You | SubAbility$ DBAddTurn | SpellDescription$ Create two 1/1 blue Bird creature tokens with flying. Take an extra turn after this one. Exile CARDNAME. +SVar:DBAddTurn: DB$ AddTurn | Defined$ You | NumTurns$ 1 | SubAbility$ DBChange | StackDescription$ None +SVar:DBChange:DB$ ChangeZone | Origin$ Stack | Destination$ Exile | StackDescription$ None +K:Foretell:4 U U +DeckHas:Ability$Token +Oracle:Create two 1/1 blue Bird creature tokens with flying. Take an extra turn after this one. Exile Alrund's Epiphany.\nForetell {4}{U}{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.) diff --git a/forge-gui/res/cardsfolder/upcoming/ascent_of_the_worthy.txt b/forge-gui/res/cardsfolder/upcoming/ascent_of_the_worthy.txt new file mode 100644 index 00000000000..9e86d9f1505 --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/ascent_of_the_worthy.txt @@ -0,0 +1,11 @@ +Name:Ascent of the Worthy +ManaCost:1 W B +Types:Enchantment Saga +K:Saga:3:DBChoose,DBChoose,DBChangeZone +SVar:DBChoose:DB$ ChooseCard | Choices$ Creature.YouCtrl | SubAbility$ DBEffect | SpellDescription$ Choose a creature you control. Until your next turn, all damage that would be dealt to creatures you control is dealt to that creature instead. +SVar:DBEffect:DB$ Effect | ReplacementEffects$ DamageEvent | SVars$ GideonSac | References$ DamageEvent,GideonSac | ExileOnMoved$ True | RememberObjects$ ChosenCard | Duration$ UntilYourNextTurn +SVar:DamageEvent:Event$ DamageDone | ActiveZones$ Command | ValidTarget$ Creature.YouCtrl | ReplaceWith$ GideonSac | DamageTarget$ Remembered | Description$ All damage that would be dealt this turn to creatures you control is dealt to the chosen creature instead (if it's still on the battlefield). +SVar:DBChangeZone:DB$ ChangeZone | Origin$ Graveyard | Destination$ Battlefield | TgtPrompt$ Choose target creature card in your graveyard | ValidTgts$ Creature.YouOwn | WithCounters$ Flying_1 | AnimateSubAbility$ Animate | SpellDescription$ Return target creature card from your graveyard to the battlefield with a flying counter on it. +SVar:GideonSac:DB$ ReplaceEffect | VarName$ Affected | VarValue$ Remembered | VarType$ Card +SVar:Animate:DB$Animate | Defined$ Remembered | Types$ Angel,Warrior | Permanent$ True +Oracle:(As this Saga enters and after your draw step, add a lore counter. Sacrifice after III.)\nI, II — Choose a creature you control. Until your next turn, all damage that would be dealt to creatures you control is dealt to that creature instead.\nIII — Return target creature card from your graveyard to the battlefield with a flying counter on it. That creature is an Angel Warrior in addition to its other types. diff --git a/forge-gui/res/cardsfolder/upcoming/augury_raven.txt b/forge-gui/res/cardsfolder/upcoming/augury_raven.txt new file mode 100644 index 00000000000..6ce07238d4a --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/augury_raven.txt @@ -0,0 +1,7 @@ +Name:Augury Raven +ManaCost:3 U +Types:Creature Bird +PT:3/3 +K:Flying +K:Foretell:1 U +Oracle:Flying\nForetell {1}{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.) diff --git a/forge-gui/res/cardsfolder/upcoming/battle_for_bretagard.txt b/forge-gui/res/cardsfolder/upcoming/battle_for_bretagard.txt new file mode 100644 index 00000000000..b5d31297e24 --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/battle_for_bretagard.txt @@ -0,0 +1,10 @@ +Name:Battle for Bretagard +ManaCost:1 G W +Types:Enchantment Saga +K:Saga:3:TrigToken1,TrigToken2,DBCopy +SVar:TrigToken1:DB$ Token | TokenAmount$ 1 | TokenScript$ w_1_1_human_warrior | TokenOwner$ You | SpellDescription$ Create a 1/1 white Human Warrior creature token. +SVar:TrigToken2:DB$ Token | TokenAmount$ 1 | TokenScript$ g_1_1_elf_warrior | TokenOwner$ You | SpellDescription$ Create a 1/1 green Elf Warrior creature token. +SVar:DBCopy:DB$ CopyPermanent | Choices$ Artifact.token+YouCtrl,Creature.token+YouCtrl | WithDifferentNames$ True | SpellDescription$ Choose any number of artifact tokens and/or creature tokens you control with different names. For each of them, create a token that’s a copy of it. +DeckHas:Ability$Token +Oracle:(As this Saga enters and after your draw step, add a lore counter. Sacrifice after III.)\nI — Create a 1/1 white Human Warrior creature token.\nII — Create a 1/1 green Elf Warrior creature token.\nIII — Choose any number of artifact tokens and/or creature tokens you control with different names. For each of them, create a token that’s a copy of it. + diff --git a/forge-gui/res/cardsfolder/upcoming/battle_mammoth.txt b/forge-gui/res/cardsfolder/upcoming/battle_mammoth.txt new file mode 100644 index 00000000000..11d0cb3d057 --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/battle_mammoth.txt @@ -0,0 +1,9 @@ +Name:Battle Mammoth +ManaCost:3 G G +Types:Creature Elephant +PT:6/5 +K:Trample +T:Mode$ BecomesTarget | ValidTarget$ Permanent.YouCtrl+inZoneBattlefield | ValidSource$ Card.OppCtrl | TriggerZones$ Battlefield | Execute$ TrigDraw | OptionalDecider$ You | TriggerDescription$ Whenever a permanent you control becomes the target of a spell or ability an opponent controls, you may draw a card. +SVar:TrigDraw:DB$Draw | NumCards$ 1 | SpellDescription$ Draw a card. +K:Foretell:2 G G +Oracle:Trample\nWhenever a permanent you control becomes the target of a spell or ability an opponent controls, you may draw a card.\nForetell {2}{G}{G} (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.) diff --git a/forge-gui/res/cardsfolder/upcoming/battle_of_frost_and_fire.txt b/forge-gui/res/cardsfolder/upcoming/battle_of_frost_and_fire.txt new file mode 100644 index 00000000000..1591ec3b7ff --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/battle_of_frost_and_fire.txt @@ -0,0 +1,12 @@ +Name:Battle of Frost and Fire +ManaCost:3 U R +Types:Enchantment Saga +K:Saga:3:DBDamageAll,DBScry,DBEffect +SVar:DBDamageAll:DB$ DamageAll | NumDmg$ 4 | ValidCards$ Creature.nonGiant,Planeswalker | ValidDescription$ each non-Giant creature and each planeswalker. | SpellDescription$ CARDNAME deals 4 damage to each non-Giant creature and each planeswalker. +SVar:DBScry:DB$ Scry | ScryNum$ 3 | SpellDescription$ Scry 3. +SVar:DBEffect:DB$ Effect | Triggers$ CastSpell | SpellDescription$ Whenever you cast a spell with converted mana cost 5 or greater this turn, draw two cards, then discard a card. +SVar:CastSpell:Mode$ SpellCast | ValidCard$ Card.cmcGE5 | ValidActivatingPlayer$ You | TriggerZones$ Command | Execute$ DBDraw | TriggerDescription$ Whenever you cast a spell with converted mana cost 5 or greater this turn, draw two cards, then discard a card. +SVar:DBDraw:DB$ Draw | Defined$ You | NumCards$ 2 | SubAbility$ DBDiscard +SVar:DBDiscard:DB$ Discard | Defined$ You | NumCards$ 1 | Mode$ TgtChoose +DeckHints:Type$Giant +Oracle:(As this Saga enters and after your draw step, add a lore counter. Sacrifice after III.)\nI — Battle of Frost and Fire deals 4 damage to each non-Giant creature and each planeswalker.\nII — Scry 3.\nIII — Whenever you cast a spell with converted mana cost 5 or greater this turn, draw two cards, then discard a card. diff --git a/forge-gui/res/cardsfolder/upcoming/behold_the_multiverse.txt b/forge-gui/res/cardsfolder/upcoming/behold_the_multiverse.txt new file mode 100644 index 00000000000..943374b6a18 --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/behold_the_multiverse.txt @@ -0,0 +1,7 @@ +Name:Behold the Multiverse +ManaCost:3 U +Types:Instant +A:SP$ Scry | Cost$ 3 U | ScryNum$ 2 | SubAbility$ DBDraw | AILogic$ BestOpportunity | SpellDescription$ Scry 2, then draw two cards. +SVar:DBDraw:DB$Draw | NumCards$ 2 +K:Foretell:1 U +Oracle:Scry 2, then draw two cards.\nForetell {1}{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.) diff --git a/forge-gui/res/cardsfolder/upcoming/bloodline_pretender.txt b/forge-gui/res/cardsfolder/upcoming/bloodline_pretender.txt new file mode 100644 index 00000000000..740d51cc872 --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/bloodline_pretender.txt @@ -0,0 +1,11 @@ +Name:Bloodline Pretender +ManaCost:3 +Types:Artifact Creature Shapeshifter +PT:2/2 +K:Changeling +K:ETBReplacement:Other:ChooseCT +SVar:ChooseCT:DB$ ChooseType | Defined$ You | Type$ Creature | AILogic$ MostProminentInComputerDeck | SpellDescription$ As CARDNAME enters the battlefield, choose a creature type. +T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Creature.Other+ChosenType+YouCtrl | TriggerZones$ Battlefield | Execute$ TrigPutCounter | TriggerDescription$ Whenever another creature of the chosen type enters the battlefield under your control, put a +1/+1 counter on CARDNAME. +SVar:TrigPutCounter:DB$PutCounter | CounterType$ P1P1 | CounterNum$ 1 +DeckHas:Ability$Counters +Oracle:Changeling\nAs Bloodline Pretender enters the battlefield, choose a creature type.\nWhenever another creature of the chosen type enters the battlefield under your control, put a +1/+1 counter on Bloodline Pretender. diff --git a/forge-gui/res/cardsfolder/upcoming/cosmos_charger.txt b/forge-gui/res/cardsfolder/upcoming/cosmos_charger.txt new file mode 100644 index 00000000000..dc2ebb62b5b --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/cosmos_charger.txt @@ -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.) + diff --git a/forge-gui/res/cardsfolder/upcoming/crush_the_weak.txt b/forge-gui/res/cardsfolder/upcoming/crush_the_weak.txt new file mode 100644 index 00000000000..7675d78fcc2 --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/crush_the_weak.txt @@ -0,0 +1,7 @@ +Name:Crush the Weak +ManaCost:2 R +Types:Sorcery +A:SP$ DamageAll | Cost$ 2 R | ValidCards$ Creature | NumDmg$ 2 | RememberDamaged$ True | ReplaceDyingDefined$ Remembered | SubAbility$ DBCleanup | SpellDescription$ CARDNAME deals 2 damage to each creature. If a creature dealt damage this way would die this turn, exile it instead. | StackDescription$ SpellDescription +SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True +K:Foretell:R +Oracle:Crush the Weak deals 2 damage to each creature. If a creature dealt damage this way would die this turn, exile it instead.\nForetell {R} (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.) diff --git a/forge-gui/res/cardsfolder/upcoming/demon_bolt.txt b/forge-gui/res/cardsfolder/upcoming/demon_bolt.txt new file mode 100644 index 00000000000..8850c59cca7 --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/demon_bolt.txt @@ -0,0 +1,6 @@ +Name:Demon Bolt +ManaCost:2 R +Types:Instant +A:SP$ DealDamage | Cost$ 2 R | ValidTgts$ Creature,Planeswalker | TgtPrompt$ Select target creature or planeswalker. | NumDmg$ 4 | SpellDescription$ CARDNAME deals 4 damage to target creature or planeswalker. +K:Foretell:R +Oracle:Demon Bolt deals 4 damage to target creature or planeswalker.\nForetell {R} (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.) diff --git a/forge-gui/res/cardsfolder/upcoming/depart_the_realm.txt b/forge-gui/res/cardsfolder/upcoming/depart_the_realm.txt new file mode 100644 index 00000000000..e66721623b0 --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/depart_the_realm.txt @@ -0,0 +1,6 @@ +Name:Depart the Realm +ManaCost:1 U +Types:Instant +A:SP$ ChangeZone | Cost$ 1 U | ValidTgts$ Permanent.nonLand | TgtPrompt$ Select target nonland permanent | Origin$ Battlefield | Destination$ Hand | SpellDescription$ Return target nonland permanent to its owner's hand. +K:Foretell:U +Oracle:Return target nonland permanent to its owner's hand.\nForetell {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.) diff --git a/forge-gui/res/cardsfolder/upcoming/doomskar.txt b/forge-gui/res/cardsfolder/upcoming/doomskar.txt new file mode 100644 index 00000000000..729389b3e93 --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/doomskar.txt @@ -0,0 +1,6 @@ +Name:Doomskar +ManaCost:3 W W +Types:Sorcery +A:SP$ DestroyAll | Cost$ 3 W W | ValidCards$ Creature | SpellDescription$ Destroy all creatures. +K:Foretell:1 W W +Oracle:Destroy all creatures.\nForetell {1}{W}{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.) diff --git a/forge-gui/res/cardsfolder/upcoming/doomskar_oracle.txt b/forge-gui/res/cardsfolder/upcoming/doomskar_oracle.txt new file mode 100644 index 00000000000..83b086270ad --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/doomskar_oracle.txt @@ -0,0 +1,11 @@ +Name:Doomskar Oracle +ManaCost:2 W +Types:Creature Human Cleric +PT:3/2 +T:Mode$ SpellCast | ValidCard$ Card.YouCtrl | TriggerZones$ Battlefield | Execute$ TrigGainLife | CheckSVar$ YouCastThisTurn | SVarCompare$ EQ2 | NoResolvingCheck$ True | TriggerDescription$ Whenever you cast your second spell each turn, you gain 2 life. +SVar:TrigGainLife:DB$ GainLife | Defined$ You | LifeAmount$ 2 +SVar:YouCastThisTurn:Count$ThisTurnCast_Card.YouCtrl +SVar:BuffedBy:Card +K:Foretell:W +DeckHas:Ability$LifeGain +Oracle:Whenever you cast your second spell each turn, you gain 2 life.\nForetell {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.) diff --git a/forge-gui/res/cardsfolder/upcoming/doomskar_titan.txt b/forge-gui/res/cardsfolder/upcoming/doomskar_titan.txt new file mode 100644 index 00000000000..2dd959f2d59 --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/doomskar_titan.txt @@ -0,0 +1,9 @@ +Name:Doomskar Titan +ManaCost:4 R R +Types:Creature Giant Berserker +PT:4/4 +T:Mode$ ChangesZone | ValidCard$ Card.Self | Origin$ Any | Destination$ Battlefield | Execute$ TrigPumpAll | TriggerDescription$ When CARDNAME enters the battlefield, creatures you control get +1/0 and gain haste until end of turn. +SVar:TrigPumpAll:DB$ PumpAll | ValidCards$ Creature.YouCtrl | NumAtt$ 1 | KW$ Haste +SVar:PlayMain1:TRUE +K:Foretell:4 R +Oracle:When Doomskar Titan enters the battlefield, creatures you control get +1/+) and gain haste until end of turn.\nForetell {4}{R} (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.) diff --git a/forge-gui/res/cardsfolder/upcoming/dream_devourer.txt b/forge-gui/res/cardsfolder/upcoming/dream_devourer.txt new file mode 100644 index 00000000000..ce74e703905 --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/dream_devourer.txt @@ -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. diff --git a/forge-gui/res/cardsfolder/upcoming/dual_strike.txt b/forge-gui/res/cardsfolder/upcoming/dual_strike.txt new file mode 100644 index 00000000000..8cd17e6ebfc --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/dual_strike.txt @@ -0,0 +1,8 @@ +Name:Dual Strike +ManaCost:R R +Types:Instant +A:SP$ DelayedTrigger | Cost$ R R | AILogic$ SpellCopy | Execute$ EffTrigCopy | ThisTurn$ True | Mode$ SpellCast | ValidCard$ Instant.cmcLE4,Sorcery.cmcLE4 | ValidActivatingPlayer$ You | SpellDescription$ When you cast your next instant or sorcery spell with converted mana cost 4 or less this turn, copy that spell. You may choose new targets for the copy. +SVar:EffTrigCopy:DB$ CopySpellAbility | Defined$ TriggeredSpellAbility | MayChooseTarget$ True +SVar:AIPriorityModifier:9 +K:Foretell:R +Oracle:When you cast your next instant or sorcery spell with converted mana cost 4 or less this turn, copy that spell. You may choose new targets for that copy.\nForetell {R} (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.) diff --git a/forge-gui/res/cardsfolder/upcoming/dwarven_reinforcements.txt b/forge-gui/res/cardsfolder/upcoming/dwarven_reinforcements.txt new file mode 100644 index 00000000000..b9b65047fef --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/dwarven_reinforcements.txt @@ -0,0 +1,7 @@ +Name:Dwarven Reinforcements +ManaCost:3 R +Types:Sorcery +A:SP$ Token | Cost$ 3 R | LegacyImage$ r 2 1 dwarf berserker khm | TokenAmount$ 2 | TokenScript$ r_2_1_dwarf_berserker | TokenOwner$ You | SpellDescription$ Create 2 2/1 red Dwarf Berserker creature tokens. +K:Foretell:1 R +DeckHas:Ability$Token +Oracle:Create 2 2/1 red Dwarf Berserker creature tokens.\nForetell {1}{R} (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.) diff --git a/forge-gui/res/cardsfolder/upcoming/ethereal_valkyrie.txt b/forge-gui/res/cardsfolder/upcoming/ethereal_valkyrie.txt new file mode 100644 index 00000000000..b46331d9ecf --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/ethereal_valkyrie.txt @@ -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.) + diff --git a/forge-gui/res/cardsfolder/upcoming/fall_of_the_impostor.txt b/forge-gui/res/cardsfolder/upcoming/fall_of_the_impostor.txt new file mode 100644 index 00000000000..6047960e2db --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/fall_of_the_impostor.txt @@ -0,0 +1,11 @@ +Name:Fall of the Impostor +ManaCost:1 G W +Types:Enchantment Saga +K:Saga:3:DBPutCounter,DBPutCounter,DBExileGreatest +SVar:DBPutCounter:DB$ PutCounter | ValidTgts$ Creature | TgtPrompt$ Select target creature | CounterType$ P1P1 | CounterNum$ 1 | TargetMin$ 0 | TargetMax$ 1 | SpellDescription$ Put a +1/+1 counter on up to one target creature. +SVar:DBExileGreatest:DB$ Pump | ValidTgts$ Player.Opponent | RememberTargets$ True | SubAbility$ DBChooseExiled | SpellDescription$ Exile a creature with the greatest power among creatures target opponent controls. +SVar:DBChooseExiled:DB$ ChooseCard | Choices$ Creature.greatestPowerControlledByRemembered | MinAmount$ 1 | Amount$ 1 | Mandatory$ True | ChoiceZone$ Battlefield | SubAbility$ DBChangeZone +SVar:DBChangeZone:DB$ ChangeZone | Origin$ Battlefield | Destination$ Exile | Defined$ ChosenCard | SubAbility$ DBCleanup +SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True +DeckHas:Ability$Counters +Oracle:I - Put a +1/+1 counter on up to one target creature.\nII - Put a +1/+1 counter on up to one target creature.\nIII - Exile a creature with the greatest power among creatures target opponent controls. diff --git a/forge-gui/res/cardsfolder/upcoming/forging_the_tyrite_sword.txt b/forge-gui/res/cardsfolder/upcoming/forging_the_tyrite_sword.txt new file mode 100644 index 00000000000..c1cdb81e3cc --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/forging_the_tyrite_sword.txt @@ -0,0 +1,8 @@ +Name:Forging the Tyrite Sword +ManaCost:1 R W +Types:Enchantment Saga +K:Saga:3:DBToken,DBToken,DBChangeZone +SVar:DBToken:DB$ Token | TokenAmount$ 1 | TokenScript$ c_a_treasure_sac | TokenOwner$ You $ SpellDescription +SVar:DBChangeZone:DB$ ChangeZone | Origin$ Library | Destination$ Hand | ChangeType$ Card.namedHalvar; God of Battle,Equipment | ChangeNum$ 1 | SpellDescription$ Search your library for a card named Halvar, God of Battle or an Equipment card, reveal it, put it into your hand, then shuffle your library. +DeckHas:Ability$Token +Oracle:(As this Saga enters and after your draw step, add a lore counter. Sacrifice after III.)\nI, II — Create a Treasure token.\nIII — Search your library for a card named Halvar, God of Battle or an Equipment card, reveal it, put it into your hand, then shuffle your library. diff --git a/forge-gui/res/cardsfolder/upcoming/glorious_protector.txt b/forge-gui/res/cardsfolder/upcoming/glorious_protector.txt new file mode 100644 index 00000000000..26ecd404faa --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/glorious_protector.txt @@ -0,0 +1,22 @@ +Name:Glorious Protector +ManaCost:2 W W +Types:Creature Angel Cleric +PT:3/4 +K:Flash +K:Flying +T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigExile | TriggerDescription$ When CARDNAME enters the battlefield, you may exile any number of other non-Angel creatures you control until CARDNAME leaves the battlefield. +SVar:TrigExile:DB$ ChangeZone | Origin$ Battlefield | Destination$ Exile | RememberChanged$ True | Hidden$ True | ChangeType$ Creature.nonAngel+YouCtrl | ChangeNum$ MaxTgts | SelectPrompt$ Choose any number of non-Angel creatures you control | SubAbility$ DBEffect | References$ MaxTgts +SVar:DBEffect:DB$ Effect | Triggers$ ComeBack | RememberObjects$ RememberedCard | ImprintCards$ Self | SVars$ TrigReturn,ExileSelf | ConditionPresent$ Card.Self | Duration$ Permanent | ForgetOnMoved$ Exile +SVar:ComeBack:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Any | ValidCard$ Card.IsImprinted | Execute$ TrigReturn | TriggerZones$ Command | TriggerController$ TriggeredCardController | Static$ True | TriggerDescription$ That creature is exiled until EFFECTSOURCE leaves the battlefield +SVar:TrigReturn:DB$ ChangeZoneAll | Origin$ Exile | Destination$ Battlefield | ChangeType$ Card.IsRemembered | SubAbility$ ExileSelf +SVar:ExileSelf:DB$ ChangeZone | Origin$ Command | Destination$ Exile | Defined$ Self +#Triggers to forget remembered on this +T:Mode$ ChangesZone | ValidCard$ Card.IsRemembered | Origin$ Exile | Destination$ Any | TriggerZones$ Battlefield | Static$ True | Execute$ TrigForget +SVar:TrigForget:DB$ Pump | ForgetObjects$ TriggeredCard +T:Mode$ ChangesZone | ValidCard$ Card.Self | Origin$ Battlefield | Destination$ Any | Static$ True | Execute$ TrigForgetAll +SVar:TrigForgetAll:DB$ Cleanup | ClearRemembered$ True +SVar:X:Count$ValidExile Card.IsRemembered+ExiledWithSource/Times.2 +SVar:MaxTgts:Count$Valid Creature.nonAngel+YouCtrl +AI:RemoveDeck:Random +K:Foretell:2 W +Oracle:Flash\nFlying\nWhen Glorious Protector enters the battlefield, you may exile any number of non-Angel creatures you control until Glorious Protector leaves the battlefield.\nForetell {2}{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.) diff --git a/forge-gui/res/cardsfolder/upcoming/gods_hall_guardian.txt b/forge-gui/res/cardsfolder/upcoming/gods_hall_guardian.txt new file mode 100644 index 00000000000..52f7fdc1910 --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/gods_hall_guardian.txt @@ -0,0 +1,7 @@ +Name:Gods' Hall Guardian +ManaCost:5 W +Types:Creature Cat +PT:3/6 +K:Vigilance +K:Foretell:3 W +Oracle:Vigilance\nForetell {3}{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.) diff --git a/forge-gui/res/cardsfolder/upcoming/harald_king_of_skemfar.txt b/forge-gui/res/cardsfolder/upcoming/harald_king_of_skemfar.txt new file mode 100644 index 00000000000..22b15fc5e5e --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/harald_king_of_skemfar.txt @@ -0,0 +1,9 @@ +Name:Harald, King of Skemfar +ManaCost:1 B G +Types:Legendary Creature Elf Warrior +PT:3/2 +K:Menace +T:Mode$ ChangesZone | ValidCard$ Card.Self | Origin$ Any | Destination$ Battlefield | Execute$ TrigDig | TriggerDescription$ When CARDNAME enters the battlefield, look at the top five cards of your library. You may reveal an Elf, Warrior, or Tyvar card from among them and put it into your hand. Put the rest on the bottom of your library in a random order. +SVar:TrigDig:DB$ Dig | DigNum$ 5 | ChangeNum$ 1 | Optional$ True | ForceRevealToController$ True | ChangeValid$ Elf,Warrior,Tyvar | RestRandomOrder$ True +DeckHints:Type$Elf|Tyvar +Oracle:Menace (This creature can’t be blocked except by two or more creatures.)\nWhen Harald, King of Skemfar enters the battlefield, look at the top five cards of your library. You may reveal an Elf, Warrior, or Tyvar card from among them and put it into your hand. Put the rest on the bottom of your library in a random order. diff --git a/forge-gui/res/cardsfolder/upcoming/harald_unites_the_elves.txt b/forge-gui/res/cardsfolder/upcoming/harald_unites_the_elves.txt new file mode 100644 index 00000000000..0e6271f39a7 --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/harald_unites_the_elves.txt @@ -0,0 +1,13 @@ +Name:Harald Unites the Elves +ManaCost:2 B G +Types:Enchantment Saga +K:Saga:3:DBMill,DBPutCounterAll,DBEffect +SVar:DBMill:DB$ Mill | NumCards$ 3 | Defined$ You | SubAbility$ DBChangeZone | SpellDescription$ Mill three cards. You may put an Elf card or Tyvar card from your graveyard onto the battlefield. +SVar:DBChangeZone:DB$ ChangeZone | Hidden$ True | ChangeType$ Elf.YouOwn,Tyvar.YouOwn | ChangeNum$ 1 | Origin$ Graveyard | Destination$ Battlefield +SVar:DBPutCounterAll:DB$ PutCounterAll | ValidCards$ Creature.Elf+YouCtrl | CounterType$ P1P1 | CounterNum$ 1 | SpellDescription$ Put a +1/+1 counter on each Elf you control. +SVar:DBEffect:DB$ Effect | Triggers$ TrigAttack | SpellDescription$ Whenever an Elf you control attacks this turn, target creature an opponent controls gets -1/-1 until end of turn. +SVar:TrigAttack:Mode$ Attacks | ValidCard$ Creature.Elf+YouCtrl | Execute$ TrigPump | TriggerZones$ Battlefield | TriggerDescription$ Whenever an Elf you control attacks this turn, target creature an opponent controls gets -1/-1 until end of turn. +SVar:TrigPump:DB$ Pump | ValidTgts$ Creature.OppCtrl | TgtPrompt$ Select target creature | NumAtt$ -1 | NumDef$ -1 | IsCurse$ True +DeckHints:Type$Elf|Tyvar +DeckHas:Ability$Counters +Oracle:(As this Saga enters and after your draw step, add a lore counter. Sacrifice after III.)\nI - Mill three cards. You may put an Elf card or Tyvar card from your graveyard onto the battlefield.\nII - Put a +1/+1 counter on each Elf you control.\nIII - Whenever an Elf you control attacks this turn, target creature an opponent controls gets -1/-1 until end of turn. diff --git a/forge-gui/res/cardsfolder/upcoming/immersturm_predator.txt b/forge-gui/res/cardsfolder/upcoming/immersturm_predator.txt new file mode 100644 index 00000000000..77c1a3bbc05 --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/immersturm_predator.txt @@ -0,0 +1,12 @@ +Name:Immersturm Predator +ManaCost:2 B R +Types:Creature Vampire Dragon +PT:3/3 +K:Flying +T:Mode$ Taps | ValidCard$ Card.Self | Execute$ TrigExile | TriggerDescription$ Whenever CARDNAME becomes tapped, exile up to one target card from a graveyard and put a +1/+1 counter on CARDNAME. +SVar:TrigExile:DB$ Changezone | ValidTgts$ Card | TargetMin$ 0 | TargetMax$ 1 | Origin$ Graveyard | Destination$ Exile | TgtPrompt$ Choose target card in a graveyard | SubAbility$ DBPutCounter +SVar:DBPutCounter:DB$ PutCounter | Defined$ Self | CounterType$ P1P1 | CounterNum$ 1 +A:AB$ Pump | Cost$ Sac<1/Creature.Other/another creature> | Defined$ Self | KW$ Indestructible | SubAbility$ DBTap | SpellDescription$ CARDNAME gains indestructible until end of turn. Tap it. +SVar:DBTap:DB$ Tap | Defined$ Self +DeckHas:Ability$Counters +Oracle:Flying\nWhenever Immersturm Predator becomes tapped, exile up to one target card from a graveyard and put a +1/+1 counter on Immersturm Predator.\nSacrifice another creature: Immersturm Predator gains indestructible until end of turn. Tap it. diff --git a/forge-gui/res/cardsfolder/upcoming/invasion_of_the_giants.txt b/forge-gui/res/cardsfolder/upcoming/invasion_of_the_giants.txt new file mode 100644 index 00000000000..75201703005 --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/invasion_of_the_giants.txt @@ -0,0 +1,14 @@ +Name:Invasion of the Giants +ManaCost:U R +Types:Enchantment Saga +K:Saga:3:DBScry,DBDraw,DBEffect +SVar:DBScry:DB$ Scry | ScryNum$ 2 | SpellDescription$ Scry 2. +SVar:DBDraw:DB$ Draw | NumCards$ 1 | SubAbility$ DBReveal | SpellDescription$ Draw a card. Then you may reveal a Giant card from your hand. When you do, CARDNAME deals 2 damage to target opponent or planeswalker. +SVar:DBReveal:DB$ ImmediateTrigger | Execute$ TrigDealDamage | UnlessCost$ Reveal<1/Giant> | UnlessPayer$ You | UnlessSwitched$ True | SpellDescription$ You may reveal a Giant card from your hand. When you do, CARDNAME deals 2 damage to target opponent or planeswalker. +SVar:TrigDealDamage:DB$ DealDamage | ValidTgts$ Player.Opponent,Planeswalker | TgtPrompt$ Select target opponent or planeswalker. | NumDmg$ 2 +SVar:DBEffect:DB$ Effect | StaticAbilities$ ReduceCost | Triggers$ TrigCastSpell | SVars$ RemoveEffect| SpellDescription$ The next Giant spell you cast this turn costs {2} less to cast. +SVar:ReduceCost:Mode$ ReduceCost | EffectZone$ Command | Type$ Spell | ValidCard$ Giant | Activator$ You | Amount$ 2 +SVar:TrigCastSpell:Mode$ SpellCast | ValidCard$ Giant | ValidActivatingPlayer$ You | TriggerZones$ Command | Execute$ RemoveEffect | Static$ True +SVar:RemoveEffect:DB$ ChangeZone | Origin$ Command | Destination$ Exile +DeckHints:Type$Giant +Oracle:(As this Saga enters and after your draw step, add a lore counter. Sacrifice after III.)\nI - Scry 2.\nII - Draw a card. Then you may reveal a Giant card from your hand. When you do, Invasion of the Giants deals 2 damage to target opponent or planeswalker.\nIII - The next Giant spell you cast this turn costs {2} less to cast. diff --git a/forge-gui/res/cardsfolder/upcoming/iron_verdict.txt b/forge-gui/res/cardsfolder/upcoming/iron_verdict.txt new file mode 100644 index 00000000000..ec7be989360 --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/iron_verdict.txt @@ -0,0 +1,6 @@ +Name:Iron Verdict +ManaCost:2 W +Types:Instant +A:SP$ DealDamage | Cost$ 2 W | ValidTgts$ Creature.tapped | NumDmg$ 5 | TgtPrompt$ Select target tapped creature | SpellDescription$ CARDNAME deals 5 damage to target tapped creature. +K:Foretell:W +Oracle:Iron Verdict deals 5 damage to target tapped creature.\nForetell {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.) diff --git a/forge-gui/res/cardsfolder/upcoming/jarl_of_the_forsaken.txt b/forge-gui/res/cardsfolder/upcoming/jarl_of_the_forsaken.txt new file mode 100644 index 00000000000..fa9d430dcea --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/jarl_of_the_forsaken.txt @@ -0,0 +1,9 @@ +Name:Jarl of the Forsaken +ManaCost:3 B +Types:Creature Zombie Cleric +PT:3/2 +K:Flash +T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigDestroy | TriggerController$ TriggeredCardController | TriggerDescription$ When CARDNAME enters the battlefield, destroy target creature or planeswalker an opponent controls that was dealt damage this turn. +SVar:TrigDestroy:DB$ Destroy | ValidTgts$ Creature.OppCtrl+wasDealtDamageThisTurn,Planeswalker.OppCtrl+wasDealtDamageThisTurn | TgtPrompt$ Select target creature or planeswalker an opponent controls that was dealt damage this turn. +K:Foretell:1 B +Oracle:Flash\nWhen Jarl of the Forsaken enters the battlefield, destroy target creature or planeswalker an opponent controls that was dealt damage this turn.\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.) diff --git a/forge-gui/res/cardsfolder/upcoming/kardur_doomscourge.txt b/forge-gui/res/cardsfolder/upcoming/kardur_doomscourge.txt new file mode 100644 index 00000000000..249a3e428b6 --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/kardur_doomscourge.txt @@ -0,0 +1,11 @@ +Name:Kardur, Doomscourge +ManaCost:2 B R +Types:Legendary Creature Demon Berserker +PT:4/3 +T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigGoad | TriggerDescription$ When CARDNAME enters the battlefield, until your next turn, creatures your opponents control attack each combat if able and attack a player other than you if able. +SVar:TrigGoad:DB$ Goad | Defined$ Valid Creature.YouDontCtrl +T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | ValidCard$ Creature.attackingLKI | Execute$ TrigDrain | TriggerZones$ Battlefield | TriggerDescription$ Whenever an attacking creature dies, each opponent loses 1 life and you gain 1 life. +SVar:TrigDrain:DB$ LoseLife | Defined$ Player.Opponent | LifeAmount$ 1 | SubAbility$ DBGainOneLife +SVar:DBGainOneLife:DB$ GainLife | Defined$ You | LifeAmount$ 1 +DeckHas:Ability$LifeGain +Oracle:When Kardur Doomscourge enters the battlefield, until your next turn, creatures your opponents control attack each combat if able and attack a player other than you if able.\nWhenever an attacking creature dies, each opponent loses 1 life and you gain 1 life. diff --git a/forge-gui/res/cardsfolder/upcoming/kardurs_vicious_return.txt b/forge-gui/res/cardsfolder/upcoming/kardurs_vicious_return.txt new file mode 100644 index 00000000000..d966cc95d8c --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/kardurs_vicious_return.txt @@ -0,0 +1,13 @@ +Name:Kardur's Vicious Return +ManaCost:2 B R +Types:Enchantment Saga +K:Saga:3:DBSacrifice,DBDiscard,DBReturn +SVar:DBSacrifice:DB$ ImmediateTrigger | Execute$ TrigDealDamage | UnlessCost$ Sac<1/Creature> | UnlessPayer$ You | UnlessSwitched$ True | SpellDescription$ You may sacrifice a creature. When you do, CARDNAME deals 3 damage to any target. +SVar:TrigDealDamage:DB$ DealDamage | ValidTgts$ Creature,Player,Planeswalker | TgtPrompt$ Select any target. | NumDmg$ 3 +SVar:DBDiscard:DB$ Discard | Defined$ Player | NumCards$ 1 | Mode$ TgtChoose | SpellDescription$ Each player discards a card. +SVar:DBReturn:DB$ ChangeZone | Origin$ Graveyard | Destination$ Battlefield | TgtPrompt$ Choose target creature card in your graveyard | ValidTgts$ Creature.YouCtrl | RememberChanged$ True | SubAbility$ DBPutCounter | SpellDescription$ Return target creature card from your graveyard to the battlefield. Put a +1/+1 counter on it. It gains haste until your next turn. +SVar:DBPutCounter:DB$PutCounter | CounterType$ P1P1 | CounterNum$ 1 | Defined$ Remembered | SubAbility$ DBPump +SVar:DBPump:DB$ Pump | Defined$ Remembered | KW$ Haste | UntilYourNextTurn$ True | SubAbility$ DBCleanup +SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True +DeckHas:Ability$Counters +Oracle:(As this Saga enters and after your draw step, add a lore counter. Sacrifice after III.)\nI - You may sacrifice a creature. When you do, Kardur's Vicious Return deals 3 damage to any target.\nII - Each player discards a card.\nIII - Return target creature card from your graveyard to the battlefield. Put a +1/+1 counter on it. It gains haste until your next turn. diff --git a/forge-gui/res/cardsfolder/upcoming/karfell_harbinger.txt b/forge-gui/res/cardsfolder/upcoming/karfell_harbinger.txt new file mode 100644 index 00000000000..f9ce4c2de7b --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/karfell_harbinger.txt @@ -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. + diff --git a/forge-gui/res/cardsfolder/upcoming/kayas_onslaught.txt b/forge-gui/res/cardsfolder/upcoming/kayas_onslaught.txt new file mode 100644 index 00000000000..30338dfd80c --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/kayas_onslaught.txt @@ -0,0 +1,6 @@ +Name:Kaya's Onslaught +ManaCost:2 W +Types:Instant +A:SP$ Pump | Cost$ 2 W | ValidTgts$ Creature | TgtPrompt$ Select target creature | NumAtt$ 1 | NumDef$ 1 | KW$ Double Strike | SpellDescription$ Target creature gets +1/+1 and gains double strike until end of turn. | StackDescription$ SpellDescription +K:Foretell:W +Oracle:Target creature gets +1/+1 and gains double strike until end of turn.\nForetell {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.) diff --git a/forge-gui/res/cardsfolder/upcoming/kolvori_god_of_kinship_the_ringhart_crest.txt b/forge-gui/res/cardsfolder/upcoming/kolvori_god_of_kinship_the_ringhart_crest.txt new file mode 100644 index 00000000000..d6dbe16169d --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/kolvori_god_of_kinship_the_ringhart_crest.txt @@ -0,0 +1,18 @@ +Name:Kolvori, God of Kinship +ManaCost:2 G G +Types:Legendary Creature God +PT:2/2 +S:Mode$ Continuous | Affected$ Card.Self | AddPower$ 4 | AddToughness$ 2 | AddKeyword$ Vigilance | IsPresent$ Creature.Legendary+YouCtrl | PresentCompare$ GE3 | Description$ As long as you control three or more legendary creatures, CARDNAME gets +4/+2 and has vigilance. +A:AB$ Dig | Cost$ 1 G T | DigNum$ 6 | ChangeNum$ 1 | ChangeValid$ Creature.Legendary | Optional$ True | RestRandomOrder$ True | SpellDescription$ Look at the top six cards of your library. You may reveal a legendary creature card from among them and put it into your hand. Put the rest on the bottom of your library in a random order. +AlternateMode:Modal +Oracle:As long as you control three or more legendary creatures, Kolvori gets +4/+2 and has vigilance.\n{1}{G}, {T}: Look at the top six cards of your library. You may reveal a legendary creature card from among them and put it into your hand. Put the rest on the bottom of your library in a random order. + +ALTERNATE + +Name:The Ringhart Crest +ManaCost:1 G +Types:Legendary Artifact +K:ETBReplacement:Other:ChooseCT +SVar:ChooseCT:DB$ ChooseType | Defined$ You | Type$ Creature | AILogic$ MostProminentInComputerDeck | SpellDescription$ As CARDNAME enters the battlefield, choose a creature type. +A:AB$ Mana | Cost$ T | Produced$ G | RestrictValid$ Creature.ChosenType,Creature.Legendary | SpellDescription$ {T}: Add {G}. Spend this mana only to cast a creature spell of the chosen type or a legendary creature spell. +Oracle:As The Ringhart Crest enters the battlefield, choose a creature type.\n{T}: Add {G}. Spend this mana only to cast a creature spell of the chosen type or a legendary creature spell. diff --git a/forge-gui/res/cardsfolder/upcoming/mammoth_growth.txt b/forge-gui/res/cardsfolder/upcoming/mammoth_growth.txt new file mode 100644 index 00000000000..bf8ebc0d11b --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/mammoth_growth.txt @@ -0,0 +1,6 @@ +Name:Mammoth Growth +ManaCost:2 G +Types:Instant +A:SP$ Pump | Cost$ 2 G | ValidTgts$ Creature | TgtPrompt$ Select target creature | NumAtt$ +4 | NumDef$ +4 | SpellDescription$ Target creature gets +4/+4 until end of turn. +K:Foretell:G +Oracle:Target creature gets +4/+4 until end of turn.\nForetell {G} (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.) diff --git a/forge-gui/res/cardsfolder/upcoming/niko_defies_destiny.txt b/forge-gui/res/cardsfolder/upcoming/niko_defies_destiny.txt new file mode 100644 index 00000000000..ee84b4c33d6 --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/niko_defies_destiny.txt @@ -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. diff --git a/forge-gui/res/cardsfolder/upcoming/old_growth_troll.txt b/forge-gui/res/cardsfolder/upcoming/old_growth_troll.txt new file mode 100644 index 00000000000..f411bbd10fd --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/old_growth_troll.txt @@ -0,0 +1,14 @@ +Name:Old-Growth Troll +ManaCost:G G G +Types:Creature Troll Warrior +PT:4/4 +K:Trample +T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | ValidCard$ Card.Self+Creature | TriggerController$ TriggeredCardController | Execute$ DBReturn | TriggerDescription$ When CARDNAME dies, if it was a creature. return it to the battlefield. It's an Aura enchantment with enchant Forest you control and “Enchanted Forest has ‘{T}: Add {G}{G}’ and ‘{1}, {T}, Sacrifice this land: Create a tapped 4/4 green Troll Warrior creature token with trample.’” +SVar:DBReturn:DB$ ChangeZone | Defined$ TriggeredNewCardLKICopy | Origin$ Graveyard | Destination$ Battlefield | AnimateSubAbility$ DBAnimate +SVar:DBAnimate:DB$ Animate | Defined$ Remembered | Types$ Enchantment,Aura | RemoveCardTypes$ True | RemoveAllAbilities$ True | Keywords$ Enchant Forest you control | Abilities$ SPAttach | staticAbilities$ STAura | Permanent$ True +SVar:STAura:Mode$ Continuous | Affected$ Land.EnchantedBy | AddAbility$ ABMana & ABToken | Description$ Enchanted Forest has ‘{T}: Add {G}{G}’ and ‘{1}, {T}, Sacrifice this land: Create a tapped 4/4 green Troll Warrior creature token with trample.’ +SVar:SPAttach:SP$ Attach | Cost$ 0 | ValidTgts$ Forest.YouCtrl | AILogic$ Pump +SVar:ABMana:AB$ Mana | Cost$ T | Produced$ G | Amount$ 2 | SpellDescription$ Add {G}{G}. +SVar:ABToken:AB$ Token | Cost$ 1 T Sac<1/CARDNAME> | TokenAmount$1 | TokenScript$ g_4_4_troll_warrior_trample | TokenOwner$ You | LegacyImage$ g 4 4 troll warrior trample khm | TokenTapped$ True | SpellDescription$ Create a tapped 4/4 green Troll Warrior creature token with trample. +DeckHas:Ability$Token +Oracle:Trample\nWhen Old-Growth Troll dies, if it was a creature, return it to the battlefield. It’s an Aura enchantment with enchant Forest you control and “Enchanted Forest has ‘{T}: Add {G}{G}’ and ‘{1}, {T}, Sacrifice this land: Create a tapped 4/4 green Troll Warrior creature token with trample.’” diff --git a/forge-gui/res/cardsfolder/upcoming/poison_the_cup.txt b/forge-gui/res/cardsfolder/upcoming/poison_the_cup.txt new file mode 100644 index 00000000000..34124c59b81 --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/poison_the_cup.txt @@ -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.) + diff --git a/forge-gui/res/cardsfolder/upcoming/ravenform.txt b/forge-gui/res/cardsfolder/upcoming/ravenform.txt new file mode 100644 index 00000000000..b07c2402e35 --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/ravenform.txt @@ -0,0 +1,9 @@ +Name:Ravenform +ManaCost:2 U +Types:Sorcery +A:SP$ ChangeZone | Cost$ 2 U | ValidTgts$ Creature,Artifact | TgtPrompt$ Select target artifact or creature | Origin$ Battlefield | Destination$ Exile | SubAbility$ DBToken | AILogic$ Pongify | SpellDescription$ Exile target artifact or creature. Its controller creates a 1/1 Blue bird creature token with flying. +SVar:DBToken:DB$ Token | TokenAmount$ 1 | TokenScript$ u_1_1_bird_flying | TokenOwner$ TargetedController +DeckHas:Ability$Token +K:Foretell:U +DeckHas:Ability$Token +Oracle:Exile target artifact or creature. Its controller creates a 1/1 Blue bird creature token with flying.\nForetell {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.) diff --git a/forge-gui/res/cardsfolder/upcoming/reidane_god_of_the_worthy_valkmira_protectors_shield.txt b/forge-gui/res/cardsfolder/upcoming/reidane_god_of_the_worthy_valkmira_protectors_shield.txt index 689740e13d2..8f06d5959c5 100644 --- a/forge-gui/res/cardsfolder/upcoming/reidane_god_of_the_worthy_valkmira_protectors_shield.txt +++ b/forge-gui/res/cardsfolder/upcoming/reidane_god_of_the_worthy_valkmira_protectors_shield.txt @@ -6,9 +6,9 @@ K:Flying K:Vigilance R:Event$ Moved | ValidCard$ Land.Snow+OppCtrl | Destination$ Battlefield | ReplaceWith$ ETBTapped | ActiveZones$ Battlefield | Description$ Snow lands your opponents control enter the battlefield tapped. SVar:ETBTapped:DB$ ChangeZone | Origin$ All | Destination$ Battlefield | Tapped$ True | Defined$ ReplacedCard -S:Mode$ RaiseCost | ValidCard$ Card.cmcGE4 | Type$ Spell | Activator$ Opponent | Amount$ 2 | Description$ Spells your opponents cast with converted mana cost 4 or greater cost {2} more to cast. +S:Mode$ RaiseCost | ValidCard$ Card.nonCreature+cmcGE4 | Type$ Spell | Activator$ Opponent | Amount$ 2 | Description$ Noncreature spells your opponents cast with converted mana cost 4 or greater cost {2} more to cast. AlternateMode:Modal -Oracle:Flying, vigilance\nSnow lands your opponents control enter the battlefield tapped.\nSpells your opponents cast with converted mana cost 4 or greater cost {2} more to cast. +Oracle:Flying, vigilance\nSnow lands your opponents control enter the battlefield tapped.\nNoncreature spells your opponents cast with converted mana cost 4 or greater cost {2} more to cast. ALTERNATE diff --git a/forge-gui/res/cardsfolder/upcoming/return_upon_the_tide.txt b/forge-gui/res/cardsfolder/upcoming/return_upon_the_tide.txt new file mode 100644 index 00000000000..1bbf9714bd6 --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/return_upon_the_tide.txt @@ -0,0 +1,9 @@ +Name:Return Upon the Tide +ManaCost:4 B +Types:Sorcery +A:SP$ ChangeZone | Cost$ 4 B | Origin$ Graveyard | Destination$ Battlefield | TgtPrompt$ Choose target creature card in your graveyard | ValidTgts$ Creature.YouCtrl | SubAbility$ DBToken | SpellDescription$ Return target creature card from your graveyard to the battlefield. If it's an Elf, create two 1/1 green Elf Warrior creature tokens. +SVar:DBToken:DB$ Token | TokenAmount$ 2 | TokenScript$ g_1_1_elf_warrior | LegacyImage$ g 1 1 elf warrior khm | TokenOwner$ You | ConditionCheckSVar$ X | ConditionSVarCompare$ GE1 | References$ X +SVar:X:Targeted$Valid Elf +K:ForeTell:3 B +DeckHas:Ability$Token +Oracle:Return target creature card from your graveyard to the battlefield. If it's an Elf, create two 1/1 green Elf Warrior creature tokens.\nForetell {3}{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.) diff --git a/forge-gui/res/cardsfolder/upcoming/rise_of_the_dread_marn.txt b/forge-gui/res/cardsfolder/upcoming/rise_of_the_dread_marn.txt new file mode 100644 index 00000000000..2c531e807a2 --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/rise_of_the_dread_marn.txt @@ -0,0 +1,8 @@ +Name:Rise of the Dread Marn +ManaCost:2 B +Types:Instant +A:SP$ Token | Cost$ 2 B | LegacyImage$ b 2 2 zombie berserker khm | TokenAmount$ X | References$ X | TokenScript$ b_2_2_zombie_berserker | TokenOwner$ You | SpellDescription$ Create X 2/2 black Zombie Berserker creature tokens, where X is the number of nontoken creatures that died this turn. +SVar:X:Count$ThisTurnEntered_Graveyard_from_Battlefield_Creature.nonToken +K:Foretell:B +DeckHas:Ability$Token +Oracle:Create X 2/2 black Zombie Berserker creature tokens, where X is the number of nontoken creatures that died this turn.\nForetell {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.) diff --git a/forge-gui/res/cardsfolder/upcoming/roots_of_wisdom.txt b/forge-gui/res/cardsfolder/upcoming/roots_of_wisdom.txt new file mode 100644 index 00000000000..294a485d28a --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/roots_of_wisdom.txt @@ -0,0 +1,11 @@ +Name:Roots of Wisdom +ManaCost:1 G +Types:Sorcery +A:SP$ Mill | Cost$ 1 G | NumCards$ 3 | Defined$ You | SubAbility$ DBChangeZone | SpellDescription$ Mill three cards, then return a land card or Elf card from your graveyard to your hand. If you can't draw a card. +SVar:DBChangeZone:DB$ ChangeZone | Hidden$ True | Mandatory$ True | ChangeType$ Elf.YouOwn,Land.YouOwn | ChangeNum$ 1 | Origin$ Graveyard | Destination$ Hand | RememberChanged$ True | SubAbility$ DBDraw +SVar:DBDraw:DB$ Draw | Defined$ You | NumCards$ 1 | ConditionCheckSVar$ X | ConditionSVarCompare$ LT1 | SubAbility$ DBCleanup | References$ X +SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True +SVar:X:Remembered$Amount +DeckHints:Type$Elf +DeckHas:Ability$Mill +Oracle:Mill three cards, then return a land card or Elf card from your graveyard to your hand. If you can’t, draw a card. (To mill a card, put the top card of your library into your graveyard.) diff --git a/forge-gui/res/cardsfolder/upcoming/sarulfs_packmate.txt b/forge-gui/res/cardsfolder/upcoming/sarulfs_packmate.txt new file mode 100644 index 00000000000..141da1e87a3 --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/sarulfs_packmate.txt @@ -0,0 +1,8 @@ +Name:Sarulf's Packmate +ManaCost:3 G +Types:Creature Wolf +PT:3/3 +T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigDraw | TriggerDescription$ When CARDNAME enters the battlefield, draw a card. +SVar:TrigDraw:DB$ Draw | Defined$ You | NumCards$ 1 +K:Foretell:1 G +Oracle:When Sarulf's Packmate enters the battlefield, draw a card.\nForetell {1}{G} (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.) diff --git a/forge-gui/res/cardsfolder/upcoming/saw_it_coming.txt b/forge-gui/res/cardsfolder/upcoming/saw_it_coming.txt new file mode 100644 index 00000000000..c92c3bb8352 --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/saw_it_coming.txt @@ -0,0 +1,6 @@ +Name:Saw it Coming +ManaCost:1 U U +Types:Instant +A:SP$ Counter | Cost$ 1 U U | TargetType$ Spell | ValidTgts$ Card | SpellDescription$ Counter target spell. +K:Foretell:1 U +Oracle:Counter target spell.\nForetell {1}{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.) diff --git a/forge-gui/res/cardsfolder/upcoming/scorn_effigy.txt b/forge-gui/res/cardsfolder/upcoming/scorn_effigy.txt new file mode 100644 index 00000000000..3f249b40004 --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/scorn_effigy.txt @@ -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.) diff --git a/forge-gui/res/cardsfolder/upcoming/shackles_of_treachery.txt b/forge-gui/res/cardsfolder/upcoming/shackles_of_treachery.txt new file mode 100644 index 00000000000..5a2bf5fcea6 --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/shackles_of_treachery.txt @@ -0,0 +1,8 @@ +Name:Shackles of Treachery +ManaCost:2 R +Types:Sorcery +A:SP$ GainControl | Cost$ 2 R | ValidTgts$ Creature | TgtPrompt$ Select target creature | LoseControl$ EOT | Untap$ True | AddKWs$ Haste | SubAbility$ DBAnimate | SpellDescription$ Gain control of target creature until end of turn. Untap that creature. Until end of turn, it gains haste and "Whenever this creature deals damage, destroy target Equipment attached to it." +SVar:DBAnimate:DB$ Animate | Defined$ ParentTarget | Triggers$ Shackles | sVars$ TrigDestroy +SVar:Shackles:Mode$ DamageDone | ValidSource$ Card.Self | Execute$ TrigDestroy | TriggerDescription$ Whenever this creature deals damage, destroy target Equipment attached to it. +SVar:TrigDestroy:DB$ Destroy | ValidTgts$ Equipment.Attached | TgtPrompt$ Select target Equipment attached to the creature +Oracle:Gain control of target creature until end of turn. Untap that creature. Until end of turn, it gains haste and "Whenever this creature deals damage, destroy target Equipment attached to it." diff --git a/forge-gui/res/cardsfolder/upcoming/shepherd_of_the_cosmos.txt b/forge-gui/res/cardsfolder/upcoming/shepherd_of_the_cosmos.txt new file mode 100644 index 00000000000..afe001ec4dc --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/shepherd_of_the_cosmos.txt @@ -0,0 +1,9 @@ +Name:Shepherd of the Cosmos +ManaCost:4 W W +Types:Creature Angel Warrior +PT:3/3 +K:Flying +T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigChange | TriggerDescription$ When CARDNAME enters the battlefield, return target permanent card with converted mana cost 2 or less from your graveyard to the battlefield. +SVar:TrigChange:DB$ ChangeZone | Origin$ Graveyard | Destination$ Battlefield | ValidTgts$ Permanent.YouOwn+cmcLE2 | TgtPrompt$ Select target permanent card with converted mana cost 2 or less in your graveyard +K:Foretell:3 W +Oracle:Flying\nWhen Shepherd of the Cosmos enters the battlefield, return target permanent card with converted mana cost 2 or less from your graveyard to the battlefield.\nForetell {3}{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.) diff --git a/forge-gui/res/cardsfolder/upcoming/skull_raid.txt b/forge-gui/res/cardsfolder/upcoming/skull_raid.txt new file mode 100644 index 00000000000..254990c2567 --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/skull_raid.txt @@ -0,0 +1,10 @@ +Name:Skull Raid +ManaCost:3 B +Types:Sorcery +A:SP$ Discard | Cost$ 3 B | ValidTgts$ Player.Opponent | TgtPrompt$ Select target opponent | Mode$ TgtChoose | NumCards$ 2 | RememberDiscarded$ True | SubAbility$ DBDraw | SpellDescription$ Target opponent discards two cards. If fewer than two cards were discarded this way, you draw cards equal to the difference. +SVar:DBDraw:DB$ Draw | Defined$ You | NumCards$ X | References$ X,Y | SubAbility$ DBCleanup +SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True +SVar:X:Number$2/Minus.Y +SVar:Y:Count$RememberedSize +K:Foretell:1 B +Oracle:Target opponent discards two cards. If fewer than two cards were discarded this way, you draw cards equal to the difference. diff --git a/forge-gui/res/cardsfolder/upcoming/starnheim_unleashed.txt b/forge-gui/res/cardsfolder/upcoming/starnheim_unleashed.txt new file mode 100644 index 00000000000..84bc531f2b7 --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/starnheim_unleashed.txt @@ -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.) diff --git a/forge-gui/res/cardsfolder/upcoming/struggle_for_skemfar.txt b/forge-gui/res/cardsfolder/upcoming/struggle_for_skemfar.txt new file mode 100644 index 00000000000..683169e2094 --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/struggle_for_skemfar.txt @@ -0,0 +1,8 @@ +Name:Struggle for Skemfar +ManaCost:3 G +Types:Sorcery +A:SP$ PutCounter | Cost$ 3 G | AILogic$ Fight | ValidTgts$ Creature.YouCtrl | TgtPrompt$ Select target creature you control to put a +1/+1 counter | CounterType$ P1P1 | CounterNum$ 1 | SubAbility$ DBFight | SpellDescription$ Put a +1/+1 counter on target creature you control. Then that creature fights target creature you don't control. +SVar:DBFight:DB$ Fight | Defined$ ParentTarget | ValidTgts$ Creature.YouDontCtrl | AILogic$ Always | TgtPrompt$ Select target creature you don't control +DeckHas:Ability$Counters +K:Foretell:G +Oracle:Put a +1/+1 counter on target creature you control. Then that creature fights target creature you don't control. (Each deals damage equal to its power to the other.)\nForetell {G} (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.) diff --git a/forge-gui/res/cardsfolder/upcoming/tergrids_shadow.txt b/forge-gui/res/cardsfolder/upcoming/tergrids_shadow.txt new file mode 100644 index 00000000000..21bcc7b46da --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/tergrids_shadow.txt @@ -0,0 +1,8 @@ +Name:Tergrid's Shadow +ManaCost:3 B B +Types:Instant +A:SP$ Sacrifice | Cost$ 3 B B | Amount$ 2 | SacValid$ Creature | Defined$ Player | SpellDescription$ Each player sacrifices two creatures. | StackDescription$ SpellDescription +AI:RemoveDeck:All +K:Foretell:2 B B +DeckHas:Ability$Sacrifice +Oracle:Each player sacrifices two creatures.\nForetell {2}{B}{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.) diff --git a/forge-gui/res/cardsfolder/upcoming/the_bears_of_littjara.txt b/forge-gui/res/cardsfolder/upcoming/the_bears_of_littjara.txt new file mode 100644 index 00000000000..d9b64203a0f --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/the_bears_of_littjara.txt @@ -0,0 +1,13 @@ +Name:The Bears of Littjara +ManaCost:1 G U +Types:Enchantment Saga +K:Saga:3:DBToken,DBAnimate,DBGangUp +SVar:DBToken:DB$ Token | TokenAmount$ 1 | TokenScript$ u_2_2_shapeshifter_changeling | TokenOwner$ You | SpellDescription$ Create a 2/2 blue Shapeshifter creature token with changeling. +SVar:DBAnimate:DB$ Animate | ValidTgts$ Creature.Shapeshifter+YouCtrl | TargetMin$ 0 | TargetMax$ MaxTargets | TgtPrompt$ Select any number of Shapeshifter creatures you control | Power$ 4 | Toughness$ 4 | Permanent$ True | SpellDescription$ Any number of target Shapeshifter creatures you control have base power and toughness 4/4. +SVar:DBGangUp:DB$ Pump | ValidTgts$ Creature,Planeswalker | TargetMin$ 0 | TargetMax$ 1 | TgtPrompt$ Choose up to one target creature or planeswalker | ImprintCards$ Targeted | SubAbility$ DBRepeatEach +SVar:DBRepeatEach:DB$ RepeatEach | RepeatCards$ Creature.YouCtrl+powerGE4 | RepeatSubAbility$ DBDamage | DamageMap$ True | SubAbility$ DBCleanup | SpellDescription$ Choose up to one target creature or planeswalker. Each creature with power 4 or greater you control deals damage equal to its power to that permanent. +SVar:DBDamage:DB$ DealDamage | Defined$ Imprinted | DamageSource$ Remembered | NumDmg$ Z | References$ Z +SVar:MaxTargets:Count$Valid Creature.Shapeshifter+YouCtrl +SVar:Z:Remembered$CardPower +DeckHas:Ability$Token +Oracle:(As this Saga enters and after your draw step, add a lore counter. Sacrifice after III.)\nI — Create a 2/2 blue Shapeshifter creature token with changeling.\nII — Any number of target Shapeshifter creatures you control have base power and toughness 4/4.\nIII — Choose up to one target creature or planeswalker. Each creature with power 4 or greater you control deals damage equal to its power to that permanent. diff --git a/forge-gui/res/cardsfolder/upcoming/the_ravens_warning.txt b/forge-gui/res/cardsfolder/upcoming/the_ravens_warning.txt new file mode 100644 index 00000000000..3cf56a010a7 --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/the_ravens_warning.txt @@ -0,0 +1,13 @@ +Name:The Raven's Warning +ManaCost:1 W U +Types:Enchantment Saga +K:Saga:3:DBToken,DBEffect,DBWish +SVar:DBToken:DB$ Token | TokenAmount$ 1 | TokenScript$ u_1_1_bird_flying | TokenOwner$ You | SubAbility$ DBGainLife | SpellDescription$ Create a 1/1 blue Bird creature token with flying. You gain 2 life. +SVar:DBGainLife:DB$ GainLife | Defined$ You | LifeAmount$ 2 +SVar:DBEffect:DB$ Effect | Triggers$ TrigDamage | SpellDescription$ Whenever one or more creatures you control with flying deal combat damage to a player this turn, look at that player’s hand and draw a card. +SVar:TrigDamage:Mode$ DamageDoneOnce | ValidSource$ Creature.YouCtrl+withFlying | ValidTarget$ Player | CombatDamage$ True | Execute$ TrigPeek | TriggerDescription$ Whenever one or more creatures you control with flying deal combat damage to a player this turn, look at that player’s hand and draw a card. +SVar:TrigPeek:DB$ RevealHand | Defined$ TriggeredTarget | SubAbility$ DBDraw | SpellDescription$ Look at that player's hand. +SVar:DBDraw:DB$ Draw | Defined$ You | NumCards$ 1 | SpellDescription$ Draw a card. +SVar:DBWish:DB$ ChangeZone | Origin$ Sideboard | Destination$ Library | OptionalDecider$ You | ChangeType$ Card.YouOwn | ChangeNum$ 1 | Optional$ True | Hidden$ True | SpellDescription$ You may put a card you own from outside the game on top of your library. +DeckHas:Ability$Token +Oracle:(As this Saga enters and after your draw step, add a lore counter. Sacrifice after III.)\nI — Create a 1/1 blue Bird creature token with flying. You gain 2 life.\nII — Whenever one or more creatures you control with flying deal combat damage to a player this turn, look at that player’s hand and draw a card.\nIII — You may put a card you own from outside the game on top of your library. diff --git a/forge-gui/res/cardsfolder/upcoming/the_three_seasons.txt b/forge-gui/res/cardsfolder/upcoming/the_three_seasons.txt new file mode 100644 index 00000000000..3cd6195eff0 --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/the_three_seasons.txt @@ -0,0 +1,10 @@ +Name:The Three Seasons +ManaCost:G U +Types:Enchantment Saga +K:Saga:3:DBMill,DBChangeZone1,DBRepeatEach +SVar:DBMill:DB$ Mill | NumCards$ 3 | Defined$ You | SpellDescription$ Mill three cards. +SVar:DBChangeZone1:DB$ ChangeZone | Origin$ Graveyard | Destination$ Hand | TargetMin$ 0 | TargetMax$ 2 | TgtPrompt$ Choose target snow permanent cards in your graveyard | ValidTgts$ Permanent.Snow+YouOwn | SpellDescription$ Return up to two target snow permanent cards from your graveyard to your hand. +SVar:DBRepeatEach:DB$ RepeatEach | RepeatPlayers$ Player | RepeatSubAbility$ DBChangeZone2 | SpellDescription$ Choose three cards in each graveyard. Their owners shuffle those cards into their libraries. +SVar:DBChangeZone2:DB$ ChangeZone | Origin$ Graveyard | Destination$ Library | ChangeType$ Card.RememberedPlayerCtrl | DefinedPlayer$ Player.IsRemembered | Chooser$ You | ChangeNum$ 3 | Hidden$ True | Shuffle$ True | Mandatory$ True +DeckHas:Ability$Mill +Oracle:(As this Saga enters and after your draw step, add a lore counter. Sacrifice after III.)\nI - Mill three cards.\nII - Return up to two target snow permanent cards from your graveyard to your hand.\nIII - Choose three cards in each graveyard. Their owners shuffle those cards into their libraries. diff --git a/forge-gui/res/cardsfolder/upcoming/the_trickster_gods_heist.txt b/forge-gui/res/cardsfolder/upcoming/the_trickster_gods_heist.txt new file mode 100644 index 00000000000..502fe6ae380 --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/the_trickster_gods_heist.txt @@ -0,0 +1,11 @@ +Name:The Trickster-God's Heist +ManaCost:2 U B +Types:Enchantment Saga +K:Saga:3:DBCreature,DBNonCreature,DBDrain +SVar:DBCreature:DB$ ExchangeControl | ValidTgts$ Creature | TargetMin$ 2 | TargetMax$ 2 | TgtPrompt$ Choose two target creatures | Optional$ True | AILogic$ TrigTwoTargets | StackDescription$ SpellDescription | SpellDescription$ You may exchange control of two target creatures. +SVar:DBNonCreature:DB$ ExchangeControl | ValidTgts$ Card.nonBasic+nonCreature | TargetMin$ 2 | TargetMax$ 2 | ValidTgts$ Permanent | TgtPrompt$ Choose two target nonbasic, noncreature permanent | TargetsWithSameCardType$ True | Optional$ True | AILogic$ TrigTwoTargets | StackDescription$ SpellDescription | SpellDescription$ You may exchange control of two target nonbasic, noncreature permanents that share a card type. +SVar:DBDrain:DB$ LoseLife | ValidTgts$ Player | TgtPrompt$ Select a player | LifeAmount$ 3 | SubAbility$ DBGainLife | SpellDescription$ Target player loses 3 life and you gain 3 life. +SVar:DBGainLife:DB$ GainLife | Defined$ You | LifeAmount$ 3 +DeckHas:Ability$LifeGain +AI:RemoveDeck:Random +Oracle:I - You may exchange control of two target creatures.\nII - You may exchange control of two target nonbasic, noncreature permanents that share a card type.\nIII - Target player loses 3 life and you gain 3 life. diff --git a/forge-gui/res/cardsfolder/upcoming/vengeful_reaper.txt b/forge-gui/res/cardsfolder/upcoming/vengeful_reaper.txt new file mode 100644 index 00000000000..5ce29fefc40 --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/vengeful_reaper.txt @@ -0,0 +1,9 @@ +Name:Vengeful Reaper +ManaCost:3 B +Types:Creature Angel Cleric +PT:2/3 +K:Flying +K:Deathtouch +K:Haste +K:Foretell:1 B +Oracle:Flying, deathtouch, haste\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.) diff --git a/forge-gui/res/cardsfolder/upcoming/vorinclex_monstrous_raider.txt b/forge-gui/res/cardsfolder/upcoming/vorinclex_monstrous_raider.txt new file mode 100644 index 00000000000..2bdb56cc448 --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/vorinclex_monstrous_raider.txt @@ -0,0 +1,14 @@ +Name:Vorinclex, Monstrous Raider +ManaCost:4 G G +Types:Legendary Creature Phyrexian Praetor +PT:6/6 +K:Trample +K:Haste +R:Event$ AddCounter | ActiveZones$ Battlefield | ValidSource$ You | ValidObject$ Permanent.inZoneBattlefield,Player | ReplaceWith$ DoubleCounters | Description$ If you would put one or more counters on a permanent or player, put twice that many of each of those kinds of counters on that permanent or player instead. +SVar:DoubleCounters:DB$ ReplaceEffect | VarName$ CounterNum | VarValue$ X | References$ X +SVar:X:ReplaceCount$CounterNum/Twice +R:Event$ AddCounter | ActiveZones$ Battlefield | ValidSource$ Opponent | ValidObject$ Permanent.inZoneBattlefield,Player | ReplaceWith$ HalfCounters | Description$ If an opponent would put one or more counters on a permanent or player, they put half that many of each of those kinds of counters on that permanent or player instead, rounded down. +SVar:HalfCounters:DB$ ReplaceEffect | VarName$ CounterNum | VarValue$ Y | References$ Y +SVar:Y:ReplaceCount$CounterNum/HalfDown +Oracle:Trample, haste\nIf you would put one or more counters on a permanent or player, put twice that many of each of those kinds of counters on that permanent or player instead.\nIf an opponent would put one or more counters on a permanent or player, they put half that many of each of those kinds of counters on that permanent or player instead, rounded down. + diff --git a/forge-gui/res/cardsfolder/upcoming/waking_the_trolls.txt b/forge-gui/res/cardsfolder/upcoming/waking_the_trolls.txt new file mode 100644 index 00000000000..6c22a9890ca --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/waking_the_trolls.txt @@ -0,0 +1,13 @@ +Name:Waking the Trolls +ManaCost:4 R G +Types:Enchantment Saga +K:Saga:3:DBDestroy,DBChangeZone,DBPump +SVar:DBDestroy:DB$ Destroy | ValidTgts$ Land | TgtPrompt$ Choose target land. | SpellDescription$ Destroy target land. +SVar:DBChangeZone:DB$ ChangeZone | ValidTgts$ Land | TgtPrompt$ Choose target land card. | Origin$ Graveyard | Destination$ Battlefield | GainControl$ True | SpellDescription$ Put target land card from a graveyard onto the battlefield under your control. +SVar:DBPump:DB$ Pump | ValidTgts$ Player.Opponent | RememberTargets$ True | TgtPrompt$ Choose target player. | SubAbility$ DBToken | SpellDescription$ Choose target player. If they control fewer lands than you, create a number of 4/4 green Troll Warrior creature tokens with trample equal to the difference. +SVar:DBToken:DB$ Token | TokenAmount$ X | References$ X,Y | TokenScript$ g_4_4_troll_warrior_trample | TokenOwner$ You | SubAbility$ DBCleanup +SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True +SVar:X:Count$Valid Land.YouCtrl/Minus.Y +SVar:Y:Count$Valid Land.RememberedPlayerCtrl +DeckHas:Ability$Token +Oracle:(As this Saga enters and after your draw step, add a lore counter. Sacrifice after III.)\nI - Destroy target land.\nII - Put target land from a graveyard onto the battlefield under your control.\nIII - Choose target player. If they control fewer lands than you, create a number of 4/4 green Troll Warrior creature tokens with trample equal to the difference. diff --git a/forge-gui/res/cardsfolder/upcoming/warhorn_blast.txt b/forge-gui/res/cardsfolder/upcoming/warhorn_blast.txt new file mode 100644 index 00000000000..64a06f40ff3 --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/warhorn_blast.txt @@ -0,0 +1,6 @@ +Name:Warhorn Blast +ManaCost:4 W +Types:Instant +A:SP$ PumpAll | Cost$ 4 W | ValidCards$ Creature.YouCtrl | NumAtt$ +2 | NumDef$ +1 | SpellDescription$ Creatures you control get +2/+1 until end of turn. +K:Foretell:2 W +Oracle:Creatures you control get +2/+1 until end of turn.\nForetell {2}{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.) diff --git a/forge-gui/res/cardsfolder/upcoming/withercrown.txt b/forge-gui/res/cardsfolder/upcoming/withercrown.txt new file mode 100644 index 00000000000..f5cf548cfec --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/withercrown.txt @@ -0,0 +1,9 @@ +Name:Withercrown +ManaCost:1 B +Types:Enchantment Aura +K:Enchant creature +A:SP$ Attach | Cost$ 1 B | ValidTgts$ Creature | AILogic$ Curse +S:Mode$ Continuous | Affected$ Card.EnchantedBy | SetPower$ 0 | AddTrigger$ WitherTrig | AddSVar$ TrigLoseLife | Description$ Enchanted creature has base power 0 and has "At the beginning of your upkeep, you lose 1 life unless you sacrifice this creature." +SVar:WitherTrig:Mode$ Phase | Phase$ Upkeep | ValidPlayer$ You | Execute$ TrigLoseLife | TriggerDescription$ At the beginning of your upkeep, you lose 1 life unless you sacrifice this creature. +SVar:TrigLoseLife:DB$ LoseLife | LifeAmount$ 1 | UnlessCost$ Sac<1/CARDNAME> | UnlessPayer$ You | UnlessAI$ LifeLE10 +Oracle:Enchant creature\nEnchanted creature has base power 0 and has "At the beginning of your upkeep, you lose 1 life unless you sacrifice this creature." diff --git a/forge-gui/res/languages/de-DE.properties b/forge-gui/res/languages/de-DE.properties index afa090b5adb..0b41a3171c1 100644 --- a/forge-gui/res/languages/de-DE.properties +++ b/forge-gui/res/languages/de-DE.properties @@ -1769,6 +1769,8 @@ lblWinsClash=gewinnt Fehde lblLosesClash=verliert Fehde #CloneEffect.java lblDoYouWantCopy=Möchtest du {0} kopieren? +#ControlExchangeEffect.java +lblExchangeControl=Do you want to exchange control of {0} and {1}? #ControlExchangeVariantEffect.java lblChooseCards=Wähle Karten #CopyPermanentEffect.java @@ -1882,6 +1884,10 @@ lblDoYouWantSacrifice=Opfern durchführen? lblFaceDownCardCantTurnFaceUp=Verdeckte Karte kann nicht umgedreht werden #ShuffleEffect.java lblHaveTargetShuffle=Soll {0} mischen? +#SubgameEffect.java +lblSubgameStart=Unterspiel {0}s Effeckt gestartet. +lblSubgameEnd=Unterspiel beendet. {0} gewinnt. {1} verliert. +lblSubgameEndDraw=Das Unterspiel endete unentschieden. #SurveilEffect.java lblDoYouWantSurveil=Möchtest du Überwachen anwenden? #TapOrUntapAllEffect.java @@ -1937,6 +1943,7 @@ lblSideboardZone=Sideboard lblAnteZone=Ante lblSchemeDeckZone=Verschwörungsdeck lblPlanarDeckZone=Weltendeck +lblSubgameZone=Unterspiel lblNoneZone=Keine #BoosterDraft.java lblChooseBlock=Wähle Block diff --git a/forge-gui/res/languages/en-US.properties b/forge-gui/res/languages/en-US.properties index 55547849226..99b38444ceb 100644 --- a/forge-gui/res/languages/en-US.properties +++ b/forge-gui/res/languages/en-US.properties @@ -1769,6 +1769,8 @@ lblWinsClash=wins clash lblLosesClash=loses clash #CloneEffect.java lblDoYouWantCopy=Do you want to copy {0}? +#ControlExchangeEffect.java +lblExchangeControl=Do you want to exchange control of {0} and {1}? #ControlExchangeVariantEffect.java lblChooseCards=Choose cards #CopyPermanentEffect.java @@ -1882,6 +1884,10 @@ lblDoYouWantSacrifice=Do you want to sacrifice? lblFaceDownCardCantTurnFaceUp=Face-down card can''t turn face up #ShuffleEffect.java lblHaveTargetShuffle=Have {0} shuffle? +#SubgameEffect.java +lblSubgameStart=Subgame started by {0}''s effect. +lblSubgameEnd=Subgame ended. {0} wins. {1} loses. +lblSubgameEndDraw=Subgame ended in a draw. #SurveilEffect.java lblDoYouWantSurveil=Do you want to surveil? #TapOrUntapAllEffect.java @@ -1937,6 +1943,7 @@ lblSideboardZone=sideboard lblAnteZone=ante lblSchemeDeckZone=schemedeck lblPlanarDeckZone=planardeck +lblSubgameZone=subgame lblNoneZone=none #BoosterDraft.java lblChooseBlock=Choose Block diff --git a/forge-gui/res/languages/es-ES.properties b/forge-gui/res/languages/es-ES.properties index 769e28dba5b..820f6a45f28 100644 --- a/forge-gui/res/languages/es-ES.properties +++ b/forge-gui/res/languages/es-ES.properties @@ -1769,6 +1769,8 @@ lblWinsClash=gana el enfrentamiento lblLosesClash=pierde el enfrentamiento #CloneEffect.java lblDoYouWantCopy=¿Quieres copiar {0}? +#ControlExchangeEffect.java +lblExchangeControl=¿Quieres intercambiar de {0} y {1}? #ControlExchangeVariantEffect.java lblChooseCards=Elige las cartas #CopyPermanentEffect.java @@ -1882,6 +1884,10 @@ lblDoYouWantSacrifice=¿Quieres sacrificar? lblFaceDownCardCantTurnFaceUp=La carta boca abajo no se puede girar boca arriba #ShuffleEffect.java lblHaveTargetShuffle=¿Ha barajado {0}? +#SubgameEffect.java +lblSubgameStart=Subgame started by {0}''s effect. +lblSubgameEnd=Subgame ended. {0} wins. {1} loses. +lblSubgameEndDraw=Subgame ended in a draw. #SurveilEffect.java lblDoYouWantSurveil=¿Quieres vigilar? #TapOrUntapAllEffect.java @@ -1937,6 +1943,7 @@ lblSideboardZone=banquillo lblAnteZone=ante lblSchemeDeckZone=mazo scheme lblPlanarDeckZone=mazo planar +lblSubgameZone=subgame lblNoneZone=ninguna #BoosterDraft.java lblChooseBlock=Selecciona Bloque diff --git a/forge-gui/res/languages/it-IT.properties b/forge-gui/res/languages/it-IT.properties index 9dba07178e2..13cd1cabab3 100644 --- a/forge-gui/res/languages/it-IT.properties +++ b/forge-gui/res/languages/it-IT.properties @@ -1769,6 +1769,8 @@ lblWinsClash=wins clash lblLosesClash=loses clash #CloneEffect.java lblDoYouWantCopy=Do you want to copy {0}? +#ControlExchangeEffect.java +lblExchangeControl=Do you want to exchange control of {0} and {1}? #ControlExchangeVariantEffect.java lblChooseCards=Choose cards #CopyPermanentEffect.java @@ -1882,6 +1884,10 @@ lblDoYouWantSacrifice=Do you want to sacrifice? lblFaceDownCardCantTurnFaceUp=Face-down card can''t turn face up #ShuffleEffect.java lblHaveTargetShuffle=Have {0} shuffle? +#SubgameEffect.java +lblSubgameStart=Subgame started by {0}''s effect. +lblSubgameEnd=Subgame ended. {0} wins. {1} loses. +lblSubgameEndDraw=Subgame ended in a draw. #SurveilEffect.java lblDoYouWantSurveil=Do you want to surveil? #TapOrUntapAllEffect.java @@ -1937,6 +1943,7 @@ lblSideboardZone=sideboard lblAnteZone=ante lblSchemeDeckZone=schemedeck lblPlanarDeckZone=planardeck +lblSubgameZone=subgame lblNoneZone=none #BoosterDraft.java lblChooseBlock=Choose Block diff --git a/forge-gui/res/languages/zh-CN.properties b/forge-gui/res/languages/zh-CN.properties index 404c1b420e9..ad90c78c663 100644 --- a/forge-gui/res/languages/zh-CN.properties +++ b/forge-gui/res/languages/zh-CN.properties @@ -1769,6 +1769,8 @@ lblWinsClash=比点赢了 lblLosesClash=比点输了 #CloneEffect.java lblDoYouWantCopy=你想要复制{0}吗? +#ControlExchangeEffect.java +lblExchangeControl=Do you want to exchange control of {0} and {1}? #ControlExchangeVariantEffect.java lblChooseCards=选择牌 #CopyPermanentEffect.java @@ -1882,6 +1884,10 @@ lblDoYouWantSacrifice=你想牺牲吗? lblFaceDownCardCantTurnFaceUp=面朝下的牌不能面朝上 #ShuffleEffect.java lblHaveTargetShuffle={0}洗牌了吗? +#SubgameEffect.java +lblSubgameStart=Subgame started by {0}''s effect. +lblSubgameEnd=Subgame ended. {0} wins. {1} loses. +lblSubgameEndDraw=Subgame ended in a draw. #SurveilEffect.java lblDoYouWantSurveil=你想刺探吗? #TapOrUntapAllEffect.java @@ -1937,6 +1943,7 @@ lblSideboardZone=备牌 lblAnteZone=赌注牌区 lblSchemeDeckZone=魔王套牌 lblPlanarDeckZone=时空套牌 +lblSubgameZone=subgame lblNoneZone=空 #BoosterDraft.java lblChooseBlock=选择环境 diff --git a/forge-gui/res/tokenscripts/w_4_4_angel_warrior_flying_vigilance.txt b/forge-gui/res/tokenscripts/w_4_4_angel_warrior_flying_vigilance.txt new file mode 100644 index 00000000000..875421fce49 --- /dev/null +++ b/forge-gui/res/tokenscripts/w_4_4_angel_warrior_flying_vigilance.txt @@ -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 diff --git a/forge-gui/src/main/java/forge/control/FControlGameEventHandler.java b/forge-gui/src/main/java/forge/control/FControlGameEventHandler.java index 0a28a6fea00..d9a8ba3be7c 100644 --- a/forge-gui/src/main/java/forge/control/FControlGameEventHandler.java +++ b/forge-gui/src/main/java/forge/control/FControlGameEventHandler.java @@ -280,6 +280,21 @@ public class FControlGameEventHandler extends IGameEventVisitor.Base { return null; } + @Override + public Void visit(final GameEventSubgameEnd event) { + if (event.maingame != null) { + for (Player p : event.maingame.getPlayers()) { + updateZone(p, ZoneType.Battlefield); + updateZone(p, ZoneType.Hand); + updateZone(p, ZoneType.Graveyard); + updateZone(p, ZoneType.Exile); + updateZone(p, ZoneType.Command); + } + return processEvent(); + } + return null; + } + @Override public Void visit(final GameEventZone event) { if (event.player != null) { diff --git a/forge-gui/src/main/java/forge/match/AbstractGuiGame.java b/forge-gui/src/main/java/forge/match/AbstractGuiGame.java index 44a7a024ed8..4d90d993e5c 100644 --- a/forge-gui/src/main/java/forge/match/AbstractGuiGame.java +++ b/forge-gui/src/main/java/forge/match/AbstractGuiGame.java @@ -185,7 +185,7 @@ public abstract class AbstractGuiGame implements IGuiGame, IMayViewCards { return true; //if not in game, card can be shown } if(GuiBase.getInterface().isLibgdxPort()){ - if(gameView.isGameOver()) { + if(gameView != null && gameView.isGameOver()) { return true; } if(spectator!=null) { //workaround fix!! this is needed on above code or it will diff --git a/forge-gui/src/main/java/forge/match/HostedMatch.java b/forge-gui/src/main/java/forge/match/HostedMatch.java index 65b638a3131..ba2bd1f9c10 100644 --- a/forge-gui/src/main/java/forge/match/HostedMatch.java +++ b/forge-gui/src/main/java/forge/match/HostedMatch.java @@ -34,6 +34,7 @@ import forge.game.GameRules; import forge.game.GameType; import forge.game.GameView; import forge.game.Match; +import forge.game.event.*; import forge.game.player.Player; import forge.game.player.PlayerView; import forge.game.player.RegisteredPlayer; @@ -68,6 +69,7 @@ public class HostedMatch { private final MatchUiEventVisitor visitor = new MatchUiEventVisitor(); private final Map nextGameDecisions = Maps.newHashMap(); private boolean isMatchOver = false; + public int subGameCount = 0; public HostedMatch() {} @@ -332,7 +334,7 @@ public class HostedMatch { return isMatchOver; } - private final class MatchUiEventVisitor implements IUiEventVisitor { + private final class MatchUiEventVisitor extends IGameEventVisitor.Base implements IUiEventVisitor { @Override public Void visit(final UiEventBlockerAssigned event) { for (final PlayerControllerHuman humanController : humanControllers) { @@ -359,6 +361,77 @@ public class HostedMatch { return null; } + @Override + public Void visit(final GameEventSubgameStart event) { + subGameCount++; + event.subgame.subscribeToEvents(SoundSystem.instance); + event.subgame.subscribeToEvents(visitor); + + final GameView gameView = event.subgame.getView(); + + Runnable switchGameView = new Runnable() { + @Override + public void run() { + for (final Player p : event.subgame.getPlayers()) { + if (p.getController() instanceof PlayerControllerHuman) { + final PlayerControllerHuman humanController = (PlayerControllerHuman) p.getController(); + final IGuiGame gui = guis.get(p.getRegisteredPlayer()); + humanController.setGui(gui); + gui.setGameView(null); + gui.setGameView(gameView); + gui.setOriginalGameController(p.getView(), humanController); + gui.openView(new TrackableCollection<>(p.getView())); + gui.setGameView(null); + gui.setGameView(gameView); + event.subgame.subscribeToEvents(new FControlGameEventHandler(humanController)); + gui.message(event.message); + } + } + } + }; + if (GuiBase.getInterface().isLibgdxPort()) + GuiBase.getInterface().invokeInEdtNow(switchGameView); + else + GuiBase.getInterface().invokeInEdtAndWait(switchGameView); + + //ensure opponents set properly + for (final Player p : event.subgame.getPlayers()) { + p.updateOpponentsForView(); + } + + return null; + } + + @Override + public Void visit(final GameEventSubgameEnd event) { + final GameView gameView = event.maingame.getView(); + Runnable switchGameView = new Runnable() { + @Override + public void run() { + for (final Player p : event.maingame.getPlayers()) { + if (p.getController() instanceof PlayerControllerHuman) { + final PlayerControllerHuman humanController = (PlayerControllerHuman) p.getController(); + final IGuiGame gui = guis.get(p.getRegisteredPlayer()); + gui.setGameView(null); + gui.setGameView(gameView); + gui.setOriginalGameController(p.getView(), humanController); + gui.openView(new TrackableCollection<>(p.getView())); + gui.setGameView(null); + gui.setGameView(gameView); + gui.updatePhase(); + gui.message(event.message); + } + } + } + }; + if (GuiBase.getInterface().isLibgdxPort()) + GuiBase.getInterface().invokeInEdtNow(switchGameView); + else + GuiBase.getInterface().invokeInEdtAndWait(switchGameView); + + return null; + } + @Subscribe public void receiveEvent(final UiEvent evt) { try { @@ -368,6 +441,16 @@ public class HostedMatch { e.printStackTrace(); } } + + @Subscribe + public void receiveGameEvent(final GameEvent evt) { + try { + evt.visit(this); + } catch (Exception e) { + System.out.println(e.getMessage()); + e.printStackTrace(); + } + } } private void addNextGameDecision(final PlayerControllerHuman controller, final NextGameDecision decision) { diff --git a/forge-gui/src/main/java/forge/player/HumanPlay.java b/forge-gui/src/main/java/forge/player/HumanPlay.java index 5663e55fc40..ef27cf3dd2f 100644 --- a/forge-gui/src/main/java/forge/player/HumanPlay.java +++ b/forge-gui/src/main/java/forge/player/HumanPlay.java @@ -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; } diff --git a/forge-gui/src/main/java/forge/player/HumanPlaySpellAbility.java b/forge-gui/src/main/java/forge/player/HumanPlaySpellAbility.java index 0742977bca0..699ff204ed7 100644 --- a/forge-gui/src/main/java/forge/player/HumanPlaySpellAbility.java +++ b/forge-gui/src/main/java/forge/player/HumanPlaySpellAbility.java @@ -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,18 @@ 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.setHostCard(oldCard); } ability.clearTargets();