diff --git a/forge-ai/src/main/java/forge/ai/AiController.java b/forge-ai/src/main/java/forge/ai/AiController.java index d8728803d39..fbd5f8b6c96 100644 --- a/forge-ai/src/main/java/forge/ai/AiController.java +++ b/forge-ai/src/main/java/forge/ai/AiController.java @@ -730,7 +730,23 @@ public class AiController { if (sa.getCardState() != null && !sa.getHostCard().isInPlay() && sa.getCardState().getStateName() == CardStateName.Modal) { sa.getHostCard().setState(CardStateName.Modal, false); } + AiPlayDecision canPlay = canPlaySa(sa); // this is the "heaviest" check, which also sets up targets, defines X, etc. + + // Account for possible Ward after the spell is fully targeted + // TODO: ideally, this should be done while targeting, so that a different target can be preferred if the best + // one is warded and can't be paid for. + if (sa.usesTargeting()) { + for (Card tgt : sa.getTargets().getTargetCards()) { + if (tgt.hasKeyword(Keyword.WARD)) { + int amount = tgt.getKeywordMagnitude(Keyword.WARD); + if (amount > 0 && !ComputerUtilCost.canPayCost(sa, player)) { + return AiPlayDecision.CantAfford; + } + } + } + } + if (sa.getCardState() != null && !sa.getHostCard().isInPlay() && sa.getCardState().getStateName() == CardStateName.Modal) { sa.getHostCard().setState(CardStateName.Original, false); } @@ -1892,6 +1908,12 @@ public class AiController { return AbilityUtils.calculateAmount(source, source.getSVar("EnergyToPay"), sa); } else if ("Vermin".equals(logic)) { return MyRandom.getRandom().nextInt(Math.max(player.getLife() - 5, 0)); + } else if ("SweepCreatures".equals(logic)) { + int maxCreatures = 0; + for (Player opp : player.getOpponents()) { + maxCreatures = Math.max(maxCreatures, opp.getCreaturesInPlay().size()); + } + return Math.min(13, maxCreatures); } return max; } diff --git a/forge-ai/src/main/java/forge/ai/ComputerUtil.java b/forge-ai/src/main/java/forge/ai/ComputerUtil.java index 3b07e537b07..049aea47f54 100644 --- a/forge-ai/src/main/java/forge/ai/ComputerUtil.java +++ b/forge-ai/src/main/java/forge/ai/ComputerUtil.java @@ -452,7 +452,7 @@ public class ComputerUtil { int mana = ComputerUtilMana.getAvailableManaEstimate(ai, false); boolean cantAffordSoon = activate.getCMC() > mana + 1; - boolean wrongColor = !activate.determineColor().hasNoColorsExcept(ColorSet.fromNames(ComputerUtilCost.getAvailableManaColors(ai, ImmutableList.of())).getColor()); + boolean wrongColor = !activate.getColor().hasNoColorsExcept(ColorSet.fromNames(ComputerUtilCost.getAvailableManaColors(ai, ImmutableList.of())).getColor()); // Only do this for spells, not activated abilities // We can't pay for this spell even if we play another land, or have wrong colors diff --git a/forge-ai/src/main/java/forge/ai/ComputerUtilCard.java b/forge-ai/src/main/java/forge/ai/ComputerUtilCard.java index 538064d763f..81e504e62ed 100644 --- a/forge-ai/src/main/java/forge/ai/ComputerUtilCard.java +++ b/forge-ai/src/main/java/forge/ai/ComputerUtilCard.java @@ -904,7 +904,7 @@ public class ComputerUtilCard { } for (final Card crd : list) { - ColorSet color = crd.determineColor(); + ColorSet color = crd.getColor(); if (color.hasWhite()) map.get(0).setValue(Integer.valueOf(map.get(0).getValue()+1)); if (color.hasBlue()) map.get(1).setValue(Integer.valueOf(map.get(1).getValue()+1)); if (color.hasBlack()) map.get(2).setValue(Integer.valueOf(map.get(2).getValue()+1)); @@ -1676,7 +1676,7 @@ public class ComputerUtilCard { } } - pumped.addNewPT(c.getCurrentPower(), c.getCurrentToughness(), timestamp); + pumped.addNewPT(c.getCurrentPower(), c.getCurrentToughness(), timestamp, 0); pumped.setPTBoost(c.getPTBoostTable()); pumped.addPTBoost(power + berserkPower, toughness, timestamp, 0); diff --git a/forge-ai/src/main/java/forge/ai/ComputerUtilCost.java b/forge-ai/src/main/java/forge/ai/ComputerUtilCost.java index 6568034d767..cb37f9c4f10 100644 --- a/forge-ai/src/main/java/forge/ai/ComputerUtilCost.java +++ b/forge-ai/src/main/java/forge/ai/ComputerUtilCost.java @@ -589,6 +589,15 @@ public class ComputerUtilCost { } } + // Ward - will be accounted for when rechecking a targeted ability + if (sa.usesTargeting()) { + for (Card tgt : sa.getTargets().getTargetCards()) { + if (tgt.hasKeyword(Keyword.WARD)) { + extraManaNeeded += tgt.getKeywordMagnitude(Keyword.WARD); + } + } + } + // TODO: Alternate costs which involve both paying mana and tapping a card, e.g. Zahid, Djinn of the Lamp // Current AI decides on each part separately, thus making it possible for the AI to cheat by // tapping a mana source for mana and for the tap cost at the same time. Until this is improved, AI diff --git a/forge-ai/src/main/java/forge/ai/ComputerUtilMana.java b/forge-ai/src/main/java/forge/ai/ComputerUtilMana.java index 67d9e972357..66b2150eeac 100644 --- a/forge-ai/src/main/java/forge/ai/ComputerUtilMana.java +++ b/forge-ai/src/main/java/forge/ai/ComputerUtilMana.java @@ -1962,7 +1962,7 @@ public class ComputerUtilMana { if (!improvise) { for (ManaCostShard toPay : cost) { for (Card c : list) { - final int mask = c.determineColor().getColor() & toPay.getColorMask(); + final int mask = c.getColor().getColor() & toPay.getColorMask(); if (mask != 0) { convoked = c; convoke.put(c, toPay); diff --git a/forge-ai/src/main/java/forge/ai/SpecialCardAi.java b/forge-ai/src/main/java/forge/ai/SpecialCardAi.java index 2a95b1cda4f..459b4714a5d 100644 --- a/forge-ai/src/main/java/forge/ai/SpecialCardAi.java +++ b/forge-ai/src/main/java/forge/ai/SpecialCardAi.java @@ -21,6 +21,7 @@ import java.util.Arrays; import java.util.Collections; import java.util.List; +import forge.game.GameEntity; import org.apache.commons.lang3.tuple.Pair; import com.google.common.base.Predicate; @@ -616,6 +617,28 @@ public class SpecialCardAi { } } + // Goblin Polka Band + public static class GoblinPolkaBand { + public static boolean consider(final Player ai, final SpellAbility sa) { + int maxPotentialTgts = Lists.newArrayList(Iterables.filter(ai.getOpponents().getCreaturesInPlay(), CardPredicates.Presets.UNTAPPED)).size(); + int maxPotentialPayment = ComputerUtilMana.determineLeftoverMana(sa, ai, "R"); + + int numTgts = Math.min(maxPotentialPayment, maxPotentialTgts); + if (numTgts == 0) { + return false; + } + + // Set Announce + sa.getHostCard().setSVar("TgtNum", String.valueOf(numTgts)); + + // Simulate random targeting + List validTgts = sa.getTargetRestrictions().getAllCandidates(sa, true); + sa.resetTargets(); + sa.getTargets().addAll(Aggregates.random(validTgts, numTgts)); + return true; + } + } + // Guilty Conscience public static class GuiltyConscience { public static Card getBestAttachTarget(final Player ai, final SpellAbility sa, final List list) { @@ -1157,6 +1180,33 @@ public class SpecialCardAi { } } + // Power Struggle + public static class PowerStruggle { + public static boolean considerFirstTarget(final Player ai, final SpellAbility sa) { + Card firstTgt = (Card)Aggregates.random(sa.getTargetRestrictions().getAllCandidates(sa, true)); + if (firstTgt != null) { + sa.getTargets().add(firstTgt); + return true; + } else { + return false; + } + } + + public static boolean considerSecondTarget(final Player ai, final SpellAbility sa) { + Card firstTgt = sa.getParent().getTargetCard(); + Iterable candidates = Iterables.filter(ai.getOpponents().getCardsIn(ZoneType.Battlefield), + Predicates.and(CardPredicates.sharesCardTypeWith(firstTgt), CardPredicates.isTargetableBy(sa))); + Card secondTgt = Aggregates.random(candidates); + if (secondTgt != null) { + sa.resetTargets(); + sa.getTargets().add(secondTgt); + return true; + } else { + return false; + } + } + } + // Price of Progress public static class PriceOfProgress { public static boolean consider(final Player ai, final SpellAbility sa) { @@ -1251,6 +1301,51 @@ public class SpecialCardAi { } } + // Savior of Ollenbock + public static class SaviorOfOllenbock { + public static boolean consider(final Player ai, final SpellAbility sa) { + CardCollection oppTargetables = CardLists.getTargetableCards(ai.getOpponents().getCreaturesInPlay(), sa); + CardCollection threats = CardLists.filter(oppTargetables, new Predicate() { + @Override + public boolean apply(Card card) { + return !ComputerUtilCard.isUselessCreature(card.getController(), card); + } + }); + CardCollection ownTgts = CardLists.filter(ai.getCardsIn(ZoneType.Graveyard), CardPredicates.Presets.CREATURES); + + // TODO: improve the conditions for when the AI is considered threatened (check the possibility of being attacked?) + int lifeInDanger = (((PlayerControllerAi) ai.getController()).getAi().getIntProperty(AiProps.AI_IN_DANGER_THRESHOLD)); + boolean threatened = !threats.isEmpty() && ((ai.getLife() <= lifeInDanger && !ai.cantLoseForZeroOrLessLife()) || ai.getLifeLostLastTurn() + ai.getLifeLostThisTurn() > 0); + + if (threatened) { + sa.getTargets().add(ComputerUtilCard.getBestCreatureAI(threats)); + } else if (!ownTgts.isEmpty()) { + Card target = ComputerUtilCard.getBestCreatureAI(ownTgts); + sa.getTargets().add(target); + + int ownExiledValue = ComputerUtilCard.evaluateCreature(target), oppExiledValue = 0; + for (Card c : ai.getGame().getCardsIn(ZoneType.Exile)) { + if (c.getExiledWith() == sa.getHostCard()) { + if (c.getOwner() == ai) { + ownExiledValue += ComputerUtilCard.evaluateCreature(c); + } else { + oppExiledValue += ComputerUtilCard.evaluateCreature(c); + } + } + } + if (ownExiledValue > oppExiledValue + 150) { + sa.getHostCard().setSVar("SacMe", "5"); + } else { + sa.getHostCard().removeSVar("SacMe"); + } + } else if (!threats.isEmpty()) { + sa.getTargets().add(ComputerUtilCard.getBestCreatureAI(threats)); + } + + return sa.isTargetNumberValid(); + } + } + // Sorin, Vengeful Bloodlord public static class SorinVengefulBloodlord { public static boolean consider(final Player ai, final SpellAbility sa) { diff --git a/forge-ai/src/main/java/forge/ai/ability/AnimateAi.java b/forge-ai/src/main/java/forge/ai/ability/AnimateAi.java index 7f4e3e3a5b8..8fa5661bc1d 100644 --- a/forge-ai/src/main/java/forge/ai/ability/AnimateAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/AnimateAi.java @@ -5,6 +5,7 @@ import com.google.common.collect.Lists; import com.google.common.collect.Maps; import forge.ai.*; import forge.card.CardType; +import forge.card.ColorSet; import forge.game.Game; import forge.game.ability.AbilityUtils; import forge.game.ability.ApiType; @@ -442,16 +443,15 @@ public class AnimateAi extends SpellAbilityAi { } // colors to be added or changed to - String tmpDesc = ""; + ColorSet finalColors = ColorSet.getNullColor(); if (sa.hasParam("Colors")) { final String colors = sa.getParam("Colors"); if (colors.equals("ChosenColor")) { - tmpDesc = CardUtil.getShortColorsString(source.getChosenColors()); + finalColors = ColorSet.fromNames(source.getChosenColors()); } else { - tmpDesc = CardUtil.getShortColorsString(Lists.newArrayList(Arrays.asList(colors.split(",")))); + finalColors = ColorSet.fromNames(colors.split(",")); } } - final String finalDesc = tmpDesc; // abilities to add to the animated being final List abilities = Lists.newArrayList(); @@ -483,7 +483,7 @@ public class AnimateAi extends SpellAbilityAi { sVars.addAll(Arrays.asList(sa.getParam("sVars").split(","))); } - AnimateEffectBase.doAnimate(card, sa, power, toughness, types, removeTypes, finalDesc, + AnimateEffectBase.doAnimate(card, sa, power, toughness, types, removeTypes, finalColors, keywords, removeKeywords, hiddenKeywords, abilities, triggers, replacements, stAbs, timestamp); diff --git a/forge-ai/src/main/java/forge/ai/ability/ChangeZoneAi.java b/forge-ai/src/main/java/forge/ai/ability/ChangeZoneAi.java index 9db0a1417b0..2c22a67411d 100644 --- a/forge-ai/src/main/java/forge/ai/ability/ChangeZoneAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/ChangeZoneAi.java @@ -195,14 +195,16 @@ public class ChangeZoneAi extends SpellAbilityAi { */ @Override protected boolean doTriggerAINoCost(Player aiPlayer, SpellAbility sa, boolean mandatory) { + String aiLogic = sa.getParamOrDefault("AILogic", ""); + if (sa.isReplacementAbility() && "Command".equals(sa.getParam("Destination")) && "ReplacedCard".equals(sa.getParam("Defined"))) { // Process the commander replacement effect ("return to Command zone instead") return doReturnCommanderLogic(sa, aiPlayer); } - if ("Always".equals(sa.getParam("AILogic"))) { + if ("Always".equals(aiLogic)) { return true; - } else if ("IfNotBuffed".equals(sa.getParam("AILogic"))) { + } else if ("IfNotBuffed".equals(aiLogic)) { if (ComputerUtilCard.isUselessCreature(aiPlayer, sa.getHostCard())) { return true; // debuffed by opponent's auras to the level that it becomes useless } @@ -215,6 +217,8 @@ public class ChangeZoneAi extends SpellAbilityAi { } } return delta <= 0; + } else if ("SaviorOfOllenbock".equals(aiLogic)) { + return SpecialCardAi.SaviorOfOllenbock.consider(aiPlayer, sa); } if (sa.isHidden()) { diff --git a/forge-ai/src/main/java/forge/ai/ability/ChooseCardAi.java b/forge-ai/src/main/java/forge/ai/ability/ChooseCardAi.java index 849005b58a2..3690b981f2c 100644 --- a/forge-ai/src/main/java/forge/ai/ability/ChooseCardAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/ChooseCardAi.java @@ -24,6 +24,7 @@ import forge.game.card.CardPredicates.Presets; import forge.game.card.CounterEnumType; import forge.game.combat.Combat; import forge.game.keyword.Keyword; +import forge.game.phase.PhaseHandler; import forge.game.phase.PhaseType; import forge.game.player.Player; import forge.game.player.PlayerPredicates; @@ -146,6 +147,16 @@ public class ChooseCardAi extends SpellAbilityAi { return checkApiLogic(ai, sa); } + protected boolean checkPhaseRestrictions(Player ai, SpellAbility sa, PhaseHandler ph) { + String aiLogic = sa.getParamOrDefault("AILogic", ""); + + if (aiLogic.equals("AtOppEOT")) { + return ph.getNextTurn().equals(ai) && ph.is(PhaseType.END_OF_TURN); + } + + return super.checkPhaseRestrictions(ai, sa, ph); + } + /* (non-Javadoc) * @see forge.card.ability.SpellAbilityAi#chooseSingleCard(forge.card.spellability.SpellAbility, java.util.List, boolean) */ diff --git a/forge-ai/src/main/java/forge/ai/ability/ChooseGenericEffectAi.java b/forge-ai/src/main/java/forge/ai/ability/ChooseGenericEffectAi.java index 7bcc6b80dfb..433b1af574f 100644 --- a/forge-ai/src/main/java/forge/ai/ability/ChooseGenericEffectAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/ChooseGenericEffectAi.java @@ -27,6 +27,7 @@ import forge.game.combat.Combat; import forge.game.combat.CombatUtil; import forge.game.cost.Cost; import forge.game.keyword.Keyword; +import forge.game.phase.PhaseHandler; import forge.game.phase.PhaseType; import forge.game.player.Player; import forge.game.spellability.AbilitySub; @@ -52,6 +53,9 @@ public class ChooseGenericEffectAi extends SpellAbilityAi { } } else if ("GideonBlackblade".equals(aiLogic)) { return SpecialCardAi.GideonBlackblade.consider(ai, sa); + } else if ("AtOppEOT".equals(aiLogic)) { + PhaseHandler ph = ai.getGame().getPhaseHandler(); + return ph.is(PhaseType.END_OF_TURN) && ph.getNextTurn() == ai; } else if ("Always".equals(aiLogic)) { return true; } @@ -244,7 +248,7 @@ public class ChooseGenericEffectAi extends SpellAbilityAi { //if Iona does prevent from casting, allow it to draw for (final Card io : player.getCardsIn(ZoneType.Battlefield, "Iona, Shield of Emeria")) { - if (imprinted.determineColor().hasAnyColor(MagicColor.fromName(io.getChosenColor()))) { + if (imprinted.getColor().hasAnyColor(MagicColor.fromName(io.getChosenColor()))) { return allow; } } diff --git a/forge-ai/src/main/java/forge/ai/ability/ChooseNumberAi.java b/forge-ai/src/main/java/forge/ai/ability/ChooseNumberAi.java index 639baa92c40..2c5d603cfaa 100644 --- a/forge-ai/src/main/java/forge/ai/ability/ChooseNumberAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/ChooseNumberAi.java @@ -11,9 +11,21 @@ public class ChooseNumberAi extends SpellAbilityAi { @Override protected boolean canPlayAI(Player aiPlayer, SpellAbility sa) { - if (!sa.hasParam("AILogic")) { + String aiLogic = sa.getParamOrDefault("AILogic", ""); + + if (aiLogic.isEmpty()) { return false; + } else if (aiLogic.equals("SweepCreatures")) { + int ownCreatureCount = aiPlayer.getCreaturesInPlay().size(); + int oppMaxCreatureCount = 0; + for (Player opp : aiPlayer.getOpponents()) { + oppMaxCreatureCount = Math.max(oppMaxCreatureCount, opp.getCreaturesInPlay().size()); + } + + // TODO: maybe check if the AI is actually pressured and/or check the total value of the creatures on both sides of the board + return ownCreatureCount > oppMaxCreatureCount + 2 || ownCreatureCount < oppMaxCreatureCount; } + TargetRestrictions tgt = sa.getTargetRestrictions(); if (tgt != null) { sa.resetTargets(); 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 a951f670074..34089303a29 100644 --- a/forge-ai/src/main/java/forge/ai/ability/ControlExchangeAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/ControlExchangeAi.java @@ -4,6 +4,7 @@ import com.google.common.base.Predicate; import com.google.common.collect.Lists; import forge.ai.ComputerUtilCard; +import forge.ai.SpecialCardAi; import forge.ai.SpellAbilityAi; import forge.game.ability.AbilityUtils; import forge.game.card.Card; @@ -80,6 +81,10 @@ public class ControlExchangeAi extends SpellAbilityAi { final TargetRestrictions tgt = sa.getTargetRestrictions(); + if ("PowerStruggle".equals(sa.getParam("AILogic"))) { + return SpecialCardAi.PowerStruggle.considerSecondTarget(aiPlayer, sa); + } + // for TrigTwoTargets logic, only get the opponents' cards for the first target CardCollectionView unfilteredList = "TrigTwoTargets".equals(sa.getParam("AILogic")) ? aiPlayer.getOpponents().getCardsIn(ZoneType.Battlefield) : diff --git a/forge-ai/src/main/java/forge/ai/ability/PermanentCreatureAi.java b/forge-ai/src/main/java/forge/ai/ability/PermanentCreatureAi.java index bf81698f1b7..0e95d23db25 100644 --- a/forge-ai/src/main/java/forge/ai/ability/PermanentCreatureAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/PermanentCreatureAi.java @@ -143,6 +143,7 @@ public class PermanentCreatureAi extends PermanentAi { boolean willDiscardNow = isOwnEOT && ai.getCardsIn(ZoneType.Hand).size() > ai.getMaxHandSize(); boolean willDieNow = combat != null && ComputerUtilCombat.lifeInSeriousDanger(ai, combat); boolean wantToCastInMain1 = ph.is(PhaseType.MAIN1, ai) && ComputerUtil.castPermanentInMain1(ai, sa); + boolean isCommander = card.isCommander(); // figure out if the card might be a valuable blocker boolean valuableBlocker = false; @@ -178,6 +179,9 @@ public class PermanentCreatureAi extends PermanentAi { if (hasFloatMana || willDiscardNow || willDieNow) { // Will lose mana in pool or about to discard a card in cleanup or about to die in combat, so use this opportunity return true; + } else if (isCommander && isMyMain1OrLater) { + // Don't hold out specifically if this card is a commander, since otherwise it leads to stupid AI choices + return true; } else if (wantToCastInMain1) { // Would rather cast it in Main 1 or as soon as possible anyway, so go for it return isMyMain1OrLater; diff --git a/forge-ai/src/main/java/forge/ai/ability/PumpAi.java b/forge-ai/src/main/java/forge/ai/ability/PumpAi.java index 3446454bcde..8a1195e9443 100644 --- a/forge-ai/src/main/java/forge/ai/ability/PumpAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/PumpAi.java @@ -436,6 +436,11 @@ public class PumpAi extends PumpAiBase { final TargetRestrictions tgt = sa.getTargetRestrictions(); sa.resetTargets(); + + if ("PowerStruggle".equals(sa.getParam("AILogic"))) { + return SpecialCardAi.PowerStruggle.considerFirstTarget(ai, sa); + } + if (sa.hasParam("TargetingPlayer") && sa.getActivatingPlayer().equals(ai) && !sa.isTrigger()) { Player targetingPlayer = AbilityUtils.getDefinedPlayers(source, sa.getParam("TargetingPlayer"), sa).get(0); sa.setTargetingPlayer(targetingPlayer); diff --git a/forge-ai/src/main/java/forge/ai/ability/TapAi.java b/forge-ai/src/main/java/forge/ai/ability/TapAi.java index 295cc2f5a9b..3a088d45dba 100644 --- a/forge-ai/src/main/java/forge/ai/ability/TapAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/TapAi.java @@ -1,11 +1,6 @@ package forge.ai.ability; -import forge.ai.AiController; -import forge.ai.AiProps; -import forge.ai.ComputerUtil; -import forge.ai.ComputerUtilCost; -import forge.ai.PlayerControllerAi; -import forge.ai.SpellAbilityAi; +import forge.ai.*; import forge.game.ability.AbilityUtils; import forge.game.card.Card; import forge.game.cost.Cost; @@ -48,6 +43,10 @@ public class TapAi extends TapAiBase { final Card source = sa.getHostCard(); final Cost abCost = sa.getPayCosts(); + if ("GoblinPolkaBand".equals(sa.getParam("AILogic"))) { + return SpecialCardAi.GoblinPolkaBand.consider(ai, sa); + } + if (!ComputerUtilCost.checkDiscardCost(ai, abCost, source, sa)) { return false; } diff --git a/forge-ai/src/main/java/forge/ai/ability/TokenAi.java b/forge-ai/src/main/java/forge/ai/ability/TokenAi.java index 35398c0bf32..4c26284fea4 100644 --- a/forge-ai/src/main/java/forge/ai/ability/TokenAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/TokenAi.java @@ -98,7 +98,12 @@ public class TokenAi extends SpellAbilityAi { sa.getRootAbility().setXManaCostPaid(x); } if (x <= 0) { - return false; // 0 tokens or 0 toughness token(s) + if ("RandomPT".equals(sa.getParam("AILogic"))) { + // e.g. Necropolis of Azar - we're guaranteed at least 1 toughness from the ability + x = 1; + } else { + return false; // 0 tokens or 0 toughness token(s) + } } } diff --git a/forge-ai/src/main/java/forge/ai/simulation/GameCopier.java b/forge-ai/src/main/java/forge/ai/simulation/GameCopier.java index 49bfdd66e2d..53df565bc54 100644 --- a/forge-ai/src/main/java/forge/ai/simulation/GameCopier.java +++ b/forge-ai/src/main/java/forge/ai/simulation/GameCopier.java @@ -275,13 +275,10 @@ public class GameCopier { // TODO: Controllers' list with timestamps should be copied. zoneOwner = playerMap.get(c.getController()); newCard.setController(zoneOwner, 0); + + newCard.setPTTable(c.getSetPTTable()); + newCard.setPTCharacterDefiningTable(c.getSetPTCharacterDefiningTable()); - int setPower = c.getSetPower(); - int setToughness = c.getSetToughness(); - if (setPower != Integer.MAX_VALUE || setToughness != Integer.MAX_VALUE) { - // TODO: Copy the full list with timestamps. - newCard.addNewPT(setPower, setToughness, newGame.getNextTimestamp()); - } newCard.setPTBoost(c.getPTBoostTable()); newCard.setDamage(c.getDamage()); diff --git a/forge-core/src/main/java/forge/deck/Deck.java b/forge-core/src/main/java/forge/deck/Deck.java index c72c6ef732a..aaba92d218c 100644 --- a/forge-core/src/main/java/forge/deck/Deck.java +++ b/forge-core/src/main/java/forge/deck/Deck.java @@ -266,7 +266,7 @@ public class Deck extends DeckBase implements Iterable> cardsWithNoEdition = new EnumMap<>(DeckSection.class); + List mainCards = new ArrayList<>(); + for (Entry e: getMain()) + mainCards.add(e.getKey().getName()); + cardsWithNoEdition.put(DeckSection.Main, getAllCardNamesWithNoSpecifiedEdition(mainCards)); + optimiseCardArtSelectionInDeckSections(cardsWithNoEdition, false); + } private ArrayList getAllCardNamesWithNoSpecifiedEdition(List cardsInSection) { ArrayList cardNamesWithNoEdition = new ArrayList<>(); List> cardRequests = CardPool.processCardList(cardsInSection); @@ -364,7 +372,7 @@ public class Deck extends DeckBase implements Iterable> cardsWithNoEdition) { + private void optimiseCardArtSelectionInDeckSections(Map> cardsWithNoEdition, boolean multiArtPrint) { StaticData data = StaticData.instance(); // Get current Card Art Preference Settings boolean isCardArtPreferenceLatestArt = data.cardArtPreferenceIsLatest(); @@ -397,13 +405,13 @@ public class Deck extends DeckBase implements Iterable 1) addAlternativeCardPrintInPoolWithMultipleArt(card, pool, totalToAdd, artCount); else diff --git a/forge-game/src/main/java/forge/game/ForgeScript.java b/forge-game/src/main/java/forge/game/ForgeScript.java index 29659708ea5..4dbb76d9e7e 100644 --- a/forge-game/src/main/java/forge/game/ForgeScript.java +++ b/forge-game/src/main/java/forge/game/ForgeScript.java @@ -24,7 +24,7 @@ public class ForgeScript { public static boolean cardStateHasProperty(CardState cardState, String property, Player sourceController, Card source, CardTraitBase spellAbility) { final boolean isColorlessSource = cardState.getCard().hasKeyword("Colorless Damage Source", cardState); - final ColorSet colors = cardState.getCard().determineColor(cardState); + final ColorSet colors = cardState.getCard().getColor(cardState); if (property.contains("White") || property.contains("Blue") || property.contains("Black") || property.contains("Red") || property.contains("Green")) { boolean mustHave = !property.startsWith("non"); diff --git a/forge-game/src/main/java/forge/game/GameActionUtil.java b/forge-game/src/main/java/forge/game/GameActionUtil.java index c8ba4da5d92..1951861fb44 100644 --- a/forge-game/src/main/java/forge/game/GameActionUtil.java +++ b/forge-game/src/main/java/forge/game/GameActionUtil.java @@ -108,7 +108,7 @@ public final class GameActionUtil { if (lkicheck) { // double freeze tracker, so it doesn't update view game.getTracker().freeze(); - source.clearChangedCardKeywords(false); + source.clearStaticChangedCardKeywords(false); CardCollection preList = new CardCollection(source); game.getAction().checkStaticAbilities(false, Sets.newHashSet(source), preList); } @@ -189,6 +189,11 @@ public final class GameActionUtil { desc.append("(").append(inst.getReminderText()).append(")"); newSA.setDescription(desc.toString()); newSA.putParam("AfterDescription", "(Disturbed)"); + final String type = source.getAlternateState().getType().toString(); + if (!type.contains("Creature")) { + final String name = source.getAlternateState().getName(); + newSA.putParam("StackDescription", name + " — " + type + " (Disturbed)"); + } newSA.setAlternativeCost(AlternativeCost.Disturb); newSA.getRestrictions().setZone(ZoneType.Graveyard); @@ -361,7 +366,25 @@ public final class GameActionUtil { if (sa == null || !sa.isSpell()) { return costs; } - final Card source = sa.getHostCard(); + + Card source = sa.getHostCard(); + final Game game = source.getGame(); + boolean lkicheck = false; + + Card newHost = ((Spell)sa).getAlternateHost(source); + if (newHost != null) { + source = newHost; + lkicheck = true; + } + + if (lkicheck) { + // double freeze tracker, so it doesn't update view + game.getTracker().freeze(); + source.clearStaticChangedCardKeywords(false); + CardCollection preList = new CardCollection(source); + game.getAction().checkStaticAbilities(false, Sets.newHashSet(source), preList); + } + for (KeywordInterface inst : source.getKeywords()) { final String keyword = inst.getOriginal(); if (keyword.startsWith("Buyback")) { @@ -404,6 +427,16 @@ public final class GameActionUtil { // Surge while having OptionalCost is none of them } + + // reset static abilities + if (lkicheck) { + game.getAction().checkStaticAbilities(false); + // clear delayed changes, this check should not have updated the view + game.getTracker().clearDelayed(); + // need to unfreeze tracker + game.getTracker().unfreeze(); + } + return costs; } diff --git a/forge-game/src/main/java/forge/game/StaticEffect.java b/forge-game/src/main/java/forge/game/StaticEffect.java index 0ad39061d58..7afd723cef9 100644 --- a/forge-game/src/main/java/forge/game/StaticEffect.java +++ b/forge-game/src/main/java/forge/game/StaticEffect.java @@ -211,7 +211,7 @@ public class StaticEffect { // remove set P/T if (hasParam("SetPower") || hasParam("SetToughness")) { - affectedCard.removeNewPT(getTimestamp()); + affectedCard.removeNewPT(getTimestamp(), ability.getId()); } // remove P/T bonus @@ -261,7 +261,15 @@ public class StaticEffect { } if (hasParam("GainTextOf")) { - affectedCard.removeTextChangeState(getTimestamp()); + affectedCard.removeChangedName(getTimestamp(), ability.getId()); + affectedCard.removeChangedManaCost(getTimestamp(), ability.getId()); + affectedCard.removeColor(getTimestamp(), ability.getId()); + affectedCard.removeChangedCardTypes(getTimestamp(), ability.getId()); + affectedCard.removeChangedCardTraits(getTimestamp(), ability.getId()); + affectedCard.removeChangedCardKeywords(getTimestamp(), ability.getId()); + affectedCard.removeNewPT(getTimestamp(), ability.getId()); + + affectedCard.updateChangedText(); } if (hasParam("Goad")) { diff --git a/forge-game/src/main/java/forge/game/ability/AbilityUtils.java b/forge-game/src/main/java/forge/game/ability/AbilityUtils.java index dae0902bba0..df32df55445 100644 --- a/forge-game/src/main/java/forge/game/ability/AbilityUtils.java +++ b/forge-game/src/main/java/forge/game/ability/AbilityUtils.java @@ -165,6 +165,15 @@ public class AbilityUtils { c = sacrificed.getFirst().getEnchantingCard(); } } + } else if (defined.equals("TopOfGraveyard")) { + final CardCollectionView grave = hostCard.getController().getCardsIn(ZoneType.Graveyard); + + if (grave.size() > 0) { // TopOfLibrary or BottomOfLibrary + c = grave.getLast(); + } else { + // we don't want this to fall through and return the "Self" + return cards; + } } else if (defined.endsWith("OfLibrary")) { final CardCollectionView lib = hostCard.getController().getCardsIn(ZoneType.Library); @@ -1789,7 +1798,7 @@ public class AbilityUtils { if (sq[0].contains("HasNumChosenColors")) { int sum = 0; for (Card card : getDefinedCards(c, sq[1], sa)) { - sum += card.determineColor().getSharedColors(ColorSet.fromNames(c.getChosenColors())).countColors(); + sum += card.getColor().getSharedColors(ColorSet.fromNames(c.getChosenColors())).countColors(); } return sum; } @@ -1990,7 +1999,7 @@ public class AbilityUtils { // Count$CardMulticolor.. if (sq[0].contains("CardMulticolor")) { - final boolean isMulti = c.determineColor().isMulticolor(); + final boolean isMulti = c.getColor().isMulticolor(); return doXMath(Integer.parseInt(sq[isMulti ? 1 : 2]), expr, c, ctb); } // Count$Madness.. @@ -2046,7 +2055,7 @@ public class AbilityUtils { } if (sq[0].contains("CardNumColors")) { - return doXMath(c.determineColor().countColors(), expr, c, ctb); + return doXMath(c.getColor().countColors(), expr, c, ctb); } if (sq[0].contains("CardNumAttacksThisTurn")) { return doXMath(c.getDamageHistory().getCreatureAttacksThisTurn(), expr, c, ctb); @@ -2369,7 +2378,7 @@ public class AbilityUtils { final CardCollection list = CardLists.getValidCards(player.getCardsIn(ZoneType.Battlefield), rest, player, c, ctb); byte n = 0; for (final Card card : list) { - n |= card.determineColor().getColor(); + n |= card.getColor().getColor(); } return doXMath(ColorSet.fromMask(n).countColors(), expr, c, ctb); } @@ -2827,7 +2836,7 @@ public class AbilityUtils { final CardCollection list = CardLists.getValidCards(player.getCardsIn(ZoneType.Battlefield), rest, player, c, ctb); byte n = 0; for (final Card card : list) { - n |= card.determineColor().getColor(); + n |= card.getColor().getColor(); } return doXMath(ColorSet.fromMask(n).countColors(), expr, c, ctb); } @@ -3766,7 +3775,7 @@ public class AbilityUtils { someCards = CardLists.filter(someCards, new Predicate() { @Override public boolean apply(final Card c) { - return c.determineColor().isMulticolor(); + return c.getColor().isMulticolor(); } }); } @@ -3775,7 +3784,7 @@ public class AbilityUtils { someCards = CardLists.filter(someCards, new Predicate() { @Override public boolean apply(final Card c) { - return c.determineColor().isMonoColor(); + return c.getColor().isMonoColor(); } }); } 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 5935f0d8053..367123c77ff 100644 --- a/forge-game/src/main/java/forge/game/ability/SpellAbilityEffect.java +++ b/forge-game/src/main/java/forge/game/ability/SpellAbilityEffect.java @@ -462,7 +462,7 @@ public abstract class SpellAbilityEffect { final Card eff = new Card(game.nextCardId(), game); eff.setTimestamp(game.getNextTimestamp()); eff.setName(name); - eff.setColor(hostCard.determineColor().getColor()); + eff.setColor(hostCard.getColor().getColor()); // if name includes emblem then it should be one if (name.startsWith("Emblem")) { eff.setEmblem(true); diff --git a/forge-game/src/main/java/forge/game/ability/effects/AnimateAllEffect.java b/forge-game/src/main/java/forge/game/ability/effects/AnimateAllEffect.java index 44d0e0f8145..5a0168055f9 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/AnimateAllEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/AnimateAllEffect.java @@ -8,12 +8,12 @@ import com.google.common.collect.ImmutableList; import forge.GameCommand; import forge.card.CardType; +import forge.card.ColorSet; import forge.game.Game; import forge.game.ability.AbilityUtils; import forge.game.card.Card; import forge.game.card.CardCollectionView; import forge.game.card.CardLists; -import forge.game.card.CardUtil; import forge.game.event.GameEventCardStatsChanged; import forge.game.spellability.SpellAbility; import forge.game.zone.ZoneType; @@ -88,16 +88,15 @@ public class AnimateAllEffect extends AnimateEffectBase { } // colors to be added or changed to - String tmpDesc = ""; + ColorSet finalColors = ColorSet.getNullColor(); if (sa.hasParam("Colors")) { final String colors = sa.getParam("Colors"); if (colors.equals("ChosenColor")) { - tmpDesc = CardUtil.getShortColorsString(host.getChosenColors()); + finalColors = ColorSet.fromNames(host.getChosenColors()); } else { - tmpDesc = CardUtil.getShortColorsString(new ArrayList<>(Arrays.asList(colors.split(",")))); + finalColors = ColorSet.fromNames(colors.split(",")); } } - final String finalDesc = tmpDesc; // abilities to add to the animated being final List abilities = new ArrayList<>(); @@ -134,7 +133,7 @@ public class AnimateAllEffect extends AnimateEffectBase { list = CardLists.getValidCards(list, valid.split(","), host.getController(), host, sa); for (final Card c : list) { - doAnimate(c, sa, power, toughness, types, removeTypes, finalDesc, + doAnimate(c, sa, power, toughness, types, removeTypes, finalColors, keywords, removeKeywords, hiddenKeywords, abilities, triggers, replacements, ImmutableList.of(), timestamp); diff --git a/forge-game/src/main/java/forge/game/ability/effects/AnimateEffect.java b/forge-game/src/main/java/forge/game/ability/effects/AnimateEffect.java index 63f36f6a89e..8a09372f426 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/AnimateEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/AnimateEffect.java @@ -6,10 +6,10 @@ import java.util.List; import com.google.common.collect.Lists; import forge.card.CardType; +import forge.card.ColorSet; import forge.game.Game; import forge.game.ability.AbilityUtils; import forge.game.card.Card; -import forge.game.card.CardUtil; import forge.game.event.GameEventCardStatsChanged; import forge.game.spellability.SpellAbility; import forge.util.Lang; @@ -100,17 +100,15 @@ public class AnimateEffect extends AnimateEffectBase { } // colors to be added or changed to - String tmpDesc = ""; + ColorSet finalColors = ColorSet.getNullColor(); if (sa.hasParam("Colors")) { final String colors = sa.getParam("Colors"); if (colors.equals("ChosenColor")) { - - tmpDesc = CardUtil.getShortColorsString(source.getChosenColors()); + finalColors = ColorSet.fromNames(source.getChosenColors()); } else { - tmpDesc = CardUtil.getShortColorsString(Arrays.asList(colors.split(","))); + finalColors = ColorSet.fromNames(Arrays.asList(colors.split(","))); } } - final String finalDesc = tmpDesc; // abilities to add to the animated being final List abilities = Lists.newArrayList(); @@ -156,12 +154,12 @@ public class AnimateEffect extends AnimateEffectBase { } for (final Card c : tgts) { - doAnimate(c, sa, power, toughness, types, removeTypes, finalDesc, + doAnimate(c, sa, power, toughness, types, removeTypes, finalColors, keywords, removeKeywords, hiddenKeywords, abilities, triggers, replacements, stAbs, timestamp); if (sa.hasParam("Name")) { - c.addChangedName(sa.getParam("Name"), timestamp); + c.addChangedName(sa.getParam("Name"), false, timestamp, 0); } // give sVars diff --git a/forge-game/src/main/java/forge/game/ability/effects/AnimateEffectBase.java b/forge-game/src/main/java/forge/game/ability/effects/AnimateEffectBase.java index 1e3922d66d0..853e2b616e3 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/AnimateEffectBase.java +++ b/forge-game/src/main/java/forge/game/ability/effects/AnimateEffectBase.java @@ -23,6 +23,7 @@ import com.google.common.collect.Lists; import forge.GameCommand; import forge.card.CardType; +import forge.card.ColorSet; import forge.card.mana.ManaCost; import forge.card.mana.ManaCostParser; import forge.game.Game; @@ -42,7 +43,7 @@ import forge.game.trigger.TriggerHandler; public abstract class AnimateEffectBase extends SpellAbilityEffect { public static void doAnimate(final Card c, final SpellAbility sa, final Integer power, final Integer toughness, - final CardType addType, final CardType removeType, final String colors, + final CardType addType, final CardType removeType, final ColorSet colors, final List keywords, final List removeKeywords, final List hiddenKeywords, List abilities, final List triggers, final List replacements, final List stAbs, final long timestamp) { @@ -89,7 +90,7 @@ public abstract class AnimateEffectBase extends SpellAbilityEffect { } if ((power != null) || (toughness != null)) { - c.addNewPT(power, toughness, timestamp); + c.addNewPT(power, toughness, timestamp, 0); } if (!addType.isEmpty() || !removeType.isEmpty() || removeCreatureTypes) { @@ -159,7 +160,7 @@ public abstract class AnimateEffectBase extends SpellAbilityEffect { public void run() { doUnanimate(c, timestamp); - c.removeChangedName(timestamp); + c.removeChangedName(timestamp, 0); c.updateStateForView(); game.fireEvent(new GameEventCardStatsChanged(c)); @@ -218,7 +219,7 @@ public abstract class AnimateEffectBase extends SpellAbilityEffect { * a long. */ static void doUnanimate(final Card c, final long timestamp) { - c.removeNewPT(timestamp); + c.removeNewPT(timestamp, 0); c.removeChangedCardKeywords(timestamp, 0); diff --git a/forge-game/src/main/java/forge/game/ability/effects/CountersPutEffect.java b/forge-game/src/main/java/forge/game/ability/effects/CountersPutEffect.java index 6a1521b21a0..a24d1145cf0 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/CountersPutEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/CountersPutEffect.java @@ -390,6 +390,9 @@ public class CountersPutEffect extends SpellAbilityEffect { if (sa.hasParam("Adapt")) { game.getTriggerHandler().runTrigger(TriggerType.Adapt, AbilityKey.mapFromCard(gameCard), false); } + if (sa.hasParam("Training")) { + game.getTriggerHandler().runTrigger(TriggerType.Trains, AbilityKey.mapFromCard(gameCard), false); + } } else { // adding counters to something like re-suspend cards // etbcounter should apply multiplier diff --git a/forge-game/src/main/java/forge/game/ability/effects/FlipOntoBattlefieldEffect.java b/forge-game/src/main/java/forge/game/ability/effects/FlipOntoBattlefieldEffect.java index 8f37045d119..df956389c3c 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/FlipOntoBattlefieldEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/FlipOntoBattlefieldEffect.java @@ -98,13 +98,18 @@ public class FlipOntoBattlefieldEffect extends SpellAbilityEffect { } private Card getNeighboringCard(Card c, int direction) { - // Currently gets the nearest (in zone order) card to the left or to the right of the designated one by type + // Currently gets the nearest (in zone order) card to the left or to the right of the designated one by type, + // as well as cards attachments by the same controller that are visually located next to the requested card. Player controller = c.getController(); + ArrayList ownAttachments = Lists.newArrayList(); ArrayList cardsOTB = Lists.newArrayList(CardLists.filter( controller.getCardsIn(ZoneType.Battlefield), new Predicate() { @Override public boolean apply(Card card) { - if (c.isCreature()) { + if (card.isAttachedToEntity(c) && card.getController() == controller) { + ownAttachments.add(card); + return true; + } else if (c.isCreature()) { return card.isCreature(); } else if (c.isPlaneswalker() || c.isArtifact() || (c.isEnchantment() && !c.isAura())) { return card.isPlaneswalker() || card.isArtifact() || (c.isEnchantment() && !c.isAura()); @@ -118,6 +123,12 @@ public class FlipOntoBattlefieldEffect extends SpellAbilityEffect { } )); + // Chance to hit an attachment + float hitAttachment = 0.50f; + if (!ownAttachments.isEmpty() && direction < 0 && MyRandom.getRandom().nextFloat() <= hitAttachment) { + return Aggregates.random(ownAttachments); + } + int loc = cardsOTB.indexOf(c); if (direction < 0 && loc > 0) { return cardsOTB.get(loc - 1); diff --git a/forge-game/src/main/java/forge/game/ability/effects/LifeExchangeVariantEffect.java b/forge-game/src/main/java/forge/game/ability/effects/LifeExchangeVariantEffect.java index 2c02e77b736..47788a4544e 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/LifeExchangeVariantEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/LifeExchangeVariantEffect.java @@ -70,12 +70,12 @@ public class LifeExchangeVariantEffect extends SpellAbilityEffect { if ((pLife > num) && p.canLoseLife()) { final int diff = pLife - num; p.loseLife(diff, false, false); - source.addNewPT(power, toughness, timestamp); + source.addNewPT(power, toughness, timestamp, 0); game.fireEvent(new GameEventCardStatsChanged(source)); } else if ((num > pLife) && p.canGainLife()) { final int diff = num - pLife; p.gainLife(diff, source, sa); - source.addNewPT(power, toughness, timestamp); + source.addNewPT(power, toughness, timestamp, 0); game.fireEvent(new GameEventCardStatsChanged(source)); } else { // do nothing if they are equal diff --git a/forge-game/src/main/java/forge/game/ability/effects/ManaEffect.java b/forge-game/src/main/java/forge/game/ability/effects/ManaEffect.java index 01bae0efda1..64b22df15b2 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/ManaEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/ManaEffect.java @@ -189,7 +189,7 @@ public class ManaEffect extends SpellAbilityEffect { res, sa.getActivatingPlayer(), card, sa); byte colors = 0; for (Card c : list) { - colors |= c.determineColor().getColor(); + colors |= c.getColor().getColor(); } if (colors == 0) return; abMana.setExpressChoice(ColorSet.fromMask(colors)); diff --git a/forge-game/src/main/java/forge/game/ability/effects/PlayLandVariantEffect.java b/forge-game/src/main/java/forge/game/ability/effects/PlayLandVariantEffect.java index 5269c48def3..c963e7538a6 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/PlayLandVariantEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/PlayLandVariantEffect.java @@ -34,7 +34,7 @@ public class PlayLandVariantEffect extends SpellAbilityEffect { cards = Lists.newArrayList(Iterables.filter(cards, cpp)); } // current color of source card - final ColorSet color = source.determineColor(); + final ColorSet color = source.getColor(); if (color.isColorless()) { return; } diff --git a/forge-game/src/main/java/forge/game/ability/effects/PowerExchangeEffect.java b/forge-game/src/main/java/forge/game/ability/effects/PowerExchangeEffect.java index 6c11a8d4bbe..574f846f145 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/PowerExchangeEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/PowerExchangeEffect.java @@ -56,8 +56,8 @@ public class PowerExchangeEffect extends SpellAbilityEffect { final long timestamp = game.getNextTimestamp(); - c1.addNewPT(power2, null, timestamp); - c2.addNewPT(power1, null, timestamp); + c1.addNewPT(power2, null, timestamp, 0); + c2.addNewPT(power1, null, timestamp, 0); game.fireEvent(new GameEventCardStatsChanged(c1)); game.fireEvent(new GameEventCardStatsChanged(c2)); @@ -70,8 +70,8 @@ public class PowerExchangeEffect extends SpellAbilityEffect { @Override public void run() { - c1.removeNewPT(timestamp); - c2.removeNewPT(timestamp); + c1.removeNewPT(timestamp, 0); + c2.removeNewPT(timestamp, 0); game.fireEvent(new GameEventCardStatsChanged(c1)); game.fireEvent(new GameEventCardStatsChanged(c2)); } diff --git a/forge-game/src/main/java/forge/game/ability/effects/ProtectAllEffect.java b/forge-game/src/main/java/forge/game/ability/effects/ProtectAllEffect.java index 475a3801502..d157cde5960 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/ProtectAllEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/ProtectAllEffect.java @@ -64,7 +64,7 @@ public class ProtectAllEffect extends SpellAbilityEffect { } } else if (sa.getParam("Gains").equals("TargetedCardColor")) { for (final Card c : sa.getSATargetingCard().getTargets().getTargetCards()) { - ColorSet cs = c.determineColor(); + ColorSet cs = c.getColor(); for (byte col : MagicColor.WUBRG) { if (cs.hasAnyColor(col)) gains.add(MagicColor.toLongString(col).toLowerCase()); diff --git a/forge-game/src/main/java/forge/game/ability/effects/TokenEffectBase.java b/forge-game/src/main/java/forge/game/ability/effects/TokenEffectBase.java index 36d09b67f5f..05fd768625b 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/TokenEffectBase.java +++ b/forge-game/src/main/java/forge/game/ability/effects/TokenEffectBase.java @@ -171,7 +171,7 @@ public abstract class TokenEffectBase extends SpellAbilityEffect { } triggerList.put(ZoneType.None, moved.getZone().getZoneType(), moved); - creator.addTokensCreatedThisTurn(); + creator.addTokensCreatedThisTurn(tok); if (clone) { moved.setCloneOrigin(host); 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 634ab28de32..2af6c5fe698 100644 --- a/forge-game/src/main/java/forge/game/card/Card.java +++ b/forge-game/src/main/java/forge/game/card/Card.java @@ -125,19 +125,30 @@ public class Card extends GameEntity implements Comparable, IHasSVars { private final Map mayPlay = Maps.newHashMap(); // changes by AF animate and continuous static effects - // x=timestamp y=StaticAbility id - private final Table changedCardTypes = TreeBasedTable.create(); - private final NavigableMap changedCardNames = Maps.newTreeMap(); - private final Table changedCardKeywords = TreeBasedTable.create(); - // x=timestamp y=StaticAbility id - private final Table changedCardTraits = TreeBasedTable.create(); - private final Table changedCardColors = TreeBasedTable.create(); - private final Table changedCardTypesCharacterDefining = TreeBasedTable.create(); - private final Table changedCardColorsCharacterDefining = TreeBasedTable.create(); + protected CardChangedType changedTypeByText = null; // Layer 3 by Text Change + // x=timestamp y=StaticAbility id + private final Table changedCardTypesByText = TreeBasedTable.create(); // Layer 3 + private final Table changedCardTypesCharacterDefining = TreeBasedTable.create(); // Layer 4 CDA + private final Table changedCardTypes = TreeBasedTable.create(); // Layer 4 - private final NavigableMap clonedStates = Maps.newTreeMap(); - private final NavigableMap textChangeStates = Maps.newTreeMap(); + private final Table changedCardNames = TreeBasedTable.create(); // Layer 3 + private final Table changedCardKeywordsByText = TreeBasedTable.create(); // Layer 3 by Text Change + protected KeywordsChange changedCardKeywordsByWord = new KeywordsChange(ImmutableList.of(), null, false); // Layer 3 by Word Change + private final Table changedCardKeywords = TreeBasedTable.create(); // Layer 6 + + // x=timestamp y=StaticAbility id + private final Table changedCardTraitsByText = TreeBasedTable.create(); // Layer 3 by Text Change + private final Table changedCardTraits = TreeBasedTable.create(); // Layer 6 + + // x=timestamp y=StaticAbility id + private final Table changedCardColorsByText = TreeBasedTable.create(); // Layer 3 by Text Change + private final Table changedCardColorsCharacterDefining = TreeBasedTable.create(); // Layer 5 CDA + private final Table changedCardColors = TreeBasedTable.create(); // Layer 5 + + protected final Table changedCardManaCost = TreeBasedTable.create(); // Layer 3 + + private final NavigableMap clonedStates = Maps.newTreeMap(); // Layer 1 private final Map mayLook = Maps.newHashMap(); private final PlayerCollection mayLookFaceDownExile = new PlayerCollection(); @@ -155,9 +166,6 @@ public class Card extends GameEntity implements Comparable, IHasSVars { private final CardChangedWords changedTextColors = new CardChangedWords(); private final CardChangedWords changedTextTypes = new CardChangedWords(); - /** List of the keywords that have been added by text changes. */ - private final List keywordsGrantedByTextChanges = Lists.newArrayList(); - /** Original values of SVars changed by text changes. */ private Map originalSVars = Maps.newHashMap(); @@ -229,11 +237,11 @@ public class Card extends GameEntity implements Comparable, IHasSVars { private long timestamp = -1; // permanents on the battlefield // stack of set power/toughness - private Map> newPT = Maps.newTreeMap(); - private Map> newPTCharacterDefining = Maps.newTreeMap(); - - // x=timestamp, y=Static Ability id or 0 - private Table> boostPT = TreeBasedTable.create(); + // x=timestamp y=StaticAbility id + private Table> newPTText = TreeBasedTable.create(); // Text Change Layer 3 + private Table> newPTCharacterDefining = TreeBasedTable.create(); // Layer 7a + private Table> newPT = TreeBasedTable.create(); // Layer 7b + private Table> boostPT = TreeBasedTable.create(); // Layer 7c private String oracleText = ""; @@ -394,18 +402,9 @@ public class Card extends GameEntity implements Comparable, IHasSVars { } public CardState getState(final CardStateName state) { - return getState(state, false); - } - public CardState getState(final CardStateName state, boolean skipTextChange) { if (state == CardStateName.FaceDown) { return getFaceDownState(); } - if (!skipTextChange) { - CardCloneStates txtStates = getLastTextChangeState(); - if (txtStates != null) { - return txtStates.get(state); - } - } CardCloneStates clStates = getLastClonedState(); if (clStates == null) { return getOriginalState(state); @@ -455,25 +454,16 @@ public class Card extends GameEntity implements Comparable, IHasSVars { // faceDown has higher priority over clone states // while text change states doesn't apply while the card is faceDown if (state != CardStateName.FaceDown) { - CardCloneStates textChangeStates = getLastTextChangeState(); - - if (textChangeStates != null) { - if (!textChangeStates.containsKey(state)) { - throw new RuntimeException(getName() + " tried to switch to non-existant text change state \"" + state + "\"!"); + CardCloneStates cloneStates = getLastClonedState(); + if (cloneStates != null) { + if (!cloneStates.containsKey(state)) { + throw new RuntimeException(getName() + " tried to switch to non-existant cloned state \"" + state + "\"!"); //return false; // Nonexistant state. } } else { - CardCloneStates cloneStates = getLastClonedState(); - if (cloneStates != null) { - if (!cloneStates.containsKey(state)) { - throw new RuntimeException(getName() + " tried to switch to non-existant cloned state \"" + state + "\"!"); - //return false; // Nonexistant state. - } - } else { - if (!states.containsKey(state)) { - System.out.println(getName() + " tried to switch to non-existant state \"" + state + "\"!"); - return false; // Nonexistant state. - } + if (!states.containsKey(state)) { + System.out.println(getName() + " tried to switch to non-existant state \"" + state + "\"!"); + return false; // Nonexistant state. } } } @@ -500,9 +490,8 @@ public class Card extends GameEntity implements Comparable, IHasSVars { if (!changedCardTypes.isEmpty()) { currentState.getView().updateType(currentState); } - if (!changedCardColors.isEmpty()) { - currentState.getView().updateColors(this); - } + updateColorForView(); + if (!changedCardKeywords.isEmpty()) { updateKeywords(); } @@ -616,30 +605,36 @@ public class Card extends GameEntity implements Comparable, IHasSVars { return retResult; - } else if (mode.equals("Flip") && (isFlipCard() || hasMergedCard())) { + } else if (mode.equals("Flip")) { // 709.4. Flipping a permanent is a one-way process. if (isFlipped()) { return false; } - // Need to remove mutated states, otherwise the changeToState() will fail - if (hasMergedCard()) { - removeMutatedStates(); - } - CardCollectionView cards = hasMergedCard() ? getMergedCards() : new CardCollection(this); boolean retResult = false; - for (final Card c : cards) { - c.flipped = true; - // a facedown card does flip but the state doesn't change - if (c.facedown) continue; + if (isFlipCard() || hasMergedCard()) { + // Need to remove mutated states, otherwise the changeToState() will fail + if (hasMergedCard()) { + removeMutatedStates(); + } + CardCollectionView cards = hasMergedCard() ? getMergedCards() : new CardCollection(this); + for (final Card c : cards) { + c.flipped = true; + // a facedown card does flip but the state doesn't change + if (c.facedown) continue; - boolean result = c.changeToState(CardStateName.Flipped); - retResult = retResult || result; - } - if (hasMergedCard()) { - rebuildMutatedStates(cause); - game.getTriggerHandler().clearActiveTriggers(this, null); - game.getTriggerHandler().registerActiveTrigger(this, false); + boolean result = c.changeToState(CardStateName.Flipped); + retResult = retResult || result; + } + if (hasMergedCard()) { + rebuildMutatedStates(cause); + game.getTriggerHandler().clearActiveTriggers(this, null); + game.getTriggerHandler().registerActiveTrigger(this, false); + } + } else { + // a card without flip state can still flip rules vise + retResult = true; + this.flipped = true; } return retResult; } else if (mode.equals("TurnFace")) { @@ -823,10 +818,25 @@ public class Card extends GameEntity implements Comparable, IHasSVars { } public final String getName(CardState state) { - if (changedCardNames.isEmpty()) { - return state.getName(); + String name = state.getName(); + for (CardChangedName change : this.changedCardNames.values()) { + if (change.isOverwrite()) { + name = change.getNewName(); + } } - return changedCardNames.lastEntry().getValue(); + return name; + } + + public final boolean hasNonLegendaryCreatureNames() { + boolean result = false; + for (CardChangedName change : this.changedCardNames.values()) { + if (change.isOverwrite()) { + result = false; + } else if (change.isAddNonLegendaryCreatureNames()) { + result = true; + } + } + return result; } @Override @@ -834,29 +844,35 @@ public class Card extends GameEntity implements Comparable, IHasSVars { currentState.setName(name0); } - public void addChangedName(final String name0, Long timestamp) { - changedCardNames.put(timestamp, name0); + public void addChangedName(final String name0, boolean addNonLegendaryCreatureNames, long timestamp, long staticId) { + changedCardNames.put(timestamp, staticId, new CardChangedName(name0, addNonLegendaryCreatureNames)); updateNameforView(); } - public void removeChangedName(Long timestamp) { - if (changedCardNames.remove(timestamp) != null) { + public void removeChangedName(long timestamp, long staticId) { + if (changedCardNames.remove(timestamp, staticId) != null) { updateNameforView(); } } + public boolean clearChangedName() { + boolean changed = !changedCardNames.isEmpty(); + changedCardNames.clear(); + return changed; + } + public void updateNameforView() { currentState.getView().updateName(currentState); } - public Map getChangedCardNames() { - return Collections.unmodifiableMap(changedCardNames); + public Table getChangedCardNames() { + return changedCardNames; } - public void setChangedCardNames(Map changedCardNames) { + public void setChangedCardNames(Table changedCardNames) { this.changedCardNames.clear(); - for (Entry entry : changedCardNames.entrySet()) { - this.changedCardNames.put(entry.getKey(), entry.getValue()); + for (Table.Cell entry : changedCardNames.cellSet()) { + this.changedCardNames.put(entry.getRowKey(), entry.getColumnKey(), entry.getValue()); } } @@ -1220,17 +1236,14 @@ public class Card extends GameEntity implements Comparable, IHasSVars { } public void updateTriggers(List list, CardState state) { - if (hasRemoveIntrinsic()) { - list.clear(); - } - - for (final CardTraitChanges ck : changedCardTraits.values()) { + for (final CardTraitChanges ck : getChangedCardTraitsList(state)) { if (ck.isRemoveAll()) { list.clear(); } list.addAll(ck.getTriggers()); } + // Keywords are already sorted by Layer for (KeywordInterface kw : getUnhiddenKeywords(state)) { list.addAll(kw.getTriggers()); } @@ -1639,10 +1652,25 @@ public class Card extends GameEntity implements Comparable, IHasSVars { public final void setManaCost(final ManaCost s) { currentState.setManaCost(s); } - public final ManaCost getManaCost() { + public final ManaCost getOriginalManaCost() { return currentState.getManaCost(); } + public final ManaCost getManaCost() { + ManaCost result = getOriginalManaCost(); + for (ManaCost mc : changedCardManaCost.values()) { + result = mc; + } + return result; + } + + public void addChangedManaCost(ManaCost cost, long timestamp, long staticId) { + changedCardManaCost.put(timestamp, staticId, cost); + } + public boolean removeChangedManaCost(long timestamp, long staticId) { + return changedCardManaCost.remove(timestamp, staticId) != null; + } + public final boolean hasChosenPlayer() { return chosenPlayer != null; } @@ -1887,7 +1915,6 @@ public class Card extends GameEntity implements Comparable, IHasSVars { sbLong.append(p[2]).append("\r\n"); } else if (keyword.equals("Unblockable")) { sbLong.append(getName()).append(" can't be blocked.\r\n"); - } else if (keyword.equals("AllNonLegendaryCreatureNames")) { sbLong.append(getName()).append(" has all names of nonlegendary creature cards.\r\n"); } else if (keyword.startsWith("IfReach")) { String[] k = keyword.split(":"); @@ -1916,10 +1943,6 @@ public class Card extends GameEntity implements Comparable, IHasSVars { final StringBuilder sb = new StringBuilder(); final StringBuilder sbLong = new StringBuilder(); - // Prepare text changes - final Set> textChanges = Sets.union( - changedTextColors.toMap().entrySet(), changedTextTypes.toMap().entrySet()); - List printedKW = new ArrayList(); int i = 0; @@ -1929,19 +1952,6 @@ public class Card extends GameEntity implements Comparable, IHasSVars { if (keyword.startsWith("SpellCantTarget")) { continue; } - // format text changes - if (CardUtil.isKeywordModifiable(keyword) - && keywordsGrantedByTextChanges.contains(inst)) { - for (final Entry e : textChanges) { - final String value = e.getValue(); - if (keyword.contains(value)) { - keyword = TextUtil.fastReplace(keyword, value, - TextUtil.concatNoSpace("", e.getKey(), " ", value)); - // assume (for now) max one change per keyword - break; - } - } - } if (keyword.startsWith("CantBeCounteredBy")) { final String[] p = keyword.split(":"); sbLong.append(p[2]).append("\r\n"); @@ -2124,7 +2134,7 @@ public class Card extends GameEntity implements Comparable, IHasSVars { || keyword.equals("Horsemanship") || keyword.equals("Infect")|| keyword.equals("Persist") || keyword.equals("Phasing") || keyword.equals("Shadow")|| keyword.equals("Skulk") || keyword.equals("Undying") || keyword.equals("Wither") || keyword.equals("Cascade") - || keyword.equals("Mentor")) { + || keyword.equals("Mentor") || keyword.equals("Training")) { if (sb.length() != 0) { sb.append("\r\n"); } @@ -2168,8 +2178,6 @@ public class Card extends GameEntity implements Comparable, IHasSVars { // keyword parsing takes care of adding a proper description } else if (keyword.equals("Unblockable")) { sbLong.append(getName()).append(" can't be blocked.\r\n"); - } else if (keyword.equals("AllNonLegendaryCreatureNames")) { - sbLong.append(getName()).append(" has all names of nonlegendary creature cards.\r\n"); } else if (keyword.startsWith("IfReach")) { String[] k = keyword.split(":"); sbLong.append(getName()).append(" can block ") @@ -2827,7 +2835,7 @@ public class Card extends GameEntity implements Comparable, IHasSVars { } public boolean hasRemoveIntrinsic() { - for (final CardChangedType ct : this.changedCardTypes.values()) { + for (final CardChangedType ct : getChangedCardTypes()) { if (ct.isRemoveLandTypes()) { return true; } @@ -2857,16 +2865,7 @@ public class Card extends GameEntity implements Comparable, IHasSVars { } public void updateSpellAbilities(List list, CardState state, Boolean mana) { - if (hasRemoveIntrinsic()) { - list.clear(); - } - - // do Basic Land Abilities there - if (null == mana || true == mana) { - updateBasicLandAbilities(list, state); - } - - for (final CardTraitChanges ck : changedCardTraits.values()) { + for (final CardTraitChanges ck : getChangedCardTraitsList(state)) { if (ck.isRemoveNonMana()) { // List only has nonMana if (null == mana) { @@ -2908,6 +2907,7 @@ public class Card extends GameEntity implements Comparable, IHasSVars { } } + // keywords should already been cleanup by layers for (KeywordInterface kw : getUnhiddenKeywords(state)) { for (SpellAbility sa : kw.getAbilities()) { if (mana == null || mana == sa.isManaAbility()) { @@ -2941,10 +2941,6 @@ public class Card extends GameEntity implements Comparable, IHasSVars { } } - public final Iterable getIntrinsicSpellAbilities() { - return currentState.getIntrinsicSpellAbilities(); - } - public final FCollectionView getAllSpellAbilities() { final FCollection res = new FCollection<>(); for (final CardStateName key : states.keySet()) { @@ -3567,7 +3563,15 @@ public class Card extends GameEntity implements Comparable, IHasSVars { // TODO add changed type by card text public Iterable getChangedCardTypes() { - return Iterables.unmodifiableIterable(Iterables.concat(changedCardTypesCharacterDefining.values(), changedCardTypes.values())); + + Iterable byText = changedTypeByText == null ? ImmutableList.of() : ImmutableList.of(this.changedTypeByText); + + return Iterables.unmodifiableIterable(Iterables.concat( + changedCardTypesByText.values(), // Layer 3 + byText, // Layer 3 by Word Changes, + changedCardTypesCharacterDefining.values(), // Layer 4 + changedCardTypes.values() // Layer 6 + )); } public Table getChangedCardTypesTable() { @@ -3580,6 +3584,14 @@ public class Card extends GameEntity implements Comparable, IHasSVars { public boolean clearChangedCardTypes() { boolean changed = false; + if (changedTypeByText != null) + changed = true; + changedTypeByText = null; + + if (!changedCardTypesByText.isEmpty()) + changed = true; + changedCardTypesByText.clear(); + if (!changedCardTypesCharacterDefining.isEmpty()) changed = true; changedCardTypesCharacterDefining.clear(); @@ -3594,6 +3606,10 @@ public class Card extends GameEntity implements Comparable, IHasSVars { public boolean clearChangedCardColors() { boolean changed = false; + if (!changedCardColorsByText.isEmpty()) + changed = true; + changedCardColorsByText.clear(); + if (!changedCardTypesCharacterDefining.isEmpty()) changed = true; changedCardTypesCharacterDefining.clear(); @@ -3605,10 +3621,24 @@ public class Card extends GameEntity implements Comparable, IHasSVars { return changed; } + public Table getChangedCardKeywordsByText() { + return changedCardKeywordsByText; + } + + public Iterable getChangedCardKeywordsList() { + + return Iterables.concat( + changedCardKeywordsByText.values(), // Layer 3 + ImmutableList.of(new KeywordsChange(ImmutableList.of(), null, this.hasRemoveIntrinsic())), // Layer 4 + changedCardKeywords.values() // Layer 6 + ); + } + public Table getChangedCardKeywords() { return changedCardKeywords; } + public Table getChangedCardColorsTable() { return changedCardColors; } @@ -3616,7 +3646,23 @@ public class Card extends GameEntity implements Comparable, IHasSVars { return changedCardColorsCharacterDefining; } public Iterable getChangedCardColors() { - return Iterables.concat(changedCardColorsCharacterDefining.values(), changedCardColors.values()); + return Iterables.concat(changedCardColorsByText.values(), changedCardColorsCharacterDefining.values(), changedCardColors.values()); + } + + public final void addChangedCardTypesByText(final CardType addType, final long timestamp, final long staticId) { + addChangedCardTypesByText(addType, timestamp, staticId, true); + } + + public final void addChangedCardTypesByText(final CardType addType, final long timestamp, final long staticId, final boolean updateView) { + changedCardTypesByText.put(timestamp, staticId, new CardChangedType(addType, null, true, true, true, false, false, false, false)); + + // setting card type via text, does overwrite any other word change effects? + this.changedTextColors.addEmpty(timestamp, staticId); + this.changedTextTypes.addEmpty(timestamp, staticId); + + if (updateView) { + updateTypesForView(); + } } public final void addChangedCardTypes(final CardType addType, final CardType removeType, @@ -3628,7 +3674,7 @@ public class Card extends GameEntity implements Comparable, IHasSVars { addType, removeType, removeSuperTypes, removeCardTypes, removeSubTypes, removeLandTypes, removeCreatureTypes, removeArtifactTypes, removeEnchantmentTypes)); if (updateView) { - currentState.getView().updateType(currentState); + updateTypesForView(); } } @@ -3660,27 +3706,36 @@ public class Card extends GameEntity implements Comparable, IHasSVars { removed |= changedCardTypes.remove(timestamp, staticId) != null; removed |= changedCardTypesCharacterDefining.remove(timestamp, staticId) != null; if (removed && updateView) { - currentState.getView().updateType(currentState); + updateTypesForView(); } } - public final void addColor(final String s, final boolean addToColors, final long timestamp, final long staticId, final boolean cda) { - (cda ? changedCardColorsCharacterDefining : changedCardColors).put(timestamp, staticId, new CardColor(s, addToColors, timestamp)); - currentState.getView().updateColors(this); - currentState.getView().updateHasChangeColors(!Iterables.isEmpty(getChangedCardColors())); + public void addColorByText(final ColorSet color, final long timestamp, final long staticId) { + changedCardColorsByText.put(timestamp, staticId, new CardColor(color, false)); + updateColorForView(); + } + + public final void addColor(final ColorSet color, final boolean addToColors, final long timestamp, final long staticId, final boolean cda) { + (cda ? changedCardColorsCharacterDefining : changedCardColors).put(timestamp, staticId, new CardColor(color, addToColors)); + updateColorForView(); } public final void removeColor(final long timestampIn, final long staticId) { boolean removed = false; + removed |= changedCardColorsByText.remove(timestampIn, staticId) != null; removed |= changedCardColors.remove(timestampIn, staticId) != null; removed |= changedCardColorsCharacterDefining.remove(timestampIn, staticId) != null; if (removed) { - currentState.getView().updateColors(this); - currentState.getView().updateHasChangeColors(!Iterables.isEmpty(getChangedCardColors())); + updateColorForView(); } } + public final void updateColorForView() { + currentState.getView().updateColors(this); + currentState.getView().updateHasChangeColors(!Iterables.isEmpty(getChangedCardColors())); + } + public final void setColor(final String color) { currentState.setColor(color); } @@ -3688,10 +3743,10 @@ public class Card extends GameEntity implements Comparable, IHasSVars { currentState.setColor(color); } - public final ColorSet determineColor() { - return determineColor(currentState); + public final ColorSet getColor() { + return getColor(currentState); } - public final ColorSet determineColor(CardState state) { + public final ColorSet getColor(CardState state) { byte colors = state.getColor(); for (final CardColor cc : getChangedCardColors()) { if (cc.isAdditional()) { @@ -3743,20 +3798,6 @@ public class Card extends GameEntity implements Comparable, IHasSVars { currentState.setBaseToughnessString(s); } - public final int getSetPower() { - if (newPTCharacterDefining.isEmpty() && newPT.isEmpty()) { - return Integer.MAX_VALUE; - } - return getLatestPT().getLeft(); - } - - public final int getSetToughness() { - if (newPTCharacterDefining.isEmpty() && newPT.isEmpty()) { - return Integer.MAX_VALUE; - } - return getLatestPT().getRight(); - } - public final void addCloneState(CardCloneStates states, final long timestamp) { clonedStates.put(timestamp, states); updateCloneState(true); @@ -3822,6 +3863,7 @@ public class Card extends GameEntity implements Comparable, IHasSVars { } else { setState(getFaceupCardStateName(), updateView, true); } + updateChangedText(); } public final CardStateName getFaceupCardStateName() { @@ -3843,102 +3885,84 @@ public class Card extends GameEntity implements Comparable, IHasSVars { return clonedStates.lastEntry().getValue(); } - public final void addTextChangeState(CardCloneStates states, final long timestamp) { - textChangeStates.put(timestamp, states); - updateCloneState(true); + public final Table> getSetPTTable() { + return newPT; } - public final boolean removeTextChangeState(final long timestamp) { - if (textChangeStates.remove(timestamp) != null) { - updateCloneState(true); - return true; + public final void setPTTable(Table> table) { + newPT.clear(); + newPT.putAll(table); + } + + public final Table> getSetPTCharacterDefiningTable() { + return newPTCharacterDefining; + } + + public final void setPTCharacterDefiningTable(Table> table) { + newPTCharacterDefining.clear(); + newPTCharacterDefining.putAll(table); + } + + public final void addNewPTByText(final Integer power, final Integer toughness, final long timestamp, final long staticId) { + newPTText.put(timestamp, staticId, Pair.of(power, toughness)); + updatePTforView(); + } + + + public final void addNewPT(final Integer power, final Integer toughness, final long timestamp, final long staticId) { + addNewPT(power, toughness, timestamp, staticId, false); + } + + public final void addNewPT(final Integer power, final Integer toughness, final long timestamp, final long staticId, final boolean cda) { + (cda ? newPTCharacterDefining : newPT).put(timestamp, staticId, Pair.of(power, toughness)); + updatePTforView(); + } + + public final void removeNewPT(final long timestamp, final long staticId) { + boolean removed = false; + + removed |= newPTText.remove(timestamp, staticId) != null; + removed |= newPT.remove(timestamp, staticId) != null; + removed |= newPTCharacterDefining.remove(timestamp, staticId) != null; + + if (removed) { + updatePTforView(); } - return false; - } - public final boolean removeTextChangeStates() { - if (textChangeStates.isEmpty()) { - return false; - } - textChangeStates.clear(); - updateCloneState(false); - return true; } - private final CardCloneStates getLastTextChangeState() { - if (textChangeStates.isEmpty()) { - return null; - } - return textChangeStates.lastEntry().getValue(); - } - - public final boolean hasTextChangeState() { - return !textChangeStates.isEmpty(); - } - /** - * - * Get the latest set Power and Toughness of this Card. - * - * @return the latest set Power and Toughness of this {@link Card} as the - * left and right values of a {@link Pair}, respectively. A value of Integer.MAX_VALUE - * means that particular property has not been set. - */ - private synchronized Pair getLatestPT() { - // Find latest set power - Integer power = null, toughness = null; - - // apply CDA first - for (Pair pt : newPTCharacterDefining.values()) { - if (pt.getLeft() != null) - power = pt.getLeft(); - if (pt.getRight() != null) - toughness = pt.getRight(); - } - // now real PT - for (Pair pt : newPT.values()) { - if (pt.getLeft() != null) - power = pt.getLeft(); - if (pt.getRight() != null) - toughness = pt.getRight(); - } - - if (power == null) - power = Integer.MAX_VALUE; - - if (toughness == null) - toughness = Integer.MAX_VALUE; - - return Pair.of(power, toughness); - } - - public final void addNewPT(final Integer power, final Integer toughness, final long timestamp) { - addNewPT(power, toughness, timestamp, false); - } - - public final void addNewPT(final Integer power, final Integer toughness, final long timestamp, final boolean cda) { - (cda ? newPTCharacterDefining : newPT).put(timestamp, Pair.of(power, toughness)); + public void updatePTforView() { getView().updateLethalDamage(this); currentState.getView().updatePower(this); currentState.getView().updateToughness(this); } - public final void removeNewPT(final long timestamp) { - boolean removed = false; + public Iterable> getPTIterable() { + return Iterables.concat(this.newPTText.values(), this.newPTCharacterDefining.values(), this.newPT.values()); + } - removed |= newPT.remove(timestamp) != null; - removed |= newPTCharacterDefining.remove(timestamp) != null; - - if (removed) { - getView().updateLethalDamage(this); - currentState.getView().updatePower(this); - currentState.getView().updateToughness(this); + public final boolean clearNewPT() { + boolean changed = false; + if (!newPTText.isEmpty()) { + changed = true; + newPTText.clear(); } + if (!newPTCharacterDefining.isEmpty()) { + changed = true; + newPTCharacterDefining.clear(); + } + if (!newPT.isEmpty()) { + changed = true; + newPT.clear(); + } + return changed; } public final int getCurrentPower() { int total = getBasePower(); - final int setPower = getSetPower(); - if (setPower != Integer.MAX_VALUE) { - total = setPower; + for (Pair p : getPTIterable()) { + if (p.getLeft() != null) { + total = p.getLeft(); + } } return total; } @@ -3971,9 +3995,10 @@ public class Card extends GameEntity implements Comparable, IHasSVars { public final int getCurrentToughness() { int total = getBaseToughness(); - final int setToughness = getSetToughness(); - if (setToughness != Integer.MAX_VALUE) { - total = setToughness; + for (Pair p : getPTIterable()) { + if (p.getRight() != null) { + total = p.getRight(); + } } return total; } @@ -4148,6 +4173,24 @@ public class Card extends GameEntity implements Comparable, IHasSVars { getGame().fireEvent(new GameEventCardTapped(this, false)); } + public final Table getChangedCardTraitsByText() { + return changedCardTraitsByText; + } + public final void setChangedCardTraitsByText(Table changes) { + changedCardTraitsByText.clear(); + for (Table.Cell e : changes.cellSet()) { + changedCardTraitsByText.put(e.getRowKey(), e.getColumnKey(), e.getValue().copy(this, true)); + } + } + public final void addChangedCardTraitsByText(Collection spells, + Collection trigger, Collection replacements, Collection statics, long timestamp, long staticId) { + changedCardTraitsByText.put(timestamp, staticId, new CardTraitChanges( + spells, null, trigger, replacements, statics, true, false + )); + // update view + updateAbilityTextForView(); + } + public final void addChangedCardTraits(Collection spells, Collection removedAbilities, Collection trigger, Collection replacements, Collection statics, boolean removeAll, boolean removeNonMana, long timestamp, long staticId) { @@ -4158,8 +4201,22 @@ public class Card extends GameEntity implements Comparable, IHasSVars { updateAbilityTextForView(); } - public final CardTraitChanges removeChangedCardTraits(long timestamp, long staticId) { - return changedCardTraits.remove(timestamp, staticId); + public final boolean removeChangedCardTraits(long timestamp, long staticId) { + boolean changed = false; + changed |= changedCardTraitsByText.remove(timestamp, staticId) != null; + changed |= changedCardTraits.remove(timestamp, staticId) != null; + return changed; + } + + public Iterable getChangedCardTraitsList(CardState state) { + List landManaAbilities = Lists.newArrayList(); + this.updateBasicLandAbilities(landManaAbilities, state); + + return Iterables.concat( + changedCardTraitsByText.values(), // Layer 3 + ImmutableList.of(new CardTraitChanges(landManaAbilities, null, null, null, null, hasRemoveIntrinsic(), false)), // Layer 4 + changedCardTraits.values() // Layer 6 + ); } public final Table getChangedCardTraits() { @@ -4174,11 +4231,16 @@ public class Card extends GameEntity implements Comparable, IHasSVars { } public boolean clearChangedCardTraits() { + boolean changed = false; + if (changedCardTraitsByText.isEmpty()) { + changed = true; + } + changedCardTraitsByText.clear(); if (changedCardTraits.isEmpty()) { - return false; + changed = true; } changedCardTraits.clear(); - return true; + return changed; } // keywords are like flying, fear, first strike, etc... @@ -4247,6 +4309,16 @@ public class Card extends GameEntity implements Comparable, IHasSVars { } } + public final void addChangedCardKeywordsByText(final List keywords, final long timestamp, final long staticId, final boolean updateView) { + // keywords should already created for Card, so no addKeywordsToCard + // this one is done for Volrath's Shapeshifter which replaces all the card text + changedCardKeywordsByText.put(timestamp, staticId, new KeywordsChange(keywords, null, true)); + + if (updateView) { + updateKeywords(); + } + } + public final void addChangedCardKeywordsInternal( final List keywords, final List removeKeywords, final boolean removeAllKeywords, @@ -4261,31 +4333,47 @@ public class Card extends GameEntity implements Comparable, IHasSVars { } } - public final KeywordsChange removeChangedCardKeywords(final long timestamp, final long staticId) { + public final boolean removeChangedCardKeywords(final long timestamp, final long staticId) { return removeChangedCardKeywords(timestamp, staticId, true); } - public final KeywordsChange removeChangedCardKeywords(final long timestamp, final long staticId, final boolean updateView) { - KeywordsChange change = changedCardKeywords.remove(timestamp, staticId); - if (change != null && updateView) { + public final boolean removeChangedCardKeywords(final long timestamp, final long staticId, final boolean updateView) { + boolean changed = false; + changed |= changedCardKeywords.remove(timestamp, staticId) != null; + changed |= changedCardKeywordsByText.remove(timestamp, staticId) != null; + if (updateView) { updateKeywords(); if (isToken()) game.fireEvent(new GameEventTokenStateUpdate(this)); } - return change; + return changed; } public boolean clearChangedCardKeywords() { return clearChangedCardKeywords(false); } public final boolean clearChangedCardKeywords(final boolean updateView) { - if (changedCardKeywords.isEmpty()) { - return false; + boolean changed = false; + if (!changedCardKeywordsByText.isEmpty()) { + changed = true; + } + changedCardKeywordsByText.clear(); + if (!changedCardKeywords.isEmpty()) { + changed = true; } changedCardKeywords.clear(); - if (updateView) { + if (changed && updateView) { updateKeywords(); } - return true; + return changed; + } + + public boolean clearStaticChangedCardKeywords(final boolean updateView) { + // remove all keywords which are done by static ability, where the staticId isn't 0 (these are currently pump or animate effects) + boolean changed = changedCardKeywords.columnKeySet().retainAll(ImmutableList.of((long)0)); + if (changed && updateView) { + updateKeywords(); + } + return changed; } // Hidden keywords will be left out @@ -4299,26 +4387,10 @@ public class Card extends GameEntity implements Comparable, IHasSVars { public final void updateKeywordsCache(final CardState state) { KeywordCollection keywords = new KeywordCollection(); - //final List keywords = Lists.newArrayList(); - if (!this.hasRemoveIntrinsic()) { - keywords.insertAll(state.getIntrinsicKeywords()); - } + // Layer 1 + keywords.insertAll(state.getIntrinsicKeywords()); - // see if keyword changes are in effect - for (final KeywordsChange ck : changedCardKeywords.values()) { - if (ck.isRemoveAllKeywords()) { - keywords.clear(); - } - else if (ck.getRemoveKeywords() != null) { - keywords.removeAll(ck.getRemoveKeywords()); - } - - keywords.removeInstances(ck.getRemovedKeywordInstances()); - - if (ck.getKeywords() != null) { - keywords.insertAll(ck.getKeywords()); - } - } + keywords.applyChanges(getChangedCardKeywordsList()); // remove Can't have keywords for (Keyword k : getCantHaveKeyword()) { @@ -4346,14 +4418,13 @@ public class Card extends GameEntity implements Comparable, IHasSVars { if (MagicColor.fromName(newWord) == 0) { throw new RuntimeException("Not a color: " + newWord); } - changedTextColors.add(timestamp, StringUtils.capitalize(originalWord), StringUtils.capitalize(newWord)); - updateKeywordsChangedText(timestamp, staticId); + changedTextColors.add(timestamp, staticId, StringUtils.capitalize(originalWord), StringUtils.capitalize(newWord)); + updateChangedText(); } public final void removeChangedTextColorWord(final Long timestamp, final long staticId) { - if (changedTextColors.remove(timestamp)) { - updateKeywordsOnRemoveChangedText(removeChangedCardKeywords(timestamp, staticId)); + if (changedTextColors.remove(timestamp, staticId)) { updateChangedText(); } } @@ -4364,59 +4435,65 @@ public class Card extends GameEntity implements Comparable, IHasSVars { * @param newWord the new type word. */ public final void addChangedTextTypeWord(final String originalWord, final String newWord, final Long timestamp, final long staticId) { - changedTextTypes.add(timestamp, originalWord, newWord); - if (getType().hasSubtype(originalWord)) { - // need other table because different layer - addChangedCardTypes(CardType.parse(newWord, true), CardType.parse(originalWord, true), - false, false, false, false, false, false, false, timestamp, staticId, true, false); - } - updateKeywordsChangedText(timestamp, staticId); + changedTextTypes.add(timestamp, staticId, originalWord, newWord); updateChangedText(); } public final void removeChangedTextTypeWord(final Long timestamp, final long staticId) { - if (changedTextTypes.remove(timestamp)) { - removeChangedCardTypes(timestamp, staticId); - updateKeywordsOnRemoveChangedText(removeChangedCardKeywords(timestamp, staticId)); + if (changedTextTypes.remove(timestamp, staticId)) { updateChangedText(); } } - private void updateKeywordsChangedText(final Long timestamp, final long staticId) { - if (hasSVar("LockInKeywords")) { - return; - } - - final List addKeywords = Lists.newArrayList(); - final List removeKeywords = Lists.newArrayList(keywordsGrantedByTextChanges); - - for (final KeywordInterface kw : currentState.getIntrinsicKeywords()) { - String oldtxt = kw.getOriginal(); - final String newtxt = AbilityUtils.applyKeywordTextChangeEffects(oldtxt, this); - if (!newtxt.equals(oldtxt)) { - KeywordInterface newKw = Keyword.getInstance(newtxt); - addKeywords.add(newKw); - removeKeywords.add(kw); - keywordsGrantedByTextChanges.add(newKw); - } - } - if (!addKeywords.isEmpty() || !removeKeywords.isEmpty()) { - addChangedCardKeywordsInternal(addKeywords, removeKeywords, false, timestamp, staticId, true); - } - } - - private void updateKeywordsOnRemoveChangedText(final KeywordsChange k) { - if (k != null) { - keywordsGrantedByTextChanges.removeAll(k.getKeywords()); - } - } - /** * Update the changed text of the intrinsic spell abilities and keywords. */ - private void updateChangedText() { + public void updateChangedText() { resetChangedSVars(); + + // update type + List toAdd = Lists.newArrayList(); + List toRemove = Lists.newArrayList(); + + // before change by text word change, apply the other card type change by text for Volrath's Shapeshifter + CardTypeView changedByText = getOriginalType().getTypeWithChanges(this.changedCardTypesByText.values()); + + for (Map.Entry e : this.changedTextTypes.entrySet()) { + if (changedByText.hasStringType(e.getKey())) { + toRemove.add(e.getKey()); + toAdd.add(e.getValue()); + } + } + + this.changedTypeByText = new CardChangedType(new CardType(toAdd, true), new CardType(toRemove, true), false, false, false, false, false, false, false); + currentState.updateChangedText(); + + // update changed text in the layer, for Volrath's Shapeshifter + for (CardTraitChanges change : this.changedCardTraitsByText.values()) { + change.changeText(); + } + + // need to get keywords before text change + KeywordCollection beforeKeywords = new KeywordCollection(); + beforeKeywords.insertAll(currentState.getIntrinsicKeywords()); + beforeKeywords.applyChanges(this.changedCardKeywordsByText.values()); + + final List addKeywords = Lists.newArrayList(); + final List removeKeywords = Lists.newArrayList(); + // Text Change for intrinsic keywords + for(KeywordInterface kw : beforeKeywords) { + String oldtxt = kw.getOriginal(); + final String newtxt = AbilityUtils.applyKeywordTextChangeEffects(oldtxt, this); + if (!newtxt.equals(oldtxt)) { + KeywordInterface newKw = Keyword.getInstance(newtxt); + addKeywords.add(newKw); + removeKeywords.add(kw); + } + } + + changedCardKeywordsByWord = new KeywordsChange(addKeywords, removeKeywords, false); + text = AbilityUtils.applyDescriptionTextChangeEffects(originalText, this); currentState.getView().updateAbilityText(this, currentState); @@ -4424,11 +4501,11 @@ public class Card extends GameEntity implements Comparable, IHasSVars { } public final ImmutableMap getChangedTextColorWords() { - return ImmutableMap.copyOf(changedTextColors.toMap()); + return ImmutableMap.copyOf(changedTextColors); } public final ImmutableMap getChangedTextTypeWords() { - return ImmutableMap.copyOf(changedTextTypes.toMap()); + return ImmutableMap.copyOf(changedTextTypes); } /** @@ -4577,17 +4654,14 @@ public class Card extends GameEntity implements Comparable, IHasSVars { } public void updateStaticAbilities(List list, CardState state) { - if (hasRemoveIntrinsic()) { - list.clear(); - } - - for (final CardTraitChanges ck : changedCardTraits.values()) { + for (final CardTraitChanges ck : getChangedCardTraitsList(state)) { if (ck.isRemoveAll()) { list.clear(); } list.addAll(ck.getStaticAbilities()); } + // keywords are already sorted by Layer for (KeywordInterface kw : getUnhiddenKeywords(state)) { list.addAll(kw.getStaticAbilities()); } @@ -4937,13 +5011,17 @@ public class Card extends GameEntity implements Comparable, IHasSVars { view.updateEmblem(this); } - public final boolean isOfColor(final String col) { return determineColor().hasAnyColor(MagicColor.fromName(col)); } - public final boolean isBlack() { return determineColor().hasBlack(); } - public final boolean isBlue() { return determineColor().hasBlue(); } - public final boolean isRed() { return determineColor().hasRed(); } - public final boolean isGreen() { return determineColor().hasGreen(); } - public final boolean isWhite() { return determineColor().hasWhite(); } - public final boolean isColorless() { return determineColor().isColorless(); } + /* + * there are easy checkers for Color. The CardUtil functions should be made + * part of the Card class, so calling out is not necessary + */ + public final boolean isOfColor(final String col) { return getColor().hasAnyColor(MagicColor.fromName(col)); } + public final boolean isBlack() { return getColor().hasBlack(); } + public final boolean isBlue() { return getColor().hasBlue(); } + public final boolean isRed() { return getColor().hasRed(); } + public final boolean isGreen() { return getColor().hasGreen(); } + public final boolean isWhite() { return getColor().hasWhite(); } + public final boolean isColorless() { return getColor().isColorless(); } public final boolean sharesNameWith(final Card c1) { // in a corner case where c1 is null, there is no name to share with. @@ -4952,8 +5030,8 @@ public class Card extends GameEntity implements Comparable, IHasSVars { } // Special Logic for SpyKit - if (c1.hasKeyword("AllNonLegendaryCreatureNames")) { - if (hasKeyword("AllNonLegendaryCreatureNames")) { + if (c1.hasNonLegendaryCreatureNames()) { + if (hasNonLegendaryCreatureNames()) { // with both does have this, then they share any name return true; } else if (getName().isEmpty()) { @@ -4990,7 +5068,7 @@ public class Card extends GameEntity implements Comparable, IHasSVars { shares |= name.equals(getState(CardStateName.RightSplit).getName()); } - if (!shares && hasKeyword("AllNonLegendaryCreatureNames")) { + if (!shares && hasNonLegendaryCreatureNames()) { // check if the name is from a face // in general token creatures does not have this final ICardFace face = StaticData.instance().getCommonCards().getFaceByName(name); @@ -6006,16 +6084,14 @@ public class Card extends GameEntity implements Comparable, IHasSVars { } public void updateReplacementEffects(List list, CardState state) { - if (hasRemoveIntrinsic()) { - list.clear(); - } - - for (final CardTraitChanges ck : changedCardTraits.values()) { + for (final CardTraitChanges ck : getChangedCardTraitsList(state)) { if (ck.isRemoveAll()) { list.clear(); } list.addAll(ck.getReplacements()); } + + // Keywords are already sorted by Layer for (KeywordInterface kw : getUnhiddenKeywords(state)) { list.addAll(kw.getReplacements()); } @@ -6944,15 +7020,14 @@ public class Card extends GameEntity implements Comparable, IHasSVars { public boolean removeChangedState() { boolean updateState = false; updateState |= removeCloneStates(); - updateState |= removeTextChangeStates(); updateState |= clearChangedCardTypes(); updateState |= clearChangedCardKeywords(); updateState |= clearChangedCardColors(); updateState |= clearChangedCardTraits(); - newPT.clear(); - newPTCharacterDefining.clear(); + updateState |= clearNewPT(); + updateState |= clearChangedName(); clearEtbCounters(); diff --git a/forge-game/src/main/java/forge/game/card/CardChangedName.java b/forge-game/src/main/java/forge/game/card/CardChangedName.java new file mode 100644 index 00000000000..63dcd82ac8f --- /dev/null +++ b/forge-game/src/main/java/forge/game/card/CardChangedName.java @@ -0,0 +1,24 @@ +package forge.game.card; + +public class CardChangedName { + + protected String newName; + protected boolean addNonLegendaryCreatureNames = false; + + public CardChangedName(String newName, boolean addNonLegendaryCreatureNames) { + this.newName = newName; + this.addNonLegendaryCreatureNames = addNonLegendaryCreatureNames; + } + + public String getNewName() { + return newName; + } + + public boolean isOverwrite() { + return newName != null; + } + + public boolean isAddNonLegendaryCreatureNames() { + return addNonLegendaryCreatureNames; + } +} diff --git a/forge-game/src/main/java/forge/game/card/CardChangedWord.java b/forge-game/src/main/java/forge/game/card/CardChangedWord.java deleted file mode 100644 index d1a1590bcca..00000000000 --- a/forge-game/src/main/java/forge/game/card/CardChangedWord.java +++ /dev/null @@ -1,21 +0,0 @@ -package forge.game.card; - -public class CardChangedWord { - - private final String originalWord, - newWord; - - public CardChangedWord(final String originalWord, final String newWord) { - this.originalWord = originalWord; - this.newWord = newWord; - } - - public String getOriginalWord() { - return originalWord; - } - - public String getNewWord() { - return newWord; - } - -} diff --git a/forge-game/src/main/java/forge/game/card/CardChangedWords.java b/forge-game/src/main/java/forge/game/card/CardChangedWords.java index 320eeef0282..e187aa951f1 100644 --- a/forge-game/src/main/java/forge/game/card/CardChangedWords.java +++ b/forge-game/src/main/java/forge/game/card/CardChangedWords.java @@ -1,15 +1,31 @@ package forge.game.card; import java.util.Map; -import java.util.Map.Entry; -import java.util.SortedMap; +import com.google.common.collect.ForwardingMap; import com.google.common.collect.ImmutableList; import com.google.common.collect.Maps; +import com.google.common.collect.Table; +import com.google.common.collect.TreeBasedTable; -public final class CardChangedWords { +public final class CardChangedWords extends ForwardingMap { - private final SortedMap map = Maps.newTreeMap(); + class WordHolder { + public String oldWord; + public String newWord; + + public boolean clear = false; + + WordHolder() { + this.clear = true; + } + WordHolder(String oldWord, String newWord) { + this.oldWord = oldWord; + this.newWord = newWord; + } + } + + private final Table map = TreeBasedTable.create(); private boolean isDirty = false; private Map resultCache = Maps.newHashMap(); @@ -17,19 +33,28 @@ public final class CardChangedWords { public CardChangedWords() { } - public Long add(final long timestamp, final String originalWord, final String newWord) { + public Long addEmpty(final long timestamp, final long staticId) { final Long stamp = Long.valueOf(timestamp); - map.put(stamp, new CardChangedWord(originalWord, newWord)); + map.put(stamp, staticId, new WordHolder()); // Table doesn't allow null value isDirty = true; return stamp; } - public boolean remove(final Long timestamp) { + public Long add(final long timestamp, final long staticId, final String originalWord, final String newWord) { + final Long stamp = Long.valueOf(timestamp); + map.put(stamp, staticId, new WordHolder(originalWord, newWord)); isDirty = true; - return map.remove(timestamp) != null; + return stamp; } - public void removeAll() { + public boolean remove(final Long timestamp, final long staticId) { + isDirty = true; + return map.remove(timestamp, staticId) != null; + } + + @Override + public void clear() { + super.clear(); map.clear(); isDirty = true; } @@ -46,26 +71,33 @@ public final class CardChangedWords { * @return a map of strings to strings, where each changed word in this * object is mapped to its corresponding replacement word. */ - public Map toMap() { + @Override + protected Map delegate() { refreshCache(); return resultCache; } private void refreshCache() { if (isDirty) { - resultCache = Maps.newHashMap(); - for (final CardChangedWord ccw : this.map.values()) { + resultCache.clear(); + for (final WordHolder ccw : this.map.values()) { + // is empty pair is for resetting the data, it is done for Volrath’s Shapeshifter + if (ccw.clear) { + resultCache.clear(); + continue; + } + // changes because a->b and b->c (resulting in a->c) final Map toBeChanged = Maps.newHashMap(); for (final Entry e : resultCache.entrySet()) { - if (e.getValue().equals(ccw.getOriginalWord())) { - toBeChanged.put(e.getKey(), ccw.getNewWord()); + if (e.getValue().equals(ccw.oldWord)) { + toBeChanged.put(e.getKey(), ccw.newWord); } } resultCache.putAll(toBeChanged); // the actual change (b->c) - resultCache.put(ccw.getOriginalWord(), ccw.getNewWord()); + resultCache.put(ccw.oldWord, ccw.newWord); } // TODO should that be removed? diff --git a/forge-game/src/main/java/forge/game/card/CardColor.java b/forge-game/src/main/java/forge/game/card/CardColor.java index 0b2bf697844..8bf0dae48ab 100644 --- a/forge-game/src/main/java/forge/game/card/CardColor.java +++ b/forge-game/src/main/java/forge/game/card/CardColor.java @@ -17,8 +17,7 @@ */ package forge.game.card; -import forge.card.mana.ManaCost; -import forge.card.mana.ManaCostParser; +import forge.card.ColorSet; /** *

@@ -39,15 +38,8 @@ public class CardColor { return this.additional; } - private final long timestamp; - public final long getTimestamp() { - return this.timestamp; - } - - CardColor(final String colors, final boolean addToColors, final long timestamp) { - final ManaCost mc = new ManaCost(new ManaCostParser(colors)); - this.colorMask = mc.getColorProfile(); + CardColor(final ColorSet colors, final boolean addToColors) { + this.colorMask = colors.getColor(); this.additional = addToColors; - this.timestamp = timestamp; } } 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 1c619716ffa..1a772cc76cf 100644 --- a/forge-game/src/main/java/forge/game/card/CardFactory.java +++ b/forge-game/src/main/java/forge/game/card/CardFactory.java @@ -136,14 +136,13 @@ public class CardFactory { // change the color of the copy (eg: Fork) if (sourceSA.hasParam("CopyIsColor")) { - String tmp = ""; + ColorSet finalColors = ColorSet.getNullColor(); final String newColor = sourceSA.getParam("CopyIsColor"); if (newColor.equals("ChosenColor")) { - tmp = CardUtil.getShortColorsString(source.getChosenColors()); + finalColors = ColorSet.fromNames(source.getChosenColors()); } else { - tmp = CardUtil.getShortColorsString(Lists.newArrayList(newColor.split(","))); + finalColors = ColorSet.fromNames(newColor.split(",")); } - final String finalColors = tmp; c.addColor(finalColors, !sourceSA.hasParam("OverwriteColors"), c.getTimestamp(), 0, false); } @@ -622,24 +621,24 @@ public class CardFactory { // if something is cloning a flip card, copy both original and // flipped state final CardState ret1 = new CardState(out, CardStateName.Original); - ret1.copyFrom(in.getState(CardStateName.Original, true), false); + ret1.copyFrom(in.getState(CardStateName.Original), false); result.put(CardStateName.Original, ret1); final CardState ret2 = new CardState(out, CardStateName.Flipped); - ret2.copyFrom(in.getState(CardStateName.Flipped, true), false); + ret2.copyFrom(in.getState(CardStateName.Flipped), false); result.put(CardStateName.Flipped, ret2); } else if (in.isAdventureCard()) { final CardState ret1 = new CardState(out, CardStateName.Original); - ret1.copyFrom(in.getState(CardStateName.Original, true), false); + ret1.copyFrom(in.getState(CardStateName.Original), false); result.put(CardStateName.Original, ret1); final CardState ret2 = new CardState(out, CardStateName.Adventure); - ret2.copyFrom(in.getState(CardStateName.Adventure, true), false); + ret2.copyFrom(in.getState(CardStateName.Adventure), false); result.put(CardStateName.Adventure, ret2); } else { // in all other cases just copy the current state to original final CardState ret = new CardState(out, CardStateName.Original); - ret.copyFrom(in.getState(in.getCurrentStateName(), true), false); + ret.copyFrom(in.getState(in.getCurrentStateName()), false); result.put(CardStateName.Original, ret); } @@ -832,7 +831,7 @@ public class CardFactory { final CardStateName state = top.getCurrentStateName(); final CardState ret = new CardState(card, state); if (top.isCloned()) { - ret.copyFrom(top.getState(state, true), false); + ret.copyFrom(top.getState(state), false); } else { ret.copyFrom(top.getOriginalState(state), false); } @@ -852,7 +851,7 @@ public class CardFactory { // For face down, flipped, transformed, melded or MDFC card, also copy the original state to avoid crash if (state != CardStateName.Original) { final CardState ret1 = new CardState(card, CardStateName.Original); - ret1.copyFrom(top.getState(CardStateName.Original, true), false); + ret1.copyFrom(top.getState(CardStateName.Original), false); result.put(CardStateName.Original, ret1); } 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 11166d7fad0..8914c464819 100644 --- a/forge-game/src/main/java/forge/game/card/CardFactoryUtil.java +++ b/forge-game/src/main/java/forge/game/card/CardFactoryUtil.java @@ -338,7 +338,7 @@ public class CardFactoryUtil { } for (final Card crd : list) { - ColorSet color = crd.determineColor(); + ColorSet color = crd.getColor(); for (int i = 0; i < cntColors; i++) { if (color.hasAnyColor(MagicColor.WUBRG[i])) map[i]++; @@ -376,7 +376,7 @@ public class CardFactoryUtil { } for (final Card crd : list) { - ColorSet color = crd.determineColor(); + ColorSet color = crd.getColor(); for (int i = 0; i < cntColors; i++) { if (color.hasAnyColor(MagicColor.WUBRG[i])) map[i]++; @@ -407,7 +407,7 @@ public class CardFactoryUtil { } for (final Card crd : list) { - ColorSet color = crd.determineColor(); + ColorSet color = crd.getColor(); for (int i = 0; i < cntColors; i++) { if (color.hasAnyColor(colorRestrictions.get(i))) { map[i]++; @@ -1842,7 +1842,7 @@ public class CardFactoryUtil { if (card.isPermanent()) { final String abPump = "DB$ Pump | Defined$ Remembered | KW$ Haste | PumpZone$ Stack " + "| ConditionDefined$ Remembered | ConditionPresent$ Creature | Duration$ UntilLoseControlOfHost"; - final AbilitySub saPump = (AbilitySub)AbilityFactory.getAbility(abPump, card); + final AbilitySub saPump = (AbilitySub) AbilityFactory.getAbility(abPump, card); String dbClean = "DB$ Cleanup | ClearRemembered$ True"; final AbilitySub saCleanup = (AbilitySub) AbilityFactory.getAbility(dbClean, card); @@ -1856,6 +1856,21 @@ public class CardFactoryUtil { inst.addTrigger(parsedUpkeepTrig); inst.addTrigger(parsedPlayTrigger); + } else if (keyword.equals("Training")) { + final String trigStr = "Mode$ Attacks | ValidCard$ Card.Self | Secondary$ True | " + + "IsPresent$ Creature.attacking+Other+powerGTX | TriggerDescription$ Training (" + + inst.getReminderText() + ")"; + + final String effect = "DB$ PutCounter | CounterType$ P1P1 | CounterNum$ 1 | Defined$ Self | Training$ True"; + final Trigger trigger = TriggerHandler.parseTrigger(trigStr, card, intrinsic); + + SpellAbility sa = AbilityFactory.getAbility(effect, card); + trigger.setSVar("X", "Count$CardPower"); + sa.setIntrinsic(intrinsic); + trigger.setOverridingAbility(sa); + + inst.addTrigger(trigger); + } else if (keyword.startsWith("Tribute")) { // use hardcoded ability name final String abStr = "TrigNotTribute"; diff --git a/forge-game/src/main/java/forge/game/card/CardPredicates.java b/forge-game/src/main/java/forge/game/card/CardPredicates.java index 55912ec3aa8..0ed368fdecd 100644 --- a/forge-game/src/main/java/forge/game/card/CardPredicates.java +++ b/forge-game/src/main/java/forge/game/card/CardPredicates.java @@ -264,7 +264,7 @@ public final class CardPredicates { return new Predicate() { @Override public boolean apply(final Card c) { - return c.determineColor().hasAnyColor(color); + return c.getColor().hasAnyColor(color); } }; } // getColor() @@ -273,7 +273,7 @@ public final class CardPredicates { return new Predicate() { @Override public boolean apply(final Card c) { - return c.determineColor().hasExactlyColor(color); + return c.getColor().hasExactlyColor(color); } }; } @@ -282,7 +282,7 @@ public final class CardPredicates { return new Predicate() { @Override public boolean apply(final Card c) { - return c.determineColor().isColorless(); + return c.getColor().isColorless(); } }; } 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 2025be855c7..98cfe6cd7d9 100644 --- a/forge-game/src/main/java/forge/game/card/CardProperty.java +++ b/forge-game/src/main/java/forge/game/card/CardProperty.java @@ -689,7 +689,7 @@ public class CardProperty { break; case "MostProminentColor": byte mask = CardFactoryUtil.getMostProminentColors(game.getCardsIn(ZoneType.Battlefield)); - if (!card.determineColor().hasAnyColor(mask)) + if (!card.getColor().hasAnyColor(mask)) return false; break; case "LastCastThisTurn": @@ -703,7 +703,7 @@ public class CardProperty { if (castSA == null) { return false; } - if (!card.determineColor().hasAnyColor(castSA.getPayingColors().getColor())) { + if (!card.getColor().hasAnyColor(castSA.getPayingColors().getColor())) { return false; } break; diff --git a/forge-game/src/main/java/forge/game/card/CardTraitChanges.java b/forge-game/src/main/java/forge/game/card/CardTraitChanges.java index 8d4530d738a..6228d3af8a8 100644 --- a/forge-game/src/main/java/forge/game/card/CardTraitChanges.java +++ b/forge-game/src/main/java/forge/game/card/CardTraitChanges.java @@ -11,14 +11,14 @@ import forge.game.staticability.StaticAbility; import forge.game.trigger.Trigger; public class CardTraitChanges implements Cloneable { - + private List triggers = Lists.newArrayList(); private List replacements = Lists.newArrayList(); private List abilities = Lists.newArrayList(); private List staticAbilities = Lists.newArrayList(); private List removedAbilities = Lists.newArrayList(); - + private boolean removeAll = false; private boolean removeNonMana = false; @@ -64,13 +64,13 @@ public class CardTraitChanges implements Cloneable { public Collection getAbilities() { return abilities; } - + /** * @return the abilities */ public Collection getRemovedAbilities() { return removedAbilities; - } + } /** * @return the staticAbilities @@ -78,11 +78,11 @@ public class CardTraitChanges implements Cloneable { public Collection getStaticAbilities() { return staticAbilities; } - + public boolean isRemoveAll() { return removeAll; } - + public boolean isRemoveNonMana() { return removeNonMana; } @@ -90,7 +90,7 @@ public class CardTraitChanges implements Cloneable { public CardTraitChanges copy(Card host, boolean lki) { try { CardTraitChanges result = (CardTraitChanges) super.clone(); - + result.abilities = Lists.newArrayList(); for (SpellAbility sa : this.abilities) { result.abilities.add(sa.copy(host, lki)); @@ -99,25 +99,43 @@ public class CardTraitChanges implements Cloneable { for (SpellAbility sa : this.removedAbilities) { result.removedAbilities.add(sa.copy(host, lki)); } - + result.triggers = Lists.newArrayList(); for (Trigger tr : this.triggers) { result.triggers.add(tr.copy(host, lki)); } - + result.replacements = Lists.newArrayList(); for (ReplacementEffect re : this.replacements) { result.replacements.add(re.copy(host, lki)); } - + result.staticAbilities = Lists.newArrayList(); for (StaticAbility sa : this.staticAbilities) { result.staticAbilities.add(sa.copy(host, lki)); } - + return result; } catch (final Exception ex) { throw new RuntimeException("CardTraitChanges : clone() error", ex); } } + + public void changeText() { + for (SpellAbility sa : this.abilities) { + sa.changeText(); + } + + for (Trigger tr : this.triggers) { + tr.changeText(); + } + + for (ReplacementEffect re : this.replacements) { + re.changeText(); + } + + for (StaticAbility sa : this.staticAbilities) { + sa.changeText(); + } + } } 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 b833f3543fd..4d6fa81ec4c 100644 --- a/forge-game/src/main/java/forge/game/card/CardUtil.java +++ b/forge-game/src/main/java/forge/game/card/CardUtil.java @@ -247,7 +247,7 @@ public final class CardUtil { newCopy.setCounters(Maps.newHashMap(in.getCounters())); - newCopy.setColor(in.determineColor().getColor()); + newCopy.setColor(in.getColor().getColor()); newCopy.setPhasedOut(in.isPhasedOut()); newCopy.setReceivedDamageFromThisTurn(in.getReceivedDamageFromThisTurn()); @@ -338,7 +338,7 @@ public final class CardUtil { byte combinedColor = 0; for (Card tgt : tgts) { - ColorSet cs = tgt.determineColor(); + ColorSet cs = tgt.getColor(); for (byte color : MagicColor.WUBRG) { if(!cs.hasAnyColor(color)) continue; @@ -362,7 +362,7 @@ public final class CardUtil { public static ColorSet getColorsYouCtrl(final Player p) { byte b = 0; for (Card c : p.getCardsIn(ZoneType.Battlefield)) { - b |= c.determineColor().getColor(); + b |= c.getColor().getColor(); } return ColorSet.fromMask(b); } diff --git a/forge-game/src/main/java/forge/game/card/CardView.java b/forge-game/src/main/java/forge/game/card/CardView.java index 2fc4104c5e7..6c99ebf52ce 100644 --- a/forge-game/src/main/java/forge/game/card/CardView.java +++ b/forge-game/src/main/java/forge/game/card/CardView.java @@ -1115,16 +1115,16 @@ public class CardView extends GameEntityView { return get(TrackableProperty.RightSplitColors); } void updateColors(Card c) { - set(TrackableProperty.Colors, c.determineColor()); + set(TrackableProperty.Colors, c.getColor()); } void updateColors(CardState c) { set(TrackableProperty.Colors, ColorSet.fromMask(c.getColor())); } void setOriginalColors(Card c) { - set(TrackableProperty.OriginalColors, c.determineColor()); + set(TrackableProperty.OriginalColors, c.getColor()); if (c.isSplitCard()) { - set(TrackableProperty.LeftSplitColors, c.determineColor(c.getState(CardStateName.LeftSplit))); - set(TrackableProperty.RightSplitColors, c.determineColor(c.getState(CardStateName.RightSplit))); + set(TrackableProperty.LeftSplitColors, c.getColor(c.getState(CardStateName.LeftSplit))); + set(TrackableProperty.RightSplitColors, c.getColor(c.getState(CardStateName.RightSplit))); } } void updateHasChangeColors(boolean hasChangeColor) { diff --git a/forge-game/src/main/java/forge/game/card/CounterEnumType.java b/forge-game/src/main/java/forge/game/card/CounterEnumType.java index 0b56739c1cf..e5712aa20fc 100644 --- a/forge-game/src/main/java/forge/game/card/CounterEnumType.java +++ b/forge-game/src/main/java/forge/game/card/CounterEnumType.java @@ -71,6 +71,8 @@ public enum CounterEnumType { CORRUPTION("CRPTN", 210, 121, 210), + CROAK("CROAK", 155, 255, 5), + CREDIT("CRDIT", 188, 197, 234), CRYSTAL("CRYST", 255, 85, 206), @@ -171,6 +173,8 @@ public enum CounterEnumType { INTERVENTION("INTRV", 205, 203, 105), + INVITATION("INVIT", 205, 0, 26), + ISOLATION("ISOLT", 250, 190, 0), JAVELIN("JAVLN", 180, 206, 172), diff --git a/forge-game/src/main/java/forge/game/card/token/TokenInfo.java b/forge-game/src/main/java/forge/game/card/token/TokenInfo.java index 03338e6ee8c..32c977ce5a2 100644 --- a/forge-game/src/main/java/forge/game/card/token/TokenInfo.java +++ b/forge-game/src/main/java/forge/game/card/token/TokenInfo.java @@ -158,7 +158,7 @@ public class TokenInfo { if (!colorMap.isEmpty()) { if (!result.isColorless()) { // change Token Colors - byte color = result.determineColor().getColor(); + byte color = result.getColor().getColor(); for (final Map.Entry e : colorMap.entrySet()) { byte v = MagicColor.fromName(e.getValue()); @@ -316,4 +316,4 @@ public class TokenInfo { return result; } -} \ No newline at end of file +} diff --git a/forge-game/src/main/java/forge/game/keyword/Equip.java b/forge-game/src/main/java/forge/game/keyword/Equip.java index 3b386e74a1d..66a5c2ad65d 100644 --- a/forge-game/src/main/java/forge/game/keyword/Equip.java +++ b/forge-game/src/main/java/forge/game/keyword/Equip.java @@ -6,7 +6,7 @@ public class Equip extends KeywordWithCost { public Equip() { } - + @Override protected void parse(String details) { String[] k = details.split(":"); @@ -15,7 +15,7 @@ public class Equip extends KeywordWithCost { type = k[2]; } } - + @Override protected String formatReminderText(String reminderText) { return String.format(reminderText, cost.toSimpleString(), type); 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 ca576fe253c..4881a60ac76 100644 --- a/forge-game/src/main/java/forge/game/keyword/Keyword.java +++ b/forge-game/src/main/java/forge/game/keyword/Keyword.java @@ -157,6 +157,7 @@ public enum Keyword { SURGE("Surge", KeywordWithCost.class, false, "You may cast this spell for its surge cost if you or a teammate has cast another spell this turn."), SUSPEND("Suspend", Suspend.class, false, "Rather than cast this card from your hand, you may pay %s and exile it with {%d:time counter} on it. At the beginning of your upkeep, remove a time counter. When the last is removed, cast it without paying its mana cost."), TOTEM_ARMOR("Totem armor", SimpleKeyword.class, true, "If enchanted permanent would be destroyed, instead remove all damage marked on it and destroy this Aura."), + TRAINING("Training", SimpleKeyword.class, false, "Whenever this creature attacks with another creature with greater power, put a +1/+1 counter on this creature."), TRAMPLE("Trample", Trample.class, true, "This creature can deal excess combat damage to the player or planeswalker it's attacking."), TRANSFIGURE("Transfigure", KeywordWithCost.class, false, "%s, Sacrifice this creature: Search your library for a creature card with the same mana value as this creature and put that card onto the battlefield, then shuffle. Transfigure only as a sorcery."), TRANSMUTE("Transmute", KeywordWithCost.class, false, "%s, Discard this card: Search your library for a card with the same mana value as this card, reveal it, and put it into your hand, then shuffle. Transmute only as a sorcery."), diff --git a/forge-game/src/main/java/forge/game/keyword/KeywordCollection.java b/forge-game/src/main/java/forge/game/keyword/KeywordCollection.java index 26c480299f7..9f4a3b1dfc9 100644 --- a/forge-game/src/main/java/forge/game/keyword/KeywordCollection.java +++ b/forge-game/src/main/java/forge/game/keyword/KeywordCollection.java @@ -181,6 +181,23 @@ public class KeywordCollection implements Iterable { return view; } + public void applyChanges(Iterable changes) { + for (final KeywordsChange ck : changes) { + if (ck.isRemoveAllKeywords()) { + clear(); + } + else if (ck.getRemoveKeywords() != null) { + removeAll(ck.getRemoveKeywords()); + } + + removeInstances(ck.getRemovedKeywordInstances()); + + if (ck.getKeywords() != null) { + insertAll(ck.getKeywords()); + } + } + } + public class KeywordCollectionView implements Iterable { protected KeywordCollectionView() { diff --git a/forge-game/src/main/java/forge/game/keyword/KeywordInstance.java b/forge-game/src/main/java/forge/game/keyword/KeywordInstance.java index ad1f528aa11..ddcf2423758 100644 --- a/forge-game/src/main/java/forge/game/keyword/KeywordInstance.java +++ b/forge-game/src/main/java/forge/game/keyword/KeywordInstance.java @@ -239,27 +239,27 @@ public abstract class KeywordInstance> implements K public KeywordInterface copy(final Card host, final boolean lki) { try { KeywordInstance result = (KeywordInstance) super.clone(); - + result.abilities = Lists.newArrayList(); for (SpellAbility sa : this.abilities) { result.abilities.add(sa.copy(host, lki)); } - + result.triggers = Lists.newArrayList(); for (Trigger tr : this.triggers) { result.triggers.add(tr.copy(host, lki)); } - + result.replacements = Lists.newArrayList(); for (ReplacementEffect re : this.replacements) { result.replacements.add(re.copy(host, lki)); } - + result.staticAbilities = Lists.newArrayList(); for (StaticAbility sa : this.staticAbilities) { result.staticAbilities.add(sa.copy(host, lki)); } - + return result; } catch (final Exception ex) { throw new RuntimeException("KeywordInstance : clone() error", ex); @@ -303,4 +303,23 @@ public abstract class KeywordInstance> implements K sa.setHostCard(host); } } + + @Override + public void setIntrinsic(final boolean value) { + for (SpellAbility sa : this.abilities) { + sa.setIntrinsic(value); + } + + for (Trigger tr : this.triggers) { + tr.setIntrinsic(value); + } + + for (ReplacementEffect re : this.replacements) { + re.setIntrinsic(value); + } + + for (StaticAbility sa : this.staticAbilities) { + sa.setIntrinsic(value); + } + } } diff --git a/forge-game/src/main/java/forge/game/keyword/KeywordInterface.java b/forge-game/src/main/java/forge/game/keyword/KeywordInterface.java index d508fbdab02..a46a555fd3c 100644 --- a/forge-game/src/main/java/forge/game/keyword/KeywordInterface.java +++ b/forge-game/src/main/java/forge/game/keyword/KeywordInterface.java @@ -21,18 +21,19 @@ public interface KeywordInterface extends Cloneable { void createTraits(final Card host, final boolean intrinsic); void createTraits(final Card host, final boolean intrinsic, final boolean clear); - + void createTraits(final Player player); void createTraits(final Player player, final boolean clear); void addTrigger(final Trigger trg); - + void addReplacement(final ReplacementEffect trg); void addSpellAbility(final SpellAbility s); void addStaticAbility(final StaticAbility st); - + void setHostCard(final Card host); + void setIntrinsic(final boolean value); /** * @return the triggers @@ -50,7 +51,7 @@ public interface KeywordInterface extends Cloneable { * @return the staticAbilities */ Collection getStaticAbilities(); - + KeywordInterface copy(final Card host, final boolean lki); boolean redundant(final Collection list); diff --git a/forge-game/src/main/java/forge/game/keyword/KeywordWithAmountAndType.java b/forge-game/src/main/java/forge/game/keyword/KeywordWithAmountAndType.java index 57a30c61578..54cf90e685d 100644 --- a/forge-game/src/main/java/forge/game/keyword/KeywordWithAmountAndType.java +++ b/forge-game/src/main/java/forge/game/keyword/KeywordWithAmountAndType.java @@ -13,7 +13,7 @@ public class KeywordWithAmountAndType extends KeywordInstance. */ @@ -33,7 +33,7 @@ import forge.game.trigger.Trigger; *

* Card_Keywords class. *

- * + * * @author Forge */ public class KeywordsChange implements Cloneable { @@ -43,9 +43,9 @@ public class KeywordsChange implements Cloneable { private boolean removeAllKeywords; /** - * + * * Construct a new {@link KeywordsChange}. - * + * * @param keywordList the list of keywords to add. * @param removeKeywordList the list of keywords to remove. * @param removeAll whether to remove all keywords. @@ -81,9 +81,9 @@ public class KeywordsChange implements Cloneable { } /** - * + * * getKeywords. - * + * * @return ArrayList */ public final Collection getKeywords() { @@ -94,9 +94,9 @@ public class KeywordsChange implements Cloneable { return this.removeKeywordInterfaces; } /** - * + * * getRemoveKeywords. - * + * * @return ArrayList */ public final List getRemoveKeywords() { @@ -104,9 +104,9 @@ public class KeywordsChange implements Cloneable { } /** - * + * * isRemoveAllKeywords. - * + * * @return boolean */ public final boolean isRemoveAllKeywords() { @@ -137,10 +137,6 @@ public class KeywordsChange implements Cloneable { public final boolean removeKeywordfromAdd(final String keyword) { return keywords.remove(keyword); } - - public final void addKeyword(final String keyword) { - keywords.add(keyword); - } public void setHostCard(final Card host) { keywords.setHostCard(host); diff --git a/forge-game/src/main/java/forge/game/keyword/Kicker.java b/forge-game/src/main/java/forge/game/keyword/Kicker.java index 816eb9254c1..b0ec21ec2af 100644 --- a/forge-game/src/main/java/forge/game/keyword/Kicker.java +++ b/forge-game/src/main/java/forge/game/keyword/Kicker.java @@ -12,7 +12,7 @@ public class Kicker extends KeywordWithCost { public Kicker() { } - + @Override protected void parse(String details) { List l = Lists.newArrayList(TextUtil.split(details, ':')); @@ -20,7 +20,7 @@ public class Kicker extends KeywordWithCost { if (l.size() > 1) cost2 = new Cost(l.get(1), false); } - + @Override protected String formatReminderText(String reminderText) { if (cost2 == null) { diff --git a/forge-game/src/main/java/forge/game/keyword/Suspend.java b/forge-game/src/main/java/forge/game/keyword/Suspend.java index 875c88d98ae..7c8d92fb474 100644 --- a/forge-game/src/main/java/forge/game/keyword/Suspend.java +++ b/forge-game/src/main/java/forge/game/keyword/Suspend.java @@ -3,7 +3,7 @@ package forge.game.keyword; public class Suspend extends KeywordWithCostAndAmount { boolean withoutCostAndAmount = false; - + @Override protected void parse(String details) { if ("".equals(details)) { diff --git a/forge-game/src/main/java/forge/game/player/AchievementTracker.java b/forge-game/src/main/java/forge/game/player/AchievementTracker.java index 4987a380b4b..3a65fd1bcd5 100644 --- a/forge-game/src/main/java/forge/game/player/AchievementTracker.java +++ b/forge-game/src/main/java/forge/game/player/AchievementTracker.java @@ -22,7 +22,7 @@ public class AchievementTracker { if (sa.isPwAbility() && sa.hasParam("Ultimate")) { activatedUltimates.add(card.getName()); } - if (card.determineColor().equals(ColorSet.ALL_COLORS)) { + if (card.getColor().equals(ColorSet.ALL_COLORS)) { challengesCompleted.add("Chromatic"); } } 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 cb557fac011..5c8129f32a8 100644 --- a/forge-game/src/main/java/forge/game/player/Player.java +++ b/forge-game/src/main/java/forge/game/player/Player.java @@ -1604,11 +1604,12 @@ public class Player extends GameEntity implements Comparable { return numTokenCreatedThisTurn; } - public final void addTokensCreatedThisTurn() { + public final void addTokensCreatedThisTurn(Card token) { numTokenCreatedThisTurn++; final Map runParams = AbilityKey.newMap(); runParams.put(AbilityKey.Player, this); runParams.put(AbilityKey.Num, numTokenCreatedThisTurn); + runParams.put(AbilityKey.Card, token); game.getTriggerHandler().runTrigger(TriggerType.TokenCreated, runParams, 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 1ba5c680b6b..71e87b7e556 100644 --- a/forge-game/src/main/java/forge/game/spellability/SpellAbilityCondition.java +++ b/forge-game/src/main/java/forge/game/spellability/SpellAbilityCondition.java @@ -299,9 +299,9 @@ public class SpellAbilityCondition extends SpellAbilityVariables { if (first == null) { return false; } - byte firstColor = first.determineColor().getColor(); + byte firstColor = first.getColor().getColor(); for (Card c : tgts) { - if (c.determineColor().getColor() != firstColor) { + if (c.getColor().getColor() != firstColor) { return false; } } diff --git a/forge-game/src/main/java/forge/game/staticability/StaticAbility.java b/forge-game/src/main/java/forge/game/staticability/StaticAbility.java index d5a922a73ee..90645c5447e 100644 --- a/forge-game/src/main/java/forge/game/staticability/StaticAbility.java +++ b/forge-game/src/main/java/forge/game/staticability/StaticAbility.java @@ -144,7 +144,7 @@ public class StaticAbility extends CardTraitBase implements IIdentifiable, Clone layers.add(StaticAbilityLayer.CONTROL); } - if (hasParam("ChangeColorWordsTo") || hasParam("GainTextOf")) { + if (hasParam("ChangeColorWordsTo") || hasParam("GainTextOf") || hasParam("AddNames")) { layers.add(StaticAbilityLayer.TEXT); } diff --git a/forge-game/src/main/java/forge/game/staticability/StaticAbilityContinuous.java b/forge-game/src/main/java/forge/game/staticability/StaticAbilityContinuous.java index a87fe1d296e..67ae4dece8e 100644 --- a/forge-game/src/main/java/forge/game/staticability/StaticAbilityContinuous.java +++ b/forge-game/src/main/java/forge/game/staticability/StaticAbilityContinuous.java @@ -32,6 +32,7 @@ import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import forge.GameCommand; +import forge.card.CardStateName; import forge.card.CardType; import forge.card.ColorSet; import forge.card.MagicColor; @@ -45,13 +46,14 @@ import forge.game.ability.ApiType; import forge.game.card.Card; import forge.game.card.CardCollection; import forge.game.card.CardCollectionView; -import forge.game.card.CardFactory; import forge.game.card.CardFactoryUtil; import forge.game.card.CardLists; import forge.game.card.CardPredicates; +import forge.game.card.CardState; import forge.game.card.CardUtil; import forge.game.cost.Cost; import forge.game.keyword.Keyword; +import forge.game.keyword.KeywordInterface; import forge.game.player.Player; import forge.game.replacement.ReplacementEffect; import forge.game.replacement.ReplacementHandler; @@ -116,9 +118,6 @@ public final class StaticAbilityContinuous { se.setParams(params); se.setTimestamp(hostCard.getTimestamp()); - String changeColorWordsTo = null; - Card gainTextSource = null; - String addP = ""; int powerBonus = 0; String addT = ""; @@ -136,7 +135,7 @@ public final class StaticAbilityContinuous { String[] addSVars = null; List addTypes = null; List removeTypes = null; - String addColors = null; + ColorSet addColors = null; String[] addTriggers = null; String[] addStatics = null; boolean removeAllAbilities = false; @@ -166,23 +165,6 @@ public final class StaticAbilityContinuous { effects.setGlobalRuleChange(GlobalRuleChange.fromString(params.get("GlobalRule"))); } - if (layer == StaticAbilityLayer.TEXT && params.containsKey("GainTextOf")) { - final String valid = params.get("GainTextOf"); - CardCollection allValid = CardLists.getValidCards(game.getCardsInGame(), valid, hostCard.getController(), hostCard, stAb); - if (allValid.size() > 1) { - // TODO: if ever necessary, support gaining text of multiple cards at the same time - System.err.println("Error: GainTextOf parameter was not defined as a unique card for " + hostCard); - } else if (allValid.size() == 1) { - gainTextSource = allValid.get(0); - } else { - gainTextSource = null; - } - } - - if (layer == StaticAbilityLayer.TEXT && params.containsKey("ChangeColorWordsTo")) { - changeColorWordsTo = params.get("ChangeColorWordsTo"); - } - if (layer == StaticAbilityLayer.SETPT &¶ms.containsKey("SetPower")) { setP = params.get("SetPower"); setPower = AbilityUtils.calculateAmount(hostCard, setP, stAb); @@ -471,22 +453,22 @@ public final class StaticAbilityContinuous { if (params.containsKey("AddColor")) { final String colors = params.get("AddColor"); if (colors.equals("ChosenColor")) { - addColors = CardUtil.getShortColorsString(hostCard.getChosenColors()); + addColors = ColorSet.fromNames(hostCard.getChosenColors()); } else if (colors.equals("All")) { - addColors = "W U B R G"; + addColors = ColorSet.ALL_COLORS; } else { - addColors = CardUtil.getShortColorsString(Arrays.asList(colors.split(" & "))); + addColors = ColorSet.fromNames(colors.split(" & ")); } } if (params.containsKey("SetColor")) { final String colors = params.get("SetColor"); if (colors.equals("ChosenColor")) { - addColors = CardUtil.getShortColorsString(hostCard.getChosenColors()); + addColors = ColorSet.fromNames(hostCard.getChosenColors()); } else if (colors.equals("All")) { - addColors = "W U B R G"; + addColors = ColorSet.ALL_COLORS; } else { - addColors = CardUtil.getShortColorsString(Arrays.asList(colors.split(" & "))); + addColors = ColorSet.fromNames(colors.split(" & ")); } overwriteColors = true; } @@ -603,29 +585,98 @@ public final class StaticAbilityContinuous { // Gain text from another card if (layer == StaticAbilityLayer.TEXT) { - if (gainTextSource != null) { - affectedCard.addTextChangeState( - CardFactory.getCloneStates(gainTextSource, affectedCard, stAb), se.getTimestamp() - ); - } - } + if (params.containsKey("GainTextOf")) { + CardCollection allValid = AbilityUtils.getDefinedCards(hostCard, params.get("GainTextOf"), stAb); + if (!allValid.isEmpty()) { + Card first = allValid.getFirst(); - // Change color words - if (changeColorWordsTo != null) { - final byte color; - if (changeColorWordsTo.equals("ChosenColor")) { - if (hostCard.hasChosenColor()) { - color = MagicColor.fromName(Iterables.getFirst(hostCard.getChosenColors(), null)); - } else { - color = 0; + // for Volrath’s Shapeshifter, respect flipped state if able? + CardState state = first.getState(affectedCard.isFlipped() && first.isFlipCard() ? CardStateName.Flipped : first.getCurrentStateName()); + + List spellAbilities = Lists.newArrayList(); + List trigger = Lists.newArrayList(); + List replacementEffects = Lists.newArrayList(); + List staticAbilities = Lists.newArrayList(); + List keywords = Lists.newArrayList(); + + for(SpellAbility sa : state.getSpellAbilities()) { + SpellAbility newSA = sa.copy(affectedCard, false); + newSA.setOriginalAbility(sa); // need to be set to get the Once Per turn Clause correct + newSA.setGrantorStatic(stAb); + //newSA.setIntrinsic(false); needs to be changed by CardTextChanges + + spellAbilities.add(newSA); + } + if (params.containsKey("GainTextAbilities")) { + for (String ability : params.get("GainTextAbilities").split(" & ")) { + final SpellAbility sa = AbilityFactory.getAbility(AbilityUtils.getSVar(stAb, ability), affectedCard, stAb); + sa.setIntrinsic(true); // needs to be affected by Text + sa.setGrantorStatic(stAb); + spellAbilities.add(sa); + } + } + for (Trigger tr : state.getTriggers()) { + Trigger newTr = tr.copy(affectedCard, false); + //newTr.setIntrinsic(false); needs to be changed by CardTextChanges + trigger.add(newTr); + } + for (ReplacementEffect re : state.getReplacementEffects()) { + ReplacementEffect newRE = re.copy(affectedCard, false); + //newRE.setIntrinsic(false); needs to be changed by CardTextChanges + replacementEffects.add(newRE); + } + for (StaticAbility sa : state.getStaticAbilities()) { + StaticAbility newST = sa.copy(affectedCard, false); + //newST.setIntrinsic(false); needs to be changed by CardTextChanges + staticAbilities.add(newST); + } + for (KeywordInterface ki : state.getIntrinsicKeywords()) { + KeywordInterface newKi = ki.copy(affectedCard, false); + //newKi.setIntrinsic(false); needs to be changed by CardTextChanges + keywords.add(newKi); + } + + // Volrath’s Shapeshifter has that card’s name, mana cost, color, types, abilities, power, and toughness. + + // name + affectedCard.addChangedName(state.getName(), false, se.getTimestamp(), stAb.getId()); + // Mana cost + affectedCard.addChangedManaCost(state.getManaCost(), se.getTimestamp(), stAb.getId()); + // color + affectedCard.addColorByText(ColorSet.fromMask(state.getColor()), i, i); + // type + affectedCard.addChangedCardTypesByText(new CardType(state.getType()), se.getTimestamp(), stAb.getId()); + // abilities + affectedCard.addChangedCardTraitsByText(spellAbilities, trigger, replacementEffects, staticAbilities, se.getTimestamp(), stAb.getId()); + affectedCard.addChangedCardKeywordsByText(keywords, se.getTimestamp(), stAb.getId(), false); + + // power and toughness + affectedCard.addNewPTByText(state.getBasePower(), state.getBaseToughness(), se.getTimestamp(), stAb.getId()); } - } else { - color = MagicColor.fromName(changeColorWordsTo); } - if (color != 0) { - final String colorName = MagicColor.toLongString(color); - affectedCard.addChangedTextColorWord("Any", colorName, se.getTimestamp(), stAb.getId()); + if (stAb.hasParam("AddNames")) { // currently only for AllNonLegendaryCreatureNames + affectedCard.addChangedName(null, true, se.getTimestamp(), stAb.getId()); + } + + // Change color words + if (params.containsKey("ChangeColorWordsTo")) { + final byte color; + String changeColorWordsTo = params.get("ChangeColorWordsTo"); + if (changeColorWordsTo.equals("ChosenColor")) { + if (hostCard.hasChosenColor()) { + color = MagicColor.fromName(Iterables.getFirst(hostCard.getChosenColors(), null)); + } else { + color = 0; + } + } else { + color = MagicColor.fromName(changeColorWordsTo); + } + + if (color != 0) { + final String colorName = MagicColor.toLongString(color); + affectedCard.addChangedTextColorWord(stAb.getParamOrDefault("ChangeColorWordsFrom", "Any"), colorName, se.getTimestamp(), stAb.getId()); + } } } @@ -640,7 +691,7 @@ public final class StaticAbilityContinuous { setToughness = AbilityUtils.calculateAmount(affectedCard, setT, stAb, true); } affectedCard.addNewPT(setPower, setToughness, - hostCard.getTimestamp(), stAb.hasParam("CharacteristicDefining")); + hostCard.getTimestamp(), stAb.getId(), stAb.hasParam("CharacteristicDefining")); } } @@ -674,7 +725,7 @@ public final class StaticAbilityContinuous { } // replace one Keyword with list of keywords if (input.startsWith("Protection") && input.contains("CardColors")) { - for (Byte color : affectedCard.determineColor()) { + for (Byte color : affectedCard.getColor()) { extraKeywords.add(input.replace("CardColors", MagicColor.toLongString(color))); } return true; diff --git a/forge-game/src/main/java/forge/game/trigger/TriggerDrawn.java b/forge-game/src/main/java/forge/game/trigger/TriggerDrawn.java index 2a6ada5654c..62d7b2f7f6d 100644 --- a/forge-game/src/main/java/forge/game/trigger/TriggerDrawn.java +++ b/forge-game/src/main/java/forge/game/trigger/TriggerDrawn.java @@ -23,9 +23,12 @@ import forge.game.Game; import forge.game.GameStage; import forge.game.ability.AbilityKey; import forge.game.card.Card; +import forge.game.card.CardCollection; +import forge.game.card.CardLists; import forge.game.phase.PhaseType; import forge.game.player.Player; import forge.game.spellability.SpellAbility; +import forge.game.zone.ZoneType; import forge.util.Localizer; /** @@ -67,6 +70,16 @@ public class TriggerDrawn extends Trigger { if (!matchesValidParam("ValidPlayer", runParams.get(AbilityKey.Player))) { return false; } + if (hasParam("ValidPlayerControls")) { + final String sIsPresent = this.getParam("ValidPlayerControls"); + final Player p = ((Player)runParams.get(AbilityKey.Player)); + CardCollection list = (CardCollection) p.getCardsIn(ZoneType.Battlefield); + list = CardLists.getValidCards(list, sIsPresent.split(","), this.getHostCard().getController(), + this.getHostCard(), this); + if (list.size() == 0) { + return false; + } + } if (hasParam("Number")) { if (number != Integer.parseInt(getParam("Number"))) { diff --git a/forge-game/src/main/java/forge/game/trigger/TriggerLifeGained.java b/forge-game/src/main/java/forge/game/trigger/TriggerLifeGained.java index d4c2859b90b..e46e0ab46e9 100644 --- a/forge-game/src/main/java/forge/game/trigger/TriggerLifeGained.java +++ b/forge-game/src/main/java/forge/game/trigger/TriggerLifeGained.java @@ -21,7 +21,11 @@ import java.util.Map; import forge.game.ability.AbilityKey; import forge.game.card.Card; +import forge.game.card.CardCollection; +import forge.game.card.CardLists; +import forge.game.player.Player; import forge.game.spellability.SpellAbility; +import forge.game.zone.ZoneType; import forge.util.Localizer; /** @@ -56,6 +60,17 @@ public class TriggerLifeGained extends Trigger { if (!matchesValidParam("ValidPlayer", runParams.get(AbilityKey.Player))) { return false; } + if (hasParam("ValidPlayerControls")) { + final String sIsPresent = this.getParam("ValidPlayerControls"); + final Player p = ((Player)runParams.get(AbilityKey.Player)); + CardCollection list = (CardCollection) p.getCardsIn(ZoneType.Battlefield); + list = CardLists.getValidCards(list, sIsPresent.split(","), this.getHostCard().getController(), + this.getHostCard(), this); + if (list.size() == 0) { + return false; + } + } + if (!matchesValidParam("ValidSource", runParams.get(AbilityKey.Source))) { return false; } diff --git a/forge-game/src/main/java/forge/game/trigger/TriggerSpellAbilityCastOrCopy.java b/forge-game/src/main/java/forge/game/trigger/TriggerSpellAbilityCastOrCopy.java index 5ab0e6a2cd5..ab1e44571af 100644 --- a/forge-game/src/main/java/forge/game/trigger/TriggerSpellAbilityCastOrCopy.java +++ b/forge-game/src/main/java/forge/game/trigger/TriggerSpellAbilityCastOrCopy.java @@ -267,7 +267,7 @@ public class TriggerSpellAbilityCastOrCopy extends Trigger { if (!m.isSnow()) { continue; } - if (cast.determineColor().sharesColorWith(ColorSet.fromMask(m.getColor()))) { + if (cast.getColor().sharesColorWith(ColorSet.fromMask(m.getColor()))) { found = true; break; } diff --git a/forge-game/src/main/java/forge/game/trigger/TriggerTokenCreated.java b/forge-game/src/main/java/forge/game/trigger/TriggerTokenCreated.java index f20cbd80805..0493537026a 100644 --- a/forge-game/src/main/java/forge/game/trigger/TriggerTokenCreated.java +++ b/forge-game/src/main/java/forge/game/trigger/TriggerTokenCreated.java @@ -61,6 +61,7 @@ public class TriggerTokenCreated extends Trigger { @Override public final void setTriggeringObjects(final SpellAbility sa, Map runParams) { sa.setTriggeringObjectsFrom(runParams, AbilityKey.Player); + sa.setTriggeringObjectsFrom(runParams, AbilityKey.Card); } /** {@inheritDoc} @@ -71,6 +72,10 @@ public class TriggerTokenCreated extends Trigger { return false; } + if (!matchesValidParam("ValidToken", runParams.get(AbilityKey.Card))) { + return false; + } + if (hasParam("OnlyFirst")) { if ((int) runParams.get(AbilityKey.Num) != 1) { return false; diff --git a/forge-game/src/main/java/forge/game/trigger/TriggerTrains.java b/forge-game/src/main/java/forge/game/trigger/TriggerTrains.java new file mode 100644 index 00000000000..eb349885e0d --- /dev/null +++ b/forge-game/src/main/java/forge/game/trigger/TriggerTrains.java @@ -0,0 +1,75 @@ +/* + * 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.spellability.SpellAbility; +import forge.util.Localizer; + +import java.util.Map; + +/** + *

+ * Trigger_Trains class. + *

+ * + * @author Forge + */ +public class TriggerTrains extends Trigger { + + /** + *

+ * Constructor for TriggerTrains. + *

+ * + * @param params + * a {@link java.util.HashMap} object. + * @param host + * a {@link forge.game.card.Card} object. + * @param intrinsic + * the intrinsic + */ + public TriggerTrains(final Map params, final Card host, final boolean intrinsic) { + super(params, host, intrinsic); + } + + /** {@inheritDoc} + * @param runParams*/ + @Override + public final boolean performTest(final Map runParams) { + if (!matchesValidParam("ValidCard", runParams.get(AbilityKey.Card))) { + return false; + } + return true; + } + + /** {@inheritDoc} */ + @Override + public final void setTriggeringObjects(final SpellAbility sa, Map runParams) { + sa.setTriggeringObjectsFrom(runParams, AbilityKey.Card); + } + + @Override + public String getImportantStackObjects(SpellAbility sa) { + StringBuilder sb = new StringBuilder(); + sb.append(Localizer.getInstance().getMessage("lblTrains")).append(": ").append(sa.getTriggeringObject(AbilityKey.Card)); + return sb.toString(); + } +} 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 387b06abd38..5bf2ceac1b9 100644 --- a/forge-game/src/main/java/forge/game/trigger/TriggerType.java +++ b/forge-game/src/main/java/forge/game/trigger/TriggerType.java @@ -107,6 +107,7 @@ public enum TriggerType { Taps(TriggerTaps.class), TapsForMana(TriggerTapsForMana.class), TokenCreated(TriggerTokenCreated.class), + Trains(TriggerTrains.class), Transformed(TriggerTransformed.class), TurnBegin(TriggerTurnBegin.class), TurnFaceUp(TriggerTurnFaceUp.class), diff --git a/forge-gui-mobile/src/forge/deck/FDeckImportDialog.java b/forge-gui-mobile/src/forge/deck/FDeckImportDialog.java index b523f174c8c..dbf63820498 100644 --- a/forge-gui-mobile/src/forge/deck/FDeckImportDialog.java +++ b/forge-gui-mobile/src/forge/deck/FDeckImportDialog.java @@ -93,11 +93,13 @@ public class FDeckImportDialog extends FDialog { public void run() { List tokens = controller.parseInput(txtInput.getText()); //ensure deck updated based on any changes to options - //if there are any unknown cards, let user know this and give them the option to cancel + //if there are any cards that cannot be imported, let user know this and give them the option to cancel StringBuilder sb = new StringBuilder(); for (DeckRecognizer.Token token : tokens) { - if ((token.getType() == TokenType.UNKNOWN_CARD) || - (token.getType() == TokenType.UNSUPPORTED_CARD)) { + if (TokenType.CARD_FROM_NOT_ALLOWED_SET.equals(token.getType()) + || TokenType.CARD_FROM_INVALID_SET.equals(token.getType()) + || TokenType.UNKNOWN_CARD.equals(token.getType()) + || TokenType.UNSUPPORTED_CARD.equals(token.getType())) { if (sb.length() > 0) { sb.append("\n"); } @@ -116,6 +118,7 @@ public class FDeckImportDialog extends FDialog { FThreads.invokeInEdtLater(new Runnable() { @Override public void run() { + deck.optimizeMainCardArt(); hide(); callback.run(deck); } diff --git a/forge-gui/res/cardsfolder/b/braids_conjurer_adept_avatar.txt b/forge-gui/res/cardsfolder/b/braids_conjurer_adept_avatar.txt index 455ca0820b2..5f3f748e5f2 100644 --- a/forge-gui/res/cardsfolder/b/braids_conjurer_adept_avatar.txt +++ b/forge-gui/res/cardsfolder/b/braids_conjurer_adept_avatar.txt @@ -2,8 +2,8 @@ Name:Braids, Conjurer Adept Avatar ManaCost:no cost Types:Vanguard HandLifeModifier:+0/+3 -A:AB$ ChangeZone | ActivationZone$ Command | Cost$ 2 | Origin$ Hand | Destination$ Battlefield | ChangeType$ Land | DefinedPlayer$ Player | ChangeNum$ 1 | Tapped$ True | SpellDescription$ Each player may put a land card from their hand onto the battlefield tapped. -A:AB$ ChangeZone | ActivationZone$ Command | Cost$ 3 | Origin$ Hand | Destination$ Battlefield | ChangeType$ Artifact.nonCreature | DefinedPlayer$ Player | ChangeNum$ 1 | SpellDescription$ Each player may put a noncreature artifact card from their hand onto the battlefield. +A:AB$ ChangeZone | ActivationZone$ Command | Cost$ 2 | Origin$ Hand | Destination$ Battlefield | ChangeType$ Land | DefinedPlayer$ Player | ChangeNum$ 1 | Tapped$ True | AILogic$ AtOppEOT | SpellDescription$ Each player may put a land card from their hand onto the battlefield tapped. +A:AB$ ChangeZone | ActivationZone$ Command | Cost$ 3 | Origin$ Hand | Destination$ Battlefield | ChangeType$ Artifact.nonCreature | DefinedPlayer$ Player | ChangeNum$ 1 | AILogic$ AtOppEOT | SpellDescription$ Each player may put a noncreature artifact card from their hand onto the battlefield. A:AB$ ChangeZone | ActivationZone$ Command | Cost$ 4 | Origin$ Hand | Destination$ Battlefield | ChangeType$ Creature | DefinedPlayer$ Player | ChangeNum$ 1 | SorcerySpeed$ True | SpellDescription$ Each player may put a creature card from their hand onto the battlefield. Activate only as a sorcery. SVar:Picture:https://downloads.cardforge.org/images/cards/VAN/Braids, Conjurer Adept Avatar.full.jpg Oracle:Hand +0, life +3\n{2}: Each player may put a land card from their hand onto the battlefield tapped.\n{3}: Each player may put a noncreature artifact card from their hand onto the battlefield.\n{4}: Each player may put a creature card from their hand onto the battlefield. Activate only as a sorcery. diff --git a/forge-gui/res/cardsfolder/e/elvish_impersonators.txt b/forge-gui/res/cardsfolder/e/elvish_impersonators.txt index 2b683ba15ac..4120741a485 100644 --- a/forge-gui/res/cardsfolder/e/elvish_impersonators.txt +++ b/forge-gui/res/cardsfolder/e/elvish_impersonators.txt @@ -6,5 +6,5 @@ K:Flying K:ETBReplacement:Other:TrigRoll SVar:TrigRoll:DB$ RollDice | ResultSVar$ SetPwr | SubAbility$ RollTough | SpellDescription$ As CARDNAME enters the battlefield, roll a six-sided die twice. Its base power becomes the first result and its base toughness becomes the second result. SVar:RollTough:DB$ RollDice | ResultSVar$ SetTgn | SubAbility$ DBAnimate -SVar:DBAnimate:DB$ Animate | Defined$ Self | Power$ SetPwr | Toughness$ SetTgn +SVar:DBAnimate:DB$ Animate | Defined$ Self | Power$ SetPwr | Toughness$ SetTgn | Duration$ Permanent Oracle:As Elvish Impersonators enters the battlefield, roll a six-sided die twice. Its base power becomes the first result and its base toughness becomes the second result. diff --git a/forge-gui/res/cardsfolder/f/faerie_dragon.txt b/forge-gui/res/cardsfolder/f/faerie_dragon.txt index f1411735f94..837ba370731 100644 --- a/forge-gui/res/cardsfolder/f/faerie_dragon.txt +++ b/forge-gui/res/cardsfolder/f/faerie_dragon.txt @@ -3,7 +3,7 @@ ManaCost:2 G G Types:Creature Dragon PT:1/3 K:Flying -A:AB$ GenericChoice | Cost$ 1 G G | AtRandom$ True | ShowChoice$ Description | Choices$ Berserk,Twiddle,BloodLust,Green,White,Red,Damage3,Flying,P3P3,Banding,Black,Blue,NoRegen,LilSneak,M2M0,ToHand,Damage1,Nerf,Exile,Orcish | StackDescription$ SpellDescription | SpellDescription$ Perform a random action. +A:AB$ GenericChoice | Cost$ 1 G G | AtRandom$ True | ShowChoice$ Description | Choices$ Berserk,Twiddle,BloodLust,Green,White,Red,Damage3,Flying,P3P3,Banding,Black,Blue,NoRegen,LilSneak,M2M0,ToHand,Damage1,Nerf,Exile,Orcish | AILogic$ AtOppEOT | StackDescription$ SpellDescription | SpellDescription$ Perform a random action. SVar:Berserk:DB$ ChooseCard | Choices$ Creature | AtRandom$ True | RememberChosen$ True | SubAbility$ DBPump1 | SpellDescription$ A creature chosen at random gains trample and gets +X/+0 until end of turn, where X is its power. At the beginning of the next end step, destroy that creature if it attacked this turn. SVar:DBPump1:DB$ Pump | Defined$ Remembered | KW$ Trample | NumAtt$ X1 | SubAbility$ DBDelayedTrigger1 SVar:DBDelayedTrigger1:DB$ DelayedTrigger | RememberObjects$ Remembered | Mode$ Phase | Phase$ End of Turn | Execute$ TrigDestroy1 | SubAbility$ DBCleanup | TriggerDescription$ At the beginning of the next end step, destroy that creature if it attacked this turn. diff --git a/forge-gui/res/cardsfolder/g/ghostly_touch.txt b/forge-gui/res/cardsfolder/g/ghostly_touch.txt index bec8b280250..2926f237229 100644 --- a/forge-gui/res/cardsfolder/g/ghostly_touch.txt +++ b/forge-gui/res/cardsfolder/g/ghostly_touch.txt @@ -3,9 +3,8 @@ ManaCost:1 U Types:Enchantment Aura K:Enchant creature A:SP$ Attach | Cost$ 1 U | ValidTgts$ Creature | AILogic$ Pump -S:Mode$ Continuous | Affected$ Creature.EnchantedBy | AddTrigger$ AttackTrigger | AddSVar$ TrigTapUnTap | Description$ Enchanted creature has "Whenever this creature attacks, you may tap or untap target permanent. -SVar:AttackTrigger:Mode$ Attacks | ValidCard$ Card.Self | Execute$ TrigTapUnTap | TriggerZones$ Battlefield | TriggerDescription$ Whenever CARDNAME attacks, you may tap or untap target permanent. +S:Mode$ Continuous | Affected$ Creature.EnchantedBy | AddTrigger$ AttackTrigger | AddSVar$ TrigTapUnTap | Description$ Enchanted creature has "Whenever this creature attacks, you may tap or untap target permanent." +SVar:AttackTrigger:Mode$ Attacks | ValidCard$ Card.Self | Execute$ TrigTapUnTap | TriggerZones$ Battlefield | TriggerDescription$ Whenever this creature attacks, you may tap or untap target permanent. SVar:TrigTapUnTap:DB$ TapOrUntap | ValidTgts$ Permanent | TgtPrompt$ Select target permanent AI:RemoveDeck:All -SVar:Picture:http://www.wizards.com/global/images/magic/general/ghostly_touch.jpg Oracle:Enchant creature\nEnchanted creature has "Whenever this creature attacks, you may tap or untap target permanent." diff --git a/forge-gui/res/cardsfolder/g/goblin_polka_band.txt b/forge-gui/res/cardsfolder/g/goblin_polka_band.txt index 4db2e326794..27265a7ef34 100644 --- a/forge-gui/res/cardsfolder/g/goblin_polka_band.txt +++ b/forge-gui/res/cardsfolder/g/goblin_polka_band.txt @@ -2,7 +2,7 @@ Name:Goblin Polka Band ManaCost:R R Types:Creature Goblin PT:1/1 -A:AB$ Tap | Announce$ TgtNum | AnnounceTitle$ any number of creatures to target | Cost$ X 2 T | XColor$ R | CostDesc$ {2}, {T}: | ValidTgts$ Creature.untapped | TargetMin$ TgtNum | TargetMax$ TgtNum | TargetsAtRandom$ True | RememberTargets$ True | SubAbility$ GoblinHangover | SpellDescription$ Tap any number of random target creatures. Goblins tapped in this way do not untap during their controllers' next untap phases. This ability costs {R} more to activate for each target. +A:AB$ Tap | Announce$ TgtNum | AnnounceTitle$ any number of creatures to target | Cost$ X 2 T | XColor$ R | CostDesc$ {2}, {T}: | ValidTgts$ Creature.untapped | TargetMin$ TgtNum | TargetMax$ TgtNum | TargetsAtRandom$ True | RememberTargets$ True | AILogic$ GoblinPolkaBand | SubAbility$ GoblinHangover | SpellDescription$ Tap any number of random target creatures. Goblins tapped in this way do not untap during their controllers' next untap phases. This ability costs {R} more to activate for each target. SVar:GoblinHangover:DB$ PumpAll | ValidCards$ Goblin.IsRemembered | KW$ HIDDEN This card doesn't untap during your next untap step. | Duration$ Permanent SVar:TgtNum:Number$0 SVar:X:SVar$TgtNum diff --git a/forge-gui/res/cardsfolder/g/power_struggle.txt b/forge-gui/res/cardsfolder/g/power_struggle.txt index 7b662e546c0..97d2730f7d0 100644 --- a/forge-gui/res/cardsfolder/g/power_struggle.txt +++ b/forge-gui/res/cardsfolder/g/power_struggle.txt @@ -2,6 +2,6 @@ Name:Power Struggle ManaCost:2 U U U Types:Enchantment T:Mode$ Phase | Phase$ Upkeep | ValidPlayer$ Player | TriggerZones$ Battlefield | Execute$ TrigPump | TriggerController$ TriggeredPlayer | TriggerDescription$ At the beginning of each player's upkeep, that player exchanges control of random target artifact, creature or land he or she controls, for control of random target permanent of the same type that a random opponent controls. -SVar:TrigPump:DB$ Pump | TargetsWithDefinedController$ TriggeredPlayer | ValidTgts$ Artifact,Creature,Land | TargetsAtRandom$ True | SubAbility$ DBExchangeControl -SVar:DBExchangeControl:DB$ ExchangeControl | Defined$ ParentTarget | ValidTgts$ Artifact,Creature,Land | TargetsWithDefinedController$ Player.OpponentOf TriggeredPlayer | TargetsWithSharedCardType$ ParentTarget | TargetsAtRandom$ True +SVar:TrigPump:DB$ Pump | TargetsWithDefinedController$ TriggeredPlayer | ValidTgts$ Artifact,Creature,Land | TargetsAtRandom$ True | AILogic$ PowerStruggle | SubAbility$ DBExchangeControl +SVar:DBExchangeControl:DB$ ExchangeControl | Defined$ ParentTarget | ValidTgts$ Artifact,Creature,Land | TargetsWithDefinedController$ Player.OpponentOf TriggeredPlayer | TargetsWithSharedCardType$ ParentTarget | TargetsAtRandom$ True | AILogic$ PowerStruggle Oracle:At the beginning of each player's upkeep, that player exchanges control of random target artifact, creature or land he or she controls, for control of random target permanent of the same type that a random opponent controls. diff --git a/forge-gui/res/cardsfolder/n/necropolis_of_azar.txt b/forge-gui/res/cardsfolder/n/necropolis_of_azar.txt index 585a1c25ca6..ae2285e9610 100644 --- a/forge-gui/res/cardsfolder/n/necropolis_of_azar.txt +++ b/forge-gui/res/cardsfolder/n/necropolis_of_azar.txt @@ -3,7 +3,7 @@ ManaCost:2 B B Types:Enchantment T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | ValidCard$ Creature.nonBlack | TriggerZones$ Battlefield | Execute$ TrigPutCounter | TriggerDescription$ Whenever a nonblack creature dies, put a husk counter on CARDNAME. SVar:TrigPutCounter:DB$ PutCounter | CounterType$ HUSK -A:AB$ Token | Cost$ 5 SubCounter<1/HUSK> | TokenScript$ spawn_of_azar | TokenPower$ X | TokenToughness$ Y | SpellDescription$ Create an X/Y black Spawn creature token with swampwalk named Spawn of Azar, where X and Y are numbers chosen at random from 1 to 3. +A:AB$ Token | Cost$ 5 SubCounter<1/HUSK> | TokenScript$ spawn_of_azar | TokenPower$ X | TokenToughness$ Y | AILogic$ RandomPT | SpellDescription$ Create an X/Y black Spawn creature token with swampwalk named Spawn of Azar, where X and Y are numbers chosen at random from 1 to 3. SVar:X:Count$Random.1.3 SVar:Y:Count$Random.1.3 DeckHas:Ability$Counters & Ability$Token diff --git a/forge-gui/res/cardsfolder/p/pandoras_box.txt b/forge-gui/res/cardsfolder/p/pandoras_box.txt index 4474b1ab52f..f0a857ee66a 100644 --- a/forge-gui/res/cardsfolder/p/pandoras_box.txt +++ b/forge-gui/res/cardsfolder/p/pandoras_box.txt @@ -1,7 +1,7 @@ Name:Pandora's Box ManaCost:5 Types:Artifact -A:AB$ ChooseCard | Cost$ 3 T | Choices$ Creature | AtRandom$ True | AllCards$ True | SubAbility$ DBRepeatEach | StackDescription$ SpellDescription | SpellDescription$ Choose a creature card at random from all players' decklists. Each player flips a coin. Each player whose coin comes up heads creates a token that's a copy of that card. +A:AB$ ChooseCard | Cost$ 3 T | Choices$ Creature | AtRandom$ True | AllCards$ True | SubAbility$ DBRepeatEach | AILogic$ AtOppEOT | StackDescription$ SpellDescription | SpellDescription$ Choose a creature card at random from all players' decklists. Each player flips a coin. Each player whose coin comes up heads creates a token that's a copy of that card. SVar:DBRepeatEach:DB$ RepeatEach | RepeatPlayers$ Player | RepeatSubAbility$ DBFlip | SubAbility$ DBCleanup SVar:DBFlip:DB$ FlipACoin | Flipper$ Remembered | NoCall$ True | HeadsSubAbility$ DBCopyPermanent SVar:DBCopyPermanent:DB$ CopyPermanent | Defined$ ChosenCard | Controller$ Remembered diff --git a/forge-gui/res/cardsfolder/s/spy_kit.txt b/forge-gui/res/cardsfolder/s/spy_kit.txt index 24c71733526..6f8542704ab 100644 --- a/forge-gui/res/cardsfolder/s/spy_kit.txt +++ b/forge-gui/res/cardsfolder/s/spy_kit.txt @@ -2,6 +2,5 @@ Name:Spy Kit ManaCost:2 Types:Artifact Equipment K:Equip:2 -S:Mode$ Continuous | Affected$ Creature.EquippedBy | AddKeyword$ AllNonLegendaryCreatureNames | AddPower$ 1 | AddToughness$ 1 | Description$ Equipped creature gets +1/+1 and has all names of nonlegendary creature cards in addition to its name. -SVar:Picture:http://www.wizards.com/global/images/magic/general/spy_kit.jpg +S:Mode$ Continuous | Affected$ Creature.EquippedBy | AddNames$ AllNonLegendaryCreatureNames | AddPower$ 1 | AddToughness$ 1 | Description$ Equipped creature gets +1/+1 and has all names of nonlegendary creature cards in addition to its name. Oracle:Equipped creature gets +1/+1 and has all names of nonlegendary creature cards in addition to its name.\nEquip {2} diff --git a/forge-gui/res/cardsfolder/upcoming/apprentice_sharpshooter.txt b/forge-gui/res/cardsfolder/upcoming/apprentice_sharpshooter.txt new file mode 100644 index 00000000000..647b0249b42 --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/apprentice_sharpshooter.txt @@ -0,0 +1,8 @@ +Name:Apprentice Sharpshooter +ManaCost:1 G +Types:Creature Human Archer +PT:1/4 +K:Reach +K:Training +DeckHas:Ability$Counters +Oracle:Reach\nTraining (Whenever this creature attacks with another creature with greater power, put a +1/+1 counter on this creature.) diff --git a/forge-gui/res/cardsfolder/upcoming/by_invitation_only.txt b/forge-gui/res/cardsfolder/upcoming/by_invitation_only.txt new file mode 100644 index 00000000000..22bdd3f1b11 --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/by_invitation_only.txt @@ -0,0 +1,7 @@ +Name:By Invitation Only +ManaCost:3 W W +Types:Sorcery +A:SP$ ChooseNumber | Defined$ You | Min$ 0 | Max$ 13 | SubAbility$ DBSac | AILogic$ SweepCreatures | StackDescription$ SpellDescription | SpellDescription$ Choose a number between 0 and 13. Each player sacrifices that many creatures. +SVar:DBSac:DB$ Sacrifice | Defined$ Player | SacValid$ Creature | Amount$ X | StackDescription$ None +SVar:X:Count$ChosenNumber +Oracle:Choose a number between 0 and 13. Each player sacrifices that many creatures. diff --git a/forge-gui/res/cardsfolder/upcoming/dorothea_vengeful_victim_dorotheas_retribution.txt b/forge-gui/res/cardsfolder/upcoming/dorothea_vengeful_victim_dorotheas_retribution.txt new file mode 100644 index 00000000000..02809e3b18a --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/dorothea_vengeful_victim_dorotheas_retribution.txt @@ -0,0 +1,31 @@ +Name:Dorothea, Vengeful Victim +ManaCost:W U +Types:Legendary Creature Spirit +PT:4/4 +K:Flying +T:Mode$ Attacks | ValidCard$ Card.Self | Execute$ DelTrig | TriggerDescription$ When CARDNAME attacks or blocks, sacrifice it at end of combat. +T:Mode$ Blocks | ValidCard$ Card.Self | Execute$ DelTrig | Secondary$ True | TriggerDescription$ Whenever CARDNAME attacks or blocks, sacrifice it at end of combat. +SVar:DelTrig:DB$ DelayedTrigger | Mode$ Phase | Phase$ EndCombat | ValidPlayer$ Player | Execute$ TrigSacrifice | TriggerDescription$ Sacrifice CARDNAME at end of combat. +SVar:TrigSacrifice:DB$ SacrificeAll | Defined$ Self | Controller$ You +SVar:SacrificeEndCombat:True +K:Disturb:1 W U +AlternateMode:DoubleFaced +DeckHas:Ability$Sacrifice & Ability$Graveyard +Oracle:Flying\nWhen Dorothea, Vengeful Victim attacks or blocks, sacrifice it at end of combat.\nDisturb {1}{W}{U} (You may cast this card from your graveyard transformed for its disturb cost.) + +ALTERNATE + +Name:Dorothea's Retribution +ManaCost:no cost +Colors:white,blue +Types:Enchantment Aura +K:Enchant creature +A:SP$ Attach | ValidTgts$ Creature | TgtPrompt$ Select target creature | AILogic$ Pump +S:Mode$ Continuous | Affected$ Creature.EnchantedBy | AddTrigger$ AttackTrigger | AddSVar$ AE | Description$ Enchanted creature has "Whenever this creature attacks, create a 4/4 white Spirit creature token with flying that's tapped and attacking. Sacrifice that token at end of combat." +SVar:AttackTrigger:Mode$ Attacks | ValidCard$ Card.Self | Execute$ TrigToken | TriggerZones$ Battlefield | TriggerDescription$ Whenever this creature attacks, create a 4/4 white Spirit creature token with flying that's tapped and attacking. Sacrifice that token at end of combat. +SVar:TrigToken:DB$ Token | TokenScript$ w_4_4_spirit_flying | TokenTapped$ True | TokenAttacking$ True | AtEOT$ SacrificeCombat +SVar:AE:SVar:HasAttackEffect:TRUE +R:Event$ Moved | ValidCard$ Card.Self | Destination$ Graveyard | ReplaceWith$ Exile | Description$ If CARDNAME would be put into a graveyard from anywhere, exile it instead. +SVar:Exile:DB$ ChangeZone | Hidden$ True | Origin$ All | Destination$ Exile | Defined$ ReplacedCard +DeckHas:Ability$Token & Ability$Sacrifice +Oracle:Enchant creature\nEnchanted creature has "Whenever this creature attacks, create a 4/4 white Spirit creature token with flying that's tapped and attacking. Sacrifice that token at end of combat."\nIf Dorothea's Retribution would be put into a graveyard from anywhere, exile it instead. diff --git a/forge-gui/res/cardsfolder/upcoming/drogskol_infantry_drogskol_armaments.txt b/forge-gui/res/cardsfolder/upcoming/drogskol_infantry_drogskol_armaments.txt new file mode 100644 index 00000000000..810d729c121 --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/drogskol_infantry_drogskol_armaments.txt @@ -0,0 +1,21 @@ +Name:Drogskol Infantry +ManaCost:1 W +Types:Creature Spirit Soldier +PT:2/2 +K:Disturb:3 W +AlternateMode:DoubleFaced +DeckHas:Ability$Graveyard +Oracle:Disturb {3}{W} (You may cast this card from your graveyard transformed for its disturb cost.) + +ALTERNATE + +Name:Drogskol Armaments +ManaCost:no cost +Colors:white +Types:Enchantment Aura +K:Enchant creature +A:SP$ Attach | ValidTgts$ Creature | TgtPrompt$ Select target creature | AILogic$ Pump +S:Mode$ Continuous | Affected$ Creature.EnchantedBy | AddPower$ 2 | AddToughness$ 2 | Description$ Enchanted creature gets +2/+2. +R:Event$ Moved | ValidCard$ Card.Self | Destination$ Graveyard | ReplaceWith$ Exile | Description$ If CARDNAME would be put into a graveyard from anywhere, exile it instead. +SVar:Exile:DB$ ChangeZone | Hidden$ True | Origin$ All | Destination$ Exile | Defined$ ReplacedCard +Oracle:Enchant creature\nEnchanted creature gets +2/+2.\nIf Drogskol Armaments would be put into a graveyard from anywhere, exile it instead. diff --git a/forge-gui/res/cardsfolder/upcoming/gluttonous_guest.txt b/forge-gui/res/cardsfolder/upcoming/gluttonous_guest.txt new file mode 100644 index 00000000000..769ef95e269 --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/gluttonous_guest.txt @@ -0,0 +1,10 @@ +Name:Gluttonous Guest +ManaCost:2 B +Types:Creature Vampire +PT:1/4 +T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigToken | TriggerDescription$ When CARDNAME enters the battlefield, create a Blood token. (It's an artifact with "{1}, {T}, Discard a card, Sacrifice this artifact: Draw a card.") +SVar:TrigToken:DB$ Token | TokenScript$ c_a_blood_draw +T:Mode$ Sacrificed | ValidCard$ Blood.token+YouCtrl | Execute$ TrigGainLife | TriggerZones$ Battlefield | TriggerDescription$ Whenever you sacrifice a Blood token, you gain 1 life. +SVar:TrigGainLife:DB$ GainLife | Defined$ You | LifeAmount$ 1 +DeckHas:Ability$Token & Ability$Sacrifice & Ability$LifeGain & Type$Blood +Oracle:When Gluttonous Guest enters the battlefield, create a Blood token. (It's an artifact with "{1}, {T}, Discard a card, Sacrifice this artifact: Draw a card.")\nWhenever you sacrifice a Blood token, you gain 1 life. diff --git a/forge-gui/res/cardsfolder/upcoming/grolnok_the_omnivore.txt b/forge-gui/res/cardsfolder/upcoming/grolnok_the_omnivore.txt new file mode 100644 index 00000000000..dbcb504e0be --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/grolnok_the_omnivore.txt @@ -0,0 +1,11 @@ +Name:Grolnok, the Omnivore +ManaCost:2 G U +Types:Legendary Creature Frog +PT:3/3 +T:Mode$ Attacks | ValidCard$ Frog.YouCtrl | TriggerZones$ Battlefield | Execute$ TrigMill | TriggerDescription$ Whenever a Frog you control attacks, mill three cards. +SVar:TrigMill:DB$ Mill | NumCards$ 3 | Defined$ You +T:Mode$ ChangesZone | ValidCard$ Permanent.YouOwn | Origin$ Library | Destination$ Graveyard | TriggerZones$ Battlefield | Execute$ TrigExile | TriggerDescription$ Whenever a permanent card is put into your graveyard from your library, exile it with a croak counter on it. +SVar:TrigExile:DB$ ChangeZone | Origin$ Graveyard | Destination$ Exile | Defined$ TriggeredCard | WithCountersType$ CROAK | WithCountersAmount$ 1 +S:Mode$ Continuous | Affected$ Card.YouOwn+counters_GE1_CROAK | AffectedZone$ Exile | MayPlay$ True | Description$ You may play lands and cast noncreature spells from among cards you own in exile with croak counters on them. +DeckHas:Ability$Mill +Oracle:Whenever a Frog you control attacks, mill three cards.\nWhenever a permanent card is put into your graveyard from your library, exile it with a croak counter on it.\nYou may play lands and cast spells from among cards you own in exile with croak counters on them. diff --git a/forge-gui/res/cardsfolder/upcoming/gryff_rider.txt b/forge-gui/res/cardsfolder/upcoming/gryff_rider.txt new file mode 100644 index 00000000000..4127701bffc --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/gryff_rider.txt @@ -0,0 +1,8 @@ +Name:Gryff Rider +ManaCost:2 W +Types:Creature Human Knight +PT:2/1 +K:Flying +K:Training +DeckHas:Ability$Counters +Oracle:Flying\nTraining (Whenever this creature attacks with another creature with greater power, put a +1/+1 counter on this creature.) diff --git a/forge-gui/res/cardsfolder/upcoming/massive_might.txt b/forge-gui/res/cardsfolder/upcoming/massive_might.txt new file mode 100644 index 00000000000..3acc79da87f --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/massive_might.txt @@ -0,0 +1,5 @@ +Name:Massive Might +ManaCost:G +Types:Instant +A:SP$ Pump | ValidTgts$ Creature | TgtPrompt$ Select target creature | NumAtt$ 2 | NumDef$ 2 | KW$ Trample | SpellDescription$ Target creature gets +2/+2 and gains trample until end of turn. +Oracle:Target creature gets +2/+2 and gains trample until end of turn. diff --git a/forge-gui/res/cardsfolder/upcoming/millicent_restless_revenant.txt b/forge-gui/res/cardsfolder/upcoming/millicent_restless_revenant.txt new file mode 100644 index 00000000000..c4de476aa80 --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/millicent_restless_revenant.txt @@ -0,0 +1,13 @@ +Name:Millicent, Restless Revenant +ManaCost:5 W U +Types:Legendary Creature Spirit Soldier +PT:4/4 +K:Flying +S:Mode$ ReduceCost | ValidCard$ Card.Self | Type$ Spell | Amount$ X | EffectZone$ All | Description$ This spell costs {1} less to cast for each Spirit you control. +SVar:X:Count$TypeYouCtrl.Spirit +T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | ValidCard$ Card.Self,Spirit.Other+nonToken+YouCtrl | Execute$ TrigToken | TriggerDescription$ Whenever CARDNAME or another nontoken Spirit you control dies or deals combat damage to a player, create a 1/1 white Spirit creature token with flying. +T:Mode$ DamageDone | ValidSource$ Card.Self,Spirit.Other+nonToken+YouCtrl | ValidTarget$ Player | Execute$ TrigToken | CombatDamage$ True | Secondary$ True | TriggerDescription$ Whenever CARDNAME or another nontoken Spirit you control dies or deals combat damage to a player, create a 1/1 white Spirit creature token with flying. +SVar:TrigToken:DB$ Token | TokenScript$ w_1_1_spirit_flying +DeckHints:Type$Spirit & Ability$Disturb +DeckHas:Ability$Token +Oracle:This spell costs {1} less to cast for each Spirit you control.\nFlying\nWhenever Millicent, Restless Revenant or another nontoken Spirit you control dies or deals combat damage to a player, create a 1/1 white Spirit creature token with flying. diff --git a/forge-gui/res/cardsfolder/upcoming/olivia_crimson_bride.txt b/forge-gui/res/cardsfolder/upcoming/olivia_crimson_bride.txt new file mode 100644 index 00000000000..92af7a3a0a8 --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/olivia_crimson_bride.txt @@ -0,0 +1,15 @@ +Name:Olivia, Crimson Bride +ManaCost:4 B R +Types:Legendary Creature Vampire Noble +PT:3/4 +K:Flying +K:Haste +T:Mode$ Attacks | ValidCard$ Card.Self | Execute$ TrigChange | TriggerZones$ Battlefield | TriggerDescription$ Whenever CARDNAME attacks, return target creature card from your graveyard to the battlefield tapped and attacking. It gains "When you don't control a legendary Vampire, exile this creature." +SVar:TrigChange:DB$ ChangeZone | Origin$ Graveyard | Destination$ Battlefield | ValidTgts$ Creature.YouOwn | Tapped$ True | Attacking$ True | RememberChanged$ True | AnimateSubAbility$ DBAnimate +SVar:DBAnimate:DB$ Animate | Defined$ Remembered | Duration$ Permanent | Triggers$ TrigOlivia +SVar:TrigOlivia:Mode$ Always | TriggerZones$ Battlefield | IsPresent$ Vampire.YouCtrl+Legendary | PresentCompare$ EQ0 | Execute$ TrigExile | TriggerDescription$ When you don't control a legendary Vampire, exile this creature. +SVar:TrigExile:DB$ ChangeZone | Defined$ Self | Origin$ Battlefield | Destination$ Exile +SVar:HasAttackEffect:TRUE +DeckHas:Ability$Graveyard +DeckHints:Type$Vampire|Legendary +Oracle:Flying, haste\nWhenever Olivia, Crimson Bride attacks, return target creature card from your graveyard to the battlefield tapped and attacking. It gains "When you don't control a legendary Vampire, exile this creature." diff --git a/forge-gui/res/cardsfolder/upcoming/overcharged_amalgam.txt b/forge-gui/res/cardsfolder/upcoming/overcharged_amalgam.txt new file mode 100644 index 00000000000..fb5d7bdca93 --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/overcharged_amalgam.txt @@ -0,0 +1,12 @@ +Name:Overcharged Amalgam +ManaCost:2 U U +Types:Creature Zombie Horror +PT:2/2 +K:Flash +K:Flying +K:Exploit +T:Mode$ Exploited | ValidCard$ Creature | ValidSource$ Card.Self | TriggerZones$ Battlefield | Execute$ TrigCounter | TriggerDescription$ When CARDNAME exploits a creature, counter target spell, activated ability, or triggered ability. +SVar:TrigCounter:DB$ Counter | TargetType$ Spell,Activated,Triggered | TgtPrompt$ Select target spell or ability | ValidTgts$ Card | SpellDescription$ Counter target spell, activated ability, or triggered ability. +AI:RemoveDeck:All +DeckHas:Ability$Sacrifice +Oracle:Flash\nFlying\nExploit (When this creature enters the battlefield, you may sacrifice a creature.)\nWhen Overcharged Amalgam exploits a creature, counter target spell, activated ability, or triggered ability. diff --git a/forge-gui/res/cardsfolder/upcoming/rot_tide_gargantua.txt b/forge-gui/res/cardsfolder/upcoming/rot_tide_gargantua.txt new file mode 100644 index 00000000000..6590217309c --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/rot_tide_gargantua.txt @@ -0,0 +1,9 @@ +Name:Rot-Tide Gargantua +ManaCost:3 B B +Types:Creature Zombie Kraken +PT:5/4 +K:Exploit +T:Mode$ Exploited | ValidCard$ Creature | ValidSource$ Card.Self | TriggerZones$ Battlefield | Execute$ TrigSac | TriggerDescription$ When CARDNAME exploits a creature, each opponent sacrifices a creature. +SVar:TrigSac:DB$ Sacrifice | Defined$ Player.Opponent | SacValid$ Creature +DeckHas:Ability$Sacrifice +Oracle:Exploit (When this creature enters the battlefield, you may sacrifice a creature.)\nWhen Rot-Tide Gargantua exploits a creature, each opponent sacrifices a creature. diff --git a/forge-gui/res/cardsfolder/upcoming/savior_of_ollenbock.txt b/forge-gui/res/cardsfolder/upcoming/savior_of_ollenbock.txt new file mode 100644 index 00000000000..d5ce78c2f30 --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/savior_of_ollenbock.txt @@ -0,0 +1,11 @@ +Name:Savior of Ollenbock +ManaCost:1 W W +Types:Creature Human Soldier +PT:1/2 +K:Training +T:Mode$ Trains | ValidCard$ Card.Self | Execute$ TrigExile | TriggerZones$ Battlefield | TriggerDescription$ Whenever CARDNAME trains, exile up to one other target creature from the battlefield or creature card from a graveyard. +SVar:TrigExile:DB$ ChangeZone | Origin$ Battlefield,Graveyard | Destination$ Exile | ValidTgts$ Creature.Other | TgtPrompt$ Select up to one other target creature from the battlefield or creature card from a graveyard | TargetMin$ 0 | TargetMax$ 1 | TgtZone$ Battlefield,Graveyard | AILogic$ SaviorOfOllenbock +T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Any | ValidCard$ Card.Self | Execute$ TrigChange | TriggerDescription$ When CARDNAME leaves the battlefield, put the exiled cards onto the battlefield under their owners' control. +SVar:TrigChange:DB$ ChangeZoneAll | ChangeType$ Card.ExiledWithSource | Origin$ Exile | Destination$ Battlefield +DeckHas:Ability$Counters +Oracle:Training (Whenever this creature attacks with another creature with greater power, put a +1/+1 counter on this creature.)\nWhenever Savior of Ollenbock trains, exile up to one other target creature from the battlefield or creature card from a graveyard.\nWhen Savior of Ollenbock leaves the battlefield, put the exiled cards onto the battlefield under their owners' control. diff --git a/forge-gui/res/cardsfolder/upcoming/sigardas_summons.txt b/forge-gui/res/cardsfolder/upcoming/sigardas_summons.txt new file mode 100644 index 00000000000..58e7fefa5cd --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/sigardas_summons.txt @@ -0,0 +1,8 @@ +Name:Sigarda's Summons +ManaCost:4 W W +Types:Enchantment +S:Mode$ Continuous | Affected$ Creature.YouCtrl+counters_GE1_P1P1 | AddKeyword$ Flying | AddType$ Angel | SetPower$ 4 | SetToughness$ 4 | Description$ Creatures you control with +1/+1 counters on them have base power and toughness 4/4, have flying, and are Angels in addition to their other types. +SVar:PlayMain1:TRUE +DeckNeeds:Ability$Counters +DeckHints:Type$Angel +Oracle:Creatures you control with +1/+1 counters on them have base power and toughness 4/4, have flying, and are Angels in addition to their other types. diff --git a/forge-gui/res/cardsfolder/upcoming/sorin_the_mirthless.txt b/forge-gui/res/cardsfolder/upcoming/sorin_the_mirthless.txt new file mode 100644 index 00000000000..10170144d34 --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/sorin_the_mirthless.txt @@ -0,0 +1,14 @@ +Name:Sorin the Mirthless +ManaCost:2 B B +Types:Legendary Planeswalker Sorin +Loyalty:4 +A:AB$ PeekAndReveal | Cost$ AddCounter<1/LOYALTY> | Planeswalker$ True | PeekAmount$ 1 | RevealOptional$ True | RememberRevealed$ True | SubAbility$ DBChangeZone | SpellDescription$ Look at the top card of your library. You may reveal that card and put it into your hand. If you do, you lose life equal to its mana value. +SVar:DBChangeZone:DB$ ChangeZone | Defined$ TopOfLibrary | Origin$ Library | Destination$ Hand | ConditionDefined$ Remembered | ConditionPresent$ Card | ConditionCompare$ EQ1 | SubAbility$ DBLoseLife | StackDescription$ None +SVar:DBLoseLife:DB$ LoseLife | LifeAmount$ X | ConditionDefined$ Remembered | ConditionPresent$ Card | ConditionCompare$ EQ1 | SubAbility$ DBCleanup | StackDescription$ None +SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True +SVar:X:Remembered$CardManaCost +A:AB$ Token | Cost$ SubCounter<2/LOYALTY> | Planeswalker$ True | TokenScript$ b_2_3_vampire_flying_lifelink | SpellDescription$ Create a 2/3 black Vampire creature token with flying and lifelink. +A:AB$ DealDamage | Cost$ SubCounter<7/LOYALTY> | Planeswalker$ True | Ultimate$ True | ValidTgts$ Creature,Player,Planeswalker | TgtPrompt$ Select any target | NumDmg$ 13 | SubAbility$ DBGainLife | SpellDescription$ CARDNAME deals 13 damage to any target. You gain 13 life. +SVar:DBGainLife:DB$ GainLife | LifeAmount$ 13 +DeckHas:Ability$Token & Ability$LifeGain & Type$Vampire +Oracle:[+1]: Look at the top card of your library. You may reveal that card and put it into your hand. If you do, you lose life equal to its mana value.\n[−2]: Create a 2/3 black Vampire creature token with flying and lifelink.\n[−7]: Sorin the Mirthless deals 13 damage to any target. You gain 13 life. diff --git a/forge-gui/res/cardsfolder/upcoming/strefan_maurer_progenitor.txt b/forge-gui/res/cardsfolder/upcoming/strefan_maurer_progenitor.txt new file mode 100644 index 00000000000..cd5b95581c2 --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/strefan_maurer_progenitor.txt @@ -0,0 +1,16 @@ +Name:Strefan, Maurer Progenitor +ManaCost:2 B R +Types:Legendary Creature Vampire Noble +PT:3/2 +K:Flying +T:Mode$ Phase | Phase$ End of Turn | ValidPlayer$ You | TriggerZones$ Battlefield | Execute$ TrigToken | TriggerDescription$ At the beginning of your end step, create a Blood token for each player who lost life this turn. +SVar:TrigToken:DB$ Token | TokenAmount$ X | TokenScript$ c_a_blood_draw +SVar:X:PlayerCountPlayers$HasPropertyLostLifeThisTurn +T:Mode$ Attacks | ValidCard$ Card.Self | Execute$ TrigChangeZone | OptionalDecider$ You | TriggerDescription$ Whenever NICKNAME attacks, you may sacrifice two Blood tokens. If you do, you may put a Vampire card from your hand onto the battlefield tapped and attacking. It gains indestructible until end of turn. +SVar:TrigChangeZone:AB$ ChangeZone | Cost$ Sac<2/Blood.token/Blood token> | Origin$ Hand | Destination$ Battlefield | SelectPrompt$ You may select a Vampire card from your hand | ChangeType$ Vampire | Tapped$ True | Attacking$ True | RememberChanged$ True | SubAbility$ DBPump +SVar:DBPump:DB$ Pump | Defined$ Self | KW$ Indestructible | SubAbility$ DBCleanup +SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True +SVar:HasAttackEffect:TRUE +DeckHas:Ability$Token & Ability$Sacrifice +DeckHints:Type$Vampire +Oracle:Flying\nAt the beginning of your end step, create a Blood token for each player who lost life this turn.\nWhenever Strefan attacks, you may sacrifice two Blood tokens. If you do, you may put a Vampire card from your hand onto the battlefield tapped and attacking. It gains indestructible until end of turn. diff --git a/forge-gui/res/cardsfolder/upcoming/torens_fist_of_the_angels.txt b/forge-gui/res/cardsfolder/upcoming/torens_fist_of_the_angels.txt new file mode 100644 index 00000000000..6f5a1cfd012 --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/torens_fist_of_the_angels.txt @@ -0,0 +1,9 @@ +Name:Torens, Fist of the Angels +ManaCost:1 G W +Types:Legendary Creature Human Cleric +PT:2/2 +K:Training +T:Mode$ SpellCast | ValidCard$ Creature | ValidActivatingPlayer$ You | Execute$ TrigToken | TriggerZones$ Battlefield | TriggerDescription$ Whenever you cast a creature spell, create a 1/1 green and white Human Soldier creature token with training. +SVar:TrigToken:DB$ Token | TokenScript$ gw_1_1_human_soldier_training +DeckHas:Ability$Counters & Ability$Token +Oracle:Training (Whenever this creature attacks with another creature with greater power, put a +1/+1 counter on this creature.)\nWhenever you cast a creature spell, create a 1/1 green and white Human Soldier creature token with training. diff --git a/forge-gui/res/cardsfolder/upcoming/voldaren_bloodcaster_bloodbat_summoner.txt b/forge-gui/res/cardsfolder/upcoming/voldaren_bloodcaster_bloodbat_summoner.txt new file mode 100644 index 00000000000..701703d1d3b --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/voldaren_bloodcaster_bloodbat_summoner.txt @@ -0,0 +1,25 @@ +Name:Voldaren Bloodcaster +ManaCost:1 B +Types:Creature Vampire Wizard +PT:2/1 +K:Flying +T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | ValidCard$ Card.Self,Creature.Other+nonToken+YouCtrl | Execute$ TrigToken | TriggerDescription$ Whenever CARDNAME or another nontoken creature you control dies, create a Blood token. (It's an artifact with "{1}, {T}, Discard a card, Sacrifice this artifact: Draw a card.") +SVar:TrigToken:DB$ Token | TokenScript$ c_a_blood_draw +T:Mode$ TokenCreated | ValidPlayer$ You | ValidToken$ Blood | IsPresent$ Blood.token+YouCtrl | PresentCompare$ GE5 | Execute$ TrigTransform | TriggerZone$ Battlefield | TriggerDescription$ Whenever you create a Blood token, if you control five or more Blood tokens, transform CARDNAME. +SVar:TrigTransform:DB$ SetState | Defined$ Self | Mode$ Transform +AlternateMode:DoubleFaced +DeckHas:Ability$Token & Ability$Sacrifice & Type$Blood +Oracle:Flying\nWhenever Voldaren Bloodcaster or another nontoken creature you control dies, create a Blood token. (It's an artifact with "{1}, {T}, Discard a card, Sacrifice this artifact: Draw a card.")\nWhenever you create a Blood token, if you control five or more Blood tokens, transform Voldaren Bloodcaster. + +ALTERNATE + +Name:Bloodbat Summoner +ManaCost:no cost +Colors:black +Types:Creature Vampire Wizard +PT:3/3 +K:Flying +T:Mode$ Phase | Phase$ BeginCombat | ValidPlayer$ You | Execute$ TrigAnimate | TriggerZones$ Battlefield | TriggerDescription$ At the beginning of combat on your turn, up to one target Blood token you control becomes a 2/2 black Bat creature with flying and haste in addition to its other types. +SVar:TrigAnimate:DB$ Animate | ValidTgts$ Blood.token+YouCtrl | TgtPrompt$ Select up to one target Blood token you control | TargetMin$ 0 | TargetMax$ 1 | Power$ 2 | Toughness$ 2 | Colors$ Black | OverwriteColors$ True | Types$ Creature,Bat | Keywords$ Flying & Haste | Duration$ Permanent +DeckHas:Type$Bat +Oracle:Flying\nAt the beginning of combat on your turn, up to one target Blood token you control becomes a 2/2 black Bat creature with flying and haste in addition to its other types. diff --git a/forge-gui/res/cardsfolder/upcoming/voldaren_estate.txt b/forge-gui/res/cardsfolder/upcoming/voldaren_estate.txt new file mode 100644 index 00000000000..ba5873b133f --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/voldaren_estate.txt @@ -0,0 +1,10 @@ +Name:Voldaren Estate +ManaCost:no cost +Types:Land +A:AB$ Mana | Cost$ T | Produced$ C | SpellDescription$ Add {C}. +A:AB$ Mana | Cost$ T PayLife<1> | Produced$ Any | Amount$ 1 | RestrictValid$ Vampire | SpellDescription$ Add one mana of any color. Spend this mana only to cast a Vampire spell. +A:AB$ Token | Cost$ 5 T | TokenScript$ c_a_blood_draw | ReduceCost$ X | SpellDescription$ Create a Blood token. This ability costs {1} less to activate for each Vampire you control. (It's an artifact with "{1}, {T}, Discard a card, Sacrifice this artifact: Draw a card.") +SVar:X:Count$TypeYouCtrl.Vampire +DeckNeeds:Type$Vampire +DeckHas:Ability$Token & Ability$Sacrifice & Type$Blood +Oracle:{T}: Add {C}.\n{T}, Pay 1 life: Add one mana of any color. Spend this mana only to cast a Vampire spell.\n{5}, {T}: Create a Blood token. This ability costs {1} less to activate for each Vampire you control. (It's an artifact with "{1}, {T}, Discard a card, Sacrifice this artifact: Draw a card.") diff --git a/forge-gui/res/cardsfolder/upcoming/weary_prisoner_wrathful_jailbreaker.txt b/forge-gui/res/cardsfolder/upcoming/weary_prisoner_wrathful_jailbreaker.txt new file mode 100644 index 00000000000..692815aa907 --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/weary_prisoner_wrathful_jailbreaker.txt @@ -0,0 +1,19 @@ +Name:Weary Prisoner +ManaCost:3 R +Types:Creature Human Werewolf +PT:2/6 +K:Defender +K:Daybound +AlternateMode:DoubleFaced +Oracle:Defender\nDaybound (If a player casts no spells during their own turn, it becomes night next turn.) + +ALTERNATE + +Name:Wrathful Jailbreaker +ManaCost:no cost +Colors:red +Types:Creature Werewolf +PT:6/6 +K:CARDNAME attacks each combat if able. +K:Nightbound +Oracle:Wrathful Jailbreaker attacks each combat if able.\nNightbound (If a player casts at least two spells during their own turn, it becomes day next turn.) diff --git a/forge-gui/res/cardsfolder/upcoming/wedding_announcement_wedding_festivity.txt b/forge-gui/res/cardsfolder/upcoming/wedding_announcement_wedding_festivity.txt new file mode 100644 index 00000000000..f7898458495 --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/wedding_announcement_wedding_festivity.txt @@ -0,0 +1,22 @@ +Name:Wedding Announcement +ManaCost:2 W +Types:Enchantment +T:Mode$ Phase | Phase$ End of Turn | ValidPlayer$ You | TriggerZones$ Battlefield | Execute$ TrigPutCounter | TriggerDescription$ At the beginning of your end step, put an invitation counter on CARDNAME. If you attacked with two or more creatures this turn, draw card. Otherwise, create a 1/1 white Human creature token. Then if CARDNAME has three or more invitation counters on it, transform it. +SVar:TrigPutCounter:DB$ PutCounter | Defined$ Self | CounterType$ INVITATION | CounterNum$ 1 | SubAbility$ DBBranch +SVar:DBBranch:DB$ Branch | BranchConditionSVar$ X | BranchConditionSVarCompare$ GE2 | TrueSubAbility$ DBDraw | FalseSubAbility$ DBToken +SVar:X:Count$AttackersDeclared +SVar:DBDraw:DB$ Draw | NumCards$ 1 | SubAbility$ DBTransform +SVar:DBToken:DB$ Token | TokenScript$ w_1_1_human | SubAbility$ DBTransform +SVar:DBTransform:DB$ SetState | Defined$ Self | ConditionDefined$ Self | ConditionPresent$ Card.counters_GE3_INVITATION | Mode$ Transform +AlternateMode:DoubleFaced +DeckHas:Ability$Counters & Ability$Token +Oracle:At the beginning of your end step, put an invitation counter on Wedding Announcement. If you attacked with two or more creatures this turn, draw card. Otherwise, create a 1/1 white Human creature token. Then if Wedding Announcement has three or more invitation counters on it, transform it. + +ALTERNATE + +Name:Wedding Festivity +ManaCost:no cost +Colors:white +Types:Enchantment +S:Mode$ Continuous | Affected$ Creature.YouCtrl | AddPower$ 1 | AddToughness$ 1 | Description$ Creatures you control get +1/+1. +Oracle:Creatures you control get +1/+1. diff --git a/forge-gui/res/cardsfolder/upcoming/wedding_invitation.txt b/forge-gui/res/cardsfolder/upcoming/wedding_invitation.txt new file mode 100644 index 00000000000..b3995b57991 --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/wedding_invitation.txt @@ -0,0 +1,10 @@ +Name:Wedding Invitation +ManaCost:2 +Types:Artifact +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 +A:AB$ Pump | Cost$ 1 Sac<1/CARDNAME> | ValidTgts$ Creature | TgtPrompt$ Select target creature | KW$ HIDDEN Unblockable | AITgts$ Vampire | SubAbility$ DBPump | StackDescription$ {c:Targeted} can't be blocked this turn. | SpellDescription$ Target creature can't be blocked this turn. If it's a Vampire, it also gains lifelink until end of turn. +SVar:DBPump:DB$ Pump | Defined$ Targeted.Vampire | KW$ Lifelink | StackDescription$ If it's a Vampire, it also gains lifelink until end of turn. +DeckHints:Type$Vampire +DeckHas:Ability$LifeGain +Oracle:When Wedding Invitation enters the battlefield, draw a card.\n{T}, Sacrifice Wedding Invitation: Target creature can't be blocked this turn. If it's a Vampire, it also gains lifelink until end of turn. diff --git a/forge-gui/res/cardsfolder/upcoming/wedding_ring.txt b/forge-gui/res/cardsfolder/upcoming/wedding_ring.txt new file mode 100644 index 00000000000..23820e0101c --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/wedding_ring.txt @@ -0,0 +1,12 @@ +Name:Wedding Ring +ManaCost:2 W W +Types:Artifact +T:Mode$ ChangesZone | ValidCard$ Card.Self+wasCast | Origin$ Any | Destination$ Battlefield | Execute$ TrigCopy | TriggerDescription$ When CARDNAME enters the battlefield, if it was cast, target opponent creates a token that's a copy of it. +SVar:TrigCopy:DB$ CopyPermanent | ValidTgts$ Opponent | TgtPrompt$ Select target opponent | Defined$ Self | Controller$ TargetedPlayer +T:Mode$ Drawn | ValidCard$ Card.OppOwn | ValidPlayer$ Player.Opponent+Active | ValidPlayerControls$ Card.namedWedding Ring | TriggerZones$ Battlefield | Execute$ TrigDraw | TriggerDescription$ Whenever an opponent who controls an artifact named Wedding Ring draws a card during their turn, you draw a card. +SVar:TrigDraw:DB$ Draw | Defined$ You | NumCards$ 1 +T:Mode$ LifeGained | ValidPlayer$ Player.Opponent+Active | ValidPlayerControls$ Card.namedWedding Ring | TriggerZones$ Battlefield | Execute$ TrigGainLife | TriggerDescription$ Whenever an opponent who controls an artifact named Wedding Ring gains life during their turn, you gain that much life. +SVar:TrigGainLife:DB$ GainLife | Defined$ You | LifeAmount$ X +SVar:X:TriggerCount$LifeAmount +DeckHas:Ability$LifeGain +Oracle:When Wedding Ring enters the battlefield, if it was cast, target opponent creates a token that's a copy of it.\n\nWhenever an opponent who controls an artifact named Wedding Ring draws a card during their turn, you draw a card.\nWhenever an opponent who controls an artifact named Wedding Ring gains life during their turn, you gain that much life. diff --git a/forge-gui/res/cardsfolder/v/volraths_shapeshifter.txt b/forge-gui/res/cardsfolder/v/volraths_shapeshifter.txt index 6d145adde29..c4f16a88aef 100644 --- a/forge-gui/res/cardsfolder/v/volraths_shapeshifter.txt +++ b/forge-gui/res/cardsfolder/v/volraths_shapeshifter.txt @@ -3,11 +3,10 @@ ManaCost:1 U U Types:Creature Phyrexian Shapeshifter PT:0/1 A:AB$ Discard | Cost$ 2 | Defined$ You | NumCards$ 1 | Mode$ TgtChoose | AILogic$ VolrathsShapeshifter | SpellDescription$ Discard a card. -S:Mode$ Continuous | Affected$ Card.Self | EffectZone$ Battlefield | GainTextOf$ Creature.TopGraveyard+YouCtrl | GainTextAbilities$ VolrathDiscard | Description$ As long as the top card of your graveyard is a creature card, CARDNAME has the full text of that card and has the text "{2}: Discard a card." (CARDNAME has that card's name, mana cost, color, types, abilities, power, and toughness.) +S:Mode$ Continuous | Affected$ Card.Self | EffectZone$ Battlefield | GainTextOf$ TopOfGraveyard.Creature | GainTextAbilities$ VolrathDiscard | Description$ As long as the top card of your graveyard is a creature card, CARDNAME has the full text of that card and has the text "{2}: Discard a card." (CARDNAME has that card's name, mana cost, color, types, abilities, power, and toughness.) SVar:VolrathDiscard:AB$ Discard | Cost$ 2 | Defined$ You | NumCards$ 1 | Mode$ TgtChoose | AILogic$ VolrathsShapeshifter | SpellDescription$ Discard a card. SVar:NeedsOrderedGraveyard:TRUE -SVar:Picture:http://www.wizards.com/global/images/magic/general/volraths_shapeshifter.jpg Oracle:As long as the top card of your graveyard is a creature card, Volrath's Shapeshifter has the full text of that card and has the text "{2}: Discard a card." (Volrath's Shapeshifter has that card's name, mana cost, color, types, abilities, power, and toughness.)\n{2}: Discard a card. diff --git a/forge-gui/res/cardsfolder/w/whimsy.txt b/forge-gui/res/cardsfolder/w/whimsy.txt index e37a06cbc71..48bc65ad822 100644 --- a/forge-gui/res/cardsfolder/w/whimsy.txt +++ b/forge-gui/res/cardsfolder/w/whimsy.txt @@ -1,7 +1,7 @@ Name:Whimsy ManaCost:X U U Types:Sorcery -A:SP$ Repeat | MaxRepeat$ X | RepeatSubAbility$ DBGenericChoice | StackDescription$ SpellDescription | SpellDescription$ Perform X random actions. +A:SP$ Repeat | MaxRepeat$ X | RepeatSubAbility$ DBGenericChoice | AILogic$ MaxX | StackDescription$ SpellDescription | SpellDescription$ Perform X random actions. SVar:DBGenericChoice:DB$ GenericChoice | ShowChoice$ Description | AtRandom$ True | Choices$ ToHand,Untap,Tap,Damage,Draw3,DestroyGain,DestroyAE,Gain3,Anoint,DestroyCL,Mill2,Wasp,Nevinyrral,Suleiman,Pandora,Discard,Fog,Sindbad SVar:ToHand:DB$ ChooseCard | Choices$ Permanent.unenchanted | AtRandom$ True | SubAbility$ DBChangeZone3 | SpellDescription$ Return a permanent that isn't enchanted chosen at random to its owner's hand. SVar:DBChangeZone3:DB$ ChangeZone | Defined$ ChosenCard | Origin$ Battlefield | Destination$ Hand | SubAbility$ DBCleanup diff --git a/forge-gui/res/conquest/planes/Forgotten_Realms/Cave of the Frost Dragon/Braids, Conjurer Adept.dck b/forge-gui/res/conquest/planes/Forgotten_Realms/Cave of the Frost Dragon/Braids, Conjurer Adept.dck new file mode 100644 index 00000000000..d69a2b2c164 --- /dev/null +++ b/forge-gui/res/conquest/planes/Forgotten_Realms/Cave of the Frost Dragon/Braids, Conjurer Adept.dck @@ -0,0 +1,38 @@ +[metadata] +Name=Braids, Conjurer Adept +[Avatar] +1 Braids, Conjurer Adept Avatar|VAN|1 +[Main] +1 Angel of Finality|AFC|1 +1 Argentum Armor|AFC|1 +1 Blink Dog|AFR|1 +1 Cave of the Frost Dragon|AFR|1 +1 Clay Golem|AFC|1 +1 Desert|AFC|1 +1 Devoted Paladin|AFR|1 +1 Dungeon Descent|AFR|1 +1 Fey Steed|AFC|1 +1 Flumph|AFR|2 +1 Grand Master of Flowers|AFR|1 +1 Holy Avenger|AFC|1 +1 Icingdeath, Frost Tyrant|AFR|1 +1 Mishra's Factory|AFC|1 +1 Monk of the Open Hand|AFR|1 +1 Moonsilver Spear|AFC|1 +1 Nadaar, Selfless Paladin|AFR|1 +1 Paladin Class|AFR|1 +4 Plains|AFR|2 +3 Plains|AFR|3 +4 Plains|AFR|4 +1 Planar Ally|AFR|1 +1 Radiant Solar|AFC|1 +1 Rally Maneuver|AFR|1 +1 Sol Ring|AFC|1 +1 Sram, Senior Edificer|AFC|1 +1 Steadfast Paladin|AFR|1 +1 The Deck of Many Things|AFR|1 +1 Thorough Investigation|AFC|1 +1 Treasure Chest|AFR|1 +1 Wall of Omens|AFC|1 +1 White Dragon|AFR|1 +[Sideboard] diff --git a/forge-gui/res/conquest/planes/Forgotten_Realms/Cave of the Frost Dragon/Bruenor Battlehammer.dck b/forge-gui/res/conquest/planes/Forgotten_Realms/Cave of the Frost Dragon/Bruenor Battlehammer.dck new file mode 100644 index 00000000000..2044a0abcaf --- /dev/null +++ b/forge-gui/res/conquest/planes/Forgotten_Realms/Cave of the Frost Dragon/Bruenor Battlehammer.dck @@ -0,0 +1,39 @@ +[metadata] +Name=Bruenor Battlehammer +[Commander] +1 Bruenor Battlehammer|AFR|1 +[Main] +1 +2 Mace|AFR|1 +1 Argentum Armor|AFC|1 +1 Command Tower|AFC|1 +1 Den of the Bugbear|AFR|1 +1 Dungeon Descent|AFR|1 +1 Dwarfhold Champion|AFR|1 +1 Evolving Wilds|AFR|1 +1 Fiendlash|AFC|2 +1 Fighter Class|AFR|1 +1 Gloom Stalker|AFR|1 +1 Greataxe|AFR|1 +1 Kick in the Door|AFR|1 +1 Mantle of the Ancients|AFC|1 +1 Masterwork of Ingenuity|AFC|1 +1 Mountain|AFR|1 +2 Mountain|AFR|2 +2 Mountain|AFR|3 +1 Nadaar, Selfless Paladin|AFR|1 +3 Plains|AFR|1 +2 Plains|AFR|2 +2 Plains|AFR|3 +1 Plate Armor|AFR|1 +1 Plundering Barbarian|AFR|1 +1 Priest of Ancient Lore|AFR|1 +1 Puresteel Paladin|AFC|1 +1 Rally Maneuver|AFR|1 +1 Ranger's Hawk|AFR|1 +1 Sram, Senior Edificer|AFC|1 +1 Steadfast Paladin|AFR|1 +1 Sword of Hours|AFC|1 +1 Veteran Dungeoneer|AFR|1 +1 You Come to the Gnoll Camp|AFR|1 +1 Zalto, Fire Giant Duke|AFR|1 +[Sideboard] diff --git a/forge-gui/res/conquest/planes/Forgotten_Realms/Cave of the Frost Dragon/Drizzt Do'Urden.dck b/forge-gui/res/conquest/planes/Forgotten_Realms/Cave of the Frost Dragon/Drizzt Do'Urden.dck new file mode 100644 index 00000000000..4d969275fb5 --- /dev/null +++ b/forge-gui/res/conquest/planes/Forgotten_Realms/Cave of the Frost Dragon/Drizzt Do'Urden.dck @@ -0,0 +1,40 @@ +[metadata] +Name=Drizzt Do'Urden +[Commander] +1 Drizzt Do'Urden|AFR|1 +[Main] +1 Abundant Growth|AFC|1 +1 Bull's Strength|AFR|1 +1 Canopy Vista|AFC|1 +1 Command Tower|AFC|1 +1 Dancing Sword|AFR|1 +1 Dawnbringer Cleric|AFR|1 +1 Dire Wolf Prowler|AFR|1 +1 Dwarfhold Champion|AFR|1 +1 Elturgard Ranger|AFR|1 +1 Evolving Wilds|AFR|1 +1 Find the Path|AFR|1 +2 Forest|AFR|1 +3 Forest|AFR|2 +1 Fortified Village|AFC|1 +1 Grasslands|AFC|1 +1 Hunter's Mark|AFR|1 +1 Indomitable Might|AFC|1 +1 Neverwinter Dryad|AFR|1 +1 Ochre Jelly|AFR|1 +3 Plains|AFR|1 +1 Plains|AFR|3 +1 Rally Maneuver|AFR|1 +1 Ranger Class|AFR|1 +1 Ranger's Longbow|AFR|1 +1 Swiftfoot Boots|AFC|1 +1 Swords to Plowshares|AFC|1 +1 The Deck of Many Things|AFR|2 +1 Thriving Grove|AFC|1 +1 Thriving Heath|AFC|1 +1 Underdark Basilisk|AFR|1 +1 Veteran Dungeoneer|AFR|1 +1 You Happen On a Glade|AFR|1 +1 You Meet in a Tavern|AFR|1 +1 You're Ambushed on the Road|AFR|1 +[Sideboard] diff --git a/forge-gui/res/conquest/planes/Forgotten_Realms/Cave of the Frost Dragon/Grand Master of Flowers.dck b/forge-gui/res/conquest/planes/Forgotten_Realms/Cave of the Frost Dragon/Grand Master of Flowers.dck new file mode 100644 index 00000000000..7f3d8b58654 --- /dev/null +++ b/forge-gui/res/conquest/planes/Forgotten_Realms/Cave of the Frost Dragon/Grand Master of Flowers.dck @@ -0,0 +1,34 @@ +[metadata] +Name=Grand Master of Flowers +[Commander] +1 Grand Master of Flowers|AFR|2 +[Main] +1 Cave of the Frost Dragon|AFR|1 +1 Celestial Unicorn|AFR|1 +1 Cleric Class|AFR|1 +1 Dawnbringer Cleric|AFR|1 +1 Delver's Torch|AFR|1 +1 Dragon's Disciple|AFR|1 +1 Dragon's Hoard|AFC|1 +1 Eternal Dragon|AFC|1 +1 Explorer's Scope|AFC|1 +1 Half-Elf Monk|AFR|1 +1 Icingdeath, Frost Tyrant|AFR|1 +1 Mantle of the Ancients|AFC|1 +1 Monk of the Open Hand|AFR|1 +1 Nadaar, Selfless Paladin|AFR|1 +3 Plains|AFR|1 +3 Plains|AFR|2 +4 Plains|AFR|3 +5 Plains|AFR|4 +1 Potion of Healing|AFR|1 +1 Priest of Ancient Lore|AFR|1 +1 Rally Maneuver|AFR|1 +1 Sol Ring|AFC|1 +1 Steadfast Paladin|AFR|1 +1 Sword of Hours|AFC|1 +1 Sword of the Animist|AFC|1 +1 The Book of Exalted Deeds|AFR|1 +1 Treasure Chest|AFR|1 +1 White Dragon|AFR|1 +[Sideboard] diff --git a/forge-gui/res/conquest/planes/Forgotten_Realms/Cave of the Frost Dragon/Icingdeath, Frost Tyrant.dck b/forge-gui/res/conquest/planes/Forgotten_Realms/Cave of the Frost Dragon/Icingdeath, Frost Tyrant.dck new file mode 100644 index 00000000000..b2bb996ff01 --- /dev/null +++ b/forge-gui/res/conquest/planes/Forgotten_Realms/Cave of the Frost Dragon/Icingdeath, Frost Tyrant.dck @@ -0,0 +1,35 @@ +[metadata] +Name=Icingdeath, Frost Tyrant +[Commander] +1 Icingdeath, Frost Tyrant|AFR|1 +[Main] +1 Arborea Pegasus|AFR|1 +1 Argentum Armor|AFC|1 +1 Basilisk Collar|AFC|1 +1 Cave of the Frost Dragon|AFR|1 +1 Clay Golem|AFC|1 +1 Dancing Sword|AFR|2 +1 Dragon's Disciple|AFR|1 +1 Dragon's Hoard|AFC|1 +1 Dungeon Map|AFR|1 +1 Eternal Dragon|AFC|1 +1 Flumph|AFR|1 +1 Holy Avenger|AFC|1 +1 Mantle of the Ancients|AFC|1 +1 Moonsilver Spear|AFC|1 +1 Nadaar, Selfless Paladin|AFR|1 +5 Plains|AFR|1 +1 Plains|AFR|2 +2 Plains|AFR|3 +6 Plains|AFR|4 +1 Planar Ally|AFR|1 +1 Portable Hole|AFR|1 +1 Ranger's Hawk|AFR|1 +1 Robe of Stars|AFC|1 +1 Sol Ring|AFC|1 +1 The Deck of Many Things|AFR|1 +1 Treasure Chest|AFR|3 +1 Treasure Vault|AFR|1 +1 Wall of Omens|AFC|1 +1 White Dragon|AFR|1 +[Sideboard] diff --git a/forge-gui/res/conquest/planes/Forgotten_Realms/Cave of the Frost Dragon/Nadaar, Selfless Paladin.dck b/forge-gui/res/conquest/planes/Forgotten_Realms/Cave of the Frost Dragon/Nadaar, Selfless Paladin.dck new file mode 100644 index 00000000000..11614bfe40b --- /dev/null +++ b/forge-gui/res/conquest/planes/Forgotten_Realms/Cave of the Frost Dragon/Nadaar, Selfless Paladin.dck @@ -0,0 +1,33 @@ +[metadata] +Name=Nadaar, Selfless Paladin +[Commander] +1 Nadaar, Selfless Paladin|AFR|1 +[Main] +1 +2 Mace|AFR|1 +1 Arborea Pegasus|AFR|1 +1 Cave of the Frost Dragon|AFR|1 +1 Dancing Sword|AFR|1 +1 Dawnbringer Cleric|AFR|1 +1 Delver's Torch|AFR|1 +1 Devoted Paladin|AFR|1 +1 Dwarfhold Champion|AFR|1 +1 Gloom Stalker|AFR|1 +1 Greataxe|AFR|1 +1 Gryff's Boon|AFC|1 +1 Moonsilver Spear|AFC|1 +1 Paladin Class|AFR|1 +5 Plains|AFR|1 +3 Plains|AFR|2 +5 Plains|AFR|3 +3 Plains|AFR|4 +1 Planar Ally|AFR|1 +1 Plate Armor|AFR|1 +1 Portable Hole|AFR|1 +1 Radiant Solar|AFC|1 +1 Ranger's Hawk|AFR|1 +1 Sword of Hours|AFC|1 +1 Thorough Investigation|AFC|1 +1 Treasure Chest|AFR|3 +1 Veteran Dungeoneer|AFR|1 +1 You're Ambushed on the Road|AFR|1 +[Sideboard] diff --git a/forge-gui/res/conquest/planes/Forgotten_Realms/Cave of the Frost Dragon/Oswald Fiddlebender.dck b/forge-gui/res/conquest/planes/Forgotten_Realms/Cave of the Frost Dragon/Oswald Fiddlebender.dck new file mode 100644 index 00000000000..92a19fdb11f --- /dev/null +++ b/forge-gui/res/conquest/planes/Forgotten_Realms/Cave of the Frost Dragon/Oswald Fiddlebender.dck @@ -0,0 +1,35 @@ +[metadata] +Name=Oswald Fiddlebender +[Commander] +1 Oswald Fiddlebender|AFR|1 +[Main] +1 +2 Mace|AFR|1 +1 Argentum Armor|AFC|1 +1 Blink Dog|AFR|1 +1 Cave of the Frost Dragon|AFR|1 +1 Chaos Wand|AFC|1 +1 Clay Golem|AFC|1 +1 Component Pouch|AFC|1 +1 Dungeon Map|AFR|1 +1 Ebony Fly|AFC|1 +1 Fey Steed|AFC|1 +1 Fifty Feet of Rope|AFR|1 +1 Hand of Vecna|AFR|1 +1 Holy Avenger|AFC|1 +1 Ingenious Smith|AFR|1 +1 Iron Golem|AFR|1 +1 Mishra's Factory|AFC|1 +1 Monk of the Open Hand|AFR|1 +1 Moonsilver Spear|AFC|1 +3 Plains|AFR|1 +4 Plains|AFR|2 +4 Plains|AFR|3 +3 Plains|AFR|4 +1 Puresteel Paladin|AFC|1 +1 Solemn Simulacrum|AFC|1 +1 Spiked Pit Trap|AFR|1 +1 Sword of Hours|AFC|1 +1 The Deck of Many Things|AFR|2 +1 Thorough Investigation|AFC|1 +1 Treasure Chest|AFR|3 +[Sideboard] diff --git a/forge-gui/res/conquest/planes/Forgotten_Realms/Cave of the Frost Dragon/Sram, Senior Edificer.dck b/forge-gui/res/conquest/planes/Forgotten_Realms/Cave of the Frost Dragon/Sram, Senior Edificer.dck new file mode 100644 index 00000000000..16d05eb823b --- /dev/null +++ b/forge-gui/res/conquest/planes/Forgotten_Realms/Cave of the Frost Dragon/Sram, Senior Edificer.dck @@ -0,0 +1,35 @@ +[metadata] +Name=Sram, Senior Edificer +[Commander] +1 Sram, Senior Edificer|AFC|1 +[Main] +1 +2 Mace|AFR|1 +1 Angelic Gift|AFC|1 +1 Basilisk Collar|AFC|1 +1 Cave of the Frost Dragon|AFR|1 +1 Clay Golem|AFC|1 +1 Cloister Gargoyle|AFR|1 +1 Colossus Hammer|AFC|1 +1 Delver's Torch|AFR|1 +1 Dwarfhold Champion|AFR|1 +1 Gloom Stalker|AFR|1 +1 Greataxe|AFR|1 +1 Gryff's Boon|AFC|1 +1 Holy Avenger|AFC|1 +1 Iron Golem|AFR|1 +1 Mantle of the Ancients|AFC|1 +1 Masterwork of Ingenuity|AFC|1 +1 Minimus Containment|AFR|1 +1 Moon-Blessed Cleric|AFR|1 +1 Moonsilver Spear|AFC|1 +4 Plains|AFR|1 +4 Plains|AFR|2 +3 Plains|AFR|3 +3 Plains|AFR|4 +1 Plate Armor|AFR|1 +1 Puresteel Paladin|AFC|1 +1 Swiftfoot Boots|AFC|1 +1 Sword of Hours|AFC|1 +1 Sword of the Animist|AFC|1 +1 Veteran Dungeoneer|AFR|1 +[Sideboard] diff --git a/forge-gui/res/conquest/planes/Forgotten_Realms/Cave of the Frost Dragon/_events.txt b/forge-gui/res/conquest/planes/Forgotten_Realms/Cave of the Frost Dragon/_events.txt new file mode 100644 index 00000000000..67ffbefd4c4 --- /dev/null +++ b/forge-gui/res/conquest/planes/Forgotten_Realms/Cave of the Frost Dragon/_events.txt @@ -0,0 +1,9 @@ +Name:Oswald Fiddlebender|Deck:Oswald Fiddlebender.dck|Variant:Commander|Avatar:Oswald Fiddlebender|Desc: +Name:Nadaar, Selfless Paladin|Deck:Nadaar, Selfless Paladin.dck|Variant:Commander|Avatar:Nadaar, Selfless Paladin|Desc: +Name:Drizzt Do'Urden|Deck:Drizzt Do'Urden.dck|Variant:Commander|Avatar:Drizzt Do'Urden|Desc: +Name:Bruenor Battlehammer|Deck:Bruenor Battlehammer.dck|Variant:Commander|Avatar:Bruenor Battlehammer|Desc: +Name:Grand Master of Flowers|Deck:Grand Master of Flowers.dck|Variant:Planeswalker|Avatar:Grand Master of Flowers|Desc: +Name:Sram, Senior Edificer|Deck:Sram, Senior Edificer.dck|Variant:Commander|Avatar:Sram, Senior Edificer|Desc: +Name:Braids, Conjurer Adept|Deck:Braids, Conjurer Adept.dck|Variant:Vanguard|Avatar:Braids, Conjurer Adept|Desc: +Name:Icingdeath, Frost Tyrant|Deck:Icingdeath, Frost Tyrant.dck|Variant:Commander|Avatar:Icingdeath, Frost Tyrant|Desc: +Name:White Planar Mage|Deck:Random|Variant:Planechase|Avatar:Planar Warden|Desc: diff --git a/forge-gui/res/conquest/planes/Forgotten_Realms/Den of the Bugbear/Delina, Wild Mage.dck b/forge-gui/res/conquest/planes/Forgotten_Realms/Den of the Bugbear/Delina, Wild Mage.dck new file mode 100644 index 00000000000..36c4f338821 --- /dev/null +++ b/forge-gui/res/conquest/planes/Forgotten_Realms/Den of the Bugbear/Delina, Wild Mage.dck @@ -0,0 +1,34 @@ +[metadata] +Name=Delina, Wild Mage +[Commander] +1 Delina, Wild Mage|AFR|1 +[Main] +1 Anger|AFC|1 +1 Barbarian Class|AFR|1 +1 Boots of Speed|AFR|1 +1 Brazen Dwarf|AFR|1 +1 Burning Hands|AFR|1 +1 Chaos Channeler|AFR|1 +1 Critical Hit|AFR|1 +1 Den of the Bugbear|AFR|1 +1 Farideh's Fireball|AFR|1 +1 Flameskull|AFR|1 +1 Gratuitous Violence|AFC|1 +1 Hoarding Ogre|AFR|1 +1 Hobgoblin Captain|AFR|1 +1 Kick in the Door|AFR|1 +1 Magic Missile|AFR|1 +1 Meteor Swarm|AFR|1 +6 Mountain|AFR|1 +2 Mountain|AFR|2 +3 Mountain|AFR|3 +4 Mountain|AFR|4 +1 Swarming Goblins|AFR|1 +1 Sword of Hours|AFC|1 +1 Taurean Mauler|AFC|1 +1 Valor Singer|AFR|1 +1 Warstorm Surge|AFC|1 +1 Wild-Magic Sorcerer|AFC|1 +1 You See a Pair of Goblins|AFR|1 +1 Zalto, Fire Giant Duke|AFR|1 +[Sideboard] diff --git a/forge-gui/res/conquest/planes/Forgotten_Realms/Den of the Bugbear/Goblin Warchief.dck b/forge-gui/res/conquest/planes/Forgotten_Realms/Den of the Bugbear/Goblin Warchief.dck new file mode 100644 index 00000000000..5192b20c412 --- /dev/null +++ b/forge-gui/res/conquest/planes/Forgotten_Realms/Den of the Bugbear/Goblin Warchief.dck @@ -0,0 +1,35 @@ +[metadata] +Name=Goblin Warchief +[Avatar] +1 Goblin Warchief Avatar|VAN|2 +[Main] +1 Bag of Holding|AFR|1 +1 Barbarian Class|AFR|1 +1 Battle Cry Goblin|AFR|1 +1 Critical Hit|AFR|1 +1 Dark-Dweller Oracle|AFC|1 +1 Den of the Bugbear|AFR|1 +1 Dragonlord's Servant|AFC|1 +1 Dragonmaster Outcast|AFC|1 +1 Dragonspeaker Shaman|AFC|1 +1 Goblin Javelineer|AFR|1 +1 Goblin Morningstar|AFR|1 +1 Heirloom Blade|AFC|1 +1 Hobgoblin Bandit Lord|AFR|2 +1 Hobgoblin Captain|AFR|1 +1 Hulking Bugbear|AFR|1 +1 Jaded Sell-Sword|AFR|1 +1 Minion of the Mighty|AFR|1 +5 Mountain|AFR|2 +1 Mountain|AFR|3 +8 Mountain|AFR|4 +1 Opportunistic Dragon|AFC|1 +1 Outpost Siege|AFC|1 +1 Skyship Stalker|AFC|1 +1 Sol Ring|AFC|1 +1 Spit Flame|AFC|1 +1 Swarming Goblins|AFR|1 +1 Tiger-Tribe Hunter|AFR|1 +1 You Come to the Gnoll Camp|AFR|1 +1 You See a Pair of Goblins|AFR|1 +[Sideboard] diff --git a/forge-gui/res/conquest/planes/Forgotten_Realms/Den of the Bugbear/Inferno of the Star Mounts.dck b/forge-gui/res/conquest/planes/Forgotten_Realms/Den of the Bugbear/Inferno of the Star Mounts.dck new file mode 100644 index 00000000000..2f94dfa32d8 --- /dev/null +++ b/forge-gui/res/conquest/planes/Forgotten_Realms/Den of the Bugbear/Inferno of the Star Mounts.dck @@ -0,0 +1,34 @@ +[metadata] +Name=Inferno of the Star Mounts +[Commander] +1 Inferno of the Star Mounts|AFR|1 +[Main] +1 Bogardan Hellkite|AFC|1 +1 Boots of Speed|AFR|1 +1 Chaos Dragon|AFC|1 +1 Colossus Hammer|AFC|1 +1 Demanding Dragon|AFC|1 +1 Dragon's Hoard|AFC|1 +1 Dragonlord's Servant|AFC|1 +1 Dragonmaster Outcast|AFC|1 +1 Dragonspeaker Shaman|AFC|1 +1 Dueling Rapier|AFR|1 +1 Fiendlash|AFC|1 +1 Goblin Morningstar|AFR|1 +1 Heirloom Blade|AFC|1 +4 Mountain|AFR|1 +3 Mountain|AFR|2 +5 Mountain|AFR|3 +3 Mountain|AFR|4 +1 Red Dragon|AFR|1 +1 Scourge of Valkas|AFC|1 +1 Skyline Despot|AFC|1 +1 Skyship Stalker|AFC|1 +1 Sol Ring|AFC|1 +1 Sword of Hours|AFC|1 +1 Sword of the Animist|AFC|1 +1 Terror of Mount Velus|AFC|1 +1 Thunderbreak Regent|AFC|1 +1 Valor Singer|AFR|1 +1 Zariel, Archduke of Avernus|AFR|2 +[Sideboard] diff --git a/forge-gui/res/conquest/planes/Forgotten_Realms/Den of the Bugbear/Minsc, Beloved Ranger.dck b/forge-gui/res/conquest/planes/Forgotten_Realms/Den of the Bugbear/Minsc, Beloved Ranger.dck new file mode 100644 index 00000000000..e38fa1eaa9c --- /dev/null +++ b/forge-gui/res/conquest/planes/Forgotten_Realms/Den of the Bugbear/Minsc, Beloved Ranger.dck @@ -0,0 +1,45 @@ +[metadata] +Name=Minsc, Beloved Ranger +[Commander] +1 Minsc, Beloved Ranger|AFR|1 +[Main] +1 Behemoth Sledge|AFC|1 +1 Brazen Dwarf|AFR|1 +1 Cave of the Frost Dragon|AFR|1 +1 Chaos Channeler|AFR|1 +1 Cinder Glade|AFC|1 +1 Colossus Hammer|AFC|1 +1 Command Tower|AFC|1 +1 Cultivate|AFC|1 +1 Den of the Bugbear|AFR|1 +1 Dragonborn Champion|AFC|1 +1 Dwarfhold Champion|AFR|1 +1 Elturgard Ranger|AFR|1 +1 Evolving Wilds|AFR|1 +1 Fighter Class|AFR|1 +1 Forest|AFR|1 +1 Forest|AFR|3 +1 Fortified Village|AFC|1 +1 Gloom Stalker|AFR|1 +1 Gratuitous Violence|AFC|1 +1 Indomitable Might|AFC|1 +1 Inspiring Bard|AFR|1 +1 Mountain|AFR|2 +1 Mountain|AFR|3 +1 Mountain|AFR|4 +1 Path of Ancestry|AFC|1 +1 Plains|AFR|3 +1 Plains|AFR|4 +1 Plundering Barbarian|AFR|1 +1 Rancor|AFC|1 +1 Ranger Class|AFR|1 +1 Ranger's Hawk|AFR|1 +1 Sol Ring|AFC|1 +1 Sword of Hours|AFC|1 +1 Tectonic Giant|AFC|1 +1 Terramorphic Expanse|AFC|1 +1 Tiger-Tribe Hunter|AFR|1 +1 Treasure Chest|AFR|1 +1 Warstorm Surge|AFC|1 +1 You Come to the Gnoll Camp|AFR|1 +[Sideboard] diff --git a/forge-gui/res/conquest/planes/Forgotten_Realms/Den of the Bugbear/Targ Nar, Demon-Fang Gnoll.dck b/forge-gui/res/conquest/planes/Forgotten_Realms/Den of the Bugbear/Targ Nar, Demon-Fang Gnoll.dck new file mode 100644 index 00000000000..3d44e743435 --- /dev/null +++ b/forge-gui/res/conquest/planes/Forgotten_Realms/Den of the Bugbear/Targ Nar, Demon-Fang Gnoll.dck @@ -0,0 +1,40 @@ +[metadata] +Name=Targ Nar, Demon-Fang Gnoll +[Commander] +1 Targ Nar, Demon-Fang Gnoll|AFR|1 +[Main] +1 Barbarian Class|AFR|1 +1 Battle Cry Goblin|AFR|1 +1 Bulette|AFR|1 +1 Cinder Glade|AFC|1 +1 Command Tower|AFC|1 +1 Den of the Bugbear|AFR|1 +1 Evolving Wilds|AFR|1 +1 Fifty Feet of Rope|AFR|1 +1 Forest|AFR|1 +1 Forest|AFR|3 +1 Forest|AFR|4 +1 Game Trail|AFC|1 +1 Gnoll Hunter|AFR|1 +1 Goblin Javelineer|AFR|1 +1 Hobgoblin Bandit Lord|AFR|2 +1 Hobgoblin Captain|AFR|1 +1 Hulking Bugbear|AFR|1 +1 Improvised Weaponry|AFR|1 +1 Intrepid Outlander|AFR|1 +1 Lair of the Hydra|AFR|1 +1 Minion of the Mighty|AFR|1 +4 Mountain|AFR|2 +3 Mountain|AFR|3 +1 Ochre Jelly|AFR|2 +1 Outpost Siege|AFC|1 +1 Plundering Barbarian|AFR|1 +1 Price of Loyalty|AFR|1 +1 Swarming Goblins|AFR|1 +1 Tiger-Tribe Hunter|AFR|1 +1 Warstorm Surge|AFC|1 +1 Werewolf Pack Leader|AFR|1 +1 Wild-Magic Sorcerer|AFC|1 +1 You See a Pair of Goblins|AFR|1 +1 Zariel, Archduke of Avernus|AFR|1 +[Sideboard] diff --git a/forge-gui/res/conquest/planes/Forgotten_Realms/Den of the Bugbear/Vrondiss, Rage of Ancients.dck b/forge-gui/res/conquest/planes/Forgotten_Realms/Den of the Bugbear/Vrondiss, Rage of Ancients.dck new file mode 100644 index 00000000000..aa239843ea1 --- /dev/null +++ b/forge-gui/res/conquest/planes/Forgotten_Realms/Den of the Bugbear/Vrondiss, Rage of Ancients.dck @@ -0,0 +1,41 @@ +[metadata] +Name=Vrondiss, Rage of Ancients +[Commander] +1 Vrondiss, Rage of Ancients|AFC|2 +[Main] +1 Bag of Tricks|AFC|1 +1 Barbarian Class|AFR|1 +1 Brazen Dwarf|AFR|1 +1 Chaos Dragon|AFC|2 +1 Cinder Glade|AFC|1 +1 Command Tower|AFC|1 +1 Critical Hit|AFR|1 +1 Delina, Wild Mage|AFR|2 +1 Den of the Bugbear|AFR|1 +1 Earth-Cult Elemental|AFR|1 +1 Evolving Wilds|AFR|1 +1 Farideh's Fireball|AFR|1 +2 Forest|AFR|1 +1 Forest|AFR|2 +1 Forest|AFR|3 +1 Game Trail|AFC|1 +1 Goblin Morningstar|AFR|1 +1 Heirloom Blade|AFC|1 +1 Hoarding Ogre|AFR|1 +1 Indomitable Might|AFC|1 +1 Indomitable Might|AFC|2 +1 Lair of the Hydra|AFR|1 +1 Leather Armor|AFR|1 +1 Loathsome Troll|AFR|1 +1 Maddening Hex|AFC|2 +4 Mountain|AFR|1 +1 Mountain|AFR|2 +1 Mountain|AFR|3 +1 Neverwinter Hydra|AFC|1 +1 Sol Ring|AFC|1 +1 Swarming Goblins|AFR|1 +1 Sword of Hours|AFC|1 +1 Sylvan Shepherd|AFR|1 +1 Treasure Chest|AFR|1 +1 Wild Endeavor|AFC|2 +[Sideboard] diff --git a/forge-gui/res/conquest/planes/Forgotten_Realms/Den of the Bugbear/Zalto, Fire Giant Duke.dck b/forge-gui/res/conquest/planes/Forgotten_Realms/Den of the Bugbear/Zalto, Fire Giant Duke.dck new file mode 100644 index 00000000000..58130c8fe33 --- /dev/null +++ b/forge-gui/res/conquest/planes/Forgotten_Realms/Den of the Bugbear/Zalto, Fire Giant Duke.dck @@ -0,0 +1,36 @@ +[metadata] +Name=Zalto, Fire Giant Duke +[Commander] +1 Zalto, Fire Giant Duke|AFR|1 +[Main] +1 Barbarian Class|AFR|1 +1 Battle Cry Goblin|AFR|1 +1 Boots of Speed|AFR|1 +1 Bucknard's Everfull Purse|AFC|1 +1 Chaos Dragon|AFC|1 +1 Den of the Bugbear|AFR|1 +1 Earth-Cult Elemental|AFR|1 +1 Goblin Javelineer|AFR|1 +1 Goblin Morningstar|AFR|1 +1 Hoarding Ogre|AFR|1 +1 Hulking Bugbear|AFR|1 +1 Improvised Weaponry|AFR|1 +1 Jaded Sell-Sword|AFR|1 +1 Kick in the Door|AFR|1 +1 Lightning Greaves|AFC|1 +1 Loyal Apprentice|AFC|1 +7 Mountain|AFR|1 +3 Mountain|AFR|2 +2 Mountain|AFR|3 +1 Mountain|AFR|4 +1 Plundering Barbarian|AFR|1 +1 Price of Loyalty|AFR|1 +1 Skyship Stalker|AFC|1 +1 Spinerock Knoll|AFC|1 +1 Swiftfoot Boots|AFC|1 +1 Tiger-Tribe Hunter|AFR|1 +1 Treasure Chest|AFR|3 +1 Unexpected Windfall|AFR|1 +1 Warstorm Surge|AFC|1 +1 Xorn|AFR|1 +[Sideboard] diff --git a/forge-gui/res/conquest/planes/Forgotten_Realms/Den of the Bugbear/Zariel, Archduke of Avernus.dck b/forge-gui/res/conquest/planes/Forgotten_Realms/Den of the Bugbear/Zariel, Archduke of Avernus.dck new file mode 100644 index 00000000000..2c9255404f4 --- /dev/null +++ b/forge-gui/res/conquest/planes/Forgotten_Realms/Den of the Bugbear/Zariel, Archduke of Avernus.dck @@ -0,0 +1,34 @@ +[metadata] +Name=Zariel, Archduke of Avernus +[Commander] +1 Zariel, Archduke of Avernus|AFR|1 +[Main] +1 Barbarian Class|AFR|1 +1 Chaos Dragon|AFC|1 +1 Demanding Dragon|AFC|1 +1 Den of the Bugbear|AFR|1 +1 Dragon's Fire|AFR|1 +1 Dragonlord's Servant|AFC|1 +1 Dragonmaster Outcast|AFC|1 +1 Dragonspeaker Shaman|AFC|1 +1 Farideh's Fireball|AFR|1 +1 Gratuitous Violence|AFC|1 +1 Improvised Weaponry|AFR|1 +1 Jaded Sell-Sword|AFR|1 +1 Minion of the Mighty|AFR|1 +4 Mountain|AFR|1 +2 Mountain|AFR|2 +5 Mountain|AFR|3 +3 Mountain|AFR|4 +1 Opportunistic Dragon|AFC|1 +1 Orb of Dragonkind|AFR|1 +1 Scourge of Valkas|AFC|1 +1 Skyship Stalker|AFC|1 +1 Sol Ring|AFC|1 +2 Spit Flame|AFC|1 +1 Sword of Hours|AFC|1 +1 The Deck of Many Things|AFR|1 +1 Thunderbreak Regent|AFC|1 +1 Vengeful Ancestor|AFC|1 +1 You See a Pair of Goblins|AFR|1 +[Sideboard] diff --git a/forge-gui/res/conquest/planes/Forgotten_Realms/Den of the Bugbear/_events.txt b/forge-gui/res/conquest/planes/Forgotten_Realms/Den of the Bugbear/_events.txt new file mode 100644 index 00000000000..3fd55213a98 --- /dev/null +++ b/forge-gui/res/conquest/planes/Forgotten_Realms/Den of the Bugbear/_events.txt @@ -0,0 +1,9 @@ +Name:Delina, Wild Mage|Deck:Delina, Wild Mage.dck|Variant:Commander|Avatar:Delina, Wild Mage|Desc: +Name:Zalto, Fire Giant Duke|Deck:Zalto, Fire Giant Duke.dck|Variant:Commander|Avatar:Zalto, Fire Giant Duke|Desc: +Name:Inferno of the Star Mounts|Deck:Inferno of the Star Mounts.dck|Variant:Commander|Avatar:Inferno of the Star Mounts|Desc: +Name:Minsc, Beloved Ranger|Deck:Minsc, Beloved Ranger.dck|Variant:Commander|Avatar:Minsc, Beloved Ranger|Desc: +Name:Zariel, Archduke of Avernus|Deck:Zariel, Archduke of Avernus.dck|Variant:Planeswalker|Avatar:Zariel, Archduke of Avernus|Desc: +Name:Targ Nar, Demon-Fang Gnoll|Deck:Targ Nar, Demon-Fang Gnoll.dck|Variant:Commander|Avatar:Targ Nar, Demon-Fang Gnoll|Desc: +Name:Goblin Warchief|Deck:Goblin Warchief.dck|Variant:Vanguard|Avatar:Goblin Warchief Avatar|Desc: +Name:Vrondiss, Rage of Ancients|Deck:Vrondiss, Rage of Ancients.dck|Variant:Commander|Avatar:Vrondiss, Rage of Ancients|Desc: +Name:Red Planar Mage|Deck:Random|Variant:Planechase|Avatar:Planar Warden|Desc: diff --git a/forge-gui/res/conquest/planes/Forgotten_Realms/Hall of Storm Giants/Farideh, Devil's Chosen.dck b/forge-gui/res/conquest/planes/Forgotten_Realms/Hall of Storm Giants/Farideh, Devil's Chosen.dck new file mode 100644 index 00000000000..7f9ed11373d --- /dev/null +++ b/forge-gui/res/conquest/planes/Forgotten_Realms/Hall of Storm Giants/Farideh, Devil's Chosen.dck @@ -0,0 +1,41 @@ +[metadata] +Name=Farideh, Devil's Chosen +[Commander] +1 Farideh, Devil's Chosen|AFR|1 +[Main] +1 Aberrant Mind Sorcerer|AFR|1 +1 Barbarian Class|AFR|1 +1 Brazen Dwarf|AFR|1 +1 Chaos Dragon|AFC|1 +1 Clay Golem|AFC|1 +1 Command Tower|AFC|1 +1 Contact Other Plane|AFR|1 +1 Critical Hit|AFR|1 +1 Delina, Wild Mage|AFR|2 +1 Den of the Bugbear|AFR|1 +1 Djinni Windseer|AFR|1 +1 Earth-Cult Elemental|AFR|1 +1 Evolving Wilds|AFR|1 +1 Farideh's Fireball|AFR|1 +1 Feywild Trickster|AFR|1 +1 Goblin Morningstar|AFR|1 +1 Hall of Storm Giants|AFR|1 +1 Hoarding Ogre|AFR|1 +1 Island|AFR|1 +1 Island|AFR|2 +1 Island|AFR|3 +2 Island|AFR|4 +1 Mountain|AFR|1 +3 Mountain|AFR|2 +1 Mountain|AFR|3 +2 Mountain|AFR|4 +1 Netherese Puzzle-Ward|AFC|2 +1 Pixie Guide|AFR|1 +1 Power of Persuasion|AFR|1 +1 Scion of Stygia|AFR|1 +1 Spiked Pit Trap|AFR|1 +1 Swarming Goblins|AFR|1 +1 Sword of Hours|AFC|1 +1 The Deck of Many Things|AFR|2 +1 Treasure Chest|AFR|3 +[Sideboard] diff --git a/forge-gui/res/conquest/planes/Forgotten_Realms/Hall of Storm Giants/Grazilaxx, Illithid Scholar.dck b/forge-gui/res/conquest/planes/Forgotten_Realms/Hall of Storm Giants/Grazilaxx, Illithid Scholar.dck new file mode 100644 index 00000000000..58463e8b1d7 --- /dev/null +++ b/forge-gui/res/conquest/planes/Forgotten_Realms/Hall of Storm Giants/Grazilaxx, Illithid Scholar.dck @@ -0,0 +1,36 @@ +[metadata] +Name=Grazilaxx, Illithid Scholar +[Commander] +1 Grazilaxx, Illithid Scholar|AFR|1 +[Main] +1 Air-Cult Elemental|AFR|1 +1 Argentum Armor|AFC|1 +1 Bar the Gate|AFR|1 +1 Blue Dragon|AFR|1 +1 Charmed Sleep|AFR|1 +1 Curator of Mysteries|AFC|1 +1 Djinni Windseer|AFR|1 +1 Dragon Turtle|AFR|1 +1 Halimar Depths|AFC|1 +1 Hall of Storm Giants|AFR|1 +5 Island|AFR|1 +2 Island|AFR|2 +2 Island|AFR|3 +4 Island|AFR|4 +1 Mind Flayer|AFR|1 +1 Mulldrifter|AFC|1 +1 Murder of Crows|AFC|1 +1 Phantasmal Image|AFC|1 +1 Phantom Steed|AFC|1 +1 Pixie Guide|AFR|1 +1 Riverwise Augur|AFC|1 +1 Shortcut Seeker|AFR|1 +1 Silver Raven|AFR|1 +1 Sol Ring|AFC|1 +1 Soulknife Spy|AFR|1 +1 Split the Party|AFR|1 +1 Sword of the Animist|AFC|1 +1 Tasha's Hideous Laughter|AFR|1 +1 Wizard Class|AFR|1 +1 Yuan-Ti Malison|AFR|1 +[Sideboard] diff --git a/forge-gui/res/conquest/planes/Forgotten_Realms/Hall of Storm Giants/Hama Pashar, Ruin Seeker.dck b/forge-gui/res/conquest/planes/Forgotten_Realms/Hall of Storm Giants/Hama Pashar, Ruin Seeker.dck new file mode 100644 index 00000000000..cdcd2c87a8e --- /dev/null +++ b/forge-gui/res/conquest/planes/Forgotten_Realms/Hall of Storm Giants/Hama Pashar, Ruin Seeker.dck @@ -0,0 +1,41 @@ +[metadata] +Name=Hama Pashar, Ruin Seeker +[Commander] +1 Hama Pashar, Ruin Seeker|AFR|1 +[Main] +1 Bar the Gate|AFR|1 +1 Cave of the Frost Dragon|AFR|1 +1 Cloister Gargoyle|AFR|1 +1 Command Tower|AFC|1 +1 Curator of Mysteries|AFC|1 +1 Delver's Torch|AFR|1 +1 Displacer Beast|AFR|1 +1 Dungeon Map|AFR|1 +1 Eccentric Apprentice|AFR|1 +1 Eel Umbra|AFC|1 +1 Evolving Wilds|AFR|1 +1 Fifty Feet of Rope|AFR|1 +1 Flood Plain|AFC|1 +1 Fly|AFR|1 +1 Hall of Storm Giants|AFR|1 +3 Island|AFR|1 +1 Keen-Eared Sentry|AFR|1 +1 Midnight Pathlighter|AFC|1 +1 Nadaar, Selfless Paladin|AFR|1 +1 Phantasmal Image|AFC|1 +3 Plains|AFR|1 +1 Planar Ally|AFR|1 +1 Port Town|AFC|1 +1 Prairie Stream|AFC|1 +1 Radiant Solar|AFC|1 +1 Ranger's Hawk|AFR|1 +1 Shortcut Seeker|AFR|1 +1 Terramorphic Expanse|AFC|1 +1 The Deck of Many Things|AFR|2 +1 Thorough Investigation|AFC|1 +1 Thriving Heath|AFC|1 +1 Thriving Isle|AFC|1 +1 Veteran Dungeoneer|AFR|1 +1 You Come to a River|AFR|1 +1 Yuan-Ti Malison|AFR|1 +[Sideboard] diff --git a/forge-gui/res/conquest/planes/Forgotten_Realms/Hall of Storm Giants/Iymrith, Desert Doom.dck b/forge-gui/res/conquest/planes/Forgotten_Realms/Hall of Storm Giants/Iymrith, Desert Doom.dck new file mode 100644 index 00000000000..89a46e48fc0 --- /dev/null +++ b/forge-gui/res/conquest/planes/Forgotten_Realms/Hall of Storm Giants/Iymrith, Desert Doom.dck @@ -0,0 +1,36 @@ +[metadata] +Name=Iymrith, Desert Doom +[Commander] +1 Iymrith, Desert Doom|AFR|1 +[Main] +1 Air-Cult Elemental|AFR|1 +1 Arcane Investigator|AFR|1 +1 Blue Dragon|AFR|1 +1 Brainstorm|AFC|1 +1 Contact Other Plane|AFR|1 +1 Djinni Windseer|AFR|1 +1 Dragon Turtle|AFR|1 +1 Dragon's Hoard|AFC|1 +1 Feywild Trickster|AFR|1 +1 Halimar Depths|AFC|1 +1 Hall of Storm Giants|AFR|1 +1 Haven of the Spirit Dragon|AFC|1 +1 Imprisoned in the Moon|AFC|1 +3 Island|AFR|1 +4 Island|AFR|2 +4 Island|AFR|3 +3 Island|AFR|4 +1 Iymrith, Desert Doom|AFR|1 +1 Mordenkainen's Polymorph|AFR|1 +1 Murder of Crows|AFC|1 +1 Netherese Puzzle-Ward|AFC|1 +1 Phantom Steed|AFC|1 +1 Pixie Guide|AFR|1 +1 Power of Persuasion|AFR|1 +1 Ray of Frost|AFR|1 +1 Silver Raven|AFR|1 +1 Spiked Pit Trap|AFR|1 +1 Sword of Hours|AFC|1 +1 The Deck of Many Things|AFR|1 +1 Treasure Chest|AFR|3 +[Sideboard] diff --git a/forge-gui/res/conquest/planes/Forgotten_Realms/Hall of Storm Giants/Jhoira of the Ghitu.dck b/forge-gui/res/conquest/planes/Forgotten_Realms/Hall of Storm Giants/Jhoira of the Ghitu.dck new file mode 100644 index 00000000000..3321b8bf62a --- /dev/null +++ b/forge-gui/res/conquest/planes/Forgotten_Realms/Hall of Storm Giants/Jhoira of the Ghitu.dck @@ -0,0 +1,45 @@ +[metadata] +Name=Jhoira of the Ghitu +[Avatar] +1 Jhoira of the Ghitu Avatar|VAN|1 +[Main] +1 Air-Cult Elemental|AFR|1 +1 Argentum Armor|AFC|1 +1 Bar the Gate|AFR|1 +1 Charmed Sleep|AFR|1 +1 Clay Golem|AFC|1 +1 Displacer Beast|AFR|1 +1 Djinni Windseer|AFR|1 +1 Feywild Trickster|AFR|1 +1 Guild Thief|AFR|1 +1 Hall of Storm Giants|AFR|1 +4 Island|AFR|1 +2 Island|AFR|2 +3 Island|AFR|3 +3 Island|AFR|4 +1 Mishra's Factory|AFC|1 +1 Murder of Crows|AFC|1 +1 Phantom Steed|AFC|1 +1 Pixie Guide|AFR|1 +1 Power of Persuasion|AFR|1 +1 Prognostic Sphinx|AFC|1 +1 Ray of Frost|AFR|1 +1 Silver Raven|AFR|1 +1 Sol Ring|AFC|1 +1 Soulknife Spy|AFR|1 +1 Sword of Hours|AFC|1 +1 Sword of the Animist|AFC|1 +1 Tasha's Hideous Laughter|AFR|1 +1 Treasure Chest|AFR|1 +1 Trickster's Talisman|AFR|1 +1 Underdark Rift|AFC|1 +1 Wizard Class|AFR|1 +1 You Come to a River|AFR|1 +[Sideboard] + +[Planes] + +[Schemes] + +[Conspiracy] + diff --git a/forge-gui/res/conquest/planes/Forgotten_Realms/Hall of Storm Giants/Minn, Wily Illusionist.dck b/forge-gui/res/conquest/planes/Forgotten_Realms/Hall of Storm Giants/Minn, Wily Illusionist.dck new file mode 100644 index 00000000000..f1a914944ff --- /dev/null +++ b/forge-gui/res/conquest/planes/Forgotten_Realms/Hall of Storm Giants/Minn, Wily Illusionist.dck @@ -0,0 +1,36 @@ +[metadata] +Name=Minn, Wily Illusionist +[Commander] +1 Minn, Wily Illusionist|AFC|2 +[Main] +1 Arcane Investigator|AFR|1 +1 Brainstorm|AFC|1 +1 Champion of Wits|AFC|1 +1 Contact Other Plane|AFR|1 +1 Curator of Mysteries|AFC|1 +1 Curse of Verbosity|AFC|1 +1 Diviner's Portent|AFC|1 +1 Ebony Fly|AFC|1 +1 Eel Umbra|AFC|1 +1 Geier Reach Sanitarium|AFC|1 +1 Halimar Depths|AFC|1 +1 Hall of Storm Giants|AFR|1 +2 Island|AFR|1 +5 Island|AFR|2 +2 Island|AFR|3 +4 Island|AFR|4 +1 Mulldrifter|AFC|1 +1 Murder of Crows|AFC|1 +1 Phantasmal Image|AFC|1 +1 Phantom Steed|AFC|1 +1 Riverwise Augur|AFC|1 +1 Serum Visions|AFC|1 +1 Shocking Grasp|AFR|1 +1 Solemn Simulacrum|AFC|1 +1 Soulknife Spy|AFR|1 +1 The Deck of Many Things|AFR|1 +1 Treasure Chest|AFR|3 +1 Winged Boots|AFC|1 +1 Wizard Class|AFR|1 +1 You Find the Villains' Lair|AFR|1 +[Sideboard] diff --git a/forge-gui/res/conquest/planes/Forgotten_Realms/Hall of Storm Giants/Mordenkainen.dck b/forge-gui/res/conquest/planes/Forgotten_Realms/Hall of Storm Giants/Mordenkainen.dck new file mode 100644 index 00000000000..52df7deb716 --- /dev/null +++ b/forge-gui/res/conquest/planes/Forgotten_Realms/Hall of Storm Giants/Mordenkainen.dck @@ -0,0 +1,34 @@ +[metadata] +Name=Mordenkainen +[Commander] +1 Mordenkainen|AFR|2 +[Main] +1 Bar the Gate|AFR|1 +1 Brainstorm|AFC|1 +1 Clay Golem|AFC|1 +1 Contact Other Plane|AFR|1 +1 Curator of Mysteries|AFC|1 +1 Demilich|AFR|1 +1 Feywild Trickster|AFR|1 +1 Guild Thief|AFR|1 +1 Hall of Storm Giants|AFR|1 +5 Island|AFR|1 +2 Island|AFR|2 +4 Island|AFR|3 +4 Island|AFR|4 +1 Mind Flayer|AFR|1 +1 Minn, Wily Illusionist|AFC|2 +1 Mulldrifter|AFC|1 +1 Phantasmal Image|AFC|1 +1 Phantom Steed|AFC|1 +1 Rimeshield Frost Giant|AFR|1 +1 Shocking Grasp|AFR|1 +1 Shortcut Seeker|AFR|1 +1 Silver Raven|AFR|1 +1 Soulknife Spy|AFR|1 +1 The Deck of Many Things|AFR|2 +1 Treasure Chest|AFR|3 +1 Wizard Class|AFR|1 +1 You Come to a River|AFR|1 +1 You Find the Villains' Lair|AFR|1 +[Sideboard] diff --git a/forge-gui/res/conquest/planes/Forgotten_Realms/Hall of Storm Giants/Volo, Guide to Monsters.dck b/forge-gui/res/conquest/planes/Forgotten_Realms/Hall of Storm Giants/Volo, Guide to Monsters.dck new file mode 100644 index 00000000000..d87ecdbbef1 --- /dev/null +++ b/forge-gui/res/conquest/planes/Forgotten_Realms/Hall of Storm Giants/Volo, Guide to Monsters.dck @@ -0,0 +1,42 @@ +[metadata] +Name=Volo, Guide to Monsters +[Commander] +1 Volo, Guide to Monsters|AFR|1 +[Main] +1 Arcane Investigator|AFR|1 +1 Bulette|AFR|1 +1 Burnished Hart|AFC|1 +1 Champion of Wits|AFC|1 +1 Clay Golem|AFC|1 +1 Curator of Mysteries|AFC|1 +1 Displacer Beast|AFR|1 +1 Djinni Windseer|AFR|1 +1 Dragon Turtle|AFR|1 +1 Ellywick Tumblestrum|AFR|2 +2 Evolving Wilds|AFR|1 +1 Feywild Trickster|AFR|1 +1 Forest|AFR|1 +1 Forest|AFR|3 +2 Forest|AFR|4 +1 Gnoll Hunter|AFR|1 +1 Grazilaxx, Illithid Scholar|AFR|2 +1 Guild Thief|AFR|1 +1 Hall of Storm Giants|AFR|1 +2 Island|AFR|1 +1 Island|AFR|3 +1 Island|AFR|4 +1 Lair of the Hydra|AFR|1 +1 Loathsome Troll|AFR|1 +1 Lumbering Falls|AFC|1 +1 Merfolk Looter|AFC|1 +1 Mulldrifter|AFC|1 +1 Neverwinter Dryad|AFR|1 +1 Neverwinter Hydra|AFC|2 +1 Ochre Jelly|AFR|1 +1 Pixie Guide|AFR|2 +1 Ranger Class|AFR|1 +1 Rimeshield Frost Giant|AFR|1 +1 Terramorphic Expanse|AFC|1 +1 Thriving Grove|AFC|1 +1 Thriving Isle|AFC|1 +[Sideboard] diff --git a/forge-gui/res/conquest/planes/Forgotten_Realms/Hall of Storm Giants/_events.txt b/forge-gui/res/conquest/planes/Forgotten_Realms/Hall of Storm Giants/_events.txt new file mode 100644 index 00000000000..6ffe1dbd9f8 --- /dev/null +++ b/forge-gui/res/conquest/planes/Forgotten_Realms/Hall of Storm Giants/_events.txt @@ -0,0 +1,9 @@ +Name:Minn, Wily Illusionist|Deck:Minn, Wily Illusionist.dck|Variant:Commander|Avatar:Minn, Wily Illusionist|Desc: +Name:Grazilaxx, Illithid Scholar|Deck:Grazilaxx, Illithid Scholar.dck|Variant:Commander|Avatar:Grazilaxx, Illithid Scholar|Desc: +Name:Iymrith, Desert Doom|Deck:Iymrith, Desert Doom.dck|Variant:Commander|Avatar:Iymrith, Desert Doom|Desc: +Name:Hama Pashar, Ruin Seeker|Deck:Hama Pashar, Ruin Seeker.dck|Variant:Commander|Avatar:Hama Pashar, Ruin Seeker|Desc: +Name:Mordenkainen|Deck:Mordenkainen.dck|Variant:Planeswalker|Avatar:Mordenkainen|Desc: +Name:Farideh, Devil's Chosen|Deck:Farideh, Devil's Chosen.dck|Variant:Commander|Avatar:Farideh, Devil's Chosen|Desc: +Name:Jhoira of the Ghitu|Deck:Jhoira of the Ghitu.dck|Variant:Vanguard|Avatar:Jhoira of the Ghitu Avatar|Desc: +Name:Volo, Guide to Monsters|Deck:Volo, Guide to Monsters.dck|Variant:Commander|Avatar:Volo, Guide to Monsters|Desc: +Name:Blue Planar Mage|Deck:Random|Variant:Planechase|Avatar:Planar Warden|Desc: diff --git a/forge-gui/res/conquest/planes/Forgotten_Realms/Hive of the Eye Tyrant/Ebondeath, Dracolich.dck b/forge-gui/res/conquest/planes/Forgotten_Realms/Hive of the Eye Tyrant/Ebondeath, Dracolich.dck new file mode 100644 index 00000000000..5bd0b8100a2 --- /dev/null +++ b/forge-gui/res/conquest/planes/Forgotten_Realms/Hive of the Eye Tyrant/Ebondeath, Dracolich.dck @@ -0,0 +1,37 @@ +[metadata] +Name=Ebondeath, Dracolich +[Commander] +1 Ebondeath, Dracolich|AFR|1 +[Main] +1 Basilisk Collar|AFC|1 +1 Chittering Witch|AFC|1 +1 Clattering Skeletons|AFR|1 +1 Dead Man's Chest|AFC|1 +1 Death Tyrant|AFC|1 +1 Demogorgon's Clutches|AFR|1 +1 Devour Intellect|AFR|1 +1 Dungeon Crawler|AFR|1 +1 Eye of Vecna|AFR|1 +1 Fates' Reversal|AFR|1 +1 Fiend of the Shadows|AFC|1 +1 Forsworn Paladin|AFR|1 +1 Grim Wanderer|AFR|1 +1 Hand of Vecna|AFR|1 +1 Hex|AFC|1 +1 Hive of the Eye Tyrant|AFR|1 +1 Mishra's Factory|AFC|1 +1 Ogre Slumlord|AFC|1 +1 Piper of the Swarm|AFC|1 +1 Power Word Kill|AFR|1 +1 Precipitous Drop|AFR|1 +3 Swamp|AFR|1 +3 Swamp|AFR|2 +3 Swamp|AFR|3 +3 Swamp|AFR|4 +1 Sword of the Animist|AFC|1 +1 The Book of Vile Darkness|AFR|1 +1 Underdark Rift|AFC|1 +1 Vorpal Sword|AFR|3 +1 Wand of Orcus|AFC|1 +1 Warlock Class|AFR|1 +[Sideboard] diff --git a/forge-gui/res/conquest/planes/Forgotten_Realms/Hive of the Eye Tyrant/Krydle of Baldur's Gate.dck b/forge-gui/res/conquest/planes/Forgotten_Realms/Hive of the Eye Tyrant/Krydle of Baldur's Gate.dck new file mode 100644 index 00000000000..091151d9bc7 --- /dev/null +++ b/forge-gui/res/conquest/planes/Forgotten_Realms/Hive of the Eye Tyrant/Krydle of Baldur's Gate.dck @@ -0,0 +1,41 @@ +[metadata] +Name=Krydle of Baldur's Gate +[Commander] +1 Krydle of Baldur's Gate|AFR|1 +[Main] +1 Baleful Beholder|AFR|2 +1 Bar the Gate|AFR|1 +1 Basilisk Collar|AFC|1 +1 Choked Estuary|AFC|1 +1 Command Tower|AFC|1 +1 Demogorgon's Clutches|AFR|1 +1 Devour Intellect|AFR|1 +1 Evolving Wilds|AFR|1 +1 Forsworn Paladin|AFR|2 +1 Gonti, Lord of Luxury|AFC|1 +1 Grim Hireling|AFC|1 +1 Guild Thief|AFR|1 +1 Hive of the Eye Tyrant|AFR|1 +1 Hoard Robber|AFR|1 +2 Island|AFR|3 +1 Island|AFR|4 +1 Lightfoot Rogue|AFR|1 +1 Ogre Slumlord|AFC|1 +1 Rogue Class|AFR|1 +1 Shortcut Seeker|AFR|1 +1 Soulknife Spy|AFR|1 +1 Sunken Hollow|AFC|1 +1 Swamp|AFR|1 +1 Swamp|AFR|2 +4 Swamp|AFR|4 +1 Thieves' Tools|AFR|1 +1 Thriving Isle|AFC|1 +1 Thriving Moor|AFC|1 +1 Trickster's Talisman|AFR|1 +1 Vorpal Sword|AFR|1 +1 Westgate Regent|AFR|1 +1 You Come to a River|AFR|1 +1 You Find the Villains' Lair|AFR|1 +1 Yuan-Ti Fang-Blade|AFR|1 +1 Yuan-Ti Malison|AFR|2 +[Sideboard] diff --git a/forge-gui/res/conquest/planes/Forgotten_Realms/Hive of the Eye Tyrant/Lolth, Spider Queen.dck b/forge-gui/res/conquest/planes/Forgotten_Realms/Hive of the Eye Tyrant/Lolth, Spider Queen.dck new file mode 100644 index 00000000000..a10d4b7ce34 --- /dev/null +++ b/forge-gui/res/conquest/planes/Forgotten_Realms/Hive of the Eye Tyrant/Lolth, Spider Queen.dck @@ -0,0 +1,35 @@ +[metadata] +Name=Lolth, Spider Queen +[Commander] +1 Lolth, Spider Queen|AFR|2 +[Main] +1 Burnished Hart|AFC|1 +1 Chittering Witch|AFC|1 +1 Clattering Skeletons|AFR|1 +1 Deadly Dispute|AFR|1 +1 Demogorgon's Clutches|AFR|1 +1 Doomed Necromancer|AFC|1 +1 Drider|AFR|1 +1 Grim Wanderer|AFR|1 +1 Hex|AFC|1 +1 Hive of the Eye Tyrant|AFR|1 +1 Ogre Slumlord|AFC|1 +1 Phthisis|AFC|1 +1 Piper of the Swarm|AFC|1 +1 Plaguecrafter|AFC|1 +1 Pontiff of Blight|AFC|1 +1 Power Word Kill|AFR|1 +1 Ray of Enfeeblement|AFR|1 +1 Reaper's Talisman|AFR|1 +1 Reassembling Skeleton|AFC|1 +1 Sepulcher Ghoul|AFR|1 +1 Shambling Ghast|AFR|1 +1 Solemn Simulacrum|AFC|1 +3 Swamp|AFR|1 +3 Swamp|AFR|2 +5 Swamp|AFR|3 +3 Swamp|AFR|4 +1 Underdark Rift|AFC|1 +1 Vampire Spawn|AFR|1 +1 Vorpal Sword|AFR|1 +[Sideboard] diff --git a/forge-gui/res/conquest/planes/Forgotten_Realms/Hive of the Eye Tyrant/Nihiloor.dck b/forge-gui/res/conquest/planes/Forgotten_Realms/Hive of the Eye Tyrant/Nihiloor.dck new file mode 100644 index 00000000000..d82f7952732 --- /dev/null +++ b/forge-gui/res/conquest/planes/Forgotten_Realms/Hive of the Eye Tyrant/Nihiloor.dck @@ -0,0 +1,43 @@ +[metadata] +Name=Nihiloor +[Commander] +1 Nihiloor|AFC|1 +[Main] +1 Arcane Sanctum|AFC|1 +1 Basilisk Collar|AFC|1 +1 Command Tower|AFC|1 +1 Consuming Vapors|AFC|1 +1 Contact Other Plane|AFR|1 +1 Danse Macabre|AFC|1 +1 Esper Panorama|AFC|1 +1 Evolving Wilds|AFR|1 +1 Fey Steed|AFC|1 +1 Grave Endeavor|AFC|1 +1 Guild Thief|AFR|1 +1 Hall of Storm Giants|AFR|1 +1 Herald of Hadar|AFR|1 +1 Hive of the Eye Tyrant|AFR|1 +1 Hostage Taker|AFC|1 +1 Island|AFR|2 +1 Island|AFR|4 +1 Lorcan, Warlock Collector|AFC|1 +1 Midnight Pathlighter|AFC|1 +1 Mind Flayer|AFR|1 +1 Necromantic Selection|AFC|1 +1 Plains|AFR|3 +1 Pontiff of Blight|AFC|1 +1 Power of Persuasion|AFR|1 +1 Priest of Ancient Lore|AFR|1 +1 Reaper's Talisman|AFR|1 +1 Sol Ring|AFC|1 +1 Swamp|AFR|1 +2 Swamp|AFR|3 +2 Swamp|AFR|4 +1 Sword of Hours|AFC|1 +1 The Deck of Many Things|AFR|2 +1 Thriving Moor|AFC|1 +1 Treasure Chest|AFR|3 +1 Vampire Spawn|AFR|1 +1 Warlock Class|AFR|1 +1 Winged Boots|AFC|1 +[Sideboard] diff --git a/forge-gui/res/conquest/planes/Forgotten_Realms/Hive of the Eye Tyrant/Orcus, Prince of Undeath.dck b/forge-gui/res/conquest/planes/Forgotten_Realms/Hive of the Eye Tyrant/Orcus, Prince of Undeath.dck new file mode 100644 index 00000000000..8f7fba5a815 --- /dev/null +++ b/forge-gui/res/conquest/planes/Forgotten_Realms/Hive of the Eye Tyrant/Orcus, Prince of Undeath.dck @@ -0,0 +1,43 @@ +[metadata] +Name=Orcus, Prince of Undeath +[Commander] +1 Orcus, Prince of Undeath|AFR|2 +[Main] +1 Basilisk Collar|AFC|1 +1 Clattering Skeletons|AFR|2 +1 Command Tower|AFC|1 +1 Death Tyrant|AFC|1 +1 Death-Priest of Myrkul|AFR|1 +1 Dungeon Crawler|AFR|1 +1 Ebondeath, Dracolich|AFR|1 +1 Evolving Wilds|AFR|1 +1 Eye of Vecna|AFR|1 +1 Flameskull|AFR|1 +1 Foreboding Ruins|AFC|1 +1 Grim Bounty|AFR|1 +1 Hand of Vecna|AFR|1 +1 Hive of the Eye Tyrant|AFR|1 +1 Mountain|AFR|2 +1 Necromantic Selection|AFC|1 +1 Path of Ancestry|AFC|1 +1 Pontiff of Blight|AFC|1 +1 Power Word Kill|AFR|1 +1 Reaper's Talisman|AFR|1 +1 Reassembling Skeleton|AFC|1 +1 Sepulcher Ghoul|AFR|1 +1 Smoldering Marsh|AFC|1 +1 Sol Ring|AFC|1 +1 Swamp|AFR|2 +2 Swamp|AFR|3 +2 Swamp|AFR|4 +1 Tainted Peak|AFC|1 +1 Terramorphic Expanse|AFC|1 +1 The Book of Vile Darkness|AFR|1 +1 Thriving Moor|AFC|1 +1 Underdark Rift|AFC|1 +1 Vampire Spawn|AFR|1 +1 Wand of Orcus|AFC|2 +1 Warlock Class|AFR|1 +1 Wight|AFR|1 +1 Zombie Ogre|AFR|1 +[Sideboard] diff --git a/forge-gui/res/conquest/planes/Forgotten_Realms/Hive of the Eye Tyrant/Rith, the Awakener.dck b/forge-gui/res/conquest/planes/Forgotten_Realms/Hive of the Eye Tyrant/Rith, the Awakener.dck new file mode 100644 index 00000000000..91240e8ee62 --- /dev/null +++ b/forge-gui/res/conquest/planes/Forgotten_Realms/Hive of the Eye Tyrant/Rith, the Awakener.dck @@ -0,0 +1,44 @@ +[metadata] +Name=Rith, the Awakener +[Avatar] +1 Rith, the Awakener Avatar|VAN|1 +[Main] +1 Adult Gold Dragon|AFR|1 +1 Arcane Sanctum|AFC|1 +1 Black Dragon|AFR|1 +1 Blue Dragon|AFR|1 +1 Bucknard's Everfull Purse|AFC|1 +1 Chaos Dragon|AFC|1 +1 Demanding Dragon|AFC|1 +1 Dragon's Disciple|AFR|1 +1 Dragon's Hoard|AFC|1 +1 Dragonlord's Servant|AFC|1 +1 Dragonmaster Outcast|AFC|1 +1 Evolving Wilds|AFR|1 +1 Fellwar Stone|AFC|1 +1 Find the Path|AFR|1 +1 Green Dragon|AFR|1 +1 Grim Hireling|AFC|1 +1 Haven of the Spirit Dragon|AFC|1 +1 Icingdeath, Frost Tyrant|AFR|1 +1 Iymrith, Desert Doom|AFR|1 +1 Kalain, Reclusive Painter|AFR|1 +1 Klauth, Unrivaled Ancient|AFC|2 +2 Mountain|AFR|2 +1 Mountain|AFR|3 +1 Old Gnawbone|AFR|1 +1 Prosper, Tome-Bound|AFC|1 +1 Red Dragon|AFR|1 +1 Sol Ring|AFC|1 +2 Swamp|AFR|4 +1 Temple of the Dragon Queen|AFR|1 +1 Terramorphic Expanse|AFC|1 +1 Thriving Grove|AFC|1 +1 Thriving Heath|AFC|1 +1 Thriving Isle|AFC|1 +1 Thriving Moor|AFC|1 +1 Thunderbreak Regent|AFC|1 +1 Tiamat|AFR|1 +1 Treasure Chest|AFR|3 +1 White Dragon|AFR|1 +[Sideboard] diff --git a/forge-gui/res/conquest/planes/Forgotten_Realms/Hive of the Eye Tyrant/Sefris of the Hidden Ways.dck b/forge-gui/res/conquest/planes/Forgotten_Realms/Hive of the Eye Tyrant/Sefris of the Hidden Ways.dck new file mode 100644 index 00000000000..ba24f121ced --- /dev/null +++ b/forge-gui/res/conquest/planes/Forgotten_Realms/Hive of the Eye Tyrant/Sefris of the Hidden Ways.dck @@ -0,0 +1,45 @@ +[metadata] +Name=Sefris of the Hidden Ways +[Commander] +1 Sefris of the Hidden Ways|AFC|1 +[Main] +1 Acererak the Archlich|AFR|1 +1 Arcane Sanctum|AFC|1 +1 Bar the Gate|AFR|1 +1 Barrowin of Clan Undurr|AFR|1 +1 Bucknard's Everfull Purse|AFC|1 +1 Clattering Skeletons|AFR|1 +1 Command Tower|AFC|1 +1 Displacer Beast|AFR|1 +1 Dungeon Crawler|AFR|1 +1 Dungeon Descent|AFR|1 +1 Dungeon Map|AFR|1 +1 Eccentric Apprentice|AFR|1 +1 Evolving Wilds|AFR|1 +1 Fates' Reversal|AFR|1 +1 Fly|AFR|1 +1 Grim Bounty|AFR|1 +1 Grim Hireling|AFC|1 +1 Hive of the Eye Tyrant|AFR|1 +1 Hoard Robber|AFR|1 +1 Island|AFR|1 +1 Island|AFR|4 +1 Midnight Pathlighter|AFC|1 +1 Path of Ancestry|AFC|1 +1 Plains|AFR|1 +1 Precipitous Drop|AFR|1 +1 Secret Door|AFR|1 +1 Shortcut Seeker|AFR|1 +1 Sol Ring|AFC|1 +1 Sunken Hollow|AFC|1 +1 Swamp|AFR|1 +1 Swamp|AFR|2 +1 Thieves' Tools|AFR|1 +1 Thriving Heath|AFC|1 +1 Thriving Isle|AFC|1 +1 Thriving Moor|AFC|1 +1 Veteran Dungeoneer|AFR|1 +1 Yuan-Ti Fang-Blade|AFR|1 +1 Yuan-Ti Malison|AFR|2 +1 Zombie Ogre|AFR|1 +[Sideboard] diff --git a/forge-gui/res/conquest/planes/Forgotten_Realms/Hive of the Eye Tyrant/Xanathar, Guild Kingpin.dck b/forge-gui/res/conquest/planes/Forgotten_Realms/Hive of the Eye Tyrant/Xanathar, Guild Kingpin.dck new file mode 100644 index 00000000000..bf933ad95fd --- /dev/null +++ b/forge-gui/res/conquest/planes/Forgotten_Realms/Hive of the Eye Tyrant/Xanathar, Guild Kingpin.dck @@ -0,0 +1,42 @@ +[metadata] +Name=Xanathar, Guild Kingpin +[Commander] +1 Xanathar, Guild Kingpin|AFR|1 +[Main] +1 Baleful Strix|AFC|1 +1 Basilisk Collar|AFC|1 +1 Brainstorm|AFC|1 +1 Choked Estuary|AFC|1 +1 Command Tower|AFC|1 +1 Dead Man's Chest|AFC|1 +1 Death Tyrant|AFC|1 +1 Demogorgon's Clutches|AFR|1 +1 Evolving Wilds|AFR|1 +1 Extract Brain|AFC|1 +1 Eyes of the Beholder|AFR|1 +1 Forbidden Alchemy|AFC|1 +1 Gonti, Lord of Luxury|AFC|1 +1 Grim Hireling|AFC|1 +1 Guild Thief|AFR|1 +1 Hive of the Eye Tyrant|AFR|1 +1 Hostage Taker|AFC|1 +1 Island|AFR|3 +2 Island|AFR|4 +1 Lightfoot Rogue|AFR|1 +1 Ogre Slumlord|AFC|1 +1 Reaper's Talisman|AFR|1 +1 Rogue Class|AFR|1 +1 Shambling Ghast|AFR|1 +1 Shortcut Seeker|AFR|1 +1 Soulknife Spy|AFR|1 +1 Sunken Hollow|AFC|1 +2 Swamp|AFR|1 +2 Swamp|AFR|3 +1 Swamp|AFR|4 +1 Thieves' Tools|AFR|1 +1 Thriving Isle|AFC|1 +1 Thriving Moor|AFC|1 +1 Treasure Chest|AFR|1 +1 Unstable Obelisk|AFC|1 +1 Yuan-Ti Fang-Blade|AFR|1 +[Sideboard] diff --git a/forge-gui/res/conquest/planes/Forgotten_Realms/Hive of the Eye Tyrant/_events.txt b/forge-gui/res/conquest/planes/Forgotten_Realms/Hive of the Eye Tyrant/_events.txt new file mode 100644 index 00000000000..82cc2b4a270 --- /dev/null +++ b/forge-gui/res/conquest/planes/Forgotten_Realms/Hive of the Eye Tyrant/_events.txt @@ -0,0 +1,9 @@ +Name:Ebondeath, Dracolich|Deck:Ebondeath, Dracolich.dck|Variant:Commander|Avatar:Ebondeath, Dracolich|Desc: +Name:Xanathar, Guild Kingpin|Deck:Xanathar, Guild Kingpin.dck|Variant:Commander|Avatar:Xanathar, Guild Kingpin|Desc: +Name:Sefris of the Hidden Ways|Deck:Sefris of the Hidden Ways.dck|Variant:Commander|Avatar:Sefris of the Hidden Ways|Desc: +Name:Krydle of Baldur's Gate|Deck:Krydle of Baldur's Gate.dck|Variant:Commander|Avatar:Krydle of Baldur's Gate|Desc: +Name:Lolth, Spider Queen|Deck:Lolth, Spider Queen.dck|Variant:Planeswalker|Avatar:Lolth, Spider Queen|Desc: +Name:Nihiloor|Deck:Nihiloor.dck|Variant:Commander|Avatar:Nihiloor|Desc: +Name:Rith, the Awakener|Deck:Rith, the Awakener.dck|Variant:Vanguard|Avatar:Rith, the Awakener Avatar|Desc: +Name:Orcus, Prince of Undeath|Deck:Orcus, Prince of Undeath.dck|Variant:Commander|Avatar:Orcus, Prince of Undeath|Desc: +Name:Black Planar Mage|Deck:Random|Variant:Planechase|Avatar:Planar Warden|Desc: diff --git a/forge-gui/res/conquest/planes/Forgotten_Realms/Lair of the Hydra/Catti-brie of Mithral Hall.dck b/forge-gui/res/conquest/planes/Forgotten_Realms/Lair of the Hydra/Catti-brie of Mithral Hall.dck new file mode 100644 index 00000000000..738064dd406 --- /dev/null +++ b/forge-gui/res/conquest/planes/Forgotten_Realms/Lair of the Hydra/Catti-brie of Mithral Hall.dck @@ -0,0 +1,42 @@ +[metadata] +Name=Catti-brie of Mithral Hall +[Commander] +1 Catti-brie of Mithral Hall|AFC|2 +[Main] +1 +2 Mace|AFR|1 +1 Arborea Pegasus|AFR|1 +1 Argentum Armor|AFC|1 +1 Basilisk Collar|AFC|1 +1 Behemoth Sledge|AFC|1 +1 Belt of Giant Strength|AFC|1 +1 Bulette|AFR|1 +1 Canopy Vista|AFC|1 +1 Cave of the Frost Dragon|AFR|1 +1 Command Tower|AFC|1 +1 Dire Wolf Prowler|AFR|1 +1 Drizzt Do'Urden|AFR|1 +1 Druid of Purification|AFC|1 +1 Elturgard Ranger|AFR|1 +1 Evolving Wilds|AFR|1 +2 Forest|AFR|1 +1 Forest|AFR|2 +2 Forest|AFR|4 +1 Fortified Village|AFC|1 +1 Grasslands|AFC|1 +1 Greataxe|AFR|1 +1 Holy Avenger|AFC|1 +1 Inspiring Bard|AFR|1 +1 Knight of Autumn|AFC|1 +1 Lair of the Hydra|AFR|1 +1 Leather Armor|AFR|1 +1 Plains|AFR|2 +1 Plains|AFR|3 +2 Plains|AFR|4 +1 Plate Armor|AFR|1 +1 Radiant Solar|AFC|1 +1 Ranger Class|AFR|1 +1 Robe of Stars|AFC|1 +1 Swiftfoot Boots|AFC|1 +1 Sword of Hours|AFC|1 +1 Viridian Longbow|AFC|1 +[Sideboard] diff --git a/forge-gui/res/conquest/planes/Forgotten_Realms/Lair of the Hydra/Ellywick Tumblestrum.dck b/forge-gui/res/conquest/planes/Forgotten_Realms/Lair of the Hydra/Ellywick Tumblestrum.dck new file mode 100644 index 00000000000..734cbc90569 --- /dev/null +++ b/forge-gui/res/conquest/planes/Forgotten_Realms/Lair of the Hydra/Ellywick Tumblestrum.dck @@ -0,0 +1,36 @@ +[metadata] +Name=Ellywick Tumblestrum +[Commander] +1 Ellywick Tumblestrum|AFR|1 +[Main] +1 Bag of Tricks|AFC|1 +1 Bull's Strength|AFR|1 +1 Circle of the Moon Druid|AFR|1 +1 Dungeon Descent|AFR|1 +1 Dungeon Map|AFR|1 +1 Fifty Feet of Rope|AFR|1 +1 Find the Path|AFR|1 +3 Forest|AFR|1 +2 Forest|AFR|2 +4 Forest|AFR|3 +4 Forest|AFR|4 +1 Inspiring Bard|AFR|1 +1 Instrument of the Bards|AFR|1 +1 Lair of the Hydra|AFR|1 +1 Neverwinter Dryad|AFR|1 +1 Neverwinter Hydra|AFC|1 +1 Ochre Jelly|AFR|1 +1 Old Gnawbone|AFR|2 +1 Owlbear|AFR|2 +1 Paradise Druid|AFC|1 +1 Rancor|AFC|1 +1 Ranger Class|AFR|1 +1 Song of Inspiration|AFC|2 +1 Sword of the Animist|AFC|1 +1 Sylvan Shepherd|AFR|1 +1 The Deck of Many Things|AFR|2 +1 Treasure Vault|AFR|1 +1 Varis, Silverymoon Ranger|AFR|1 +1 Wandering Troubadour|AFR|1 +1 You Find a Cursed Idol|AFR|1 +[Sideboard] diff --git a/forge-gui/res/conquest/planes/Forgotten_Realms/Lair of the Hydra/Galea, Kindler of Hope.dck b/forge-gui/res/conquest/planes/Forgotten_Realms/Lair of the Hydra/Galea, Kindler of Hope.dck new file mode 100644 index 00000000000..dcf864eb983 --- /dev/null +++ b/forge-gui/res/conquest/planes/Forgotten_Realms/Lair of the Hydra/Galea, Kindler of Hope.dck @@ -0,0 +1,45 @@ +[metadata] +Name=Galea, Kindler of Hope +[Commander] +1 Galea, Kindler of Hope|AFC|1 +[Main] +1 +2 Mace|AFR|1 +1 Arcane Signet|AFC|1 +1 Argentum Armor|AFC|1 +1 Behemoth Sledge|AFC|1 +1 Belt of Giant Strength|AFC|1 +1 Canopy Vista|AFC|1 +1 Catti-brie of Mithral Hall|AFC|1 +1 Cave of the Frost Dragon|AFR|1 +1 Colossus Hammer|AFC|1 +1 Command Tower|AFC|1 +1 Drizzt Do'Urden|AFR|1 +1 Ellywick Tumblestrum|AFR|1 +1 Elturgard Ranger|AFR|1 +1 Evolving Wilds|AFR|1 +1 Flood Plain|AFC|1 +1 Forest|AFR|4 +1 Grasslands|AFC|1 +1 Gretchen Titchwillow|AFR|1 +1 Hall of Storm Giants|AFR|1 +1 Holy Avenger|AFC|1 +1 Island|AFR|1 +1 Knight of Autumn|AFC|1 +1 Lumbering Falls|AFC|1 +1 Midnight Pathlighter|AFC|1 +1 Path of Ancestry|AFC|1 +1 Plains|AFR|2 +1 Puresteel Paladin|AFC|1 +1 Seaside Citadel|AFC|1 +1 Sol Ring|AFC|1 +1 Soulknife Spy|AFR|1 +1 Sram, Senior Edificer|AFC|1 +1 Storvald, Frost Giant Jarl|AFC|1 +1 Sword of Hours|AFC|1 +1 Sword of the Animist|AFC|1 +1 Thriving Grove|AFC|1 +1 Thriving Heath|AFC|1 +1 Thriving Isle|AFC|1 +1 Trickster's Talisman|AFR|1 +1 Winged Boots|AFC|1 +[Sideboard] diff --git a/forge-gui/res/conquest/planes/Forgotten_Realms/Lair of the Hydra/Heartwood Storyteller.dck b/forge-gui/res/conquest/planes/Forgotten_Realms/Lair of the Hydra/Heartwood Storyteller.dck new file mode 100644 index 00000000000..9288eac2f89 --- /dev/null +++ b/forge-gui/res/conquest/planes/Forgotten_Realms/Lair of the Hydra/Heartwood Storyteller.dck @@ -0,0 +1,44 @@ +[metadata] +Name=Heartwood Storyteller +[Avatar] +1 Heartwood Storyteller Avatar|VAN|1 +[Main] +1 Bard Class|AFR|1 +1 Cinder Glade|AFC|1 +1 Circle of Dreams Druid|AFR|1 +1 Cultivate|AFC|1 +1 Delina, Wild Mage|AFR|1 +1 Den of the Bugbear|AFR|1 +1 Dragon's Hoard|AFC|1 +1 Dragonmaster Outcast|AFC|1 +1 Ellywick Tumblestrum|AFR|1 +1 Etali, Primal Storm|AFC|1 +1 Evolving Wilds|AFR|1 +1 Find the Path|AFR|1 +2 Forest|AFR|1 +2 Forest|AFR|3 +1 Game Trail|AFC|1 +1 Inferno of the Star Mounts|AFR|1 +1 Instrument of the Bards|AFR|1 +1 Klauth, Unrivaled Ancient|AFC|1 +1 Lair of the Hydra|AFR|1 +1 Mishra's Factory|AFC|1 +1 Mosswort Bridge|AFC|1 +1 Mountain|AFR|1 +1 Mountain|AFR|2 +1 Mountain|AFR|4 +1 Old Gnawbone|AFR|1 +1 Orazca Relic|AFC|1 +1 Paradise Druid|AFC|1 +1 Savage Ventmaw|AFC|1 +1 Sol Ring|AFC|1 +1 Spinerock Knoll|AFC|1 +1 Sword of the Animist|AFC|1 +1 Targ Nar, Demon-Fang Gnoll|AFR|1 +1 The Tarrasque|AFR|1 +1 Thriving Grove|AFC|1 +1 Varis, Silverymoon Ranger|AFR|1 +1 Vrondiss, Rage of Ancients|AFC|1 +1 Zalto, Fire Giant Duke|AFR|1 +1 Zariel, Archduke of Avernus|AFR|1 +[Sideboard] diff --git a/forge-gui/res/conquest/planes/Forgotten_Realms/Lair of the Hydra/Old Gnawbone.dck b/forge-gui/res/conquest/planes/Forgotten_Realms/Lair of the Hydra/Old Gnawbone.dck new file mode 100644 index 00000000000..b496610ead6 --- /dev/null +++ b/forge-gui/res/conquest/planes/Forgotten_Realms/Lair of the Hydra/Old Gnawbone.dck @@ -0,0 +1,34 @@ +[metadata] +Name=Old Gnawbone +[Commander] +1 Old Gnawbone|AFR|1 +[Main] +1 Abundant Growth|AFC|1 +1 Bucknard's Everfull Purse|AFC|1 +1 Bulette|AFR|1 +1 Chameleon Colossus|AFC|1 +1 Circle of Dreams Druid|AFR|1 +1 Colossal Majesty|AFC|1 +1 Cultivate|AFC|1 +1 Dragon's Hoard|AFC|1 +1 Find the Path|AFR|1 +4 Forest|AFR|1 +5 Forest|AFR|2 +4 Forest|AFR|3 +2 Forest|AFR|4 +1 Froghemoth|AFR|1 +1 Green Dragon|AFR|1 +1 Hunter's Mark|AFR|1 +1 Lair of the Hydra|AFR|1 +1 Loathsome Troll|AFR|1 +1 Paradise Druid|AFC|1 +1 Prosperous Innkeeper|AFR|1 +1 Purple Worm|AFR|1 +1 Sol Ring|AFC|1 +1 Spoils of the Hunt|AFR|1 +1 The Tarrasque|AFR|1 +1 Treasure Vault|AFR|1 +1 Wandering Troubadour|AFR|1 +1 Wild Endeavor|AFC|1 +1 You Find a Cursed Idol|AFR|1 +[Sideboard] diff --git a/forge-gui/res/conquest/planes/Forgotten_Realms/Lair of the Hydra/Trelasarra, Moon Dancer.dck b/forge-gui/res/conquest/planes/Forgotten_Realms/Lair of the Hydra/Trelasarra, Moon Dancer.dck new file mode 100644 index 00000000000..5669d5f1987 --- /dev/null +++ b/forge-gui/res/conquest/planes/Forgotten_Realms/Lair of the Hydra/Trelasarra, Moon Dancer.dck @@ -0,0 +1,43 @@ +[metadata] +Name=Trelasarra, Moon Dancer +[Avatar] +1 Trelasarra, Moon Dancer|AFR|1 +[Main] +1 Basilisk Collar|AFC|1 +1 Behemoth Sledge|AFC|1 +1 Canopy Vista|AFC|1 +1 Cave of the Frost Dragon|AFR|1 +1 Celestial Unicorn|AFR|1 +1 Cleric Class|AFR|1 +1 Command Tower|AFC|1 +1 Dawnbringer Cleric|AFR|1 +1 Evolving Wilds|AFR|1 +2 Forest|AFR|1 +1 Forest|AFR|2 +1 Forest|AFR|3 +1 Forest|AFR|4 +1 Fortified Village|AFC|1 +1 Froghemoth|AFR|1 +1 Grasslands|AFC|1 +1 Hill Giant Herdgorger|AFR|1 +1 Indomitable Might|AFC|1 +1 Inspiring Bard|AFR|1 +1 Knight of Autumn|AFC|1 +1 Lair of the Hydra|AFR|1 +1 Lurking Roper|AFR|1 +1 Moonsilver Spear|AFC|1 +2 Plains|AFR|2 +1 Plains|AFR|3 +1 Plains|AFR|4 +1 Potion of Healing|AFR|1 +1 Priest of Ancient Lore|AFR|1 +1 Prosperous Innkeeper|AFR|1 +1 Radiant Solar|AFC|1 +1 Shamanic Revelation|AFC|1 +1 Song of Inspiration|AFC|1 +1 Steadfast Paladin|AFR|1 +1 Sylvan Shepherd|AFR|1 +1 The Book of Exalted Deeds|AFR|1 +1 Treasure Chest|AFR|1 +1 You Meet in a Tavern|AFR|1 +[Sideboard] diff --git a/forge-gui/res/conquest/planes/Forgotten_Realms/Lair of the Hydra/Varis, Silverymoon Ranger.dck b/forge-gui/res/conquest/planes/Forgotten_Realms/Lair of the Hydra/Varis, Silverymoon Ranger.dck new file mode 100644 index 00000000000..a1aabfe66eb --- /dev/null +++ b/forge-gui/res/conquest/planes/Forgotten_Realms/Lair of the Hydra/Varis, Silverymoon Ranger.dck @@ -0,0 +1,34 @@ +[metadata] +Name=Varis, Silverymoon Ranger +[Commander] +1 Varis, Silverymoon Ranger|AFR|1 +[Main] +1 Argentum Armor|AFC|1 +1 Bag of Holding|AFR|1 +1 Bag of Tricks|AFC|1 +1 Basilisk Collar|AFC|1 +1 Bull's Strength|AFR|1 +1 Circle of Dreams Druid|AFR|1 +1 Circle of the Moon Druid|AFR|1 +1 Elturgard Ranger|AFR|1 +3 Forest|AFR|1 +4 Forest|AFR|2 +5 Forest|AFR|3 +3 Forest|AFR|4 +1 Froghemoth|AFR|1 +1 Green Dragon|AFR|1 +1 Hunter's Mark|AFR|1 +1 Indomitable Might|AFC|1 +1 Inspiring Bard|AFR|1 +1 Lair of the Hydra|AFR|1 +1 Long Rest|AFR|1 +1 Ochre Jelly|AFR|1 +1 Owlbear|AFR|1 +1 Paradise Druid|AFC|1 +1 Ranger Class|AFR|1 +1 Ranger's Longbow|AFR|1 +1 Swiftfoot Boots|AFC|1 +1 Verdant Embrace|AFC|1 +1 Viridian Longbow|AFC|1 +1 You Meet in a Tavern|AFR|1 +[Sideboard] diff --git a/forge-gui/res/conquest/planes/Forgotten_Realms/Lair of the Hydra/Wulfgar of Icewind Dale.dck b/forge-gui/res/conquest/planes/Forgotten_Realms/Lair of the Hydra/Wulfgar of Icewind Dale.dck new file mode 100644 index 00000000000..bfd24c67baa --- /dev/null +++ b/forge-gui/res/conquest/planes/Forgotten_Realms/Lair of the Hydra/Wulfgar of Icewind Dale.dck @@ -0,0 +1,40 @@ +[metadata] +Name=Wulfgar of Icewind Dale +[Commander] +1 Wulfgar of Icewind Dale|AFC|1 +[Main] +1 Argentum Armor|AFC|1 +1 Atarka, World Render|AFC|1 +1 Barbarian Class|AFR|1 +1 Beast Within|AFC|1 +1 Choose Your Weapon|AFR|1 +1 Cinder Glade|AFC|1 +1 Colossal Majesty|AFC|1 +1 Colossus Hammer|AFC|1 +1 Command Tower|AFC|1 +1 Delina, Wild Mage|AFR|2 +1 Evolving Wilds|AFR|1 +1 Explorer's Scope|AFC|1 +1 Forest|AFR|1 +2 Forest|AFR|2 +2 Forest|AFR|4 +1 Game Trail|AFC|1 +1 Gnoll Hunter|AFR|2 +1 Gratuitous Violence|AFC|1 +1 Greataxe|AFR|1 +1 Hobgoblin Captain|AFR|1 +1 Indomitable Might|AFC|2 +2 Mountain|AFR|1 +3 Mountain|AFR|3 +1 Mountain|AFR|4 +1 Plundering Barbarian|AFR|1 +1 Return of the Wildspeaker|AFC|1 +1 Savage Ventmaw|AFC|1 +1 Sword of Hours|AFC|1 +1 Sword of the Animist|AFC|1 +1 Sylvan Shepherd|AFR|1 +1 Tectonic Giant|AFC|1 +1 Thriving Grove|AFC|1 +1 Vengeful Ancestor|AFC|2 +1 Werewolf Pack Leader|AFR|1 +[Sideboard] diff --git a/forge-gui/res/conquest/planes/Forgotten_Realms/Lair of the Hydra/_events.txt b/forge-gui/res/conquest/planes/Forgotten_Realms/Lair of the Hydra/_events.txt new file mode 100644 index 00000000000..1ed10aaca82 --- /dev/null +++ b/forge-gui/res/conquest/planes/Forgotten_Realms/Lair of the Hydra/_events.txt @@ -0,0 +1,9 @@ +Name:Varis, Silverymoon Ranger|Deck:Varis, Silverymoon Ranger.dck|Variant:Commander|Avatar:Varis, Silverymoon Ranger|Desc: +Name:Trelasarra, Moon Dancer|Deck:Trelasarra, Moon Dancer.dck|Variant:Commander|Avatar:Trelasarra, Moon Dancer|Desc: +Name:Old Gnawbone|Deck:Old Gnawbone.dck|Variant:Commander|Avatar:Old Gnawbone|Desc: +Name:Catti-brie of Mithral Hall|Deck:Catti-brie of Mithral Hall.dck|Variant:Commander|Avatar:Catti-brie of Mithral Hall|Desc: +Name:Ellywick Tumblestrum|Deck:Ellywick Tumblestrum.dck|Variant:Planeswalker|Avatar:Ellywick Tumblestrum|Desc: +Name:Wulfgar of Icewind Dale|Deck:Wulfgar of Icewind Dale|Variant:Commander|Avatar:Wulfgar of Icewind Dale|Desc: +Name:Heartwood Storyteller|Deck:Heartwood Storyteller.dck|Variant:Vanguard|Avatar:Heartwood Storyteller|Desc: +Name:Galea, Kindler of Hope|Deck:Galea, Kindler of Hope.dck|Variant:Commander|Avatar:Galea, Kindler of Hope|Desc: +Name:Green Planar Mage|Deck:Random|Variant:Planechase|Avatar:Planar Warden|Desc: diff --git a/forge-gui/res/conquest/planes/Forgotten_Realms/plane_cards.txt b/forge-gui/res/conquest/planes/Forgotten_Realms/plane_cards.txt new file mode 100644 index 00000000000..8d78f3c187d --- /dev/null +++ b/forge-gui/res/conquest/planes/Forgotten_Realms/plane_cards.txt @@ -0,0 +1,5 @@ +Cave of the Frost Dragon +Hall of Storm Giants +Den of the Bugbear +Lair of the Hydra +Hive of the Eye Tyrant diff --git a/forge-gui/res/conquest/planes/Forgotten_Realms/regions.txt b/forge-gui/res/conquest/planes/Forgotten_Realms/regions.txt new file mode 100644 index 00000000000..ed6b57431d3 --- /dev/null +++ b/forge-gui/res/conquest/planes/Forgotten_Realms/regions.txt @@ -0,0 +1,5 @@ +Name:Cave of the Frost Dragon|Art:Cave of the Frost Dragon|Colors:W +Name:Hall of Storm Giants|Art:Hall of Storm Giants|Colors:U +Name:Den of the Bugbear|Art:Den of the Bugbear|Colors:R +Name:Lair of the Hydra|Art:Lair of the Hydra|Colors:G +Name:Hive of the Eye Tyrant|Art:Hive of the Eye Tyrant|Colors:B diff --git a/forge-gui/res/conquest/planes/Forgotten_Realms/sets.txt b/forge-gui/res/conquest/planes/Forgotten_Realms/sets.txt new file mode 100644 index 00000000000..d31755dbca2 --- /dev/null +++ b/forge-gui/res/conquest/planes/Forgotten_Realms/sets.txt @@ -0,0 +1,2 @@ +AFR +AFC diff --git a/forge-gui/res/conquest/planes/Unstable_Realm/Unstable Frontier/Crazy Dungeon Master.dck b/forge-gui/res/conquest/planes/Unstable_Realm/Unstable Frontier/Crazy Dungeon Master.dck index 91d10150532..1b81a765b6d 100644 --- a/forge-gui/res/conquest/planes/Unstable_Realm/Unstable Frontier/Crazy Dungeon Master.dck +++ b/forge-gui/res/conquest/planes/Unstable_Realm/Unstable Frontier/Crazy Dungeon Master.dck @@ -3,43 +3,58 @@ Name=Crazy Dungeon Master [Commander] 1 Dungeon Master|HTR [Main] -1 As Luck Would Have It|UST -1 Black Lotus|LEA -1 Box of Free-Range Goblins|UST -1 Chicken Egg|UGL -1 Chicken à la King|UND -1 Chittering Doom|UST -1 City of Brass|ARN -1 Elvish Impersonators|UGL -1 Gateway Plaza|RNA -1 Gilded Lotus|MRD -1 Goblin Tutor|UGL -1 Grand Coliseum|ONS -1 Growth Spurt|UGL -1 Hammer Helper|UST -1 Hammer Jammer|UST -1 Hydradoodle|UST -1 Inhumaniac|UST -1 Jumbo Imp|UGL -1 Krazy Kow|UGL -1 Lobe Lobber|UST -1 Lotus Bloom|TSP -1 Lotus Vale|WTH -1 Mad Science Fair Project|UST -1 Mox Emerald|LEA -1 Mox Jet|LEA -1 Mox Pearl|LEA -1 Mox Ruby|LEA -1 Mox Sapphire|LEA -1 Mox Tantalite|MH1 -1 Painiac|UST -1 Poultrygeist|UGL -1 Rupture Spire|M19 -1 Spark Fiend|UGL -1 Strategy, Schmategy|UGL -1 Sword of Dungeons & Dragons|UST -1 Temp of the Damned|UGL -1 Time Out|UND -1 Urza's Science Fair Project|UGL -1 Willing Test Subject|UST +1 As Luck Would Have It|UST|1 +1 Aswan Jaguar|PAST|1 +1 AWOL|UND|1 +1 Black Lotus|LEA|1 +1 Blacker Lotus|UGL|1 +1 Box of Free-Range Goblins|UST|1 +1 Call from the Grave|PAST|1 +1 Chicken Egg|UGL|1 +1 Chicken à la King|UND|1 +1 Chittering Doom|UST|1 +1 City of Brass|ARN|1 +1 Elvish Impersonators|UGL|1 +1 Faerie Dragon|PAST|1 +2 Forest|UGL|1 +1 Gateway Plaza|RNA|1 +1 Gem Bazaar|PAST|1 +1 Gilded Lotus|MRD|1 +1 Goblin Polka Band|PAST|1 +1 Goblin Tutor|UGL|1 +1 Grand Coliseum|ONS|1 +1 Growth Spurt|UGL|1 +1 Hammer Helper|UST|1 +1 Hammer Jammer|UST|1 +1 Hydradoodle|UST|1 +1 Inhumaniac|UST|1 +2 Island|UGL|1 +1 Jumbo Imp|UGL|1 +1 Krazy Kow|UGL|1 +1 Lobe Lobber|UST|1 +1 Lotus Field|M20|1 +1 Lotus Vale|WTH|1 +2 Mountain|UGL|1 +1 Mox Emerald|LEA|1 +1 Mox Jet|LEA|1 +1 Mox Pearl|LEA|1 +1 Mox Ruby|LEA|1 +1 Mox Sapphire|LEA|1 +1 Mox Tantalite|MH1|1 +1 Necropolis of Azar|PAST|1 +1 Orcish Catapult|PAST|1 +1 Painiac|UST|1 +1 Pandora's Box|PAST|1 +2 Plains|UGL|1 +1 Poultrygeist|UGL|1 +1 Rupture Spire|M19|1 +1 Spark Fiend|UGL|1 +1 Strategy, Schmategy|UGL|1 +2 Swamp|UGL|1 +1 Sword of Dungeons & Dragons|UST|1 +1 Temp of the Damned|UGL|1 +1 Time Out|UND|1 +1 Urza's Science Fair Project|UGL|1 +1 Whimsy|PAST|1 +1 Willing Test Subject|UST|1 [Sideboard] diff --git a/forge-gui/res/conquest/planes/Unstable_Realm/sets.txt b/forge-gui/res/conquest/planes/Unstable_Realm/sets.txt index 843f23237c9..3c1ecc4f67f 100644 --- a/forge-gui/res/conquest/planes/Unstable_Realm/sets.txt +++ b/forge-gui/res/conquest/planes/Unstable_Realm/sets.txt @@ -4,3 +4,4 @@ UST UND CMB1 PCEL +PAST diff --git a/forge-gui/res/conquest/planes/planes.txt b/forge-gui/res/conquest/planes/planes.txt index 1ea056417c6..581246d06ef 100644 --- a/forge-gui/res/conquest/planes/planes.txt +++ b/forge-gui/res/conquest/planes/planes.txt @@ -2,6 +2,7 @@ Name:Alara|RegionSize:9|Desc:As the boundaries between the shards dissolve, cult Name:Amonkhet|RegionSize:9|Desc:On the surface, Amonkhet seems like a marvelous place to live, but something unsettling and nefarious lurks behind the grand facade.\nConsists of 45 events. Contains cards from AKH, HOU, some C17, and Amonkhet Invocations. Name:Dominaria|RegionSize:9|Desc:The legendary plane, once the Nexus of the Multiverse. With the last Time Rift closed, Dominaria's mana flowed back into the land instantaneously, and the world healed and rejuvenated quickly with that infusion of power. After generations of peaceful development, much of Dominaria has managed to rebuild the cultures of its past. Yet, shortly after the Mending, the Cabal began to grow in strength and came under the control of the Demonlord Belzenlok. Legends say that a mysterious Time Vault is hidden somewhere deep in this plane, though no one knows if this rumor is true...\nConsists of 45 events. Contains cards from DOM and most of M19 and C18. Name:Eldraine|RegionSize:9|Desc:Welcome to Eldraine — a storybook land of castles and cauldrons, of chivalrous knights and trickster faeries. But like the devious creatures and beguiling magic that lurk among the shadows, this world is not all that it seems. Join the five courts of the Realm on an epic adventure for honor and glory, or venture a darker path into the mysterious Wilds. Which path will you choose?\nConsists of 45 events. Contains cards from ELD, M20, and C19. +Name:Forgotten_Realms|RegionSize:9|Desc:A world of strange lands, dangerous creatures, and mighty deities, where magic and supernatural phenomena are quite real. Battle with iconic monsters like beholders, mimics, mind flayers, and — of course — legendary dragons!\nConsists of 45 events. Contains cards from AFR and AFC. Name:Ikoria|RegionSize:9|Desc:On the treacherous world of Ikoria, gargantuan beasts fight for survival while humans hide at the bottom of the food chain—forever in fear of the creatures beyond the walls and the human traitors known as “bonders” who believe the monsters misunderstood. Will you fight the behemoths at your door, or fight alongside them?\nConsists of 45 events. Contains cards from IKO, M21, and C20. Name:Innistrad|RegionSize:9|Desc:On this plane, humanity is terrorized by vampires, werewolves, zombies, and ghouls.\nConsists of 45 events. Contains cards from ISD, DKA, AVR, SOI, EMN, and C14. Name:Ixalan|RegionSize:9|Desc:On Ixalan, the untamed jungles have hidden a coveted secret: Orazca, the city of gold, and rivals embark on a journey to claim the plane's greatest fortune for themselves.\nConsists of 45 events. Contains cards from XLN, RIX. @@ -18,5 +19,5 @@ Name:Tarkir|RegionSize:9|Desc:A plane dominated by five powerful clans... or fiv Name:Theros|RegionSize:9|Desc:Mortals tremble before an awe-inspiring pantheon of gods.\nConsists of 45 events. Contains cards from THS, BNG, JOU, HOP, PCA, THB, and more. Name:Time_Vault|RegionSize:6|Unreachable:True|Desc:A mysterious and legendary Time Vault, allowing one to travel back in time and revisit the ages long past and challenge the legends of Dominaria.\nConsists of 12 events. Contains cards from the early core sets up to 9th edition, Dominaria-themed expansions (Ice Age, Mirage, Urza's Saga, Invasion, Odyssey, Onslaught, and Time Spiral blocks), the original Commander, and Modern Horizons.\n\nThe portal to this plane is unstable and will close soon, so hasten your step, planeswalker, while you have the chance... Name:Ulgrotha|RegionSize:6|Unreachable:True|Desc: -Name:Unstable_Realm|RegionSize:6|Unreachable:True|Desc:A realm so unstable that it seems like the planeswalker daring to travel to it could easily become unhinged. Creatures lurking inside appear to be unglued. Using this portal is strictly unsanctioned, so anything from hurt feelings to lost sanity due to traveling through this portal is the planeswalker's own responsibility.\nConsists of 12 events. This plane requires Non-Legal cards to be enabled, make sure you enable this option before playing. Contains cards from UGL, UNH, UST, UND, CMB1, and PCEL.\n\nThe portal to this plane is unstable and will close soon, so hasten your step, planeswalker, while you have the chance... +Name:Unstable_Realm|RegionSize:6|Unreachable:True|Desc:A realm so unstable that it seems like the planeswalker daring to travel to it could easily become unhinged. Creatures lurking inside appear to be unglued. Using this portal is strictly unsanctioned, so anything from hurt feelings to lost sanity due to traveling through this portal is the planeswalker's own responsibility.\nConsists of 12 events. This plane requires Non-Legal cards to be enabled, make sure you enable this option before playing. Contains cards from UGL, UNH, UST, UND, CMB1, PAST, and PCEL.\n\nThe portal to this plane is unstable and will close soon, so hasten your step, planeswalker, while you have the chance... Name:Zendikar|RegionSize:9|Desc:This land of primal mana was lethal even before its Eldrazi prisoners escaped.\nConsists of 60 events. Contains cards from ZEN, WWK, ROE, BFZ, OGW, C16, ZNR, ZNC, and ZNE. diff --git a/forge-gui/res/editions/Commander Collection Black.txt b/forge-gui/res/editions/Commander Collection Black.txt index f8a4eca1251..f4d19ce461c 100644 --- a/forge-gui/res/editions/Commander Collection Black.txt +++ b/forge-gui/res/editions/Commander Collection Black.txt @@ -1,6 +1,6 @@ [metadata] Code=CC2 -Date=2021-12-31 +Date=2022-01-28 Name=Commander Collection Black Type=Collector_Edition ScryfallCode=CC2 diff --git a/forge-gui/res/editions/Innistrad Crimson Vow Commander.txt b/forge-gui/res/editions/Innistrad Crimson Vow Commander.txt index d4b8bb45002..95b5fbf46d6 100644 --- a/forge-gui/res/editions/Innistrad Crimson Vow Commander.txt +++ b/forge-gui/res/editions/Innistrad Crimson Vow Commander.txt @@ -6,9 +6,9 @@ Type=Commander ScryfallCode=VOC [cards] -1 M Millicent, Restless Revenant @ -2 M Strefan, Maurer Progenitor @ -32 M Wedding Ring @ -39 M Millicent, Restless Revenant @ -40 M Strefan, Maurer Progenitor @ -70 M Wedding Ring @ +1 M Millicent, Restless Revenant @Denman Rooke +2 M Strefan, Maurer Progenitor @Chris Rallis +32 M Wedding Ring @Olena Richards +39 M Millicent, Restless Revenant @Denman Rooke +40 M Strefan, Maurer Progenitor @Chris Rallis +70 M Wedding Ring @Olena Richards diff --git a/forge-gui/res/editions/Innistrad Crimson Vow.txt b/forge-gui/res/editions/Innistrad Crimson Vow.txt index a46d8a6a4cc..5b160e13d54 100644 --- a/forge-gui/res/editions/Innistrad Crimson Vow.txt +++ b/forge-gui/res/editions/Innistrad Crimson Vow.txt @@ -8,83 +8,91 @@ Type=Expansion ScryfallCode=VOW [cards] -5 R By Invitation Only @ -10 C Drogskol Infantry @ -15 C Gryff Rider @ -34 M Savior of Ollenbock @ -36 R Sigarda's Summons @ -38 R Thalia, Guardian of Thraben @ -45 R Wedding Announcement @ -60 U Geistlight Snare @ -71 R Overcharged Amalgam @ -103 R Demonic Bargain @ -112 U Fell Stinger @ -114 C Gluttonous Guest @ -129 C Rot-Tide Gargantua @ -131 M Sorin the Mirthless @ -137 R Voldaren Bloodcaster @ -154 R Dominating Vampire @ -184 C Weary Prisoner @ -185 C Apprentice Sharpshooter @ -197 R Dig Up @ -208 C Massive Might @ -235 R Dorothea, Vengeful Victim @ -238 R Grolnok, the Omnivore @ -245 M Olivia, Crimson Bride @ -260 C Wedding Invitation @ -261 R Deathcap Glade @ -262 R Dreamroot Cascade @ -264 R Shattered Sanctum @ -265 R Stormcarved Coast @ -266 R Sundown Pass @ -267 R Voldaren Estate @ -268 L Plains @ -269 L Plains @ -270 L Island @ -271 L Island @ -272 L Swamp @ -273 L Swamp @ -274 L Mountain @ -275 L Mountain @ -276 L Forest @ -277 L Forest @ -278 M Sorin the Mirthless @ -281 R Deathcap Glade @ -282 R Dreamroot Cascade @ -283 R Shattered Sanctum @ -284 R Stormcarved Coast @ -285 R Sundown Pass @ -292 C Gluttonous Guest @ +5 R By Invitation Only @Micah Epstein +10 C Drogskol Infantry @Cristi Balanescu +15 C Gryff Rider @Yongjae Choi +34 M Savior of Ollenbock @Aaron J. Riley +36 R Sigarda's Summons @Nestor Ossandon Leal +38 R Thalia, Guardian of Thraben @Magali Villeneuve +45 R Wedding Announcement @Caroline Gariba +60 U Geistlight Snare @Anato Finnstark +71 R Overcharged Amalgam @Mike Jordana +103 R Demonic Bargain @Sam Guay +112 U Fell Stinger @Lars Grant-West +114 C Gluttonous Guest @Jesper Ejsing +129 C Rot-Tide Gargantua @Filip Burburan +131 M Sorin the Mirthless @Martina Fackova +137 R Voldaren Bloodcaster @Kim Sokol +150 R Change of Fortune @Sam Guay +154 R Dominating Vampire @PINDURSKI +180 U Vampires' Vengeance @Chris Cold +181 M Volatile Arsonist @Gabor Szikszai +184 C Weary Prisoner @Jason Rainville +185 C Apprentice Sharpshooter @Steve Prescott +197 R Dig Up @Slawomir Maniak +208 C Massive Might @Iris Compiet +231 R Anje, Maid of Dishonor @Yongjae Choi +235 R Dorothea, Vengeful Victim @Marta Nael +238 R Grolnok, the Omnivore @Simon Dominic +245 M Olivia, Crimson Bride @Anna Steinbauer +260 C Wedding Invitation @Justyna Gil +261 R Deathcap Glade @Sam Burley +262 R Dreamroot Cascade @Sam Burley +264 R Shattered Sanctum @Muhammad Firdaus +265 R Stormcarved Coast @Sarah Finnigan +266 R Sundown Pass @Muhammad Firdaus +267 R Voldaren Estate @Richard Wright +268 L Plains @Daria Khlebnikova +269 L Plains @Indra Nugroho +270 L Island @Alayna Danner +271 L Island @Rio Krisma +272 L Swamp @Pig Hands +273 L Swamp @Kerby Rosanes +274 L Mountain @Alayna Danner +275 L Mountain @Daria Khlebnikova +276 L Forest @Pig Hands +277 L Forest @Indra Nugroho +278 M Sorin the Mirthless @Justyna Gil +281 R Deathcap Glade @Muhammad Firdaus +282 R Dreamroot Cascade @Jokubas Uogintas +283 R Shattered Sanctum @Donato Giancola +284 R Stormcarved Coast @Jokubas Uogintas +285 R Sundown Pass @Johannes Voss +292 C Gluttonous Guest @Marie Magny 297 M Sorin the Mirthless @ -298 R Voldaren Bloodcaster @ -305 R Dominating Vampire @ +298 R Voldaren Bloodcaster @Samuel Araya +305 R Dominating Vampire @Christian Angel +309 R Anje, Maid of Dishonor @Christian Angel 315 M Olivia, Crimson Bride @ -318 R Thalia, Guardian of Thraben @ -322 R Dorothea, Vengeful Victim @ -324 R Grolnok, the Omnivore @ -330 M Savior of Ollenbock @ -331 R Thalia, Guardian of Thraben @ -337 M Sorin the Mirthless @ -338 R Voldaren Bloodcaster @ -343 M Olivia, Crimson Bride @ -346 R By Invitation Only @ -352 M Savior of Ollenbock @ -353 R Sigarda's Summons @ -355 R Wedding Announcement @ -363 R Overcharged Amalgam @ -368 R Demonic Bargain @ -387 R Dig Up @ -397 R Voldaren Estate @ -398 L Plains @ -399 L Island @ -400 L Swamp @ -401 L Mountain @ -402 L Forest @ -403 R Voldaren Estate @ -404 R Sigarda's Summons @ -405 U Geistlight Snare @ -406 U Fell Stinger @ -407 R Dominating Vampire @ +318 R Thalia, Guardian of Thraben @Cabrol +322 R Dorothea, Vengeful Victim @Karmazid +324 R Grolnok, the Omnivore @DZO +330 M Savior of Ollenbock @Johannes Voss +331 R Thalia, Guardian of Thraben @Magali Villeneuve +337 M Sorin the Mirthless @Bastien L. Deharme +338 R Voldaren Bloodcaster @Johann Bodin +339 U Vampires' Vengeance @Dave Kendall +343 M Olivia, Crimson Bride @Bastien L. Deharme +346 R By Invitation Only @Micah Epstein +352 M Savior of Ollenbock @Aaron J. Riley +353 R Sigarda's Summons @Nestor Ossandon Leal +355 R Wedding Announcement @Caroline Gariba +363 R Overcharged Amalgam @Mike Jordana +368 R Demonic Bargain @Sam Guay +375 R Change of Fortune @Sam Guay +387 R Dig Up @Slawomir Maniak +397 R Voldaren Estate @Richard Wright +398 L Plains @Sam White +399 L Island @Sam White +400 L Swamp @Sam White +401 L Mountain @Sam White +402 L Forest @Sam White +403 R Voldaren Estate @Cliff Childs +404 R Sigarda's Summons @Wisnu Tan +405 U Geistlight Snare @Anato Finnstark +406 U Fell Stinger @Lars Grant-West +407 R Dominating Vampire @PINDURSKI [tokens] c_a_blood_draw +gw_1_1_human_soldier_training diff --git a/forge-gui/res/editions/Innistrad Double Feature.txt b/forge-gui/res/editions/Innistrad Double Feature.txt new file mode 100644 index 00000000000..d630e6bf559 --- /dev/null +++ b/forge-gui/res/editions/Innistrad Double Feature.txt @@ -0,0 +1,9 @@ +[metadata] +Code=DBL +Date=2022-01-28 +Name=Innistrad: Double Feature +Type=Draft +ScryfallCode=DBL + +[cards] +516 R Torens, Fist of the Angels @Justine Cruz diff --git a/forge-gui/res/languages/de-DE.properties b/forge-gui/res/languages/de-DE.properties index db2f2362579..31cb928ee23 100644 --- a/forge-gui/res/languages/de-DE.properties +++ b/forge-gui/res/languages/de-DE.properties @@ -1589,6 +1589,8 @@ lblTapped=Getappt #TriggerTapsForMana.java lblTappedForMana=für Mana getappt lblProduced=Erzeugte +#TriggerTrains.java +lblTrains=Trainiert #TriggerTransformed.java lblTransformed=Transformiert #TriggerTurnFaceUp.java diff --git a/forge-gui/res/languages/en-US.properties b/forge-gui/res/languages/en-US.properties index bcda90abb88..c22b8e08639 100644 --- a/forge-gui/res/languages/en-US.properties +++ b/forge-gui/res/languages/en-US.properties @@ -1589,6 +1589,8 @@ lblTapped=Tapped #TriggerTapsForMana.java lblTappedForMana=Tapped for Mana lblProduced=Produced +#TriggerTrains.java +lblTrains=Trains #TriggerTransformed.java lblTransformed=Transformed #TriggerTurnFaceUp.java @@ -1941,8 +1943,8 @@ lblWin=win lblLose=lose #FlipOntoBattlefieldEffect.java lblChooseDesiredLocation=Choose a card to represent the center of the desired card landing location. -lblDidNotFlipOver=The card did not flip over. -lblFlippedOver=The card flipped over {0} time(s). +lblDidNotFlipOver=The card did not turn over. +lblFlippedOver=The card turned over {0} time(s). lblDidNotLandOnCards=The card did not land on any cards. lblLandedOnOneCard=The card landed on {0}. lblLandedOnTwoCards=The card landed on {0} and {1}. diff --git a/forge-gui/res/languages/es-ES.properties b/forge-gui/res/languages/es-ES.properties index cb3047d8e3c..8ea46c95fe9 100644 --- a/forge-gui/res/languages/es-ES.properties +++ b/forge-gui/res/languages/es-ES.properties @@ -1589,6 +1589,8 @@ lblTapped=Girado #TriggerTapsForMana.java lblTappedForMana=Girado para maná lblProduced=Producido +#TriggerTrains.java +lblTrains=Trains #TriggerTransformed.java lblTransformed=Transformado #TriggerTurnFaceUp.java @@ -1939,8 +1941,8 @@ lblWin=gana lblLose=pierde #FlipOntoBattlefieldEffect.java lblChooseDesiredLocation=Choose a card to represent the center of the desired card landing location. -lblDidNotFlipOver=The card did not flip over. -lblFlippedOver=The card flipped over {0} time(s). +lblDidNotFlipOver=The card did not turn over. +lblFlippedOver=The card turned over {0} time(s). lblDidNotLandOnCards=The card did not land on any cards. lblLandedOnOneCard=The card landed on {0}. lblLandedOnTwoCards=The card landed on {0} and {1}. diff --git a/forge-gui/res/languages/it-IT.properties b/forge-gui/res/languages/it-IT.properties index 98de6dd4347..c089272c174 100644 --- a/forge-gui/res/languages/it-IT.properties +++ b/forge-gui/res/languages/it-IT.properties @@ -1587,6 +1587,8 @@ lblTapped=Tappato #TriggerTapsForMana.java lblTappedForMana=Tappato per produrre mana lblProduced=Prodotto +#TriggerTrains.java +lblTrains=Trains #TriggerTransformed.java lblTransformed=Trasformato #TriggerTurnFaceUp.java @@ -1938,8 +1940,8 @@ lblWin=hai vinto lblLose=hai perso #FlipOntoBattlefieldEffect.java lblChooseDesiredLocation=Choose a card to represent the center of the desired card landing location. -lblDidNotFlipOver=The card did not flip over. -lblFlippedOver=The card flipped over {0} time(s). +lblDidNotFlipOver=The card did not turn over. +lblFlippedOver=The card turned over {0} time(s). lblDidNotLandOnCards=The card did not land on any cards. lblLandedOnOneCard=The card landed on {0}. lblLandedOnTwoCards=The card landed on {0} and {1}. diff --git a/forge-gui/res/languages/ja-JP.properties b/forge-gui/res/languages/ja-JP.properties index 5949d490ed0..ec920c6c054 100644 --- a/forge-gui/res/languages/ja-JP.properties +++ b/forge-gui/res/languages/ja-JP.properties @@ -1588,6 +1588,8 @@ lblTapped=タップした #TriggerTapsForMana.java lblTappedForMana=マナのためにタップした lblProduced=生産した +#TriggerTrains.java +lblTrains=Trains #TriggerTransformed.java lblTransformed=変身した #TriggerTurnFaceUp.java @@ -1938,8 +1940,8 @@ lblWin=勝ち lblLose=負け #FlipOntoBattlefieldEffect.java lblChooseDesiredLocation=Choose a card to represent the center of the desired card landing location. -lblDidNotFlipOver=The card did not flip over. -lblFlippedOver=The card flipped over {0} time(s). +lblDidNotFlipOver=The card did not turn over. +lblFlippedOver=The card turned over {0} time(s). lblDidNotLandOnCards=The card did not land on any cards. lblLandedOnOneCard=The card landed on {0}. lblLandedOnTwoCards=The card landed on {0} and {1}. diff --git a/forge-gui/res/languages/zh-CN.properties b/forge-gui/res/languages/zh-CN.properties index c28b53f03e4..77ea552a0be 100644 --- a/forge-gui/res/languages/zh-CN.properties +++ b/forge-gui/res/languages/zh-CN.properties @@ -888,11 +888,11 @@ lblasavatar=作为头像 lblfromschemedeck=从魔王套牌 lblfromplanardeck=从时空竞逐套牌 lblfromconspiracydeck=从诡局套牌 -lblfromdungeondeck=from dungeon deck +lblfromdungeondeck=从地牢套牌 lbltoschemedeck=到魔王套牌 lbltoplanardeck=到时空竞逐套牌 lbltoconspiracydeck=到诡局套牌 -lbltodungeondeck=to dungeon deck +lbltodungeondeck=到地牢套牌 lblMove=移到 #VDock.java lblDock=停靠栏 @@ -1590,6 +1590,8 @@ lblTapped=已横置 #TriggerTapsForMana.java lblTappedForMana=为法术力横置 lblProduced=产生 +#TriggerTrains.java +lblTrains=Trains #TriggerTransformed.java lblTransformed=已转化 #TriggerTurnFaceUp.java @@ -1614,12 +1616,12 @@ btnQuit=退出 btnContinue=继续 btnRestart=重新开始 #LimitedPoolType.java -lblLimitedPoolFull=Full Cardpool -lblLimitedBlock=Block / Set -lblLimitedPrerelease=Prerelease -lblLimitedFantasy=Fantasy Block -lblLimitedCustom=Custom Cube -lblLimitedChaos=Chaos Draft +lblLimitedPoolFull=全牌池 +lblLimitedBlock=环境/系列 +lblLimitedPrerelease=预售 +lblLimitedFantasy=幻想环境 +lblLimitedCustom=自定义Cube +lblLimitedChaos=混沌轮抓 #TournamentWinLoseController.java btnSaveQuit=保存并退出 lblCongratulations=恭喜! @@ -1941,12 +1943,12 @@ lblCallCoinFlip=掷骰子 lblWin=赢 lblLose=输 #FlipOntoBattlefieldEffect.java -lblChooseDesiredLocation=Choose a card to represent the center of the desired card landing location. -lblDidNotFlipOver=The card did not flip over. -lblFlippedOver=The card flipped over {0} time(s). -lblDidNotLandOnCards=The card did not land on any cards. -lblLandedOnOneCard=The card landed on {0}. -lblLandedOnTwoCards=The card landed on {0} and {1}. +lblChooseDesiredLocation=选择一张牌来指定牌张将要降落的位置。 +lblDidNotFlipOver=牌张未翻转。 +lblFlippedOver=牌张翻转了{0}次。 +lblDidNotLandOnCards=牌张没有落到任何卡上。 +lblLandedOnOneCard=牌张落在了{0}上。 +lblLandedOnTwoCards=牌张落在了{0}和{1}上。 #InvestigateEffect.java lblWouldYouLikeInvestigate=你想要探查吗? #LifeSetEffect.java @@ -2151,8 +2153,8 @@ lblGameplayResults=游戏结果 lblResultIs=结果为:{0} lblPlayerRandomChosenNumberIs={0}随机选择的数字为{1} lblPlayerChoosesNumberIs={0}选择的数字为:{1} -lblRandomColorChosen=Randomly chosen color: {0} -lblRandomTypeChosen=Randomly chosen type: {0} +lblRandomColorChosen=随机选择的颜色为{0} +lblRandomTypeChosen=随机选择的类型为{0} lblPlayerChooseValueOfEffectOfCard={0}选择{2}对{1}生效 lblPlayerFlipComesUpValue={0}掷到了{1} lblPlayerActionFlip={0}{1}了骰子 diff --git a/forge-gui/res/lists/TypeLists.txt b/forge-gui/res/lists/TypeLists.txt index 0f0c14cfd53..a0003cb3d5d 100644 --- a/forge-gui/res/lists/TypeLists.txt +++ b/forge-gui/res/lists/TypeLists.txt @@ -301,6 +301,7 @@ Rune Saga Shrine [ArtifactTypes] +Blood Clue:Clues Contraption Equipment diff --git a/forge-gui/res/tokenscripts/b_2_3_vampire_flying_lifelink.txt b/forge-gui/res/tokenscripts/b_2_3_vampire_flying_lifelink.txt new file mode 100644 index 00000000000..87ec10ae959 --- /dev/null +++ b/forge-gui/res/tokenscripts/b_2_3_vampire_flying_lifelink.txt @@ -0,0 +1,8 @@ +Name:Vampire +ManaCost:no cost +Types:Creature Vampire +Colors:black +PT:2/3 +K:Flying +K:Lifelink +Oracle:Flying, lifelink diff --git a/forge-gui/res/tokenscripts/c_a_blood_draw.txt b/forge-gui/res/tokenscripts/c_a_blood_draw.txt new file mode 100644 index 00000000000..c16816c0b0a --- /dev/null +++ b/forge-gui/res/tokenscripts/c_a_blood_draw.txt @@ -0,0 +1,5 @@ +Name:Blood +ManaCost:no cost +Types:Artifact Blood +A:AB$ Draw | Cost$ 1 T Discard<1/Card> Sac<1/CARDNAME/this artifact> | NumCards$ 1 | SpellDescription$ Draw a card. +Oracle:{1}, {T}, Discard a card, Sacrifice this artifact: Draw a card. diff --git a/forge-gui/res/tokenscripts/gw_1_1_human_soldier_training.txt b/forge-gui/res/tokenscripts/gw_1_1_human_soldier_training.txt new file mode 100644 index 00000000000..dd45be9288e --- /dev/null +++ b/forge-gui/res/tokenscripts/gw_1_1_human_soldier_training.txt @@ -0,0 +1,7 @@ +Name:Human Soldier +ManaCost:no cost +Types:Creature Human Soldier +Colors:white,green +PT:1/1 +K:Training +Oracle:Training (Whenever this creature attacks with another creature with greater power, put a +1/+1 counter on this creature.) diff --git a/forge-gui/res/tokenscripts/w_4_4_spirit_flying.txt b/forge-gui/res/tokenscripts/w_4_4_spirit_flying.txt new file mode 100644 index 00000000000..f93b7ac1154 --- /dev/null +++ b/forge-gui/res/tokenscripts/w_4_4_spirit_flying.txt @@ -0,0 +1,7 @@ +Name:Spirit +ManaCost:no cost +Types:Creature Spirit +Colors:white +PT:4/4 +K:Flying +Oracle:Flying diff --git a/forge-gui/src/main/java/forge/gamemodes/match/input/InputConfirm.java b/forge-gui/src/main/java/forge/gamemodes/match/input/InputConfirm.java index f44c76a5a1a..ba4ce9dd2a4 100644 --- a/forge-gui/src/main/java/forge/gamemodes/match/input/InputConfirm.java +++ b/forge-gui/src/main/java/forge/gamemodes/match/input/InputConfirm.java @@ -21,6 +21,7 @@ import java.util.List; import com.google.common.collect.ImmutableList; +import forge.game.ability.ApiType; import forge.game.card.Card; import forge.game.card.CardView; import forge.game.spellability.SpellAbility; @@ -73,7 +74,13 @@ public class InputConfirm extends InputSyncronizedBase { } public static boolean confirm(final PlayerControllerHuman controller, final SpellAbility sa, final String message, final boolean defaultIsYes, final List options) { if (GuiBase.getInterface().isLibgdxPort()) { - return controller.getGui().confirm((sa==null)?null:CardView.get(sa.getHostCard()), message, defaultIsYes, options); + if (sa == null) + return controller.getGui().confirm(null, message, defaultIsYes, options); + if (sa.getTargets() != null && sa.getTargets().isTargetingAnyCard() && sa.getTargets().size() == 1) + return controller.getGui().confirm((sa.getTargetCard()==null)?null:CardView.get(sa.getTargetCard()), message, defaultIsYes, options); + if (ApiType.Play.equals(sa.getApi()) && sa.getHostCard() != null && sa.getHostCard().getImprintedCards().size() == 1) + return controller.getGui().confirm((sa.getHostCard().getImprintedCards().get(0)==null)?null:CardView.get(sa.getHostCard().getImprintedCards().get(0)), message, defaultIsYes, options); + return controller.getGui().confirm(CardView.get(sa.getHostCard()), message, defaultIsYes, options); } else { InputConfirm inp; if (options.size() == 2) { diff --git a/forge-gui/src/main/java/forge/gamemodes/match/input/InputSelectCardsForConvokeOrImprovise.java b/forge-gui/src/main/java/forge/gamemodes/match/input/InputSelectCardsForConvokeOrImprovise.java index b7012d86fff..55c20e6a897 100644 --- a/forge-gui/src/main/java/forge/gamemodes/match/input/InputSelectCardsForConvokeOrImprovise.java +++ b/forge-gui/src/main/java/forge/gamemodes/match/input/InputSelectCardsForConvokeOrImprovise.java @@ -73,7 +73,7 @@ public final class InputSelectCardsForConvokeOrImprovise extends InputSelectMany if (improvise) { chosenColor = ManaCostShard.COLORLESS.getColorMask(); } else { - ColorSet colors = card.determineColor(); + ColorSet colors = card.getColor(); if (colors.isMulticolor()) { //if card is multicolor, strip out any colors which can't be paid towards remaining cost colors = ColorSet.fromMask(colors.getColor() & remainingCost.getUnpaidColors()); diff --git a/forge-gui/src/main/java/forge/player/HumanPlay.java b/forge-gui/src/main/java/forge/player/HumanPlay.java index 9b333c9765c..3e537f4b2aa 100644 --- a/forge-gui/src/main/java/forge/player/HumanPlay.java +++ b/forge-gui/src/main/java/forge/player/HumanPlay.java @@ -11,6 +11,7 @@ import org.apache.commons.lang3.StringUtils; import com.google.common.collect.Iterables; +import forge.card.CardStateName; import forge.card.mana.ManaCost; import forge.game.Game; import forge.game.GameActionUtil; @@ -83,14 +84,20 @@ public class HumanPlay { sa.setActivatingPlayer(p); boolean flippedToCast = sa.isSpell() && source.isFaceDown(); - source.setSplitStateToPlayAbility(sa); sa = chooseOptionalAdditionalCosts(p, sa); if (sa == null) { return false; } + final CardStateName oldState = source.getCurrentStateName(); + source.setSplitStateToPlayAbility(sa); + // extra play check if (sa.isSpell() && !sa.canPlay()) { + // in case human won't pay optional cost + if (source.getCurrentStateName() != oldState) { + source.setState(oldState, true); + } return false; } @@ -143,7 +150,7 @@ public class HumanPlay { final SpellAbility choosen = c.getAbilityToPlay(original.getHostCard(), abilities); - List list = GameActionUtil.getOptionalCostValues(choosen); + List list = GameActionUtil.getOptionalCostValues(choosen); if (!list.isEmpty()) { list = c.chooseOptionalCosts(choosen, list); }