mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-20 12:48:00 +00:00
Unblockable fixes (#1662)
* Add shortcuts * Fix cards * Clean up * Fix CantBlockBy checks * Fix stack overflow Co-authored-by: tool4EvEr <tool4EvEr@192.168.0.59>
This commit is contained in:
@@ -359,7 +359,7 @@ public class AiAttackController {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
final List<Card> notNeededAsBlockers = new CardCollection(attackers);
|
final CardCollection notNeededAsBlockers = new CardCollection(attackers);
|
||||||
|
|
||||||
// don't hold back creatures that can't block any of the human creatures
|
// don't hold back creatures that can't block any of the human creatures
|
||||||
final List<Card> blockers = getPossibleBlockers(attackers, opponentsAttackers, true);
|
final List<Card> blockers = getPossibleBlockers(attackers, opponentsAttackers, true);
|
||||||
@@ -378,7 +378,7 @@ public class AiAttackController {
|
|||||||
int thresholdMod = 0;
|
int thresholdMod = 0;
|
||||||
int lastAcceptableBaselineLife = 0;
|
int lastAcceptableBaselineLife = 0;
|
||||||
if (pilotsNonAggroDeck) {
|
if (pilotsNonAggroDeck) {
|
||||||
lastAcceptableBaselineLife = ComputerUtil.predictNextCombatsRemainingLife(ai, playAggro, pilotsNonAggroDeck, 0, new CardCollection(notNeededAsBlockers));
|
lastAcceptableBaselineLife = ComputerUtil.predictNextCombatsRemainingLife(ai, playAggro, pilotsNonAggroDeck, 0, notNeededAsBlockers);
|
||||||
if (!ai.isCardInPlay("Laboratory Maniac")) {
|
if (!ai.isCardInPlay("Laboratory Maniac")) {
|
||||||
// AI is getting milled out
|
// AI is getting milled out
|
||||||
thresholdMod += 3 - Math.min(ai.getCardsIn(ZoneType.Library).size(), 3);
|
thresholdMod += 3 - Math.min(ai.getCardsIn(ZoneType.Library).size(), 3);
|
||||||
@@ -397,7 +397,7 @@ public class AiAttackController {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
notNeededAsBlockers.add(c);
|
notNeededAsBlockers.add(c);
|
||||||
int currentBaselineLife = ComputerUtil.predictNextCombatsRemainingLife(ai, playAggro, pilotsNonAggroDeck, 0, new CardCollection(notNeededAsBlockers));
|
int currentBaselineLife = ComputerUtil.predictNextCombatsRemainingLife(ai, playAggro, pilotsNonAggroDeck, 0, notNeededAsBlockers);
|
||||||
// AI doesn't know from what it will lose, so it might still keep an unnecessary blocker back sometimes
|
// AI doesn't know from what it will lose, so it might still keep an unnecessary blocker back sometimes
|
||||||
if (currentBaselineLife == Integer.MIN_VALUE) {
|
if (currentBaselineLife == Integer.MIN_VALUE) {
|
||||||
notNeededAsBlockers.remove(c);
|
notNeededAsBlockers.remove(c);
|
||||||
@@ -839,6 +839,7 @@ public class AiAttackController {
|
|||||||
if (attackMax != -1 && combat.getAttackers().size() >= attackMax)
|
if (attackMax != -1 && combat.getAttackers().size() >= attackMax)
|
||||||
return aiAggression;
|
return aiAggression;
|
||||||
|
|
||||||
|
// TODO if lifeInDanger use chance to hold back some
|
||||||
if (canAttackWrapper(attacker, defender) && isEffectiveAttacker(ai, attacker, combat, defender)) {
|
if (canAttackWrapper(attacker, defender) && isEffectiveAttacker(ai, attacker, combat, defender)) {
|
||||||
combat.addAttacker(attacker, defender);
|
combat.addAttacker(attacker, defender);
|
||||||
}
|
}
|
||||||
@@ -848,7 +849,7 @@ public class AiAttackController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Cards that are remembered to attack anyway (e.g. temporarily stolen creatures)
|
// Cards that are remembered to attack anyway (e.g. temporarily stolen creatures)
|
||||||
if (ai.getController() instanceof PlayerControllerAi) {
|
if (ai.getController().isAI()) {
|
||||||
// Only do this if |ai| is actually an AI - as we could be trying to predict how the human will attack.
|
// Only do this if |ai| is actually an AI - as we could be trying to predict how the human will attack.
|
||||||
for (Card attacker : this.attackers) {
|
for (Card attacker : this.attackers) {
|
||||||
if (AiCardMemory.isRememberedCard(ai, attacker, AiCardMemory.MemorySet.MANDATORY_ATTACKERS)) {
|
if (AiCardMemory.isRememberedCard(ai, attacker, AiCardMemory.MemorySet.MANDATORY_ATTACKERS)) {
|
||||||
@@ -894,7 +895,7 @@ public class AiAttackController {
|
|||||||
aiAggression = 6;
|
aiAggression = 6;
|
||||||
for (Card attacker : this.attackers) {
|
for (Card attacker : this.attackers) {
|
||||||
// reached max, breakup
|
// reached max, breakup
|
||||||
if (attackMax != -1 && combat.getAttackers().size() >= attackMax)
|
if (combat.getAttackers().size() >= attackMax)
|
||||||
break;
|
break;
|
||||||
if (canAttackWrapper(attacker, defender) && shouldAttack(attacker, this.blockers, combat, defender)) {
|
if (canAttackWrapper(attacker, defender) && shouldAttack(attacker, this.blockers, combat, defender)) {
|
||||||
combat.addAttacker(attacker, defender);
|
combat.addAttacker(attacker, defender);
|
||||||
|
|||||||
@@ -1061,7 +1061,7 @@ public class AiBlockController {
|
|||||||
|
|
||||||
// remove all attackers that can't be blocked anyway
|
// remove all attackers that can't be blocked anyway
|
||||||
for (final Card a : attackers) {
|
for (final Card a : attackers) {
|
||||||
if (!CombatUtil.canBeBlocked(a, ai)) {
|
if (!CombatUtil.canBeBlocked(a, null, ai)) { // pass null to skip redundant checks for performance
|
||||||
attackersLeft.remove(a);
|
attackersLeft.remove(a);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1055,11 +1055,9 @@ public class AiController {
|
|||||||
// Cheaper Spectacle costs should be preferred
|
// Cheaper Spectacle costs should be preferred
|
||||||
// FIXME: Any better way to identify that these are the same ability, one with Spectacle and one not?
|
// FIXME: Any better way to identify that these are the same ability, one with Spectacle and one not?
|
||||||
// (looks like it's not a full-fledged alternative cost as such, and is not processed with other alt costs)
|
// (looks like it's not a full-fledged alternative cost as such, and is not processed with other alt costs)
|
||||||
if (a.isSpectacle() && !b.isSpectacle()
|
if (a.isSpectacle() && !b.isSpectacle() && a1 < b1) {
|
||||||
&& a.getPayCosts().getTotalMana().getCMC() < b.getPayCosts().getTotalMana().getCMC()) {
|
|
||||||
return 1;
|
return 1;
|
||||||
} else if (b.isSpectacle() && !a.isSpectacle()
|
} else if (b.isSpectacle() && !a.isSpectacle() && b1 < a1) {
|
||||||
&& b.getPayCosts().getTotalMana().getCMC() < a.getPayCosts().getTotalMana().getCMC()) {
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1088,6 +1086,9 @@ public class AiController {
|
|||||||
if (source.hasSVar("AIPriorityModifier")) {
|
if (source.hasSVar("AIPriorityModifier")) {
|
||||||
p += Integer.parseInt(source.getSVar("AIPriorityModifier"));
|
p += Integer.parseInt(source.getSVar("AIPriorityModifier"));
|
||||||
}
|
}
|
||||||
|
if (ComputerUtilCard.isCardRemAIDeck(source)) {
|
||||||
|
p -= 10;
|
||||||
|
}
|
||||||
// don't play equipments before having any creatures
|
// don't play equipments before having any creatures
|
||||||
if (source.isEquipment() && noCreatures) {
|
if (source.isEquipment() && noCreatures) {
|
||||||
p -= 9;
|
p -= 9;
|
||||||
@@ -1691,6 +1692,7 @@ public class AiController {
|
|||||||
Iterables.removeIf(saList, new Predicate<SpellAbility>() {
|
Iterables.removeIf(saList, new Predicate<SpellAbility>() {
|
||||||
@Override
|
@Override
|
||||||
public boolean apply(final SpellAbility spellAbility) { //don't include removedAI cards if somehow the AI can play the ability or gain control of unsupported card
|
public boolean apply(final SpellAbility spellAbility) { //don't include removedAI cards if somehow the AI can play the ability or gain control of unsupported card
|
||||||
|
// TODO allow when experimental profile?
|
||||||
return spellAbility instanceof LandAbility || (spellAbility.getHostCard() != null && ComputerUtilCard.isCardRemAIDeck(spellAbility.getHostCard()));
|
return spellAbility instanceof LandAbility || (spellAbility.getHostCard() != null && ComputerUtilCard.isCardRemAIDeck(spellAbility.getHostCard()));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -385,6 +385,9 @@ public class ComputerUtilCard {
|
|||||||
* @return the card
|
* @return the card
|
||||||
*/
|
*/
|
||||||
public static Card getBestCreatureAI(final Iterable<Card> list) {
|
public static Card getBestCreatureAI(final Iterable<Card> list) {
|
||||||
|
if (Iterables.size(list) == 1) {
|
||||||
|
return Iterables.get(list, 0);
|
||||||
|
}
|
||||||
return Aggregates.itemWithMax(Iterables.filter(list, CardPredicates.Presets.CREATURES), ComputerUtilCard.creatureEvaluator);
|
return Aggregates.itemWithMax(Iterables.filter(list, CardPredicates.Presets.CREATURES), ComputerUtilCard.creatureEvaluator);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -397,6 +400,9 @@ public class ComputerUtilCard {
|
|||||||
* @return a {@link forge.game.card.Card} object.
|
* @return a {@link forge.game.card.Card} object.
|
||||||
*/
|
*/
|
||||||
public static Card getWorstCreatureAI(final Iterable<Card> list) {
|
public static Card getWorstCreatureAI(final Iterable<Card> list) {
|
||||||
|
if (Iterables.size(list) == 1) {
|
||||||
|
return Iterables.get(list, 0);
|
||||||
|
}
|
||||||
return Aggregates.itemWithMin(Iterables.filter(list, CardPredicates.Presets.CREATURES), ComputerUtilCard.creatureEvaluator);
|
return Aggregates.itemWithMin(Iterables.filter(list, CardPredicates.Presets.CREATURES), ComputerUtilCard.creatureEvaluator);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -410,6 +416,9 @@ public class ComputerUtilCard {
|
|||||||
* @return a {@link forge.game.card.Card} object.
|
* @return a {@link forge.game.card.Card} object.
|
||||||
*/
|
*/
|
||||||
public static Card getBestCreatureToBounceAI(final CardCollectionView list) {
|
public static Card getBestCreatureToBounceAI(final CardCollectionView list) {
|
||||||
|
if (Iterables.size(list) == 1) {
|
||||||
|
return Iterables.get(list, 0);
|
||||||
|
}
|
||||||
final int tokenBonus = 60;
|
final int tokenBonus = 60;
|
||||||
Card biggest = null;
|
Card biggest = null;
|
||||||
int biggestvalue = -1;
|
int biggestvalue = -1;
|
||||||
|
|||||||
@@ -1565,8 +1565,9 @@ public class AttachAi extends SpellAbilityAi {
|
|||||||
|
|
||||||
boolean canBeBlocked = false;
|
boolean canBeBlocked = false;
|
||||||
for (Player opp : ai.getOpponents()) {
|
for (Player opp : ai.getOpponents()) {
|
||||||
if (CombatUtil.canBeBlocked(card, opp)) {
|
if (CombatUtil.canBeBlocked(card, null, opp)) {
|
||||||
canBeBlocked = true;
|
canBeBlocked = true;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -69,10 +69,10 @@ public class EffectAi extends SpellAbilityAi {
|
|||||||
randomReturn = true;
|
randomReturn = true;
|
||||||
}
|
}
|
||||||
} else if (logic.equals("Fog")) {
|
} else if (logic.equals("Fog")) {
|
||||||
if (game.getPhaseHandler().isPlayerTurn(sa.getActivatingPlayer())) {
|
if (phase.isPlayerTurn(sa.getActivatingPlayer())) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!game.getPhaseHandler().is(PhaseType.COMBAT_DECLARE_BLOCKERS)) {
|
if (!phase.is(PhaseType.COMBAT_DECLARE_BLOCKERS)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!game.getStack().isEmpty()) {
|
if (!game.getStack().isEmpty()) {
|
||||||
@@ -216,9 +216,12 @@ public class EffectAi extends SpellAbilityAi {
|
|||||||
} else if (logic.equals("Fight")) {
|
} else if (logic.equals("Fight")) {
|
||||||
return FightAi.canFightAi(ai, sa, 0, 0);
|
return FightAi.canFightAi(ai, sa, 0, 0);
|
||||||
} else if (logic.equals("Pump")) {
|
} else if (logic.equals("Pump")) {
|
||||||
if (SpellApiToAi.Converter.get(sa.getApi()).canPlayAIWithSubs(ai, sa)) {
|
List<Card> options = ai.getCreaturesInPlay();
|
||||||
|
if (phase.isPlayerTurn(ai) && phase.getPhase().isBefore(PhaseType.COMBAT_DECLARE_BLOCKERS) && !options.isEmpty()) {
|
||||||
|
sa.getTargets().add(ComputerUtilCard.getBestCreatureAI(options));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
} else if (logic.equals("Burn")) {
|
} else if (logic.equals("Burn")) {
|
||||||
// for DamageDeal sub-abilities (eg. Wild Slash, Skullcrack)
|
// for DamageDeal sub-abilities (eg. Wild Slash, Skullcrack)
|
||||||
SpellAbility burn = sa.getSubAbility();
|
SpellAbility burn = sa.getSubAbility();
|
||||||
@@ -245,7 +248,7 @@ public class EffectAi extends SpellAbilityAi {
|
|||||||
Card host = sa.getHostCard();
|
Card host = sa.getHostCard();
|
||||||
Combat combat = game.getCombat();
|
Combat combat = game.getCombat();
|
||||||
if (combat != null && combat.isAttacking(host, ai) && !combat.isBlocked(host)
|
if (combat != null && combat.isAttacking(host, ai) && !combat.isBlocked(host)
|
||||||
&& game.getPhaseHandler().is(PhaseType.COMBAT_DECLARE_BLOCKERS)
|
&& phase.is(PhaseType.COMBAT_DECLARE_BLOCKERS)
|
||||||
&& !AiCardMemory.isRememberedCard(ai, host, AiCardMemory.MemorySet.ACTIVATED_THIS_TURN)) {
|
&& !AiCardMemory.isRememberedCard(ai, host, AiCardMemory.MemorySet.ACTIVATED_THIS_TURN)) {
|
||||||
AiCardMemory.rememberCard(ai, host, AiCardMemory.MemorySet.ACTIVATED_THIS_TURN); // ideally needs once per combat or something
|
AiCardMemory.rememberCard(ai, host, AiCardMemory.MemorySet.ACTIVATED_THIS_TURN); // ideally needs once per combat or something
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -102,7 +102,7 @@ public final class EncodeAi extends SpellAbilityAi {
|
|||||||
public boolean apply(final Card c) {
|
public boolean apply(final Card c) {
|
||||||
boolean canAttackOpponent = false;
|
boolean canAttackOpponent = false;
|
||||||
for (Player opp : ai.getOpponents()) {
|
for (Player opp : ai.getOpponents()) {
|
||||||
if (CombatUtil.canAttack(c, opp) && !CombatUtil.canBeBlocked(c, opp)) {
|
if (CombatUtil.canAttack(c, opp) && !CombatUtil.canBeBlocked(c, null, opp)) {
|
||||||
canAttackOpponent = true;
|
canAttackOpponent = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -336,7 +336,7 @@ public abstract class PumpAiBase extends SpellAbilityAi {
|
|||||||
Keyword.FLANKING).isEmpty();
|
Keyword.FLANKING).isEmpty();
|
||||||
} else if (keyword.startsWith("Trample")) {
|
} else if (keyword.startsWith("Trample")) {
|
||||||
return !ph.isPlayerTurn(opp) && (CombatUtil.canAttack(card, opp) || (combat != null && combat.isAttacking(card)))
|
return !ph.isPlayerTurn(opp) && (CombatUtil.canAttack(card, opp) || (combat != null && combat.isAttacking(card)))
|
||||||
&& CombatUtil.canBeBlocked(card, opp)
|
&& CombatUtil.canBeBlocked(card, null, opp)
|
||||||
&& !ph.getPhase().isAfter(PhaseType.COMBAT_DECLARE_ATTACKERS)
|
&& !ph.getPhase().isAfter(PhaseType.COMBAT_DECLARE_ATTACKERS)
|
||||||
&& newPower > 1
|
&& newPower > 1
|
||||||
&& Iterables.any(opp.getCreaturesInPlay(), CardPredicates.possibleBlockers(card));
|
&& Iterables.any(opp.getCreaturesInPlay(), CardPredicates.possibleBlockers(card));
|
||||||
|
|||||||
@@ -555,10 +555,19 @@ public class CombatUtil {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Unblockable check
|
||||||
|
for (final Card ca : attacker.getGame().getCardsIn(ZoneType.STATIC_ABILITIES_SOURCE_ZONES)) {
|
||||||
|
for (final StaticAbility stAb : ca.getStaticAbilities()) {
|
||||||
|
if (stAb.applyAbility("CantBlockBy", attacker, null)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return canBeBlocked(attacker, defendingPlayer);
|
return canBeBlocked(attacker, defendingPlayer);
|
||||||
}
|
}
|
||||||
|
|
||||||
// can the attacker be blocked at all?
|
|
||||||
/**
|
/**
|
||||||
* <p>
|
* <p>
|
||||||
* canBeBlocked.
|
* canBeBlocked.
|
||||||
|
|||||||
@@ -302,7 +302,7 @@ public class StaticAbility extends CardTraitBase implements IIdentifiable, Clone
|
|||||||
|
|
||||||
if (mode.equals("CantAttack")) {
|
if (mode.equals("CantAttack")) {
|
||||||
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")) { // null allowed, so no instanceof check
|
||||||
return StaticAbilityCantAttackBlock.applyCantBlockByAbility(this, card, (Card)target);
|
return StaticAbilityCantAttackBlock.applyCantBlockByAbility(this, card, (Card)target);
|
||||||
} else if (mode.equals("CanAttackIfHaste")) {
|
} else if (mode.equals("CanAttackIfHaste")) {
|
||||||
return StaticAbilityCantAttackBlock.applyCanAttackHasteAbility(this, card, target);
|
return StaticAbilityCantAttackBlock.applyCanAttackHasteAbility(this, card, target);
|
||||||
|
|||||||
@@ -115,7 +115,7 @@ public class StaticAbilityCantAttackBlock {
|
|||||||
if (stAb.hasParam("ValidBlocker")) {
|
if (stAb.hasParam("ValidBlocker")) {
|
||||||
boolean stillblock = true;
|
boolean stillblock = true;
|
||||||
for (final String v : stAb.getParam("ValidBlocker").split(",")) {
|
for (final String v : stAb.getParam("ValidBlocker").split(",")) {
|
||||||
if (blocker.isValid(v, host.getController(), host, stAb)) {
|
if (blocker != null && blocker.isValid(v, host.getController(), host, stAb)) {
|
||||||
stillblock = false;
|
stillblock = false;
|
||||||
//Dragon Hunter check
|
//Dragon Hunter check
|
||||||
if (v.contains("withoutReach") && blocker.hasStartOfKeyword("IfReach")) {
|
if (v.contains("withoutReach") && blocker.hasStartOfKeyword("IfReach")) {
|
||||||
|
|||||||
@@ -3,5 +3,5 @@ ManaCost:3 W U
|
|||||||
Types:Creature Human Knight
|
Types:Creature Human Knight
|
||||||
PT:2/5
|
PT:2/5
|
||||||
K:Vigilance
|
K:Vigilance
|
||||||
K:Unblockable
|
S:Mode$ CantBlockBy | ValidAttacker$ Creature.Self | Description$ CARDNAME can't be blocked.
|
||||||
Oracle:Vigilance\nAzorius Knight-Arbiter can't be blocked.
|
Oracle:Vigilance\nAzorius Knight-Arbiter can't be blocked.
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ Name:Gray Harbor Merfolk
|
|||||||
ManaCost:1 U
|
ManaCost:1 U
|
||||||
Types:Creature Merfolk Rogue
|
Types:Creature Merfolk Rogue
|
||||||
PT:0/3
|
PT:0/3
|
||||||
K:Unblockable
|
S:Mode$ CantBlockBy | ValidAttacker$ Creature.Self | Description$ CARDNAME can't be blocked.
|
||||||
S:Mode$ Continuous | Affected$ Card.Self | AddPower$ 2 | IsPresent$ Creature.IsCommander+YouCtrl,Planeswalker.IsCommander+YouCtrl | Description$ CARDNAME gets +2/+0 as long as you control a commander that's a creature or planeswalker.
|
S:Mode$ Continuous | Affected$ Card.Self | AddPower$ 2 | IsPresent$ Creature.IsCommander+YouCtrl,Planeswalker.IsCommander+YouCtrl | Description$ CARDNAME gets +2/+0 as long as you control a commander that's a creature or planeswalker.
|
||||||
AI:RemoveDeck:NonCommander
|
AI:RemoveDeck:NonCommander
|
||||||
Oracle:Gray Harbor Merfolk can't be blocked.\nGray Harbor Merfolk gets +2/+0 as long as you control a commander that's a creature or planeswalker.
|
Oracle:Gray Harbor Merfolk can't be blocked.\nGray Harbor Merfolk gets +2/+0 as long as you control a commander that's a creature or planeswalker.
|
||||||
|
|||||||
@@ -2,5 +2,5 @@ Name:Jhessian Infiltrator
|
|||||||
ManaCost:G U
|
ManaCost:G U
|
||||||
Types:Creature Human Rogue
|
Types:Creature Human Rogue
|
||||||
PT:2/2
|
PT:2/2
|
||||||
K:Unblockable
|
S:Mode$ CantBlockBy | ValidAttacker$ Creature.Self | Description$ CARDNAME can't be blocked.
|
||||||
Oracle:Jhessian Infiltrator can't be blocked.
|
Oracle:Jhessian Infiltrator can't be blocked.
|
||||||
|
|||||||
@@ -2,5 +2,5 @@ Name:Phantom Warrior
|
|||||||
ManaCost:1 U U
|
ManaCost:1 U U
|
||||||
Types:Creature Illusion Warrior
|
Types:Creature Illusion Warrior
|
||||||
PT:2/2
|
PT:2/2
|
||||||
K:Unblockable
|
S:Mode$ CantBlockBy | ValidAttacker$ Creature.Self | Description$ CARDNAME can't be blocked.
|
||||||
Oracle:Phantom Warrior can't be blocked.
|
Oracle:Phantom Warrior can't be blocked.
|
||||||
|
|||||||
@@ -2,5 +2,5 @@ Name:Plasma Elemental
|
|||||||
ManaCost:5 U
|
ManaCost:5 U
|
||||||
Types:Creature Elemental
|
Types:Creature Elemental
|
||||||
PT:4/1
|
PT:4/1
|
||||||
K:Unblockable
|
S:Mode$ CantBlockBy | ValidAttacker$ Creature.Self | Description$ CARDNAME can't be blocked.
|
||||||
Oracle:Plasma Elemental can't be blocked.
|
Oracle:Plasma Elemental can't be blocked.
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ Name:Soulsworn Spirit
|
|||||||
ManaCost:3 U
|
ManaCost:3 U
|
||||||
Types:Creature Spirit
|
Types:Creature Spirit
|
||||||
PT:2/1
|
PT:2/1
|
||||||
K:Unblockable
|
S:Mode$ CantBlockBy | ValidAttacker$ Creature.Self | Description$ CARDNAME can't be blocked.
|
||||||
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ Detain | TriggerDescription$ When CARDNAME enters the battlefield, detain target creature an opponent controls. (Until your next turn, that creature can't attack or block and its activated abilities can't be activated.)
|
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ Detain | TriggerDescription$ When CARDNAME enters the battlefield, detain target creature an opponent controls. (Until your next turn, that creature can't attack or block and its activated abilities can't be activated.)
|
||||||
SVar:Detain:DB$ Pump | KW$ HIDDEN CARDNAME can't attack or block. & HIDDEN CARDNAME's activated abilities can't be activated. | IsCurse$ True | Duration$ UntilYourNextTurn | ValidTgts$ Creature.OppCtrl | TgtPrompt$ Select target creature your opponent controls to detain.
|
SVar:Detain:DB$ Pump | KW$ HIDDEN CARDNAME can't attack or block. & HIDDEN CARDNAME's activated abilities can't be activated. | IsCurse$ True | Duration$ UntilYourNextTurn | ValidTgts$ Creature.OppCtrl | TgtPrompt$ Select target creature your opponent controls to detain.
|
||||||
SVar:PlayMain1:TRUE
|
SVar:PlayMain1:TRUE
|
||||||
|
|||||||
@@ -2,5 +2,5 @@ Name:Triton Shorestalker
|
|||||||
ManaCost:U
|
ManaCost:U
|
||||||
Types:Creature Merfolk Rogue
|
Types:Creature Merfolk Rogue
|
||||||
PT:1/1
|
PT:1/1
|
||||||
K:Unblockable
|
S:Mode$ CantBlockBy | ValidAttacker$ Creature.Self | Description$ CARDNAME can't be blocked.
|
||||||
Oracle:Triton Shorestalker can't be blocked.
|
Oracle:Triton Shorestalker can't be blocked.
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ Name:Vedalken Infiltrator
|
|||||||
ManaCost:1 U
|
ManaCost:1 U
|
||||||
Types:Creature Vedalken Rogue
|
Types:Creature Vedalken Rogue
|
||||||
PT:1/3
|
PT:1/3
|
||||||
K:Unblockable
|
S:Mode$ CantBlockBy | ValidAttacker$ Creature.Self | Description$ CARDNAME can't be blocked.
|
||||||
S:Mode$ Continuous | Affected$ Card.Self | AddPower$ 1 | Condition$ Metalcraft | Description$ Metalcraft — CARDNAME gets +1/+0 as long as you control three or more artifacts.
|
S:Mode$ Continuous | Affected$ Card.Self | AddPower$ 1 | Condition$ Metalcraft | Description$ Metalcraft — CARDNAME gets +1/+0 as long as you control three or more artifacts.
|
||||||
SVar:BuffedBy:Artifact
|
SVar:BuffedBy:Artifact
|
||||||
Oracle:Vedalken Infiltrator can't be blocked.\nMetalcraft — Vedalken Infiltrator gets +1/+0 as long as you control three or more artifacts.
|
Oracle:Vedalken Infiltrator can't be blocked.\nMetalcraft — Vedalken Infiltrator gets +1/+0 as long as you control three or more artifacts.
|
||||||
|
|||||||
Reference in New Issue
Block a user