mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-18 11:48:02 +00:00
Merge branch 'Card-Forge:master' into master
This commit is contained in:
@@ -84,7 +84,7 @@ public class EnemyEdit extends FormPanel {
|
||||
currentData.speed= ((Double) speed.getValue()).floatValue();
|
||||
currentData.spawnRate=((Double) spawnRate.getValue()).floatValue();
|
||||
currentData.difficulty=((Double) difficulty.getValue()).floatValue();
|
||||
currentData.deck= deck.getEdit().getText();
|
||||
currentData.deck= deck.getEdit().getText().split(",");
|
||||
currentData.rewards= rewards.getRewards();
|
||||
preview.setSpritePath(currentData.sprite);
|
||||
}
|
||||
@@ -113,7 +113,7 @@ public class EnemyEdit extends FormPanel {
|
||||
equipment.setText(String.join(",",currentData.equipment));
|
||||
else
|
||||
equipment.setText("");
|
||||
deck.getEdit().setText(currentData.deck);
|
||||
deck.getEdit().setText(String.join(",",currentData.deck));
|
||||
speed.setValue(new Float(currentData.speed).doubleValue());
|
||||
spawnRate.setValue(new Float(currentData.spawnRate).doubleValue());
|
||||
difficulty.setValue(new Float(currentData.difficulty).doubleValue());
|
||||
|
||||
@@ -179,7 +179,7 @@ public class AiAttackController {
|
||||
|
||||
List<Player> opps = Lists.newArrayList(ai.getOpponents());
|
||||
if (forCombatDmg) {
|
||||
for (Player p : ai.getOpponents().threadSafeIterable()) {
|
||||
for (Player p : ai.getOpponents()) {
|
||||
if (p.isMonarch() && ai.canBecomeMonarch()) {
|
||||
// just increase the odds for now instead of being fully predictable
|
||||
// as it could lead to other too complex factors giving this reasoning negative impact
|
||||
|
||||
@@ -782,15 +782,9 @@ public class AiController {
|
||||
return AiPlayDecision.CantAfford;
|
||||
}
|
||||
}
|
||||
if (wardCost.hasSpecificCostType(CostPayLife.class)) {
|
||||
int lifeToPay = wardCost.getCostPartByType(CostPayLife.class).convertAmount();
|
||||
if (lifeToPay > player.getLife() || (lifeToPay == player.getLife() && !player.cantLoseForZeroOrLessLife())) {
|
||||
return AiPlayDecision.CantAfford;
|
||||
}
|
||||
}
|
||||
if (wardCost.hasSpecificCostType(CostDiscard.class)
|
||||
&& wardCost.getCostPartByType(CostDiscard.class).convertAmount() > player.getCardsIn(ZoneType.Hand).size()) {
|
||||
return AiPlayDecision.CantAfford;
|
||||
SpellAbilityAi topAI = new SpellAbilityAi() {};
|
||||
if (!topAI.willPayCosts(player, sa , wardCost, host)) {
|
||||
return AiPlayDecision.CostNotAcceptable;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1473,11 +1467,9 @@ public class AiController {
|
||||
return singleSpellAbilityList(simPicker.chooseSpellAbilityToPlay(null));
|
||||
}
|
||||
|
||||
CardCollection landsWannaPlay = ComputerUtilAbility.getAvailableLandsToPlay(game, player);
|
||||
CardCollection playBeforeLand = CardLists.filter(
|
||||
player.getCardsIn(ZoneType.Hand), CardPredicates.hasSVar("PlayBeforeLandDrop")
|
||||
);
|
||||
|
||||
if (!playBeforeLand.isEmpty()) {
|
||||
SpellAbility wantToPlayBeforeLand = chooseSpellAbilityToPlayFromList(
|
||||
ComputerUtilAbility.getSpellAbilities(playBeforeLand, player), false
|
||||
@@ -1487,6 +1479,7 @@ public class AiController {
|
||||
}
|
||||
}
|
||||
|
||||
CardCollection landsWannaPlay = ComputerUtilAbility.getAvailableLandsToPlay(game, player);
|
||||
if (landsWannaPlay != null) {
|
||||
landsWannaPlay = filterLandsToPlay(landsWannaPlay);
|
||||
Log.debug("Computer " + game.getPhaseHandler().getPhase().nameForUi);
|
||||
|
||||
@@ -448,7 +448,7 @@ public class AiCostDecision extends CostDecisionMakerBase {
|
||||
|
||||
final AiController aic = ((PlayerControllerAi)player.getController()).getAi();
|
||||
CardCollectionView list = aic.chooseSacrificeType(cost.getType(), ability, isEffect(), c, null);
|
||||
return PaymentDecision.card(list);
|
||||
return list == null ? null : PaymentDecision.card(list);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -33,6 +33,7 @@ import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Multimap;
|
||||
|
||||
import forge.ai.AiCardMemory.MemorySet;
|
||||
import forge.ai.ability.ChooseGenericEffectAi;
|
||||
import forge.ai.ability.ProtectAi;
|
||||
import forge.ai.ability.TokenAi;
|
||||
@@ -449,14 +450,27 @@ public class ComputerUtil {
|
||||
}
|
||||
|
||||
// try everything when about to die
|
||||
if (game.getPhaseHandler().getPhase().equals(PhaseType.COMBAT_DECLARE_BLOCKERS)
|
||||
&& ComputerUtilCombat.lifeInSeriousDanger(ai, game.getCombat())) {
|
||||
final CardCollection nonCreatures = CardLists.getNotType(typeList, "Creature");
|
||||
if (!nonCreatures.isEmpty()) {
|
||||
return ComputerUtilCard.getWorstAI(nonCreatures);
|
||||
} else if (!typeList.isEmpty()) {
|
||||
// TODO make sure survival is possible in case the creature blocks a trampler
|
||||
return ComputerUtilCard.getWorstAI(typeList);
|
||||
if (game.getPhaseHandler().getPhase().equals(PhaseType.COMBAT_DECLARE_BLOCKERS)) {
|
||||
// in some rare situations the call to lifeInDanger could lead us back here, this will prevent an overflow
|
||||
boolean preventReturn = sa != null && sa.isManaAbility();
|
||||
if (preventReturn) {
|
||||
AiCardMemory.rememberCard(ai, sa.getHostCard(), MemorySet.HELD_MANA_SOURCES_FOR_NEXT_SPELL);
|
||||
}
|
||||
|
||||
boolean danger = ComputerUtilCombat.lifeInSeriousDanger(ai, game.getCombat());
|
||||
|
||||
if (preventReturn) {
|
||||
AiCardMemory.forgetCard(ai, sa.getHostCard(), MemorySet.HELD_MANA_SOURCES_FOR_NEXT_SPELL);
|
||||
}
|
||||
|
||||
if (danger) {
|
||||
final CardCollection nonCreatures = CardLists.getNotType(typeList, "Creature");
|
||||
if (!nonCreatures.isEmpty()) {
|
||||
return ComputerUtilCard.getWorstAI(nonCreatures);
|
||||
} else if (!typeList.isEmpty()) {
|
||||
// TODO make sure survival is possible in case the creature blocks a trampler
|
||||
return ComputerUtilCard.getWorstAI(typeList);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -609,7 +623,7 @@ public class ComputerUtil {
|
||||
int count = 0;
|
||||
|
||||
while (count < amount) {
|
||||
Card prefCard = getCardPreference(ai, source, "SacCost", typeList);
|
||||
Card prefCard = getCardPreference(ai, source, "SacCost", typeList, ability);
|
||||
if (prefCard == null) {
|
||||
prefCard = ComputerUtilCard.getWorstAI(typeList);
|
||||
}
|
||||
|
||||
@@ -45,6 +45,7 @@ import forge.game.combat.CombatUtil;
|
||||
import forge.game.cost.Cost;
|
||||
import forge.game.cost.CostPayEnergy;
|
||||
import forge.game.cost.CostRemoveCounter;
|
||||
import forge.game.cost.CostUntap;
|
||||
import forge.game.keyword.Keyword;
|
||||
import forge.game.keyword.KeywordCollection;
|
||||
import forge.game.keyword.KeywordInterface;
|
||||
@@ -1417,12 +1418,14 @@ public class ComputerUtilCard {
|
||||
double nonCombatChance = 0.0f;
|
||||
double combatChance = 0.0f;
|
||||
// non-combat Haste: has an activated ability with tap cost
|
||||
for (SpellAbility ab : c.getSpellAbilities()) {
|
||||
Cost abCost = ab.getPayCosts();
|
||||
if (abCost != null && abCost.hasTapCost()
|
||||
&& (!abCost.hasManaCost() || ComputerUtilMana.canPayManaCost(ab, ai, 0, false))) {
|
||||
nonCombatChance += 0.5f;
|
||||
break;
|
||||
if (c.isAbilitySick()) {
|
||||
for (SpellAbility ab : c.getSpellAbilities()) {
|
||||
Cost abCost = ab.getPayCosts();
|
||||
if (abCost != null && (abCost.hasTapCost() || abCost.hasSpecificCostType(CostUntap.class))
|
||||
&& (!abCost.hasManaCost() || ComputerUtilMana.canPayManaCost(ab, ai, sa.getPayCosts().getTotalMana().getCMC(), false))) {
|
||||
nonCombatChance += 0.5f;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// combat Haste: only grant it if the creature will attack
|
||||
@@ -1730,6 +1733,7 @@ public class ComputerUtilCard {
|
||||
}
|
||||
final long timestamp2 = c.getGame().getNextTimestamp(); //is this necessary or can the timestamp be re-used?
|
||||
pumped.addChangedCardKeywordsInternal(toCopy, null, false, timestamp2, 0, false);
|
||||
pumped.updateKeywordsCache(pumped.getCurrentState());
|
||||
applyStaticContPT(ai.getGame(), pumped, new CardCollection(c));
|
||||
return pumped;
|
||||
}
|
||||
@@ -1916,7 +1920,7 @@ public class ComputerUtilCard {
|
||||
}
|
||||
}
|
||||
if (!canBeBlocked) {
|
||||
boolean threat = atk.getNetCombatDamage() >= ai.getLife() - lifeInDanger;
|
||||
boolean threat = ComputerUtilCombat.getAttack(atk) >= ai.getLife() - lifeInDanger;
|
||||
if (!priorityRemovalOnlyInDanger || threat) {
|
||||
priorityCards.add(atk);
|
||||
}
|
||||
|
||||
@@ -328,10 +328,10 @@ public class ComputerUtilCombat {
|
||||
if (blockers.size() == 0
|
||||
|| StaticAbilityAssignCombatDamageAsUnblocked.assignCombatDamageAsUnblocked(attacker)) {
|
||||
unblocked.add(attacker);
|
||||
} else if (attacker.hasKeyword(Keyword.TRAMPLE)
|
||||
&& getAttack(attacker) > totalShieldDamage(attacker, blockers)) {
|
||||
if (!attacker.hasKeyword(Keyword.INFECT)) {
|
||||
damage += getAttack(attacker) - totalShieldDamage(attacker, blockers);
|
||||
} else if (attacker.hasKeyword(Keyword.TRAMPLE) && !attacker.hasKeyword(Keyword.INFECT)) {
|
||||
int dmgAfterShielding = getAttack(attacker) - totalShieldDamage(attacker, blockers);
|
||||
if (dmgAfterShielding > 0) {
|
||||
damage += dmgAfterShielding;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -369,13 +369,14 @@ public class ComputerUtilCombat {
|
||||
if (blockers.size() == 0
|
||||
|| StaticAbilityAssignCombatDamageAsUnblocked.assignCombatDamageAsUnblocked(attacker)) {
|
||||
unblocked.add(attacker);
|
||||
} else if (attacker.hasKeyword(Keyword.TRAMPLE)
|
||||
&& getAttack(attacker) > totalShieldDamage(attacker, blockers)) {
|
||||
} else if (attacker.hasKeyword(Keyword.TRAMPLE)) {
|
||||
int trampleDamage = getAttack(attacker) - totalShieldDamage(attacker, blockers);
|
||||
if (attacker.hasKeyword(Keyword.INFECT)) {
|
||||
poison += trampleDamage;
|
||||
if (trampleDamage > 0) {
|
||||
if (attacker.hasKeyword(Keyword.INFECT)) {
|
||||
poison += trampleDamage;
|
||||
}
|
||||
poison += predictPoisonFromTriggers(attacker, ai, trampleDamage);
|
||||
}
|
||||
poison += predictPoisonFromTriggers(attacker, ai, trampleDamage);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -686,7 +687,7 @@ public class ComputerUtilCombat {
|
||||
final int defenderDefense = blocker.getLethalDamage() - flankingMagnitude + defBushidoMagnitude;
|
||||
|
||||
return defenderDefense;
|
||||
} // shieldDamage
|
||||
}
|
||||
|
||||
// For AI safety measures like Regeneration
|
||||
/**
|
||||
@@ -2053,7 +2054,6 @@ public class ComputerUtilCombat {
|
||||
if (block.size() == 1) {
|
||||
final Card blocker = block.getFirst();
|
||||
|
||||
// trample
|
||||
if (hasTrample) {
|
||||
int dmgToKill = getEnoughDamageToKill(blocker, dmgCanDeal, attacker, true);
|
||||
|
||||
@@ -2109,7 +2109,7 @@ public class ComputerUtilCombat {
|
||||
}
|
||||
}
|
||||
return damageMap;
|
||||
} // setAssignedDamage()
|
||||
}
|
||||
|
||||
// how much damage is enough to kill the creature (for AI)
|
||||
/**
|
||||
|
||||
@@ -234,7 +234,7 @@ public class ComputerUtilCost {
|
||||
return true;
|
||||
}
|
||||
|
||||
public static boolean checkForManaSacrificeCost(final Player ai, final Cost cost, final Card source, final SpellAbility sourceAbility, final boolean effect) {
|
||||
public static boolean checkForManaSacrificeCost(final Player ai, final Cost cost, final SpellAbility sourceAbility, final boolean effect) {
|
||||
// TODO cheating via autopay can still happen, need to get the real ai player from controlledBy
|
||||
if (cost == null || !ai.isAI()) {
|
||||
return true;
|
||||
@@ -247,18 +247,17 @@ public class ComputerUtilCost {
|
||||
exclude.addAll(AiCardMemory.getMemorySet(ai, MemorySet.PAYS_SAC_COST));
|
||||
}
|
||||
if (part.payCostFromSource()) {
|
||||
list.add(source);
|
||||
list.add(sourceAbility.getHostCard());
|
||||
} else if (part.getType().equals("OriginalHost")) {
|
||||
list.add(sourceAbility.getOriginalHost());
|
||||
} else if (part.getAmount().equals("All")) {
|
||||
// Does the AI want to use Sacrifice All?
|
||||
return false;
|
||||
} else {
|
||||
final String amount = part.getAmount();
|
||||
Integer c = part.convertAmount();
|
||||
|
||||
if (c == null) {
|
||||
c = AbilityUtils.calculateAmount(source, amount, sourceAbility);
|
||||
c = part.getAbilityAmount(sourceAbility);
|
||||
}
|
||||
final AiController aic = ((PlayerControllerAi)ai.getController()).getAi();
|
||||
CardCollectionView choices = aic.chooseSacrificeType(part.getType(), sourceAbility, effect, c, exclude);
|
||||
@@ -636,7 +635,7 @@ public class ComputerUtilCost {
|
||||
|
||||
return ComputerUtilMana.canPayManaCost(sa, player, extraManaNeeded, effect)
|
||||
&& CostPayment.canPayAdditionalCosts(sa.getPayCosts(), sa);
|
||||
} // canPayCost()
|
||||
}
|
||||
|
||||
public static boolean willPayUnlessCost(SpellAbility sa, Player payer, Cost cost, boolean alreadyPaid, FCollectionView<Player> payers) {
|
||||
final Card source = sa.getHostCard();
|
||||
|
||||
@@ -316,10 +316,6 @@ public class ComputerUtilMana {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!ComputerUtilCost.checkForManaSacrificeCost(ai, ma.getPayCosts(), ma.getHostCard(), ma, ma.isTrigger())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (sa.getApi() == ApiType.Animate) {
|
||||
// For abilities like Genju of the Cedars, make sure that we're not activating the aura ability by tapping the enchanted card for mana
|
||||
if (saHost.isAura() && "Enchanted".equals(sa.getParam("Defined"))
|
||||
@@ -381,9 +377,15 @@ public class ComputerUtilMana {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (canPayShardWithSpellAbility(toPay, ai, paymentChoice, sa, checkCosts, cost.getXManaCostPaidByColor())) {
|
||||
return paymentChoice;
|
||||
if (!canPayShardWithSpellAbility(toPay, ai, paymentChoice, sa, checkCosts, cost.getXManaCostPaidByColor())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!ComputerUtilCost.checkForManaSacrificeCost(ai, ma.getPayCosts(), ma, ma.isTrigger())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return paymentChoice;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -620,6 +620,9 @@ public abstract class GameState {
|
||||
|
||||
game.getAction().checkStateEffects(true); //ensure state based effects and triggers are updated
|
||||
|
||||
// prevent interactions with objects from old state
|
||||
game.copyLastState();
|
||||
|
||||
// Set negative or zero life after state effects if need be, important for some puzzles that rely on
|
||||
// pre-setting negative life (e.g. PS_NEO4).
|
||||
for (int i = 0; i < playerStates.size(); i++) {
|
||||
|
||||
@@ -43,8 +43,6 @@ import forge.game.card.CardView;
|
||||
import forge.game.card.CounterType;
|
||||
import forge.game.combat.Combat;
|
||||
import forge.game.cost.Cost;
|
||||
import forge.game.cost.CostAdjustment;
|
||||
import forge.game.cost.CostExile;
|
||||
import forge.game.cost.CostPart;
|
||||
import forge.game.cost.CostPartMana;
|
||||
import forge.game.keyword.KeywordInterface;
|
||||
@@ -698,24 +696,6 @@ public class PlayerControllerAi extends PlayerController {
|
||||
ability.setActivatingPlayer(c.getController(), true);
|
||||
ability.setCardState(sa.getCardState());
|
||||
|
||||
// FIXME: This is a hack to check if the AI can play the "exile from library" pay costs (Cumulative Upkeep,
|
||||
// e.g. Thought Lash). We have to do it and bail early if the AI can't pay, because otherwise the AI will
|
||||
// pay the cost partially, which should not be possible
|
||||
int nExileLib = 0;
|
||||
List<CostPart> parts = CostAdjustment.adjust(cost, sa).getCostParts();
|
||||
for (final CostPart part : parts) {
|
||||
if (part instanceof CostExile) {
|
||||
CostExile exile = (CostExile) part;
|
||||
if (exile.from == ZoneType.Library) {
|
||||
nExileLib += exile.convertAmount();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (nExileLib > c.getController().getCardsIn(ZoneType.Library).size()) {
|
||||
return false;
|
||||
}
|
||||
// - End of hack for Exile a card from library Cumulative Upkeep -
|
||||
|
||||
if (ComputerUtilCost.canPayCost(ability, c.getController(), true)) {
|
||||
ComputerUtil.playNoStack(c.getController(), ability, getGame(), true);
|
||||
// transfer this info for Balduvian Fallen
|
||||
|
||||
@@ -1580,7 +1580,7 @@ public class AttachAi extends SpellAbilityAi {
|
||||
&& canBeBlocked
|
||||
&& ComputerUtilCombat.canAttackNextTurn(card);
|
||||
} else if (keyword.equals("Haste")) {
|
||||
return card.hasSickness() && ph.isPlayerTurn(sa.getActivatingPlayer()) && !card.isTapped()
|
||||
return card.hasSickness() && ph.isPlayerTurn(ai) && !card.isTapped()
|
||||
&& card.getNetCombatDamage() + powerBonus > 0
|
||||
&& !ph.getPhase().isAfter(PhaseType.COMBAT_DECLARE_ATTACKERS)
|
||||
&& ComputerUtilCombat.canAttackNextTurn(card);
|
||||
|
||||
@@ -113,9 +113,9 @@ public abstract class PumpAiBase extends SpellAbilityAi {
|
||||
return CombatUtil.canAttack(card, ai) || CombatUtil.canBlock(card, true);
|
||||
}
|
||||
if (!ph.isPlayerTurn(ai)) {
|
||||
return CombatUtil.canAttack(card, ai)
|
||||
&& (card.getNetCombatDamage() > 0)
|
||||
&& !ph.getPhase().isAfter(PhaseType.COMBAT_DECLARE_ATTACKERS);
|
||||
return card.getNetCombatDamage() > 0
|
||||
&& !ph.getPhase().isAfter(PhaseType.COMBAT_DECLARE_ATTACKERS)
|
||||
&& CombatUtil.canAttack(card, ai);
|
||||
} else {
|
||||
if (ph.getPhase().isAfter(PhaseType.COMBAT_DECLARE_BLOCKERS)
|
||||
|| ph.getPhase().isBefore(PhaseType.MAIN1)) {
|
||||
@@ -129,7 +129,7 @@ public abstract class PumpAiBase extends SpellAbilityAi {
|
||||
&& (combat == null || !combat.isAttacking(c))) {
|
||||
return false;
|
||||
}
|
||||
return CombatUtil.canAttack(c, card.getController()) || (combat != null && combat.isAttacking(c));
|
||||
return (combat != null && combat.isAttacking(c)) || CombatUtil.canAttack(c, card.getController());
|
||||
}
|
||||
});
|
||||
return CombatUtil.canBlockAtLeastOne(card, attackers);
|
||||
@@ -148,8 +148,8 @@ public abstract class PumpAiBase extends SpellAbilityAi {
|
||||
return false;
|
||||
}
|
||||
// the cards controller needs to be the one attacked
|
||||
return CombatUtil.canAttack(c, card.getController()) || (combat != null && combat.isAttacking(c)
|
||||
&& card.getController().equals(combat.getDefenderPlayerByAttacker(c)));
|
||||
return (combat != null && combat.isAttacking(c) && card.getController().equals(combat.getDefenderPlayerByAttacker(c))) ||
|
||||
CombatUtil.canAttack(c, card.getController());
|
||||
}
|
||||
});
|
||||
return CombatUtil.canBlockAtLeastOne(card, attackers);
|
||||
@@ -199,7 +199,7 @@ public abstract class PumpAiBase extends SpellAbilityAi {
|
||||
final boolean evasive = keyword.endsWith("Shadow");
|
||||
// give evasive keywords to creatures that can or do attack
|
||||
if (evasive) {
|
||||
return !ph.isPlayerTurn(opp) && (CombatUtil.canAttack(card, opp) || (combat != null && combat.isAttacking(card)))
|
||||
return !ph.isPlayerTurn(opp) && ((combat != null && combat.isAttacking(card)) || CombatUtil.canAttack(card, opp))
|
||||
&& !ph.getPhase().isAfter(PhaseType.COMBAT_DECLARE_ATTACKERS)
|
||||
&& newPower > 0
|
||||
&& Iterables.any(opp.getCreaturesInPlay(), CardPredicates.possibleBlockers(card));
|
||||
@@ -231,7 +231,7 @@ public abstract class PumpAiBase extends SpellAbilityAi {
|
||||
}
|
||||
}
|
||||
}
|
||||
return !ph.isPlayerTurn(opp) && (CombatUtil.canAttack(card, opp) || (combat != null && combat.isAttacking(card)))
|
||||
return !ph.isPlayerTurn(opp) && ((combat != null && combat.isAttacking(card)) || CombatUtil.canAttack(card, opp))
|
||||
&& !ph.getPhase().isAfter(PhaseType.COMBAT_DECLARE_ATTACKERS)
|
||||
&& newPower > 0
|
||||
&& Iterables.any(CardLists.filter(opp.getCreaturesInPlay(), CardPredicates.possibleBlockers(card)),
|
||||
@@ -244,25 +244,25 @@ public abstract class PumpAiBase extends SpellAbilityAi {
|
||||
&& ComputerUtilCombat.lifeInDanger(ai, game.getCombat())) {
|
||||
return true;
|
||||
}
|
||||
return !ph.isPlayerTurn(opp) && (CombatUtil.canAttack(card, opp) || (combat != null && combat.isAttacking(card)))
|
||||
return !ph.isPlayerTurn(opp) && ((combat != null && combat.isAttacking(card)) || CombatUtil.canAttack(card, opp))
|
||||
&& !ph.getPhase().isAfter(PhaseType.COMBAT_DECLARE_ATTACKERS)
|
||||
&& newPower > 0
|
||||
&& !CardLists.getNotKeyword(CardLists.filter(opp.getCreaturesInPlay(), CardPredicates.possibleBlockers(card)),
|
||||
Keyword.HORSEMANSHIP).isEmpty();
|
||||
} else if (keyword.endsWith("Intimidate")) {
|
||||
return !ph.isPlayerTurn(opp) && (CombatUtil.canAttack(card, opp) || (combat != null && combat.isAttacking(card)))
|
||||
return !ph.isPlayerTurn(opp) && ((combat != null && combat.isAttacking(card)) || CombatUtil.canAttack(card, opp))
|
||||
&& !ph.getPhase().isAfter(PhaseType.COMBAT_DECLARE_ATTACKERS)
|
||||
&& newPower > 0
|
||||
&& !CardLists.getNotType(CardLists.filter(
|
||||
opp.getCreaturesInPlay(), CardPredicates.possibleBlockers(card)), "Artifact").isEmpty();
|
||||
} else if (keyword.endsWith("Fear")) {
|
||||
return !ph.isPlayerTurn(opp) && (CombatUtil.canAttack(card, opp) || (combat != null && combat.isAttacking(card)))
|
||||
return !ph.isPlayerTurn(opp) && ((combat != null && combat.isAttacking(card)) || CombatUtil.canAttack(card, opp))
|
||||
&& !ph.getPhase().isAfter(PhaseType.COMBAT_DECLARE_ATTACKERS)
|
||||
&& newPower > 0
|
||||
&& !CardLists.getNotColor(CardLists.getNotType(CardLists.filter(
|
||||
opp.getCreaturesInPlay(), CardPredicates.possibleBlockers(card)), "Artifact"), MagicColor.BLACK).isEmpty();
|
||||
} else if (keyword.endsWith("Haste")) {
|
||||
return card.hasSickness() && !ph.isPlayerTurn(opp) && !card.isTapped()
|
||||
return CombatUtil.isAttackerSick(card, opp) && !ph.isPlayerTurn(opp) && !card.isTapped()
|
||||
&& newPower > 0
|
||||
&& !ph.getPhase().isAfter(PhaseType.COMBAT_DECLARE_ATTACKERS)
|
||||
&& ComputerUtilCombat.canAttackNextTurn(card);
|
||||
@@ -293,7 +293,7 @@ public abstract class PumpAiBase extends SpellAbilityAi {
|
||||
}
|
||||
return false;
|
||||
} else if (keyword.startsWith("Bushido")) {
|
||||
return !ph.isPlayerTurn(opp) && (CombatUtil.canAttack(card, opp) || (combat != null && combat.isAttacking(card)))
|
||||
return !ph.isPlayerTurn(opp) && ((combat != null && combat.isAttacking(card)) || CombatUtil.canAttack(card, opp))
|
||||
&& !ph.getPhase().isAfter(PhaseType.COMBAT_DECLARE_BLOCKERS)
|
||||
&& !opp.getCreaturesInPlay().isEmpty()
|
||||
&& Iterables.any(opp.getCreaturesInPlay(), CardPredicates.possibleBlockers(card));
|
||||
@@ -320,22 +320,22 @@ public abstract class PumpAiBase extends SpellAbilityAi {
|
||||
}
|
||||
return false;
|
||||
} else if (keyword.equals("Double Strike")) {
|
||||
return !ph.isPlayerTurn(opp) && (CombatUtil.canAttack(card, opp) || (combat != null && combat.isAttacking(card)))
|
||||
return !ph.isPlayerTurn(opp) && ((combat != null && combat.isAttacking(card)) || CombatUtil.canAttack(card, opp))
|
||||
&& newPower > 0
|
||||
&& !ph.getPhase().isAfter(PhaseType.COMBAT_DECLARE_BLOCKERS);
|
||||
} else if (keyword.startsWith("Rampage")) {
|
||||
return !ph.isPlayerTurn(opp) && (CombatUtil.canAttack(card, opp) || (combat != null && combat.isAttacking(card)))
|
||||
return !ph.isPlayerTurn(opp) && ((combat != null && combat.isAttacking(card)) || CombatUtil.canAttack(card, opp))
|
||||
&& newPower > 0
|
||||
&& !ph.getPhase().isAfter(PhaseType.COMBAT_DECLARE_ATTACKERS)
|
||||
&& CardLists.filter(opp.getCreaturesInPlay(), CardPredicates.possibleBlockers(card)).size() >= 2;
|
||||
} else if (keyword.startsWith("Flanking")) {
|
||||
return !ph.isPlayerTurn(opp) && (CombatUtil.canAttack(card, opp) || (combat != null && combat.isAttacking(card)))
|
||||
return !ph.isPlayerTurn(opp) && ((combat != null && combat.isAttacking(card)) || CombatUtil.canAttack(card, opp))
|
||||
&& newPower > 0
|
||||
&& !ph.getPhase().isAfter(PhaseType.COMBAT_DECLARE_ATTACKERS)
|
||||
&& !CardLists.getNotKeyword(CardLists.filter(opp.getCreaturesInPlay(), CardPredicates.possibleBlockers(card)),
|
||||
Keyword.FLANKING).isEmpty();
|
||||
} else if (keyword.startsWith("Trample")) {
|
||||
return !ph.isPlayerTurn(opp) && (CombatUtil.canAttack(card, opp) || (combat != null && combat.isAttacking(card)))
|
||||
return !ph.isPlayerTurn(opp) && ((combat != null && combat.isAttacking(card)) || CombatUtil.canAttack(card, opp))
|
||||
&& CombatUtil.canBeBlocked(card, null, opp)
|
||||
&& !ph.getPhase().isAfter(PhaseType.COMBAT_DECLARE_ATTACKERS)
|
||||
&& newPower > 1
|
||||
@@ -347,8 +347,7 @@ public abstract class PumpAiBase extends SpellAbilityAi {
|
||||
if (combat != null && combat.isBlocking(card) && !card.hasKeyword(Keyword.WITHER)) {
|
||||
return true;
|
||||
}
|
||||
return (!ph.isPlayerTurn(opp))
|
||||
&& (CombatUtil.canAttack(card, opp) || (combat != null && combat.isAttacking(card)))
|
||||
return !ph.isPlayerTurn(opp) && ((combat != null && combat.isAttacking(card)) || CombatUtil.canAttack(card, opp))
|
||||
&& !ph.getPhase().isAfter(PhaseType.COMBAT_DECLARE_BLOCKERS);
|
||||
} else if (keyword.endsWith("Wither")) {
|
||||
if (newPower <= 0 || card.hasKeyword(Keyword.INFECT)) {
|
||||
@@ -376,25 +375,25 @@ public abstract class PumpAiBase extends SpellAbilityAi {
|
||||
} else if (keyword.equals("Persist")) {
|
||||
return card.getBaseToughness() > 1 && !card.hasKeyword(Keyword.UNDYING);
|
||||
} else if (keyword.equals("Islandwalk")) {
|
||||
return !ph.isPlayerTurn(opp) && (CombatUtil.canAttack(card, opp) || (combat != null && combat.isAttacking(card)))
|
||||
return !ph.isPlayerTurn(opp) && ((combat != null && combat.isAttacking(card)) || CombatUtil.canAttack(card, opp))
|
||||
&& !ph.getPhase().isAfter(PhaseType.COMBAT_DECLARE_ATTACKERS)
|
||||
&& newPower > 0
|
||||
&& !CardLists.getType(opp.getLandsInPlay(), "Island").isEmpty()
|
||||
&& Iterables.any(opp.getCreaturesInPlay(), CardPredicates.possibleBlockers(card));
|
||||
} else if (keyword.equals("Swampwalk")) {
|
||||
return !ph.isPlayerTurn(opp) && (CombatUtil.canAttack(card, opp) || (combat != null && combat.isAttacking(card)))
|
||||
return !ph.isPlayerTurn(opp) && ((combat != null && combat.isAttacking(card)) || CombatUtil.canAttack(card, opp))
|
||||
&& !ph.getPhase().isAfter(PhaseType.COMBAT_DECLARE_ATTACKERS)
|
||||
&& newPower > 0
|
||||
&& !CardLists.getType(opp.getLandsInPlay(), "Swamp").isEmpty()
|
||||
&& Iterables.any(opp.getCreaturesInPlay(), CardPredicates.possibleBlockers(card));
|
||||
} else if (keyword.equals("Mountainwalk")) {
|
||||
return !ph.isPlayerTurn(opp) && (CombatUtil.canAttack(card, opp) || (combat != null && combat.isAttacking(card)))
|
||||
return !ph.isPlayerTurn(opp) && ((combat != null && combat.isAttacking(card)) || CombatUtil.canAttack(card, opp))
|
||||
&& !ph.getPhase().isAfter(PhaseType.COMBAT_DECLARE_ATTACKERS)
|
||||
&& newPower > 0
|
||||
&& !CardLists.getType(opp.getLandsInPlay(), "Mountain").isEmpty()
|
||||
&& Iterables.any(opp.getCreaturesInPlay(), CardPredicates.possibleBlockers(card));
|
||||
} else if (keyword.equals("Forestwalk")) {
|
||||
return !ph.isPlayerTurn(opp) && (CombatUtil.canAttack(card, opp) || (combat != null && combat.isAttacking(card)))
|
||||
return !ph.isPlayerTurn(opp) && ((combat != null && combat.isAttacking(card)) || CombatUtil.canAttack(card, opp))
|
||||
&& !ph.getPhase().isAfter(PhaseType.COMBAT_DECLARE_ATTACKERS)
|
||||
&& newPower > 0
|
||||
&& !CardLists.getType(opp.getLandsInPlay(), "Forest").isEmpty()
|
||||
|
||||
@@ -206,12 +206,7 @@ public class GameCopier {
|
||||
Card newCard = map.map(origHostCard);
|
||||
SpellAbility newSa = null;
|
||||
if (origSa.isSpell()) {
|
||||
for (SpellAbility sa : newCard.getAllSpellAbilities()) {
|
||||
if (sa.getDescription().equals(origSa.getDescription())) {
|
||||
newSa = sa;
|
||||
break;
|
||||
}
|
||||
}
|
||||
newSa = findSAInCard(origSa, newCard);
|
||||
}
|
||||
if (newSa != null) {
|
||||
newSa.setActivatingPlayer(map.map(origSa.getActivatingPlayer()), true);
|
||||
|
||||
@@ -11,6 +11,10 @@ public class MultiTargetSelector {
|
||||
public static class Targets {
|
||||
private ArrayList<PossibleTargetSelector.Targets> targets;
|
||||
|
||||
public int size() {
|
||||
return targets.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
@@ -24,8 +28,8 @@ public class MultiTargetSelector {
|
||||
}
|
||||
}
|
||||
|
||||
private List<PossibleTargetSelector> selectors;
|
||||
private List<SpellAbility> targetingSAs;
|
||||
private final List<PossibleTargetSelector> selectors;
|
||||
private final List<SpellAbility> targetingSAs;
|
||||
private int currentIndex;
|
||||
|
||||
public MultiTargetSelector(SpellAbility sa, List<AbilitySub> plannedSubs) {
|
||||
@@ -52,8 +56,8 @@ public class MultiTargetSelector {
|
||||
public Targets getLastSelectedTargets() {
|
||||
Targets targets = new Targets();
|
||||
targets.targets = new ArrayList<>(selectors.size());
|
||||
for (int i = 0; i < selectors.size(); i++) {
|
||||
targets.targets.add(selectors.get(i).getLastSelectedTargets());
|
||||
for (PossibleTargetSelector selector : selectors) {
|
||||
targets.targets.add(selector.getLastSelectedTargets());
|
||||
}
|
||||
return targets;
|
||||
}
|
||||
@@ -78,34 +82,62 @@ public class MultiTargetSelector {
|
||||
currentIndex = -1;
|
||||
}
|
||||
|
||||
public void selectTargetsByIndex(int i) {
|
||||
public boolean selectTargetsByIndex(int i) {
|
||||
// The caller is telling us to select the i-th possible set of targets.
|
||||
if (i < currentIndex) {
|
||||
reset();
|
||||
}
|
||||
while (currentIndex < i) {
|
||||
selectNextTargets();
|
||||
if (!selectNextTargets()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean selectNextTargets() {
|
||||
if (currentIndex == -1) {
|
||||
for (PossibleTargetSelector selector : selectors) {
|
||||
if (!selector.selectNextTargets()) {
|
||||
private boolean selectTargetsStartingFrom(int selectorIndex) {
|
||||
// Don't reset the current selector, as it still has the correct list of targets set and has
|
||||
// to remember its current/next target index. Subsequent selectors need a reset since their
|
||||
// possible targets may change based on what was chosen for earlier ones.
|
||||
if (selectors.get(selectorIndex).selectNextTargets()) {
|
||||
for (int i = selectorIndex + 1; i < selectors.size(); i++) {
|
||||
selectors.get(i).reset();
|
||||
if (!selectors.get(i).selectNextTargets()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
currentIndex = 0;
|
||||
return true;
|
||||
}
|
||||
for (int i = selectors.size() - 1; i >= 0; i--) {
|
||||
if (selectors.get(i).selectNextTargets()) {
|
||||
currentIndex++;
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean selectNextTargets() {
|
||||
if (selectors.size() == 0) {
|
||||
return false;
|
||||
}
|
||||
if (currentIndex == -1) {
|
||||
// Select the first set of targets (calls selectNextTargets() on each selector).
|
||||
if (selectTargetsStartingFrom(0)) {
|
||||
currentIndex = 0;
|
||||
return true;
|
||||
}
|
||||
selectors.get(i).reset();
|
||||
selectors.get(i).selectNextTargets();
|
||||
// No possible targets.
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
// Subsequent call, first try selecting a new target for the last selector. If that doesn't
|
||||
// work, backtrack (decrement selector index) and try selecting targets from there.
|
||||
// This approach ensures that leaf selectors (end of list) are advanced first, before
|
||||
// previous ones, so that we get an AA,AB,BA,BB ordering.
|
||||
int selectorIndex = selectors.size() - 1;
|
||||
while (!selectTargetsStartingFrom(selectorIndex)) {
|
||||
if (selectorIndex == 0) {
|
||||
// No more possible targets.
|
||||
return false;
|
||||
}
|
||||
selectorIndex--;
|
||||
}
|
||||
currentIndex++;
|
||||
return true;
|
||||
}
|
||||
|
||||
private static boolean conditionsAreMet(SpellAbility saOrSubSa) {
|
||||
|
||||
@@ -17,12 +17,11 @@ import forge.game.spellability.TargetRestrictions;
|
||||
|
||||
public class PossibleTargetSelector {
|
||||
private final SpellAbility sa;
|
||||
private SpellAbility targetingSa;
|
||||
private int targetingSaIndex;
|
||||
private final SpellAbility targetingSa;
|
||||
private final int targetingSaIndex;
|
||||
private int maxTargets;
|
||||
private TargetRestrictions tgt;
|
||||
private int targetIndex;
|
||||
private List<GameObject> validTargets;
|
||||
private int nextTargetIndex;
|
||||
private final List<GameObject> validTargets = new ArrayList<>();
|
||||
|
||||
public static class Targets {
|
||||
final int targetingSaIndex;
|
||||
@@ -36,7 +35,7 @@ public class PossibleTargetSelector {
|
||||
this.targetIndex = targetIndex;
|
||||
this.description = description;
|
||||
|
||||
if (targetIndex < 0 || targetIndex >= originalTargetCount) {
|
||||
if (targetIndex != -1 && (targetIndex < 0 || targetIndex >= originalTargetCount)) {
|
||||
throw new IllegalArgumentException("Invalid targetIndex=" + targetIndex);
|
||||
}
|
||||
}
|
||||
@@ -51,12 +50,11 @@ public class PossibleTargetSelector {
|
||||
this.sa = sa;
|
||||
this.targetingSa = targetingSa;
|
||||
this.targetingSaIndex = targetingSaIndex;
|
||||
this.validTargets = new ArrayList<>();
|
||||
generateValidTargets(sa.getHostCard().getController());
|
||||
reset();
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
targetIndex = 0;
|
||||
nextTargetIndex = 0;
|
||||
validTargets.clear();
|
||||
generateValidTargets(sa.getHostCard().getController());
|
||||
}
|
||||
@@ -67,7 +65,7 @@ public class PossibleTargetSelector {
|
||||
}
|
||||
sa.setActivatingPlayer(player, true);
|
||||
targetingSa.resetTargets();
|
||||
tgt = targetingSa.getTargetRestrictions();
|
||||
TargetRestrictions tgt = targetingSa.getTargetRestrictions();
|
||||
maxTargets = tgt.getMaxTargets(sa.getHostCard(), targetingSa);
|
||||
|
||||
SimilarTargetSkipper skipper = new SimilarTargetSkipper();
|
||||
@@ -80,8 +78,8 @@ public class PossibleTargetSelector {
|
||||
}
|
||||
|
||||
private static class SimilarTargetSkipper {
|
||||
private ArrayListMultimap<String, Card> validTargetsMap = ArrayListMultimap.create();
|
||||
private HashMap<Card, String> cardTypeStrings = new HashMap<>();
|
||||
private final ArrayListMultimap<String, Card> validTargetsMap = ArrayListMultimap.create();
|
||||
private final HashMap<Card, String> cardTypeStrings = new HashMap<>();
|
||||
private HashMap<Card, Integer> creatureScores;
|
||||
|
||||
private int getCreatureScore(Card c) {
|
||||
@@ -190,16 +188,7 @@ public class PossibleTargetSelector {
|
||||
}
|
||||
|
||||
public Targets getLastSelectedTargets() {
|
||||
return new Targets(targetingSaIndex, validTargets.size(), targetIndex - 1, targetingSa.getTargets().toString());
|
||||
}
|
||||
|
||||
public boolean selectTargetsByIndex(int targetIndex) {
|
||||
if (targetIndex >= validTargets.size()) {
|
||||
return false;
|
||||
}
|
||||
selectTargetsByIndexImpl(targetIndex);
|
||||
this.targetIndex = targetIndex + 1;
|
||||
return true;
|
||||
return new Targets(targetingSaIndex, validTargets.size(), nextTargetIndex - 1, targetingSa.getTargets().toString());
|
||||
}
|
||||
|
||||
public boolean selectTargets(Targets targets) {
|
||||
@@ -208,16 +197,16 @@ public class PossibleTargetSelector {
|
||||
return false;
|
||||
}
|
||||
selectTargetsByIndexImpl(targets.targetIndex);
|
||||
this.targetIndex = targets.targetIndex + 1;
|
||||
this.nextTargetIndex = targets.targetIndex + 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean selectNextTargets() {
|
||||
if (targetIndex >= validTargets.size()) {
|
||||
if (nextTargetIndex >= validTargets.size()) {
|
||||
return false;
|
||||
}
|
||||
selectTargetsByIndexImpl(targetIndex);
|
||||
targetIndex++;
|
||||
selectTargetsByIndexImpl(nextTargetIndex);
|
||||
nextTargetIndex++;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,9 +81,11 @@ public class SimulationController {
|
||||
}
|
||||
|
||||
public void doneEvaluating(Score score) {
|
||||
if (score.value > bestScore.value) {
|
||||
// if we're here during a deeper level this hasn't been called for the level above yet
|
||||
// in such case we need to check that this decision has really lead to the improvement in score
|
||||
if (getLastDecision().initialScore.value < score.value && score.value > bestScore.value) {
|
||||
bestScore = score;
|
||||
bestSequence = currentStack.get(currentStack.size() - 1);
|
||||
bestSequence = getLastDecision();
|
||||
}
|
||||
currentStack.remove(currentStack.size() - 1);
|
||||
}
|
||||
|
||||
@@ -184,7 +184,7 @@ public class GameAction {
|
||||
// if to Battlefield and it is caused by an replacement effect,
|
||||
// try to get previous LKI if able
|
||||
ReplacementEffect re = cause.getReplacementEffect();
|
||||
if (ReplacementType.Moved.equals(re.getMode())) {
|
||||
if (ReplacementType.Moved.equals(re.getMode()) && cause.getReplacingObject(AbilityKey.CardLKI).equals(c)) {
|
||||
lastKnownInfo = (Card) cause.getReplacingObject(AbilityKey.CardLKI);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -768,7 +768,7 @@ public final class GameActionUtil {
|
||||
final Game game = sourceCard.getGame();
|
||||
final Card eff = new Card(game.nextCardId(), game);
|
||||
eff.setTimestamp(game.getNextTimestamp());
|
||||
eff.setName(sourceCard.getName() + "'s Effect");
|
||||
eff.setName(sourceCard + "'s Effect");
|
||||
eff.setOwner(controller);
|
||||
|
||||
eff.setImageKey(sourceCard.getImageKey());
|
||||
|
||||
@@ -185,7 +185,8 @@ public final class AbilityFactory {
|
||||
String cost = mapParams.get("Cost");
|
||||
if (cost == null) {
|
||||
if (type == AbilityRecordType.Spell) {
|
||||
if (state.getFirstAbility() != null && state.getFirstAbility().isSpell()) {
|
||||
SpellAbility firstAbility = state.getFirstAbility();
|
||||
if (firstAbility != null && firstAbility.isSpell()) {
|
||||
// TODO might remove when Enchant Keyword is refactored
|
||||
System.err.println(state.getName() + " already has Spell using mana cost");
|
||||
}
|
||||
|
||||
@@ -2843,11 +2843,7 @@ public class AbilityUtils {
|
||||
if (sq[0].startsWith("ColorsCtrl")) {
|
||||
final String restriction = l[0].substring(11);
|
||||
final CardCollection list = CardLists.getValidCards(player.getCardsIn(ZoneType.Battlefield), restriction, player, c, ctb);
|
||||
byte n = 0;
|
||||
for (final Card card : list) {
|
||||
n |= card.getColor().getColor();
|
||||
}
|
||||
return doXMath(ColorSet.fromMask(n).countColors(), expr, c, ctb);
|
||||
return doXMath(CardUtil.getColorsFromCards(list).countColors(), expr, c, ctb);
|
||||
}
|
||||
|
||||
// TODO move below to handlePaid
|
||||
|
||||
@@ -118,12 +118,10 @@ public abstract class SpellAbilityEffect {
|
||||
int amount = AbilityUtils.calculateAmount(sa.getHostCard(), svar, sa);
|
||||
sb.append(" ");
|
||||
sb.append(TextUtil.enclosedParen(TextUtil.concatNoSpace(svar,"=",String.valueOf(amount))));
|
||||
} else {
|
||||
if (sa.costHasManaX()) {
|
||||
int amount = sa.getXManaCostPaid() == null ? 0 : sa.getXManaCostPaid();
|
||||
sb.append(" ");
|
||||
sb.append(TextUtil.enclosedParen(TextUtil.concatNoSpace("X","=",String.valueOf(amount))));
|
||||
}
|
||||
} else if (sa.costHasManaX()) {
|
||||
int amount = sa.getXManaCostPaid() == null ? 0 : sa.getXManaCostPaid();
|
||||
sb.append(" ");
|
||||
sb.append(TextUtil.enclosedParen(TextUtil.concatNoSpace("X","=",String.valueOf(amount))));
|
||||
}
|
||||
|
||||
String currentName = CardTranslation.getTranslatedName(sa.getHostCard().getName());
|
||||
|
||||
@@ -64,16 +64,13 @@ public class SacrificeEffect extends SpellAbilityEffect {
|
||||
|
||||
table.replaceCounterEffect(game, sa, true);
|
||||
|
||||
Cost cumCost = new Cost(sa.getParam("CumulativeUpkeep"), true);
|
||||
Cost payCost = new Cost(ManaCost.ZERO, true);
|
||||
int n = card.getCounters(CounterEnumType.AGE);
|
||||
|
||||
// multiply cost
|
||||
for (int i = 0; i < n; ++i) {
|
||||
payCost.add(cumCost);
|
||||
if (n > 0) {
|
||||
Cost cumCost = new Cost(sa.getParam("CumulativeUpkeep"), true);
|
||||
payCost.mergeTo(cumCost, n);
|
||||
}
|
||||
|
||||
sa.setCumulativeupkeep(true);
|
||||
game.updateLastStateForCard(card);
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
@@ -2322,7 +2322,8 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|
||||
|| keyword.startsWith("Cycling") || keyword.startsWith("TypeCycling")
|
||||
|| keyword.startsWith("Encore") || keyword.startsWith("Mutate") || keyword.startsWith("Dungeon")
|
||||
|| keyword.startsWith("Class") || keyword.startsWith("Blitz")
|
||||
|| keyword.startsWith("Specialize") || keyword.equals("Ravenous")) {
|
||||
|| keyword.startsWith("Specialize") || keyword.equals("Ravenous")
|
||||
|| keyword.equals("For Mirrodin")) {
|
||||
// keyword parsing takes care of adding a proper description
|
||||
} else if(keyword.startsWith("Read ahead")) {
|
||||
sb.append(Localizer.getInstance().getMessage("lblReadAhead")).append(" (").append(Localizer.getInstance().getMessage("lblReadAheadDesc"));
|
||||
@@ -2441,14 +2442,14 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|
||||
}
|
||||
|
||||
// add As an additional cost to Permanent spells
|
||||
if (state.getFirstAbility() != null && type.isPermanent()) {
|
||||
SpellAbility first = state.getFirstAbility();
|
||||
SpellAbility first = state.getFirstAbility();
|
||||
if (first != null && type.isPermanent()) {
|
||||
if (first.isSpell()) {
|
||||
Cost cost = first.getPayCosts();
|
||||
if (cost != null && !cost.isOnlyManaCost()) {
|
||||
String additionalDesc = "";
|
||||
if (state.getFirstAbility().hasParam("AdditionalDesc")) {
|
||||
additionalDesc = state.getFirstAbility().getParam("AdditionalDesc");
|
||||
if (first.hasParam("AdditionalDesc")) {
|
||||
additionalDesc = first.getParam("AdditionalDesc");
|
||||
}
|
||||
sb.append(cost.toString().replace("\n", "")).append(" ").append(additionalDesc);
|
||||
sb.append(linebreak);
|
||||
@@ -2681,13 +2682,14 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
|
||||
// Give spellText line breaks for easier reading
|
||||
sb.append(text.replaceAll("\\\\r\\\\n", "\r\n"));
|
||||
String spellText = text.replaceAll("\\\\r\\\\n", "\r\n");
|
||||
sb.append(spellText);
|
||||
|
||||
// NOTE:
|
||||
if (sb.toString().contains(" (NOTE: ")) {
|
||||
if (spellText.contains(" (NOTE: ")) {
|
||||
sb.insert(sb.indexOf("(NOTE: "), "\r\n");
|
||||
}
|
||||
if (sb.toString().contains("(NOTE: ") && sb.toString().endsWith(".)") && !sb.toString().endsWith("\r\n")) {
|
||||
if (spellText.contains("(NOTE: ") && spellText.endsWith(".)") && !spellText.endsWith("\r\n")) {
|
||||
sb.append("\r\n");
|
||||
}
|
||||
|
||||
@@ -3756,14 +3758,19 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|
||||
public final CardTypeView getOriginalType() {
|
||||
return getOriginalType(currentState);
|
||||
}
|
||||
public final CardTypeView getOriginalType(CardState state) {
|
||||
public final CardTypeView getOriginalType(CardState state) {
|
||||
return state.getType();
|
||||
}
|
||||
|
||||
// TODO add changed type by card text
|
||||
public Iterable<CardChangedType> getChangedCardTypes() {
|
||||
// If there are no changed types, just return an empty immutable list, which actually
|
||||
// produces a surprisingly large speedup by avoid lots of temp objects and making iteration
|
||||
// over the result much faster. (This function gets called a lot!)
|
||||
if (changedCardTypesByText.isEmpty() && changedTypeByText == null && changedCardTypesCharacterDefining.isEmpty() && changedCardTypes.isEmpty()) {
|
||||
return ImmutableList.of();
|
||||
}
|
||||
Iterable<CardChangedType> byText = changedTypeByText == null ? ImmutableList.of() : ImmutableList.of(this.changedTypeByText);
|
||||
|
||||
return Iterables.unmodifiableIterable(Iterables.concat(
|
||||
changedCardTypesByText.values(), // Layer 3
|
||||
byText, // Layer 3 by Word Changes,
|
||||
@@ -7287,4 +7294,11 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|
||||
public boolean attackVigilance() {
|
||||
return StaticAbilityAttackVigilance.attackVigilance(this);
|
||||
}
|
||||
|
||||
public boolean isAbilitySick() {
|
||||
if (!isSick()) {
|
||||
return false;
|
||||
}
|
||||
return !StaticAbilityActivateAbilityAsIfHaste.canActivate(this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1211,6 +1211,31 @@ public class CardFactoryUtil {
|
||||
trigger.setOverridingAbility(AbilityFactory.getAbility(effect, card));
|
||||
|
||||
inst.addTrigger(trigger);
|
||||
|
||||
} else if (keyword.equals("For Mirrodin")) {
|
||||
final StringBuilder sbTrig = new StringBuilder();
|
||||
sbTrig.append("Mode$ ChangesZone | Destination$ Battlefield | ");
|
||||
sbTrig.append("ValidCard$ Card.Self | TriggerDescription$ ");
|
||||
sbTrig.append("For Mirrodin! (").append(inst.getReminderText()).append(")");
|
||||
|
||||
final String sbRebel = "DB$ Token | TokenScript$ r_2_2_rebel | TokenOwner$ You | RememberTokens$ True";
|
||||
final SpellAbility saRebel= AbilityFactory.getAbility(sbRebel, card);
|
||||
|
||||
final String sbAttach = "DB$ Attach | Defined$ Remembered";
|
||||
final AbilitySub saAttach = (AbilitySub) AbilityFactory.getAbility(sbAttach, card);
|
||||
saRebel.setSubAbility(saAttach);
|
||||
|
||||
final String sbClear = "DB$ Cleanup | ClearRemembered$ True";
|
||||
final AbilitySub saClear = (AbilitySub) AbilityFactory.getAbility(sbClear, card);
|
||||
saAttach.setSubAbility(saClear);
|
||||
|
||||
final Trigger etbTrigger = TriggerHandler.parseTrigger(sbTrig.toString(), card, intrinsic);
|
||||
|
||||
etbTrigger.setOverridingAbility(saRebel);
|
||||
|
||||
saRebel.setIntrinsic(intrinsic);
|
||||
inst.addTrigger(etbTrigger);
|
||||
|
||||
} else if (keyword.startsWith("Graft")) {
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
sb.append("DB$ MoveCounter | Source$ Self | Defined$ TriggeredCardLKICopy");
|
||||
@@ -3655,10 +3680,8 @@ public class CardFactoryUtil {
|
||||
String effect = "Mode$ CantTransform | ValidCard$ Creature.Self | ExceptCause$ SpellAbility.Daybound | Secondary$ True | Description$ This permanent can't be transformed except by its daybound ability.";
|
||||
inst.addStaticAbility(StaticAbility.create(effect, state.getCard(), state, intrinsic));
|
||||
} else if (keyword.equals("Decayed")) {
|
||||
String effect = "Mode$ Continuous | Affected$ Card.Self | AddHiddenKeyword$ CARDNAME can't block. | " +
|
||||
"Secondary$ True";
|
||||
String effect = "Mode$ CantBlockBy | ValidBlocker$ Creature.Self | Secondary$ True | Description$ CARDNAME can't block.";
|
||||
StaticAbility st = StaticAbility.create(effect, state.getCard(), state, intrinsic);
|
||||
st.setSVar("SacrificeEndCombat", "True");
|
||||
inst.addStaticAbility(st);
|
||||
} else if (keyword.equals("Defender")) {
|
||||
String effect = "Mode$ CantAttack | ValidCard$ Card.Self | DefenderKeyword$ True | Secondary$ True";
|
||||
@@ -3777,7 +3800,7 @@ public class CardFactoryUtil {
|
||||
" | Description$ Strive - " + inst.getReminderText();
|
||||
inst.addStaticAbility(StaticAbility.create(effect, state.getCard(), state, intrinsic));
|
||||
} else if (keyword.equals("Unleash")) {
|
||||
String effect = "Mode$ Continuous | Affected$ Card.Self+counters_GE1_P1P1 | AddHiddenKeyword$ CARDNAME can't block.";
|
||||
String effect = "Mode$ CantBlockBy | ValidBlocker$ Creature.Self+counters_GE1_P1P1 | Secondary$ True | Description$ CARDNAME can't block.";
|
||||
inst.addStaticAbility(StaticAbility.create(effect, state.getCard(), state, intrinsic));
|
||||
} else if (keyword.equals("Undaunted")) {
|
||||
String effect = "Mode$ ReduceCost | ValidCard$ Card.Self | Type$ Spell | Secondary$ True"
|
||||
|
||||
@@ -241,6 +241,8 @@ public final class CardUtil {
|
||||
newCopy.setColor(in.getColor().getColor());
|
||||
newCopy.setPhasedOut(in.getPhasedOut());
|
||||
|
||||
newCopy.setTapped(in.isTapped());
|
||||
|
||||
newCopy.setDamageHistory(in.getDamageHistory());
|
||||
newCopy.setDamageReceivedThisTurn(in.getDamageReceivedThisTurn());
|
||||
|
||||
@@ -356,9 +358,9 @@ public final class CardUtil {
|
||||
return res;
|
||||
}
|
||||
|
||||
public static ColorSet getColorsYouCtrl(final Player p) {
|
||||
public static ColorSet getColorsFromCards(Iterable<Card> list) {
|
||||
byte b = 0;
|
||||
for (Card c : p.getCardsIn(ZoneType.Battlefield)) {
|
||||
for (Card c : list) {
|
||||
b |= c.getColor().getColor();
|
||||
}
|
||||
return ColorSet.fromMask(b);
|
||||
|
||||
@@ -1132,9 +1132,6 @@ public class CombatUtil {
|
||||
if (!canBlock(blocker, nextTurn)) {
|
||||
return false;
|
||||
}
|
||||
if (!canBeBlocked(attacker, blocker.getController())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isUnblockableFromLandwalk(attacker, blocker.getController())
|
||||
&& !blocker.hasKeyword("CARDNAME can block creatures with landwalk abilities as though they didn't have those abilities.")) {
|
||||
|
||||
@@ -254,7 +254,6 @@ public class Cost implements Serializable {
|
||||
else
|
||||
manaParts.append(part).append(" ");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (parsedMana == null && (manaParts.length() > 0 || xCantBe0)) {
|
||||
@@ -889,7 +888,24 @@ public class Cost implements Serializable {
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public void mergeTo(Cost source, int amt) {
|
||||
// multiply to create the full cost
|
||||
if (amt > 1) {
|
||||
// to double itself we need to work on a copy
|
||||
Cost sourceCpy = source.copy();
|
||||
for (int i = 1; i < amt; ++i) {
|
||||
// in theory setAmount could be used instead but it depends on the cost complexity (probably not worth trying to determine that first)
|
||||
source.add(sourceCpy);
|
||||
}
|
||||
}
|
||||
// combine costs (these shouldn't mix together)
|
||||
this.add(source, false);
|
||||
}
|
||||
|
||||
public Cost add(Cost cost1) {
|
||||
return add(cost1, true);
|
||||
}
|
||||
public Cost add(Cost cost1, boolean mergeAdditional) {
|
||||
CostPartMana costPart2 = this.getCostMana();
|
||||
List<CostPart> toRemove = Lists.newArrayList();
|
||||
for (final CostPart part : cost1.getCostParts()) {
|
||||
@@ -912,9 +928,10 @@ public class Cost implements Serializable {
|
||||
} else {
|
||||
costParts.add(0, new CostPartMana(oldManaCost.toManaCost(), r));
|
||||
}
|
||||
} else if (part instanceof CostDiscard || part instanceof CostDraw ||
|
||||
part instanceof CostAddMana || part instanceof CostPayLife
|
||||
|| part instanceof CostPutCounter || part instanceof CostTapType) {
|
||||
} else if (part instanceof CostPutCounter || (mergeAdditional && // below usually not desired because they're from different causes
|
||||
(part instanceof CostDiscard || part instanceof CostDraw ||
|
||||
part instanceof CostAddMana || part instanceof CostPayLife ||
|
||||
part instanceof CostSacrifice || part instanceof CostTapType))) {
|
||||
boolean alreadyAdded = false;
|
||||
for (final CostPart other : costParts) {
|
||||
if ((other.getClass().equals(part.getClass()) || (part instanceof CostPutCounter && ((CostPutCounter)part).getCounter().is(CounterEnumType.LOYALTY))) &&
|
||||
@@ -922,7 +939,7 @@ public class Cost implements Serializable {
|
||||
StringUtils.isNumeric(part.getAmount()) &&
|
||||
StringUtils.isNumeric(other.getAmount())) {
|
||||
String amount = String.valueOf(part.convertAmount() + other.convertAmount());
|
||||
if (part instanceof CostPutCounter) { // path for Carth & Cumulative Upkeep
|
||||
if (part instanceof CostPutCounter) { // CR 606.5 path for Carth
|
||||
if (other instanceof CostPutCounter && ((CostPutCounter)other).getCounter().equals(((CostPutCounter) part).getCounter())) {
|
||||
costParts.add(new CostPutCounter(amount, ((CostPutCounter) part).getCounter(), part.getType(), part.getTypeDescription()));
|
||||
} else if (other instanceof CostRemoveCounter && ((CostRemoveCounter)other).counter.is(CounterEnumType.LOYALTY)) {
|
||||
@@ -936,6 +953,8 @@ public class Cost implements Serializable {
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
} else if (part instanceof CostSacrifice) {
|
||||
costParts.add(new CostSacrifice(amount, part.getType(), part.getTypeDescription()));
|
||||
} else if (part instanceof CostDiscard) {
|
||||
costParts.add(new CostDiscard(amount, part.getType(), part.getTypeDescription()));
|
||||
} else if (part instanceof CostDraw) {
|
||||
|
||||
@@ -112,17 +112,14 @@ public class CostAdjustment {
|
||||
}
|
||||
|
||||
final String scost = st.getParamOrDefault("Cost", "1");
|
||||
Cost part = new Cost(scost, sa.isAbility());
|
||||
int count = 0;
|
||||
|
||||
if (st.hasParam("ForEachShard")) {
|
||||
CostPartMana mc = cost.getCostMana();
|
||||
if (mc != null) {
|
||||
byte atom = ManaAtom.fromName(st.getParam("ForEachShard").toLowerCase());
|
||||
for (ManaCostShard shard : mc.getManaCostFor(sa)) {
|
||||
if ((shard.getColorMask() & atom) != 0) {
|
||||
++count;
|
||||
}
|
||||
ManaCost mc = sa.getHostCard().getManaCost();
|
||||
byte atom = ManaAtom.fromName(st.getParam("ForEachShard").toLowerCase());
|
||||
for (ManaCostShard shard : mc) {
|
||||
if ((shard.getColorMask() & atom) != 0) {
|
||||
++count;
|
||||
}
|
||||
}
|
||||
} else if (st.hasParam("Amount")) {
|
||||
@@ -157,8 +154,9 @@ public class CostAdjustment {
|
||||
// Amount 1 as default
|
||||
count = 1;
|
||||
}
|
||||
for (int i = 0; i < count; ++i) {
|
||||
cost.add(part);
|
||||
if (count > 0) {
|
||||
Cost part = new Cost(scost, sa.isAbility());
|
||||
cost.mergeTo(part, count);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -179,7 +177,7 @@ public class CostAdjustment {
|
||||
originalCard.turnFaceDownNoUpdate();
|
||||
isStateChangeToFaceDown = true;
|
||||
}
|
||||
} // isSpell
|
||||
}
|
||||
|
||||
CardCollection cardsOnBattlefield = new CardCollection(game.getCardsIn(ZoneType.Battlefield));
|
||||
cardsOnBattlefield.addAll(game.getCardsIn(ZoneType.Stack));
|
||||
|
||||
@@ -88,7 +88,6 @@ public class CostExert extends CostPartWithList {
|
||||
typeList = CardLists.getValidCards(typeList, this.getType().split(";"), payer, source, ability);
|
||||
final int amount = this.getAbilityAmount(ability);
|
||||
|
||||
|
||||
return needsAnnoucement || (typeList.size() >= amount);
|
||||
}
|
||||
|
||||
|
||||
@@ -37,7 +37,6 @@ import forge.game.mana.ManaCostBeingPaid;
|
||||
import forge.game.mana.ManaPool;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
import forge.game.zone.ZoneType;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
@@ -200,12 +199,9 @@ public class CostPayment extends ManaConversionMatrix {
|
||||
PaymentDecision decision = part.accept(decisionMaker);
|
||||
if (null == decision) return false;
|
||||
|
||||
// the AI will try to exile the same card repeatedly unless it does it immediately
|
||||
final boolean payImmediately = part instanceof CostExile && ((CostExile) part).from == ZoneType.Library;
|
||||
|
||||
// wrap the payment and push onto the cost stack
|
||||
game.costPaymentStack.push(part, this);
|
||||
if ((decisionMaker.paysRightAfterDecision() || payImmediately) && !part.payAsDecided(decisionMaker.getPlayer(), decision, ability, decisionMaker.isEffect())) {
|
||||
if (decisionMaker.paysRightAfterDecision() && !part.payAsDecided(decisionMaker.getPlayer(), decision, ability, decisionMaker.isEffect())) {
|
||||
game.costPaymentStack.pop(); // cost is resolved
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -82,7 +82,6 @@ public class CostPutCardToLib extends CostPartWithList {
|
||||
sb.append(Cost.convertAmountTypeToWords(i, getAmount(), desc));
|
||||
}
|
||||
|
||||
|
||||
if (sameZone) {
|
||||
sb.append(" from the same ").append(from);
|
||||
} else if (!this.payCostFromSource()) {
|
||||
|
||||
@@ -20,7 +20,6 @@ package forge.game.cost;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
import forge.game.staticability.StaticAbilityActivateAbilityAsIfHaste;
|
||||
|
||||
/**
|
||||
* The Class CostTap.
|
||||
@@ -62,7 +61,7 @@ public class CostTap extends CostPart {
|
||||
@Override
|
||||
public final boolean canPay(final SpellAbility ability, final Player payer, final boolean effect) {
|
||||
final Card source = ability.getHostCard();
|
||||
return source.isUntapped() && !isAbilitySick(source);
|
||||
return source.isUntapped() && !source.isAbilitySick();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -74,11 +73,4 @@ public class CostTap extends CostPart {
|
||||
public <T> T accept(ICostVisitor<T> visitor) {
|
||||
return visitor.visit(this);
|
||||
}
|
||||
|
||||
public boolean isAbilitySick(final Card source) {
|
||||
if (!source.isSick()) {
|
||||
return false;
|
||||
}
|
||||
return !StaticAbilityActivateAbilityAsIfHaste.canActivate(source);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,6 @@ package forge.game.cost;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
import forge.game.staticability.StaticAbilityActivateAbilityAsIfHaste;
|
||||
|
||||
/**
|
||||
* The Class CostUntap.
|
||||
@@ -73,7 +72,7 @@ public class CostUntap extends CostPart {
|
||||
@Override
|
||||
public final boolean canPay(final SpellAbility ability, final Player payer, final boolean effect) {
|
||||
final Card source = ability.getHostCard();
|
||||
return source.isTapped() && !isAbilitySick(source);
|
||||
return source.isTapped() && !source.isAbilitySick();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -85,11 +84,4 @@ public class CostUntap extends CostPart {
|
||||
public <T> T accept(ICostVisitor<T> visitor) {
|
||||
return visitor.visit(this);
|
||||
}
|
||||
|
||||
public boolean isAbilitySick(final Card source) {
|
||||
if (!source.isSick()) {
|
||||
return false;
|
||||
}
|
||||
return !StaticAbilityActivateAbilityAsIfHaste.canActivate(source);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,6 +86,7 @@ public enum Keyword {
|
||||
FLASH("Flash", SimpleKeyword.class, true, "You may cast this spell any time you could cast an instant."),
|
||||
FLASHBACK("Flashback", KeywordWithCost.class, false, "You may cast this card from your graveyard for its flashback cost. Then exile it."),
|
||||
FLYING("Flying", SimpleKeyword.class, true, "This creature can't be blocked except by creatures with flying or reach."),
|
||||
FOR_MIRRODIN("For Mirrodin", SimpleKeyword.class, false, "When this Equipment enters the battlefield, create a 2/2 red Rebel creature token, then attach this to it."),
|
||||
FORETELL("Foretell", KeywordWithCost.class, false, "During your turn, you may pay {2} and exile this card from your hand face down. Cast it on a later turn for its foretell cost."),
|
||||
FORTIFY("Fortify", KeywordWithCost.class, false, "%s: Attach to target land you control. Fortify only as a sorcery."),
|
||||
FRENZY("Frenzy", KeywordWithAmount.class, false, "Whenever this creature attacks and isn't blocked, it gets +%d/+0 until end of turn."),
|
||||
|
||||
@@ -286,7 +286,7 @@ public class AbilityManaPart implements java.io.Serializable {
|
||||
return;
|
||||
|
||||
TriggerHandler handler = card.getGame().getTriggerHandler();
|
||||
Trigger trig = TriggerHandler.parseTrigger(sVarHolder.getSVar(this.triggersWhenSpent), sourceCard, false);
|
||||
Trigger trig = TriggerHandler.parseTrigger(sVarHolder.getSVar(this.triggersWhenSpent), sourceCard, false, sVarHolder);
|
||||
handler.registerOneTrigger(trig);
|
||||
}
|
||||
|
||||
|
||||
@@ -17,8 +17,6 @@
|
||||
*/
|
||||
package forge.game.spellability;
|
||||
|
||||
import org.apache.commons.lang3.ObjectUtils;
|
||||
|
||||
import forge.card.CardStateName;
|
||||
import forge.card.mana.ManaCost;
|
||||
import forge.game.card.Card;
|
||||
@@ -26,6 +24,10 @@ import forge.game.card.CardUtil;
|
||||
import forge.game.cost.Cost;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.staticability.StaticAbility;
|
||||
import forge.util.CardTranslation;
|
||||
import forge.util.Localizer;
|
||||
import org.apache.commons.lang3.ObjectUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
public class LandAbility extends Ability {
|
||||
|
||||
@@ -85,10 +87,12 @@ public class LandAbility extends Ability {
|
||||
|
||||
@Override
|
||||
public String toUnsuppressedString() {
|
||||
StringBuilder sb = new StringBuilder("Play land");
|
||||
|
||||
Localizer localizer = Localizer.getInstance();
|
||||
StringBuilder sb = new StringBuilder(StringUtils.capitalize(localizer.getMessage("lblPlayLand")));
|
||||
|
||||
if (getHostCard().isModal()) {
|
||||
sb.append(" (").append(getHostCard().getName(ObjectUtils.firstNonNull(getCardStateName(), CardStateName.Original))).append(")");
|
||||
sb.append(" (").append(CardTranslation.getTranslatedName(getHostCard().getName(ObjectUtils.firstNonNull(getCardStateName(), CardStateName.Original)))).append(")");
|
||||
}
|
||||
|
||||
StaticAbility sta = getMayPlay();
|
||||
|
||||
@@ -127,7 +127,6 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
|
||||
|
||||
private boolean aftermath = false;
|
||||
|
||||
private boolean cumulativeupkeep = false;
|
||||
private boolean blessing = false;
|
||||
private Integer chapter = null;
|
||||
private boolean lastChapter = false;
|
||||
@@ -516,6 +515,10 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
|
||||
return this.hasParam("Ninjutsu");
|
||||
}
|
||||
|
||||
public boolean isCumulativeupkeep() {
|
||||
return hasParam("CumulativeUpkeep");
|
||||
}
|
||||
|
||||
public boolean isEpic() {
|
||||
AbilitySub sub = this.getSubAbility();
|
||||
while (sub != null && !sub.hasParam("Epic")) {
|
||||
@@ -1119,7 +1122,7 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
|
||||
|
||||
clone.setPayCosts(getPayCosts().copy());
|
||||
if (manaPart != null) {
|
||||
clone.manaPart = new AbilityManaPart(this, mapParams);
|
||||
clone.manaPart = new AbilityManaPart(clone, mapParams);
|
||||
}
|
||||
|
||||
// need to copy the damage tables
|
||||
@@ -1353,17 +1356,6 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
|
||||
}
|
||||
}
|
||||
|
||||
if (tr.isSameController()) {
|
||||
Player newController;
|
||||
if (entity instanceof Card) {
|
||||
newController = ((Card) entity).getController();
|
||||
for (final Card c : targetChosen.getTargetCards()) {
|
||||
if (entity != c && !c.getController().equals(newController))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (hasParam("MaxTotalTargetPower") && entity instanceof Card) {
|
||||
int soFar = Aggregates.sum(getTargets().getTargetCards(), CardPredicates.Accessors.fnGetNetPower);
|
||||
// only add if it isn't already targeting
|
||||
@@ -1377,6 +1369,17 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
|
||||
}
|
||||
}
|
||||
|
||||
if (tr.isSameController()) {
|
||||
Player newController;
|
||||
if (entity instanceof Card) {
|
||||
newController = ((Card) entity).getController();
|
||||
for (final Card c : targetChosen.getTargetCards()) {
|
||||
if (entity != c && !c.getController().equals(newController))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (tr.isDifferentControllers()) {
|
||||
Player newController;
|
||||
if (entity instanceof Card) {
|
||||
@@ -2140,13 +2143,6 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
|
||||
return ForgeScript.spellAbilityHasProperty(this, property, sourceController, source, spellAbility);
|
||||
}
|
||||
|
||||
public boolean isCumulativeupkeep() {
|
||||
return cumulativeupkeep;
|
||||
}
|
||||
public void setCumulativeupkeep(boolean cumulativeupkeep0) {
|
||||
cumulativeupkeep = cumulativeupkeep0;
|
||||
}
|
||||
|
||||
// Return whether this spell tracks what color mana is spent to cast it for the sake of the effect
|
||||
public boolean tracksManaSpent() {
|
||||
if (hostCard == null || hostCard.getRules() == null) { return false; }
|
||||
|
||||
@@ -223,11 +223,6 @@ public class StaticAbilityCantAttackBlock {
|
||||
if (!stAb.matchesValidParam("ValidTarget", target)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final Player defender = target instanceof Card ? ((Card) target).getController() : (Player) target;
|
||||
if (!stAb.matchesValidParam("ValidDefender", defender)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -72,7 +72,7 @@ public class StaticAbilityCantGainLosePayLife {
|
||||
}
|
||||
|
||||
if (!stAb.matchesValidParam("ValidCause", cause)) {
|
||||
return false;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (applyCommonAbility(stAb, player)) {
|
||||
|
||||
@@ -17,8 +17,14 @@
|
||||
*/
|
||||
package forge.game.staticability;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import forge.game.Game;
|
||||
import forge.game.GameEntity;
|
||||
import forge.game.ability.ApiType;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.keyword.Keyword;
|
||||
import forge.game.player.Player;
|
||||
@@ -149,15 +155,27 @@ public class StaticAbilityCantTarget {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (spellAbility.hasParam("ValidTgts") &&
|
||||
(stAb.hasParam("SourceCanOnlyTarget")
|
||||
&& (!spellAbility.getParam("ValidTgts").contains(stAb.getParam("SourceCanOnlyTarget"))
|
||||
|| spellAbility.getParam("ValidTgts").contains(","))
|
||||
|| spellAbility.getParam("ValidTgts").contains("non" + stAb.getParam("SourceCanOnlyTarget")
|
||||
)
|
||||
)
|
||||
){
|
||||
return false;
|
||||
if (stAb.hasParam("SourceCanOnlyTarget")) {
|
||||
SpellAbility root = spellAbility.getRootAbility();
|
||||
List<SpellAbility> choices = null;
|
||||
if (root.getApi() == ApiType.Charm) {
|
||||
choices = Lists.newArrayList(root.getAdditionalAbilityList("Choices"));
|
||||
} else {
|
||||
choices = Lists.newArrayList(root);
|
||||
}
|
||||
Iterator<SpellAbility> it = choices.iterator();
|
||||
SpellAbility next = it.next();
|
||||
while (next != null) {
|
||||
if (next.usesTargeting() && (!next.getParam("ValidTgts").contains(stAb.getParam("SourceCanOnlyTarget"))
|
||||
|| next.getParam("ValidTgts").contains(","))
|
||||
|| next.getParam("ValidTgts").contains("non" + stAb.getParam("SourceCanOnlyTarget"))) {
|
||||
return false;
|
||||
}
|
||||
next = next.getSubAbility();
|
||||
if (next == null && it.hasNext()) {
|
||||
next = it.next();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
@@ -249,7 +249,7 @@ public final class StaticAbilityContinuous {
|
||||
}
|
||||
// two variants for Red vs. red in keyword
|
||||
if (input.contains("ColorsYouCtrl") || input.contains("colorsYouCtrl")) {
|
||||
final ColorSet colorsYouCtrl = CardUtil.getColorsYouCtrl(controller);
|
||||
final ColorSet colorsYouCtrl = CardUtil.getColorsFromCards(controller.getCardsIn(ZoneType.Battlefield));
|
||||
|
||||
for (byte color : colorsYouCtrl) {
|
||||
final String colorWord = MagicColor.toLongString(color);
|
||||
|
||||
@@ -341,7 +341,7 @@ public abstract class Trigger extends TriggerReplacementBase {
|
||||
}
|
||||
|
||||
// host controller will be null when adding card in a simulation game
|
||||
if (this.getHostCard().getController() == null || game.getAge() != GameStage.Play || !meetsCommonRequirements(this.mapParams)) {
|
||||
if (this.getHostCard().getController() == null || (game.getAge() != GameStage.Play && game.getAge() != GameStage.RestartedByKarn) || !meetsCommonRequirements(this.mapParams)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -109,6 +109,10 @@ public class TriggerChangesZone extends Trigger {
|
||||
if (leavesLKIZone) {
|
||||
moved = (Card) runParams.get(AbilityKey.CardLKI);
|
||||
}
|
||||
if ("Battlefield".equals(runParams.get(AbilityKey.Destination))) {
|
||||
List<Card> etbLKI = moved.getController().getZone(ZoneType.Battlefield).getCardsAddedThisTurn(null);
|
||||
moved = etbLKI.get(etbLKI.lastIndexOf(moved));
|
||||
}
|
||||
|
||||
if (!matchesValid(moved, getParam("ValidCard").split(","))) {
|
||||
return false;
|
||||
|
||||
@@ -28,9 +28,7 @@ public class GameSimulationTest extends SimulationTest {
|
||||
Game game = initAndCreateGame();
|
||||
Player p = game.getPlayers().get(1);
|
||||
|
||||
addCard("Plains", p);
|
||||
addCard("Plains", p);
|
||||
addCard("Plains", p);
|
||||
addCards("Plains", 3, p);
|
||||
String heraldCardName = "Herald of Anafenza";
|
||||
Card herald = addCard(heraldCardName, p);
|
||||
herald.setSickness(false);
|
||||
@@ -69,9 +67,7 @@ public class GameSimulationTest extends SimulationTest {
|
||||
sliver.setSickness(false);
|
||||
Card herald = addCard(heraldCardName, p);
|
||||
herald.setSickness(false);
|
||||
addCard("Plains", p);
|
||||
addCard("Plains", p);
|
||||
addCard("Plains", p);
|
||||
addCards("Plains", 3, p);
|
||||
addCard("Spear of Heliod", p);
|
||||
|
||||
game.getPhaseHandler().devModeSet(PhaseType.MAIN1, p);
|
||||
@@ -160,8 +156,7 @@ public class GameSimulationTest extends SimulationTest {
|
||||
Game game = initAndCreateGame();
|
||||
Player p = game.getPlayers().get(1);
|
||||
addCard("Black Knight", p);
|
||||
for (int i = 0; i < 5; i++)
|
||||
addCard("Swamp", p);
|
||||
addCards("Swamp", 5, p);
|
||||
|
||||
String merchantCardName = "Gray Merchant of Asphodel";
|
||||
Card c = addCardToZone(merchantCardName, p, ZoneType.Hand);
|
||||
@@ -614,9 +609,7 @@ public class GameSimulationTest extends SimulationTest {
|
||||
Game game = initAndCreateGame();
|
||||
Player p = game.getPlayers().get(1);
|
||||
|
||||
addCard("Forest", p);
|
||||
addCard("Forest", p);
|
||||
addCard("Forest", p);
|
||||
addCards("Forest", 3, p);
|
||||
Card callTheScionsCard = addCardToZone("Call the Scions", p, ZoneType.Hand);
|
||||
|
||||
game.getPhaseHandler().devModeSet(PhaseType.MAIN1, p);
|
||||
@@ -692,9 +685,7 @@ public class GameSimulationTest extends SimulationTest {
|
||||
String broodName = "Brood Monitor";
|
||||
|
||||
// enough to cast Chandra's Ignition
|
||||
for (int i = 0; i < 5; ++i) {
|
||||
addCard("Mountain", p1);
|
||||
}
|
||||
addCards("Mountain", 5, p1);
|
||||
|
||||
Card kalitas = addCard(kalitasName, p1);
|
||||
Card pridemate = addCard(pridemateName, p1);
|
||||
@@ -749,9 +740,7 @@ public class GameSimulationTest extends SimulationTest {
|
||||
String broodName = "Brood Monitor";
|
||||
|
||||
// enough to cast Chandra's Ignition
|
||||
for (int i = 0; i < 5; ++i) {
|
||||
addCard("Mountain", p1);
|
||||
}
|
||||
addCards("Mountain", 5, p1);
|
||||
|
||||
Card kalitas = addCard(kalitasName, p1);
|
||||
addCard(pridemateName, p1);
|
||||
@@ -808,9 +797,7 @@ public class GameSimulationTest extends SimulationTest {
|
||||
String palisadeName = "Palisade Giant";
|
||||
|
||||
// enough to cast Chandra's Ignition
|
||||
for (int i = 0; i < 5; ++i) {
|
||||
addCard("Mountain", p1);
|
||||
}
|
||||
addCards("Mountain", 5, p1);
|
||||
|
||||
Card kalitas = addCard(kalitasName, p1);
|
||||
Card pridemate = addCard(pridemateName, p1);
|
||||
@@ -875,9 +862,7 @@ public class GameSimulationTest extends SimulationTest {
|
||||
String meliraName = "Melira, Sylvok Outcast";
|
||||
|
||||
// enough to cast Cone of Flame
|
||||
for (int i = 0; i < 5; ++i) {
|
||||
addCard("Mountain", p1);
|
||||
}
|
||||
addCards("Mountain", 5, p1);
|
||||
|
||||
addCard(soulfireName, p1);
|
||||
addCard(pridemateName, p1);
|
||||
@@ -890,10 +875,7 @@ public class GameSimulationTest extends SimulationTest {
|
||||
|
||||
coneSA.setTargetCard(bearCard); // one damage to bear
|
||||
coneSA.getSubAbility().setTargetCard(giantCard); // two damage to giant
|
||||
coneSA.getSubAbility().getSubAbility().getTargets().add(p2); // three
|
||||
// damage
|
||||
// to
|
||||
// player
|
||||
coneSA.getSubAbility().getSubAbility().getTargets().add(p2); // three damage to player
|
||||
|
||||
game.getPhaseHandler().devModeSet(PhaseType.MAIN2, p1);
|
||||
game.getAction().checkStateEffects(true);
|
||||
@@ -906,8 +888,7 @@ public class GameSimulationTest extends SimulationTest {
|
||||
Card simGiant = findCardWithName(simGame, giantCardName);
|
||||
Card simPridemate = findCardWithName(simGame, pridemateName);
|
||||
|
||||
// spell deals multiple damages to multiple targets, only one cause of
|
||||
// lifegain
|
||||
// spell deals multiple damages to multiple targets, only one cause of lifegain
|
||||
AssertJUnit.assertNotNull(simPridemate);
|
||||
AssertJUnit.assertTrue(simPridemate.hasCounters());
|
||||
AssertJUnit.assertEquals(1, simPridemate.getCounters(CounterEnumType.P1P1));
|
||||
@@ -1178,9 +1159,7 @@ public class GameSimulationTest extends SimulationTest {
|
||||
String bearName = "Runeclaw Bear";
|
||||
String greetingName = "Alchemist's Greeting";
|
||||
|
||||
for (int i = 0; i < 5; ++i) {
|
||||
addCard("Mountain", p);
|
||||
}
|
||||
addCards("Mountain", 5, p);
|
||||
|
||||
addCard(soulfireName, p);
|
||||
addCard(pridemateName, p);
|
||||
@@ -1243,9 +1222,7 @@ public class GameSimulationTest extends SimulationTest {
|
||||
String elementalName = "Air Elemental";
|
||||
String shockName = "Shock";
|
||||
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
addCard("Mountain", p);
|
||||
}
|
||||
addCards("Mountain", 3, p);
|
||||
|
||||
addCard(soulfireName, p);
|
||||
addCard(pridemateName, p);
|
||||
@@ -1340,9 +1317,7 @@ public class GameSimulationTest extends SimulationTest {
|
||||
addCardToZone("Kalitas, Traitor of Ghet", p, ZoneType.Battlefield);
|
||||
addCardToZone("Anointed Procession", p, ZoneType.Battlefield);
|
||||
addCardToZone("Swamp", p, ZoneType.Battlefield);
|
||||
for (int i = 0; i < 4; i++) {
|
||||
addCardToZone("Mountain", p, ZoneType.Battlefield);
|
||||
}
|
||||
addCards("Mountain", 4, p);
|
||||
|
||||
Card goblin = addCardToZone("Raging Goblin", opp, ZoneType.Battlefield);
|
||||
Card goblin2 = addCardToZone("Raging Goblin", opp, ZoneType.Battlefield);
|
||||
@@ -1599,9 +1574,7 @@ public class GameSimulationTest extends SimulationTest {
|
||||
addCard("Teysa Karlov", p);
|
||||
addCard("Xathrid Necromancer", p);
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
addCardToZone("Plains", p, ZoneType.Battlefield);
|
||||
}
|
||||
addCards("Plains", 4, p);
|
||||
|
||||
Card wrathOfGod = addCardToZone("Wrath of God", p, ZoneType.Hand);
|
||||
game.getPhaseHandler().devModeSet(PhaseType.MAIN2, p);
|
||||
@@ -1780,10 +1753,7 @@ public class GameSimulationTest extends SimulationTest {
|
||||
AssertJUnit.assertFalse(giant.isDoubleFaced());
|
||||
AssertJUnit.assertFalse(giant.canTransform(null));
|
||||
|
||||
addCard("Forest", p);
|
||||
addCard("Forest", p);
|
||||
addCard("Forest", p);
|
||||
addCard("Forest", p);
|
||||
addCards("Forest", 4, p);
|
||||
addCard("Island", p);
|
||||
|
||||
Card cytoCard = addCardToZone("Cytoshape", p, ZoneType.Hand);
|
||||
@@ -1895,12 +1865,8 @@ public class GameSimulationTest extends SimulationTest {
|
||||
Player p = game.getPlayers().get(0);
|
||||
game.getPhaseHandler().devModeSet(PhaseType.MAIN1, p);
|
||||
|
||||
for (int i = 0; i < 7; i++) {
|
||||
addCardToZone("Plains", p, ZoneType.Battlefield);
|
||||
}
|
||||
for (int i = 0; i < 7; i++) {
|
||||
addCardToZone("Island", p, ZoneType.Battlefield);
|
||||
}
|
||||
addCards("Plains", 7, p);
|
||||
addCards("Island", 7, p);
|
||||
|
||||
Card gideon = addCardToZone("Gideon Blackblade", p, ZoneType.Hand);
|
||||
Card sparkDouble = addCardToZone("Spark Double", p, ZoneType.Hand);
|
||||
@@ -1926,15 +1892,9 @@ public class GameSimulationTest extends SimulationTest {
|
||||
Player p = game.getPlayers().get(0);
|
||||
game.getPhaseHandler().devModeSet(PhaseType.MAIN1, p);
|
||||
|
||||
for (int i = 0; i < 7; i++) {
|
||||
addCardToZone("Plains", p, ZoneType.Battlefield);
|
||||
}
|
||||
for (int i = 0; i < 7; i++) {
|
||||
addCardToZone("Island", p, ZoneType.Battlefield);
|
||||
}
|
||||
for (int i = 0; i < 7; i++) {
|
||||
addCardToZone("Forest", p, ZoneType.Battlefield);
|
||||
}
|
||||
addCards("Plains", 7, p);
|
||||
addCards("Island", 7, p);
|
||||
addCards("Forest", 7, p);
|
||||
|
||||
Card tgtLand = addCardToZone("Wastes", p, ZoneType.Battlefield);
|
||||
|
||||
@@ -1967,12 +1927,8 @@ public class GameSimulationTest extends SimulationTest {
|
||||
Player p = game.getPlayers().get(0);
|
||||
game.getPhaseHandler().devModeSet(PhaseType.MAIN1, p);
|
||||
|
||||
for (int i = 0; i < 7; i++) {
|
||||
addCardToZone("Swamp", p, ZoneType.Battlefield);
|
||||
}
|
||||
for (int i = 0; i < 7; i++) {
|
||||
addCardToZone("Forest", p, ZoneType.Battlefield);
|
||||
}
|
||||
addCards("Swamp", 7, p);
|
||||
addCards("Forest", 7, p);
|
||||
|
||||
addCardToZone("Basking Rootwalla", p, ZoneType.Graveyard);
|
||||
Card ooze = addCardToZone("Necrotic Ooze", p, ZoneType.Hand);
|
||||
@@ -1996,9 +1952,7 @@ public class GameSimulationTest extends SimulationTest {
|
||||
Player p = game.getPlayers().get(0);
|
||||
game.getPhaseHandler().devModeSet(PhaseType.MAIN2, p);
|
||||
|
||||
for (int i = 0; i < 7; i++) {
|
||||
addCardToZone("Swamp", p, ZoneType.Battlefield);
|
||||
}
|
||||
addCards("Swamp", 7, p);
|
||||
|
||||
Card epo = addCardToZone("Epochrasite", p, ZoneType.Graveyard);
|
||||
Card animate = addCardToZone("Animate Dead", p, ZoneType.Hand);
|
||||
@@ -2145,9 +2099,7 @@ public class GameSimulationTest extends SimulationTest {
|
||||
Card serraAngel = addCardToZone("Serra Angel", p1, ZoneType.Battlefield);
|
||||
Card actOfTreason = addCardToZone("Act of Treason", p0, ZoneType.Hand);
|
||||
Card pathToExile = addCardToZone("Path to Exile", p0, ZoneType.Hand);
|
||||
for (int i = 0; i < 4; i++) {
|
||||
addCardToZone("Plateau", p0, ZoneType.Battlefield);
|
||||
}
|
||||
addCards("Plateau", 4, p0);
|
||||
addCardToZone("Island", p1, ZoneType.Library);
|
||||
addCardToZone("Forest", p0, ZoneType.Library);
|
||||
|
||||
@@ -2182,8 +2134,7 @@ public class GameSimulationTest extends SimulationTest {
|
||||
Player p = game.getPlayers().get(0);
|
||||
String WCname = "Woodland Champion";
|
||||
addCard(WCname, p);
|
||||
for (int i = 0; i < 5; i++)
|
||||
addCard("Island", p);
|
||||
addCards("Island", 5, p);
|
||||
|
||||
String CardName = "Eternal Skylord";
|
||||
Card c = addCardToZone(CardName, p, ZoneType.Hand);
|
||||
@@ -2214,8 +2165,7 @@ public class GameSimulationTest extends SimulationTest {
|
||||
String waywardServant = "Wayward Servant";
|
||||
String goblin = "Raging Goblin";
|
||||
|
||||
for (int i = 0; i < 8; i++)
|
||||
addCard("Swamp", p);
|
||||
addCards("Swamp", 8, p);
|
||||
|
||||
Card cardEverAfter = addCardToZone(everAfter, p, ZoneType.Hand);
|
||||
Card cardWaywardServant = addCardToZone(waywardServant, p, ZoneType.Graveyard);
|
||||
@@ -2296,9 +2246,7 @@ public class GameSimulationTest extends SimulationTest {
|
||||
String alphaBrawlName = "Alpha Brawl";
|
||||
|
||||
// enough to cast Alpha Brawl
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
addCard("Mountain", p2);
|
||||
}
|
||||
addCards("Mountain", 8, p2);
|
||||
|
||||
Card nishoba = addCard(nishobaName, p1);
|
||||
nishoba.addCounterInternal(CounterEnumType.P1P1, 7, p1, false, null, null);
|
||||
@@ -2366,10 +2314,9 @@ public class GameSimulationTest extends SimulationTest {
|
||||
|
||||
Card glarecaster = addCard(glarecasterName, p);
|
||||
// enough to activate Glarecaster and cast Inferno
|
||||
for (int i = 0; i < 7; ++i) {
|
||||
addCard("Plains", p);
|
||||
addCard("Mountain", p);
|
||||
}
|
||||
addCards("Plains", 7, p);
|
||||
addCards("Mountain", 7, p);
|
||||
|
||||
Card infernoCard = addCardToZone("Inferno", p, ZoneType.Hand);
|
||||
SpellAbility infernoSA = infernoCard.getFirstSpellAbility();
|
||||
|
||||
@@ -2407,9 +2354,7 @@ public class GameSimulationTest extends SimulationTest {
|
||||
addCard(grumName, p);
|
||||
Card mowu = addCardToZone(mowuName, p, ZoneType.Hand);
|
||||
|
||||
for (int i = 0; i < 7; ++i) {
|
||||
addCard("Forest", p);
|
||||
}
|
||||
addCards("Forest", 7, p);
|
||||
SpellAbility mowuSA = mowu.getFirstSpellAbility();
|
||||
|
||||
game.getPhaseHandler().devModeSet(PhaseType.MAIN2, p);
|
||||
@@ -2437,10 +2382,8 @@ public class GameSimulationTest extends SimulationTest {
|
||||
addCard(grumName, p);
|
||||
Card corpsejack = addCardToZone(corpsejackName, p, ZoneType.Hand);
|
||||
|
||||
for (int i = 0; i < 7; ++i) {
|
||||
addCard("Forest", p);
|
||||
addCard("Swamp", p);
|
||||
}
|
||||
addCards("Forest", 7, p);
|
||||
addCards("Swamp", 7, p);
|
||||
SpellAbility corpsejackSA = corpsejack.getFirstSpellAbility();
|
||||
|
||||
game.getPhaseHandler().devModeSet(PhaseType.MAIN2, p);
|
||||
@@ -2473,9 +2416,7 @@ public class GameSimulationTest extends SimulationTest {
|
||||
|
||||
Card everAfter = addCardToZone(everAfterName, p, ZoneType.Hand);
|
||||
|
||||
for (int i = 0; i < 7; ++i) {
|
||||
addCard("Swamp", p);
|
||||
}
|
||||
addCards("Swamp", 7, p);
|
||||
SpellAbility everSA = everAfter.getFirstSpellAbility();
|
||||
everSA.getTargets().add(corpsejack);
|
||||
everSA.getTargets().add(mentor);
|
||||
|
||||
@@ -137,4 +137,10 @@ public class SimulationTest {
|
||||
protected Card addCard(String name, Player p) {
|
||||
return addCardToZone(name, p, ZoneType.Battlefield);
|
||||
}
|
||||
|
||||
protected void addCards(String name, int count, Player p) {
|
||||
for (int i = 0; i < count; i++) {
|
||||
addCard(name, p);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,11 +95,8 @@ public class SpellAbilityPickerSimulationTest extends SimulationTest {
|
||||
Game game = initAndCreateGame();
|
||||
Player p = game.getPlayers().get(1);
|
||||
|
||||
addCard("Island", p);
|
||||
addCard("Island", p);
|
||||
addCard("Forest", p);
|
||||
addCard("Forest", p);
|
||||
addCard("Forest", p);
|
||||
addCards("Island", 2, p);
|
||||
addCards("Forest", 3, p);
|
||||
|
||||
Card tatyova = addCardToZone("Tatyova, Benthic Druid", p, ZoneType.Hand);
|
||||
addCardToZone("Forest", p, ZoneType.Hand);
|
||||
@@ -169,10 +166,7 @@ public class SpellAbilityPickerSimulationTest extends SimulationTest {
|
||||
Game game = initAndCreateGame();
|
||||
Player p = game.getPlayers().get(1);
|
||||
|
||||
addCard("Mountain", p);
|
||||
addCard("Mountain", p);
|
||||
addCard("Mountain", p);
|
||||
addCard("Mountain", p);
|
||||
addCards("Mountain", 4, p);
|
||||
Card spell = addCardToZone("Fiery Confluence", p, ZoneType.Hand);
|
||||
|
||||
Player opponent = game.getPlayers().get(0);
|
||||
@@ -198,10 +192,7 @@ public class SpellAbilityPickerSimulationTest extends SimulationTest {
|
||||
Game game = initAndCreateGame();
|
||||
Player p = game.getPlayers().get(1);
|
||||
|
||||
addCard("Mountain", p);
|
||||
addCard("Mountain", p);
|
||||
addCard("Mountain", p);
|
||||
addCard("Mountain", p);
|
||||
addCards("Mountain", 4, p);
|
||||
Card spell = addCardToZone("Fiery Confluence", p, ZoneType.Hand);
|
||||
|
||||
Player opponent = game.getPlayers().get(0);
|
||||
@@ -226,8 +217,7 @@ public class SpellAbilityPickerSimulationTest extends SimulationTest {
|
||||
Game game = initAndCreateGame();
|
||||
Player p = game.getPlayers().get(1);
|
||||
|
||||
addCard("Mountain", p);
|
||||
addCard("Mountain", p);
|
||||
addCards("Mountain", 2, p);
|
||||
Card spell = addCardToZone("Arc Trail", p, ZoneType.Hand);
|
||||
|
||||
Player opponent = game.getPlayers().get(0);
|
||||
@@ -289,8 +279,7 @@ public class SpellAbilityPickerSimulationTest extends SimulationTest {
|
||||
Game game = initAndCreateGame();
|
||||
Player p = game.getPlayers().get(1);
|
||||
|
||||
addCard("Mountain", p);
|
||||
addCard("Mountain", p);
|
||||
addCards("Mountain", 2, p);
|
||||
Card abbot = addCardToZone("Abbot of Keral Keep", p, ZoneType.Hand);
|
||||
addCardToZone("Lightning Bolt", p, ZoneType.Hand);
|
||||
// Note: This assumes the top of library is revealed. If the AI is made
|
||||
@@ -321,9 +310,7 @@ public class SpellAbilityPickerSimulationTest extends SimulationTest {
|
||||
Game game = initAndCreateGame();
|
||||
Player p = game.getPlayers().get(1);
|
||||
|
||||
addCard("Mountain", p);
|
||||
addCard("Mountain", p);
|
||||
addCard("Mountain", p);
|
||||
addCards("Mountain", 3, p);
|
||||
Card abbot = addCardToZone("Abbot of Keral Keep", p, ZoneType.Hand);
|
||||
// Note: This assumes the top of library is revealed. If the AI is made
|
||||
// smarter to not assume that, then this test can be updated to have
|
||||
@@ -426,8 +413,7 @@ public class SpellAbilityPickerSimulationTest extends SimulationTest {
|
||||
Card blocker = addCard("Fugitive Wizard", opponent);
|
||||
Card attacker1 = addCard("Dwarven Trader", p);
|
||||
attacker1.setSickness(false);
|
||||
addCard("Swamp", p);
|
||||
addCard("Swamp", p);
|
||||
addCards("Swamp", 2, p);
|
||||
addCardToZone("Doom Blade", p, ZoneType.Hand);
|
||||
|
||||
game.getPhaseHandler().devModeSet(PhaseType.MAIN1, p);
|
||||
@@ -455,9 +441,7 @@ public class SpellAbilityPickerSimulationTest extends SimulationTest {
|
||||
Player opponent = game.getPlayers().get(0);
|
||||
|
||||
addCardToZone("Chaos Warp", p, ZoneType.Hand);
|
||||
addCard("Mountain", p);
|
||||
addCard("Mountain", p);
|
||||
addCard("Mountain", p);
|
||||
addCards("Mountain", 3, p);
|
||||
|
||||
addCard("Plains", opponent);
|
||||
addCard("Mountain", opponent);
|
||||
@@ -489,8 +473,7 @@ public class SpellAbilityPickerSimulationTest extends SimulationTest {
|
||||
Game game = initAndCreateGame();
|
||||
Player p = game.getPlayers().get(1);
|
||||
|
||||
addCard("Island", p);
|
||||
addCard("Island", p);
|
||||
addCards("Forest", 2, p);
|
||||
addCardToZone("Counterspell", p, ZoneType.Hand);
|
||||
addCardToZone("Unsummon", p, ZoneType.Hand);
|
||||
|
||||
@@ -605,4 +588,74 @@ public class SpellAbilityPickerSimulationTest extends SimulationTest {
|
||||
// Still, this test case exercises the code path and ensures we don't crash in this case.
|
||||
AssertJUnit.assertEquals(1, picker.getNumSimulations());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void threeDistinctTargetSpell() {
|
||||
Game game = initAndCreateGame();
|
||||
Player p = game.getPlayers().get(1);
|
||||
Player opponent = game.getPlayers().get(0);
|
||||
|
||||
addCardToZone("Incremental Growth", p, ZoneType.Hand);
|
||||
addCards("Forest", 5, p);
|
||||
addCard("Forest Bear", p);
|
||||
addCard("Flying Men", opponent);
|
||||
addCard("Runeclaw Bear", p);
|
||||
addCard("Water Elemental", opponent);
|
||||
addCard("Grizzly Bears", p);
|
||||
|
||||
game.getPhaseHandler().devModeSet(PhaseType.MAIN2, p);
|
||||
game.getAction().checkStateEffects(true);
|
||||
SpellAbilityPicker picker = new SpellAbilityPicker(game, p);
|
||||
SpellAbility sa = picker.chooseSpellAbilityToPlay(null);
|
||||
AssertJUnit.assertNotNull(sa);
|
||||
MultiTargetSelector.Targets targets = picker.getPlan().getSelectedDecision().targets;
|
||||
AssertJUnit.assertEquals(3, targets.size());
|
||||
AssertJUnit.assertTrue(targets.toString().contains("Forest Bear"));
|
||||
AssertJUnit.assertTrue(targets.toString().contains("Runeclaw Bear"));
|
||||
AssertJUnit.assertTrue(targets.toString().contains("Grizzly Bear"));
|
||||
// Expected 5*4*3=60 iterations (5 choices for first target, 4 for next, 3 for last.)
|
||||
AssertJUnit.assertEquals(60, picker.getNumSimulations());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void threeDistinctTargetSpellCantBeCast() {
|
||||
Game game = initAndCreateGame();
|
||||
Player p = game.getPlayers().get(1);
|
||||
Player opponent = game.getPlayers().get(0);
|
||||
|
||||
addCardToZone("Incremental Growth", p, ZoneType.Hand);
|
||||
addCards("Forest", 5, p);
|
||||
addCard("Forest Bear", p);
|
||||
addCard("Flying Men", opponent);
|
||||
|
||||
game.getPhaseHandler().devModeSet(PhaseType.MAIN2, p);
|
||||
game.getAction().checkStateEffects(true);
|
||||
SpellAbilityPicker picker = new SpellAbilityPicker(game, p);
|
||||
SpellAbility sa = picker.chooseSpellAbilityToPlay(null);
|
||||
AssertJUnit.assertNull(sa);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void correctTargetChoicesWithTwoTargetSpell() {
|
||||
Game game = initAndCreateGame();
|
||||
Player p = game.getPlayers().get(1);
|
||||
Player opponent = game.getPlayers().get(0);
|
||||
|
||||
addCardToZone("Rites of Reaping", p, ZoneType.Hand);
|
||||
addCard("Swamp", p);
|
||||
addCards("Forest", 5, p);
|
||||
addCard("Flying Men", opponent);
|
||||
addCard("Forest Bear", p);
|
||||
addCard("Water Elemental", opponent);
|
||||
|
||||
game.getPhaseHandler().devModeSet(PhaseType.MAIN2, p);
|
||||
game.getAction().checkStateEffects(true);
|
||||
SpellAbilityPicker picker = new SpellAbilityPicker(game, p);
|
||||
SpellAbility sa = picker.chooseSpellAbilityToPlay(null);
|
||||
AssertJUnit.assertNotNull(sa);
|
||||
MultiTargetSelector.Targets targets = picker.getPlan().getSelectedDecision().targets;
|
||||
AssertJUnit.assertEquals(2, targets.size());
|
||||
AssertJUnit.assertTrue(targets.toString().contains("Forest Bear"));
|
||||
AssertJUnit.assertTrue(targets.toString().contains("Flying Men"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package forge.adventure.data;
|
||||
|
||||
import forge.adventure.util.CardUtil;
|
||||
import forge.adventure.util.*;
|
||||
import forge.deck.Deck;
|
||||
|
||||
/**
|
||||
@@ -11,7 +11,7 @@ import forge.deck.Deck;
|
||||
public class EnemyData {
|
||||
public String name;
|
||||
public String sprite;
|
||||
public String deck;
|
||||
public String[] deck;
|
||||
public boolean copyPlayerDeck = false;
|
||||
public String ai;
|
||||
public boolean boss = false;
|
||||
@@ -54,6 +54,6 @@ public class EnemyData {
|
||||
}
|
||||
|
||||
public Deck generateDeck(boolean isFantasyMode, boolean useGeneticAI) {
|
||||
return CardUtil.getDeck(deck, true, isFantasyMode, colors, life > 13, life > 16 && useGeneticAI);
|
||||
return CardUtil.getDeck(deck[Current.player().getEnemyDeckNumber(this.name, deck.length)], true, isFantasyMode, colors, life > 13, life > 16 && useGeneticAI);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -693,4 +693,23 @@ public class AdventurePlayer implements Serializable, SaveFileContent {
|
||||
questFlags.clear();
|
||||
}
|
||||
|
||||
public int getEnemyDeckNumber(String enemyName, int maxDecks){
|
||||
int deckNumber = 0;
|
||||
if (statistic.getWinLossRecord().get(enemyName)!=null)
|
||||
{
|
||||
int playerWins = statistic.getWinLossRecord().get(enemyName).getKey();
|
||||
int enemyWins = statistic.getWinLossRecord().get(enemyName).getValue();
|
||||
if (playerWins > enemyWins){
|
||||
int deckNumberAfterAlgorithmOutput = (int)((playerWins-enemyWins) * (difficultyData.enemyLifeFactor / 3));
|
||||
if (deckNumberAfterAlgorithmOutput < maxDecks){
|
||||
deckNumber = deckNumberAfterAlgorithmOutput;
|
||||
}
|
||||
else {
|
||||
deckNumber = maxDecks-1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return deckNumber;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
9
forge-gui/res/adventure/Shandalar/decks/red_bad[1].json
Normal file
9
forge-gui/res/adventure/Shandalar/decks/red_bad[1].json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"name":"Red bad",
|
||||
"template":
|
||||
{
|
||||
"count":70,
|
||||
"colors":["Red"],
|
||||
"rares":0.3
|
||||
}
|
||||
}
|
||||
9
forge-gui/res/adventure/Shandalar/decks/red_bad[2].json
Normal file
9
forge-gui/res/adventure/Shandalar/decks/red_bad[2].json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"name":"Red bad",
|
||||
"template":
|
||||
{
|
||||
"count":60,
|
||||
"colors":["Red"],
|
||||
"rares":0.5
|
||||
}
|
||||
}
|
||||
9
forge-gui/res/adventure/Shandalar/decks/red_bad[3].json
Normal file
9
forge-gui/res/adventure/Shandalar/decks/red_bad[3].json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"name":"Red bad",
|
||||
"template":
|
||||
{
|
||||
"count":60,
|
||||
"colors":["Red"],
|
||||
"rares":0.8
|
||||
}
|
||||
}
|
||||
9
forge-gui/res/adventure/Shandalar/decks/red_bad[4].json
Normal file
9
forge-gui/res/adventure/Shandalar/decks/red_bad[4].json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"name":"Red bad",
|
||||
"template":
|
||||
{
|
||||
"count":50,
|
||||
"colors":["Red"],
|
||||
"rares":1
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -3,7 +3,7 @@ ManaCost:5 R
|
||||
Types:Creature Devil
|
||||
PT:4/2
|
||||
K:Undying
|
||||
T:Mode$ ChangesZone | Origin$ Graveyard | Destination$ Battlefield | TriggerZones$ Battlefield | ValidCard$ Card.Self,Creature.YouOwn+Other | Execute$ ReanimateDmg | TriggerDescription$ Whenever CARDNAME or another creature enters the battlefield from your graveyard, that creature deals damage equal to its power to any target.
|
||||
T:Mode$ ChangesZone | Origin$ Graveyard | Destination$ Battlefield | TriggerZones$ Battlefield | ValidCard$ Card.Self+YouOwn,Creature.YouOwn+Other | Execute$ ReanimateDmg | TriggerDescription$ Whenever CARDNAME or another creature enters the battlefield from your graveyard, that creature deals damage equal to its power to any target.
|
||||
SVar:ReanimateDmg:DB$ DealDamage | ValidTgts$ Creature,Player,Planeswalker | TgtPrompt$ Select any target | DamageSource$ TriggeredCard | NumDmg$ Damage
|
||||
SVar:Damage:TriggeredCard$CardPower
|
||||
Oracle:Undying (When this creature dies, if it had no +1/+1 counters on it, return it to the battlefield under its owner's control with a +1/+1 counter on it.)\nWhenever Flayer of the Hatebound or another creature enters the battlefield from your graveyard, that creature deals damage equal to its power to any target.
|
||||
|
||||
@@ -3,6 +3,6 @@ ManaCost:4 R
|
||||
Types:Creature Orc Warrior
|
||||
PT:5/4
|
||||
K:Haste
|
||||
S:Mode$ CanAttackIfHaste | ValidDefender$ Opponent | Description$ All creatures can attack your opponents and planeswalkers your opponents control as though those creatures had haste.
|
||||
S:Mode$ CanAttackIfHaste | ValidTarget$ Opponent,Planeswalker.OppCtrl | Description$ All creatures can attack your opponents and planeswalkers your opponents control as though those creatures had haste.
|
||||
SVar:PlayMain1:TRUE
|
||||
Oracle:Haste\nAll creatures can attack your opponents and planeswalkers your opponents control as though those creatures had haste.
|
||||
|
||||
@@ -3,7 +3,7 @@ ManaCost:1 B B
|
||||
Types:Legendary Creature Zombie Knight
|
||||
PT:3/3
|
||||
S:Mode$ Continuous | Affected$ Card.Self | MayPlay$ True | AffectedZone$ Graveyard | EffectZone$ Graveyard
|
||||
S:Mode$ CantBeCast | ValidCard$ Card.Self | Origin$ Exile,Hand,Library,Command | EffectZone$ Graveyard,Hand,Library,Command | Description$ You may cast CARDNAME from your graveyard, but not from anywhere else.
|
||||
S:Mode$ CantBeCast | ValidCard$ Card.Self | Origin$ Exile,Hand,Library,Command | EffectZone$ Graveyard,Hand,Library,Command,Stack | Description$ You may cast CARDNAME from your graveyard, but not from anywhere else.
|
||||
S:Mode$ Continuous | Affected$ Knight.YouCtrl | MayPlay$ True | EffectZone$ Battlefield | AffectedZone$ Graveyard | Description$ As long as CARDNAME is on the battlefield, you may play Knight cards from your graveyard.
|
||||
T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | ValidCard$ Card.Self | Execute$ TrigLose | TriggerDescription$ When CARDNAME dies, you lose 2 life.
|
||||
SVar:TrigLose:DB$ LoseLife | Defined$ You | LifeAmount$ 2
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
Name:Land Grant
|
||||
ManaCost:1 G
|
||||
Types:Sorcery
|
||||
S:Mode$ Continuous | CharacteristicDefining$ True | AddKeyword$ Alternative Cost:Reveal<1/Hand> | CheckSVar$ X | SVarCompare$ EQ0 | Description$ If you have no land cards in hand, you may reveal your hand rather than pay Land Grant's mana cost.
|
||||
S:Mode$ Continuous | CharacteristicDefining$ True | AddKeyword$ Alternative Cost:Reveal<1/Hand> | CheckSVar$ X | SVarCompare$ EQ0 | Description$ If you have no land cards in hand, you may reveal your hand rather than pay this spell's mana cost.
|
||||
SVar:X:Count$TypeInYourHand.Land
|
||||
A:SP$ ChangeZone | Cost$ 1 G | Origin$ Library | Destination$ Hand | ChangeType$ Forest | ChangeNum$ 1 | SpellDescription$ Search your library for a Forest card, reveal that card, put it into your hand, then shuffle.
|
||||
Oracle:If you have no land cards in hand, you may reveal your hand rather than pay this spell's mana cost.\nSearch your library for a Forest card, reveal that card, put it into your hand, then shuffle.
|
||||
|
||||
@@ -3,7 +3,7 @@ ManaCost:3
|
||||
Types:Artifact Creature Phyrexian Dragon
|
||||
PT:2/2
|
||||
K:Double Strike
|
||||
T:Mode$ ChangesZone | Origin$ Graveyard | Destination$ Battlefield | OptionalDecider$ You | TriggerZones$ Battlefield | ValidCard$ Card.Self | Execute$ TrigDiscard | TriggerDescription$ When CARDNAME enters the battlefield from your graveyard, you may discard your hand. If you do, draw three cards.
|
||||
T:Mode$ ChangesZone | Origin$ Graveyard | Destination$ Battlefield | OptionalDecider$ You | TriggerZones$ Battlefield | ValidCard$ Card.Self+YouOwn | Execute$ TrigDiscard | TriggerDescription$ When CARDNAME enters the battlefield from your graveyard, you may discard your hand. If you do, draw three cards.
|
||||
SVar:TrigDiscard:DB$ Discard | Mode$ Hand | Defined$ You | SubAbility$ DBDraw
|
||||
SVar:DBDraw:DB$ Draw | Defined$ You | NumCards$ 3
|
||||
K:Unearth:3 R R
|
||||
|
||||
@@ -2,7 +2,7 @@ Name:Thaumatic Compass
|
||||
ManaCost:2
|
||||
Types:Artifact
|
||||
A:AB$ ChangeZone | Cost$ 3 T | Origin$ Library | Destination$ Hand | ChangeType$ Land.Basic | ChangeNum$ 1 | SpellDescription$ Search your library for a basic land card and put that card into your hand, then shuffle.
|
||||
T:Mode$ Phase | Phase$ End of Turn | TriggerZones$ Battlefield | IsPresent$ Land.YouCtrl | PresentCompare$ GE7 | Execute$ DBTransform | TriggerDescription$ At the beginning of your end step, if you control seven or more lands, transform Thaumatic Compass.
|
||||
T:Mode$ Phase | Phase$ End of Turn | ValidPlayer$ You | TriggerZones$ Battlefield | IsPresent$ Land.YouCtrl | PresentCompare$ GE7 | Execute$ DBTransform | TriggerDescription$ At the beginning of your end step, if you control seven or more lands, transform Thaumatic Compass.
|
||||
SVar:DBTransform:DB$ SetState | Defined$ Self | Mode$ Transform
|
||||
AlternateMode:DoubleFaced
|
||||
Oracle:{3}, {T}: Search your library for a basic land card, reveal it, put it into your hand, then shuffle.\nAt the beginning of your end step, if you control seven or more lands, transform Thaumatic Compass.
|
||||
|
||||
@@ -2,7 +2,7 @@ Name:The Lady of Otaria
|
||||
ManaCost:3 R G
|
||||
Types:Legendary Creature Avatar
|
||||
PT:5/5
|
||||
SVar:AltCost:Cost$ tapXType<3/Creature.Dwarf> | Description$ You may tap three untapped Dwarves you control rather than pay this spell's mana cost.
|
||||
SVar:AltCost:Cost$ tapXType<3/Dwarf> | Description$ You may tap three untapped Dwarves you control rather than pay this spell's mana cost.
|
||||
T:Mode$ Phase | Phase$ End of Turn | TriggerZones$ Battlefield | Execute$ TrigDig | CheckSVar$ X | TriggerDescription$ At the beginning of each end step, if a land you controlled was put into your graveyard from the battlefield this turn, reveal the top four cards of your library. Put any number of Dwarf cards from among them into your hand and the rest on the bottom of your library in a random order.
|
||||
SVar:TrigDig:DB$ Dig | DigNum$ 4 | ChangeValid$ Dwarf | DestinationZone$ Hand | RestRandomOrder$ True | AnyNumber$ True
|
||||
SVar:X:Count$ThisTurnEntered_Graveyard_from_Battlefield_Land.YouCtrl+YouOwn
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
Name:Archfiend of the Dross
|
||||
ManaCost:2 B
|
||||
Types:Creature Phyrexian Demon
|
||||
PT:6/6
|
||||
K:Flying
|
||||
K:etbCounter:OIL:4
|
||||
T:Mode$ Phase | Phase$ Upkeep | ValidPlayer$ You | TriggerZones$ Battlefield | Execute$ TrigRemoveCtr | TriggerDescription$ At the beginning of your upkeep, remove an oil counter from CARDNAME. Then if it has no oil counters on it, you lose the game.
|
||||
SVar:TrigRemoveCtr:DB$ RemoveCounter | Defined$ Self | CounterType$ OIL | CounterNum$ 1 | SubAbility$ LoseGame
|
||||
SVar:LoseGame:DB$ LosesGame | Defined$ You | ConditionDefined$ Self | ConditionPresent$ Card.counters_EQ0_OIL
|
||||
T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | ValidCard$ Creature.OppCtrl | TriggerZones$ Battlefield | Execute$ TrigLoseLife | TriggerDescription$ Whenever a creature an opponent controls dies, its controller loses 2 life.
|
||||
SVar:TrigLoseLife:DB$ LoseLife | LifeAmount$ 2 | Defined$ TriggeredCardController
|
||||
DeckHas:Ability$Counters
|
||||
AI:RemoveDeck:Random
|
||||
Oracle:Flying\nArchfiend of the Dross enters the battlefield with four oil counters on it.\nAt the beginning of your upkeep, remove an oil counter from Archfiend of the Dross. Then if it has no oil counters on it, you lose the game.\nWhenever a creature an opponent controls dies, its controller loses 2 life.
|
||||
@@ -0,0 +1,8 @@
|
||||
Name:Black Sun's Twilight
|
||||
ManaCost:X B
|
||||
Types:Instant
|
||||
A:SP$ Pump | ValidTgts$ Creature | TgtPrompt$ Select up to one target creature | TargetMin$ 0 | TargetMax$ 1 | NumAtt$ -X | NumDef$ -X | IsCurse$ True | SubAbility$ DBReanimate | SpellDescription$ Up to one target creature gets -X/-X until end of turn. If X is 5 or more, return a creature card with mana value X or less from your graveyard to the battlefield tapped.
|
||||
SVar:DBReanimate:DB$ ChangeZone | Origin$ Graveyard | Chooser$ You | ChangeNum$ 1 | ConditionCheckSVar$ X | ConditionSVarCompare$ GE5 | Destination$ Battlefield | Hidden$ True | Tapped$ True | ChangeType$ Creature.YouOwn+cmcLEX
|
||||
SVar:X:Count$xPaid
|
||||
DeckHas:Ability$Graveyard
|
||||
Oracle:Up to one target creature gets -X/-X until end of turn. If X is 5 or more, return a creature card with mana value X or less from your graveyard to the battlefield tapped.
|
||||
@@ -0,0 +1,9 @@
|
||||
Name:Blade of Shared Souls
|
||||
ManaCost:2 U
|
||||
Types:Artifact Equipment
|
||||
K:For Mirrodin
|
||||
T:Mode$ Attached | ValidSource$ Card.Self | TriggerZones$ Battlefield | ValidTarget$ Creature | Execute$ TrigCopy | TriggerDescription$ Whenever CARDNAME becomes attached to a creature, for as long as CARDNAME remains attached to it, you may have that creature become a copy of another target creature you control.
|
||||
SVar:TrigCopy:DB$ Clone | ValidTgts$ Creature.YouCtrl+NotTriggeredTarget | TgtPrompt$ Choose another target creature you control | CloneTarget$ TriggeredTargetLKICopy | Duration$ UntilUnattached
|
||||
K:Equip:2
|
||||
DeckHas:Ability$Token & Type$Rebel & Color$Red
|
||||
Oracle:For Mirrodin! (When this Equipment enters the battlefield, create a 2/2 red Rebel creature token, then attach this to it.)\nWhenever Blade of Shared Souls becomes attached to a creature, for as long as Blade of Shared Souls remains attached to it, you may have that creature become a copy of another target creature you control.\nEquip {2}
|
||||
11
forge-gui/res/cardsfolder/upcoming/bloated_contaminator.txt
Normal file
11
forge-gui/res/cardsfolder/upcoming/bloated_contaminator.txt
Normal file
@@ -0,0 +1,11 @@
|
||||
Name:Bloated Contaminator
|
||||
ManaCost:2 G
|
||||
Types:Creature Phyrexian Beast
|
||||
PT:4/4
|
||||
K:Trample
|
||||
K:Toxic:1
|
||||
T:Mode$ DamageDone | ValidSource$ Card.Self | ValidTarget$ Player | CombatDamage$ True | Execute$ TrigProliferate | TriggerDescription$ Whenever CARDNAME deals combat damage to a player, proliferate.
|
||||
SVar:TrigProliferate:DB$ Proliferate
|
||||
DeckHas:Ability$Proliferate
|
||||
DeckHints:Type$Planeswalker & Ability$Counters
|
||||
Oracle:Trample\nToxic 1\nWhenever Bloated Contaminator deals combat damage to a player, proliferate.
|
||||
11
forge-gui/res/cardsfolder/upcoming/conduit_of_worlds.txt
Normal file
11
forge-gui/res/cardsfolder/upcoming/conduit_of_worlds.txt
Normal file
@@ -0,0 +1,11 @@
|
||||
Name:Conduit of Worlds
|
||||
ManaCost:2 G G
|
||||
Types:Artifact
|
||||
S:Mode$ Continuous | Affected$ Land.YouOwn | MayPlay$ True | AffectedZone$ Graveyard | Description$ You may play lands from your graveyard.
|
||||
A:AB$ Play | Cost$ T | ValidSA$ Spell | SorcerySpeed$ True | TgtPrompt$ Choose target nonland permanent card in your graveyard | ConditionCheckSVar$ X | ConditionSVarCompare$ EQ0 | TgtZone$ Graveyard | ValidTgts$ Permanent.nonLand+YouOwn | RememberPlayed$ True | Optional$ True | SubAbility$ DBEffect | SpellDescription$ Choose target nonland permanent card in your graveyard. If you haven't cast a spell this turn, you may cast that card. If you do, you can't cast additional spells this turn. Activate only as a sorcery.
|
||||
SVar:DBEffect:DB$ Effect | Name$ Conduit of Worlds's Effect | ConditionDefined$ Remembered | ConditionPresent$ Card | StaticAbilities$ STCantBeCast | SubAbility$ DBCleanup
|
||||
SVar:STCantBeCast:Mode$ CantBeCast | EffectZone$ Command | ValidCard$ Card | Caster$ You | Description$ You can't cast additional spells this turn.
|
||||
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
|
||||
SVar:X:Count$ThisTurnCast_Spell.YouCtrl
|
||||
DeckHints:Ability$Graveyard|Mill|Dredge
|
||||
Oracle:You may play lands from your graveyard.\n{T}: Choose target nonland permanent card in your graveyard. If you haven't cast a spell this turn, you may cast that card. If you do, you can't cast additional spells this turn. Activate only as a sorcery.
|
||||
8
forge-gui/res/cardsfolder/upcoming/dragonwing_glider.txt
Normal file
8
forge-gui/res/cardsfolder/upcoming/dragonwing_glider.txt
Normal file
@@ -0,0 +1,8 @@
|
||||
Name:Dragonwing Glider
|
||||
ManaCost:3 R R
|
||||
Types:Artifact Equipment
|
||||
K:For Mirrodin
|
||||
S:Mode$ Continuous | Affected$ Creature.EquippedBy | AddPower$ 2 | AddToughness$ 2 | AddKeyword$ Flying & Haste | Description$ Equipped creature gets +2/+2 and has flying and haste.
|
||||
K:Equip:3 R R
|
||||
DeckHas:Ability$Token & Type$Rebel
|
||||
Oracle:For Mirrodin! (When this Equipment enters the battlefield, create a 2/2 red Rebel creature token, then attach this to it.)\nEquipped creature gets +2/+2 and has flying and haste.\nEquip {3}{R}{R}
|
||||
@@ -0,0 +1,5 @@
|
||||
Name:Encroaching Mycosynth
|
||||
ManaCost:3 U
|
||||
Types:Artifact
|
||||
S:Mode$ Continuous | Affected$ Permanent.nonLand+YouCtrl | AffectedZone$ Battlefield,Hand,Graveyard,Exile,Stack,Library,Command | AddType$ Artifact | Description$ Nonland permanents you control are artifacts in addition to their other types. The same is true for permanent spells you control and nonland permanent cards you own that aren't on the battlefield.
|
||||
Oracle:Nonland permanents you control are artifacts in addition to their other types. The same is true for permanent spells you control and nonland permanent cards you own that aren't on the battlefield.
|
||||
12
forge-gui/res/cardsfolder/upcoming/evolved_spinoderm.txt
Normal file
12
forge-gui/res/cardsfolder/upcoming/evolved_spinoderm.txt
Normal file
@@ -0,0 +1,12 @@
|
||||
Name:Evolved Spinoderm
|
||||
ManaCost:2 G G
|
||||
Types:Creature Phyrexian Beast
|
||||
PT:5/5
|
||||
K:etbCounter:OIL:4
|
||||
S:Mode$ Continuous | Affected$ Creature.Self+counters_LE2_OIL | AddKeyword$ Trample | Description$ CARDNAME has trample as long as it has two or fewer oil counters on it. Otherwise, it has hexproof.
|
||||
S:Mode$ Continuous | Affected$ Creature.Self+counters_GT2_OIL | AddKeyword$ Hexproof
|
||||
T:Mode$ Phase | Phase$ Upkeep | ValidPlayer$ You | TriggerZones$ Battlefield | Execute$ TrigRemoveCtr | TriggerDescription$ At the beginning of your upkeep, remove an oil counter from CARDNAME. Then if it has no oil counters on it, sacrifice it.
|
||||
SVar:TrigRemoveCtr:DB$ RemoveCounter | Defined$ Self | CounterType$ OIL | CounterNum$ 1 | SubAbility$ DBSacrifice
|
||||
SVar:DBSacrifice:DB$ Sacrifice | ConditionDefined$ Self | ConditionPresent$ Card.counters_EQ0_OIL
|
||||
DeckHas:Ability$Counters
|
||||
Oracle:Evolved Spinoderm enters the battlefield with four oil counters on it.\nEvolved Spinoderm has trample as long as it has two or fewer oil counters on it. Otherwise, it has hexproof.\nAt the beginning of your upkeep, remove an oil counter from Evolved Spinoderm. Then if it has no oil counters on it, sacrifice it.
|
||||
@@ -0,0 +1,12 @@
|
||||
Name:Geth, Thane of Contracts
|
||||
ManaCost:1 B B
|
||||
Types:Legendary Creature Phyrexian Zombie
|
||||
PT:3/4
|
||||
S:Mode$ Continuous | Affected$ Creature.Other+YouCtrl | AddPower$ -1 | AddToughness$ -1 | Description$ Other creatures you control get -1/-1.
|
||||
A:AB$ ChangeZone | Cost$ 1 B B T | Origin$ Graveyard | SorcerySpeed$ True | Destination$ Battlefield | ValidTgts$ Creature.YouCtrl | TgtPrompt$ Select target creature in your graveyard | SubAbility$ DBAnimate | SpellDescription$ Return target creature card from your graveyard to the battlefield. It gains "If this creature would leave the battlefield, exile it instead of putting it anywhere else." Activate only as a sorcery.
|
||||
SVar:DBAnimate:DB$ Animate | Replacements$ ReplaceLeaves | Defined$ Targeted | Duration$ Permanent
|
||||
SVar:ReplaceLeaves:Event$ Moved | ActiveZones$ Battlefield | Origin$ Battlefield | ValidCard$ Card.Self | ReplaceWith$ Exile | Description$ If this creature would leave the battlefield, exile it instead.
|
||||
SVar:Exile:DB$ ChangeZone | Origin$ Battlefield | Destination$ Exile | Defined$ ReplacedCard
|
||||
DeckHints:Ability$Graveyard|Mill|Dredge|Sacrifice
|
||||
DeckHas:Ability$Graveyard
|
||||
Oracle:Other creatures you control get -1/-1.\n{1}{B}{B}, {T}: Return target creature card from your graveyard to the battlefield. It gains "If this creature would leave the battlefield, exile it instead of putting it anywhere else." Activate only as a sorcery.
|
||||
13
forge-gui/res/cardsfolder/upcoming/glissa_sunslayer.txt
Normal file
13
forge-gui/res/cardsfolder/upcoming/glissa_sunslayer.txt
Normal file
@@ -0,0 +1,13 @@
|
||||
Name:Glissa Sunslayer
|
||||
ManaCost:1 B G
|
||||
Types:Legendary Creature Phyrexian Zombie Elf
|
||||
PT:3/3
|
||||
K:First strike
|
||||
K:Deathtouch
|
||||
T:Mode$ DamageDone | ValidSource$ Card.Self | ValidTarget$ Player | CombatDamage$ True | Execute$ TrigCharm | TriggerZones$ Battlefield | TriggerDescription$ Whenever CARDNAME deals combat damage to a player, ABILITY
|
||||
SVar:TrigCharm:DB$ Charm | Choices$ DBDraw,DBDestroy,DBRemove
|
||||
SVar:DBDraw:DB$ Draw | SubAbility$ DBLoseLife | SpellDescription$ You draw a card and you lose 1 life.
|
||||
SVar:DBLoseLife:DB$ LoseLife | LifeAmount$ 1
|
||||
SVar:DBDestroy:DB$ Destroy | ValidTgts$ Enchantment | SpellDescription$ Destroy target enchantment.
|
||||
SVar:DBRemove:DB$ RemoveCounter | ValidTgts$ Permanent | TgtPrompt$ Select target permanent | CounterType$ Any | CounterNum$ 3 | SpellDescription$ Remove up to three counters from target permanent.
|
||||
Oracle:First strike, deathtouch\nWhenever Glissa Sunslayer deals combat damage to a player, choose one —\n• You draw a card and you lose 1 life.\n• Destroy target enchantment.\n• Remove up to three counters from target permanent.
|
||||
@@ -0,0 +1,8 @@
|
||||
Name:Graaz, Unstoppable Juggernaut
|
||||
ManaCost:8
|
||||
Types:Legendary Artifact Creature Juggernaut
|
||||
PT:7/5
|
||||
S:Mode$ MustAttack | ValidCreature$ Juggernaut.YouCtrl | Description$ Juggernauts you control attack each combat if able.
|
||||
S:Mode$ CantBlockBy | ValidAttacker$ Juggernaut.YouCtrl | ValidBlocker$ Creature.Wall | Description$ Juggernauts you control can't be blocked by Walls.
|
||||
S:Mode$ Continuous | Affected$ Creature.Other+YouCtrl | AffectedZone$ Battlefield | SetPower$ 5 | SetToughness$ 3 | AddType$ Juggernaut | Description$ Other creatures you control have base power and toughness 5/3 and are Juggernauts in addition to their other creature types.
|
||||
Oracle:Juggernauts you control attack each combat if able.\nJuggernauts you control can't be blocked by Walls.\nOther creatures you control have base power and toughness 5/3 and are Juggernauts in addition to their other creature types.
|
||||
@@ -0,0 +1,16 @@
|
||||
Name:Jace, the Perfected Mind
|
||||
ManaCost:2 PU U
|
||||
Types:Legendary Planeswalker Jace
|
||||
Loyalty:5
|
||||
K:Compleated
|
||||
A:AB$ Pump | Cost$ AddCounter<1/LOYALTY> | Planeswalker$ True | NumAtt$ -3 | IsCurse$ True | Duration$ UntilYourNextTurn | TargetMin$ 0 | TargetMax$ 1 | ValidTgts$ Creature | TgtPrompt$ Select up to one target creature | SpellDescription$ Until your next turn, up to one target creature gets -3/-0.
|
||||
A:AB$ Mill | Cost$ SubCounter<2/LOYALTY> | NumCards$ 3 | ValidTgts$ Player | TgtPrompt$ Select target player | SubAbility$ DBDraw | SpellDescription$ Target player mills three cards.
|
||||
SVar:DBDraw:DB$ Draw | NumCards$ Y | SpellDescription$ Then if a graveyard has twenty or more cards in it, you draw three cards. Otherwise, you draw a card.
|
||||
A:AB$ Mill | Cost$ SubCounter<X/LOYALTY> | NumCards$ Z | ValidTgts$ Player | TgtPrompt$ Select target player | SpellDescription$ Target player mills three times X cards.
|
||||
SVar:Y:Count$Compare CheckGrave GE20.3.1
|
||||
SVar:CheckGrave:PlayerCountPlayers$HighestValidGraveyard Card.YouOwn
|
||||
SVar:X:Count$xPaid
|
||||
SVar:Z:SVar$X/Times.3
|
||||
DeckHas:Ability$Mill
|
||||
DeckHints:Ability$Mill
|
||||
Oracle:Compleated ({U/P} can be paid with {U} or 2 life. If life was paid, this planeswalker enters with two fewer loyalty counters.)\n[+1]: Until your next turn, up to one target creature gets -3/-0.\n[−2]: Target player mills three cards. Then if a graveyard has twenty or more cards in it, you draw three cards. Otherwise, you draw a card.\n[−X]: Target player mills three times X cards.
|
||||
@@ -0,0 +1,10 @@
|
||||
Name:Karumonix, the Rat King
|
||||
ManaCost:1 B B
|
||||
Types:Legendary Creature Phyrexian Rat
|
||||
PT:3/3
|
||||
K:Toxic:1
|
||||
S:Mode$ Continuous | Affected$ Rat.YouCtrl+Other | AddKeyword$ Toxic:1 | Description$ Other Rats you control have toxic 1.
|
||||
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigDig | TriggerDescription$ When NICKNAME enters the battlefield, look at the top five cards of your library. You may reveal any number of Rat cards from among them and put the revealed cards into your hand. Put the rest on the bottom of your library in a random order.
|
||||
SVar:TrigDig:DB$ Dig | DigNum$ 5 | AnyNumber$ True | ChangeValid$ Rat | RestRandomOrder$ True
|
||||
DeckNeeds:Type$Rat
|
||||
Oracle:Toxic 1\nOther Rats you control have toxic 1.\nWhen Karumonix enters the battlefield, look at the top five cards of your library. You may reveal any number of Rat cards from among them and put the revealed cards into your hand. Put the rest on the bottom of your library in a random order.
|
||||
11
forge-gui/res/cardsfolder/upcoming/kemba_kha_enduring.txt
Normal file
11
forge-gui/res/cardsfolder/upcoming/kemba_kha_enduring.txt
Normal file
@@ -0,0 +1,11 @@
|
||||
Name:Kemba, Kha Enduring
|
||||
ManaCost:1 W
|
||||
Types:Creature Cat Cleric
|
||||
PT:2/2
|
||||
T:Mode$ ChangesZone | ValidCard$ Card.Self,Creature.Other+Cat+YouCtrl | Origin$ Any | Destination$ Battlefield | TriggerZones$ Battlefield | Execute$ TrigAttach | TriggerDescription$ Whenever CARDNAME or another Cat enters the battlefield under your control, attach up to one target Equipment you control to that creature.
|
||||
SVar:TrigAttach:DB$ Attach | Defined$ TriggeredCard | Object$ Targeted | ValidTgts$ Equipment.YouCtrl | TgtPrompt$ Select up to one target Equipment you control | TargetMin$ 0 | TargetMax$ 1
|
||||
S:Mode$ Continuous | Affected$ Creature.YouCtrl+equipped | AddPower$ 1 | AddToughness$ 1 | Description$ Equipped creatures you control get +1/+1.
|
||||
A:AB$ Token | Cost$ 3 W W | TokenScript$ w_2_2_cat | TokenAmount$ 1 | TokenOwner$ You | SpellDescription$ Create a 2/2 white Cat creature token.
|
||||
DeckNeeds:Type$Equipment
|
||||
DeckHas:Ability$Token
|
||||
Oracle:Whenever Kemba, Kha Enduring or another Cat enters the battlefield under your control, attach up to one target Equipment you control to that creature.\nEquipped creatures you control get +1/+1.\n{3}{W}{W}: Create a 2/2 white Cat creature token.
|
||||
@@ -0,0 +1,11 @@
|
||||
Name:Malcator, Purity Overseer
|
||||
ManaCost:1 W U
|
||||
Types:Creature Phyrexian Elephant Wizard
|
||||
PT:1/1
|
||||
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigToken | TriggerDescription$ TriggerDescription$ When CARDNAME enters the battlefield, create a 3/3 colorless Phyrexian Golem artifact creature token.
|
||||
T:Mode$ Phase | Phase$ End of Turn | ValidPlayer$ You | TriggerZones$ Battlefield | CheckSVar$ X | SVarCompare$ GE3 | Execute$ TrigToken | TriggerDescription$ At the beginning of your end step, if three or more artifacts entered the battlefield under your control this turn, create a 3/3 colorless Phyrexian Golem artifact creature token.
|
||||
SVar:TrigToken:DB$ Token | TokenAmount$ 1 | TokenScript$ c_3_3_a_phyrexian_golem | TokenOwner$ You
|
||||
SVar:X:Count$ThisTurnEntered_Battlefield_Artifact.YouCtrl
|
||||
DeckHas:Ability$Token & Type$Golem|Artifact
|
||||
DeckHints:Type$Artifact
|
||||
Oracle:When Malcator, Purity Overseer enters the battlefield, create a 3/3 colorless Phyrexian Golem artifact creature token.\nAt the beginning of your end step, if three or more artifacts entered the battlefield under your control this turn, create a 3/3 colorless Phyrexian Golem artifact creature token.
|
||||
15
forge-gui/res/cardsfolder/upcoming/mercurial_spelldancer.txt
Normal file
15
forge-gui/res/cardsfolder/upcoming/mercurial_spelldancer.txt
Normal file
@@ -0,0 +1,15 @@
|
||||
Name:Mercurial Spelldancer
|
||||
ManaCost:1 U
|
||||
Types:Creature Phyrexian Rogue
|
||||
PT:2/1
|
||||
S:Mode$ CantBlockBy | ValidAttacker$ Creature.Self | Description$ CARDNAME can't be blocked.
|
||||
T:Mode$ SpellCast | ValidCard$ Card.nonCreature | ValidActivatingPlayer$ You | Execute$ TrigPutCounter | TriggerZones$ Battlefield | TriggerDescription$ Whenever you cast a noncreature spell, put an oil counter on CARDNAME.
|
||||
SVar:TrigPutCounter:DB$ PutCounter | Defined$ Self | CounterType$ OIL | CounterNum$ 1
|
||||
T:Mode$ DamageDone | ValidSource$ Card.Self | ValidTarget$ Player | Execute$ TrigDelayTrig | CombatDamage$ True | TriggerDescription$ Whenever CARDNAME deals combat damage to a player, you may remove two oil counters from it. If you do, when you cast your next instant or sorcery spell this turn, copy that spell. You may choose new targets for the copy.
|
||||
SVar:TrigDelayTrig:AB$ DelayedTrigger| Cost$ SubCounter<2/OIL> | AILogic$ SpellCopy | Execute$ EffTrigCopy | ThisTurn$ True | Mode$ SpellCast | ValidCard$ Instant,Sorcery | ValidActivatingPlayer$ You | SpellDescription$ When you cast your next instant or sorcery spell this turn, copy that spell. You may choose new targets for the copy.
|
||||
SVar:EffTrigCopy:DB$ CopySpellAbility | Defined$ TriggeredSpellAbility | MayChooseTarget$ True
|
||||
SVar:HasAttackEffect:TRUE
|
||||
SVar:BuffedBy:Card.nonCreature
|
||||
DeckHas:Ability$Counters
|
||||
DeckHints:Type$Instant|Sorcery
|
||||
Oracle:Mercurial Spelldancer can't be blocked.\nWhenever you cast a noncreature spell, put an oil counter on Mercurial Spelldancer.\nWhenever Mercurial Spelldancer deals combat damage to a player, you may remove two oil counters from it. If you do, when you cast your next instant or sorcery spell this turn, copy that spell. You may choose new targets for the copy.
|
||||
6
forge-gui/res/cardsfolder/upcoming/mirran_safehouse.txt
Normal file
6
forge-gui/res/cardsfolder/upcoming/mirran_safehouse.txt
Normal file
@@ -0,0 +1,6 @@
|
||||
Name:Mirran Safehouse
|
||||
ManaCost:3
|
||||
Types:Artifact
|
||||
S:Mode$ Continuous | Affected$ Card.Self | EffectZone$ Battlefield | GainsAbilitiesOf$ Land | GainsAbilitiesOfZones$ Graveyard | Description$ As long as CARDNAME is on the battlefield, it has all activated abilities of all land cards in all graveyards.
|
||||
DeckHints:Ability$Graveyard
|
||||
Oracle:As long as Mirran Safehouse is on the battlefield, it has all activated abilities of all land cards in all graveyards.
|
||||
@@ -0,0 +1,15 @@
|
||||
Name:Nahiri, the Unforgiving
|
||||
ManaCost:1 R RW W
|
||||
Types:Legendary Planeswalker Nahiri
|
||||
Loyalty:5
|
||||
K:Compleated
|
||||
A:AB$ Effect | Cost$ AddCounter<1/LOYALTY> | Planeswalker$ True | ValidTgts$ Creature | TargetMin$ 0 | TargetMax$ 1 | RememberObjects$ Targeted | ExileOnMoved$ Battlefield | Duration$ UntilYourNextTurn | StaticAbilities$ MustAttack | SpellDescription$ Until your next turn, up to one target creature attacks a player each combat if able.
|
||||
SVar:MustAttack:Mode$ MustAttack | MustAttack$ Player | ValidCreature$ Card.IsRemembered | Description$ This creature attacks this turn if able.
|
||||
A:AB$ Draw | Cost$ AddCounter<1/LOYALTY> | Planeswalker$ True | SubAbility$ DBDiscard | SpellDescription$ Draw a card, then discard a card.
|
||||
SVar:DBDiscard:DB$ Discard | Defined$ You | NumCards$ 1 | Mode$ TgtChoose
|
||||
A:AB$ ChangeZone | Cost$ AddCounter<0/LOYALTY> | Planeswalker$ True | SubAbility$ DBCopy | Origin$ Graveyard | RememberChanged$ True | Destination$ Exile | TgtPrompt$ Select target creature or equipment in your graveyard | ValidTgts$ Creature.YouCtrl+cmcLTX,Equipment.YouCtrl+cmcLTX | SpellDescription$ Exile target creature or Equipment card with mana value less than NICKNAME's loyalty from your graveyard. Create a token that's a copy of it. That token gains haste. Exile it at the beginning of the next end step.
|
||||
SVar:DBCopy:DB$ CopyPermanent | Defined$ Remembered | PumpKeywords$ Haste | AtEOT$ Exile | SubAbility$ DBCleanup
|
||||
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
|
||||
DeckHas:Ability$Discard|Token
|
||||
SVar:X:Count$CardCounters.LOYALTY
|
||||
Oracle:Compleated ({R/W} can be paid with {R}, {W}, or 2 life. If life was paid, this planeswalker enters with two fewer loyalty counters.)\n[+1]: Until your next turn, up to one target creature attacks a player each combat if able.\n[+1]: Discard a card, then draw a card.\n[+0]:Exile target creature or Equipment card with mana value less than Nahiri's loyalty from your graveyard. Create a token that's a copy of it. That token gains haste. Exile it at the beginning of the next end step.
|
||||
10
forge-gui/res/cardsfolder/upcoming/norns_wellspring.txt
Normal file
10
forge-gui/res/cardsfolder/upcoming/norns_wellspring.txt
Normal file
@@ -0,0 +1,10 @@
|
||||
Name:Norn's Wellspring
|
||||
ManaCost:1 W
|
||||
Types:Artifact
|
||||
T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | ValidCard$ Creature.YouCtrl | TriggerZones$ Battlefield | Execute$ TrigScry | TriggerDescription$ Whenever a creature you control dies, scry 1 and put an oil counter on CARDNAME.
|
||||
SVar:TrigScry:DB$ Scry | ScryNum$ 1 | SubAbility$ DBCounter
|
||||
SVar:DBCounter:DB$ PutCounter | Defined$ Self | CounterType$ OIL | CounterNum$ 1
|
||||
A:AB$ Draw | Cost$ 1 T SubCounter<2/OIL> | SpellDescription$ Draw a card.
|
||||
DeckHas:Ability$Counters
|
||||
DeckHints:Ability$Sacrifice
|
||||
Oracle:Whenever a creature you control dies, scry 1 and put an oil counter on Norn's Wellspring.\n{1}, {T}, Remove two oil counters from Norn's Wellspring: Draw a card.
|
||||
12
forge-gui/res/cardsfolder/upcoming/ovika_enigma_goliath.txt
Normal file
12
forge-gui/res/cardsfolder/upcoming/ovika_enigma_goliath.txt
Normal file
@@ -0,0 +1,12 @@
|
||||
Name:Ovika, Enigma Goliath
|
||||
ManaCost:5 U R
|
||||
Types:Legendary Creature Phyrexian Nightmare
|
||||
PT:6/6
|
||||
K:Flying
|
||||
K:Ward:3 PayLife<3>
|
||||
T:Mode$ SpellCast | ValidCard$ Card.nonCreature | ValidActivatingPlayer$ You | TriggerZones$ Battlefield | Execute$ TrigToken | TriggerDescription$ Whenever you cast a noncreature spell, create X 1/1 red Phyrexian Goblin creature tokens, where X is the mana value of that spell. They gain haste until end of turn.
|
||||
SVar:TrigToken:DB$ Token | TokenAmount$ X | TokenScript$ r_1_1_phyrexian_goblin | TokenOwner$ You | PumpKeywords$ Haste | PumpDuration$ EOT
|
||||
SVar:X:TriggeredStackInstance$CardManaCostLKI
|
||||
SVar:BuffedBy:Card.nonCreature
|
||||
DeckHas:Ability$Token & Type$Goblin
|
||||
Oracle:Flying\nWard — {3}, Pay 3 life.\nWhenever you cast a noncreature spell, create X 1/1 red Phyrexian Goblin creature tokens, where X is the mana value of that spell. They gain haste until end of turn.
|
||||
9
forge-gui/res/cardsfolder/upcoming/red_suns_twilight.txt
Normal file
9
forge-gui/res/cardsfolder/upcoming/red_suns_twilight.txt
Normal file
@@ -0,0 +1,9 @@
|
||||
Name:Red Sun's Twilight
|
||||
ManaCost:X R R
|
||||
Types:Sorcery
|
||||
A:SP$ Destroy | TargetMin$ 0 | TargetMax$ X | ValidTgts$ Artifact | TgtPrompt$ Select X target artifacts | RememberDestroyed$ True | SubAbility$ DBCopy | SpellDescription$ Destroy up to X target artifacts.
|
||||
SVar:DBCopy:DB$ CopyPermanent | Defined$ Remembered | ConditionCheckSVar$ X | ConditionSVarCompare$ GE5 | PumpKeywords$ Haste | SubAbility$ DBCleanup | AtEOT$ Exile | AILogic$ BeforeCombat | SpellDescription$ If X is 5 or more, for each artifact destroyed this way, create a token that's a copy of it. Those tokens gain haste. Exile them at the beginning of the next end step.
|
||||
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
|
||||
SVar:X:Count$xPaid
|
||||
DeckHas:Ability$Token
|
||||
Oracle:Destroy up to X target artifacts. If X is 5 or more, for each artifact destroyed this way, create a token that's a copy of it. Those tokens gain haste. Exile them at the beginning of the next end step.
|
||||
@@ -0,0 +1,13 @@
|
||||
Name:Ria Ivor, Bane of Bladehold
|
||||
ManaCost:2 W B
|
||||
Types:Legendary Creature Phyrexian Knight
|
||||
PT:3/4
|
||||
K:Battle cry
|
||||
T:Mode$ Phase | Phase$ BeginCombat | ValidPlayer$ You | TriggerZones$ Battlefield | Execute$ TrigEffect | TriggerDescription$ At the beginning of combat on your turn, the next time target creature would deal combat damage to one or more players this combat, prevent that damage. If damage is prevented this way, create that many 1/1 colorless Phyrexian Mite artifact creature tokens with toxic 1 and "This creature can't block."
|
||||
SVar:TrigEffect:DB$ Effect | ValidTgts$ Creature | Duration$ UntilEndOfCombat | ReplacementEffects$ StrikeWithAwe | ExileOnMoved$ Battlefield | RememberObjects$ Targeted | AILogic$ Fog
|
||||
SVar:StrikeWithAwe:Event$ DamageDone | IsCombat$ True | ValidTarget$ Player | ValidSource$ Card.IsRemembered | ReplaceWith$ CreateTokensInstead | PreventionEffect$ True | Description$ The next time target creature would deal combat damage to one or more players this combat, prevent that damage. If damage is prevented this way, create that many 1/1 colorless Phyrexian Mite artifact creature tokens with toxic 1 and "This creature can't block."
|
||||
SVar:CreateTokensInstead:DB$ Token | TokenAmount$ X | TokenScript$ c_1_1_a_phyrexian_mite_toxic_noblock | TokenOwner$ You | SubAbility$ ExileEffect
|
||||
SVar:ExileEffect:DB$ ChangeZone | Defined$ Self | Origin$ Command | Destination$ Exile
|
||||
SVar:X:ReplaceCount$DamageAmount
|
||||
DeckHas:Ability$Token & Type$Mite|Artifact
|
||||
Oracle:Battle cry\nAt the beginning of combat on your turn, the next time target creature would deal combat damage to one or more players this combat, prevent that damage. If damage is prevented this way, create that many 1/1 colorless Phyrexian Mite artifact creature tokens with toxic 1 and "This creature can't block."
|
||||
12
forge-gui/res/cardsfolder/upcoming/skrelv_defector_mite.txt
Normal file
12
forge-gui/res/cardsfolder/upcoming/skrelv_defector_mite.txt
Normal file
@@ -0,0 +1,12 @@
|
||||
Name:Skrelv, Defector Mite
|
||||
ManaCost:W
|
||||
Types:Legendary Artifact Creature Phyrexian Mite
|
||||
PT:1/1
|
||||
K:Toxic:1
|
||||
S:Mode$ CantBlockBy | ValidBlocker$ Creature.Self | Description$ CARDNAME can't block.
|
||||
A:AB$ ChooseColor | Cost$ PW T | Defined$ You | AILogic$ MostProminentInHumanDeck | SubAbility$ DBPump | SpellDescription$ Choose a color. Another target creature you control gains toxic 1 and hexproof from that color until end of turn.
|
||||
SVar:DBPump:DB$ Pump | Defined$ Targeted | ValidTgts$ Creature.YouCtrl+Other | TgtPrompt$ Select another target creature you control | KW$ Hexproof:Card.ChosenColor:chosen & Toxic:1 | StackDescription$ {c:Targeted} gains toxic 1 and hexproof from that color until end of turn. | DefinedKW$ ChosenColor | SubAbility$ DBEffect
|
||||
SVar:DBEffect:DB$ Effect | RememberObjects$ Targeted | StaticAbilities$ CantBlockBy | SubAbility$ DBCleanup | SpellDescription$ It can't be blocked by creatures of that color this turn.
|
||||
SVar:DBCleanup:DB$ Cleanup | ClearChosenColor$ True
|
||||
SVar:CantBlockBy:Mode$ CantBlockBy | ValidAttacker$ Creature.IsRemembered | ValidBlocker$ Creature.ChosenColor | Description$ This creature can't be blocked by creatures of the chosen color this turn.
|
||||
Oracle:Toxic 1\nSkrelv, Defector Mite can't block.\n{PW}, {T}: Choose a color. Another target creature you control gains toxic 1 and hexproof from that color until end of turn. It can't be blocked by creatures of that color this turn. ({PW} may be paid for with either {W} or 2 life.)
|
||||
9
forge-gui/res/cardsfolder/upcoming/soulless_jailer.txt
Normal file
9
forge-gui/res/cardsfolder/upcoming/soulless_jailer.txt
Normal file
@@ -0,0 +1,9 @@
|
||||
Name:Soulless Jailer
|
||||
ManaCost:2
|
||||
Types:Artifact Creature Phyrexian Golem
|
||||
PT:0/4
|
||||
R:Event$ Moved | Layer$ CantHappen | ActiveZones$ Battlefield | Origin$ Graveyard | Destination$ Battlefield | ValidLKI$ Permanent | Prevent$ True | Description$ Permanent cards in graveyards can't enter the battlefield.
|
||||
S:Mode$ CantBeCast | Origin$ Graveyard,Exile | Description$ Players can't cast spells from graveyards or exile.
|
||||
SVar:NonStackingEffect:True
|
||||
AI:RemoveDeck:Random
|
||||
Oracle:Permanent cards in graveyards can't enter the battlefield.\nPlayers can't cast noncreature spells from graveyards or exile.
|
||||
@@ -0,0 +1,9 @@
|
||||
Name:Tablet of Compleation
|
||||
ManaCost:2
|
||||
Types:Artifact
|
||||
A:AB$ PutCounter | Cost$ T | CounterType$ OIL | CounterNum$ 1 | SpellDescription$ Put an oil counter on CARDNAME.
|
||||
A:AB$ Mana | Cost$ T | CheckSVar$ X | Produced$ C | SVarCompare$ GE2 | SpellDescription$ Add {C}. Activate only if CARDNAME has two or more oil counters on it.
|
||||
A:AB$ Draw | Cost$ 1 T | CheckSVar$ X | SVarCompare$ GE5 | SpellDescription$ Draw a card. Activate only if CARDNAME has five or more oil counters on it.
|
||||
SVar:X:Count$CardCounters.OIL
|
||||
DeckHas:Ability$Counters
|
||||
Oracle:{T}: Put an oil counter on Tablet of Compleation.\n{T}: Add {C}. Activate only if Tablet of Compleation has two or more oil counters on it.\n{1}, {T}: Draw a card. Activate only if Tablet of Compleation has five or more oil counters on it.
|
||||
@@ -0,0 +1,9 @@
|
||||
Name:The Filigree Sylex
|
||||
ManaCost:2
|
||||
Types:Legendary Artifact
|
||||
A:AB$ PutCounter | Cost$ T | CounterType$ OIL | CounterNum$ 1 | SpellDescription$ Put an oil counter on CARDNAME.
|
||||
A:AB$ DestroyAll | Cost$ T Sac<1/CARDNAME> | ValidCards$ Permanent.nonLand+cmcEQX | SpellDescription$ Destroy each nonland permanent with mana value equal to the number of oil counters on CARDNAME.
|
||||
A:AB$ DealDamage | Cost$ T RemoveAnyCounter<10/OIL/Permanent.YouCtrl/among permanents you control> Sac<1/CARDNAME> | ValidTgts$ Creature,Player,Planeswalker | TgtPrompt$ Select any target | NumDmg$ 10 | SpellDescription$ It deals 10 damage to any target.
|
||||
SVar:X:Count$CardCounters.OIL
|
||||
DeckHas:Ability$Counters|Sacrifice
|
||||
Oracle:{T}: Put an oil counter on The Filigree Sylex.\n{T}, Sacrifice The Filigree Sylex: Destroy each nonland permanent with mana value equal to the number of oil counters on The Filigree Sylex.\n{T}, Remove ten oil counters from among permanents you control and sacrifice The Filigree Sylex: It deals 10 damage to any target.
|
||||
@@ -0,0 +1,9 @@
|
||||
Name:Thrun, Breaker of Silence
|
||||
ManaCost:3 G G
|
||||
Types:Legendary Creature Troll Shaman
|
||||
PT:5/5
|
||||
K:This spell can't be countered.
|
||||
K:Trample
|
||||
S:Mode$ CantTarget | ValidCard$ Card.Self | ValidSource$ Card.nonGreen+OppCtrl | Description$ CARDNAME can't be the target of nongreen spells your opponents control or abilities from nongreen sources your opponents control.
|
||||
S:Mode$ Continuous | Affected$ Card.Self | AddKeyword$ Indestructible | Condition$ PlayerTurn | Description$ As long as it's your turn, NICKNAME has indestructible.
|
||||
Oracle:This spell can't be countered.\nTrample\nThrun, Breaker of Silence can't be the target of nongreen spells your opponents control or abilities from nongreen sources your opponents control.\nAs long as it's your turn, Thrun has indestructible.
|
||||
@@ -0,0 +1,11 @@
|
||||
Name:Tyvar, Jubilant Brawler
|
||||
ManaCost:1 B G
|
||||
Types:Legendary Planeswalker Tyvar
|
||||
Loyalty:3
|
||||
S:Mode$ ActivateAbilityAsIfHaste | ValidCard$ Creature.YouCtrl | Description$ You may activate abilities of creatures you control as though those creatures had haste.
|
||||
A:AB$ Untap | Cost$ AddCounter<1/LOYALTY> | Planeswalker$ True | ValidTgts$ Creature | TargetMin$ 0 | TargetMax$ 1 | SpellDescription$ Untap up to one target creature.
|
||||
A:AB$ Mill | Cost$ SubCounter<1/LOYALTY> | Planeswalker$ True | NumCards$ 3 | Defined$ You | SubAbility$ DBChange | SpellDescription$ Mill three cards, then you may return a creature card with mana value 2 or less from your graveyard to the battlefield.
|
||||
SVar:DBChange:DB$ ChangeZone | Origin$ Graveyard | Destination$ Battlefield | SelectPrompt$ Choose a creature card with mana value 2 or less | Hidden$ True | ChangeType$ Creature.YouOwn+cmcLE2
|
||||
DeckHas:Ability$Graveyard|Mill
|
||||
DeckHints:Ability$Graveyard
|
||||
Oracle:You may activate abilities of creatures you control as though those creatures had haste.\n[+1]: Untap up to one target creature.\n[−2]: Mill three cards, then you may return a creature card with mana value 2 or less from your graveyard to the battlefield.
|
||||
13
forge-gui/res/cardsfolder/upcoming/unctus_grand_metatect.txt
Normal file
13
forge-gui/res/cardsfolder/upcoming/unctus_grand_metatect.txt
Normal file
@@ -0,0 +1,13 @@
|
||||
Name:Unctus, Grand Metatect
|
||||
ManaCost:1 U U
|
||||
Types:Legendary Artifact Creature Phyrexian Vedalken
|
||||
PT:2/4
|
||||
S:Mode$ Continuous | Affected$ Creature.Other+Blue+YouCtrl | AddTrigger$ LootTrig | Description$ Other blue creatures you control have "Whenever this creature becomes tapped, draw a card, then discard a card."
|
||||
SVar:LootTrig:Mode$ Taps | ValidCard$ Card.Self | Execute$ TrigDraw | TriggerDescription$ Whenever this creature becomes tapped, draw a card, then discard a card.
|
||||
SVar:TrigDraw:DB$ Draw | SubAbility$ DBDiscard
|
||||
SVar:DBDiscard:DB$ Discard | Mode$ TgtChoose
|
||||
S:Mode$ Continuous | Affected$ Creature.Artifact+Other+YouCtrl | AddPower$ 1 | AddToughness$ 1 | Description$ Other artifact creatures you control get +1/+1.
|
||||
A:AB$ Animate | Cost$ PU | ValidTgts$ Creature.YouCtrl | Colors$ Blue | Types$ Artifact | SorcerySpeed$ True | SpellDescription$ Until end of turn, target creature you control becomes a blue artifact in addition to its other colors and types. Activate only as a sorcery. ({PU} can be paid with either {U} or 2 life.)
|
||||
DeckHints:Type$Artifact
|
||||
DeckHas:Ability$Discard
|
||||
Oracle:Other blue creatures you control have "Whenever this creature becomes tapped, draw a card, then discard a card."\nOther artifact creatures you control get + 1/+1.\n{P/U}: Until end of turn, target creature you control becomes a blue artifact in addition to its other colors and types. Activate only as a sorcery. ({U/P} can be paid with either {U} or 2 life.)
|
||||
9
forge-gui/res/cardsfolder/upcoming/urabrasks_forge.txt
Normal file
9
forge-gui/res/cardsfolder/upcoming/urabrasks_forge.txt
Normal file
@@ -0,0 +1,9 @@
|
||||
Name:Urabrask's Forge
|
||||
ManaCost:2 R
|
||||
Types:Artifact
|
||||
T:Mode$ Phase | Phase$ Upkeep | ValidPlayer$ You | TriggerZones$ Battlefield | Execute$ TrigPutCounter | TriggerDescription$ At the beginning of combat on your turn, put an oil counter on CARDNAME, then create an X/1 red Phyrexian Horror creature token with trample and haste, where X is the number of oil counters on Urabrask's Forge. Sacrifice that token at the beginning of the next end step.
|
||||
SVar:TrigPutCounter:DB$ PutCounter | Defined$ Self | CounterType$ OIL | CounterNum$ 1 | SubAbility$ DBToken
|
||||
SVar:DBToken:DB$ Token | TokenAmount$ 1 | TokenScript$ r_x_1_phyrexian_horror_trample_haste | TokenOwner$ You | AtEOT$ Sacrifice | TokenPower$ X
|
||||
SVar:X:Count$CardCounters.OIL
|
||||
DeckHas:Ability$Token|Counters|Sacrifice & Type$Phyrexian|Horror
|
||||
Oracle:At the beginning of combat on your turn, put an oil counter on Urabrask's Forge, then create an X/1 red Phyrexian Horror creature token with trample and haste, where X is the number of oil counters on Urabrask's Forge. Sacrifice that token at the beginning of the next end step.
|
||||
10
forge-gui/res/cardsfolder/upcoming/venerated_rotpriest.txt
Normal file
10
forge-gui/res/cardsfolder/upcoming/venerated_rotpriest.txt
Normal file
@@ -0,0 +1,10 @@
|
||||
Name:Venerated Rotpriest
|
||||
ManaCost:G
|
||||
Types:Creature Phyrexian Druid
|
||||
PT:1/2
|
||||
K:Toxic:1
|
||||
T:Mode$ BecomesTarget | ValidTarget$ Creature.YouCtrl | ValidSource$ Spell | TriggerZones$ Battlefield | Execute$ TrigPoison | TriggerDescription$ Whenever a creature you control becomes the target of a spell, target opponent gets a poison counter.
|
||||
SVar:TrigPoison:DB$ Poison | ValidTgts$ Opponent | TgtPrompt$ Select target opponent | Num$ 1
|
||||
DeckHas:Ability$Counters
|
||||
DeckHints:Type$Aura
|
||||
Oracle:Toxic 1\nWhenever a creature you control becomes the target of a spell, target opponent gets a poison counter.
|
||||
@@ -0,0 +1,12 @@
|
||||
Name:Vindictive Flamestoker
|
||||
ManaCost:R
|
||||
Types:Creature Phyrexian Wizard
|
||||
PT:1/2
|
||||
T:Mode$ SpellCast | ValidCard$ Card.nonCreature | ValidActivatingPlayer$ You | Execute$ TrigPutCounter | TriggerZones$ Battlefield | TriggerDescription$ Whenever you cast a noncreature spell, put an oil counter on CARDNAME.
|
||||
SVar:TrigPutCounter:DB$ PutCounter | Defined$ Self | CounterType$ OIL | CounterNum$ 1
|
||||
A:AB$ Discard | Cost$ 6 R Sac<1/CARDNAME> | Defined$ You | Mode$ Hand | ReduceCost$ X | SubAbility$ DBDraw | SpellDescription$ Discard your hand, then draw four cards. This ability costs {1} less to activate for each oil counter on CARDNAME.
|
||||
SVar:DBDraw:DB$ Draw | Defined$ You | NumCards$ 4
|
||||
SVar:X:Count$CardCounters.OIL
|
||||
DeckHints:Type$Instant|Sorcery
|
||||
DeckHas:Ability$Counters|Sacrifice|Discard
|
||||
Oracle:Whenever you cast a noncreature spell, put an oil counter on Vindictive Flamestoker.\n{6}{R}, Sacrifice Vindictive Flamestoker: Discard your hand, then draw four cards. This ability costs {1} less to activate for each oil counter on Vindictive Flamestoker.
|
||||
@@ -0,0 +1,15 @@
|
||||
Name:Vraska, Betrayal's Sting
|
||||
ManaCost:4 B PB
|
||||
Types:Legendary Planeswalker Vraska
|
||||
Loyalty:6
|
||||
K:Compleated
|
||||
A:AB$ Draw | Cost$ AddCounter<0/LOYALTY> | Planeswalker$ True | SubAbility$ DBLoseLife | SpellDescription$ You draw a card and you lose 1 life. Proliferate.
|
||||
SVar:DBLoseLife:DB$ LoseLife | LifeAmount$ 1 | SubAbility$ DBProliferate
|
||||
SVar:DBProliferate:DB$ Proliferate
|
||||
A:AB$ Animate | Cost$ SubCounter<2/LOYALTY> | Planeswalker$ True | ValidTgts$ Creature | TgtPrompt$ Select target creature | RemoveAllAbilities$ True | Colors$ Green | Types$ Artifact,Treasure | Abilities$ TreasureSac | RemoveCardTypes$ True | Duration$ Permanent | SpellDescription$ Target creature becomes a Treasure artifact with "{T}, Sacrifice this artifact: Add one mana of any color" and loses all other card types and abilities.
|
||||
SVar:TreasureSac:AB$ Mana | Cost$ T Sac<1/CARDNAME> | Produced$ Any | SpellDescription$ Add one mana of any color.
|
||||
A:AB$ Poison | Cost$ SubCounter<9/LOYALTY> | ValidTgts$ Player | TgtPrompt$ Select target player | ConditionCheckSVar$ X | ConditionSVarCompare$ LT9 | Num$ Difference | SpellDescription$ If target player has fewer than nine poison counters, they get a number of poison counters equal to the difference.
|
||||
SVar:X:TargetedPlayer$PoisonCounters
|
||||
SVar:Difference:Number$9/Minus.X
|
||||
DeckHints:Ability$Counters & Keyword$Infect|Toxic|Poisonous
|
||||
Oracle:Compleated ({PB} can be paid with {B} or 2 life. If life was paid, this planeswalker enters with two fewer loyalty counters.)\n[0]: You draw a card and you lose 1 life. Proliferate.\n[−2]: Target creature becomes a Treasure artifact with "{T}, Sacrifice this artifact: Add one mana of any color" and loses all other card types and abilities.\n[−9]: If target player has fewer than nine poison counters, they get a number of poison counters equal to the difference.
|
||||
10
forge-gui/res/cardsfolder/upcoming/white_suns_twilight.txt
Normal file
10
forge-gui/res/cardsfolder/upcoming/white_suns_twilight.txt
Normal file
@@ -0,0 +1,10 @@
|
||||
Name:White Sun's Twilight
|
||||
ManaCost:X W W
|
||||
Types:Sorcery
|
||||
A:SP$ GainLife | LifeAmount$ X | SubAbility$ DBToken | SpellDescription$ You gain X life.
|
||||
SVar:DBToken:DB$ Token | RememberTokens$ True | TokenAmount$ X | TokenScript$ c_1_1_a_phyrexian_mite_toxic_noblock | TokenOwner$ You | SubAbility$ DBWrath | SpellDescription$ Create X 1/1 colorless Phyrexian Mite artifact creature tokens with toxic 1 and "This creature can't block."
|
||||
SVar:DBWrath:DB$ DestroyAll | ValidCards$ Creature.IsNotRemembered | ConditionCheckSVar$ X | ConditionSVarCompare$ GE5 | SubAbility$ DBCleanup | SpellDescription$ If X is 5 or more, Destroy all other creatures.
|
||||
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
|
||||
SVar:X:Count$xPaid
|
||||
DeckHas:Ability$Token|LifeGain & Type$Phyrexian|Mite|Artifact
|
||||
Oracle:You gain X life. Create X 1/1 colorless Phyrexian Mite artifact creature tokens with toxic 1 and "This creature can't block." If X is 5 or more, destroy all other creatures. (Players dealt combat damage by a creature with toxic 1 also get a poison counter.)
|
||||
@@ -289,6 +289,7 @@ ScryfallCode=IKO
|
||||
|
||||
[buy a box]
|
||||
275 M Zilortha, Strength Incarnate @Antonio José Manzanedo
|
||||
275a M Zilortha, Strength Incarnate @Chase Stone
|
||||
|
||||
[borderless]
|
||||
276 M Lukka, Coppercoat Outcast @Kieran Yanner
|
||||
|
||||
@@ -6,12 +6,80 @@ Type=Expansion
|
||||
ScryfallCode=ONE
|
||||
|
||||
[cards]
|
||||
2 U Annex Sentry @David Astruga
|
||||
10 M Elesh Norn, Mother of Machines @Martina Fackova
|
||||
11 R The Eternal Wanderer @Alix Branwyn
|
||||
19 R Kemba, Kha Enduring @Zoltan Boros
|
||||
23 M Mondrak, Glory Dominus @Jason A. Engle
|
||||
24 R Norn's Wellspring @Jonas De Ro
|
||||
26 U Ossification @Nino Vecia
|
||||
31 U Resistance Reunited @Aurore Folny
|
||||
33 R Skrelv, Defector Mite @Brian Valeza
|
||||
34 R Skrelv's Hive @Heonhwa Choe
|
||||
38 R White Sun's Twilight @Julian Kok Joon Wen
|
||||
42 R Blade of Shared Souls @Volkan Baǵa
|
||||
43 R Blue Sun's Twilight @Piotr Dura
|
||||
47 R Encroaching Mycosynth @Martin de Diego Sádaba
|
||||
49 C Experimental Augury @Donato Giancola
|
||||
57 M Jace, the Perfected Mind @Chase Stone
|
||||
61 R Mercurial Spelldancer @Marcela Bolívar
|
||||
63 R Mindsplice Apparatus @Ovidio Cartagena
|
||||
75 R Unctus, Grand Metatect @Andrew Mar
|
||||
82 R Archfiend of the Dross @Lie Setiawan
|
||||
84 R Black Sun's Twilight @Jonas De Ro
|
||||
95 R Geth, Thane of Contracts @Martin de Diego Sádaba
|
||||
98 R Karumonix, the Rat King @Jason A. Engle
|
||||
104 R Phyrexian Arena @Martina Fackova
|
||||
105 M Phyrexian Obliterator @Maxim Kostin
|
||||
108 U Sheoldred's Edict @Helge C. Balzer
|
||||
115 M Vraska, Betrayal's Sting @Chase Stone
|
||||
126 R Dragonwing Glider @Andreas Zafiratos
|
||||
138 R Koth, Fire of Resistance @Eric Wilkerson
|
||||
145 R Red Sun's Twilight @Julian Kok Joon Wen
|
||||
149 R Slobad, Iron Goblin @Chris Seaman
|
||||
153 R Urabrask's Forge @Lie Setiawan
|
||||
154 R Vindictive Flamestoker @Xavier Ribeiro
|
||||
159 R Bloated Contaminator @Johann Bodin
|
||||
163 R Conduit of Worlds @Jokubas Uogintas
|
||||
166 R Evolved Spinoderm @Svetlin Velinov
|
||||
169 R Green Sun's Twilight @Yeong-Hao Han
|
||||
175 M Nissa, Ascended Animist @Chase Stone
|
||||
178 U Paladin of Predation @Lorenzo Mastroianni
|
||||
186 R Thrun, Breaker of Silence @Simon Dominic
|
||||
192 R Venerated Rotpriest @Brian Valeza
|
||||
197 U Bladehold War-Whip @Tony Foti
|
||||
201 R Ezuri, Stalker of Spheres @Fariba Khamseh
|
||||
202 R Glissa Sunslayer @Gaboleps
|
||||
203 R Jor Kadeen, First Goldwarden @Jeremy Wilson
|
||||
204 R Kaito, Dancing Shadow @Daarken
|
||||
205 R Kaya, Intangible Slayer @Marta Nael
|
||||
206 R Kethek, Crucible Goliath @Zoltan Boros
|
||||
207 M Lukka, Bound to Ruin @Chase Stone
|
||||
209 R Melira, the Living Cure @Miranda Meeks
|
||||
210 R Migloz, Maze Crusher @Zezhou Chen
|
||||
211 R Nahiri, the Unforgiving @Chase Stone
|
||||
213 R Ovika, Enigma Goliath @Antonio José Manzanedo
|
||||
214 R Ria Ivor, Bane of Bladehold @Andreas Zafiratos
|
||||
216 U Slaughter Singer @Adam Burn
|
||||
218 R Tyvar, Jubilant Brawler @Victor Adame Minguez
|
||||
219 R Venser, Corpse Puppet @Igor Kieryluk
|
||||
222 R Argentum Masticore @Zack Stella
|
||||
225 R Graaz, Unstoppable Juggernaut @Nestor Ossandon Leal
|
||||
227 R The Filigree Sylex @
|
||||
233 R Monument to Perfection @Igor Kieryluk
|
||||
234 U Myr Convert @José Parodi
|
||||
241 R Soulless Jailer @Donato Giancola
|
||||
242 M Staff of Compleation @Igor Krstic
|
||||
245 R Tablet of Compleation @Martin de Diego Sádaba
|
||||
246 R Zenith Chronicler @Johann Bodin
|
||||
248 R Blackcleave Cliffs @Yeong-Hao Han
|
||||
249 R Copperline Gorge @Yeong-Hao Han
|
||||
254 R Mirrex @
|
||||
255 R The Monumental Facade @Bruce Brenneise
|
||||
256 R The Mycosynth Gardens @Andrew Mar
|
||||
257 R Razorverge Thicket @Randy Gallegos
|
||||
258 R Seachrome Coast @Mauricio Calle
|
||||
259 R The Seedcore @Kasia 'Kafis' Zielińska
|
||||
262 L Plains @Alayna Danner
|
||||
263 L Island @Alayna Danner
|
||||
264 L Swamp @Alayna Danner
|
||||
@@ -23,22 +91,91 @@ ScryfallCode=ONE
|
||||
270 L Mountain @Mark Riddick
|
||||
271 L Forest @Mark Riddick
|
||||
272 L Plains @Sergey Glushakov
|
||||
273 L Island @David Alvarez
|
||||
273 L Island @David Álvarez
|
||||
274 L Swamp @Julian Kok Joon Wen
|
||||
275 L Mountain @Muhammad Firdaus
|
||||
276 L Forest @Nadia Hurianova
|
||||
277 U Ossification @Nino Vecia
|
||||
278 C Experimental Augury @Donato Giancola
|
||||
279 U Sheoldred's Edict @Helge C. Balzer
|
||||
280 U Bladehold War-Whip @Tony Foti
|
||||
281 U Slaughter Singer @Adam Burn
|
||||
283 R Phyrexian Arena @Martina Fackova
|
||||
284 R Green Sun's Twilight @Piotr Dura
|
||||
292 R Karumonix, the Rat King @Jason A. Engle
|
||||
297 U Myr Convert @JungShan
|
||||
298 M Elesh Norn, Mother of Machines @Dominik Mayer
|
||||
299 M Mondrak, Glory Dominus @Flavio Girón
|
||||
301 R Skrelv, Defector Mite @Sidharth Chaturvedi
|
||||
304 R Archfiend of the Dross @Sidharth Chaturvedi
|
||||
306 R Geth, Thane of Contracts @Flavio Girón
|
||||
307 R Karumonix, the Rat King @Flavio Girón
|
||||
308 M Phyrexian Obliterator @Yu-ki Nishimoto
|
||||
311 R Slobad, Iron Goblin @Dominik Mayer
|
||||
317 R Ezuri, Stalker of Spheres @JungShan
|
||||
318 R Glissa Sunslayer @Ravenna Tran
|
||||
322 R Ovika, Enigma Goliath @Dominik Mayer
|
||||
324 R Venser, Corpse Puppet @Dominik Mayer
|
||||
325 M Jace, the Perfected Mind @Chase Stone
|
||||
330 R Kemba, Kha Enduring @Izumi Tomoki
|
||||
332 R Jor Kadeen, First Goldwarden @Sansyu
|
||||
333 R Melira, the Living Cure @Miyuki Aramaki
|
||||
334 R Graaz, Unstoppable Juggernaut @Kutat
|
||||
335 R The Eternal Wanderer @Kento Matsuura
|
||||
336 M Jace, the Perfected Mind @Showichi Furumi
|
||||
337 M Vraska, Betrayal's Sting @Sansyu
|
||||
338 R Koth, Fire of Resistance @Ai Nanahira
|
||||
339 M Nissa, Ascended Animist @Gou Tanabe
|
||||
340 R Kaito, Dancing Shadow @Kento Matsuura
|
||||
341 R Kaya, Intangible Slayer @Showichi Furumi
|
||||
342 M Lukka, Bound to Ruin @Yuki Fujisawa
|
||||
343 R Nahiri, the Unforgiving @Hiro Usuda
|
||||
344 R Tyvar, Jubilant Brawler @Iinuma Yuuki
|
||||
345 M Elesh Norn, Mother of Machines @Pedro Potier
|
||||
346 M Mondrak, Glory Dominus @rishxxv
|
||||
351 M Phyrexian Obliterator @Pedro Potier
|
||||
358 M Staff of Compleation @Joshua Alvarado
|
||||
360 M Jace, the Perfected Mind @Joshua Alvarado
|
||||
361 M Vraska, Betrayal's Sting @Scott M. Fischer
|
||||
362 M Nissa, Ascended Animist @Cabrol
|
||||
363 M Lukka, Bound to Ruin @DZO
|
||||
364 R Nahiri, the Unforgiving @Scott M. Fischer
|
||||
365 L Plains @Dan Mumford
|
||||
366 L Island @Evan Cagle
|
||||
367 L Swamp @Daria Khlebnikova
|
||||
368 L Mountain @Indra Nugroho
|
||||
369 L Forest @Alayna Danner
|
||||
370 R Blackcleave Cliffs @Daniel Ljunggren
|
||||
371 R Copperline Gorge @Sergey Glushakov
|
||||
372 M Darkslick Shores @Daria Khlebnikova
|
||||
373 R Razorverge Thicket @Jokubas Uogintas
|
||||
374 R Seachrome Coast @Jokubas Uogintas
|
||||
375 R Norn's Wellspring @Jonas De Ro
|
||||
376 R Skrelv's Hive @Heonhwa Choe
|
||||
377 R White Sun's Twilight @Julian Kok Joon Wen
|
||||
378 R Blade of Shared Souls @Volkan Baǵa
|
||||
379 R Blue Sun's Twilight @Piotr Dura
|
||||
380 R Encroaching Mycosynth @Martin de Diego Sádaba
|
||||
381 R Mercurial Spelldancer @Marcela Bolívar
|
||||
383 R Black Sun's Twilight @Jonas De Ro
|
||||
387 R Urabrask's Forge @Lie Setiawan
|
||||
388 R Vindictive Flamestoker @Xavier Ribeiro
|
||||
389 R Bloated Contaminator @Johann Bodin
|
||||
390 R Conduit of Worlds @Jokubas Uogintas
|
||||
391 R Green Sun's Twilight @Yeong-Hao Han
|
||||
392 R Venerated Rotpriest @Brian Valeza
|
||||
401 R The Monumental Facade @Bruce Brenneise
|
||||
403 R The Seedcore @Kasia 'Kafis' Zielińska
|
||||
404 R Mite Overseer @Nestor Ossandon Leal
|
||||
405 R Serum Sovereign @Chris Rallis
|
||||
406 R Kinzu of the Bleak Coven @Andreas Zafiratos
|
||||
407 R Rhuk, Hexgold Nabber @Andrea De Dominicis
|
||||
408 R Goliath Hatchery @Simon Dominic
|
||||
409 R Mite Overseer @Nestor Ossandon Leal
|
||||
410 R Serum Sovereign @Chris Rallis
|
||||
411 R Kinzu of the Bleak Coven @Andreas Zafiratos
|
||||
412 R Rhuk, Hexgold Nabber @Andrea De Dominicis
|
||||
413 R Goliath Hatchery @Simon Dominic
|
||||
414 M Elesh Norn, Mother of Machines @Martina Fackova
|
||||
415 M Elesh Norn, Mother of Machines @Junji Ito
|
||||
416 M Elesh Norn, Mother of Machines @Richard Whitters
|
||||
@@ -46,7 +183,26 @@ ScryfallCode=ONE
|
||||
419 M Elesh Norn, Mother of Machines @Junji Ito
|
||||
420 M Elesh Norn, Mother of Machines @Dominik Mayer
|
||||
421 M Elesh Norn, Mother of Machines @Richard Whitters
|
||||
422 R The Eternal Wanderer @Kento Matsuura
|
||||
423 R Kemba, Kha Enduring @Izumi Tomoki
|
||||
424 M Mondrak, Glory Dominus @Flavio Girón
|
||||
427 R Skrelv, Defector Mite @Sidharth Chaturvedi
|
||||
428 M Jace, the Perfected Mind @Showichi Furumi
|
||||
434 R Archfiend of the Dross @Sidharth Chaturvedi
|
||||
438 R Geth, Thane of Contracts @Flavio Girón
|
||||
439 R Karumonix, the Rat King @Flavio Girón
|
||||
440 M Phyrexian Obliterator @Yu-ki Nishimoto
|
||||
442 M Vraska, Betrayal's Sting @Sansyu
|
||||
446 R Koth, Fire of Resistance @Ai Nanahira
|
||||
448 R Slobad, Iron Goblin @Dominik Mayer
|
||||
454 M Nissa, Ascended Animist @Gou Tanabe
|
||||
461 R Glissa Sunslayer @Ravenna Tran
|
||||
462 R Jor Kadeen, First Goldwarden @Sansyu
|
||||
463 R Kaito, Dancing Shadow @Kento Matsuura
|
||||
464 R Kaya, Intangible Slayer @Showichi Furumi
|
||||
467 M Lukka, Bound to Ruin @Yuki Fujisawa
|
||||
469 R Melira, the Living Cure @Miyuki Aramaki
|
||||
471 R Nahiri, the Unforgiving @Hiro Usuda
|
||||
476 R Tyvar, Jubilant Brawler @Iinuma Yuuki
|
||||
477 R Venser, Corpse Puppet @Dominik Mayer
|
||||
478 R Graaz, Unstoppable Juggernaut @Kutat
|
||||
|
||||
@@ -11,224 +11,258 @@ ScryfallCode=POR
|
||||
|
||||
[cards]
|
||||
1 R Alabaster Dragon @Ted Naifeh
|
||||
157 R Alluring Scent @Ted Naifeh
|
||||
158 U Anaconda @Andrew Robinson
|
||||
158† U Anaconda @Andrew Robinson
|
||||
40 R Ancestral Memories @Dan Frazier
|
||||
2 C Angelic Blessing @DiTerlizzi
|
||||
3 R Archangel @Quinton Hoover
|
||||
4 U Ardent Militia @Mike Raabe
|
||||
5 R Armageddon @John Avon
|
||||
6 C Armored Pegasus @Andrew Robinson
|
||||
79 U Arrogant Vampire @Zina Saunders
|
||||
80 U Assassin's Blade @John Matson
|
||||
41 R Balance of Power @Adam Rex
|
||||
42 U Baleful Stare @John Coulthart
|
||||
159 U Bee Sting @Phil Foglio
|
||||
118 U Blaze @Gerry Grace
|
||||
118s U Blaze @David A. Cherry
|
||||
6d C Armored Pegasus @Andrew Robinson
|
||||
7 R Blessed Reversal @Zina Saunders
|
||||
8 R Blinding Light @John Coulthart
|
||||
81 C Bog Imp @Christopher Rush
|
||||
82 C Bog Raiders @Steve Luke
|
||||
83 U Bog Wraith @Ted Naifeh
|
||||
119 U Boiling Seas @Tom Wänerstrand
|
||||
9 C Border Guard @Kev Walker
|
||||
10 C Breath of Life @DiTerlizzi
|
||||
160 U Bull Hippo @Roger Raupp
|
||||
120 C Burning Cloak @Scott M. Fischer
|
||||
43 R Capricious Sorcerer @Zina Saunders
|
||||
84 U Charging Bandits @Dermot Power
|
||||
11 U Charging Paladin @Kev Walker
|
||||
161 R Charging Rhino @Una Fricker
|
||||
12 U Defiant Stand @Hannibal King
|
||||
13 C Devoted Hero @DiTerlizzi
|
||||
14 C False Peace @Zina Saunders
|
||||
15 C Fleet-Footed Monk @D. Alexander Gregory
|
||||
16 C Foot Soldiers @Kev Walker
|
||||
17 R Gift of Estates @Kaja Foglio
|
||||
18 R Harsh Justice @John Coulthart
|
||||
19 C Keen-Eyed Archers @Alan Rabinowitz
|
||||
20 C Knight Errant @Dan Frazier
|
||||
21 C Path of Peace @Pete Venters
|
||||
22 C Regal Unicorn @Zina Saunders
|
||||
23 U Renewing Dawn @John Avon
|
||||
24 C Sacred Knight @Donato Giancola
|
||||
25 C Sacred Nectar @Janine Johnston
|
||||
26 U Seasoned Marshal @Zina Saunders
|
||||
27 R Spiritual Guardian @Terese Nielsen
|
||||
28 C Spotted Griffin @William Simpson
|
||||
29 U Starlight @John Avon
|
||||
30 U Starlit Angel @Rebecca Guay
|
||||
30s U Starlit Angel @徐晓鸣
|
||||
31 C Steadfastness @Kev Walker
|
||||
32 R Stern Marshal @D. Alexander Gregory
|
||||
33 R Temporary Truce @Mike Raabe
|
||||
34 U Valorous Charge @Douglas Shuler
|
||||
35 U Venerable Monk @D. Alexander Gregory
|
||||
36 U Vengeance @Andrew Robinson
|
||||
37 U Wall of Swords @Douglas Shuler
|
||||
38 C Warrior's Charge @Ted Naifeh
|
||||
38† C Warrior's Charge @Ted Naifeh
|
||||
39 R Wrath of God @Mike Raabe
|
||||
40 R Ancestral Memories @Dan Frazier
|
||||
41 R Balance of Power @Adam Rex
|
||||
42 U Baleful Stare @John Coulthart
|
||||
43 R Capricious Sorcerer @Zina Saunders
|
||||
44 C Cloak of Feathers @Rebecca Guay
|
||||
45 R Cloud Dragon @John Avon
|
||||
46 C Cloud Pirates @Phil Foglio
|
||||
46d C Cloud Pirates @Phil Foglio
|
||||
47 U Cloud Spirit @DiTerlizzi
|
||||
48 U Command of Unsummoning @Phil Foglio
|
||||
49 C Coral Eel @Una Fricker
|
||||
121 C Craven Giant @Ron Spencer
|
||||
50 R Cruel Fate @Adrian Smith
|
||||
51 U Deep-Sea Serpent @Scott M. Fischer
|
||||
52 R Djinn of the Lamp @DiTerlizzi
|
||||
53 C Deja Vu @Hannibal King
|
||||
54 R Exhaustion @DiTerlizzi
|
||||
55 U Flux @Ted Naifeh
|
||||
56 C Giant Octopus @John Matson
|
||||
57 C Horned Turtle @Adrian Smith
|
||||
57s C Horned Turtle @王玉群
|
||||
58 U Ingenious Thief @Dan Frazier
|
||||
59 U Man-o'-War @Una Fricker
|
||||
60 C Merfolk of the Pearl Trident @DiTerlizzi
|
||||
61 U Mystic Denial @Hannibal King
|
||||
62 C Omen @Eric Peterson
|
||||
63 C Owl Familiar @Janine Johnston
|
||||
64 U Personal Tutor @D. Alexander Gregory
|
||||
65 R Phantom Warrior @Dan Frazier
|
||||
66 R Prosperity @Phil Foglio
|
||||
66s R Prosperity @李有良
|
||||
67 C Snapping Drake @Christopher Rush
|
||||
67d C Snapping Drake @Christopher Rush
|
||||
68 C Sorcerous Sight @Kaja Foglio
|
||||
69 C Storm Crow @Una Fricker
|
||||
69d C Storm Crow @Una Fricker
|
||||
70 C Symbol of Unsummoning @Adam Rex
|
||||
71 R Taunt @Phil Foglio
|
||||
71s R Taunt @杨钊
|
||||
72 U Theft of Dreams @Adam Rex
|
||||
73 R Thing from the Deep @Paolo Parente
|
||||
74 C Tidal Surge @Douglas Shuler
|
||||
75 C Time Ebb @Alan Rabinowitz
|
||||
76 C Touch of Brilliance @John Coulthart
|
||||
77 C Wind Drake @Zina Saunders
|
||||
78 U Withering Gaze @Scott M. Fischer
|
||||
79 U Arrogant Vampire @Zina Saunders
|
||||
80 U Assassin's Blade @John Matson
|
||||
80s U Assassin's Blade @徐晓鸣
|
||||
81 C Bog Imp @Christopher Rush
|
||||
82 C Bog Raiders @Steve Luke
|
||||
83 U Bog Wraith @Ted Naifeh
|
||||
84 U Charging Bandits @Dermot Power
|
||||
85 C Craven Knight @Charles Gillespie
|
||||
86 R Cruel Bargain @Adrian Smith
|
||||
50 R Cruel Fate @Adrian Smith
|
||||
87 R Cruel Tutor @Kev Walker
|
||||
51 U Deep-Sea Serpent @Scott M. Fischer
|
||||
162 U Deep Wood @Paolo Parente
|
||||
12 U Defiant Stand @Hannibal King
|
||||
53 C Deja Vu @Hannibal King
|
||||
122 U Desert Drake @Gerry Grace
|
||||
123 R Devastation @Steve Luke
|
||||
13 C Devoted Hero @DiTerlizzi
|
||||
52 R Djinn of the Lamp @DiTerlizzi
|
||||
88 R Dread Charge @Ted Naifeh
|
||||
89 R Dread Reaper @Christopher Rush
|
||||
90 U Dry Spell @Roger Raupp
|
||||
124 R Earthquake @Adrian Smith
|
||||
91 R Ebon Dragon @Donato Giancola
|
||||
163 C Elite Cat Warrior @Eric Peterson
|
||||
163† C Elite Cat Warrior @Eric Peterson
|
||||
164 C Elven Cache @Rebecca Guay
|
||||
165 C Elvish Ranger @DiTerlizzi
|
||||
92 R Endless Cockroaches @Ron Spencer
|
||||
54 R Exhaustion @DiTerlizzi
|
||||
14 C False Peace @Zina Saunders
|
||||
93 C Feral Shadow @Colin MacNeil
|
||||
93d C Feral Shadow @Colin MacNeil
|
||||
94 R Final Strike @John Coulthart
|
||||
95 U Gravedigger @Scott M. Fischer
|
||||
96 C Hand of Death @John Coulthart
|
||||
96† C Hand of Death @John Coulthart
|
||||
97 C Howling Fury @Mike Dringenberg
|
||||
98 R King's Assassin @Zina Saunders
|
||||
99 R Mercenary Knight @Adrian Smith
|
||||
100 C Mind Knives @Rebecca Guay
|
||||
101 C Mind Rot @Steve Luke
|
||||
102 C Muck Rats @Colin MacNeil
|
||||
103 U Nature's Ruin @Mike Dringenberg
|
||||
104 U Noxious Toad @Adrian Smith
|
||||
105 C Python @Alan Rabinowitz
|
||||
106 U Rain of Tears @Eric Peterson
|
||||
107 C Raise Dead @Charles Gillespie
|
||||
107s C Raise Dead @李尤松
|
||||
108 R Serpent Assassin @Roger Raupp
|
||||
109 C Serpent Warrior @Roger Raupp
|
||||
110 C Skeletal Crocodile @Mike Dringenberg
|
||||
111 C Skeletal Snake @John Matson
|
||||
112 C Soul Shred @Alan Rabinowitz
|
||||
113 C Undying Beast @Steve Luke
|
||||
114 U Vampiric Feast @D. Alexander Gregory
|
||||
114s U Vampiric Feast @王峰
|
||||
115 C Vampiric Touch @Zina Saunders
|
||||
116 U Virtue's Ruin @Mike Dringenberg
|
||||
117 R Wicked Pact @Adam Rex
|
||||
117s R Wicked Pact @杨光恒
|
||||
118 U Blaze @Gerry Grace
|
||||
118† U Blaze @Gerry Grace
|
||||
118s U Blaze @David A. Cherry
|
||||
119 U Boiling Seas @Tom Wänerstrand
|
||||
120 C Burning Cloak @Scott M. Fischer
|
||||
121 C Craven Giant @Ron Spencer
|
||||
122 U Desert Drake @Gerry Grace
|
||||
123 R Devastation @Steve Luke
|
||||
124 R Earthquake @Adrian Smith
|
||||
125 R Fire Dragon @William Simpson
|
||||
126 U Fire Imp @DiTerlizzi
|
||||
127 C Fire Snake @Steve Luke
|
||||
128 R Fire Tempest @Mike Dringenberg
|
||||
129 U Flashfires @Randy Gallegos
|
||||
15 C Fleet-Footed Monk @D. Alexander Gregory
|
||||
55 U Flux @Ted Naifeh
|
||||
16 C Foot Soldiers @Kev Walker
|
||||
212 L Forest @John Avon
|
||||
212s L Forest @李铁
|
||||
213 L Forest @John Avon
|
||||
213s L Forest @李铁
|
||||
130 R Forked Lightning @Ted Naifeh
|
||||
166 C Fruition @Steve Luke
|
||||
56 C Giant Octopus @John Matson
|
||||
167 C Giant Spider @Randy Gallegos
|
||||
17 R Gift of Estates @Kaja Foglio
|
||||
131 C Goblin Bully @Pete Venters
|
||||
168 C Gorilla Warrior @John Matson
|
||||
95 U Gravedigger @Scott M. Fischer
|
||||
169 C Grizzly Bears @Zina Saunders
|
||||
96 C Hand of Death @John Coulthart
|
||||
96† C Hand of Death @John Coulthart
|
||||
18 R Harsh Justice @John Coulthart
|
||||
132 C Highland Giant @Ron Spencer
|
||||
133 C Hill Giant @Randy Gallegos
|
||||
57 C Horned Turtle @Adrian Smith
|
||||
97 C Howling Fury @Mike Dringenberg
|
||||
134 U Hulking Cyclops @Paolo Parente
|
||||
134s U Hulking Cyclops @Lin Yan
|
||||
135 C Hulking Goblin @Pete Venters
|
||||
170 R Hurricane @Andrew Robinson
|
||||
58 U Ingenious Thief @Dan Frazier
|
||||
200 L Island @Eric Peterson
|
||||
200s L Island @库雪明
|
||||
201 L Island @Eric Peterson
|
||||
201s L Island @库雪明
|
||||
171 C Jungle Lion @Janine Johnston
|
||||
19 C Keen-Eyed Archers @Alan Rabinowitz
|
||||
98 R King's Assassin @Zina Saunders
|
||||
20 C Knight Errant @Dan Frazier
|
||||
136 R Last Chance @Hannibal King
|
||||
137 C Lava Axe @Adrian Smith
|
||||
138 U Lava Flow @Mike Dringenberg
|
||||
139 C Lizard Warrior @Roger Raupp
|
||||
59 U Man-o'-War @Una Fricker
|
||||
99 R Mercenary Knight @Adrian Smith
|
||||
60 C Merfolk of the Pearl Trident @DiTerlizzi
|
||||
100 C Mind Knives @Rebecca Guay
|
||||
101 C Mind Rot @Steve Luke
|
||||
140 C Minotaur Warrior @Scott M. Fischer
|
||||
172 C Mobilize @Rebecca Guay
|
||||
173 C Monstrous Growth @Dan Frazier
|
||||
173† C Monstrous Growth @Dan Frazier
|
||||
174 U Moon Sprite @Terese Nielsen
|
||||
208 L Mountain @Brian Durfee
|
||||
208s L Mountain @康羽
|
||||
209 L Mountain @Brian Durfee
|
||||
209s L Mountain @康羽
|
||||
141 U Mountain Goat @Una Fricker
|
||||
102 C Muck Rats @Colin MacNeil
|
||||
61 U Mystic Denial @Hannibal King
|
||||
175 R Natural Order @Alan Rabinowitz
|
||||
176 U Natural Spring @Janine Johnston
|
||||
177 R Nature's Cloak @Rebecca Guay
|
||||
178 C Nature's Lore @Terese Nielsen
|
||||
103 U Nature's Ruin @Mike Dringenberg
|
||||
179 U Needle Storm @Charles Gillespie
|
||||
104 U Noxious Toad @Adrian Smith
|
||||
62 C Omen @Eric Peterson
|
||||
63 C Owl Familiar @Janine Johnston
|
||||
180 C Panther Warriors @Eric Peterson
|
||||
21 C Path of Peace @Pete Venters
|
||||
64 U Personal Tutor @D. Alexander Gregory
|
||||
65 R Phantom Warrior @Dan Frazier
|
||||
142 R Pillaging Horde @Kev Walker
|
||||
196 L Plains @Douglas Shuler
|
||||
196s L Plains @Jack Wei
|
||||
197 L Plains @Douglas Shuler
|
||||
197s L Plains @Jack Wei
|
||||
181 U Plant Elemental @Ted Naifeh
|
||||
182 R Primeval Force @Randy Gallegos
|
||||
66 R Prosperity @Phil Foglio
|
||||
143 R Pyroclasm @John Matson
|
||||
105 C Python @Alan Rabinowitz
|
||||
144 C Raging Cougar @Terese Nielsen
|
||||
145 C Raging Goblin @Pete Venters
|
||||
145† C Raging Goblin @Pete Venters
|
||||
146 C Raging Minotaur @Scott M. Fischer
|
||||
147 U Rain of Salt @Charles Gillespie
|
||||
106 U Rain of Tears @Eric Peterson
|
||||
107 C Raise Dead @Charles Gillespie
|
||||
183 C Redwood Treefolk @Steve Luke
|
||||
22 C Regal Unicorn @Zina Saunders
|
||||
23 U Renewing Dawn @John Avon
|
||||
184 C Rowan Treefolk @Gerry Grace
|
||||
24 C Sacred Knight @Donato Giancola
|
||||
25 C Sacred Nectar @Janine Johnston
|
||||
148 C Scorching Spear @Mike Raabe
|
||||
149 U Scorching Winds @D. Alexander Gregory
|
||||
26 U Seasoned Marshal @Zina Saunders
|
||||
108 R Serpent Assassin @Roger Raupp
|
||||
109 C Serpent Warrior @Roger Raupp
|
||||
110 C Skeletal Crocodile @Mike Dringenberg
|
||||
111 C Skeletal Snake @John Matson
|
||||
67 C Snapping Drake @Christopher Rush
|
||||
68 C Sorcerous Sight @Kaja Foglio
|
||||
112 C Soul Shred @Alan Rabinowitz
|
||||
185 C Spined Wurm @Colin MacNeil
|
||||
27 R Spiritual Guardian @Terese Nielsen
|
||||
150 C Spitting Earth @Hannibal King
|
||||
28 C Spotted Griffin @William Simpson
|
||||
186 C Stalking Tiger @Colin MacNeil
|
||||
29 U Starlight @John Avon
|
||||
30 U Starlit Angel @Rebecca Guay
|
||||
31 C Steadfastness @Kev Walker
|
||||
32 R Stern Marshal @D. Alexander Gregory
|
||||
151 C Stone Rain @John Matson
|
||||
69 C Storm Crow @Una Fricker
|
||||
152 R Thundermare @Bob Eggleton
|
||||
153 R Volcanic Dragon @Tom Wänerstrand
|
||||
154 C Volcanic Hammer @Christopher Rush
|
||||
155 U Wall of Granite @Kev Walker
|
||||
156 R Winds of Change @Adam Rex
|
||||
157 R Alluring Scent @Ted Naifeh
|
||||
158 U Anaconda @Andrew Robinson
|
||||
158† U Anaconda @Andrew Robinson
|
||||
159 U Bee Sting @Phil Foglio
|
||||
160 U Bull Hippo @Roger Raupp
|
||||
160d U Bull Hippo @Roger Raupp
|
||||
161 R Charging Rhino @Una Fricker
|
||||
162 U Deep Wood @Paolo Parente
|
||||
163 C Elite Cat Warrior @Eric Peterson
|
||||
163† C Elite Cat Warrior @Eric Peterson
|
||||
164 C Elven Cache @Rebecca Guay
|
||||
164s C Elven Cache @张艺娜
|
||||
165 C Elvish Ranger @DiTerlizzi
|
||||
166 C Fruition @Steve Luke
|
||||
166s C Fruition @王玉群
|
||||
167 C Giant Spider @Randy Gallegos
|
||||
168 C Gorilla Warrior @John Matson
|
||||
169 C Grizzly Bears @Zina Saunders
|
||||
170 R Hurricane @Andrew Robinson
|
||||
171 C Jungle Lion @Janine Johnston
|
||||
172 C Mobilize @Rebecca Guay
|
||||
173 C Monstrous Growth @Dan Frazier
|
||||
173† C Monstrous Growth @Dan Frazier
|
||||
174 U Moon Sprite @Terese Nielsen
|
||||
175 R Natural Order @Alan Rabinowitz
|
||||
176 U Natural Spring @Janine Johnston
|
||||
177 R Nature's Cloak @Rebecca Guay
|
||||
178 C Nature's Lore @Terese Nielsen
|
||||
179 U Needle Storm @Charles Gillespie
|
||||
180 C Panther Warriors @Eric Peterson
|
||||
181 U Plant Elemental @Ted Naifeh
|
||||
182 R Primeval Force @Randy Gallegos
|
||||
183 C Redwood Treefolk @Steve Luke
|
||||
184 C Rowan Treefolk @Gerry Grace
|
||||
185 C Spined Wurm @Colin MacNeil
|
||||
186 C Stalking Tiger @Colin MacNeil
|
||||
187 R Summer Bloom @Kaja Foglio
|
||||
188 R Sylvan Tutor @Kaja Foglio
|
||||
189 R Thundering Wurm @Paolo Parente
|
||||
190 R Treetop Defense @Zina Saunders
|
||||
191 U Untamed Wilds @Romas Kukalis
|
||||
192 U Whiptail Wurm @Una Fricker
|
||||
193 C Willow Dryad @D. Alexander Gregory
|
||||
194 U Winter's Grasp @Paolo Parente
|
||||
195 R Wood Elves @Rebecca Guay
|
||||
196 L Plains @Douglas Shuler
|
||||
196s L Plains @Jack Wei
|
||||
197 L Plains @Douglas Shuler
|
||||
197s L Plains @Jack Wei
|
||||
198 L Plains @Douglas Shuler
|
||||
198s L Plains @Jack Wei
|
||||
199 L Plains @Douglas Shuler
|
||||
199s L Plains @Jack Wei
|
||||
200 L Island @Eric Peterson
|
||||
200s L Island @库雪明
|
||||
201 L Island @Eric Peterson
|
||||
201s L Island @库雪明
|
||||
202 L Island @Eric Peterson
|
||||
202s L Island @库雪明
|
||||
203 L Island @Eric Peterson
|
||||
203s L Island @库雪明
|
||||
204 L Swamp @Romas Kukalis
|
||||
205 L Swamp @Romas Kukalis
|
||||
206 L Swamp @Romas Kukalis
|
||||
207 L Swamp @Romas Kukalis
|
||||
188 R Sylvan Tutor @Kaja Foglio
|
||||
70 C Symbol of Unsummoning @Adam Rex
|
||||
71 R Taunt @Phil Foglio
|
||||
33 R Temporary Truce @Mike Raabe
|
||||
72 U Theft of Dreams @Adam Rex
|
||||
73 R Thing from the Deep @Paolo Parente
|
||||
189 R Thundering Wurm @Paolo Parente
|
||||
152 R Thundermare @Bob Eggleton
|
||||
74 C Tidal Surge @Douglas Shuler
|
||||
75 C Time Ebb @Alan Rabinowitz
|
||||
76 C Touch of Brilliance @John Coulthart
|
||||
190 R Treetop Defense @Zina Saunders
|
||||
113 C Undying Beast @Steve Luke
|
||||
191 U Untamed Wilds @Romas Kukalis
|
||||
34 U Valorous Charge @Douglas Shuler
|
||||
114 U Vampiric Feast @D. Alexander Gregory
|
||||
115 C Vampiric Touch @Zina Saunders
|
||||
35 U Venerable Monk @D. Alexander Gregory
|
||||
36 U Vengeance @Andrew Robinson
|
||||
116 U Virtue's Ruin @Mike Dringenberg
|
||||
153 R Volcanic Dragon @Tom Wänerstrand
|
||||
154 C Volcanic Hammer @Christopher Rush
|
||||
155 U Wall of Granite @Kev Walker
|
||||
37 U Wall of Swords @Douglas Shuler
|
||||
38 C Warrior's Charge @Ted Naifeh
|
||||
38† C Warrior's Charge @Ted Naifeh
|
||||
192 U Whiptail Wurm @Una Fricker
|
||||
117 R Wicked Pact @Adam Rex
|
||||
193 C Willow Dryad @D. Alexander Gregory
|
||||
77 C Wind Drake @Zina Saunders
|
||||
156 R Winds of Change @Adam Rex
|
||||
194 U Winter's Grasp @Paolo Parente
|
||||
78 U Withering Gaze @Scott M. Fischer
|
||||
195 R Wood Elves @Rebecca Guay
|
||||
39 R Wrath of God @Mike Raabe
|
||||
208 L Mountain @Brian Durfee
|
||||
208s L Mountain @康羽
|
||||
209 L Mountain @Brian Durfee
|
||||
209s L Mountain @康羽
|
||||
210 L Mountain @Brian Durfee
|
||||
210s L Mountain @康羽
|
||||
211 L Mountain @Brian Durfee
|
||||
211s L Mountain @康羽
|
||||
212 L Forest @John Avon
|
||||
212s L Forest @李铁
|
||||
213 L Forest @John Avon
|
||||
213s L Forest @李铁
|
||||
214 L Forest @John Avon
|
||||
214s L Forest @李铁
|
||||
215 L Forest @John Avon
|
||||
215s L Forest @李铁
|
||||
|
||||
@@ -609,6 +609,7 @@ ScryfallCode=SLD
|
||||
638 R Fury Sliver @Paolo Parente
|
||||
640 R Homing Sliver @Trevor Hairsine
|
||||
641 R Magma Sliver @Wayne England
|
||||
642 R Sedge Sliver @Richard Kane Ferguson
|
||||
643 R Spiteful Sliver @Johann Bodin
|
||||
644 R Striking Sliver @Maciej Kuciara
|
||||
645 R Thorncaster Sliver @Trevor Claxton
|
||||
|
||||
9
forge-gui/res/editions/Wizards Play Network 2023.txt
Normal file
9
forge-gui/res/editions/Wizards Play Network 2023.txt
Normal file
@@ -0,0 +1,9 @@
|
||||
[metadata]
|
||||
Code=PW23
|
||||
Date=2023-01-01
|
||||
Name=Wizards Play Network 2023
|
||||
Type=Promo
|
||||
ScryfallCode=PW23
|
||||
|
||||
[cards]
|
||||
1 R Norn's Annex @James Paick
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user