mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-19 12:18:00 +00:00
Merge branch 'master' into 'typo'
# Conflicts: # forge-gui/res/cardsfolder/e/eternal_dominion.txt
This commit is contained in:
@@ -1318,7 +1318,6 @@ public class AiBlockController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int evalAtk = ComputerUtilCard.evaluateCreature(attacker, true, false);
|
int evalAtk = ComputerUtilCard.evaluateCreature(attacker, true, false);
|
||||||
int evalBlk = ComputerUtilCard.evaluateCreature(blocker, true, false);
|
|
||||||
boolean atkEmbalm = (attacker.hasStartOfKeyword("Embalm") || attacker.hasStartOfKeyword("Eternalize")) && !attacker.isToken();
|
boolean atkEmbalm = (attacker.hasStartOfKeyword("Embalm") || attacker.hasStartOfKeyword("Eternalize")) && !attacker.isToken();
|
||||||
boolean blkEmbalm = (blocker.hasStartOfKeyword("Embalm") || blocker.hasStartOfKeyword("Eternalize")) && !blocker.isToken();
|
boolean blkEmbalm = (blocker.hasStartOfKeyword("Embalm") || blocker.hasStartOfKeyword("Eternalize")) && !blocker.isToken();
|
||||||
|
|
||||||
@@ -1327,10 +1326,13 @@ public class AiBlockController {
|
|||||||
chance = Math.max(0, chance - chanceModForEmbalm);
|
chance = Math.max(0, chance - chanceModForEmbalm);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (blocker.isFaceDown() && !checkingOther && blocker.getState(CardStateName.Original).getType().isCreature()) {
|
int evalBlk;
|
||||||
|
if (blocker.isFaceDown() && blocker.getView().canFaceDownBeShownTo(ai.getView(), false) && blocker.getState(CardStateName.Original).getType().isCreature()) {
|
||||||
// if the blocker is a face-down creature (e.g. cast via Morph, Manifest), evaluate it
|
// if the blocker is a face-down creature (e.g. cast via Morph, Manifest), evaluate it
|
||||||
// in relation to the original state, not to the Morph state
|
// in relation to the original state, not to the Morph state
|
||||||
evalBlk = ComputerUtilCard.evaluateCreature(Card.fromPaperCard(blocker.getPaperCard(), ai), false, true);
|
evalBlk = ComputerUtilCard.evaluateCreature(Card.fromPaperCard(blocker.getPaperCard(), ai), false, true);
|
||||||
|
} else {
|
||||||
|
evalBlk = ComputerUtilCard.evaluateCreature(blocker, true, false);
|
||||||
}
|
}
|
||||||
int chanceToSavePW = chanceToTradeDownToSaveWalker > 0 && evalAtk + 1 < evalBlk ? chanceToTradeDownToSaveWalker : chanceToTradeToSaveWalker;
|
int chanceToSavePW = chanceToTradeDownToSaveWalker > 0 && evalAtk + 1 < evalBlk ? chanceToTradeDownToSaveWalker : chanceToTradeToSaveWalker;
|
||||||
boolean powerParityOrHigher = blocker.getNetPower() <= attacker.getNetPower();
|
boolean powerParityOrHigher = blocker.getNetPower() <= attacker.getNetPower();
|
||||||
|
|||||||
@@ -125,15 +125,8 @@ public class AttachAi extends SpellAbilityAi {
|
|||||||
|
|
||||||
if (ComputerUtilAbility.getAbilitySourceName(sa).equals("Chained to the Rocks")) {
|
if (ComputerUtilAbility.getAbilitySourceName(sa).equals("Chained to the Rocks")) {
|
||||||
final SpellAbility effectExile = AbilityFactory.getAbility(source.getSVar("TrigExile"), source);
|
final SpellAbility effectExile = AbilityFactory.getAbility(source.getSVar("TrigExile"), source);
|
||||||
final ZoneType origin = ZoneType.listValueOf(effectExile.getParam("Origin")).get(0);
|
|
||||||
final TargetRestrictions exile_tgt = effectExile.getTargetRestrictions();
|
final TargetRestrictions exile_tgt = effectExile.getTargetRestrictions();
|
||||||
final CardCollection list = CardLists.getValidCards(ai.getGame().getCardsIn(origin), exile_tgt.getValidTgts(), ai, source, effectExile);
|
final CardCollection targets = CardLists.filter(CardUtil.getValidCardsToTarget(exile_tgt, effectExile), CardPredicates.canBeAttached(source));
|
||||||
final CardCollection targets = CardLists.filter(list, new Predicate<Card>() {
|
|
||||||
@Override
|
|
||||||
public boolean apply(final Card c) {
|
|
||||||
return !(c.hasProtectionFrom(source) || c.hasKeyword(Keyword.SHROUD) || c.hasKeyword(Keyword.HEXPROOF));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return !targets.isEmpty();
|
return !targets.isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1325,8 +1318,7 @@ public class AttachAi extends SpellAbilityAi {
|
|||||||
|
|
||||||
// Is a SA that moves target attachment
|
// Is a SA that moves target attachment
|
||||||
if ("MoveTgtAura".equals(sa.getParam("AILogic"))) {
|
if ("MoveTgtAura".equals(sa.getParam("AILogic"))) {
|
||||||
CardCollection list = CardLists.getValidCards(aiPlayer.getGame().getCardsIn(tgt.getZone()), tgt.getValidTgts(), sa.getActivatingPlayer(), attachSource, sa);
|
CardCollection list = new CardCollection(CardUtil.getValidCardsToTarget(tgt, sa));
|
||||||
list = CardLists.filter(list, Predicates.not(CardPredicates.isProtectedFrom(attachSource)));
|
|
||||||
list = CardLists.filter(list, Predicates.or(CardPredicates.isControlledByAnyOf(aiPlayer.getOpponents()), new Predicate<Card>() {
|
list = CardLists.filter(list, Predicates.or(CardPredicates.isControlledByAnyOf(aiPlayer.getOpponents()), new Predicate<Card>() {
|
||||||
@Override
|
@Override
|
||||||
public boolean apply(final Card card) {
|
public boolean apply(final Card card) {
|
||||||
@@ -1336,7 +1328,7 @@ public class AttachAi extends SpellAbilityAi {
|
|||||||
|
|
||||||
return !list.isEmpty() ? ComputerUtilCard.getBestAI(list) : null;
|
return !list.isEmpty() ? ComputerUtilCard.getBestAI(list) : null;
|
||||||
} else if ("Unenchanted".equals(sa.getParam("AILogic"))) {
|
} else if ("Unenchanted".equals(sa.getParam("AILogic"))) {
|
||||||
CardCollection list = CardLists.getValidCards(aiPlayer.getGame().getCardsIn(tgt.getZone()), tgt.getValidTgts(), sa.getActivatingPlayer(), attachSource, sa);
|
CardCollection list = new CardCollection(CardUtil.getValidCardsToTarget(tgt, sa));
|
||||||
CardCollection preferred = CardLists.filter(list, new Predicate<Card>() {
|
CardCollection preferred = CardLists.filter(list, new Predicate<Card>() {
|
||||||
@Override
|
@Override
|
||||||
public boolean apply(final Card card) {
|
public boolean apply(final Card card) {
|
||||||
@@ -1361,18 +1353,7 @@ public class AttachAi extends SpellAbilityAi {
|
|||||||
if (tgt == null) {
|
if (tgt == null) {
|
||||||
list = AbilityUtils.getDefinedCards(attachSource, sa.getParam("Defined"), sa);
|
list = AbilityUtils.getDefinedCards(attachSource, sa.getParam("Defined"), sa);
|
||||||
} else {
|
} else {
|
||||||
list = CardLists.getValidCards(aiPlayer.getGame().getCardsIn(tgt.getZone()), tgt.getValidTgts(), sa.getActivatingPlayer(), attachSource, sa);
|
list = CardLists.filter(CardUtil.getValidCardsToTarget(tgt, sa), CardPredicates.canBeAttached(attachSource));
|
||||||
list = CardLists.filter(list, CardPredicates.canBeAttached(attachSource));
|
|
||||||
|
|
||||||
// TODO If Attaching without casting, don't need to actually target.
|
|
||||||
// I believe this is the only case where mandatory will be true, so just
|
|
||||||
// check that when starting that work
|
|
||||||
// But we shouldn't attach to things with Protection
|
|
||||||
if (!mandatory) {
|
|
||||||
list = CardLists.getTargetableCards(list, sa);
|
|
||||||
} else {
|
|
||||||
list = CardLists.filter(list, Predicates.not(CardPredicates.isProtectedFrom(attachSource)));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (list.isEmpty()) {
|
if (list.isEmpty()) {
|
||||||
|
|||||||
@@ -187,7 +187,7 @@ public class CopyPermanentAi extends SpellAbilityAi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (choice == null) { // can't find anything left
|
if (choice == null) { // can't find anything left
|
||||||
if (!sa.isTargetNumberValid() || (sa.getTargets().size() == 0)) {
|
if (!sa.isTargetNumberValid() || sa.getTargets().size() == 0) {
|
||||||
sa.resetTargets();
|
sa.resetTargets();
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -598,9 +598,9 @@ public class DamageDealAi extends DamageAiBase {
|
|||||||
lastTgt = humanCreature;
|
lastTgt = humanCreature;
|
||||||
dmg -= assignedDamage;
|
dmg -= assignedDamage;
|
||||||
}
|
}
|
||||||
if (!source.hasProtectionFrom(humanCreature)) {
|
// protection is already checked by target above
|
||||||
dmgTaken += humanCreature.getNetPower();
|
dmgTaken += humanCreature.getNetPower();
|
||||||
}
|
|
||||||
if (dmg == 0) {
|
if (dmg == 0) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,21 +1,18 @@
|
|||||||
package forge.ai.ability;
|
package forge.ai.ability;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import forge.ai.ComputerUtil;
|
import forge.ai.ComputerUtil;
|
||||||
import forge.ai.ComputerUtilAbility;
|
import forge.ai.ComputerUtilAbility;
|
||||||
import forge.ai.ComputerUtilCard;
|
import forge.ai.ComputerUtilCard;
|
||||||
import forge.ai.ComputerUtilCombat;
|
import forge.ai.ComputerUtilCombat;
|
||||||
import forge.ai.SpellAbilityAi;
|
import forge.ai.SpellAbilityAi;
|
||||||
import forge.game.ability.AbilityFactory;
|
|
||||||
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.Card;
|
||||||
import forge.game.card.CardCollection;
|
import forge.game.card.CardCollection;
|
||||||
import forge.game.card.CardCollectionView;
|
import forge.game.card.CardCollectionView;
|
||||||
import forge.game.card.CardLists;
|
import forge.game.card.CardLists;
|
||||||
import forge.game.keyword.Keyword;
|
|
||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
import forge.game.spellability.AbilitySub;
|
import forge.game.spellability.AbilitySub;
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
@@ -263,15 +260,13 @@ public class FightAi extends SpellAbilityAi {
|
|||||||
for (Trigger t : aiCreature.getTriggers()) {
|
for (Trigger t : aiCreature.getTriggers()) {
|
||||||
if (t.getMode() == TriggerType.SpellCast) {
|
if (t.getMode() == TriggerType.SpellCast) {
|
||||||
SpellAbility sa = t.ensureAbility();
|
SpellAbility sa = t.ensureAbility();
|
||||||
final Map<String, String> params = t.getMapParams();
|
|
||||||
if (sa == null) {
|
if (sa == null) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (ApiType.PutCounter.equals(sa.getApi())) {
|
if (ApiType.PutCounter.equals(sa.getApi())) {
|
||||||
if ("Card.Self".equals(params.get("TargetsValid")) && "You".equals(params.get("ValidActivatingPlayer"))) {
|
if ("Card.Self".equals(t.getParam("TargetsValid")) && "You".equals(t.getParam("ValidActivatingPlayer"))) {
|
||||||
SpellAbility heroic = AbilityFactory.getAbility(aiCreature.getSVar(params.get("Execute")),aiCreature);
|
if ("Self".equals(sa.getParam("Defined")) && "P1P1".equals(sa.getParam("CounterType"))) {
|
||||||
if ("Self".equals(heroic.getParam("Defined")) && "P1P1".equals(heroic.getParam("CounterType"))) {
|
return AbilityUtils.calculateAmount(aiCreature, sa.getParam("CounterNum"), sa);
|
||||||
return AbilityUtils.calculateAmount(aiCreature, heroic.getParam("CounterNum"), heroic);
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -299,12 +294,14 @@ public class FightAi extends SpellAbilityAi {
|
|||||||
if (opponent.getSVar("Targeting").equals("Dies")) {
|
if (opponent.getSVar("Targeting").equals("Dies")) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (opponent.hasProtectionFrom(fighter) || !opponent.canBeDestroyed() || opponent.getShieldCount() > 0
|
// the damage prediction is later
|
||||||
|| ComputerUtil.canRegenerate(opponent.getController(), opponent)) {
|
int damage = fighter.getNetPower() + pumpAttack;
|
||||||
|
if (damage <= 0 || opponent.getShieldCount() > 0 || ComputerUtil.canRegenerate(opponent.getController(), opponent)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (fighter.hasKeyword(Keyword.DEATHTOUCH)
|
// try to predict the damage that fighter would deal to opponent
|
||||||
|| ComputerUtilCombat.getDamageToKill(opponent, true) <= fighter.getNetPower() + pumpAttack) {
|
// this should also handle if the opponents creature can be destroyed or not
|
||||||
|
if (ComputerUtilCombat.getEnoughDamageToKill(opponent, damage, fighter, false) <= damage) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ public class GoadAi extends SpellAbilityAi {
|
|||||||
if (list.isEmpty())
|
if (list.isEmpty())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (game.getPlayers().size() >= 2) {
|
if (game.getPlayers().size() > 2) {
|
||||||
// use this part only in multiplayer
|
// use this part only in multiplayer
|
||||||
CardCollection betterList = CardLists.filter(list, new Predicate<Card>() {
|
CardCollection betterList = CardLists.filter(list, new Predicate<Card>() {
|
||||||
@Override
|
@Override
|
||||||
@@ -79,10 +79,43 @@ public class GoadAi extends SpellAbilityAi {
|
|||||||
|
|
||||||
// AI does not find a good creature to goad.
|
// AI does not find a good creature to goad.
|
||||||
// because if it would goad a creature it would attack AI.
|
// because if it would goad a creature it would attack AI.
|
||||||
// AI might not have enough infomation to block it
|
// AI might not have enough information to block it
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean doTriggerAINoCost(Player ai, SpellAbility sa, boolean mandatory) {
|
||||||
|
if (checkApiLogic(ai, sa)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (!mandatory) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (sa.usesTargeting()) {
|
||||||
|
if (sa.getTargetRestrictions().canTgtPlayer()) {
|
||||||
|
for (Player opp : ai.getOpponents()) {
|
||||||
|
if (sa.canTarget(opp)) {
|
||||||
|
sa.getTargets().add(opp);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (sa.canTarget(ai)) {
|
||||||
|
sa.getTargets().add(ai);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
List<Card> list = CardLists.getTargetableCards(ai.getGame().getCardsIn(ZoneType.Battlefield), sa);
|
||||||
|
|
||||||
|
if (list.isEmpty())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
sa.getTargets().add(ComputerUtilCard.getWorstCreatureAI(list));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ public class ReplaceDamageAi extends SpellAbilityAi {
|
|||||||
if (c.hasSVar("MustBeBlocked")) {
|
if (c.hasSVar("MustBeBlocked")) {
|
||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
// TODO check if target can receive counters
|
// TODO check if target can receive counters + sort these to the front if that can prevent loss
|
||||||
if (c.hasKeyword(Keyword.INFECT)) {
|
if (c.hasKeyword(Keyword.INFECT)) {
|
||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ public class RevealAi extends RevealAiBase {
|
|||||||
@Override
|
@Override
|
||||||
protected boolean checkApiLogic(final Player ai, final SpellAbility sa) {
|
protected boolean checkApiLogic(final Player ai, final SpellAbility sa) {
|
||||||
// we can reuse this function here...
|
// we can reuse this function here...
|
||||||
final boolean bFlag = revealHandTargetAI(ai, sa/* , true, false */);
|
final boolean bFlag = revealHandTargetAI(ai, sa, false);
|
||||||
|
|
||||||
if (!bFlag) {
|
if (!bFlag) {
|
||||||
return false;
|
return false;
|
||||||
@@ -80,7 +80,7 @@ public class RevealAi extends RevealAiBase {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!revealHandTargetAI(ai, sa/*, false, mandatory*/)) {
|
if (!revealHandTargetAI(ai, sa, mandatory)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ import forge.game.zone.ZoneType;
|
|||||||
|
|
||||||
public abstract class RevealAiBase extends SpellAbilityAi {
|
public abstract class RevealAiBase extends SpellAbilityAi {
|
||||||
|
|
||||||
protected boolean revealHandTargetAI(final Player ai, final SpellAbility sa) {
|
protected boolean revealHandTargetAI(final Player ai, final SpellAbility sa, boolean mandatory) {
|
||||||
if (sa.usesTargeting()) {
|
if (sa.usesTargeting()) {
|
||||||
// ability is targeted
|
// ability is targeted
|
||||||
sa.resetTargets();
|
sa.resetTargets();
|
||||||
@@ -29,7 +29,7 @@ public abstract class RevealAiBase extends SpellAbilityAi {
|
|||||||
|
|
||||||
Player p = Collections.max(opps, PlayerPredicates.compareByZoneSize(ZoneType.Hand));
|
Player p = Collections.max(opps, PlayerPredicates.compareByZoneSize(ZoneType.Hand));
|
||||||
|
|
||||||
if (p.getCardsIn(ZoneType.Hand).isEmpty()) {
|
if (!mandatory && p.getCardsIn(ZoneType.Hand).isEmpty()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
sa.getTargets().add(p);
|
sa.getTargets().add(p);
|
||||||
@@ -45,7 +45,7 @@ public abstract class RevealAiBase extends SpellAbilityAi {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public boolean chkAIDrawback(SpellAbility sa, Player ai) {
|
public boolean chkAIDrawback(SpellAbility sa, Player ai) {
|
||||||
revealHandTargetAI(ai, sa);
|
revealHandTargetAI(ai, sa, false);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ public class RevealHandAi extends RevealAiBase {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected boolean checkApiLogic(final Player ai, final SpellAbility sa) {
|
protected boolean checkApiLogic(final Player ai, final SpellAbility sa) {
|
||||||
final boolean bFlag = revealHandTargetAI(ai, sa/*, true, false*/);
|
final boolean bFlag = revealHandTargetAI(ai, sa, false);
|
||||||
|
|
||||||
if (!bFlag) {
|
if (!bFlag) {
|
||||||
return false;
|
return false;
|
||||||
@@ -29,8 +29,7 @@ public class RevealHandAi extends RevealAiBase {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean doTriggerAINoCost(Player ai, SpellAbility sa, boolean mandatory) {
|
protected boolean doTriggerAINoCost(Player ai, SpellAbility sa, boolean mandatory) {
|
||||||
|
return revealHandTargetAI(ai, sa, mandatory);
|
||||||
return revealHandTargetAI(ai, sa/*, false, mandatory*/);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ import forge.game.replacement.ReplacementEffect;
|
|||||||
import forge.game.replacement.ReplacementType;
|
import forge.game.replacement.ReplacementType;
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
import forge.game.spellability.TargetRestrictions;
|
import forge.game.spellability.TargetRestrictions;
|
||||||
import forge.game.staticability.StaticAbility;
|
import forge.game.staticability.StaticAbilityCantAttach;
|
||||||
import forge.game.zone.ZoneType;
|
import forge.game.zone.ZoneType;
|
||||||
|
|
||||||
public abstract class GameEntity extends GameObject implements IIdentifiable {
|
public abstract class GameEntity extends GameObject implements IIdentifiable {
|
||||||
@@ -222,15 +222,7 @@ public abstract class GameEntity extends GameObject implements IIdentifiable {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// CantTarget static abilities
|
// check for rules
|
||||||
for (final Card ca : getGame().getCardsIn(ZoneType.STATIC_ABILITIES_SOURCE_ZONES)) {
|
|
||||||
for (final StaticAbility stAb : ca.getStaticAbilities()) {
|
|
||||||
if (stAb.applyAbility("CantAttach", attach, this)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (attach.isAura() && !canBeEnchantedBy(attach)) {
|
if (attach.isAura() && !canBeEnchantedBy(attach)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -241,8 +233,13 @@ public abstract class GameEntity extends GameObject implements IIdentifiable {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// check for can't attach static
|
||||||
|
if (StaticAbilityCantAttach.cantAttach(this, attach, checkSBA)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// true for all
|
// true for all
|
||||||
return !hasProtectionFrom(attach, checkSBA);
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected boolean canBeEquippedBy(final Card aura) {
|
protected boolean canBeEquippedBy(final Card aura) {
|
||||||
@@ -260,6 +257,8 @@ public abstract class GameEntity extends GameObject implements IIdentifiable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected boolean canBeEnchantedBy(final Card aura) {
|
protected boolean canBeEnchantedBy(final Card aura) {
|
||||||
|
// TODO need to check for multiple Enchant Keywords
|
||||||
|
|
||||||
SpellAbility sa = aura.getFirstAttachSpell();
|
SpellAbility sa = aura.getFirstAttachSpell();
|
||||||
TargetRestrictions tgt = null;
|
TargetRestrictions tgt = null;
|
||||||
if (sa != null) {
|
if (sa != null) {
|
||||||
@@ -269,11 +268,6 @@ public abstract class GameEntity extends GameObject implements IIdentifiable {
|
|||||||
return !((tgt != null) && !isValid(tgt.getValidTgts(), aura.getController(), aura, sa));
|
return !((tgt != null) && !isValid(tgt.getValidTgts(), aura.getController(), aura, sa));
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean hasProtectionFrom(final Card source) {
|
|
||||||
return hasProtectionFrom(source, false);
|
|
||||||
}
|
|
||||||
public abstract boolean hasProtectionFrom(final Card source, final boolean checkSBA);
|
|
||||||
|
|
||||||
// Counters!
|
// Counters!
|
||||||
public boolean hasCounters() {
|
public boolean hasCounters() {
|
||||||
return !counters.isEmpty();
|
return !counters.isEmpty();
|
||||||
|
|||||||
@@ -519,7 +519,7 @@ public abstract class SpellAbilityEffect {
|
|||||||
|
|
||||||
String valid = sa.getParamOrDefault("ReplaceDyingValid", "Card.IsRemembered");
|
String valid = sa.getParamOrDefault("ReplaceDyingValid", "Card.IsRemembered");
|
||||||
|
|
||||||
String repeffstr = "Event$ Moved | ValidCard$ " + valid +
|
String repeffstr = "Event$ Moved | ValidLKI$ " + valid +
|
||||||
"| Origin$ Battlefield | Destination$ Graveyard " +
|
"| Origin$ Battlefield | Destination$ Graveyard " +
|
||||||
"| Description$ If that permanent would die this turn, exile it instead.";
|
"| Description$ If that permanent would die this turn, exile it instead.";
|
||||||
String effect = "DB$ ChangeZone | Defined$ ReplacedCard | Origin$ Battlefield | Destination$ " + zone;
|
String effect = "DB$ ChangeZone | Defined$ ReplacedCard | Origin$ Battlefield | Destination$ " + zone;
|
||||||
|
|||||||
@@ -127,7 +127,7 @@ public abstract class AnimateEffectBase extends SpellAbilityEffect {
|
|||||||
// itself a static ability)
|
// itself a static ability)
|
||||||
final List<StaticAbility> addedStaticAbilities = Lists.newArrayList();
|
final List<StaticAbility> addedStaticAbilities = Lists.newArrayList();
|
||||||
for (final String s : stAbs) {
|
for (final String s : stAbs) {
|
||||||
addedStaticAbilities.add(new StaticAbility(AbilityUtils.getSVar(sa, s), c, sa.getCardState()));
|
addedStaticAbilities.add(StaticAbility.create(AbilityUtils.getSVar(sa, s), c, sa.getCardState(), false));
|
||||||
}
|
}
|
||||||
|
|
||||||
final GameCommand unanimate = new GameCommand() {
|
final GameCommand unanimate = new GameCommand() {
|
||||||
|
|||||||
@@ -298,7 +298,8 @@ public class CounterEffect extends SpellAbilityEffect {
|
|||||||
private static void removeFromStack(final SpellAbility tgtSA, final SpellAbility srcSA, final SpellAbilityStackInstance si, CardZoneTable triggerList) {
|
private static void removeFromStack(final SpellAbility tgtSA, final SpellAbility srcSA, final SpellAbilityStackInstance si, CardZoneTable triggerList) {
|
||||||
final Game game = tgtSA.getActivatingPlayer().getGame();
|
final Game game = tgtSA.getActivatingPlayer().getGame();
|
||||||
Card movedCard = null;
|
Card movedCard = null;
|
||||||
final Zone originZone = tgtSA.getHostCard().getZone();
|
final Card c = tgtSA.getHostCard();
|
||||||
|
final Zone originZone = c.getZone();
|
||||||
|
|
||||||
// Run any applicable replacement effects.
|
// Run any applicable replacement effects.
|
||||||
final Map<AbilityKey, Object> repParams = AbilityKey.mapFromAffected(tgtSA.getHostCard());
|
final Map<AbilityKey, Object> repParams = AbilityKey.mapFromAffected(tgtSA.getHostCard());
|
||||||
@@ -310,8 +311,8 @@ public class CounterEffect extends SpellAbilityEffect {
|
|||||||
game.getStack().remove(si);
|
game.getStack().remove(si);
|
||||||
|
|
||||||
// if the target card on stack was a spell with Bestow, then unbestow it
|
// if the target card on stack was a spell with Bestow, then unbestow it
|
||||||
if (tgtSA.getHostCard() != null && tgtSA.getHostCard().isBestowed()) {
|
if (c.isBestowed()) {
|
||||||
tgtSA.getHostCard().unanimateBestow(true);
|
c.unanimateBestow(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<AbilityKey, Object> params = AbilityKey.newMap();
|
Map<AbilityKey, Object> params = AbilityKey.newMap();
|
||||||
@@ -327,33 +328,32 @@ public class CounterEffect extends SpellAbilityEffect {
|
|||||||
// For Ability-targeted counterspells - do not move it anywhere,
|
// For Ability-targeted counterspells - do not move it anywhere,
|
||||||
// even if Destination$ is specified.
|
// even if Destination$ is specified.
|
||||||
} else if (destination.equals("Graveyard")) {
|
} else if (destination.equals("Graveyard")) {
|
||||||
movedCard = game.getAction().moveToGraveyard(tgtSA.getHostCard(), srcSA, params);
|
movedCard = game.getAction().moveToGraveyard(c, srcSA, params);
|
||||||
} else if (destination.equals("Exile")) {
|
} else if (destination.equals("Exile")) {
|
||||||
movedCard = game.getAction().exile(tgtSA.getHostCard(), srcSA, params);
|
movedCard = game.getAction().exile(c, srcSA, params);
|
||||||
} else if (destination.equals("TopOfLibrary")) {
|
} else if (destination.equals("TopOfLibrary")) {
|
||||||
movedCard = game.getAction().moveToLibrary(tgtSA.getHostCard(), srcSA, params);
|
movedCard = game.getAction().moveToLibrary(c, srcSA, params);
|
||||||
} else if (destination.equals("Hand")) {
|
} else if (destination.equals("Hand")) {
|
||||||
movedCard = game.getAction().moveToHand(tgtSA.getHostCard(), srcSA, params);
|
movedCard = game.getAction().moveToHand(c, srcSA, params);
|
||||||
} else if (destination.equals("Battlefield")) {
|
} else if (destination.equals("Battlefield")) {
|
||||||
if (tgtSA instanceof SpellPermanent) {
|
if (tgtSA instanceof SpellPermanent) {
|
||||||
Card c = tgtSA.getHostCard();
|
|
||||||
c.setController(srcSA.getActivatingPlayer(), 0);
|
c.setController(srcSA.getActivatingPlayer(), 0);
|
||||||
movedCard = game.getAction().moveToPlay(c, srcSA.getActivatingPlayer(), srcSA, params);
|
movedCard = game.getAction().moveToPlay(c, srcSA.getActivatingPlayer(), srcSA, params);
|
||||||
} else {
|
} else {
|
||||||
movedCard = game.getAction().moveToPlay(tgtSA.getHostCard(), srcSA.getActivatingPlayer(), srcSA, params);
|
movedCard = game.getAction().moveToPlay(c, srcSA.getActivatingPlayer(), srcSA, params);
|
||||||
movedCard.setController(srcSA.getActivatingPlayer(), 0);
|
movedCard.setController(srcSA.getActivatingPlayer(), 0);
|
||||||
}
|
}
|
||||||
} else if (destination.equals("BottomOfLibrary")) {
|
} else if (destination.equals("BottomOfLibrary")) {
|
||||||
movedCard = game.getAction().moveToBottomOfLibrary(tgtSA.getHostCard(), srcSA, params);
|
movedCard = game.getAction().moveToBottomOfLibrary(c, srcSA, params);
|
||||||
} else if (destination.equals("ShuffleIntoLibrary")) {
|
} else if (destination.equals("ShuffleIntoLibrary")) {
|
||||||
movedCard = game.getAction().moveToBottomOfLibrary(tgtSA.getHostCard(), srcSA, params);
|
movedCard = game.getAction().moveToBottomOfLibrary(c, srcSA, params);
|
||||||
tgtSA.getHostCard().getController().shuffle(srcSA);
|
c.getController().shuffle(srcSA);
|
||||||
} else {
|
} else {
|
||||||
throw new IllegalArgumentException("AbilityFactory_CounterMagic: Invalid Destination argument for card "
|
throw new IllegalArgumentException("AbilityFactory_CounterMagic: Invalid Destination argument for card "
|
||||||
+ srcSA.getHostCard().getName());
|
+ srcSA.getHostCard().getName());
|
||||||
}
|
}
|
||||||
// Run triggers
|
// Run triggers
|
||||||
final Map<AbilityKey, Object> runParams = AbilityKey.mapFromCard(tgtSA.getHostCard());
|
final Map<AbilityKey, Object> runParams = AbilityKey.mapFromCard(c);
|
||||||
runParams.put(AbilityKey.Player, tgtSA.getActivatingPlayer());
|
runParams.put(AbilityKey.Player, tgtSA.getActivatingPlayer());
|
||||||
runParams.put(AbilityKey.Cause, srcSA.getHostCard());
|
runParams.put(AbilityKey.Cause, srcSA.getHostCard());
|
||||||
runParams.put(AbilityKey.CounteredSA, tgtSA);
|
runParams.put(AbilityKey.CounteredSA, tgtSA);
|
||||||
|
|||||||
@@ -9,13 +9,13 @@ public class HauntEffect extends SpellAbilityEffect {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void resolve(SpellAbility sa) {
|
public void resolve(SpellAbility sa) {
|
||||||
Card card = sa.getHostCard();
|
Card host = sa.getHostCard();
|
||||||
final Game game = card.getGame();
|
final Game game = host.getGame();
|
||||||
card = game.getCardState(card, null);
|
Card card = game.getCardState(host, null);
|
||||||
if (card == null) {
|
if (card == null) {
|
||||||
return;
|
return;
|
||||||
} else if (sa.usesTargeting() && !card.isToken()) {
|
} else if (sa.usesTargeting() && !card.isToken() && host.equalsWithTimestamp(card)) {
|
||||||
// haunt target but only if card is no token
|
// haunt target but only if card is no token and still in grave
|
||||||
final Card copy = game.getAction().exile(card, sa);
|
final Card copy = game.getAction().exile(card, sa);
|
||||||
sa.getTargets().getFirstTargetedCard().addHauntedBy(copy);
|
sa.getTargets().getFirstTargetedCard().addHauntedBy(copy);
|
||||||
} else if (!sa.usesTargeting() && card.getHaunting() != null) {
|
} else if (!sa.usesTargeting() && card.getHaunting() != null) {
|
||||||
|
|||||||
@@ -24,7 +24,6 @@ public class ReplaceCounterEffect extends SpellAbilityEffect {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void resolve(SpellAbility sa) {
|
public void resolve(SpellAbility sa) {
|
||||||
|
|
||||||
final Card card = sa.getHostCard();
|
final Card card = sa.getHostCard();
|
||||||
|
|
||||||
// outside of Replacement Effect, unwanted result
|
// outside of Replacement Effect, unwanted result
|
||||||
@@ -64,8 +63,6 @@ public class ReplaceCounterEffect extends SpellAbilityEffect {
|
|||||||
counterTable.get(p).put(e.getKey(), value);
|
counterTable.get(p).put(e.getKey(), value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
for (Map.Entry<Optional<Player>, Map<CounterType, Integer>> e : counterTable.entrySet()) {
|
for (Map.Entry<Optional<Player>, Map<CounterType, Integer>> e : counterTable.entrySet()) {
|
||||||
if (!sa.matchesValidParam("ValidSource", e.getKey().orNull())) {
|
if (!sa.matchesValidParam("ValidSource", e.getKey().orNull())) {
|
||||||
|
|||||||
@@ -1912,9 +1912,6 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|
|||||||
for (KeywordInterface inst : keywords) {
|
for (KeywordInterface inst : keywords) {
|
||||||
String keyword = inst.getOriginal();
|
String keyword = inst.getOriginal();
|
||||||
try {
|
try {
|
||||||
if (keyword.startsWith("SpellCantTarget")) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (keyword.startsWith("CantBeCounteredBy")) {
|
if (keyword.startsWith("CantBeCounteredBy")) {
|
||||||
final String[] p = keyword.split(":");
|
final String[] p = keyword.split(":");
|
||||||
sbLong.append(p[2]).append("\r\n");
|
sbLong.append(p[2]).append("\r\n");
|
||||||
@@ -4596,8 +4593,7 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|
|||||||
}
|
}
|
||||||
public final StaticAbility addStaticAbility(final String s) {
|
public final StaticAbility addStaticAbility(final String s) {
|
||||||
if (!s.trim().isEmpty()) {
|
if (!s.trim().isEmpty()) {
|
||||||
final StaticAbility stAb = new StaticAbility(s, this, currentState);
|
final StaticAbility stAb = StaticAbility.create(s, this, currentState, true);
|
||||||
stAb.setIntrinsic(true);
|
|
||||||
currentState.addStaticAbility(stAb);
|
currentState.addStaticAbility(stAb);
|
||||||
return stAb;
|
return stAb;
|
||||||
}
|
}
|
||||||
@@ -5693,103 +5689,6 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|
|||||||
public Card getMeldedWith() { return meldedWith; }
|
public Card getMeldedWith() { return meldedWith; }
|
||||||
public void setMeldedWith(Card meldedWith) { this.meldedWith = meldedWith; }
|
public void setMeldedWith(Card meldedWith) { this.meldedWith = meldedWith; }
|
||||||
|
|
||||||
public boolean hasProtectionFrom(final Card source) {
|
|
||||||
return hasProtectionFrom(source, false, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean hasProtectionFrom(final Card source, final boolean checkSBA) {
|
|
||||||
return hasProtectionFrom(source, checkSBA, false);
|
|
||||||
}
|
|
||||||
public boolean hasProtectionFrom(final Card source, final boolean checkSBA, final boolean damageSource) {
|
|
||||||
if (source == null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isImmutable()) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Protection only works on the Battlefield
|
|
||||||
if (!isInPlay()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
final boolean colorlessDamage = damageSource && source.hasKeyword("Colorless Damage Source");
|
|
||||||
|
|
||||||
for (final KeywordInterface inst : getKeywords(Keyword.PROTECTION)) {
|
|
||||||
String kw = inst.getOriginal();
|
|
||||||
if (kw.equals("Protection from white")) {
|
|
||||||
if (source.isWhite() && !colorlessDamage) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
} else if (kw.equals("Protection from blue")) {
|
|
||||||
if (source.isBlue() && !colorlessDamage) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
} else if (kw.equals("Protection from black")) {
|
|
||||||
if (source.isBlack() && !colorlessDamage) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
} else if (kw.equals("Protection from red")) {
|
|
||||||
if (source.isRed() && !colorlessDamage) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
} else if (kw.equals("Protection from green")) {
|
|
||||||
if (source.isGreen() && !colorlessDamage) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
} else if (kw.equals("Protection from all colors")) {
|
|
||||||
if (!source.isColorless() && !colorlessDamage) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
} else if (kw.equals("Protection from colorless")) {
|
|
||||||
if (source.isColorless() || colorlessDamage) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
} else if (kw.equals("Protection from everything")) {
|
|
||||||
return true;
|
|
||||||
} else if (kw.startsWith("Protection:")) { // uses isValid; Protection:characteristic:desc:exception
|
|
||||||
final String[] kws = kw.split(":");
|
|
||||||
String characteristic = kws[1];
|
|
||||||
|
|
||||||
if (characteristic.startsWith("Player")) {
|
|
||||||
// TODO need to handle that better in CardProperty
|
|
||||||
if (source.getController().isValid(characteristic.split(","), getController(), this, null)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// if damageSource then it does only check damage color..
|
|
||||||
if (damageSource) {
|
|
||||||
if (characteristic.endsWith("White") || characteristic.endsWith("Blue")
|
|
||||||
|| characteristic.endsWith("Black") || characteristic.endsWith("Red")
|
|
||||||
|| characteristic.endsWith("Green") || characteristic.endsWith("Colorless")
|
|
||||||
|| characteristic.endsWith("MonoColor") || characteristic.endsWith("MultiColor")) {
|
|
||||||
characteristic += "Source";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
final String[] characteristics = characteristic.split(",");
|
|
||||||
final String[] exceptions = kws.length > 3 ? kws[3].split(",") : null; // check "This effect cannot remove sth"
|
|
||||||
if (source.isValid(characteristics, getController(), this, null)
|
|
||||||
&& (!checkSBA || exceptions == null || !source.isValid(exceptions, getController(), this, null))) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (kw.startsWith("Protection from opponent of ")) {
|
|
||||||
final String playerName = kw.substring("Protection from opponent of ".length());
|
|
||||||
if (source.getController().isOpponentOf(playerName)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
} else if (kw.startsWith("Protection from ")) {
|
|
||||||
final String protectType = CardType.getSingularType(kw.substring("Protection from ".length()));
|
|
||||||
if (source.getType().hasStringType(protectType)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
public String getProtectionKey() {
|
public String getProtectionKey() {
|
||||||
String protectKey = "";
|
String protectKey = "";
|
||||||
boolean pR = false; boolean pG = false; boolean pB = false; boolean pU = false; boolean pW = false;
|
boolean pR = false; boolean pG = false; boolean pB = false; boolean pU = false; boolean pW = false;
|
||||||
@@ -5933,30 +5832,10 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hasProtectionFrom(sa.getHostCard())) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isPhasedOut()) {
|
if (isPhasedOut()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
final Card source = sa.getHostCard();
|
|
||||||
|
|
||||||
if (sa.isSpell()) {
|
|
||||||
// TODO replace with Static Ability
|
|
||||||
for (KeywordInterface inst : source.getKeywords()) {
|
|
||||||
String kw = inst.getOriginal();
|
|
||||||
if (!kw.startsWith("SpellCantTarget")) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
final String[] k = kw.split(":");
|
|
||||||
final String[] restrictions = k[1].split(",");
|
|
||||||
if (isValid(restrictions, source.getController(), source, null)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -745,9 +745,7 @@ public class CardFactory {
|
|||||||
for (final String s : str.split(",")) {
|
for (final String s : str.split(",")) {
|
||||||
if (origSVars.containsKey(s)) {
|
if (origSVars.containsKey(s)) {
|
||||||
final String actualStatic = origSVars.get(s);
|
final String actualStatic = origSVars.get(s);
|
||||||
final StaticAbility grantedStatic = new StaticAbility(actualStatic, out, sa.getCardState());
|
state.addStaticAbility(StaticAbility.create(actualStatic, out, sa.getCardState(), true));
|
||||||
grantedStatic.setIntrinsic(true);
|
|
||||||
state.addStaticAbility(grantedStatic);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -677,13 +677,13 @@ public class CardFactoryUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static String getProtectionValid(final String kw, final boolean damage) {
|
public static String getProtectionValid(final String kw, final boolean damage) {
|
||||||
String validSource = "Card."; // TODO extend for Emblem too
|
String validSource = "";
|
||||||
|
|
||||||
if (kw.startsWith("Protection:")) {
|
if (kw.startsWith("Protection:")) {
|
||||||
final String[] kws = kw.split(":");
|
final String[] kws = kw.split(":");
|
||||||
String characteristic = kws[1];
|
String characteristic = kws[1];
|
||||||
if (characteristic.startsWith("Player")) {
|
if (characteristic.startsWith("Player")) {
|
||||||
validSource += "ControlledBy " + characteristic;
|
validSource = "ControlledBy " + characteristic;
|
||||||
} else {
|
} else {
|
||||||
if (damage && (characteristic.endsWith("White") || characteristic.endsWith("Blue")
|
if (damage && (characteristic.endsWith("White") || characteristic.endsWith("Blue")
|
||||||
|| characteristic.endsWith("Black") || characteristic.endsWith("Red")
|
|| characteristic.endsWith("Black") || characteristic.endsWith("Red")
|
||||||
@@ -691,34 +691,37 @@ public class CardFactoryUtil {
|
|||||||
|| characteristic.endsWith("MonoColor") || characteristic.endsWith("MultiColor"))) {
|
|| characteristic.endsWith("MonoColor") || characteristic.endsWith("MultiColor"))) {
|
||||||
characteristic += "Source";
|
characteristic += "Source";
|
||||||
}
|
}
|
||||||
validSource = characteristic;
|
return characteristic;
|
||||||
}
|
}
|
||||||
} else if (kw.startsWith("Protection from ")) {
|
} else if (kw.startsWith("Protection from ")) {
|
||||||
String protectType = kw.substring("Protection from ".length());
|
String protectType = kw.substring("Protection from ".length());
|
||||||
if (protectType.equals("white")) {
|
if (protectType.equals("white")) {
|
||||||
validSource += "White" + (damage ? "Source" : "");
|
validSource = "White" + (damage ? "Source" : "");
|
||||||
} else if (protectType.equals("blue")) {
|
} else if (protectType.equals("blue")) {
|
||||||
validSource += "Blue" + (damage ? "Source" : "");
|
validSource = "Blue" + (damage ? "Source" : "");
|
||||||
} else if (protectType.equals("black")) {
|
} else if (protectType.equals("black")) {
|
||||||
validSource += "Black" + (damage ? "Source" : "");
|
validSource = "Black" + (damage ? "Source" : "");
|
||||||
} else if (protectType.equals("red")) {
|
} else if (protectType.equals("red")) {
|
||||||
validSource += "Red" + (damage ? "Source" : "");
|
validSource = "Red" + (damage ? "Source" : "");
|
||||||
} else if (protectType.equals("green")) {
|
} else if (protectType.equals("green")) {
|
||||||
validSource += "Green" + (damage ? "Source" : "");
|
validSource = "Green" + (damage ? "Source" : "");
|
||||||
} else if (protectType.equals("colorless")) {
|
} else if (protectType.equals("colorless")) {
|
||||||
validSource += "Colorless" + (damage ? "Source" : "");
|
validSource = "Colorless" + (damage ? "Source" : "");
|
||||||
} else if (protectType.equals("all colors")) {
|
} else if (protectType.equals("all colors")) {
|
||||||
validSource += "nonColorless" + (damage ? "Source" : "");
|
validSource = "nonColorless" + (damage ? "Source" : "");
|
||||||
} else if (protectType.equals("everything")) {
|
} else if (protectType.equals("everything")) {
|
||||||
validSource = "";
|
return "";
|
||||||
} else if (protectType.startsWith("opponent of ")) {
|
} else if (protectType.startsWith("opponent of ")) {
|
||||||
final String playerName = protectType.substring("opponent of ".length());
|
final String playerName = protectType.substring("opponent of ".length());
|
||||||
validSource += "ControlledBy Player.OpponentOf PlayerNamed_" + playerName;
|
validSource = "ControlledBy Player.OpponentOf PlayerNamed_" + playerName;
|
||||||
} else {
|
} else {
|
||||||
validSource = CardType.getSingularType(protectType);
|
validSource = CardType.getSingularType(protectType);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return validSource;
|
if (validSource.isEmpty()) {
|
||||||
|
return validSource;
|
||||||
|
}
|
||||||
|
return "Card." + validSource + ",Emblem." + validSource;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ReplacementEffect makeEtbCounter(final String kw, final CardState card, final boolean intrinsic) {
|
public static ReplacementEffect makeEtbCounter(final String kw, final CardState card, final boolean intrinsic) {
|
||||||
@@ -1290,13 +1293,12 @@ public class CardFactoryUtil {
|
|||||||
triggers.add(haunterETB);
|
triggers.add(haunterETB);
|
||||||
}
|
}
|
||||||
|
|
||||||
// First, create trigger that runs when the haunter goes to the
|
// First, create trigger that runs when the haunter goes to the graveyard
|
||||||
// graveyard
|
|
||||||
final StringBuilder sbHaunter = new StringBuilder();
|
final StringBuilder sbHaunter = new StringBuilder();
|
||||||
sbHaunter.append("Mode$ ChangesZone | Origin$ ");
|
sbHaunter.append("Mode$ ChangesZone | Origin$ ");
|
||||||
sbHaunter.append(card.isCreature() ? "Battlefield" : "Stack");
|
sbHaunter.append(card.isCreature() ? "Battlefield" : "Stack | ResolvedCard$ True");
|
||||||
sbHaunter.append(" | Destination$ Graveyard | ValidCard$ Card.Self");
|
sbHaunter.append(" | Destination$ Graveyard | ValidCard$ Card.Self");
|
||||||
sbHaunter.append(" | Static$ True | Secondary$ True | TriggerDescription$ Blank");
|
sbHaunter.append(" | Secondary$ True | TriggerDescription$ " + inst.getReminderText());
|
||||||
|
|
||||||
final Trigger haunterDies = TriggerHandler.parseTrigger(sbHaunter.toString(), card, intrinsic);
|
final Trigger haunterDies = TriggerHandler.parseTrigger(sbHaunter.toString(), card, intrinsic);
|
||||||
|
|
||||||
@@ -2965,7 +2967,7 @@ public class CardFactoryUtil {
|
|||||||
final String manacost = k[1];
|
final String manacost = k[1];
|
||||||
|
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
sb.append("AB$ PutCounter| Cost$ ").append(manacost);
|
sb.append("AB$ PutCounter | Cost$ ").append(manacost);
|
||||||
sb.append(" | PrecostDesc$ Level Up | CostDesc$ ").append(ManaCostParser.parse(manacost));
|
sb.append(" | PrecostDesc$ Level Up | CostDesc$ ").append(ManaCostParser.parse(manacost));
|
||||||
sb.append(" | SorcerySpeed$ True | LevelUp$ True | CounterNum$ 1 | CounterType$ LEVEL");
|
sb.append(" | SorcerySpeed$ True | LevelUp$ True | CounterNum$ 1 | CounterType$ LEVEL");
|
||||||
if (card.hasSVar("maxLevel")) {
|
if (card.hasSVar("maxLevel")) {
|
||||||
@@ -3398,8 +3400,6 @@ public class CardFactoryUtil {
|
|||||||
|
|
||||||
public static void addStaticAbility(final KeywordInterface inst, final CardState state, final boolean intrinsic) {
|
public static void addStaticAbility(final KeywordInterface inst, final CardState state, final boolean intrinsic) {
|
||||||
String keyword = inst.getOriginal();
|
String keyword = inst.getOriginal();
|
||||||
String effect = null;
|
|
||||||
Map<String, String> svars = Maps.newHashMap();
|
|
||||||
|
|
||||||
if (keyword.startsWith("Affinity")) {
|
if (keyword.startsWith("Affinity")) {
|
||||||
final String[] k = keyword.split(":");
|
final String[] k = keyword.split(":");
|
||||||
@@ -3419,20 +3419,24 @@ public class CardFactoryUtil {
|
|||||||
sb.append("Mode$ ReduceCost | ValidCard$ Card.Self | Type$ Spell | Amount$ AffinityX | EffectZone$ All");
|
sb.append("Mode$ ReduceCost | ValidCard$ Card.Self | Type$ Spell | Amount$ AffinityX | EffectZone$ All");
|
||||||
sb.append("| Description$ Affinity for ").append(desc);
|
sb.append("| Description$ Affinity for ").append(desc);
|
||||||
sb.append(" (").append(inst.getReminderText()).append(")");
|
sb.append(" (").append(inst.getReminderText()).append(")");
|
||||||
effect = sb.toString();
|
String effect = sb.toString();
|
||||||
|
|
||||||
svars.put("AffinityX", "Count$Valid " + t + ".YouCtrl");
|
StaticAbility st = StaticAbility.create(effect, state.getCard(), state, intrinsic);
|
||||||
|
|
||||||
|
st.setSVar("AffinityX", "Count$Valid " + t + ".YouCtrl");
|
||||||
|
inst.addStaticAbility(st);
|
||||||
} else if (keyword.equals("Changeling")) {
|
} else if (keyword.equals("Changeling")) {
|
||||||
effect = "Mode$ Continuous | EffectZone$ All | Affected$ Card.Self" +
|
String effect = "Mode$ Continuous | EffectZone$ All | Affected$ Card.Self" +
|
||||||
" | CharacteristicDefining$ True | AddAllCreatureTypes$ True | Secondary$ True" +
|
" | CharacteristicDefining$ True | AddAllCreatureTypes$ True | Secondary$ True" +
|
||||||
" | Description$ Changeling (" + inst.getReminderText() + ")";
|
" | Description$ Changeling (" + inst.getReminderText() + ")";
|
||||||
|
inst.addStaticAbility(StaticAbility.create(effect, state.getCard(), state, intrinsic));
|
||||||
} else if (keyword.equals("Cipher")) {
|
} else if (keyword.equals("Cipher")) {
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
sb.append("Mode$ Continuous | EffectZone$ Exile | Affected$ Card.EncodedWithSource");
|
sb.append("Mode$ Continuous | EffectZone$ Exile | Affected$ Card.EncodedWithSource");
|
||||||
sb.append(" | AddTrigger$ CipherTrigger");
|
sb.append(" | AddTrigger$ CipherTrigger");
|
||||||
sb.append(" | Description$ Cipher (").append(inst.getReminderText()).append(")");
|
sb.append(" | Description$ Cipher (").append(inst.getReminderText()).append(")");
|
||||||
|
|
||||||
effect = sb.toString();
|
String effect = sb.toString();
|
||||||
|
|
||||||
sb = new StringBuilder();
|
sb = new StringBuilder();
|
||||||
|
|
||||||
@@ -3445,8 +3449,12 @@ public class CardFactoryUtil {
|
|||||||
|
|
||||||
String ab = "DB$ Play | Defined$ OriginalHost | WithoutManaCost$ True | CopyCard$ True";
|
String ab = "DB$ Play | Defined$ OriginalHost | WithoutManaCost$ True | CopyCard$ True";
|
||||||
|
|
||||||
svars.put("CipherTrigger", trig);
|
StaticAbility st = StaticAbility.create(effect, state.getCard(), state, intrinsic);
|
||||||
svars.put("PlayEncoded", ab);
|
|
||||||
|
st.setSVar("CipherTrigger", trig);
|
||||||
|
st.setSVar("PlayEncoded", ab);
|
||||||
|
|
||||||
|
inst.addStaticAbility(st);
|
||||||
} else if (keyword.startsWith("Class")) {
|
} else if (keyword.startsWith("Class")) {
|
||||||
final String[] k = keyword.split(":");
|
final String[] k = keyword.split(":");
|
||||||
final String level = k[1];
|
final String level = k[1];
|
||||||
@@ -3475,24 +3483,32 @@ public class CardFactoryUtil {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
effect = "Mode$ Continuous | Affected$ Card.Self | ClassLevel$ " + level + " | " + params;
|
String effect = "Mode$ Continuous | Affected$ Card.Self | ClassLevel$ " + level + " | " + params;
|
||||||
if (descAdded) {
|
if (descAdded) {
|
||||||
effect += " | Description$ " + desc.toString();
|
effect += " | Description$ " + desc.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inst.addStaticAbility(StaticAbility.create(effect, state.getCard(), state, intrinsic));
|
||||||
} else if (keyword.startsWith("Dash")) {
|
} else if (keyword.startsWith("Dash")) {
|
||||||
effect = "Mode$ Continuous | Affected$ Card.Self+dashed | AddKeyword$ Haste";
|
String effect = "Mode$ Continuous | Affected$ Card.Self+dashed | AddKeyword$ Haste";
|
||||||
|
inst.addStaticAbility(StaticAbility.create(effect, state.getCard(), state, intrinsic));
|
||||||
} else if (keyword.equals("Daybound")) {
|
} else if (keyword.equals("Daybound")) {
|
||||||
effect = "Mode$ CantTransform | ValidCard$ Creature.Self | ExceptCause$ SpellAbility.Daybound | Secondary$ True | Description$ This permanent can't be transformed except by its daybound ability.";
|
String effect = "Mode$ CantTransform | ValidCard$ Creature.Self | ExceptCause$ SpellAbility.Daybound | Secondary$ True | Description$ This permanent can't be transformed except by its daybound ability.";
|
||||||
|
inst.addStaticAbility(StaticAbility.create(effect, state.getCard(), state, intrinsic));
|
||||||
} else if (keyword.equals("Decayed")) {
|
} else if (keyword.equals("Decayed")) {
|
||||||
effect = "Mode$ Continuous | Affected$ Card.Self | AddHiddenKeyword$ CARDNAME can't block. | " +
|
String effect = "Mode$ Continuous | Affected$ Card.Self | AddHiddenKeyword$ CARDNAME can't block. | " +
|
||||||
"Secondary$ True";
|
"Secondary$ True";
|
||||||
svars.put("SacrificeEndCombat", "True");
|
StaticAbility st = StaticAbility.create(effect, state.getCard(), state, intrinsic);
|
||||||
|
st.setSVar("SacrificeEndCombat", "True");
|
||||||
|
inst.addStaticAbility(st);
|
||||||
} else if (keyword.equals("Defender")) {
|
} else if (keyword.equals("Defender")) {
|
||||||
effect = "Mode$ CantAttack | ValidCard$ Card.Self | DefenderKeyword$ True | Secondary$ True";
|
String effect = "Mode$ CantAttack | ValidCard$ Card.Self | DefenderKeyword$ True | Secondary$ True";
|
||||||
|
inst.addStaticAbility(StaticAbility.create(effect, state.getCard(), state, intrinsic));
|
||||||
} else if (keyword.equals("Devoid")) {
|
} else if (keyword.equals("Devoid")) {
|
||||||
effect = "Mode$ Continuous | EffectZone$ All | Affected$ Card.Self" +
|
String effect = "Mode$ Continuous | EffectZone$ All | Affected$ Card.Self" +
|
||||||
" | CharacteristicDefining$ True | SetColor$ Colorless | Secondary$ True" +
|
" | CharacteristicDefining$ True | SetColor$ Colorless | Secondary$ True" +
|
||||||
" | Description$ Devoid (" + inst.getReminderText() + ")";
|
" | Description$ Devoid (" + inst.getReminderText() + ")";
|
||||||
|
inst.addStaticAbility(StaticAbility.create(effect, state.getCard(), state, intrinsic));
|
||||||
} else if (keyword.startsWith("Escalate")) {
|
} else if (keyword.startsWith("Escalate")) {
|
||||||
final String[] k = keyword.split(":");
|
final String[] k = keyword.split(":");
|
||||||
final String manacost = k[1];
|
final String manacost = k[1];
|
||||||
@@ -3506,15 +3522,18 @@ public class CardFactoryUtil {
|
|||||||
}
|
}
|
||||||
sb.append(cost.toSimpleString());
|
sb.append(cost.toSimpleString());
|
||||||
|
|
||||||
effect = "Mode$ RaiseCost | ValidCard$ Card.Self | Type$ Spell | Secondary$ True"
|
String effect = "Mode$ RaiseCost | ValidCard$ Card.Self | Type$ Spell | Secondary$ True"
|
||||||
+ " | Amount$ Escalate | Cost$ "+ manacost +" | EffectZone$ All"
|
+ " | Amount$ Escalate | Cost$ "+ manacost +" | EffectZone$ All"
|
||||||
+ " | Description$ " + sb.toString() + " (" + inst.getReminderText() + ")";
|
+ " | Description$ " + sb.toString() + " (" + inst.getReminderText() + ")";
|
||||||
|
inst.addStaticAbility(StaticAbility.create(effect, state.getCard(), state, intrinsic));
|
||||||
} else if (keyword.equals("Fear")) {
|
} else if (keyword.equals("Fear")) {
|
||||||
effect = "Mode$ CantBlockBy | ValidAttacker$ Creature.Self | ValidBlocker$ Creature.nonArtifact+nonBlack | Secondary$ True " +
|
String effect = "Mode$ CantBlockBy | ValidAttacker$ Creature.Self | ValidBlocker$ Creature.nonArtifact+nonBlack | Secondary$ True " +
|
||||||
" | Description$ Fear ( " + inst.getReminderText() + ")";
|
" | Description$ Fear ( " + inst.getReminderText() + ")";
|
||||||
|
inst.addStaticAbility(StaticAbility.create(effect, state.getCard(), state, intrinsic));
|
||||||
} else if (keyword.equals("Flying")) {
|
} else if (keyword.equals("Flying")) {
|
||||||
effect = "Mode$ CantBlockBy | ValidAttacker$ Creature.Self | ValidBlocker$ Creature.withoutFlying+withoutReach | Secondary$ True " +
|
String effect = "Mode$ CantBlockBy | ValidAttacker$ Creature.Self | ValidBlocker$ Creature.withoutFlying+withoutReach | Secondary$ True " +
|
||||||
" | Description$ Flying ( " + inst.getReminderText() + ")";
|
" | Description$ Flying ( " + inst.getReminderText() + ")";
|
||||||
|
inst.addStaticAbility(StaticAbility.create(effect, state.getCard(), state, intrinsic));
|
||||||
} else if (keyword.startsWith("Hexproof")) {
|
} else if (keyword.startsWith("Hexproof")) {
|
||||||
final StringBuilder sbDesc = new StringBuilder("Hexproof");
|
final StringBuilder sbDesc = new StringBuilder("Hexproof");
|
||||||
final StringBuilder sbValid = new StringBuilder();
|
final StringBuilder sbValid = new StringBuilder();
|
||||||
@@ -3526,55 +3545,84 @@ public class CardFactoryUtil {
|
|||||||
sbValid.append("| ValidSource$ ").append(k[1]);
|
sbValid.append("| ValidSource$ ").append(k[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
effect = "Mode$ CantTarget | Hexproof$ True | ValidCard$ Card.Self | Secondary$ True"
|
String effect = "Mode$ CantTarget | Hexproof$ True | ValidCard$ Card.Self | Secondary$ True"
|
||||||
+ sbValid.toString() + " | Activator$ Opponent | Description$ "
|
+ sbValid.toString() + " | Activator$ Opponent | Description$ "
|
||||||
+ sbDesc.toString() + " (" + inst.getReminderText() + ")";
|
+ sbDesc.toString() + " (" + inst.getReminderText() + ")";
|
||||||
|
inst.addStaticAbility(StaticAbility.create(effect, state.getCard(), state, intrinsic));
|
||||||
} else if (keyword.equals("Horsemanship")) {
|
} else if (keyword.equals("Horsemanship")) {
|
||||||
effect = "Mode$ CantBlockBy | ValidAttacker$ Creature.Self | ValidBlocker$ Creature.withoutHorsemanship | Secondary$ True " +
|
String effect = "Mode$ CantBlockBy | ValidAttacker$ Creature.Self | ValidBlocker$ Creature.withoutHorsemanship | Secondary$ True " +
|
||||||
" | Description$ Horsemanship ( " + inst.getReminderText() + ")";
|
" | Description$ Horsemanship ( " + inst.getReminderText() + ")";
|
||||||
|
inst.addStaticAbility(StaticAbility.create(effect, state.getCard(), state, intrinsic));
|
||||||
} else if (keyword.equals("Intimidate")) {
|
} else if (keyword.equals("Intimidate")) {
|
||||||
effect = "Mode$ CantBlockBy | ValidAttacker$ Creature.Self | ValidBlocker$ Creature.nonArtifact+notSharesColorWith | Secondary$ True " +
|
String effect = "Mode$ CantBlockBy | ValidAttacker$ Creature.Self | ValidBlocker$ Creature.nonArtifact+notSharesColorWith | Secondary$ True " +
|
||||||
" | Description$ Intimidate ( " + inst.getReminderText() + ")";
|
" | Description$ Intimidate ( " + inst.getReminderText() + ")";
|
||||||
|
inst.addStaticAbility(StaticAbility.create(effect, state.getCard(), state, intrinsic));
|
||||||
} else if (keyword.equals("Nightbound")) {
|
} else if (keyword.equals("Nightbound")) {
|
||||||
effect = "Mode$ CantTransform | ValidCard$ Creature.Self | ExceptCause$ SpellAbility.Nightbound | Secondary$ True | Description$ This permanent can't be transformed except by its nightbound ability.";
|
String effect = "Mode$ CantTransform | ValidCard$ Creature.Self | ExceptCause$ SpellAbility.Nightbound | Secondary$ True | Description$ This permanent can't be transformed except by its nightbound ability.";
|
||||||
|
inst.addStaticAbility(StaticAbility.create(effect, state.getCard(), state, intrinsic));
|
||||||
} else if (keyword.startsWith("Protection")) {
|
} else if (keyword.startsWith("Protection")) {
|
||||||
String valid = getProtectionValid(keyword, false);
|
String valid = getProtectionValid(keyword, false);
|
||||||
effect = "Mode$ CantBlockBy | ValidAttacker$ Creature.Self ";
|
|
||||||
|
// Block
|
||||||
|
String effect = "Mode$ CantBlockBy | ValidAttacker$ Creature.Self | Secondary$ True ";
|
||||||
|
String desc = "Protection ( " + inst.getReminderText() + ")";
|
||||||
if (!valid.isEmpty()) {
|
if (!valid.isEmpty()) {
|
||||||
effect += "| ValidBlocker$ " + valid;
|
effect += "| ValidBlocker$ " + valid;
|
||||||
}
|
}
|
||||||
effect += " | Secondary$ True | Description$ Protection ( " + inst.getReminderText() + ")";
|
effect += " | Description$ " + desc;
|
||||||
|
inst.addStaticAbility(StaticAbility.create(effect, state.getCard(), state, intrinsic));
|
||||||
|
|
||||||
|
// Target
|
||||||
|
effect = "Mode$ CantTarget | Protection$ True | ValidCard$ Card.Self | Secondary$ True ";
|
||||||
|
if (!valid.isEmpty()) {
|
||||||
|
effect += "| ValidSource$ " + valid;
|
||||||
|
}
|
||||||
|
effect += " | Description$ " + desc;
|
||||||
|
inst.addStaticAbility(StaticAbility.create(effect, state.getCard(), state, intrinsic));
|
||||||
|
|
||||||
|
// Attach
|
||||||
|
effect = "Mode$ CantAttach | Protection$ True | Target$ Card.Self | Secondary$ True ";
|
||||||
|
if (!valid.isEmpty()) {
|
||||||
|
effect += "| ValidCard$ " + valid;
|
||||||
|
}
|
||||||
|
// This effect doesn't remove something
|
||||||
|
if (keyword.startsWith("Protection:")) {
|
||||||
|
final String[] kws = keyword.split(":");
|
||||||
|
if (kws.length > 3) {
|
||||||
|
effect += "| Exceptions$ " + kws[3];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
effect += " | Description$ " + desc;
|
||||||
|
inst.addStaticAbility(StaticAbility.create(effect, state.getCard(), state, intrinsic));
|
||||||
} else if (keyword.equals("Shroud")) {
|
} else if (keyword.equals("Shroud")) {
|
||||||
effect = "Mode$ CantTarget | Shroud$ True | ValidCard$ Card.Self | Secondary$ True"
|
String effect = "Mode$ CantTarget | Shroud$ True | ValidCard$ Card.Self | Secondary$ True"
|
||||||
+ " | Description$ Shroud (" + inst.getReminderText() + ")";
|
+ " | Description$ Shroud (" + inst.getReminderText() + ")";
|
||||||
|
inst.addStaticAbility(StaticAbility.create(effect, state.getCard(), state, intrinsic));
|
||||||
} else if (keyword.equals("Skulk")) {
|
} else if (keyword.equals("Skulk")) {
|
||||||
effect = "Mode$ CantBlockBy | ValidAttacker$ Creature.Self | ValidBlocker$ Creature.powerGTX | Secondary$ True " +
|
String effect = "Mode$ CantBlockBy | ValidAttacker$ Creature.Self | ValidBlocker$ Creature.powerGTX | Secondary$ True " +
|
||||||
" | Description$ Skulk ( " + inst.getReminderText() + ")";
|
" | Description$ Skulk ( " + inst.getReminderText() + ")";
|
||||||
svars.put("X", "Count$CardPower");
|
StaticAbility st = StaticAbility.create(effect, state.getCard(), state, intrinsic);
|
||||||
|
st.setSVar("X", "Count$CardPower");
|
||||||
|
inst.addStaticAbility(st);
|
||||||
} else if (keyword.startsWith("Strive")) {
|
} else if (keyword.startsWith("Strive")) {
|
||||||
final String[] k = keyword.split(":");
|
final String[] k = keyword.split(":");
|
||||||
final String manacost = k[1];
|
final String manacost = k[1];
|
||||||
|
|
||||||
effect = "Mode$ RaiseCost | ValidCard$ Card.Self | Type$ Spell | Amount$ Strive | Cost$ "+ manacost +" | EffectZone$ All" +
|
String effect = "Mode$ RaiseCost | ValidCard$ Card.Self | Type$ Spell | Amount$ Strive | Cost$ "+ manacost +" | EffectZone$ All" +
|
||||||
" | Description$ Strive - " + inst.getReminderText();
|
" | Description$ Strive - " + inst.getReminderText();
|
||||||
|
inst.addStaticAbility(StaticAbility.create(effect, state.getCard(), state, intrinsic));
|
||||||
} else if (keyword.equals("Unleash")) {
|
} else if (keyword.equals("Unleash")) {
|
||||||
effect = "Mode$ Continuous | Affected$ Card.Self+counters_GE1_P1P1 | AddHiddenKeyword$ CARDNAME can't block.";
|
String effect = "Mode$ Continuous | Affected$ Card.Self+counters_GE1_P1P1 | AddHiddenKeyword$ CARDNAME can't block.";
|
||||||
|
inst.addStaticAbility(StaticAbility.create(effect, state.getCard(), state, intrinsic));
|
||||||
} else if (keyword.equals("Undaunted")) {
|
} else if (keyword.equals("Undaunted")) {
|
||||||
effect = "Mode$ ReduceCost | ValidCard$ Card.Self | Type$ Spell | Secondary$ True"
|
String effect = "Mode$ ReduceCost | ValidCard$ Card.Self | Type$ Spell | Secondary$ True"
|
||||||
+ "| Amount$ Undaunted | EffectZone$ All | Description$ Undaunted (" + inst.getReminderText() + ")";
|
+ "| Amount$ Undaunted | EffectZone$ All | Description$ Undaunted (" + inst.getReminderText() + ")";
|
||||||
|
inst.addStaticAbility(StaticAbility.create(effect, state.getCard(), state, intrinsic));
|
||||||
} else if (keyword.equals("MayFlashSac")) {
|
} else if (keyword.equals("MayFlashSac")) {
|
||||||
effect = "Mode$ Continuous | EffectZone$ All | Affected$ Card.Self | Secondary$ True | MayPlay$ True"
|
String effect = "Mode$ Continuous | EffectZone$ All | Affected$ Card.Self | Secondary$ True | MayPlay$ True"
|
||||||
+ " | MayPlayNotSorcerySpeed$ True | MayPlayWithFlash$ True | MayPlayText$ Sacrifice at the next cleanup step"
|
+ " | MayPlayNotSorcerySpeed$ True | MayPlayWithFlash$ True | MayPlayText$ Sacrifice at the next cleanup step"
|
||||||
+ " | AffectedZone$ Exile,Graveyard,Hand,Library,Stack | Description$ " + inst.getReminderText();
|
+ " | AffectedZone$ Exile,Graveyard,Hand,Library,Stack | Description$ " + inst.getReminderText();
|
||||||
}
|
inst.addStaticAbility(StaticAbility.create(effect, state.getCard(), state, intrinsic));
|
||||||
|
|
||||||
if (effect != null) {
|
|
||||||
StaticAbility st = new StaticAbility(effect, state.getCard(), state);
|
|
||||||
st.setIntrinsic(intrinsic);
|
|
||||||
for (Map.Entry<String, String> e : svars.entrySet()) {
|
|
||||||
st.setSVar(e.getKey(), e.getValue());
|
|
||||||
}
|
|
||||||
inst.addStaticAbility(st);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -224,15 +224,6 @@ public final class CardPredicates {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final Predicate<Card> isProtectedFrom(final Card source) {
|
|
||||||
return new Predicate<Card>() {
|
|
||||||
@Override
|
|
||||||
public boolean apply(final Card c) {
|
|
||||||
return c.hasProtectionFrom(source);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public static final Predicate<Card> restriction(final String[] restrictions, final Player sourceController, final Card source, final CardTraitBase spellAbility) {
|
public static final Predicate<Card> restriction(final String[] restrictions, final Player sourceController, final Card source, final CardTraitBase spellAbility) {
|
||||||
return new Predicate<Card>() {
|
return new Predicate<Card>() {
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -562,7 +562,7 @@ public class CardView extends GameEntityView {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean canFaceDownBeShownTo(final PlayerView viewer, boolean skip) {
|
public boolean canFaceDownBeShownTo(final PlayerView viewer, boolean skip) {
|
||||||
if (!isFaceDown()) {
|
if (!isFaceDown()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -529,8 +529,7 @@ public class Player extends GameEntity implements Comparable<Player> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Run triggers
|
// Run triggers
|
||||||
final Map<AbilityKey, Object> runParams = AbilityKey.newMap();
|
final Map<AbilityKey, Object> runParams = AbilityKey.mapFromPlayer(this);
|
||||||
runParams.put(AbilityKey.Player, this);
|
|
||||||
runParams.put(AbilityKey.LifeAmount, lifeGain);
|
runParams.put(AbilityKey.LifeAmount, lifeGain);
|
||||||
runParams.put(AbilityKey.Source, source);
|
runParams.put(AbilityKey.Source, source);
|
||||||
runParams.put(AbilityKey.SourceSA, sa);
|
runParams.put(AbilityKey.SourceSA, sa);
|
||||||
@@ -602,8 +601,7 @@ public class Player extends GameEntity implements Comparable<Player> {
|
|||||||
lifeLostThisTurn += toLose;
|
lifeLostThisTurn += toLose;
|
||||||
|
|
||||||
// Run triggers
|
// Run triggers
|
||||||
final Map<AbilityKey, Object> runParams = AbilityKey.newMap();
|
final Map<AbilityKey, Object> runParams = AbilityKey.mapFromPlayer(this);
|
||||||
runParams.put(AbilityKey.Player, this);
|
|
||||||
runParams.put(AbilityKey.LifeAmount, toLose);
|
runParams.put(AbilityKey.LifeAmount, toLose);
|
||||||
runParams.put(AbilityKey.FirstTime, firstLost);
|
runParams.put(AbilityKey.FirstTime, firstLost);
|
||||||
game.getTriggerHandler().runTrigger(TriggerType.LifeLost, runParams, false);
|
game.getTriggerHandler().runTrigger(TriggerType.LifeLost, runParams, false);
|
||||||
@@ -630,8 +628,7 @@ public class Player extends GameEntity implements Comparable<Player> {
|
|||||||
loseLife(lifePayment, false, false);
|
loseLife(lifePayment, false, false);
|
||||||
|
|
||||||
// Run triggers
|
// Run triggers
|
||||||
final Map<AbilityKey, Object> runParams = AbilityKey.newMap();
|
final Map<AbilityKey, Object> runParams = AbilityKey.mapFromPlayer(this);
|
||||||
runParams.put(AbilityKey.Player, this);
|
|
||||||
runParams.put(AbilityKey.LifeAmount, lifePayment);
|
runParams.put(AbilityKey.LifeAmount, lifePayment);
|
||||||
game.getTriggerHandler().runTrigger(TriggerType.PayLife, runParams, false);
|
game.getTriggerHandler().runTrigger(TriggerType.PayLife, runParams, false);
|
||||||
|
|
||||||
@@ -1097,70 +1094,7 @@ public class Player extends GameEntity implements Comparable<Player> {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return !hasProtectionFrom(sa.getHostCard());
|
return true;
|
||||||
}
|
|
||||||
|
|
||||||
public boolean hasProtectionFromDamage(final Card source) {
|
|
||||||
return hasProtectionFrom(source, false, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean hasProtectionFrom(final Card source, final boolean checkSBA) {
|
|
||||||
return hasProtectionFrom(source, checkSBA, false);
|
|
||||||
}
|
|
||||||
public boolean hasProtectionFrom(final Card source, final boolean checkSBA, final boolean damageSource) {
|
|
||||||
final boolean colorlessDamage = damageSource && source.hasKeyword("Colorless Damage Source");
|
|
||||||
for (KeywordInterface ki : keywords) {
|
|
||||||
String kw = ki.getOriginal();
|
|
||||||
if (kw.startsWith("Protection")) {
|
|
||||||
if (kw.startsWith("Protection:")) { // uses isValid
|
|
||||||
final String characteristic = kw.split(":")[1];
|
|
||||||
if (characteristic.startsWith("Player")) {
|
|
||||||
// Protection:PlayerUID
|
|
||||||
if (source.getController().isValid(characteristic, this, null, null)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
final String[] characteristics = characteristic.split(",");
|
|
||||||
if (source.isValid(characteristics, this, null, null)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (kw.equals("Protection from white")) {
|
|
||||||
if (source.isWhite() && !colorlessDamage) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
} else if (kw.equals("Protection from blue")) {
|
|
||||||
if (source.isBlue() && !colorlessDamage) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
} else if (kw.equals("Protection from black")) {
|
|
||||||
if (source.isBlack() && !colorlessDamage) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
} else if (kw.equals("Protection from red")) {
|
|
||||||
if (source.isRed() && !colorlessDamage) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
} else if (kw.equals("Protection from green")) {
|
|
||||||
if (source.isGreen() && !colorlessDamage) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
} else if (kw.equals("Protection from all colors")) {
|
|
||||||
if (!source.isColorless() && !colorlessDamage) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
} else if (kw.equals("Protection from everything")) {
|
|
||||||
return true;
|
|
||||||
} else if (kw.startsWith("Protection from ")) {
|
|
||||||
final String protectType = CardType.getSingularType(kw.substring("Protection from ".length()));
|
|
||||||
if (source.getType().hasStringType(protectType)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void surveil(int num, SpellAbility cause, CardZoneTable table) {
|
public void surveil(int num, SpellAbility cause, CardZoneTable table) {
|
||||||
@@ -1212,8 +1146,7 @@ public class Player extends GameEntity implements Comparable<Player> {
|
|||||||
getGame().fireEvent(new GameEventSurveil(this, numToTop, numToGrave));
|
getGame().fireEvent(new GameEventSurveil(this, numToTop, numToGrave));
|
||||||
|
|
||||||
surveilThisTurn++;
|
surveilThisTurn++;
|
||||||
final Map<AbilityKey, Object> runParams = AbilityKey.newMap();
|
final Map<AbilityKey, Object> runParams = AbilityKey.mapFromPlayer(this);
|
||||||
runParams.put(AbilityKey.Player, this);
|
|
||||||
runParams.put(AbilityKey.NumThisTurn, surveilThisTurn);
|
runParams.put(AbilityKey.NumThisTurn, surveilThisTurn);
|
||||||
getGame().getTriggerHandler().runTrigger(TriggerType.Surveil, runParams, false);
|
getGame().getTriggerHandler().runTrigger(TriggerType.Surveil, runParams, false);
|
||||||
}
|
}
|
||||||
@@ -1329,7 +1262,7 @@ public class Player extends GameEntity implements Comparable<Player> {
|
|||||||
}
|
}
|
||||||
view.updateNumDrawnThisTurn(this);
|
view.updateNumDrawnThisTurn(this);
|
||||||
|
|
||||||
final Map<AbilityKey, Object> runParams = Maps.newHashMap();
|
final Map<AbilityKey, Object> runParams = AbilityKey.mapFromPlayer(this);
|
||||||
|
|
||||||
// CR 121.8 card was drawn as part of another sa (e.g. paying with Chromantic Sphere), hide it temporarily
|
// CR 121.8 card was drawn as part of another sa (e.g. paying with Chromantic Sphere), hide it temporarily
|
||||||
if (game.getTopLibForPlayer(this) != null && getPaidForSA() != null && cause != null && getPaidForSA() != cause.getRootAbility()) {
|
if (game.getTopLibForPlayer(this) != null && getPaidForSA() != null && cause != null && getPaidForSA() != cause.getRootAbility()) {
|
||||||
@@ -1341,7 +1274,6 @@ public class Player extends GameEntity implements Comparable<Player> {
|
|||||||
// Run triggers
|
// Run triggers
|
||||||
runParams.put(AbilityKey.Card, c);
|
runParams.put(AbilityKey.Card, c);
|
||||||
runParams.put(AbilityKey.Number, numDrawnThisTurn);
|
runParams.put(AbilityKey.Number, numDrawnThisTurn);
|
||||||
runParams.put(AbilityKey.Player, this);
|
|
||||||
game.getTriggerHandler().runTrigger(TriggerType.Drawn, runParams, false);
|
game.getTriggerHandler().runTrigger(TriggerType.Drawn, runParams, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1545,8 +1477,7 @@ public class Player extends GameEntity implements Comparable<Player> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
final Map<AbilityKey, Object> runParams = AbilityKey.newMap();
|
final Map<AbilityKey, Object> runParams = AbilityKey.mapFromPlayer(this);
|
||||||
runParams.put(AbilityKey.Player, this);
|
|
||||||
runParams.put(AbilityKey.Card, c);
|
runParams.put(AbilityKey.Card, c);
|
||||||
runParams.put(AbilityKey.Cause, cause);
|
runParams.put(AbilityKey.Cause, cause);
|
||||||
runParams.put(AbilityKey.IsMadness, discardMadness);
|
runParams.put(AbilityKey.IsMadness, discardMadness);
|
||||||
@@ -1564,8 +1495,7 @@ public class Player extends GameEntity implements Comparable<Player> {
|
|||||||
|
|
||||||
public final void addTokensCreatedThisTurn(Card token) {
|
public final void addTokensCreatedThisTurn(Card token) {
|
||||||
numTokenCreatedThisTurn++;
|
numTokenCreatedThisTurn++;
|
||||||
final Map<AbilityKey, Object> runParams = AbilityKey.newMap();
|
final Map<AbilityKey, Object> runParams = AbilityKey.mapFromPlayer(this);
|
||||||
runParams.put(AbilityKey.Player, this);
|
|
||||||
runParams.put(AbilityKey.Num, numTokenCreatedThisTurn);
|
runParams.put(AbilityKey.Num, numTokenCreatedThisTurn);
|
||||||
runParams.put(AbilityKey.Card, token);
|
runParams.put(AbilityKey.Card, token);
|
||||||
game.getTriggerHandler().runTrigger(TriggerType.TokenCreated, runParams, false);
|
game.getTriggerHandler().runTrigger(TriggerType.TokenCreated, runParams, false);
|
||||||
@@ -1581,8 +1511,7 @@ public class Player extends GameEntity implements Comparable<Player> {
|
|||||||
|
|
||||||
public final void addForetoldThisTurn() {
|
public final void addForetoldThisTurn() {
|
||||||
numForetoldThisTurn++;
|
numForetoldThisTurn++;
|
||||||
final Map<AbilityKey, Object> runParams = AbilityKey.newMap();
|
final Map<AbilityKey, Object> runParams = AbilityKey.mapFromPlayer(this);
|
||||||
runParams.put(AbilityKey.Player, this);
|
|
||||||
runParams.put(AbilityKey.Num, numForetoldThisTurn);
|
runParams.put(AbilityKey.Num, numForetoldThisTurn);
|
||||||
game.getTriggerHandler().runTrigger(TriggerType.Foretell, runParams, false);
|
game.getTriggerHandler().runTrigger(TriggerType.Foretell, runParams, false);
|
||||||
}
|
}
|
||||||
@@ -1717,8 +1646,7 @@ public class Player extends GameEntity implements Comparable<Player> {
|
|||||||
getZone(ZoneType.Library).setCards(getController().cheatShuffle(list));
|
getZone(ZoneType.Library).setCards(getController().cheatShuffle(list));
|
||||||
|
|
||||||
// Run triggers
|
// Run triggers
|
||||||
final Map<AbilityKey, Object> runParams = AbilityKey.newMap();
|
final Map<AbilityKey, Object> runParams = AbilityKey.mapFromPlayer(this);
|
||||||
runParams.put(AbilityKey.Player, this);
|
|
||||||
runParams.put(AbilityKey.Source, sa);
|
runParams.put(AbilityKey.Source, sa);
|
||||||
game.getTriggerHandler().runTrigger(TriggerType.Shuffled, runParams, false);
|
game.getTriggerHandler().runTrigger(TriggerType.Shuffled, runParams, false);
|
||||||
|
|
||||||
@@ -2007,12 +1935,7 @@ public class Player extends GameEntity implements Comparable<Player> {
|
|||||||
return getOutcome().lossState != null;
|
return getOutcome().lossState != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Rule 704.5a - If a player has 0 or less life, he or she loses the game.
|
// check this first because of Lich's Mirror (704.7)
|
||||||
final boolean hasNoLife = getLife() <= 0;
|
|
||||||
if (hasNoLife && !cantLoseForZeroOrLessLife()) {
|
|
||||||
return loseConditionMet(GameLossReason.LifeReachedZero, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Rule 704.5b - If a player attempted to draw a card from a library with no cards in it
|
// Rule 704.5b - If a player attempted to draw a card from a library with no cards in it
|
||||||
// since the last time state-based actions were checked, he or she loses the game.
|
// since the last time state-based actions were checked, he or she loses the game.
|
||||||
if (triedToDrawFromEmptyLibrary) {
|
if (triedToDrawFromEmptyLibrary) {
|
||||||
@@ -2023,6 +1946,12 @@ public class Player extends GameEntity implements Comparable<Player> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Rule 704.5a - If a player has 0 or less life, he or she loses the game.
|
||||||
|
final boolean hasNoLife = getLife() <= 0;
|
||||||
|
if (hasNoLife && !cantLoseForZeroOrLessLife()) {
|
||||||
|
return loseConditionMet(GameLossReason.LifeReachedZero, null);
|
||||||
|
}
|
||||||
|
|
||||||
// Rule 704.5c - If a player has ten or more poison counters, he or she loses the game.
|
// Rule 704.5c - If a player has ten or more poison counters, he or she loses the game.
|
||||||
if (getCounters(CounterEnumType.POISON) >= 10) {
|
if (getCounters(CounterEnumType.POISON) >= 10) {
|
||||||
return loseConditionMet(GameLossReason.Poisoned, null);
|
return loseConditionMet(GameLossReason.Poisoned, null);
|
||||||
@@ -2244,8 +2173,7 @@ public class Player extends GameEntity implements Comparable<Player> {
|
|||||||
}
|
}
|
||||||
public final void addInvestigatedThisTurn() {
|
public final void addInvestigatedThisTurn() {
|
||||||
investigatedThisTurn++;
|
investigatedThisTurn++;
|
||||||
final Map<AbilityKey, Object> runParams = AbilityKey.newMap();
|
final Map<AbilityKey, Object> runParams = AbilityKey.mapFromPlayer(this);
|
||||||
runParams.put(AbilityKey.Player, this);
|
|
||||||
runParams.put(AbilityKey.Num, investigatedThisTurn);
|
runParams.put(AbilityKey.Num, investigatedThisTurn);
|
||||||
game.getTriggerHandler().runTrigger(TriggerType.Investigated, runParams, false);
|
game.getTriggerHandler().runTrigger(TriggerType.Investigated, runParams, false);
|
||||||
}
|
}
|
||||||
@@ -2265,10 +2193,9 @@ public class Player extends GameEntity implements Comparable<Player> {
|
|||||||
sacrificedThisTurn.add(cpy);
|
sacrificedThisTurn.add(cpy);
|
||||||
|
|
||||||
// Run triggers
|
// Run triggers
|
||||||
final Map<AbilityKey, Object> runParams = AbilityKey.newMap();
|
final Map<AbilityKey, Object> runParams = AbilityKey.mapFromPlayer(this);
|
||||||
// use a copy that preserves last known information about the card (e.g. for Savra, Queen of the Golgari + Painter's Servant)
|
// use a copy that preserves last known information about the card (e.g. for Savra, Queen of the Golgari + Painter's Servant)
|
||||||
runParams.put(AbilityKey.Card, cpy);
|
runParams.put(AbilityKey.Card, cpy);
|
||||||
runParams.put(AbilityKey.Player, this);
|
|
||||||
runParams.put(AbilityKey.Cause, source);
|
runParams.put(AbilityKey.Cause, source);
|
||||||
runParams.put(AbilityKey.CostStack, game.costPaymentStack);
|
runParams.put(AbilityKey.CostStack, game.costPaymentStack);
|
||||||
runParams.put(AbilityKey.IndividualCostPaymentInstance, game.costPaymentStack.peek());
|
runParams.put(AbilityKey.IndividualCostPaymentInstance, game.costPaymentStack.peek());
|
||||||
@@ -3513,8 +3440,7 @@ public class Player extends GameEntity implements Comparable<Player> {
|
|||||||
// Change this if something would make multiple player learn at the same time
|
// Change this if something would make multiple player learn at the same time
|
||||||
|
|
||||||
// Discard Trigger outside Effect
|
// Discard Trigger outside Effect
|
||||||
final Map<AbilityKey, Object> runParams = AbilityKey.newMap();
|
final Map<AbilityKey, Object> runParams = AbilityKey.mapFromPlayer(this);
|
||||||
runParams.put(AbilityKey.Player, this);
|
|
||||||
runParams.put(AbilityKey.Cards, new CardCollection(c));
|
runParams.put(AbilityKey.Cards, new CardCollection(c));
|
||||||
runParams.put(AbilityKey.Cause, sa);
|
runParams.put(AbilityKey.Cause, sa);
|
||||||
runParams.put(AbilityKey.FirstTime, firstDiscard);
|
runParams.put(AbilityKey.FirstTime, firstDiscard);
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ public class PlayerFactoryUtil {
|
|||||||
|
|
||||||
public static void addStaticAbility(final KeywordInterface inst, final Player player) {
|
public static void addStaticAbility(final KeywordInterface inst, final Player player) {
|
||||||
String keyword = inst.getOriginal();
|
String keyword = inst.getOriginal();
|
||||||
String effect = null;
|
|
||||||
if (keyword.startsWith("Hexproof")) {
|
if (keyword.startsWith("Hexproof")) {
|
||||||
final StringBuilder sbDesc = new StringBuilder("Hexproof");
|
final StringBuilder sbDesc = new StringBuilder("Hexproof");
|
||||||
final StringBuilder sbValid = new StringBuilder();
|
final StringBuilder sbValid = new StringBuilder();
|
||||||
@@ -23,18 +23,40 @@ public class PlayerFactoryUtil {
|
|||||||
sbValid.append("| ValidSource$ ").append(k[1]);
|
sbValid.append("| ValidSource$ ").append(k[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
effect = "Mode$ CantTarget | Hexproof$ True | ValidPlayer$ Player.You | Secondary$ True "
|
String effect = "Mode$ CantTarget | Hexproof$ True | ValidPlayer$ Player.You | Secondary$ True "
|
||||||
+ sbValid.toString() + " | Activator$ Opponent | EffectZone$ Command | Description$ "
|
+ sbValid.toString() + " | Activator$ Opponent | EffectZone$ Command | Description$ "
|
||||||
+ sbDesc.toString() + " (" + inst.getReminderText() + ")";
|
+ sbDesc.toString() + " (" + inst.getReminderText() + ")";
|
||||||
} else if (keyword.equals("Shroud")) {
|
|
||||||
effect = "Mode$ CantTarget | Shroud$ True | ValidPlayer$ Player.You | Secondary$ True "
|
|
||||||
+ "| EffectZone$ Command | Description$ Shroud (" + inst.getReminderText() + ")";
|
|
||||||
}
|
|
||||||
if (effect != null) {
|
|
||||||
final Card card = player.getKeywordCard();
|
final Card card = player.getKeywordCard();
|
||||||
StaticAbility st = new StaticAbility(effect, card, card.getCurrentState());
|
inst.addStaticAbility(StaticAbility.create(effect, card, card.getCurrentState(), false));
|
||||||
st.setIntrinsic(false);
|
} else if (keyword.equals("Shroud")) {
|
||||||
inst.addStaticAbility(st);
|
String effect = "Mode$ CantTarget | Shroud$ True | ValidPlayer$ Player.You | Secondary$ True "
|
||||||
|
+ "| EffectZone$ Command | Description$ Shroud (" + inst.getReminderText() + ")";
|
||||||
|
|
||||||
|
final Card card = player.getKeywordCard();
|
||||||
|
inst.addStaticAbility(StaticAbility.create(effect, card, card.getCurrentState(), false));
|
||||||
|
} else if (keyword.startsWith("Protection")) {
|
||||||
|
String valid = CardFactoryUtil.getProtectionValid(keyword, false);
|
||||||
|
String effect = "Mode$ CantTarget | Protection$ True | ValidCard$ Player.You | Secondary$ True ";
|
||||||
|
if (!valid.isEmpty()) {
|
||||||
|
effect += "| ValidSource$ " + valid;
|
||||||
|
}
|
||||||
|
final Card card = player.getKeywordCard();
|
||||||
|
inst.addStaticAbility(StaticAbility.create(effect, card, card.getCurrentState(), false));
|
||||||
|
|
||||||
|
// Attach
|
||||||
|
effect = "Mode$ CantAttach | Protection$ True | Target$ Player.You | Secondary$ True ";
|
||||||
|
if (!valid.isEmpty()) {
|
||||||
|
effect += "| ValidCard$ " + valid;
|
||||||
|
}
|
||||||
|
// This effect doesn't remove something
|
||||||
|
if (keyword.startsWith("Protection:")) {
|
||||||
|
final String[] kws = keyword.split(":");
|
||||||
|
if (kws.length > 3) {
|
||||||
|
effect += "| Exceptions$ " + kws[3];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
inst.addStaticAbility(StaticAbility.create(effect, card, card.getCurrentState(), false));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -504,7 +504,7 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
|
|||||||
}
|
}
|
||||||
|
|
||||||
public boolean isCycling() {
|
public boolean isCycling() {
|
||||||
return this.isAlternativeCost(AlternativeCost.Cycling);
|
return isAlternativeCost(AlternativeCost.Cycling);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isBoast() {
|
public boolean isBoast() {
|
||||||
@@ -515,6 +515,14 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
|
|||||||
return this.hasParam("Ninjutsu");
|
return this.hasParam("Ninjutsu");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isEpic() {
|
||||||
|
AbilitySub sub = this.getSubAbility();
|
||||||
|
while (sub != null && !sub.hasParam("Epic")) {
|
||||||
|
sub = sub.getSubAbility();
|
||||||
|
}
|
||||||
|
return sub != null && sub.hasParam("Epic");
|
||||||
|
}
|
||||||
|
|
||||||
// If this is not null, then ability was made in a factory
|
// If this is not null, then ability was made in a factory
|
||||||
public ApiType getApi() {
|
public ApiType getApi() {
|
||||||
return api;
|
return api;
|
||||||
@@ -777,7 +785,10 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
|
|||||||
public void resetOnceResolved() {
|
public void resetOnceResolved() {
|
||||||
//resetPaidHash(); // FIXME: if uncommented, breaks Dragon Presence, e.g. Orator of Ojutai + revealing a Dragon from hand.
|
//resetPaidHash(); // FIXME: if uncommented, breaks Dragon Presence, e.g. Orator of Ojutai + revealing a Dragon from hand.
|
||||||
// Is it truly necessary at this point? The paid hash seems to be reset on all SA instance operations.
|
// Is it truly necessary at this point? The paid hash seems to be reset on all SA instance operations.
|
||||||
resetTargets();
|
// Epic spell keeps original targets
|
||||||
|
if (!isEpic()) {
|
||||||
|
resetTargets();
|
||||||
|
}
|
||||||
resetTriggeringObjects();
|
resetTriggeringObjects();
|
||||||
resetTriggerRemembered();
|
resetTriggerRemembered();
|
||||||
|
|
||||||
|
|||||||
@@ -234,6 +234,12 @@ public class StaticAbility extends CardTraitBase implements IIdentifiable, Clone
|
|||||||
this(parseParams(params, host), host, state);
|
this(parseParams(params, host), host, state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static StaticAbility create(final String params, final Card host, CardState state, boolean intrinsic) {
|
||||||
|
StaticAbility st = new StaticAbility(params, state.getCard(), state);
|
||||||
|
st.setIntrinsic(intrinsic);
|
||||||
|
return st;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Instantiates a new static ability.
|
* Instantiates a new static ability.
|
||||||
*
|
*
|
||||||
@@ -325,8 +331,6 @@ public class StaticAbility extends CardTraitBase implements IIdentifiable, Clone
|
|||||||
return StaticAbilityCantAttackBlock.applyCantAttackAbility(this, card, target);
|
return StaticAbilityCantAttackBlock.applyCantAttackAbility(this, card, target);
|
||||||
} else if (mode.equals("CantBlockBy") && target instanceof Card) {
|
} else if (mode.equals("CantBlockBy") && target instanceof Card) {
|
||||||
return StaticAbilityCantAttackBlock.applyCantBlockByAbility(this, card, (Card)target);
|
return StaticAbilityCantAttackBlock.applyCantBlockByAbility(this, card, (Card)target);
|
||||||
} else if (mode.equals("CantAttach")) {
|
|
||||||
return StaticAbilityCantAttach.applyCantAttachAbility(this, card, target);
|
|
||||||
} else if (mode.equals("CanAttackIfHaste")) {
|
} else if (mode.equals("CanAttackIfHaste")) {
|
||||||
return StaticAbilityCantAttackBlock.applyCanAttackHasteAbility(this, card, target);
|
return StaticAbilityCantAttackBlock.applyCanAttackHasteAbility(this, card, target);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,29 +2,52 @@ package forge.game.staticability;
|
|||||||
|
|
||||||
import forge.game.GameEntity;
|
import forge.game.GameEntity;
|
||||||
import forge.game.card.Card;
|
import forge.game.card.Card;
|
||||||
|
import forge.game.zone.ZoneType;
|
||||||
|
|
||||||
public class StaticAbilityCantAttach {
|
public class StaticAbilityCantAttach {
|
||||||
|
|
||||||
public static boolean applyCantAttachAbility(final StaticAbility stAb, final Card card, final GameEntity target) {
|
static String MODE = "CantAttach";
|
||||||
if (!stAb.matchesValidParam("ValidCard", card)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!stAb.matchesValidParam("Target", target)) {
|
public static boolean cantAttach(final GameEntity target, final Card card, boolean checkSBA) {
|
||||||
return false;
|
// CantTarget static abilities
|
||||||
}
|
for (final Card ca : target.getGame().getCardsIn(ZoneType.STATIC_ABILITIES_SOURCE_ZONES)) {
|
||||||
|
for (final StaticAbility stAb : ca.getStaticAbilities()) {
|
||||||
|
if (!stAb.getParam("Mode").equals(MODE) || stAb.isSuppressed() || !stAb.checkConditions()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (stAb.hasParam("ValidCardToTarget")) {
|
if (applyCantAttachAbility(stAb, card, target, checkSBA)) {
|
||||||
if (!(target instanceof Card)) {
|
return true;
|
||||||
return false;
|
}
|
||||||
}
|
}
|
||||||
Card tcard = (Card) target;
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (!stAb.matchesValid(card, stAb.getParam("ValidCardToTarget").split(","), tcard)) {
|
public static boolean applyCantAttachAbility(final StaticAbility stAb, final Card card, final GameEntity target, boolean checkSBA) {
|
||||||
return false;
|
if (!stAb.matchesValidParam("ValidCard", card)) {
|
||||||
}
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
if (!stAb.matchesValidParam("Target", target)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stAb.hasParam("ValidCardToTarget")) {
|
||||||
|
if (!(target instanceof Card)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Card tcard = (Card) target;
|
||||||
|
|
||||||
|
if (!stAb.matchesValid(card, stAb.getParam("ValidCardToTarget").split(","), tcard)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (checkSBA && stAb.matchesValidParam("Exceptions", card)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -875,9 +875,7 @@ public final class StaticAbilityContinuous {
|
|||||||
s = TextUtil.fastReplace(s, "ConvertedManaCost", costcmc);
|
s = TextUtil.fastReplace(s, "ConvertedManaCost", costcmc);
|
||||||
}
|
}
|
||||||
|
|
||||||
StaticAbility stat = new StaticAbility(s, affectedCard, stAb.getCardState());
|
addedStaticAbility.add(StaticAbility.create(s, affectedCard, stAb.getCardState(), false));
|
||||||
stat.setIntrinsic(false);
|
|
||||||
addedStaticAbility.add(stat);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -130,6 +130,12 @@ public class TriggerChangesZone extends Trigger {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (hasParam("ResolvedCard")) {
|
||||||
|
if (!runParams.containsKey(AbilityKey.Fizzle)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Check number of lands ETB this turn on triggered card's controller
|
// Check number of lands ETB this turn on triggered card's controller
|
||||||
if (hasParam("CheckOnTriggeredCard")) {
|
if (hasParam("CheckOnTriggeredCard")) {
|
||||||
final String[] condition = getParam("CheckOnTriggeredCard").split(" ", 2);
|
final String[] condition = getParam("CheckOnTriggeredCard").split(" ", 2);
|
||||||
|
|||||||
@@ -253,7 +253,7 @@ public class MagicStack /* extends MyObservable */ implements Iterable<SpellAbil
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!sp.isCopied() && !hasLegalTargeting(sp, source)) {
|
if (!sp.isCopied() && !hasLegalTargeting(sp)) {
|
||||||
String str = source + " - [Couldn't add to stack, failed to target] - " + sp.getDescription();
|
String str = source + " - [Couldn't add to stack, failed to target] - " + sp.getDescription();
|
||||||
System.err.println(str + sp.getAllTargetChoices());
|
System.err.println(str + sp.getAllTargetChoices());
|
||||||
game.getGameLog().add(GameLogEntryType.STACK_ADD, str);
|
game.getGameLog().add(GameLogEntryType.STACK_ADD, str);
|
||||||
@@ -604,14 +604,14 @@ public class MagicStack /* extends MyObservable */ implements Iterable<SpellAbil
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public final boolean hasLegalTargeting(final SpellAbility sa, final Card source) {
|
public final boolean hasLegalTargeting(final SpellAbility sa) {
|
||||||
if (sa == null) {
|
if (sa == null) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (!sa.isTargetNumberValid()) {
|
if (!sa.isTargetNumberValid()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return hasLegalTargeting(sa.getSubAbility(), source);
|
return hasLegalTargeting(sa.getSubAbility());
|
||||||
}
|
}
|
||||||
|
|
||||||
private final boolean hasFizzled(final SpellAbility sa, final Card source, final Boolean parentFizzled) {
|
private final boolean hasFizzled(final SpellAbility sa, final Card source, final Boolean parentFizzled) {
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ public enum ZoneType {
|
|||||||
Subgame(true, "lblSubgameZone"),
|
Subgame(true, "lblSubgameZone"),
|
||||||
None(true, "lblNoneZone");
|
None(true, "lblNoneZone");
|
||||||
|
|
||||||
public static final List<ZoneType> STATIC_ABILITIES_SOURCE_ZONES = Arrays.asList(Battlefield, Graveyard, Exile, Command/*, Hand*/);
|
public static final List<ZoneType> STATIC_ABILITIES_SOURCE_ZONES = Arrays.asList(Battlefield, Graveyard, Exile, Command, Stack/*, Hand*/);
|
||||||
|
|
||||||
private final boolean holdsHiddenInfo;
|
private final boolean holdsHiddenInfo;
|
||||||
private final String zoneName;
|
private final String zoneName;
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
Name:Dream Leash
|
Name:Dream Leash
|
||||||
ManaCost:3 U U
|
ManaCost:3 U U
|
||||||
Types:Enchantment Aura
|
Types:Enchantment Aura
|
||||||
Text:You can't choose an untapped creature as this spell's target as you cast it.
|
|
||||||
K:Enchant permanent
|
K:Enchant permanent
|
||||||
K:SpellCantTarget:Permanent.untapped
|
S:Mode$ CantTarget | EffectZone$ Stack | ValidSource$ Spell.Self | ValidCard$ Permanent.untapped | Description$ You can't choose an untapped permanent as this spell's target as you cast it.
|
||||||
A:SP$ Attach | Cost$ 3 U U | ValidTgts$ Permanent | AILogic$ GainControl
|
A:SP$ Attach | Cost$ 3 U U | ValidTgts$ Permanent | AILogic$ GainControl
|
||||||
S:Mode$ Continuous | Affected$ Card.EnchantedBy | GainControl$ You | Description$ You control enchanted permanent.
|
S:Mode$ Continuous | Affected$ Card.EnchantedBy | GainControl$ You | Description$ You control enchanted permanent.
|
||||||
Oracle:Enchant permanent\nYou can't choose an untapped permanent as this spell's target as you cast it.\nYou control enchanted permanent.
|
Oracle:Enchant permanent\nYou can't choose an untapped permanent as this spell's target as you cast it.\nYou control enchanted permanent.
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
Name:Enthralling Hold
|
Name:Enthralling Hold
|
||||||
ManaCost:3 U U
|
ManaCost:3 U U
|
||||||
Types:Enchantment Aura
|
Types:Enchantment Aura
|
||||||
Text:You can't choose an untapped creature as this spell's target as you cast it.
|
|
||||||
K:Enchant creature
|
K:Enchant creature
|
||||||
K:SpellCantTarget:Creature.untapped
|
S:Mode$ CantTarget | EffectZone$ Stack | ValidSource$ Spell.Self | ValidCard$ Creature.untapped | Description$ You can't choose an untapped creature as this spell's target as you cast it.
|
||||||
A:SP$ Attach | Cost$ 3 U U | ValidTgts$ Creature | AILogic$ GainControl
|
A:SP$ Attach | Cost$ 3 U U | ValidTgts$ Creature | AILogic$ GainControl
|
||||||
S:Mode$ Continuous | Affected$ Card.EnchantedBy | GainControl$ You | Description$ You control enchanted creature.
|
S:Mode$ Continuous | Affected$ Card.EnchantedBy | GainControl$ You | Description$ You control enchanted creature.
|
||||||
Oracle:Enchant creature\nYou can't choose an untapped creature as this spell's target as you cast it.\nYou control enchanted creature.
|
Oracle:Enchant creature\nYou can't choose an untapped creature as this spell's target as you cast it.\nYou control enchanted creature.
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ Name:Eternal Dominion
|
|||||||
ManaCost:7 U U U
|
ManaCost:7 U U U
|
||||||
Types:Sorcery
|
Types:Sorcery
|
||||||
K:Epic
|
K:Epic
|
||||||
A:SP$ ChangeZone | Origin$ Library | Destination$ Battlefield | ValidTgts$ Opponent | TgtPrompt$ Select target opponent | ChangeType$ Artifact,Creature,Enchantment,Land | ChangeNum$ 1 | GainControl$ True | IsCurse$ True | StackDescription$ SpellDescription | SpellDescription$ Search target opponent's library for an artifact, creature, enchantment, or land card and put that card onto the battlefield under your control. Then that player shuffles their library.
|
A:SP$ ChangeZone | Cost$ 7 U U U | Origin$ Library | Destination$ Battlefield | ValidTgts$ Opponent | ChangeType$ Artifact,Creature,Enchantment,Land | ChangeNum$ 1 | GainControl$ True | IsCurse$ True | StackDescription$ SpellDescription | SpellDescription$ Search target opponent's library for an artifact, creature, enchantment, or land card. Put that card onto the battlefield under your control. Then that player shuffles.
|
||||||
#TODO: Tutoring in general can be improved to make this card work better for the AI. Currently the AI will grab the most expensive targets in the opponent's library (which is not necessarily a bad thing in itself, but not always optimal).
|
#TODO: Tutoring in general can be improved to make this card work better for the AI. Currently the AI will grab the most expensive targets in the opponent's library (which is not necessarily a bad thing in itself, but not always optimal).
|
||||||
AI:RemoveDeck:Random
|
AI:RemoveDeck:Random
|
||||||
Oracle:Search target opponent's library for an artifact, creature, enchantment, or land card. Put that card onto the battlefield under your control. Then that player shuffles.\nEpic (For the rest of the game, you can't cast spells. At the beginning of each of your upkeeps, copy this spell except for its epic ability. You may choose a new target for the copy.)
|
Oracle:Search target opponent's library for an artifact, creature, enchantment, or land card. Put that card onto the battlefield under your control. Then that player shuffles.\nEpic (For the rest of the game, you can't cast spells. At the beginning of each of your upkeeps, copy this spell except for its epic ability. You may choose a new target for the copy.)
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ Name:Gisa, Glorious Resurrector
|
|||||||
ManaCost:2 B B
|
ManaCost:2 B B
|
||||||
Types:Legendary Creature Human Wizard
|
Types:Legendary Creature Human Wizard
|
||||||
PT:4/4
|
PT:4/4
|
||||||
R:Event$ Moved | ActiveZones$ Battlefield | CheckSelfLKIZone$ True | Origin$ Battlefield | Destination$ Graveyard | ValidCard$ Creature.OppCtrl | ReplaceWith$ Exile | Description$ If a creature an opponent controls would die, exile it instead.
|
R:Event$ Moved | ActiveZones$ Battlefield | CheckSelfLKIZone$ True | Origin$ Battlefield | Destination$ Graveyard | ValidLKI$ Creature.OppCtrl | ReplaceWith$ Exile | Description$ If a creature an opponent controls would die, exile it instead.
|
||||||
SVar:Exile:DB$ ChangeZone | Hidden$ True | Origin$ All | Destination$ Exile | Defined$ ReplacedCard
|
SVar:Exile:DB$ ChangeZone | Hidden$ True | Origin$ All | Destination$ Exile | Defined$ ReplacedCard
|
||||||
T:Mode$ Phase | Phase$ Upkeep | ValidPlayer$ You | TriggerZones$ Battlefield | IsPresent$ Card.ExiledWithSource | PresentZone$ Exile | Execute$ TrigChange | TriggerDescription$ At the beginning of your upkeep, put all creature cards exiled with CARDNAME onto the battlefield under your control. They gain decayed. (A creature with decayed can't block. When it attacks, sacrifice it at end of combat.)
|
T:Mode$ Phase | Phase$ Upkeep | ValidPlayer$ You | TriggerZones$ Battlefield | IsPresent$ Card.ExiledWithSource | PresentZone$ Exile | Execute$ TrigChange | TriggerDescription$ At the beginning of your upkeep, put all creature cards exiled with CARDNAME onto the battlefield under your control. They gain decayed. (A creature with decayed can't block. When it attacks, sacrifice it at end of combat.)
|
||||||
SVar:TrigChange:DB$ ChangeZoneAll | ChangeType$ Card.ExiledWithSource | Origin$ Exile | Destination$ Battlefield | GainControl$ True | RememberChanged$ True | SubAbility$ DBPump
|
SVar:TrigChange:DB$ ChangeZoneAll | ChangeType$ Card.ExiledWithSource | Origin$ Exile | Destination$ Battlefield | GainControl$ True | RememberChanged$ True | SubAbility$ DBPump
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
Name:Hardened Scales
|
Name:Hardened Scales
|
||||||
ManaCost:G
|
ManaCost:G
|
||||||
Types:Enchantment
|
Types:Enchantment
|
||||||
R:Event$ AddCounter | ActiveZones$ Battlefield | ValidCard$ Creature.YouCtrl+InZoneBattlefield | ValidCounterType$ P1P1 | ReplaceWith$ AddOneMoreCounters | Description$ If one or more +1/+1 counters would be put on a creature you control, that many plus one +1/+1 counters are put on it instead.
|
R:Event$ AddCounter | ActiveZones$ Battlefield | ValidCard$ Creature.YouCtrl+inZoneBattlefield | ValidCounterType$ P1P1 | ReplaceWith$ AddOneMoreCounters | Description$ If one or more +1/+1 counters would be put on a creature you control, that many plus one +1/+1 counters are put on it instead.
|
||||||
SVar:AddOneMoreCounters:DB$ ReplaceCounter | ValidCounterType$ P1P1 | ChooseCounter$ True | Amount$ X
|
SVar:AddOneMoreCounters:DB$ ReplaceCounter | ValidCounterType$ P1P1 | ChooseCounter$ True | Amount$ X
|
||||||
SVar:X:ReplaceCount$CounterNum/Plus.1
|
SVar:X:ReplaceCount$CounterNum/Plus.1
|
||||||
AI:RemoveDeck:Random
|
AI:RemoveDeck:Random
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
Name:Rite of Flame
|
Name:Rite of Flame
|
||||||
ManaCost:R
|
ManaCost:R
|
||||||
Types:Sorcery
|
Types:Sorcery
|
||||||
A:SP$ Mana | Produced$ R | Amount$ 2 | AILogic$ ManaRitual | AINoRecursiveCheck$ True | SpellDescription$ Add {R}{R}, then add {R} for each card named CARDNAME in each graveyard.
|
A:SP$ Mana | Produced$ R | Amount$ 2 | AILogic$ ManaRitual | AINoRecursiveCheck$ True | SubAbility$ SubMana | SpellDescription$ Add {R}{R}, then add {R} for each card named CARDNAME in each graveyard.
|
||||||
SVar:SubMana:DB$ Mana | Produced$ R | Amount$ X
|
SVar:SubMana:DB$ Mana | Produced$ R | Amount$ X
|
||||||
SVar:X:Count$NamedInAllYards.Rite of Flame
|
SVar:X:Count$NamedInAllYards.Rite of Flame
|
||||||
DeckHints:Name$Rite of Flame
|
DeckHints:Name$Rite of Flame
|
||||||
|
|||||||
@@ -2,5 +2,5 @@ Name:Submerged Boneyard
|
|||||||
ManaCost:no cost
|
ManaCost:no cost
|
||||||
Types:Land
|
Types:Land
|
||||||
K:CARDNAME enters the battlefield tapped.
|
K:CARDNAME enters the battlefield tapped.
|
||||||
A:AB$ Mana | Cost$ T | Produced$ Combo U | SpellDescription$ Add {U} or {B}.
|
A:AB$ Mana | Cost$ T | Produced$ Combo U B | SpellDescription$ Add {U} or {B}.
|
||||||
Oracle:Submerged Boneyard enters the battlefield tapped.\n{T}: Add {U} or {B}.
|
Oracle:Submerged Boneyard enters the battlefield tapped.\n{T}: Add {U} or {B}.
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ Types:Legendary Creature Vampire Warlock
|
|||||||
PT:1/1
|
PT:1/1
|
||||||
K:Menace
|
K:Menace
|
||||||
K:Lifelink
|
K:Lifelink
|
||||||
R:Event$ Moved | ActiveZones$ Battlefield | Origin$ Battlefield | Destination$ Graveyard | ValidCard$ Card.Creature+nonToken+OppOwn | ReplaceWith$ Exile | Description$ If a nontoken creature an opponent controls would die, exile it instead. When you do, you may pay {2}. If you do, create a 1/1 black and green Pest creature token with "When this creature dies, you gain 1 life."
|
R:Event$ Moved | ActiveZones$ Battlefield | Origin$ Battlefield | Destination$ Graveyard | ValidLKI$ Card.Creature+nonToken+OppOwn | ReplaceWith$ Exile | Description$ If a nontoken creature an opponent controls would die, exile it instead. When you do, you may pay {2}. If you do, create a 1/1 black and green Pest creature token with "When this creature dies, you gain 1 life."
|
||||||
SVar:Exile:DB$ ChangeZone | Hidden$ True | Origin$ All | Destination$ Exile | Defined$ ReplacedCard | SubAbility$ DBImmediateTrigger
|
SVar:Exile:DB$ ChangeZone | Hidden$ True | Origin$ All | Destination$ Exile | Defined$ ReplacedCard | SubAbility$ DBImmediateTrigger
|
||||||
SVar:DBImmediateTrigger:DB$ ImmediateTrigger | Execute$ TrigToken | TriggerDescription$ If a nontoken creature an opponent controls would die, exile it instead. When you do, you may pay {2}. If you do, create a 1/1 black and green Pest creature token with "When this creature dies, you gain 1 life."
|
SVar:DBImmediateTrigger:DB$ ImmediateTrigger | Execute$ TrigToken | TriggerDescription$ If a nontoken creature an opponent controls would die, exile it instead. When you do, you may pay {2}. If you do, create a 1/1 black and green Pest creature token with "When this creature dies, you gain 1 life."
|
||||||
SVar:TrigToken:AB$ Token | Cost$ 2 | TokenAmount$ 1 | TokenScript$ bg_1_1_pest_lifegain | TokenOwner$ You
|
SVar:TrigToken:AB$ Token | Cost$ 2 | TokenAmount$ 1 | TokenScript$ bg_1_1_pest_lifegain | TokenOwner$ You
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ ManaCost:4 B B
|
|||||||
Types:Creature Horror
|
Types:Creature Horror
|
||||||
PT:4/5
|
PT:4/5
|
||||||
K:Trample
|
K:Trample
|
||||||
R:Event$ Moved | ActiveZones$ Battlefield | Origin$ Battlefield | Destination$ Graveyard | ValidCard$ Creature.Other | ReplaceWith$ Exile | Description$ If another creature would die, exile it instead.
|
R:Event$ Moved | ActiveZones$ Battlefield | Origin$ Battlefield | Destination$ Graveyard | ValidLKI$ Creature.Other | ReplaceWith$ Exile | Description$ If another creature would die, exile it instead.
|
||||||
SVar:Exile:DB$ ChangeZone | Hidden$ True | Origin$ All | Destination$ Exile | Defined$ ReplacedCard | SubAbility$ DBRemember | RememberChanged$ True
|
SVar:Exile:DB$ ChangeZone | Hidden$ True | Origin$ All | Destination$ Exile | Defined$ ReplacedCard | SubAbility$ DBRemember | RememberChanged$ True
|
||||||
SVar:DBRemember:DB$ Pump | ConditionDefined$ Remembered | ConditionPresent$ Card.inZoneExile | ConditionCompare$ GE1
|
SVar:DBRemember:DB$ Pump | ConditionDefined$ Remembered | ConditionPresent$ Card.inZoneExile | ConditionCompare$ GE1
|
||||||
T:Mode$ ChangesZone | Origin$ Exile | Destination$ Any | Static$ True | ValidCard$ Card.IsRemembered+ExiledWithSource | Execute$ DBForget
|
T:Mode$ ChangesZone | Origin$ Exile | Destination$ Any | Static$ True | ValidCard$ Card.IsRemembered+ExiledWithSource | Execute$ DBForget
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ ManaCost:1
|
|||||||
Types:Legendary Artifact Creature Insect
|
Types:Legendary Artifact Creature Insect
|
||||||
PT:0/0
|
PT:0/0
|
||||||
K:Modular:1
|
K:Modular:1
|
||||||
R:Event$ AddCounter | ActiveZones$ Battlefield | ValidCause$ Triggered.Modular | ValidCard$ Creature.YouCtrl+InZoneBattlefield | ValidCounterType$ P1P1 | ReplaceWith$ AddOneMoreCounter | Description$ If a modular triggered ability would put one or more +1/+1 counters on a creature you control, that many plus one +1/+1 counters are put on it instead.
|
R:Event$ AddCounter | ActiveZones$ Battlefield | ValidCause$ Triggered.Modular | ValidCard$ Creature.YouCtrl+inZoneBattlefield | ValidCounterType$ P1P1 | ReplaceWith$ AddOneMoreCounter | Description$ If a modular triggered ability would put one or more +1/+1 counters on a creature you control, that many plus one +1/+1 counters are put on it instead.
|
||||||
SVar:AddOneMoreCounter:DB$ ReplaceCounter | ValidCounterType$ P1P1 | ChooseCounter$ True | Amount$ X
|
SVar:AddOneMoreCounter:DB$ ReplaceCounter | ValidCounterType$ P1P1 | ChooseCounter$ True | Amount$ X
|
||||||
SVar:X:ReplaceCount$CounterNum/Plus.1
|
SVar:X:ReplaceCount$CounterNum/Plus.1
|
||||||
A:AB$ Destroy | Cost$ R | ValidTgts$ Artifact.YouCtrl | TgtPrompt$ Choose target artifact you control | SpellDescription$ Destroy target artifact you control.
|
A:AB$ Destroy | Cost$ R | ValidTgts$ Artifact.YouCtrl | TgtPrompt$ Choose target artifact you control | SpellDescription$ Destroy target artifact you control.
|
||||||
|
|||||||
Reference in New Issue
Block a user