mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-12 00:38:44 +00:00
Merge branch 'Card-Forge:master' into master
This commit is contained in:
@@ -277,7 +277,7 @@ public class AiBlockController {
|
|||||||
|
|
||||||
if (mode == TriggerType.DamageDone) {
|
if (mode == TriggerType.DamageDone) {
|
||||||
if (trigger.matchesValidParam("ValidSource", attacker)
|
if (trigger.matchesValidParam("ValidSource", attacker)
|
||||||
&& attacker.getNetCombatDamage() > 0
|
&& !"False".equals(trigger.getParam("CombatDamage")) && attacker.getNetCombatDamage() > 0
|
||||||
&& trigger.matchesValidParam("ValidTarget", combat.getDefenderByAttacker(attacker))) {
|
&& trigger.matchesValidParam("ValidTarget", combat.getDefenderByAttacker(attacker))) {
|
||||||
value += 50;
|
value += 50;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -812,16 +812,11 @@ public class AiController {
|
|||||||
for (Card tgt : tc.getTargetCards()) {
|
for (Card tgt : tc.getTargetCards()) {
|
||||||
// TODO some older cards don't use the keyword, so check for trigger instead
|
// TODO some older cards don't use the keyword, so check for trigger instead
|
||||||
if (tgt.hasKeyword(Keyword.WARD) && tgt.isInPlay() && tgt.getController().isOpponentOf(host.getController())) {
|
if (tgt.hasKeyword(Keyword.WARD) && tgt.isInPlay() && tgt.getController().isOpponentOf(host.getController())) {
|
||||||
int amount = 0;
|
|
||||||
Cost wardCost = ComputerUtilCard.getTotalWardCost(tgt);
|
Cost wardCost = ComputerUtilCard.getTotalWardCost(tgt);
|
||||||
if (wardCost.hasManaCost()) {
|
if (wardCost.hasManaCost()) {
|
||||||
amount = wardCost.getTotalMana().getCMC();
|
xCost = wardCost.getTotalMana().getCMC() > 0;
|
||||||
if (amount > 0 && !ComputerUtilCost.canPayCost(sa, player, true)) {
|
|
||||||
return AiPlayDecision.CantAfford;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
SpellAbilityAi topAI = new SpellAbilityAi() {
|
SpellAbilityAi topAI = new SpellAbilityAi() {};
|
||||||
};
|
|
||||||
if (!topAI.willPayCosts(player, sa, wardCost, host)) {
|
if (!topAI.willPayCosts(player, sa, wardCost, host)) {
|
||||||
return AiPlayDecision.CostNotAcceptable;
|
return AiPlayDecision.CostNotAcceptable;
|
||||||
}
|
}
|
||||||
@@ -831,7 +826,7 @@ public class AiController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// check if some target raised cost
|
// check if some target raised cost
|
||||||
if (oldCMC > -1) {
|
if (!xCost && oldCMC > -1) {
|
||||||
int finalCMC = CostAdjustment.adjust(sa.getPayCosts(), sa).getTotalMana().getCMC();
|
int finalCMC = CostAdjustment.adjust(sa.getPayCosts(), sa).getTotalMana().getCMC();
|
||||||
if (finalCMC > oldCMC) {
|
if (finalCMC > oldCMC) {
|
||||||
xCost = true;
|
xCost = true;
|
||||||
|
|||||||
@@ -673,7 +673,6 @@ public class ComputerUtilCombat {
|
|||||||
&& !blocker.hasKeyword(Keyword.INDESTRUCTIBLE)) {
|
&& !blocker.hasKeyword(Keyword.INDESTRUCTIBLE)) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // flanking
|
} // flanking
|
||||||
|
|
||||||
final int defBushidoMagnitude = blocker.getKeywordMagnitude(Keyword.BUSHIDO);
|
final int defBushidoMagnitude = blocker.getKeywordMagnitude(Keyword.BUSHIDO);
|
||||||
@@ -859,7 +858,7 @@ public class ComputerUtilCombat {
|
|||||||
}
|
}
|
||||||
} else if (mode == TriggerType.DamageDone) {
|
} else if (mode == TriggerType.DamageDone) {
|
||||||
willTrigger = true;
|
willTrigger = true;
|
||||||
if (trigger.hasParam("ValidSource")) {
|
if (trigger.hasParam("ValidSource") && !"False".equals(trigger.getParam("CombatDamage"))) {
|
||||||
if (!(trigger.matchesValidParam("ValidSource", defender)
|
if (!(trigger.matchesValidParam("ValidSource", defender)
|
||||||
&& defender.getNetCombatDamage() > 0
|
&& defender.getNetCombatDamage() > 0
|
||||||
&& trigger.matchesValidParam("ValidTarget", attacker))) {
|
&& trigger.matchesValidParam("ValidTarget", attacker))) {
|
||||||
@@ -2461,7 +2460,7 @@ public class ComputerUtilCombat {
|
|||||||
CardCollectionView trigCards = attacker.getController().getCardsIn(ZoneType.Battlefield);
|
CardCollectionView trigCards = attacker.getController().getCardsIn(ZoneType.Battlefield);
|
||||||
for (Card c : trigCards) {
|
for (Card c : trigCards) {
|
||||||
for (Trigger t : c.getTriggers()) {
|
for (Trigger t : c.getTriggers()) {
|
||||||
if (t.getMode() == TriggerType.DamageDone && "True".equals(t.getParam("CombatDamage")) && t.matchesValidParam("ValidSource", attacker)) {
|
if (t.getMode() == TriggerType.DamageDone && !"False".equals(t.getParam("CombatDamage")) && t.matchesValidParam("ValidSource", attacker)) {
|
||||||
SpellAbility ab = t.getOverridingAbility();
|
SpellAbility ab = t.getOverridingAbility();
|
||||||
if (ab.getApi() == ApiType.Poison && "TriggeredTarget".equals(ab.getParam("Defined"))) {
|
if (ab.getApi() == ApiType.Poison && "TriggeredTarget".equals(ab.getParam("Defined"))) {
|
||||||
pd += AbilityUtils.calculateAmount(attacker, ab.getParam("Num"), ab);
|
pd += AbilityUtils.calculateAmount(attacker, ab.getParam("Num"), ab);
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import forge.game.phase.PhaseType;
|
|||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
import forge.game.spellability.Spell;
|
import forge.game.spellability.Spell;
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
|
import forge.game.spellability.TargetChoices;
|
||||||
import forge.game.zone.ZoneType;
|
import forge.game.zone.ZoneType;
|
||||||
import forge.util.MyRandom;
|
import forge.util.MyRandom;
|
||||||
import forge.util.TextUtil;
|
import forge.util.TextUtil;
|
||||||
@@ -556,12 +557,14 @@ public class ComputerUtilCost {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Ward - will be accounted for when rechecking a targeted ability
|
// Ward - will be accounted for when rechecking a targeted ability
|
||||||
if (!sa.isTrigger() && sa.usesTargeting() && (!sa.isSpell() || !cannotBeCountered)) {
|
if (!sa.isTrigger() && (!sa.isSpell() || !cannotBeCountered)) {
|
||||||
for (Card tgt : sa.getTargets().getTargetCards()) {
|
for (TargetChoices tc : sa.getAllTargetChoices()) {
|
||||||
if (tgt.hasKeyword(Keyword.WARD) && tgt.isInPlay() && tgt.getController().isOpponentOf(sa.getHostCard().getController())) {
|
for (Card tgt : tc.getTargetCards()) {
|
||||||
Cost wardCost = ComputerUtilCard.getTotalWardCost(tgt);
|
if (tgt.hasKeyword(Keyword.WARD) && tgt.isInPlay() && tgt.getController().isOpponentOf(sa.getHostCard().getController())) {
|
||||||
if (wardCost.hasManaCost()) {
|
Cost wardCost = ComputerUtilCard.getTotalWardCost(tgt);
|
||||||
extraManaNeeded += wardCost.getTotalMana().getCMC();
|
if (wardCost.hasManaCost()) {
|
||||||
|
extraManaNeeded += wardCost.getTotalMana().getCMC();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -86,7 +86,7 @@ public class AnimateAi extends SpellAbilityAi {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Don't use instant speed animate abilities before AI's COMBAT_BEGIN
|
// Don't use instant speed animate abilities before AI's COMBAT_BEGIN
|
||||||
if (!ph.is(PhaseType.COMBAT_BEGIN) && ph.isPlayerTurn(ai) && !SpellAbilityAi.isSorcerySpeed(sa, ai)
|
if (!ph.is(PhaseType.COMBAT_BEGIN) && ph.isPlayerTurn(ai) && !isSorcerySpeed(sa, ai)
|
||||||
&& !sa.hasParam("ActivationPhases") && !"Permanent".equals(sa.getParam("Duration"))) {
|
&& !sa.hasParam("ActivationPhases") && !"Permanent".equals(sa.getParam("Duration"))) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -174,7 +174,7 @@ public class AnimateAi extends SpellAbilityAi {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!SpellAbilityAi.isSorcerySpeed(sa, aiPlayer) && !"Permanent".equals(sa.getParam("Duration"))) {
|
if (!isSorcerySpeed(sa, aiPlayer) && !"Permanent".equals(sa.getParam("Duration"))) {
|
||||||
if (sa.hasParam("Crew") && c.isCreature()) {
|
if (sa.hasParam("Crew") && c.isCreature()) {
|
||||||
// Do not try to crew a vehicle which is already a creature
|
// Do not try to crew a vehicle which is already a creature
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -1069,7 +1069,7 @@ public class ChangeZoneAi extends SpellAbilityAi {
|
|||||||
}
|
}
|
||||||
if (!immediately && (!game.getPhaseHandler().getNextTurn().equals(ai)
|
if (!immediately && (!game.getPhaseHandler().getNextTurn().equals(ai)
|
||||||
|| game.getPhaseHandler().getPhase().isBefore(PhaseType.END_OF_TURN))
|
|| game.getPhaseHandler().getPhase().isBefore(PhaseType.END_OF_TURN))
|
||||||
&& !sa.hasParam("PlayerTurn") && !SpellAbilityAi.isSorcerySpeed(sa, ai)
|
&& !sa.hasParam("PlayerTurn") && !isSorcerySpeed(sa, ai)
|
||||||
&& !ComputerUtil.activateForCost(sa, ai)) {
|
&& !ComputerUtil.activateForCost(sa, ai)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -242,7 +242,7 @@ public class CloneAi extends SpellAbilityAi {
|
|||||||
// don't use instant speed clone abilities outside computers
|
// don't use instant speed clone abilities outside computers
|
||||||
// Combat_Begin step
|
// Combat_Begin step
|
||||||
if (!ph.is(PhaseType.COMBAT_BEGIN)
|
if (!ph.is(PhaseType.COMBAT_BEGIN)
|
||||||
&& ph.isPlayerTurn(ai) && !SpellAbilityAi.isSorcerySpeed(sa, ai)
|
&& ph.isPlayerTurn(ai) && !isSorcerySpeed(sa, ai)
|
||||||
&& !sa.hasParam("ActivationPhases") && sa.hasParam("Duration")) {
|
&& !sa.hasParam("ActivationPhases") && sa.hasParam("Duration")) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -123,7 +123,7 @@ public class CounterAi extends SpellAbilityAi {
|
|||||||
|
|
||||||
if (toPay <= usableManaSources) {
|
if (toPay <= usableManaSources) {
|
||||||
// If this is a reusable Resource, feel free to play it most of the time
|
// If this is a reusable Resource, feel free to play it most of the time
|
||||||
if (!SpellAbilityAi.playReusable(ai, sa)) {
|
if (!playReusable(ai, sa)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -287,7 +287,7 @@ public class CounterAi extends SpellAbilityAi {
|
|||||||
if (toPay <= usableManaSources) {
|
if (toPay <= usableManaSources) {
|
||||||
// If this is a reusable Resource, feel free to play it most
|
// If this is a reusable Resource, feel free to play it most
|
||||||
// of the time
|
// of the time
|
||||||
if (!SpellAbilityAi.playReusable(ai,sa) || (MyRandom.getRandom().nextFloat() < .4)) {
|
if (!playReusable(ai,sa) || (MyRandom.getRandom().nextFloat() < .4)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ public class CountersMoveAi extends SpellAbilityAi {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!SpellAbilityAi.playReusable(ai, sa)) {
|
if (!playReusable(ai, sa)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -437,7 +437,7 @@ public class CountersPutAi extends CountersAi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (sa.usesTargeting()) {
|
if (sa.usesTargeting()) {
|
||||||
if (!ai.getGame().getStack().isEmpty() && !SpellAbilityAi.isSorcerySpeed(sa, ai)) {
|
if (!ai.getGame().getStack().isEmpty() && !isSorcerySpeed(sa, ai)) {
|
||||||
// only evaluates case where all tokens are placed on a single target
|
// only evaluates case where all tokens are placed on a single target
|
||||||
if (sa.getMinTargets() < 2) {
|
if (sa.getMinTargets() < 2) {
|
||||||
if (ComputerUtilCard.canPumpAgainstRemoval(ai, sa)) {
|
if (ComputerUtilCard.canPumpAgainstRemoval(ai, sa)) {
|
||||||
@@ -550,7 +550,7 @@ public class CountersPutAi extends CountersAi {
|
|||||||
if (sa.isCurse()) {
|
if (sa.isCurse()) {
|
||||||
choice = chooseCursedTarget(list, type, amount, ai);
|
choice = chooseCursedTarget(list, type, amount, ai);
|
||||||
} else {
|
} else {
|
||||||
if (type.equals("P1P1") && !SpellAbilityAi.isSorcerySpeed(sa, ai)) {
|
if (type.equals("P1P1") && !isSorcerySpeed(sa, ai)) {
|
||||||
for (Card c : list) {
|
for (Card c : list) {
|
||||||
if (ComputerUtilCard.shouldPumpCard(ai, sa, c, amount, amount, Lists.newArrayList())) {
|
if (ComputerUtilCard.shouldPumpCard(ai, sa, c, amount, amount, Lists.newArrayList())) {
|
||||||
choice = c;
|
choice = c;
|
||||||
@@ -623,7 +623,7 @@ public class CountersPutAi extends CountersAi {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// Instant +1/+1
|
// Instant +1/+1
|
||||||
if (type.equals("P1P1") && !SpellAbilityAi.isSorcerySpeed(sa, ai)) {
|
if (type.equals("P1P1") && !isSorcerySpeed(sa, ai)) {
|
||||||
if (!(ph.getNextTurn() == ai && ph.is(PhaseType.END_OF_TURN) && abCost.isReusuableResource())) {
|
if (!(ph.getNextTurn() == ai && ph.is(PhaseType.END_OF_TURN) && abCost.isReusuableResource())) {
|
||||||
return false; // only if next turn and cost is reusable
|
return false; // only if next turn and cost is reusable
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -137,7 +137,7 @@ public class CountersPutAllAi extends SpellAbilityAi {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (SpellAbilityAi.playReusable(ai, sa)) {
|
if (playReusable(ai, sa)) {
|
||||||
return chance;
|
return chance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,14 +1,11 @@
|
|||||||
package forge.ai.ability;
|
package forge.ai.ability;
|
||||||
|
|
||||||
import com.google.common.collect.Iterables;
|
|
||||||
|
|
||||||
import forge.ai.ComputerUtil;
|
import forge.ai.ComputerUtil;
|
||||||
import forge.ai.ComputerUtilCombat;
|
import forge.ai.ComputerUtilCombat;
|
||||||
import forge.ai.SpellAbilityAi;
|
import forge.ai.SpellAbilityAi;
|
||||||
import forge.game.Game;
|
import forge.game.Game;
|
||||||
import forge.game.card.Card;
|
import forge.game.card.Card;
|
||||||
import forge.game.card.CardCollectionView;
|
import forge.game.card.CardCollectionView;
|
||||||
import forge.game.card.CardPredicates;
|
|
||||||
import forge.game.keyword.Keyword;
|
import forge.game.keyword.Keyword;
|
||||||
import forge.game.phase.PhaseHandler;
|
import forge.game.phase.PhaseHandler;
|
||||||
import forge.game.phase.PhaseType;
|
import forge.game.phase.PhaseType;
|
||||||
@@ -46,11 +43,6 @@ public abstract class DamageAiBase extends SpellAbilityAi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected boolean shouldTgtP(final Player comp, final SpellAbility sa, final int d, final boolean noPrevention) {
|
protected boolean shouldTgtP(final Player comp, final SpellAbility sa, final int d, final boolean noPrevention) {
|
||||||
// TODO: once the "planeswalker redirection" rule is removed completely, just remove this code and
|
|
||||||
// remove the "burn Planeswalkers" code in the called method.
|
|
||||||
return shouldTgtP(comp, sa, d, noPrevention, false);
|
|
||||||
}
|
|
||||||
protected boolean shouldTgtP(final Player comp, final SpellAbility sa, final int d, final boolean noPrevention, final boolean noPlaneswalkerRedirection) {
|
|
||||||
int restDamage = d;
|
int restDamage = d;
|
||||||
final Game game = comp.getGame();
|
final Game game = comp.getGame();
|
||||||
Player enemy = comp.getWeakestOpponent();
|
Player enemy = comp.getWeakestOpponent();
|
||||||
@@ -89,13 +81,6 @@ public abstract class DamageAiBase extends SpellAbilityAi {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// burn Planeswalkers
|
|
||||||
// TODO: Must be removed completely when the "planeswalker redirection" rule is removed.
|
|
||||||
if (!noPlaneswalkerRedirection
|
|
||||||
&& Iterables.any(enemy.getCardsIn(ZoneType.Battlefield), CardPredicates.Presets.PLANESWALKERS)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (avoidTargetP(comp, sa)) {
|
if (avoidTargetP(comp, sa)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -131,7 +116,7 @@ public abstract class DamageAiBase extends SpellAbilityAi {
|
|||||||
// chance to burn player based on current hand size
|
// chance to burn player based on current hand size
|
||||||
if (hand.size() > 2) {
|
if (hand.size() > 2) {
|
||||||
float value = 0;
|
float value = 0;
|
||||||
if (SpellAbilityAi.isSorcerySpeed(sa, comp)) {
|
if (isSorcerySpeed(sa, comp)) {
|
||||||
//lower chance for sorcery as other spells may be cast in main2
|
//lower chance for sorcery as other spells may be cast in main2
|
||||||
if (phase.isPlayerTurn(comp) && phase.is(PhaseType.MAIN2)) {
|
if (phase.isPlayerTurn(comp) && phase.is(PhaseType.MAIN2)) {
|
||||||
value = 1.0f * restDamage / enemy.getLife();
|
value = 1.0f * restDamage / enemy.getLife();
|
||||||
|
|||||||
@@ -1,28 +1,10 @@
|
|||||||
package forge.ai.ability;
|
package forge.ai.ability;
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
|
||||||
import org.apache.commons.lang3.tuple.Pair;
|
|
||||||
|
|
||||||
import com.google.common.base.Predicate;
|
import com.google.common.base.Predicate;
|
||||||
import com.google.common.base.Predicates;
|
import com.google.common.base.Predicates;
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
import com.google.common.collect.Maps;
|
import com.google.common.collect.Maps;
|
||||||
|
import forge.ai.*;
|
||||||
import forge.ai.AiController;
|
|
||||||
import forge.ai.AiProps;
|
|
||||||
import forge.ai.ComputerUtil;
|
|
||||||
import forge.ai.ComputerUtilAbility;
|
|
||||||
import forge.ai.ComputerUtilCard;
|
|
||||||
import forge.ai.ComputerUtilCombat;
|
|
||||||
import forge.ai.ComputerUtilCost;
|
|
||||||
import forge.ai.ComputerUtilMana;
|
|
||||||
import forge.ai.PlayerControllerAi;
|
|
||||||
import forge.ai.SpecialCardAi;
|
|
||||||
import forge.ai.SpellAbilityAi;
|
|
||||||
import forge.card.MagicColor;
|
import forge.card.MagicColor;
|
||||||
import forge.card.mana.ManaCost;
|
import forge.card.mana.ManaCost;
|
||||||
import forge.game.Game;
|
import forge.game.Game;
|
||||||
@@ -30,11 +12,7 @@ import forge.game.GameEntity;
|
|||||||
import forge.game.GameObject;
|
import forge.game.GameObject;
|
||||||
import forge.game.ability.AbilityUtils;
|
import forge.game.ability.AbilityUtils;
|
||||||
import forge.game.ability.ApiType;
|
import forge.game.ability.ApiType;
|
||||||
import forge.game.card.Card;
|
import forge.game.card.*;
|
||||||
import forge.game.card.CardCollection;
|
|
||||||
import forge.game.card.CardLists;
|
|
||||||
import forge.game.card.CardPredicates;
|
|
||||||
import forge.game.card.CounterEnumType;
|
|
||||||
import forge.game.cost.Cost;
|
import forge.game.cost.Cost;
|
||||||
import forge.game.cost.CostPartMana;
|
import forge.game.cost.CostPartMana;
|
||||||
import forge.game.keyword.Keyword;
|
import forge.game.keyword.Keyword;
|
||||||
@@ -51,6 +29,12 @@ import forge.game.staticability.StaticAbilityMustTarget;
|
|||||||
import forge.game.zone.ZoneType;
|
import forge.game.zone.ZoneType;
|
||||||
import forge.util.Aggregates;
|
import forge.util.Aggregates;
|
||||||
import forge.util.MyRandom;
|
import forge.util.MyRandom;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.apache.commons.lang3.tuple.Pair;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
public class DamageDealAi extends DamageAiBase {
|
public class DamageDealAi extends DamageAiBase {
|
||||||
@Override
|
@Override
|
||||||
@@ -630,7 +614,7 @@ public class DamageDealAi extends DamageAiBase {
|
|||||||
if (tgt.canTgtPlaneswalker()) {
|
if (tgt.canTgtPlaneswalker()) {
|
||||||
// We can damage planeswalkers with this, consider targeting.
|
// We can damage planeswalkers with this, consider targeting.
|
||||||
Card c = dealDamageChooseTgtPW(ai, sa, dmg, noPrevention, enemy, false);
|
Card c = dealDamageChooseTgtPW(ai, sa, dmg, noPrevention, enemy, false);
|
||||||
if (c != null && !shouldTgtP(ai, sa, dmg, noPrevention, true)) {
|
if (c != null && !shouldTgtP(ai, sa, dmg, noPrevention)) {
|
||||||
tcs.add(c);
|
tcs.add(c);
|
||||||
if (divided) {
|
if (divided) {
|
||||||
int assignedDamage = ComputerUtilCombat.getEnoughDamageToKill(c, dmg, source, false, noPrevention);
|
int assignedDamage = ComputerUtilCombat.getEnoughDamageToKill(c, dmg, source, false, noPrevention);
|
||||||
@@ -746,17 +730,17 @@ public class DamageDealAi extends DamageAiBase {
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// TODO: Improve Damage, we shouldn't just target the player just because we can
|
|
||||||
if (sa.canTarget(enemy) && sa.canAddMoreTarget()) {
|
if (sa.canTarget(enemy) && sa.canAddMoreTarget()) {
|
||||||
if (((phase.is(PhaseType.END_OF_TURN) && phase.getNextTurn().equals(ai))
|
if ((phase.is(PhaseType.END_OF_TURN) && phase.getNextTurn().equals(ai))
|
||||||
|| (SpellAbilityAi.isSorcerySpeed(sa, ai) && phase.is(PhaseType.MAIN2))
|
|| (isSorcerySpeed(sa, ai) && phase.is(PhaseType.MAIN2))
|
||||||
|| ("PingAfterAttack".equals(logic) && phase.getPhase().isAfter(PhaseType.COMBAT_DECLARE_ATTACKERS) && phase.isPlayerTurn(ai))
|
|| immediately) {
|
||||||
|| immediately || shouldTgtP(ai, sa, dmg, noPrevention)) &&
|
boolean pingAfterAttack = "PingAfterAttack".equals(logic) && phase.getPhase().isAfter(PhaseType.COMBAT_DECLARE_ATTACKERS) && phase.isPlayerTurn(ai);
|
||||||
(!avoidTargetP(ai, sa))) {
|
if ((pingAfterAttack && !avoidTargetP(ai, sa)) || shouldTgtP(ai, sa, dmg, noPrevention)) {
|
||||||
tcs.add(enemy);
|
tcs.add(enemy);
|
||||||
if (divided) {
|
if (divided) {
|
||||||
sa.addDividedAllocation(enemy, dmg);
|
sa.addDividedAllocation(enemy, dmg);
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -836,7 +820,7 @@ public class DamageDealAi extends DamageAiBase {
|
|||||||
if (!positive && !(saMe instanceof AbilitySub)) {
|
if (!positive && !(saMe instanceof AbilitySub)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!urgent && !SpellAbilityAi.playReusable(ai, saMe)) {
|
if (!urgent && !playReusable(ai, saMe)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ public class DayTimeAi extends SpellAbilityAi {
|
|||||||
|
|
||||||
if ((sa.getHostCard().isCreature() && sa.getPayCosts().hasTapCost()) || sa.getPayCosts().hasManaCost()) {
|
if ((sa.getHostCard().isCreature() && sa.getPayCosts().hasTapCost()) || sa.getPayCosts().hasManaCost()) {
|
||||||
// If it involves a cost that may put us at a disadvantage, better activate before own turn if possible
|
// If it involves a cost that may put us at a disadvantage, better activate before own turn if possible
|
||||||
if (!SpellAbilityAi.isSorcerySpeed(sa, aiPlayer)) {
|
if (!isSorcerySpeed(sa, aiPlayer)) {
|
||||||
return ph.is(PhaseType.END_OF_TURN) && ph.getNextTurn() == aiPlayer;
|
return ph.is(PhaseType.END_OF_TURN) && ph.getNextTurn() == aiPlayer;
|
||||||
} else {
|
} else {
|
||||||
return ph.is(PhaseType.MAIN2, aiPlayer); // Give other things a chance to be cast (e.g. Celestus)
|
return ph.is(PhaseType.MAIN2, aiPlayer); // Give other things a chance to be cast (e.g. Celestus)
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ public class DebuffAi extends SpellAbilityAi {
|
|||||||
|| !game.getStack().isEmpty()) {
|
|| !game.getStack().isEmpty()) {
|
||||||
// Instant-speed pumps should not be cast outside of combat when the
|
// Instant-speed pumps should not be cast outside of combat when the
|
||||||
// stack is empty, unless there are specific activation phase requirements
|
// stack is empty, unless there are specific activation phase requirements
|
||||||
if (!SpellAbilityAi.isSorcerySpeed(sa, ai) && !sa.hasParam("ActivationPhases")) {
|
if (!isSorcerySpeed(sa, ai) && !sa.hasParam("ActivationPhases")) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -174,7 +174,7 @@ public class DestroyAi extends SpellAbilityAi {
|
|||||||
if (CardLists.getNotType(list, "Creature").isEmpty()) {
|
if (CardLists.getNotType(list, "Creature").isEmpty()) {
|
||||||
list = ComputerUtilCard.prioritizeCreaturesWorthRemovingNow(ai, list, false);
|
list = ComputerUtilCard.prioritizeCreaturesWorthRemovingNow(ai, list, false);
|
||||||
}
|
}
|
||||||
if (!SpellAbilityAi.playReusable(ai, sa)) {
|
if (!playReusable(ai, sa)) {
|
||||||
list = CardLists.filter(list, Predicates.not(CardPredicates.hasCounter(CounterEnumType.SHIELD, 1)));
|
list = CardLists.filter(list, Predicates.not(CardPredicates.hasCounter(CounterEnumType.SHIELD, 1)));
|
||||||
|
|
||||||
list = CardLists.filter(list, new Predicate<Card>() {
|
list = CardLists.filter(list, new Predicate<Card>() {
|
||||||
|
|||||||
@@ -101,13 +101,13 @@ public class DigAi extends SpellAbilityAi {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (SpellAbilityAi.playReusable(ai, sa)) {
|
if (playReusable(ai, sa)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((!game.getPhaseHandler().getNextTurn().equals(ai)
|
if ((!game.getPhaseHandler().getNextTurn().equals(ai)
|
||||||
|| game.getPhaseHandler().getPhase().isBefore(PhaseType.END_OF_TURN))
|
|| game.getPhaseHandler().getPhase().isBefore(PhaseType.END_OF_TURN))
|
||||||
&& !sa.hasParam("PlayerTurn") && !SpellAbilityAi.isSorcerySpeed(sa, ai)
|
&& !sa.hasParam("PlayerTurn") && !isSorcerySpeed(sa, ai)
|
||||||
&& (ai.getCardsIn(ZoneType.Hand).size() > 1 || game.getPhaseHandler().getPhase().isBefore(PhaseType.DRAW))
|
&& (ai.getCardsIn(ZoneType.Hand).size() > 1 || game.getPhaseHandler().getPhase().isBefore(PhaseType.DRAW))
|
||||||
&& !ComputerUtil.activateForCost(sa, ai)) {
|
&& !ComputerUtil.activateForCost(sa, ai)) {
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -62,13 +62,13 @@ public class DigMultipleAi extends SpellAbilityAi {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (SpellAbilityAi.playReusable(ai, sa)) {
|
if (playReusable(ai, sa)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((!game.getPhaseHandler().getNextTurn().equals(ai)
|
if ((!game.getPhaseHandler().getNextTurn().equals(ai)
|
||||||
|| game.getPhaseHandler().getPhase().isBefore(PhaseType.END_OF_TURN))
|
|| game.getPhaseHandler().getPhase().isBefore(PhaseType.END_OF_TURN))
|
||||||
&& !sa.hasParam("PlayerTurn") && !SpellAbilityAi.isSorcerySpeed(sa, ai)
|
&& !sa.hasParam("PlayerTurn") && !isSorcerySpeed(sa, ai)
|
||||||
&& (ai.getCardsIn(ZoneType.Hand).size() > 1 || game.getPhaseHandler().getPhase().isBefore(PhaseType.DRAW))
|
&& (ai.getCardsIn(ZoneType.Hand).size() > 1 || game.getPhaseHandler().getPhase().isBefore(PhaseType.DRAW))
|
||||||
&& !ComputerUtil.activateForCost(sa, ai)) {
|
&& !ComputerUtil.activateForCost(sa, ai)) {
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ public class DigUntilAi extends SpellAbilityAi {
|
|||||||
Card source = sa.getHostCard();
|
Card source = sa.getHostCard();
|
||||||
final String logic = sa.getParamOrDefault("AILogic", "");
|
final String logic = sa.getParamOrDefault("AILogic", "");
|
||||||
double chance = .4; // 40 percent chance with instant speed stuff
|
double chance = .4; // 40 percent chance with instant speed stuff
|
||||||
if (SpellAbilityAi.isSorcerySpeed(sa, ai)) {
|
if (isSorcerySpeed(sa, ai)) {
|
||||||
chance = .667; // 66.7% chance for sorcery speed (since it will
|
chance = .667; // 66.7% chance for sorcery speed (since it will
|
||||||
// never activate EOT)
|
// never activate EOT)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -168,7 +168,7 @@ public class DrawAi extends SpellAbilityAi {
|
|||||||
@Override
|
@Override
|
||||||
protected boolean checkPhaseRestrictions(Player ai, SpellAbility sa, PhaseHandler ph, String logic) {
|
protected boolean checkPhaseRestrictions(Player ai, SpellAbility sa, PhaseHandler ph, String logic) {
|
||||||
if ((!ph.getNextTurn().equals(ai) || ph.getPhase().isBefore(PhaseType.END_OF_TURN))
|
if ((!ph.getNextTurn().equals(ai) || ph.getPhase().isBefore(PhaseType.END_OF_TURN))
|
||||||
&& !sa.hasParam("PlayerTurn") && !SpellAbilityAi.isSorcerySpeed(sa, ai)
|
&& !sa.hasParam("PlayerTurn") && !isSorcerySpeed(sa, ai)
|
||||||
&& ai.getCardsIn(ZoneType.Hand).size() > 1 && !ComputerUtil.activateForCost(sa, ai)
|
&& ai.getCardsIn(ZoneType.Hand).size() > 1 && !ComputerUtil.activateForCost(sa, ai)
|
||||||
&& !"YawgmothsBargain".equals(logic)) {
|
&& !"YawgmothsBargain".equals(logic)) {
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ public class FlipOntoBattlefieldAi extends SpellAbilityAi {
|
|||||||
PhaseHandler ph = sa.getHostCard().getGame().getPhaseHandler();
|
PhaseHandler ph = sa.getHostCard().getGame().getPhaseHandler();
|
||||||
String logic = sa.getParamOrDefault("AILogic", "");
|
String logic = sa.getParamOrDefault("AILogic", "");
|
||||||
|
|
||||||
if (!SpellAbilityAi.isSorcerySpeed(sa, aiPlayer) && sa.getPayCosts().hasManaCost()) {
|
if (!isSorcerySpeed(sa, aiPlayer) && sa.getPayCosts().hasManaCost()) {
|
||||||
return ph.is(PhaseType.END_OF_TURN);
|
return ph.is(PhaseType.END_OF_TURN);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -111,7 +111,7 @@ public class LifeGainAi extends SpellAbilityAi {
|
|||||||
|
|
||||||
return lifeCritical || activateForCost
|
return lifeCritical || activateForCost
|
||||||
|| (ph.getNextTurn().equals(ai) && !ph.getPhase().isBefore(PhaseType.END_OF_TURN))
|
|| (ph.getNextTurn().equals(ai) && !ph.getPhase().isBefore(PhaseType.END_OF_TURN))
|
||||||
|| sa.hasParam("PlayerTurn") || SpellAbilityAi.isSorcerySpeed(sa, ai);
|
|| sa.hasParam("PlayerTurn") || isSorcerySpeed(sa, ai);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -180,8 +180,8 @@ public class LifeGainAi extends SpellAbilityAi {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (SpellAbilityAi.isSorcerySpeed(sa, ai)
|
if (isSorcerySpeed(sa, ai)
|
||||||
|| sa.getSubAbility() != null || SpellAbilityAi.playReusable(ai, sa)) {
|
|| sa.getSubAbility() != null || playReusable(ai, sa)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -144,7 +144,7 @@ public class LifeLoseAi extends SpellAbilityAi {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (SpellAbilityAi.isSorcerySpeed(sa, ai) || sa.hasParam("ActivationPhases") || SpellAbilityAi.playReusable(ai, sa)
|
if (isSorcerySpeed(sa, ai) || sa.hasParam("ActivationPhases") || playReusable(ai, sa)
|
||||||
|| ComputerUtil.activateForCost(sa, ai)) {
|
|| ComputerUtil.activateForCost(sa, ai)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ public class ManifestAi extends SpellAbilityAi {
|
|||||||
if (!buff) {
|
if (!buff) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else if (!SpellAbilityAi.isSorcerySpeed(sa, ai)) {
|
} else if (!isSorcerySpeed(sa, ai)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -152,7 +152,7 @@ public class ManifestAi extends SpellAbilityAi {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Probably should be a little more discerning on playing during OPPs turn
|
// Probably should be a little more discerning on playing during OPPs turn
|
||||||
if (SpellAbilityAi.playReusable(ai, sa)) {
|
if (playReusable(ai, sa)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (game.getPhaseHandler().is(PhaseType.COMBAT_DECLARE_ATTACKERS)) {
|
if (game.getPhaseHandler().is(PhaseType.COMBAT_DECLARE_ATTACKERS)) {
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ public class MillAi extends SpellAbilityAi {
|
|||||||
return (ph.is(PhaseType.MAIN1) || ph.is(PhaseType.MAIN2)) && ph.isPlayerTurn(ai); // Chandra, Torch of Defiance and similar
|
return (ph.is(PhaseType.MAIN1) || ph.is(PhaseType.MAIN2)) && ph.isPlayerTurn(ai); // Chandra, Torch of Defiance and similar
|
||||||
}
|
}
|
||||||
if (!sa.isPwAbility()) { // Planeswalker abilities are only activated at sorcery speed
|
if (!sa.isPwAbility()) { // Planeswalker abilities are only activated at sorcery speed
|
||||||
if ("You".equals(sa.getParam("Defined")) && !(!SpellAbilityAi.isSorcerySpeed(sa, ai) && ph.is(PhaseType.END_OF_TURN)
|
if ("You".equals(sa.getParam("Defined")) && !(!isSorcerySpeed(sa, ai) && ph.is(PhaseType.END_OF_TURN)
|
||||||
&& ph.getNextTurn().equals(ai))) {
|
&& ph.getNextTurn().equals(ai))) {
|
||||||
return false; // only self-mill at opponent EOT
|
return false; // only self-mill at opponent EOT
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,21 +1,15 @@
|
|||||||
package forge.ai.ability;
|
package forge.ai.ability;
|
||||||
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
|
||||||
|
|
||||||
import com.google.common.base.Predicates;
|
import com.google.common.base.Predicates;
|
||||||
import com.google.common.collect.Iterables;
|
import com.google.common.collect.Iterables;
|
||||||
|
|
||||||
import forge.ai.ComputerUtil;
|
import forge.ai.ComputerUtil;
|
||||||
import forge.ai.ComputerUtilCost;
|
import forge.ai.ComputerUtilCost;
|
||||||
import forge.ai.ComputerUtilMana;
|
import forge.ai.ComputerUtilMana;
|
||||||
import forge.ai.SpellAbilityAi;
|
import forge.ai.SpellAbilityAi;
|
||||||
|
import forge.card.CardStateName;
|
||||||
import forge.card.CardType.Supertype;
|
import forge.card.CardType.Supertype;
|
||||||
import forge.card.mana.ManaCost;
|
import forge.card.mana.ManaCost;
|
||||||
import forge.game.card.Card;
|
import forge.game.card.*;
|
||||||
import forge.game.card.CardCollection;
|
|
||||||
import forge.game.card.CardCollectionView;
|
|
||||||
import forge.game.card.CardLists;
|
|
||||||
import forge.game.card.CardPredicates;
|
|
||||||
import forge.game.cost.Cost;
|
import forge.game.cost.Cost;
|
||||||
import forge.game.keyword.KeywordInterface;
|
import forge.game.keyword.KeywordInterface;
|
||||||
import forge.game.mana.ManaCostBeingPaid;
|
import forge.game.mana.ManaCostBeingPaid;
|
||||||
@@ -24,6 +18,7 @@ import forge.game.phase.PhaseType;
|
|||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
import forge.game.zone.ZoneType;
|
import forge.game.zone.ZoneType;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
public class PermanentAi extends SpellAbilityAi {
|
public class PermanentAi extends SpellAbilityAi {
|
||||||
|
|
||||||
@@ -49,19 +44,19 @@ public class PermanentAi extends SpellAbilityAi {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected boolean checkApiLogic(final Player ai, final SpellAbility sa) {
|
protected boolean checkApiLogic(final Player ai, final SpellAbility sa) {
|
||||||
final Card card = sa.getHostCard();
|
final Card source = sa.getHostCard();
|
||||||
|
|
||||||
// check on legendary
|
// check on legendary
|
||||||
if (!card.ignoreLegendRule() && ai.isCardInPlay(card.getName())) {
|
if (!source.ignoreLegendRule() && ai.isCardInPlay(source.getName())) {
|
||||||
// TODO check the risk we'd lose the effect with bad timing
|
// TODO check the risk we'd lose the effect with bad timing
|
||||||
if (!card.hasSVar("AILegendaryException")) {
|
if (!source.hasSVar("AILegendaryException")) {
|
||||||
// AiPlayDecision.WouldDestroyLegend
|
// AiPlayDecision.WouldDestroyLegend
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
String specialRule = card.getSVar("AILegendaryException");
|
String specialRule = source.getSVar("AILegendaryException");
|
||||||
if ("TwoCopiesAllowed".equals(specialRule)) {
|
if ("TwoCopiesAllowed".equals(specialRule)) {
|
||||||
// One extra copy allowed on the battlefield, e.g. Brothers Yamazaki
|
// One extra copy allowed on the battlefield, e.g. Brothers Yamazaki
|
||||||
if (CardLists.count(ai.getCardsIn(ZoneType.Battlefield), CardPredicates.nameEquals(card.getName())) > 1) {
|
if (CardLists.count(ai.getCardsIn(ZoneType.Battlefield), CardPredicates.nameEquals(source.getName())) > 1) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else if ("AlwaysAllowed".equals(specialRule)) {
|
} else if ("AlwaysAllowed".equals(specialRule)) {
|
||||||
@@ -73,23 +68,7 @@ public class PermanentAi extends SpellAbilityAi {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* -- not used anymore after Ixalan (Planeswalkers are now legendary, not unique by subtype) --
|
if (source.getType().hasSupertype(Supertype.World)) {
|
||||||
if (card.isPlaneswalker()) {
|
|
||||||
CardCollection list = CardLists.filter(ai.getCardsIn(ZoneType.Battlefield),
|
|
||||||
CardPredicates.Presets.PLANESWALKERS);
|
|
||||||
for (String type : card.getType().getSubtypes()) { // determine
|
|
||||||
// planewalker
|
|
||||||
// subtype
|
|
||||||
final CardCollection cl = CardLists.getType(list, type);
|
|
||||||
if (!cl.isEmpty()) {
|
|
||||||
// AiPlayDecision.WouldDestroyOtherPlaneswalker
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}*/
|
|
||||||
|
|
||||||
if (card.getType().hasSupertype(Supertype.World)) {
|
|
||||||
CardCollection list = CardLists.getType(ai.getCardsIn(ZoneType.Battlefield), "World");
|
CardCollection list = CardLists.getType(ai.getCardsIn(ZoneType.Battlefield), "World");
|
||||||
if (!list.isEmpty()) {
|
if (!list.isEmpty()) {
|
||||||
// AiPlayDecision.WouldDestroyWorldEnchantment
|
// AiPlayDecision.WouldDestroyWorldEnchantment
|
||||||
@@ -101,7 +80,6 @@ public class PermanentAi extends SpellAbilityAi {
|
|||||||
if (mana.countX() > 0) {
|
if (mana.countX() > 0) {
|
||||||
// Set PayX here to maximum value.
|
// Set PayX here to maximum value.
|
||||||
final int xPay = ComputerUtilCost.getMaxXValue(sa, ai, false);
|
final int xPay = ComputerUtilCost.getMaxXValue(sa, ai, false);
|
||||||
final Card source = sa.getHostCard();
|
|
||||||
if (source.hasConverge()) {
|
if (source.hasConverge()) {
|
||||||
int nColors = ComputerUtilMana.getConvergeCount(sa, ai);
|
int nColors = ComputerUtilMana.getConvergeCount(sa, ai);
|
||||||
for (int i = 1; i <= xPay; i++) {
|
for (int i = 1; i <= xPay; i++) {
|
||||||
@@ -123,7 +101,7 @@ public class PermanentAi extends SpellAbilityAi {
|
|||||||
}
|
}
|
||||||
} else if (mana.isZero()) {
|
} else if (mana.isZero()) {
|
||||||
// if mana is zero, but card mana cost does have X, then something is wrong
|
// if mana is zero, but card mana cost does have X, then something is wrong
|
||||||
ManaCost cardCost = card.getManaCost();
|
ManaCost cardCost = source.getManaCost();
|
||||||
if (cardCost != null && cardCost.countX() > 0) {
|
if (cardCost != null && cardCost.countX() > 0) {
|
||||||
// AiPlayDecision.CantPlayAi
|
// AiPlayDecision.CantPlayAi
|
||||||
return false;
|
return false;
|
||||||
@@ -143,6 +121,38 @@ public class PermanentAi extends SpellAbilityAi {
|
|||||||
sa.setXManaCostPaid(xPay);
|
sa.setXManaCostPaid(xPay);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ("ChaliceOfTheVoid".equals(source.getSVar("AICurseEffect"))) {
|
||||||
|
int maxX = sa.getXManaCostPaid(); // as set above
|
||||||
|
CardCollection otherChalices = CardLists.filter(ai.getGame().getCardsIn(ZoneType.Battlefield), CardPredicates.nameEquals("Chalice of the Void"));
|
||||||
|
outer: for (int i = 0; i <= maxX; i++) {
|
||||||
|
for (Card chalice : otherChalices) {
|
||||||
|
if (chalice.getCounters(CounterEnumType.CHARGE) == i) {
|
||||||
|
continue outer; // already disabled, no point in adding another one
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// assume the AI knows the deck lists of its opponents and if we see a card in a certain zone except for the library or hand,
|
||||||
|
// it likely won't be cast unless it's bounced back (ideally, this should also somehow account for hidden information such as face down cards in exile)
|
||||||
|
final int manaValue = i;
|
||||||
|
CardCollection aiCards = CardLists.filter(ai.getAllCards(), card -> (card.isInZone(ZoneType.Library) || !card.isInZone(ZoneType.Hand))
|
||||||
|
&& card.getState(CardStateName.Original).getManaCost() != null
|
||||||
|
&& card.getState(CardStateName.Original).getManaCost().getCMC() == manaValue);
|
||||||
|
CardCollection oppCards = CardLists.filter(ai.getStrongestOpponent().getAllCards(), card -> (card.isInZone(ZoneType.Library) || !card.isInZone(ZoneType.Hand))
|
||||||
|
&& card.getState(CardStateName.Original).getManaCost() != null
|
||||||
|
&& card.getState(CardStateName.Original).getManaCost().getCMC() == manaValue);
|
||||||
|
if (manaValue == 0) {
|
||||||
|
aiCards = CardLists.filter(aiCards, Predicates.not(CardPredicates.isType("Land")));
|
||||||
|
oppCards = CardLists.filter(oppCards, Predicates.not(CardPredicates.isType("Land")));
|
||||||
|
// also filter out other Chalices in our own deck
|
||||||
|
aiCards = CardLists.filter(aiCards, Predicates.not(CardPredicates.nameEquals("Chalice of the Void")));
|
||||||
|
}
|
||||||
|
if (oppCards.size() > 3 && oppCards.size() >= aiCards.size() * 2) {
|
||||||
|
sa.setXManaCostPaid(manaValue);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (sa.hasParam("Announce") && sa.getParam("Announce").startsWith("Multikicker")) {
|
if (sa.hasParam("Announce") && sa.getParam("Announce").startsWith("Multikicker")) {
|
||||||
// String announce = sa.getParam("Announce");
|
// String announce = sa.getParam("Announce");
|
||||||
ManaCost mkCost = sa.getMultiKickerManaCost();
|
ManaCost mkCost = sa.getMultiKickerManaCost();
|
||||||
@@ -152,13 +162,13 @@ public class PermanentAi extends SpellAbilityAi {
|
|||||||
mCost = ManaCost.combine(mCost, mkCost);
|
mCost = ManaCost.combine(mCost, mkCost);
|
||||||
ManaCostBeingPaid mcbp = new ManaCostBeingPaid(mCost);
|
ManaCostBeingPaid mcbp = new ManaCostBeingPaid(mCost);
|
||||||
if (!ComputerUtilMana.canPayManaCost(mcbp, sa, ai, false)) {
|
if (!ComputerUtilMana.canPayManaCost(mcbp, sa, ai, false)) {
|
||||||
card.setKickerMagnitude(i);
|
source.setKickerMagnitude(i);
|
||||||
sa.setSVar("Multikicker", String.valueOf(i));
|
sa.setSVar("Multikicker", String.valueOf(i));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
card.setKickerMagnitude(i + 1);
|
source.setKickerMagnitude(i + 1);
|
||||||
}
|
}
|
||||||
if (isZeroCost && card.getKickerMagnitude() == 0) {
|
if (isZeroCost && source.getKickerMagnitude() == 0) {
|
||||||
// Bail if the card cost was {0} and no multikicker was paid (e.g. Everflowing Chalice).
|
// Bail if the card cost was {0} and no multikicker was paid (e.g. Everflowing Chalice).
|
||||||
// TODO: update this if there's ever a card where it makes sense to play it for {0} with no multikicker
|
// TODO: update this if there's ever a card where it makes sense to play it for {0} with no multikicker
|
||||||
return false;
|
return false;
|
||||||
@@ -166,13 +176,13 @@ public class PermanentAi extends SpellAbilityAi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// don't play cards without being able to pay the upkeep for
|
// don't play cards without being able to pay the upkeep for
|
||||||
for (KeywordInterface inst : card.getKeywords()) {
|
for (KeywordInterface inst : source.getKeywords()) {
|
||||||
String ability = inst.getOriginal();
|
String ability = inst.getOriginal();
|
||||||
if (ability.startsWith("UpkeepCost")) {
|
if (ability.startsWith("UpkeepCost")) {
|
||||||
final String[] k = ability.split(":");
|
final String[] k = ability.split(":");
|
||||||
final String costs = k[1];
|
final String costs = k[1];
|
||||||
|
|
||||||
final SpellAbility emptyAbility = new SpellAbility.EmptySa(card, ai);
|
final SpellAbility emptyAbility = new SpellAbility.EmptySa(source, ai);
|
||||||
emptyAbility.setPayCosts(new Cost(costs, true));
|
emptyAbility.setPayCosts(new Cost(costs, true));
|
||||||
emptyAbility.setTargetRestrictions(sa.getTargetRestrictions());
|
emptyAbility.setTargetRestrictions(sa.getTargetRestrictions());
|
||||||
emptyAbility.setCardState(sa.getCardState());
|
emptyAbility.setCardState(sa.getCardState());
|
||||||
@@ -186,8 +196,8 @@ public class PermanentAi extends SpellAbilityAi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// check for specific AI preferences
|
// check for specific AI preferences
|
||||||
if (card.hasSVar("AICastPreference")) {
|
if (source.hasSVar("AICastPreference")) {
|
||||||
String pref = card.getSVar("AICastPreference");
|
String pref = source.getSVar("AICastPreference");
|
||||||
String[] groups = StringUtils.split(pref, "|");
|
String[] groups = StringUtils.split(pref, "|");
|
||||||
boolean dontCast = false;
|
boolean dontCast = false;
|
||||||
for (String group : groups) {
|
for (String group : groups) {
|
||||||
@@ -205,7 +215,7 @@ public class PermanentAi extends SpellAbilityAi {
|
|||||||
// Only cast unless there are X or more cards like this on the battlefield under AI control already,
|
// Only cast unless there are X or more cards like this on the battlefield under AI control already,
|
||||||
CardCollectionView valid = param.contains("Globally") ? ai.getGame().getCardsIn(ZoneType.Battlefield)
|
CardCollectionView valid = param.contains("Globally") ? ai.getGame().getCardsIn(ZoneType.Battlefield)
|
||||||
: ai.getCardsIn(ZoneType.Battlefield);
|
: ai.getCardsIn(ZoneType.Battlefield);
|
||||||
CardCollection ctrld = CardLists.filter(valid, CardPredicates.nameEquals(card.getName()));
|
CardCollection ctrld = CardLists.filter(valid, CardPredicates.nameEquals(source.getName()));
|
||||||
|
|
||||||
int numControlled = 0;
|
int numControlled = 0;
|
||||||
if (param.endsWith("WithoutOppAuras")) {
|
if (param.endsWith("WithoutOppAuras")) {
|
||||||
@@ -242,7 +252,7 @@ public class PermanentAi extends SpellAbilityAi {
|
|||||||
// if there are X-1 mana sources in play but the AI has an extra land in hand
|
// if there are X-1 mana sources in play but the AI has an extra land in hand
|
||||||
CardCollection m = ComputerUtilMana.getAvailableManaSources(ai, true);
|
CardCollection m = ComputerUtilMana.getAvailableManaSources(ai, true);
|
||||||
int extraMana = CardLists.count(ai.getCardsIn(ZoneType.Hand), CardPredicates.Presets.LANDS) > 0 ? 1 : 0;
|
int extraMana = CardLists.count(ai.getCardsIn(ZoneType.Hand), CardPredicates.Presets.LANDS) > 0 ? 1 : 0;
|
||||||
if (card.getName().equals("Illusions of Grandeur")) {
|
if (source.getName().equals("Illusions of Grandeur")) {
|
||||||
// TODO: this is currently hardcoded for specific Illusions-Donate cost reduction spells, need to make this generic.
|
// TODO: this is currently hardcoded for specific Illusions-Donate cost reduction spells, need to make this generic.
|
||||||
extraMana += Math.min(3, CardLists.filter(ai.getCardsIn(ZoneType.Battlefield), Predicates.or(CardPredicates.nameEquals("Sapphire Medallion"), CardPredicates.nameEquals("Helm of Awakening"))).size()) * 2; // each cost-reduction spell accounts for {1} in both Illusions and Donate
|
extraMana += Math.min(3, CardLists.filter(ai.getCardsIn(ZoneType.Battlefield), Predicates.or(CardPredicates.nameEquals("Sapphire Medallion"), CardPredicates.nameEquals("Helm of Awakening"))).size()) * 2; // each cost-reduction spell accounts for {1} in both Illusions and Donate
|
||||||
}
|
}
|
||||||
@@ -260,7 +270,7 @@ public class PermanentAi extends SpellAbilityAi {
|
|||||||
break; // disregard other preferences, always cast as a last resort
|
break; // disregard other preferences, always cast as a last resort
|
||||||
}
|
}
|
||||||
} else if (param.equals("OnlyFromZone")) {
|
} else if (param.equals("OnlyFromZone")) {
|
||||||
if (!card.getZone().getZoneType().toString().equals(value)) {
|
if (!source.getZone().getZoneType().toString().equals(value)) {
|
||||||
dontCast = true;
|
dontCast = true;
|
||||||
break; // limit casting to a specific zone only
|
break; // limit casting to a specific zone only
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -235,11 +235,19 @@ public class PermanentCreatureAi extends PermanentAi {
|
|||||||
* worth it. Not sure what 4. is for. 5. needs to be updated to ensure
|
* worth it. Not sure what 4. is for. 5. needs to be updated to ensure
|
||||||
* that the net toughness is still positive after static effects.
|
* that the net toughness is still positive after static effects.
|
||||||
*/
|
*/
|
||||||
|
// AiPlayDecision.WouldBecomeZeroToughnessCreature
|
||||||
|
if (card.hasStartOfKeyword("etbCounter") || mana.countX() != 0
|
||||||
|
|| card.hasETBTrigger(false) || card.hasETBReplacement() || card.hasSVar("NoZeroToughnessAI")) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
final Card copy = CardUtil.getLKICopy(card);
|
final Card copy = CardUtil.getLKICopy(card);
|
||||||
ComputerUtilCard.applyStaticContPT(game, copy, null);
|
ComputerUtilCard.applyStaticContPT(game, copy, null);
|
||||||
// AiPlayDecision.WouldBecomeZeroToughnessCreature
|
if (copy.getNetToughness() > 0) {
|
||||||
return copy.getNetToughness() > 0 || copy.hasStartOfKeyword("etbCounter") || mana.countX() != 0
|
return true;
|
||||||
|| copy.hasETBTrigger(false) || copy.hasETBReplacement() || copy.hasSVar("NoZeroToughnessAI");
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -163,7 +163,7 @@ public class ProtectAi extends SpellAbilityAi {
|
|||||||
protected boolean checkPhaseRestrictions(final Player ai, final SpellAbility sa, final PhaseHandler ph) {
|
protected boolean checkPhaseRestrictions(final Player ai, final SpellAbility sa, final PhaseHandler ph) {
|
||||||
final boolean notAiMain1 = !(ph.getPlayerTurn() == ai && ph.getPhase() == PhaseType.MAIN1);
|
final boolean notAiMain1 = !(ph.getPlayerTurn() == ai && ph.getPhase() == PhaseType.MAIN1);
|
||||||
// sorceries can only give protection in order to create an unblockable attacker
|
// sorceries can only give protection in order to create an unblockable attacker
|
||||||
return !SpellAbilityAi.isSorcerySpeed(sa, ai) || !notAiMain1;
|
return !isSorcerySpeed(sa, ai) || !notAiMain1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -87,7 +87,7 @@ public class PumpAi extends PumpAiBase {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return SpellAbilityAi.isSorcerySpeed(sa, ai) || (ph.getNextTurn().equals(ai) && !ph.getPhase().isBefore(PhaseType.END_OF_TURN));
|
return isSorcerySpeed(sa, ai) || (ph.getNextTurn().equals(ai) && !ph.getPhase().isBefore(PhaseType.END_OF_TURN));
|
||||||
} else if (logic.equals("Aristocrat")) {
|
} else if (logic.equals("Aristocrat")) {
|
||||||
final boolean isThreatened = ComputerUtil.predictThreatenedObjects(ai, null, true).contains(sa.getHostCard());
|
final boolean isThreatened = ComputerUtil.predictThreatenedObjects(ai, null, true).contains(sa.getHostCard());
|
||||||
if (!ph.is(PhaseType.COMBAT_DECLARE_BLOCKERS) && !isThreatened) {
|
if (!ph.is(PhaseType.COMBAT_DECLARE_BLOCKERS) && !isThreatened) {
|
||||||
@@ -118,7 +118,7 @@ public class PumpAi extends PumpAiBase {
|
|||||||
|| ph.getPhase().isAfter(PhaseType.COMBAT_DECLARE_BLOCKERS))) {
|
|| ph.getPhase().isAfter(PhaseType.COMBAT_DECLARE_BLOCKERS))) {
|
||||||
// Instant-speed pumps should not be cast outside of combat when the
|
// Instant-speed pumps should not be cast outside of combat when the
|
||||||
// stack is empty
|
// stack is empty
|
||||||
return sa.isCurse() || SpellAbilityAi.isSorcerySpeed(sa, ai) || main1Preferred;
|
return sa.isCurse() || isSorcerySpeed(sa, ai) || main1Preferred;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -367,7 +367,7 @@ public class PumpAi extends PumpAiBase {
|
|||||||
if (ComputerUtilCard.shouldPumpCard(ai, sa, card, defense, attack, keywords, false)) {
|
if (ComputerUtilCard.shouldPumpCard(ai, sa, card, defense, attack, keywords, false)) {
|
||||||
return true;
|
return true;
|
||||||
} else if (containsUsefulKeyword(ai, keywords, card, sa, attack)) {
|
} else if (containsUsefulKeyword(ai, keywords, card, sa, attack)) {
|
||||||
if (game.getPhaseHandler().isPreCombatMain() && SpellAbilityAi.isSorcerySpeed(sa, ai) ||
|
if (game.getPhaseHandler().isPreCombatMain() && isSorcerySpeed(sa, ai) ||
|
||||||
game.getPhaseHandler().is(PhaseType.COMBAT_DECLARE_ATTACKERS, ai) ||
|
game.getPhaseHandler().is(PhaseType.COMBAT_DECLARE_ATTACKERS, ai) ||
|
||||||
game.getPhaseHandler().is(PhaseType.COMBAT_BEGIN, ai)) {
|
game.getPhaseHandler().is(PhaseType.COMBAT_BEGIN, ai)) {
|
||||||
Card pumped = ComputerUtilCard.getPumpedCreature(ai, sa, card, 0, 0, keywords);
|
Card pumped = ComputerUtilCard.getPumpedCreature(ai, sa, card, 0, 0, keywords);
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import com.google.common.collect.Iterables;
|
|||||||
|
|
||||||
import forge.ai.AiPlayDecision;
|
import forge.ai.AiPlayDecision;
|
||||||
import forge.ai.PlayerControllerAi;
|
import forge.ai.PlayerControllerAi;
|
||||||
import forge.ai.SpellAbilityAi;
|
|
||||||
import forge.game.ability.AbilityUtils;
|
import forge.game.ability.AbilityUtils;
|
||||||
import forge.game.card.Card;
|
import forge.game.card.Card;
|
||||||
import forge.game.cost.Cost;
|
import forge.game.cost.Cost;
|
||||||
@@ -26,7 +25,7 @@ public class RevealAi extends RevealAiBase {
|
|||||||
|
|
||||||
boolean randomReturn = MyRandom.getRandom().nextFloat() <= Math.pow(.667, sa.getActivationsThisTurn() + 1);
|
boolean randomReturn = MyRandom.getRandom().nextFloat() <= Math.pow(.667, sa.getActivationsThisTurn() + 1);
|
||||||
|
|
||||||
if (SpellAbilityAi.playReusable(ai, sa)) {
|
if (playReusable(ai, sa)) {
|
||||||
randomReturn = true;
|
randomReturn = true;
|
||||||
}
|
}
|
||||||
return randomReturn;
|
return randomReturn;
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package forge.ai.ability;
|
package forge.ai.ability;
|
||||||
|
|
||||||
import forge.ai.SpellAbilityAi;
|
|
||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
import forge.util.MyRandom;
|
import forge.util.MyRandom;
|
||||||
@@ -20,7 +19,7 @@ public class RevealHandAi extends RevealAiBase {
|
|||||||
|
|
||||||
boolean randomReturn = MyRandom.getRandom().nextFloat() <= Math.pow(.667, sa.getActivationsThisTurn() + 1);
|
boolean randomReturn = MyRandom.getRandom().nextFloat() <= Math.pow(.667, sa.getActivationsThisTurn() + 1);
|
||||||
|
|
||||||
if (SpellAbilityAi.playReusable(ai, sa)) {
|
if (playReusable(ai, sa)) {
|
||||||
randomReturn = true;
|
randomReturn = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ public class ScryAi extends SpellAbilityAi {
|
|||||||
// even if there's no mana cost.
|
// even if there's no mana cost.
|
||||||
if (sa.getPayCosts().hasTapCost()
|
if (sa.getPayCosts().hasTapCost()
|
||||||
&& (sa.getPayCosts().hasManaCost() || (sa.getHostCard() != null && sa.getHostCard().isCreature()))
|
&& (sa.getPayCosts().hasManaCost() || (sa.getHostCard() != null && sa.getHostCard().isCreature()))
|
||||||
&& !SpellAbilityAi.isSorcerySpeed(sa, ai)) {
|
&& !isSorcerySpeed(sa, ai)) {
|
||||||
return ph.getNextTurn() == ai && ph.is(PhaseType.END_OF_TURN);
|
return ph.getNextTurn() == ai && ph.is(PhaseType.END_OF_TURN);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -69,7 +69,7 @@ public class ScryAi extends SpellAbilityAi {
|
|||||||
|
|
||||||
// in the playerturn Scry should only be done in Main1 or in upkeep if able
|
// in the playerturn Scry should only be done in Main1 or in upkeep if able
|
||||||
if (ph.isPlayerTurn(ai)) {
|
if (ph.isPlayerTurn(ai)) {
|
||||||
if (SpellAbilityAi.isSorcerySpeed(sa, ai)) {
|
if (isSorcerySpeed(sa, ai)) {
|
||||||
return ph.is(PhaseType.MAIN1) || sa.isPwAbility();
|
return ph.is(PhaseType.MAIN1) || sa.isPwAbility();
|
||||||
} else {
|
} else {
|
||||||
return ph.is(PhaseType.UPKEEP);
|
return ph.is(PhaseType.UPKEEP);
|
||||||
@@ -121,12 +121,12 @@ public class ScryAi extends SpellAbilityAi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
double chance = .4; // 40 percent chance of milling with instant speed stuff
|
double chance = .4; // 40 percent chance of milling with instant speed stuff
|
||||||
if (SpellAbilityAi.isSorcerySpeed(sa, ai)) {
|
if (isSorcerySpeed(sa, ai)) {
|
||||||
chance = .667; // 66.7% chance for sorcery speed (since it will never activate EOT)
|
chance = .667; // 66.7% chance for sorcery speed (since it will never activate EOT)
|
||||||
}
|
}
|
||||||
boolean randomReturn = MyRandom.getRandom().nextFloat() <= Math.pow(chance, sa.getActivationsThisTurn() + 1);
|
boolean randomReturn = MyRandom.getRandom().nextFloat() <= Math.pow(chance, sa.getActivationsThisTurn() + 1);
|
||||||
|
|
||||||
if (SpellAbilityAi.playReusable(ai, sa)) {
|
if (playReusable(ai, sa)) {
|
||||||
randomReturn = true;
|
randomReturn = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -54,13 +54,13 @@ public class SurveilAi extends SpellAbilityAi {
|
|||||||
// even if there's no mana cost.
|
// even if there's no mana cost.
|
||||||
if (sa.getPayCosts().hasTapCost()
|
if (sa.getPayCosts().hasTapCost()
|
||||||
&& (sa.getPayCosts().hasManaCost() || (sa.getHostCard() != null && sa.getHostCard().isCreature()))
|
&& (sa.getPayCosts().hasManaCost() || (sa.getHostCard() != null && sa.getHostCard().isCreature()))
|
||||||
&& !SpellAbilityAi.isSorcerySpeed(sa, ai)) {
|
&& !isSorcerySpeed(sa, ai)) {
|
||||||
return ph.getNextTurn() == ai && ph.is(PhaseType.END_OF_TURN);
|
return ph.getNextTurn() == ai && ph.is(PhaseType.END_OF_TURN);
|
||||||
}
|
}
|
||||||
|
|
||||||
// in the player's turn Surveil should only be done in Main1 or in Upkeep if able
|
// in the player's turn Surveil should only be done in Main1 or in Upkeep if able
|
||||||
if (ph.isPlayerTurn(ai)) {
|
if (ph.isPlayerTurn(ai)) {
|
||||||
if (SpellAbilityAi.isSorcerySpeed(sa, ai)) {
|
if (isSorcerySpeed(sa, ai)) {
|
||||||
return ph.is(PhaseType.MAIN1) || sa.isPwAbility();
|
return ph.is(PhaseType.MAIN1) || sa.isPwAbility();
|
||||||
} else {
|
} else {
|
||||||
return ph.is(PhaseType.UPKEEP);
|
return ph.is(PhaseType.UPKEEP);
|
||||||
@@ -104,12 +104,12 @@ public class SurveilAi extends SpellAbilityAi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
double chance = .4; // 40 percent chance for instant speed
|
double chance = .4; // 40 percent chance for instant speed
|
||||||
if (SpellAbilityAi.isSorcerySpeed(sa, ai)) {
|
if (isSorcerySpeed(sa, ai)) {
|
||||||
chance = .667; // 66.7% chance for sorcery speed (since it will never activate EOT)
|
chance = .667; // 66.7% chance for sorcery speed (since it will never activate EOT)
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean randomReturn = MyRandom.getRandom().nextFloat() <= Math.pow(chance, sa.getActivationsThisTurn() + 1);
|
boolean randomReturn = MyRandom.getRandom().nextFloat() <= Math.pow(chance, sa.getActivationsThisTurn() + 1);
|
||||||
if (SpellAbilityAi.playReusable(ai, sa)) {
|
if (playReusable(ai, sa)) {
|
||||||
randomReturn = true;
|
randomReturn = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ public class TapAi extends TapAiBase {
|
|||||||
if (turn.isOpponentOf(ai) && phase.getPhase().isBefore(PhaseType.COMBAT_DECLARE_ATTACKERS)) {
|
if (turn.isOpponentOf(ai) && phase.getPhase().isBefore(PhaseType.COMBAT_DECLARE_ATTACKERS)) {
|
||||||
// Tap things down if it's Human's turn
|
// Tap things down if it's Human's turn
|
||||||
} else if (turn.equals(ai)) {
|
} else if (turn.equals(ai)) {
|
||||||
if (SpellAbilityAi.isSorcerySpeed(sa, ai) && phase.getPhase().isBefore(PhaseType.COMBAT_BEGIN)) {
|
if (isSorcerySpeed(sa, ai) && phase.getPhase().isBefore(PhaseType.COMBAT_BEGIN)) {
|
||||||
// Cast it if it's a sorcery.
|
// Cast it if it's a sorcery.
|
||||||
} else if (phase.getPhase().isBefore(PhaseType.COMBAT_DECLARE_BLOCKERS)) {
|
} else if (phase.getPhase().isBefore(PhaseType.COMBAT_DECLARE_BLOCKERS)) {
|
||||||
// Aggro Brains are willing to use TapEffects aggressively instead of defensively
|
// Aggro Brains are willing to use TapEffects aggressively instead of defensively
|
||||||
@@ -33,7 +33,7 @@ public class TapAi extends TapAiBase {
|
|||||||
// Don't tap down after blockers
|
// Don't tap down after blockers
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else if (!SpellAbilityAi.playReusable(ai, sa)) {
|
} else if (!playReusable(ai, sa)) {
|
||||||
// Generally don't want to tap things with an Instant during Players turn outside of combat
|
// Generally don't want to tap things with an Instant during Players turn outside of combat
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -133,7 +133,7 @@ public class TokenAi extends SpellAbilityAi {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ((ph.isPlayerTurn(ai) || ph.getPhase().isBefore(PhaseType.COMBAT_DECLARE_ATTACKERS))
|
if ((ph.isPlayerTurn(ai) || ph.getPhase().isBefore(PhaseType.COMBAT_DECLARE_ATTACKERS))
|
||||||
&& !sa.hasParam("ActivationPhases") && !sa.hasParam("PlayerTurn") && !SpellAbilityAi.isSorcerySpeed(sa, ai)
|
&& !sa.hasParam("ActivationPhases") && !sa.hasParam("PlayerTurn") && !isSorcerySpeed(sa, ai)
|
||||||
&& !haste && !pwMinus) {
|
&& !haste && !pwMinus) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ public class VentureAi extends SpellAbilityAi {
|
|||||||
// If this has a mana cost, do it at opponent's EOT if able to prevent spending mana early; if sorcery, do it in Main2
|
// If this has a mana cost, do it at opponent's EOT if able to prevent spending mana early; if sorcery, do it in Main2
|
||||||
PhaseHandler ph = aiPlayer.getGame().getPhaseHandler();
|
PhaseHandler ph = aiPlayer.getGame().getPhaseHandler();
|
||||||
if (sa.getPayCosts().hasManaCost() || sa.getPayCosts().hasTapCost()) {
|
if (sa.getPayCosts().hasManaCost() || sa.getPayCosts().hasTapCost()) {
|
||||||
if (SpellAbilityAi.isSorcerySpeed(sa, aiPlayer)) {
|
if (isSorcerySpeed(sa, aiPlayer)) {
|
||||||
return ph.is(PhaseType.MAIN2, aiPlayer);
|
return ph.is(PhaseType.MAIN2, aiPlayer);
|
||||||
} else {
|
} else {
|
||||||
return ph.is(PhaseType.END_OF_TURN) && ph.getNextTurn() == aiPlayer;
|
return ph.is(PhaseType.END_OF_TURN) && ph.getNextTurn() == aiPlayer;
|
||||||
|
|||||||
@@ -1582,10 +1582,6 @@ public class AbilityUtils {
|
|||||||
host.addRemembered(sa.getTargets());
|
host.addRemembered(sa.getTargets());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sa.hasParam("ImprintTargets") && sa.usesTargeting()) {
|
|
||||||
host.addImprintedCards(sa.getTargets().getTargetCards());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sa.hasParam("RememberCostMana")) {
|
if (sa.hasParam("RememberCostMana")) {
|
||||||
host.clearRemembered();
|
host.clearRemembered();
|
||||||
ManaCostBeingPaid activationMana = new ManaCostBeingPaid(sa.getPayCosts().getTotalMana());
|
ManaCostBeingPaid activationMana = new ManaCostBeingPaid(sa.getPayCosts().getTotalMana());
|
||||||
|
|||||||
@@ -62,10 +62,6 @@ public class FightEffect extends DamageBaseEffect {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sa.hasParam("RememberObjects")) {
|
|
||||||
host.addRemembered(AbilityUtils.getDefinedObjects(host, sa.getParam("RememberObjects"), sa));
|
|
||||||
}
|
|
||||||
|
|
||||||
Player controller = host.getController();
|
Player controller = host.getController();
|
||||||
boolean isOptional = sa.hasParam("Optional");
|
boolean isOptional = sa.hasParam("Optional");
|
||||||
|
|
||||||
@@ -147,7 +143,6 @@ public class FightEffect extends DamageBaseEffect {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void dealDamage(final SpellAbility sa, Card fighterA, Card fighterB) {
|
private void dealDamage(final SpellAbility sa, Card fighterA, Card fighterB) {
|
||||||
|
|
||||||
boolean usedDamageMap = true;
|
boolean usedDamageMap = true;
|
||||||
CardDamageMap damageMap = sa.getDamageMap();
|
CardDamageMap damageMap = sa.getDamageMap();
|
||||||
CardDamageMap preventMap = sa.getPreventMap();
|
CardDamageMap preventMap = sa.getPreventMap();
|
||||||
|
|||||||
@@ -70,7 +70,11 @@ public class StaticAbilityDisableTriggers {
|
|||||||
|
|
||||||
if (trigMode.equals(TriggerType.ChangesZone)) {
|
if (trigMode.equals(TriggerType.ChangesZone)) {
|
||||||
// Cause of the trigger – the card changing zones
|
// Cause of the trigger – the card changing zones
|
||||||
if (!stAb.matchesValidParam("ValidCause", runParams.get(AbilityKey.Card))) {
|
Card moved = (Card) runParams.get(AbilityKey.Card);
|
||||||
|
if ("Battlefield".equals(regtrig.getParam("Origin"))) {
|
||||||
|
moved = (Card) runParams.get(AbilityKey.CardLKI);
|
||||||
|
}
|
||||||
|
if (!stAb.matchesValidParam("ValidCause", moved)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!stAb.matchesValidParam("Destination", runParams.get(AbilityKey.Destination))) {
|
if (!stAb.matchesValidParam("Destination", runParams.get(AbilityKey.Destination))) {
|
||||||
|
|||||||
@@ -89,7 +89,11 @@ public class StaticAbilityPanharmonicon {
|
|||||||
|
|
||||||
if (trigMode.equals(TriggerType.ChangesZone)) {
|
if (trigMode.equals(TriggerType.ChangesZone)) {
|
||||||
// Cause of the trigger – the card changing zones
|
// Cause of the trigger – the card changing zones
|
||||||
if (!stAb.matchesValidParam("ValidCause", runParams.get(AbilityKey.Card))) {
|
Card moved = (Card) runParams.get(AbilityKey.Card);
|
||||||
|
if ("Battlefield".equals(trigger.getParam("Origin"))) {
|
||||||
|
moved = (Card) runParams.get(AbilityKey.CardLKI);
|
||||||
|
}
|
||||||
|
if (!stAb.matchesValidParam("ValidCause", moved)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!stAb.matchesValidParam("Origin", runParams.get(AbilityKey.Origin))) {
|
if (!stAb.matchesValidParam("Origin", runParams.get(AbilityKey.Origin))) {
|
||||||
|
|||||||
@@ -120,19 +120,8 @@ public class TriggerChangesZone extends Trigger {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hasParam("ValidCause")) {
|
if (!matchesValidParam("ValidCause", runParams.get(AbilityKey.Cause))) {
|
||||||
if (!runParams.containsKey(AbilityKey.Cause)) {
|
return false;
|
||||||
return false;
|
|
||||||
}
|
|
||||||
SpellAbility cause = (SpellAbility) runParams.get(AbilityKey.Cause);
|
|
||||||
if (cause == null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (!matchesValid(cause, getParam("ValidCause").split(","))) {
|
|
||||||
if (!matchesValid(cause.getHostCard(), getParam("ValidCause").split(","))) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hasParam("Fizzle")) {
|
if (hasParam("Fizzle")) {
|
||||||
|
|||||||
@@ -25,7 +25,9 @@ public class TriggerChangesZoneAll extends Trigger {
|
|||||||
|
|
||||||
if (!matchesValidParam("ValidCause", runParams.get(AbilityKey.Cause))) {
|
if (!matchesValidParam("ValidCause", runParams.get(AbilityKey.Cause))) {
|
||||||
return false;
|
return false;
|
||||||
} else if (hasParam("ValidAmount")) {
|
}
|
||||||
|
|
||||||
|
if (hasParam("ValidAmount")) {
|
||||||
int right = AbilityUtils.calculateAmount(hostCard, getParam("ValidAmount").substring(2), this);
|
int right = AbilityUtils.calculateAmount(hostCard, getParam("ValidAmount").substring(2), this);
|
||||||
if (!Expressions.compare(this.filterCards(table).size(), getParam("ValidAmount").substring(0, 2), right)) { return false; }
|
if (!Expressions.compare(this.filterCards(table).size(), getParam("ValidAmount").substring(0, 2), right)) { return false; }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,5 +7,5 @@ SVar:TrigCounter:DB$ Counter | Defined$ TriggeredSpellAbility
|
|||||||
SVar:X:Count$xPaid
|
SVar:X:Count$xPaid
|
||||||
SVar:Y:Count$CardCounters.CHARGE
|
SVar:Y:Count$CardCounters.CHARGE
|
||||||
SVar:AICurseEffect:ChaliceOfTheVoid
|
SVar:AICurseEffect:ChaliceOfTheVoid
|
||||||
AI:RemoveDeck:All
|
AI:RemoveDeck:Random
|
||||||
Oracle:Chalice of the Void enters the battlefield with X charge counters on it.\nWhenever a player casts a spell with mana value equal to the number of charge counters on Chalice of the Void, counter that spell.
|
Oracle:Chalice of the Void enters the battlefield with X charge counters on it.\nWhenever a player casts a spell with mana value equal to the number of charge counters on Chalice of the Void, counter that spell.
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ Types:Legendary Creature Human Shaman
|
|||||||
PT:2/2
|
PT:2/2
|
||||||
T:Mode$ SpellCast | ValidCard$ Card.Red | ValidActivatingPlayer$ You | TriggerZones$ Battlefield | Execute$ TrigUntap | TriggerDescription$ Whenever you cast a red spell, untap CARDNAME.
|
T:Mode$ SpellCast | ValidCard$ Card.Red | ValidActivatingPlayer$ You | TriggerZones$ Battlefield | Execute$ TrigUntap | TriggerDescription$ Whenever you cast a red spell, untap CARDNAME.
|
||||||
SVar:TrigUntap:DB$ Untap | Defined$ Self
|
SVar:TrigUntap:DB$ Untap | Defined$ Self
|
||||||
A:AB$ DealDamage | Cost$ T | ValidTgts$ Player,Planeswalker | TgtPrompt$ Select target player or planeswalker | NumDmg$ 1 | SubAbility$ DBTransform | AILogic$ PingAfterAttack | SpellDescription$ CARDNAME deals 1 damage to target player or planeswalker. If CARDNAME has dealt 3 or more damage this turn, exile her, then return her to the battlefield transformed under her owner's control.
|
A:AB$ DealDamage | Cost$ T | ValidTgts$ Player,Planeswalker | TgtPrompt$ Select target player or planeswalker | NumDmg$ 1 | SubAbility$ DBTransform | AILogic$ PingAfterAttack | SpellDescription$ CARDNAME deals 1 damage to target player or planeswalker. If NICKNAME has dealt 3 or more damage this turn, exile her, then return her to the battlefield transformed under her owner's control.
|
||||||
SVar:DBTransform:DB$ ChangeZone | Origin$ Battlefield | Destination$ Exile | RememberChanged$ True | SubAbility$ DBReturn | ConditionCheckSVar$ X | ConditionSVarCompare$ GE3 | StackDescription$ If CARDNAME has dealt 3 or more damage this turn, exile her, then return her to the battlefield transformed under her owner's control.
|
SVar:DBTransform:DB$ ChangeZone | Origin$ Battlefield | Destination$ Exile | RememberChanged$ True | SubAbility$ DBReturn | ConditionCheckSVar$ X | ConditionSVarCompare$ GE3 | StackDescription$ If CARDNAME has dealt 3 or more damage this turn, exile her, then return her to the battlefield transformed under her owner's control.
|
||||||
SVar:DBReturn:DB$ ChangeZone | Defined$ Remembered | Origin$ Exile | Destination$ Battlefield | Transformed$ True | SubAbility$ DBCleanup | StackDescription$
|
SVar:DBReturn:DB$ ChangeZone | Defined$ Remembered | Origin$ Exile | Destination$ Battlefield | Transformed$ True | SubAbility$ DBCleanup | StackDescription$
|
||||||
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
|
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ Name:Cleaver Skaab
|
|||||||
ManaCost:3 U
|
ManaCost:3 U
|
||||||
Types:Creature Zombie Horror
|
Types:Creature Zombie Horror
|
||||||
PT:2/4
|
PT:2/4
|
||||||
A:AB$ CopyPermanent | Cost$ 3 T Sac<1/Zombie.Other/another zombie> | Defined$ Sacrificed | NumCopies$ 2 | AILogic$ AtOppEOT
|
A:AB$ CopyPermanent | Cost$ 3 T Sac<1/Zombie.Other/another zombie> | Defined$ Sacrificed | NumCopies$ 2 | AILogic$ AtOppEOT | SpellDescription$ Create two tokens that are copies of the sacrificed creature.
|
||||||
DeckNeeds:Type$Zombie
|
DeckNeeds:Type$Zombie
|
||||||
DeckHas:Ability$Sacrifice|Token
|
DeckHas:Ability$Sacrifice|Token
|
||||||
SVar:AIPreference:SacCost$Zombie.Other
|
SVar:AIPreference:SacCost$Zombie.Other
|
||||||
|
|||||||
@@ -2,10 +2,9 @@ Name:Parallax Tide
|
|||||||
ManaCost:2 U U
|
ManaCost:2 U U
|
||||||
Types:Enchantment
|
Types:Enchantment
|
||||||
K:Fading:5
|
K:Fading:5
|
||||||
A:AB$ ChangeZone | Cost$ SubCounter<1/FADE> | ValidTgts$ Land | TgtPrompt$ Select target land | ImprintTargets$ True | Origin$ Battlefield | Destination$ Exile | SpellDescription$ Exile target land.
|
A:AB$ ChangeZone | Cost$ SubCounter<1/FADE> | ValidTgts$ Land | TgtPrompt$ Select target land | Origin$ Battlefield | Destination$ Exile | SpellDescription$ Exile target land.
|
||||||
T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Any | ValidCard$ Card.Self | Execute$ TrigReturn | TriggerDescription$ When CARDNAME leaves the battlefield, each player returns to the battlefield all cards they own exiled with CARDNAME.
|
T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Any | ValidCard$ Card.Self | Execute$ TrigReturn | TriggerDescription$ When CARDNAME leaves the battlefield, each player returns to the battlefield all cards they own exiled with CARDNAME.
|
||||||
SVar:TrigReturn:DB$ ChangeZone | Defined$ Imprinted | Origin$ Exile | Destination$ Battlefield | SubAbility$ DBCleanup
|
SVar:TrigReturn:DB$ ChangeZone | Defined$ ExiledWith | Origin$ Exile | Destination$ Battlefield
|
||||||
SVar:DBCleanup:DB$ Cleanup | ClearImprinted$ True
|
|
||||||
SVar:PlayMain1:TRUE
|
SVar:PlayMain1:TRUE
|
||||||
AI:RemoveDeck:All
|
AI:RemoveDeck:All
|
||||||
Oracle:Fading 5 (This enchantment enters the battlefield with five fade counters on it. At the beginning of your upkeep, remove a fade counter from it. If you can't, sacrifice it.)\nRemove a fade counter from Parallax Tide: Exile target land.\nWhen Parallax Tide leaves the battlefield, each player returns to the battlefield all cards they own exiled with Parallax Tide.
|
Oracle:Fading 5 (This enchantment enters the battlefield with five fade counters on it. At the beginning of your upkeep, remove a fade counter from it. If you can't, sacrifice it.)\nRemove a fade counter from Parallax Tide: Exile target land.\nWhen Parallax Tide leaves the battlefield, each player returns to the battlefield all cards they own exiled with Parallax Tide.
|
||||||
|
|||||||
@@ -2,10 +2,9 @@ Name:Parallax Wave
|
|||||||
ManaCost:2 W W
|
ManaCost:2 W W
|
||||||
Types:Enchantment
|
Types:Enchantment
|
||||||
K:Fading:5
|
K:Fading:5
|
||||||
A:AB$ ChangeZone | Cost$ SubCounter<1/FADE> | ValidTgts$ Creature | TgtPrompt$ Select target creature | ImprintTargets$ True | Origin$ Battlefield | Destination$ Exile | SpellDescription$ Exile target creature.
|
A:AB$ ChangeZone | Cost$ SubCounter<1/FADE> | ValidTgts$ Creature | TgtPrompt$ Select target creature | Origin$ Battlefield | Destination$ Exile | SpellDescription$ Exile target creature.
|
||||||
T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Any | ValidCard$ Card.Self | Execute$ TrigReturn | TriggerDescription$ When CARDNAME leaves the battlefield, each player returns to the battlefield all cards they own exiled with CARDNAME.
|
T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Any | ValidCard$ Card.Self | Execute$ TrigReturn | TriggerDescription$ When CARDNAME leaves the battlefield, each player returns to the battlefield all cards they own exiled with CARDNAME.
|
||||||
SVar:TrigReturn:DB$ ChangeZone | Defined$ Imprinted | Origin$ Exile | Destination$ Battlefield | SubAbility$ DBCleanup
|
SVar:TrigReturn:DB$ ChangeZone | Defined$ ExiledWith | Origin$ Exile | Destination$ Battlefield
|
||||||
SVar:DBCleanup:DB$ Cleanup | ClearImprinted$ True
|
|
||||||
SVar:PlayMain1:TRUE
|
SVar:PlayMain1:TRUE
|
||||||
AI:RemoveDeck:All
|
AI:RemoveDeck:All
|
||||||
Oracle:Fading 5 (This enchantment enters the battlefield with five fade counters on it. At the beginning of your upkeep, remove a fade counter from it. If you can't, sacrifice it.)\nRemove a fade counter from Parallax Wave: Exile target creature.\nWhen Parallax Wave leaves the battlefield, each player returns to the battlefield all cards they own exiled with Parallax Wave.
|
Oracle:Fading 5 (This enchantment enters the battlefield with five fade counters on it. At the beginning of your upkeep, remove a fade counter from it. If you can't, sacrifice it.)\nRemove a fade counter from Parallax Wave: Exile target creature.\nWhen Parallax Wave leaves the battlefield, each player returns to the battlefield all cards they own exiled with Parallax Wave.
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ Name:Preston, the Vanisher
|
|||||||
ManaCost:3 W
|
ManaCost:3 W
|
||||||
Types:Legendary Creature Rabbit Wizard
|
Types:Legendary Creature Rabbit Wizard
|
||||||
PT:2/5
|
PT:2/5
|
||||||
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Creature.nonToken+wasNotCast+YouCtrl | TriggerZones$ Battlefield | Execute$ TrigCopyPermanent | TriggerDescription$ Whenever another nontoken creature enters the battlefield under your control, if it wasn't cast, create a token that's a copy of that creature except it's a 0/1 white Illusion.
|
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Creature.Other+nonToken+wasNotCast+YouCtrl | TriggerZones$ Battlefield | Execute$ TrigCopyPermanent | TriggerDescription$ Whenever another nontoken creature enters the battlefield under your control, if it wasn't cast, create a token that's a copy of that creature except it's a 0/1 white Illusion.
|
||||||
SVar:TrigCopyPermanent:DB$ CopyPermanent | Defined$ TriggeredCardLKICopy | NumCopies$ 1 | SetColor$ White | SetCreatureTypes$ Illusion | SetPower$ 0 | SetToughness$ 1
|
SVar:TrigCopyPermanent:DB$ CopyPermanent | Defined$ TriggeredCardLKICopy | NumCopies$ 1 | SetColor$ White | SetCreatureTypes$ Illusion | SetPower$ 0 | SetToughness$ 1
|
||||||
A:AB$ ChangeZone | Cost$ 1 W Sac<5/Illusion> | ValidTgts$ Permanent.nonLand | Origin$ Battlefield | Destination$ Exile | TgtPrompt$ Select target nonland permanent | SpellDescription$ Exile target nonland permanent.
|
A:AB$ ChangeZone | Cost$ 1 W Sac<5/Illusion> | ValidTgts$ Permanent.nonLand | Origin$ Battlefield | Destination$ Exile | TgtPrompt$ Select target nonland permanent | SpellDescription$ Exile target nonland permanent.
|
||||||
DeckHas:Ability$Token|Sacrifice & Type$Illusion
|
DeckHas:Ability$Token|Sacrifice & Type$Illusion
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
Name:Sacred Ground
|
Name:Sacred Ground
|
||||||
ManaCost:1 W
|
ManaCost:1 W
|
||||||
Types:Enchantment
|
Types:Enchantment
|
||||||
T:Mode$ Sacrificed | ValidCard$ Land.YouOwn | ValidCause$ SpellAbility.OppCtrl | Execute$ TrigReturn | TriggerZones$ Battlefield | TriggerDescription$ Whenever a spell or ability an opponent controls causes a land to be put into your graveyard from the battlefield, return that card to the battlefield.
|
T:Mode$ ChangesZone | ValidCard$ Land.YouOwn | ValidCause$ SpellAbility.OppCtrl | Execute$ TrigReturn | TriggerZones$ Battlefield | TriggerDescription$ Whenever a spell or ability an opponent controls causes a land to be put into your graveyard from the battlefield, return that card to the battlefield.
|
||||||
T:Mode$ Destroyed | ValidCauser$ Player.Opponent | ValidCard$ Land.YouOwn | Execute$ TrigReturn | Secondary$ True | TriggerZones$ Battlefield | TriggerDescription$ Whenever a spell or ability an opponent controls causes a land to be put into your graveyard from the battlefield, return that card to the battlefield.
|
SVar:TrigReturn:DB$ ChangeZone | Defined$ TriggeredNewCardLKICopy | Origin$ Graveyard | Destination$ Battlefield
|
||||||
SVar:TrigReturn:DB$ ChangeZone | Defined$ TriggeredCard | Origin$ Graveyard | Destination$ Battlefield
|
|
||||||
AI:RemoveDeck:Random
|
AI:RemoveDeck:Random
|
||||||
Oracle:Whenever a spell or ability an opponent controls causes a land to be put into your graveyard from the battlefield, return that card to the battlefield.
|
Oracle:Whenever a spell or ability an opponent controls causes a land to be put into your graveyard from the battlefield, return that card to the battlefield.
|
||||||
|
|||||||
@@ -2,9 +2,9 @@ Name:Skyshroud Ambush
|
|||||||
ManaCost:1 G
|
ManaCost:1 G
|
||||||
Types:Instant
|
Types:Instant
|
||||||
A:SP$ Pump | ValidTgts$ Creature.YouCtrl | TgtPrompt$ Choose target creature you control | ImprintCards$ ThisTargetedCard | SubAbility$ DBFight | StackDescription$ Target creature you control [{c:ThisTargetedCard}] | SpellDescription$ Target creature you control fights target creature you don't control. When the creature you control wins the fight, draw a card.
|
A:SP$ Pump | ValidTgts$ Creature.YouCtrl | TgtPrompt$ Choose target creature you control | ImprintCards$ ThisTargetedCard | SubAbility$ DBFight | StackDescription$ Target creature you control [{c:ThisTargetedCard}] | SpellDescription$ Target creature you control fights target creature you don't control. When the creature you control wins the fight, draw a card.
|
||||||
SVar:DBFight:DB$ Fight | Defined$ ParentTarget | ValidTgts$ Creature.YouDontCtrl | TgtPrompt$ Choose target creature you don't control | RememberObjects$ ThisTargetedCard | SubAbility$ DBEffect | StackDescription$ fights target creature you don't control [{c:ThisTargetedCard}]. When the creature you control wins the fight, draw a card.
|
SVar:DBFight:DB$ Fight | Defined$ ParentTarget | ValidTgts$ Creature.YouDontCtrl | TgtPrompt$ Choose target creature you don't control | SubAbility$ DBEffect | StackDescription$ fights target creature you don't control [{c:ThisTargetedCard}]. When the creature you control wins the fight, draw a card.
|
||||||
SVar:DBEffect:DB$ Effect | Triggers$ TrigWin | RememberObjects$ Remembered | ImprintCards$ Imprinted | Duration$ UntilStateBasedActionChecked | SubAbility$ DBCleanup
|
SVar:DBEffect:DB$ Effect | Triggers$ TrigWin | RememberObjects$ ParentTarget | ImprintCards$ Imprinted | Duration$ UntilStateBasedActionChecked | SubAbility$ DBCleanup
|
||||||
SVar:TrigWin:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Any | ValidCard$ Creature.IsRemembered | IsPresent$ Creature.IsImprinted | NoResolvingCheck$ True | Execute$ TrigDraw | TriggerDescription$ When the creature you control wins the fight, draw a card.
|
SVar:TrigWin:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Any | ValidCard$ Creature.IsRemembered | IsPresent$ Creature.IsImprinted | NoResolvingCheck$ True | Execute$ TrigDraw | TriggerDescription$ When the creature you control wins the fight, draw a card.
|
||||||
SVar:TrigDraw:DB$ Draw | NumCards$ 1
|
SVar:TrigDraw:DB$ Draw | NumCards$ 1
|
||||||
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True | ClearImprinted$ True
|
SVar:DBCleanup:DB$ Cleanup | ClearImprinted$ True
|
||||||
Oracle:Target creature you control fights target creature you don't control. When the creature you control wins the fight, draw a card.
|
Oracle:Target creature you control fights target creature you don't control. When the creature you control wins the fight, draw a card.
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ PT:4/5
|
|||||||
K:Exalted
|
K:Exalted
|
||||||
T:Mode$ Attacks | ValidCard$ Creature.YouCtrl | Alone$ True | TriggerZones$ Battlefield | OptionalDecider$ You | Execute$ TrigSearch | TriggerDescription$ Whenever a creature you control attacks alone, you may search your library for an Aura card that could enchant that creature, put it onto the battlefield attached to that creature, then shuffle.
|
T:Mode$ Attacks | ValidCard$ Creature.YouCtrl | Alone$ True | TriggerZones$ Battlefield | OptionalDecider$ You | Execute$ TrigSearch | TriggerDescription$ Whenever a creature you control attacks alone, you may search your library for an Aura card that could enchant that creature, put it onto the battlefield attached to that creature, then shuffle.
|
||||||
SVar:TrigSearch:DB$ Pump | RememberObjects$ TriggeredAttacker | SubAbility$ DBMoveAura
|
SVar:TrigSearch:DB$ Pump | RememberObjects$ TriggeredAttacker | SubAbility$ DBMoveAura
|
||||||
SVar:DBMoveAura:DB$ ChangeZone | Origin$ Library | Destination$ Battlefield | ChangeType$ Aura.CanEnchantRemembered+YouCtrl | AttachedTo$ Remembered | ChangeNum$ 1 | Hidden$ True | ShuffleNonMandatory$ True
|
SVar:DBMoveAura:DB$ ChangeZone | Origin$ Library | Destination$ Battlefield | ChangeType$ Aura.CanEnchantRemembered+YouCtrl | AttachedTo$ Remembered | ChangeNum$ 1 | Hidden$ True | ShuffleNonMandatory$ True | SubAbility$ DBCleanup
|
||||||
|
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
|
||||||
AI:RemoveDeck:Random
|
AI:RemoveDeck:Random
|
||||||
Oracle:Exalted (Whenever a creature you control attacks alone, that creature gets +1/+1 until end of turn.)\nWhenever a creature you control attacks alone, you may search your library for an Aura card that could enchant that creature, put it onto the battlefield attached to that creature, then shuffle.
|
Oracle:Exalted (Whenever a creature you control attacks alone, that creature gets +1/+1 until end of turn.)\nWhenever a creature you control attacks alone, you may search your library for an Aura card that could enchant that creature, put it onto the battlefield attached to that creature, then shuffle.
|
||||||
|
|||||||
@@ -4,6 +4,6 @@ Types:Artifact Equipment
|
|||||||
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigInitiative | TriggerDescription$ When CARDNAME enters the battlefield, you take the initiative.
|
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigInitiative | TriggerDescription$ When CARDNAME enters the battlefield, you take the initiative.
|
||||||
SVar:TrigInitiative:DB$ TakeInitiative
|
SVar:TrigInitiative:DB$ TakeInitiative
|
||||||
T:Mode$ AttackerBlocked | ValidCard$ Creature.EquippedBy | Execute$ TrigDamage | TriggerDescription$ Whenever equipped creature becomes blocked, it deals 2 damage to each creature blocking it.
|
T:Mode$ AttackerBlocked | ValidCard$ Creature.EquippedBy | Execute$ TrigDamage | TriggerDescription$ Whenever equipped creature becomes blocked, it deals 2 damage to each creature blocking it.
|
||||||
SVar:TrigDamage:DB$ DamageAll | NumDmg$ 2 | ValidCards$ Creature.blockingEquipped
|
SVar:TrigDamage:DB$ DamageAll | NumDmg$ 2 | ValidCards$ Creature.blockingEquipped | DamageSource$ TriggeredAttackerLKICopy
|
||||||
K:Equip:1
|
K:Equip:1
|
||||||
Oracle:When Trailblazer's Torch enters the battlefield, you take the initiative.\nWhenever equipped creature becomes blocked, it deals 2 damage to each creature blocking it.\nEquip {1} ({1}: Attach to target creature you control. Equip only as a sorcery.)
|
Oracle:When Trailblazer's Torch enters the battlefield, you take the initiative.\nWhenever equipped creature becomes blocked, it deals 2 damage to each creature blocking it.\nEquip {1} ({1}: Attach to target creature you control. Equip only as a sorcery.)
|
||||||
|
|||||||
@@ -5,6 +5,6 @@ PT:1/3
|
|||||||
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigScry | TriggerDescription$ When CARDNAME enters the battlefield, scry 2.
|
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigScry | TriggerDescription$ When CARDNAME enters the battlefield, scry 2.
|
||||||
SVar:TrigScry:DB$ Scry | ScryNum$ 2
|
SVar:TrigScry:DB$ Scry | ScryNum$ 2
|
||||||
A:AB$ BecomeMonarch | Cost$ 3 R T Sac<1/NICKNAME> | ValidTgts$ Player | SubAbility$ DBDamage | SpellDescription$ Target player becomes the monarch. NICKNAME deals 3 damage to any target.
|
A:AB$ BecomeMonarch | Cost$ 3 R T Sac<1/NICKNAME> | ValidTgts$ Player | SubAbility$ DBDamage | SpellDescription$ Target player becomes the monarch. NICKNAME deals 3 damage to any target.
|
||||||
SVar:DBPower:DB$ DealDamage | ValidTgts$ Any | NumDmg$ 3
|
SVar:DBDamage:DB$ DealDamage | ValidTgts$ Any | NumDmg$ 3
|
||||||
DeckHas:Ability$Sacrifice
|
DeckHas:Ability$Sacrifice
|
||||||
Oracle:When Denethor, Stone Seer enters the battlefield, scry 2.\n{3}{R}, {T}, Sacrifice Denethor: Target player becomes the monarch. Denethor deals 3 damage to any target.
|
Oracle:When Denethor, Stone Seer enters the battlefield, scry 2.\n{3}{R}, {T}, Sacrifice Denethor: Target player becomes the monarch. Denethor deals 3 damage to any target.
|
||||||
@@ -2,7 +2,6 @@ Name:Esquire of the King
|
|||||||
ManaCost:W
|
ManaCost:W
|
||||||
Types:Creature Human Soldier
|
Types:Creature Human Soldier
|
||||||
PT:1/1
|
PT:1/1
|
||||||
K:Flying
|
|
||||||
A:AB$ PumpAll | NumAtt$ +1 | NumDef$ +1 | Cost$ 4 W T | ValidCards$ Creature.YouCtrl | ReduceCost$ X | SpellDescription$ Creatures you control get +1/+1 until end of turn. This ability costs {2} less to activate if you control a legendary creature.
|
A:AB$ PumpAll | NumAtt$ +1 | NumDef$ +1 | Cost$ 4 W T | ValidCards$ Creature.YouCtrl | ReduceCost$ X | SpellDescription$ Creatures you control get +1/+1 until end of turn. This ability costs {2} less to activate if you control a legendary creature.
|
||||||
SVar:X:Count$Compare Y GE1.2.0
|
SVar:X:Count$Compare Y GE1.2.0
|
||||||
SVar:Y:Count$Valid Creature.Legendary+YouCtrl
|
SVar:Y:Count$Valid Creature.Legendary+YouCtrl
|
||||||
|
|||||||
@@ -4,8 +4,8 @@ Types:Legendary Creature Human Advisor
|
|||||||
PT:1/4
|
PT:1/4
|
||||||
S:Mode$ CantBlockBy | ValidAttacker$ Creature.Self | Description$ CARDNAME can't be blocked.
|
S:Mode$ CantBlockBy | ValidAttacker$ Creature.Self | Description$ CARDNAME can't be blocked.
|
||||||
T:Mode$ DamageDone | ValidSource$ Card.Self | ValidTarget$ Player | CombatDamage$ True | Execute$ TrigDigUntil | TriggerZones$ Battlefield | TriggerDescription$ Whenever NICKNAME deals combat damage to a player, that player exiles cards from the top of their library until they exile an instant or sorcery card. You may cast that card without paying its mana cost. Then that player puts the exiled cards that weren't cast this way on the bottom of their library in a random order.
|
T:Mode$ DamageDone | ValidSource$ Card.Self | ValidTarget$ Player | CombatDamage$ True | Execute$ TrigDigUntil | TriggerZones$ Battlefield | TriggerDescription$ Whenever NICKNAME deals combat damage to a player, that player exiles cards from the top of their library until they exile an instant or sorcery card. You may cast that card without paying its mana cost. Then that player puts the exiled cards that weren't cast this way on the bottom of their library in a random order.
|
||||||
SVar:TrigDigUntil:DB$ DigUntil | Defined$ TriggeredTarget | Valid$ Instant,Sorcery | ValidDescription$ instant or sorcery | FoundDestination$ Exile | RevealedDestination$ Exile | RememberFound$ True | IsCurse$ True | SubAbility$ DBPlay
|
SVar:TrigDigUntil:DB$ DigUntil | Defined$ TriggeredTarget | Valid$ Instant,Sorcery | ValidDescription$ instant or sorcery | FoundDestination$ Exile | RevealedDestination$ Exile | RememberFound$ True | ImprintRevealed$ True | IsCurse$ True | SubAbility$ DBPlay
|
||||||
SVar:DBPlay:DB$ Play | ValidZone$ Exile | Valid$ Instant.IsRemembered,Sorcery.IsRemembered | ValidSA$ Spell | WithoutManaCost$ True | Optional$ True | ForgetPlayed$ True | SubAbility$ DBRestRandomOrder
|
SVar:DBPlay:DB$ Play | ValidZone$ Exile | Valid$ Instant.IsRemembered,Sorcery.IsRemembered | ValidSA$ Spell | WithoutManaCost$ True | Optional$ True | SubAbility$ DBRestRandomOrder
|
||||||
SVar:DBRestRandomOrder:DB$ ChangeZoneAll | ChangeType$ Card.IsRemembered | Origin$ Exile | Destination$ Library | LibraryPosition$ -1 | RandomOrder$ True | SubAbility$ DBCleanup
|
SVar:DBRestRandomOrder:DB$ ChangeZoneAll | ChangeType$ Card.IsImprinted | Origin$ Exile | Destination$ Library | LibraryPosition$ -1 | RandomOrder$ True | SubAbility$ DBCleanup
|
||||||
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
|
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True | ClearImprinted$ True
|
||||||
Oracle:Grima, Saruman's Footman can't be blocked.\nWhenever Grima deals combat damage to a player, that player exiles cards from the top of their library until they exile an instant or sorcery card. You may cast that card without paying its mana cost. Then that player puts the exiled cards that weren't cast this way on the bottom of their library in a random order.
|
Oracle:Grima, Saruman's Footman can't be blocked.\nWhenever Grima deals combat damage to a player, that player exiles cards from the top of their library until they exile an instant or sorcery card. You may cast that card without paying its mana cost. Then that player puts the exiled cards that weren't cast this way on the bottom of their library in a random order.
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
Name:Vivien's Invocation
|
Name:Vivien's Invocation
|
||||||
ManaCost:5 G G
|
ManaCost:5 G G
|
||||||
Types:Sorcery
|
Types:Sorcery
|
||||||
A:SP$ Dig | Cost$ 5 G G | DigNum$ 7 | ChangeNum$ 1 | ChangeValid$ Creature | Optional$ True | RestRandomOrder$ True | DestinationZone$ Battlefield | ForceRevealToController$ True | SpellDescription$ Look at the top seven cards of your library. You may put a creature card from among them onto the battlefield. Put the rest on the bottom of your library in a random order. When a creature is put onto the battlefield this way, it deals damage equal to its power to target creature an opponent controls.
|
A:SP$ Dig | Cost$ 5 G G | DigNum$ 7 | ChangeNum$ 1 | ChangeValid$ Creature | Optional$ True | RestRandomOrder$ True | DestinationZone$ Battlefield | ForceRevealToController$ True | RememberChanged$ True | SubAbility$ DBTrig | SpellDescription$ Look at the top seven cards of your library. You may put a creature card from among them onto the battlefield. Put the rest on the bottom of your library in a random order. When a creature is put onto the battlefield this way, it deals damage equal to its power to target creature an opponent controls.
|
||||||
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Creature | ValidCause$ Card.Self | Execute$ DBDealDamage | Secondary$ True | TriggerDescription$ When a creature is put onto the battlefield this way, it deals damage equals to its power to target creature an opponent controls.
|
SVar:DBTrig:DB$ ImmediateTrigger | RememberObjects$ Remembered | Execute$ DBDealDamage | ConditionDefined$ Remembered | ConditionPresent$ Card | SubAbility$ DBCleanup | TriggerDescription$ When a creature is put onto the battlefield this way, it deals damage equals to its power to target creature an opponent controls.
|
||||||
SVar:DBDealDamage:DB$ DealDamage | ValidTgts$ Creature.OppCtrl | TgtPrompt$ Select target creature an opponent controls | NumDmg$ X | DamageSource$ TriggeredCard
|
SVar:DBDealDamage:DB$ DealDamage | ValidTgts$ Creature.OppCtrl | TgtPrompt$ Select target creature an opponent controls | NumDmg$ X | DamageSource$ DelayTriggerRememberedLKI
|
||||||
SVar:X:TriggeredCard$CardPower
|
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
|
||||||
|
SVar:X:TriggerRemembered$CardPower
|
||||||
Oracle:Look at the top seven cards of your library. You may put a creature card from among them onto the battlefield. Put the rest on the bottom of your library in a random order. When a creature is put onto the battlefield this way, it deals damage equal to its power to target creature an opponent controls.
|
Oracle:Look at the top seven cards of your library. You may put a creature card from among them onto the battlefield. Put the rest on the bottom of your library in a random order. When a creature is put onto the battlefield this way, it deals damage equal to its power to target creature an opponent controls.
|
||||||
|
|||||||
27
forge-gui/res/editions/Explorer Anthology 3.txt
Normal file
27
forge-gui/res/editions/Explorer Anthology 3.txt
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
[metadata]
|
||||||
|
Code=EA3
|
||||||
|
Date=2023-07-18
|
||||||
|
Name=Explorer Anthology 3
|
||||||
|
Type=Online
|
||||||
|
ScryfallCode=EA3
|
||||||
|
|
||||||
|
[cards]
|
||||||
|
1 R Cyclonic Rift @Chris Rahn
|
||||||
|
2 R Abbot of Keral Keep @Deruchenko Alexander
|
||||||
|
3 U Shrapnel Blast @Hideaki Takamura
|
||||||
|
4 R Eidolon of Blossoms @Min Yum
|
||||||
|
5 R Chord of Calling @Karl Kopinski
|
||||||
|
6 M Worldspine Wurm @Richard Wright
|
||||||
|
7 U Golgari Charm @Zoltan Boros
|
||||||
|
8 U Simic Charm @Zoltan Boros
|
||||||
|
9 C Izzet Charm @Zoltan Boros
|
||||||
|
10 U Gruul Charm @Zoltan Boros
|
||||||
|
11 U Orzhov Charm @Zoltan Boros
|
||||||
|
12 R Voice of Resurgence @Winona Nelson
|
||||||
|
13 R Deathrite Shaman @Steve Argyle
|
||||||
|
14 R Canopy Vista @Adam Paquette
|
||||||
|
15 R Cinder Glade @Adam Paquette
|
||||||
|
16 R Smoldering Marsh @Adam Paquette
|
||||||
|
17 R Sunken Hollow @Adam Paquette
|
||||||
|
18 R Prairie Stream @Adam Paquette
|
||||||
|
19 R Thespian's Stage @John Avon
|
||||||
27
forge-gui/res/editions/Historic Anthology 7.txt
Normal file
27
forge-gui/res/editions/Historic Anthology 7.txt
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
[metadata]
|
||||||
|
Code=HA7
|
||||||
|
Date=2023-07-18
|
||||||
|
Name=Historic Anthology 7
|
||||||
|
Type=Online
|
||||||
|
ScryfallCode=HA7
|
||||||
|
|
||||||
|
[cards]
|
||||||
|
1 M Sun Titan @Todd Lockwood
|
||||||
|
2 M Frost Titan @Mike Bierek
|
||||||
|
3 M Vendilion Clique @Willian Murai
|
||||||
|
4 M Grave Titan @Nils Hamm
|
||||||
|
5 M Inferno Titan @Kev Walker
|
||||||
|
6 C Tribal Flames @Zack Stella
|
||||||
|
7 M Primeval Titan @Aleksi Briclot
|
||||||
|
8 C Wild Nacatl @Wayne Reynolds
|
||||||
|
9 U Acidic Slime @Karl Kopinski
|
||||||
|
10 R Tooth and Nail @Jesper Ejsing
|
||||||
|
11 U Bloodbraid Elf @Raymond Swanland
|
||||||
|
12 U Mortarpod @Eric Deschamps
|
||||||
|
13 U Worn Powerstone @Henry G. Higginbotham
|
||||||
|
14 R Sword of Fire and Ice @Mark Zug
|
||||||
|
15 R Fiery Islet @Richard Wright
|
||||||
|
16 R Sunbaked Canyon @Yeong-Hao Han
|
||||||
|
17 R Nurturing Peatland @Sam Burley
|
||||||
|
18 R Silent Clearing @Seb McKinnon
|
||||||
|
19 R Waterlogged Grove @John Avon
|
||||||
@@ -93,7 +93,7 @@ ScryfallCode=JMP
|
|||||||
85 R Angel of the Dire Hour @Jack Wang
|
85 R Angel of the Dire Hour @Jack Wang
|
||||||
86 R Angelic Arbiter @Steve Argyle
|
86 R Angelic Arbiter @Steve Argyle
|
||||||
87 C Angelic Edict @Trevor Claxton
|
87 C Angelic Edict @Trevor Claxton
|
||||||
88 U Angelic Page @Chris Rahn
|
88 C Angelic Page @Chris Rahn
|
||||||
89 R Archon of Justice @Jason Chan
|
89 R Archon of Justice @Jason Chan
|
||||||
90 R Archon of Redemption @Steven Belledin
|
90 R Archon of Redemption @Steven Belledin
|
||||||
91 C Battlefield Promotion @Scott Murphy
|
91 C Battlefield Promotion @Scott Murphy
|
||||||
|
|||||||
8
forge-gui/res/formats/Archived/Explorer/2023-07-18.txt
Normal file
8
forge-gui/res/formats/Archived/Explorer/2023-07-18.txt
Normal file
File diff suppressed because one or more lines are too long
7
forge-gui/res/formats/Archived/Historic/2023-07-18.txt
Normal file
7
forge-gui/res/formats/Archived/Historic/2023-07-18.txt
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
[format]
|
||||||
|
Name:Historic (HA7)
|
||||||
|
Type:Archived
|
||||||
|
Subtype:Arena
|
||||||
|
Effective:2023-07-18
|
||||||
|
Sets:XLN, RIX, DOM, M19, ANA, PANA, GRN, G18, RNA, WAR, M20, ELD, HA1, THB, HA2, IKO, HA3, M21, JMP, AJMP, AKR, ANB, ZNR, KLR, KHM, HA4, STX, STA, HA5, AFR, J21, MID, VOW, YMID, NEO, YNEO, SNC, YSNC, HBG, HA6, EA1, DMU, YDMU, BRO, BRR, YBRO, EA2, ONE, YONE, SIR, SIS, MOM, MUL, MAT, LTR, HA7, EA3
|
||||||
|
Banned:Agent of Treachery; Brainstorm; Channel; Counterspell; Dark Ritual; Demonic Tutor; Field of the Dead; Lightning Bolt; Memory Lapse; Mishra's Bauble; Natural Order; Nexus of Fate; Oko, Thief of Crowns; Once Upon a Time; Ragavan, Nimble Pilferer; Swords to Plowshares; Thassa's Oracle; Tibalt's Trickery; Time Warp; Uro, Titan of Nature's Wrath; Veil of Summer; Wilderness Reclamation
|
||||||
@@ -4,5 +4,5 @@ Type:Digital
|
|||||||
Subtype:Arena
|
Subtype:Arena
|
||||||
Effective:2019-11-21
|
Effective:2019-11-21
|
||||||
Order:142
|
Order:142
|
||||||
Sets:XLN, RIX, DOM, M19, ANA, PANA, GRN, G18, RNA, WAR, M20, ELD, HA1, THB, HA2, IKO, HA3, M21, JMP, AJMP, AKR, ANB, ZNR, KLR, KHM, HA4, STX, STA, HA5, AFR, J21, MID, VOW, YMID, NEO, YNEO, SNC, YSNC, HBG, HA6, EA1, DMU, YDMU, BRO, BRR, YBRO, EA2, ONE, YONE, SIR, SIS, MOM, MUL, MAT, LTR
|
Sets:XLN, RIX, DOM, M19, ANA, PANA, GRN, G18, RNA, WAR, M20, ELD, HA1, THB, HA2, IKO, HA3, M21, JMP, AJMP, AKR, ANB, ZNR, KLR, KHM, HA4, STX, STA, HA5, AFR, J21, MID, VOW, YMID, NEO, YNEO, SNC, YSNC, HBG, HA6, EA1, DMU, YDMU, BRO, BRR, YBRO, EA2, ONE, YONE, SIR, SIS, MOM, MUL, MAT, LTR, HA7, EA3
|
||||||
Banned:Agent of Treachery; Brainstorm; Channel; Counterspell; Dark Ritual; Demonic Tutor; Field of the Dead; Lightning Bolt; Memory Lapse; Mishra's Bauble; Natural Order; Nexus of Fate; Oko, Thief of Crowns; Once Upon a Time; Ragavan, Nimble Pilferer; Swords to Plowshares; Thassa's Oracle; Tibalt's Trickery; Time Warp; Uro, Titan of Nature's Wrath; Veil of Summer; Wilderness Reclamation
|
Banned:Agent of Treachery; Brainstorm; Channel; Counterspell; Dark Ritual; Demonic Tutor; Field of the Dead; Lightning Bolt; Memory Lapse; Mishra's Bauble; Natural Order; Nexus of Fate; Oko, Thief of Crowns; Once Upon a Time; Ragavan, Nimble Pilferer; Swords to Plowshares; Thassa's Oracle; Tibalt's Trickery; Time Warp; Uro, Titan of Nature's Wrath; Veil of Summer; Wilderness Reclamation
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 1.4 MiB After Width: | Height: | Size: 1.5 MiB |
Binary file not shown.
|
Before Width: | Height: | Size: 697 KiB After Width: | Height: | Size: 823 KiB |
Reference in New Issue
Block a user