mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-14 17:58:01 +00:00
KeywordEnum: replace more String Keywords checks with Enums
This commit is contained in:
@@ -30,6 +30,7 @@ import forge.game.card.*;
|
||||
import forge.game.combat.Combat;
|
||||
import forge.game.combat.CombatUtil;
|
||||
import forge.game.combat.GlobalAttackRestrictions;
|
||||
import forge.game.keyword.Keyword;
|
||||
import forge.game.keyword.KeywordInterface;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
@@ -201,8 +202,11 @@ public class AiAttackController {
|
||||
if (ComputerUtilCombat.poisonIfUnblocked(attacker, opp) > 0) {
|
||||
return true;
|
||||
}
|
||||
if (this.attackers.size() == 1 && attacker.hasKeyword("Exalted")
|
||||
&& ComputerUtilCombat.predictDamageTo(opp, 1, attacker, true) > 0) {
|
||||
|
||||
// TODO check if that makes sense
|
||||
int exalted = ai.countExaltedBonus();
|
||||
if (this.attackers.size() == 1 && exalted > 0
|
||||
&& ComputerUtilCombat.predictDamageTo(opp, exalted, attacker, true) > 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -316,7 +320,7 @@ public class AiAttackController {
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (c.hasKeyword("Vigilance")) {
|
||||
if (c.hasKeyword(Keyword.VIGILANCE)) {
|
||||
vigilantes.add(c);
|
||||
notNeededAsBlockers.remove(c); // they will be re-added later
|
||||
if (canBlockAnAttacker(c, opponentsAttackers, false)) {
|
||||
@@ -543,7 +547,7 @@ public class AiAttackController {
|
||||
|
||||
int trampleDamage = 0;
|
||||
for (Card attacker : blockedAttackers) {
|
||||
if (attacker.hasKeyword("Trample")) {
|
||||
if (attacker.hasKeyword(Keyword.TRAMPLE)) {
|
||||
int damage = ComputerUtilCombat.getAttack(attacker);
|
||||
for (Card blocker : this.blockers) {
|
||||
if (CombatUtil.canBlock(attacker, blocker)) {
|
||||
@@ -729,8 +733,9 @@ public class AiAttackController {
|
||||
|
||||
// Exalted
|
||||
if (combat.getAttackers().isEmpty()) {
|
||||
boolean exalted = false;
|
||||
int exaltedCount = 0;
|
||||
boolean exalted = ai.countExaltedBonus() > 2;
|
||||
|
||||
if (!exalted) {
|
||||
for (Card c : ai.getCardsIn(ZoneType.Battlefield)) {
|
||||
if (c.getName().equals("Rafiq of the Many") || c.getName().equals("Battlegrace Angel")) {
|
||||
exalted = true;
|
||||
@@ -740,12 +745,6 @@ public class AiAttackController {
|
||||
exalted = true;
|
||||
break;
|
||||
}
|
||||
if (c.hasKeyword("Exalted")) {
|
||||
exaltedCount++;
|
||||
if (exaltedCount > 2) {
|
||||
exalted = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (exalted) {
|
||||
@@ -1061,7 +1060,7 @@ public class AiAttackController {
|
||||
public final static int getAttack(final Card c) {
|
||||
int n = c.getNetCombatDamage();
|
||||
|
||||
if (c.hasKeyword("Double Strike")) {
|
||||
if (c.hasKeyword(Keyword.DOUBLE_STRIKE)) {
|
||||
n *= 2;
|
||||
}
|
||||
|
||||
@@ -1092,7 +1091,7 @@ public class AiAttackController {
|
||||
|
||||
// Is it a creature that has a more valuable ability with a tap cost than what it can do by attacking?
|
||||
if ((attacker.hasSVar("NonCombatPriority"))
|
||||
&& (!attacker.hasKeyword("Vigilance"))) {
|
||||
&& (!attacker.hasKeyword(Keyword.VIGILANCE))) {
|
||||
// For each level of priority, enemy has to have life as much as the creature's power
|
||||
// so a priority of 4 means the creature will not attack unless it can defeat that player in 4 successful attacks.
|
||||
// the lower the priroity, the less willing the AI is to use the creature for attacking.
|
||||
@@ -1144,7 +1143,7 @@ public class AiAttackController {
|
||||
&& CombatUtil.canBlock(attacker, defender)) {
|
||||
numberOfPossibleBlockers += 1;
|
||||
if (isWorthLessThanAllKillers && ComputerUtilCombat.canDestroyAttacker(ai, attacker, defender, combat, false)
|
||||
&& !(attacker.hasKeyword("Undying") && attacker.getCounters(CounterType.P1P1) == 0)) {
|
||||
&& !(attacker.hasKeyword(Keyword.UNDYING) && attacker.getCounters(CounterType.P1P1) == 0)) {
|
||||
canBeKilledByOne = true; // there is a single creature on the battlefield that can kill the creature
|
||||
// see if the defending creature is of higher or lower
|
||||
// value. We don't want to attack only to lose value
|
||||
@@ -1160,15 +1159,12 @@ public class AiAttackController {
|
||||
if (defender.getSVar("HasCombatEffect").equals("TRUE") || defender.getSVar("HasBlockEffect").equals("TRUE")) {
|
||||
canKillAllDangerous = false;
|
||||
} else {
|
||||
for (KeywordInterface inst : defender.getKeywords()) {
|
||||
String keyword = inst.getOriginal();
|
||||
if (keyword.equals("Wither") || keyword.equals("Infect") || keyword.equals("Lifelink")) {
|
||||
if (defender.hasKeyword(Keyword.WITHER) || defender.hasKeyword(Keyword.INFECT)
|
||||
|| defender.hasKeyword(Keyword.LIFELINK)) {
|
||||
canKillAllDangerous = false;
|
||||
break;
|
||||
// there is a creature that can survive an attack from this creature
|
||||
// and combat will have negative effects
|
||||
}
|
||||
}
|
||||
|
||||
// Check if maybe we are too reckless in adding this attacker
|
||||
if (canKillAllDangerous) {
|
||||
@@ -1191,7 +1187,7 @@ public class AiAttackController {
|
||||
}
|
||||
}
|
||||
|
||||
if (!attacker.hasKeyword("Vigilance") && ComputerUtilCard.canBeKilledByRoyalAssassin(ai, attacker)) {
|
||||
if (!attacker.hasKeyword(Keyword.VIGILANCE) && ComputerUtilCard.canBeKilledByRoyalAssassin(ai, attacker)) {
|
||||
canKillAllDangerous = false;
|
||||
canBeKilled = true;
|
||||
canBeKilledByOne = true;
|
||||
@@ -1277,7 +1273,7 @@ public class AiAttackController {
|
||||
// creature would leave the battlefield
|
||||
// no pain in exerting it
|
||||
shouldExert = true;
|
||||
} else if (c.hasKeyword("Vigilance")) {
|
||||
} else if (c.hasKeyword(Keyword.VIGILANCE)) {
|
||||
// Free exert - why not?
|
||||
shouldExert = true;
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ import forge.game.GameEntity;
|
||||
import forge.game.card.*;
|
||||
import forge.game.combat.Combat;
|
||||
import forge.game.combat.CombatUtil;
|
||||
import forge.game.keyword.Keyword;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.trigger.Trigger;
|
||||
import forge.game.trigger.TriggerType;
|
||||
@@ -180,7 +181,7 @@ public class AiBlockController {
|
||||
|
||||
if (attacker.hasStartOfKeyword("CantBeBlockedByAmount LT")
|
||||
|| attacker.hasKeyword("CARDNAME can't be blocked unless all creatures defending player controls block it.")
|
||||
|| attacker.hasKeyword("Menace")) {
|
||||
|| attacker.hasKeyword(Keyword.MENACE)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -204,11 +205,11 @@ public class AiBlockController {
|
||||
&& !ComputerUtilCombat.attackerHasThreateningAfflict(attacker, ai)) {
|
||||
blocker = ComputerUtilCard.getWorstCreatureAI(safeBlockers);
|
||||
// check whether it's better to block a creature without trample to absorb more damage
|
||||
if (attacker.hasKeyword("Trample")) {
|
||||
if (attacker.hasKeyword(Keyword.TRAMPLE)) {
|
||||
boolean doNotBlock = false;
|
||||
for (Card other : attackersLeft) {
|
||||
if (other.equals(attacker) || !CombatUtil.canBlock(other, blocker)
|
||||
|| other.hasKeyword("Trample")
|
||||
|| other.hasKeyword(Keyword.TRAMPLE)
|
||||
|| ComputerUtilCombat.attackerHasThreateningAfflict(other, ai)
|
||||
|| ComputerUtilCombat.canDestroyBlocker(ai, blocker, other, combat, false)
|
||||
|| other.hasKeyword("You may have CARDNAME assign its combat damage as though it weren't blocked.")) {
|
||||
@@ -231,10 +232,9 @@ public class AiBlockController {
|
||||
// 3.Blockers that can destroy the attacker and have an upside when dying
|
||||
killingBlockers = getKillingBlockers(combat, attacker, blockers);
|
||||
for (Card b : killingBlockers) {
|
||||
if ((b.hasKeyword("Undying") && b.getCounters(CounterType.P1P1) == 0)
|
||||
|| b.hasSVar("SacMe")
|
||||
|| (b.hasStartOfKeyword("Vanishing") && b.getCounters(CounterType.TIME) == 1)
|
||||
|| (b.hasStartOfKeyword("Fading") && b.getCounters(CounterType.FADE) == 0)
|
||||
if ((b.hasKeyword(Keyword.UNDYING) && b.getCounters(CounterType.P1P1) == 0) || b.hasSVar("SacMe")
|
||||
|| (b.hasKeyword(Keyword.VANISHING) && b.getCounters(CounterType.TIME) == 1)
|
||||
|| (b.hasKeyword(Keyword.FADING) && b.getCounters(CounterType.FADE) == 0)
|
||||
|| b.hasSVar("EndOfTurnLeavePlay")) {
|
||||
blocker = b;
|
||||
break;
|
||||
@@ -293,8 +293,7 @@ public class AiBlockController {
|
||||
|
||||
// 6. Blockers that don't survive until the next turn anyway
|
||||
for (final Card attacker : attackersLeft) {
|
||||
if (attacker.hasStartOfKeyword("CantBeBlockedByAmount LT")
|
||||
|| attacker.hasKeyword("Menace")
|
||||
if (attacker.hasStartOfKeyword("CantBeBlockedByAmount LT") || attacker.hasKeyword(Keyword.MENACE)
|
||||
|| attacker.hasKeyword("CARDNAME can't be blocked unless all creatures defending player controls block it.")) {
|
||||
continue;
|
||||
}
|
||||
@@ -304,8 +303,8 @@ public class AiBlockController {
|
||||
final List<Card> blockers = getPossibleBlockers(combat, attacker, blockersLeft, true);
|
||||
|
||||
for (Card b : blockers) {
|
||||
if ((b.hasStartOfKeyword("Vanishing") && b.getCounters(CounterType.TIME) == 1)
|
||||
|| (b.hasStartOfKeyword("Fading") && b.getCounters(CounterType.FADE) == 0)
|
||||
if ((b.hasKeyword(Keyword.VANISHING) && b.getCounters(CounterType.TIME) == 1)
|
||||
|| (b.hasKeyword(Keyword.FADING) && b.getCounters(CounterType.FADE) == 0)
|
||||
|| b.hasSVar("EndOfTurnLeavePlay")) {
|
||||
blocker = b;
|
||||
if (!ComputerUtilCombat.canDestroyAttacker(ai, attacker, blocker, combat, false)) {
|
||||
@@ -531,7 +530,7 @@ public class AiBlockController {
|
||||
|
||||
// Try to block a Menace attacker with two blockers, neither of which will die
|
||||
for (final Card attacker : attackersLeft) {
|
||||
if (!attacker.hasKeyword("Menace") && !attacker.hasStartOfKeyword("CantBeBlockedByAmount LT2")) {
|
||||
if (!attacker.hasKeyword(Keyword.MENACE) && !attacker.hasStartOfKeyword("CantBeBlockedByAmount LT2")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -590,7 +589,7 @@ public class AiBlockController {
|
||||
for (final Card attacker : attackersLeft) {
|
||||
|
||||
if (attacker.hasStartOfKeyword("CantBeBlockedByAmount LT")
|
||||
|| attacker.hasKeyword("Menace")
|
||||
|| attacker.hasKeyword(Keyword.MENACE)
|
||||
|| attacker.hasKeyword("CARDNAME can't be blocked unless all creatures defending player controls block it.")) {
|
||||
continue;
|
||||
}
|
||||
@@ -645,7 +644,7 @@ public class AiBlockController {
|
||||
if (attacker.hasStartOfKeyword("CantBeBlockedByAmount LT")
|
||||
|| attacker.hasKeyword("You may have CARDNAME assign its combat damage as though it weren't blocked.")
|
||||
|| attacker.hasKeyword("CARDNAME can't be blocked unless all creatures defending player controls block it.")
|
||||
|| attacker.hasKeyword("Menace")
|
||||
|| attacker.hasKeyword(Keyword.MENACE)
|
||||
|| ComputerUtilCombat.attackerHasThreateningAfflict(attacker, ai)) {
|
||||
attackers.remove(0);
|
||||
makeChumpBlocks(combat, attackers);
|
||||
@@ -657,7 +656,7 @@ public class AiBlockController {
|
||||
final Card blocker = ComputerUtilCard.getWorstCreatureAI(chumpBlockers);
|
||||
|
||||
// check if it's better to block a creature with lower power and without trample
|
||||
if (attacker.hasKeyword("Trample")) {
|
||||
if (attacker.hasKeyword(Keyword.TRAMPLE)) {
|
||||
final int damageAbsorbed = blocker.getLethalDamage();
|
||||
if (attacker.getNetCombatDamage() > damageAbsorbed) {
|
||||
for (Card other : attackers) {
|
||||
@@ -665,7 +664,7 @@ public class AiBlockController {
|
||||
continue;
|
||||
}
|
||||
if (other.getNetCombatDamage() >= damageAbsorbed
|
||||
&& !other.hasKeyword("Trample")
|
||||
&& !other.hasKeyword(Keyword.TRAMPLE)
|
||||
&& !other.hasKeyword("You may have CARDNAME assign its combat damage as though it weren't blocked.")
|
||||
&& !ComputerUtilCombat.attackerHasThreateningAfflict(other, ai)
|
||||
&& CombatUtil.canBlock(other, blocker, combat)) {
|
||||
@@ -696,7 +695,7 @@ public class AiBlockController {
|
||||
for (final Card attacker : currentAttackers) {
|
||||
|
||||
if (!attacker.hasStartOfKeyword("CantBeBlockedByAmount LT")
|
||||
&& !attacker.hasKeyword("Menace")
|
||||
&& !attacker.hasKeyword(Keyword.MENACE)
|
||||
&& !attacker.hasKeyword("CARDNAME can't be blocked unless all creatures defending player controls block it.")) {
|
||||
continue;
|
||||
}
|
||||
@@ -729,7 +728,7 @@ public class AiBlockController {
|
||||
|
||||
List<Card> chumpBlockers;
|
||||
|
||||
List<Card> tramplingAttackers = CardLists.getKeyword(attackers, "Trample");
|
||||
List<Card> tramplingAttackers = CardLists.getKeyword(attackers, Keyword.TRAMPLE);
|
||||
tramplingAttackers = CardLists.filter(tramplingAttackers, Predicates.not(rampagesOrNeedsManyToBlock));
|
||||
|
||||
// TODO - should check here for a "rampage-like" trigger that replaced
|
||||
@@ -738,7 +737,7 @@ public class AiBlockController {
|
||||
|
||||
for (final Card attacker : tramplingAttackers) {
|
||||
|
||||
if (((attacker.hasStartOfKeyword("CantBeBlockedByAmount LT") || attacker.hasKeyword("Menace")) && !combat.isBlocked(attacker))
|
||||
if (((attacker.hasStartOfKeyword("CantBeBlockedByAmount LT") || attacker.hasKeyword(Keyword.MENACE)) && !combat.isBlocked(attacker))
|
||||
|| attacker.hasKeyword("You may have CARDNAME assign its combat damage as though it weren't blocked.")
|
||||
|| attacker.hasKeyword("CARDNAME can't be blocked unless all creatures defending player controls block it.")) {
|
||||
continue;
|
||||
@@ -789,15 +788,15 @@ public class AiBlockController {
|
||||
}
|
||||
}
|
||||
// don't try to kill what can't be killed
|
||||
if (attacker.hasKeyword("indestructible") || ComputerUtil.canRegenerate(ai, attacker)) {
|
||||
if (attacker.hasKeyword(Keyword.INDESTRUCTIBLE) || ComputerUtil.canRegenerate(ai, attacker)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Try to add blockers that could be destroyed, but are worth less than the attacker
|
||||
// Don't use blockers without First Strike or Double Strike if attacker has it
|
||||
if (ComputerUtilCombat.dealsFirstStrikeDamage(attacker, false, combat)) {
|
||||
safeBlockers = CardLists.getKeyword(blockers, "First Strike");
|
||||
safeBlockers.addAll(CardLists.getKeyword(blockers, "Double Strike"));
|
||||
safeBlockers = CardLists.getKeyword(blockers, Keyword.FIRST_STRIKE);
|
||||
safeBlockers.addAll(CardLists.getKeyword(blockers, Keyword.DOUBLE_STRIKE));
|
||||
} else {
|
||||
safeBlockers = new ArrayList<>(blockers);
|
||||
}
|
||||
@@ -870,7 +869,7 @@ public class AiBlockController {
|
||||
for (final Card attacker : attackers) {
|
||||
GameEntity def = combat.getDefenderByAttacker(attacker);
|
||||
if (def instanceof Card && threatenedPWs.contains((Card) def)) {
|
||||
if (attacker.hasKeyword("Trample")) {
|
||||
if (attacker.hasKeyword(Keyword.TRAMPLE)) {
|
||||
// don't bother trying to chump a trampling creature
|
||||
continue;
|
||||
}
|
||||
@@ -1025,22 +1024,26 @@ public class AiBlockController {
|
||||
lifeInDanger = false;
|
||||
}
|
||||
// if life is still in danger
|
||||
// Reinforce blockers blocking attackers with trample if life is still
|
||||
// Reinforce blockers blocking attackers with trample if life is
|
||||
// still
|
||||
// in danger
|
||||
if (lifeInDanger && ComputerUtilCombat.lifeInDanger(ai, combat)) {
|
||||
reinforceBlockersAgainstTrample(combat);
|
||||
} else {
|
||||
lifeInDanger = false;
|
||||
}
|
||||
// Support blockers not destroying the attacker with more blockers to
|
||||
// Support blockers not destroying the attacker with more blockers
|
||||
// to
|
||||
// try to kill the attacker
|
||||
if (!lifeInDanger) {
|
||||
reinforceBlockersToKill(combat);
|
||||
}
|
||||
|
||||
// == 2. If the AI life would still be in danger make a safer approach ==
|
||||
// == 2. If the AI life would still be in danger make a safer
|
||||
// approach ==
|
||||
if (lifeInDanger && ComputerUtilCombat.lifeInDanger(ai, combat)) {
|
||||
clearBlockers(combat, possibleBlockers); // reset every block assignment
|
||||
clearBlockers(combat, possibleBlockers); // reset every block
|
||||
// assignment
|
||||
makeTradeBlocks(combat); // choose necessary trade blocks
|
||||
// if life is in danger
|
||||
makeGoodBlocks(combat);
|
||||
@@ -1061,9 +1064,11 @@ public class AiBlockController {
|
||||
reinforceBlockersToKill(combat);
|
||||
}
|
||||
|
||||
// == 3. If the AI life would be in serious danger make an even safer approach ==
|
||||
// == 3. If the AI life would be in serious danger make an even
|
||||
// safer approach ==
|
||||
if (lifeInDanger && ComputerUtilCombat.lifeInSeriousDanger(ai, combat)) {
|
||||
clearBlockers(combat, possibleBlockers); // reset every block assignment
|
||||
clearBlockers(combat, possibleBlockers); // reset every block
|
||||
// assignment
|
||||
makeChumpBlocks(combat); // choose chump blocks
|
||||
if (ComputerUtilCombat.lifeInDanger(ai, combat)) {
|
||||
makeTradeBlocks(combat); // choose necessary trade
|
||||
@@ -1078,7 +1083,8 @@ public class AiBlockController {
|
||||
reinforceBlockersAgainstTrample(combat);
|
||||
}
|
||||
makeGangBlocks(combat);
|
||||
// Support blockers not destroying the attacker with more blockers
|
||||
// Support blockers not destroying the attacker with more
|
||||
// blockers
|
||||
// to try to kill the attacker
|
||||
reinforceBlockersToKill(combat);
|
||||
}
|
||||
@@ -1167,7 +1173,8 @@ public class AiBlockController {
|
||||
* Orders a blocker that put onto the battlefield blocking. Depends heavily
|
||||
* on the implementation of orderBlockers().
|
||||
*/
|
||||
public static CardCollection orderBlocker(final Card attacker, final Card blocker, final CardCollection oldBlockers) {
|
||||
public static CardCollection orderBlocker(final Card attacker, final Card blocker,
|
||||
final CardCollection oldBlockers) {
|
||||
// add blocker to existing ordering
|
||||
// sort by evaluate, then insert it appropriately
|
||||
// relies on current implementation of orderBlockers()
|
||||
@@ -1182,17 +1189,21 @@ public class AiBlockController {
|
||||
boolean newBlockerIsAdded = false;
|
||||
// The new blocker comes right after this one
|
||||
final Card newBlockerRightAfter = (newBlockerIndex == 0 ? null : allBlockers.get(newBlockerIndex - 1));
|
||||
if (newBlockerRightAfter == null && damage >= ComputerUtilCombat.getEnoughDamageToKill(blocker, damage, attacker, true)) {
|
||||
if (newBlockerRightAfter == null
|
||||
&& damage >= ComputerUtilCombat.getEnoughDamageToKill(blocker, damage, attacker, true)) {
|
||||
result.add(blocker);
|
||||
newBlockerIsAdded = true;
|
||||
}
|
||||
// Don't bother to keep damage up-to-date after the new blocker is added, as we can't modify the order of the other cards anyway
|
||||
// Don't bother to keep damage up-to-date after the new blocker is
|
||||
// added, as we can't modify the order of the other cards anyway
|
||||
for (final Card c : oldBlockers) {
|
||||
final int lethal = ComputerUtilCombat.getEnoughDamageToKill(c, damage, attacker, true);
|
||||
damage -= lethal;
|
||||
result.add(c);
|
||||
if (!newBlockerIsAdded && c == newBlockerRightAfter && damage <= ComputerUtilCombat.getEnoughDamageToKill(blocker, damage, attacker, true)) {
|
||||
// If blocker is right after this card in priority and we have sufficient damage to kill it, add it here
|
||||
if (!newBlockerIsAdded && c == newBlockerRightAfter
|
||||
&& damage <= ComputerUtilCombat.getEnoughDamageToKill(blocker, damage, attacker, true)) {
|
||||
// If blocker is right after this card in priority and we have
|
||||
// sufficient damage to kill it, add it here
|
||||
result.add(blocker);
|
||||
newBlockerIsAdded = true;
|
||||
}
|
||||
|
||||
@@ -42,6 +42,7 @@ import forge.game.card.CardPredicates.Presets;
|
||||
import forge.game.combat.Combat;
|
||||
import forge.game.combat.CombatUtil;
|
||||
import forge.game.cost.*;
|
||||
import forge.game.keyword.Keyword;
|
||||
import forge.game.mana.ManaCostBeingPaid;
|
||||
import forge.game.phase.PhaseType;
|
||||
import forge.game.player.Player;
|
||||
@@ -159,15 +160,15 @@ public class AiController {
|
||||
if ("NonActive".equals(curse) && !player.equals(game.getPhaseHandler().getPlayerTurn())) {
|
||||
return true;
|
||||
} else if ("DestroyCreature".equals(curse) && sa.isSpell() && host.isCreature()
|
||||
&& !sa.getHostCard().hasKeyword("Indestructible")) {
|
||||
&& !sa.getHostCard().hasKeyword(Keyword.INDESTRUCTIBLE)) {
|
||||
return true;
|
||||
} else if ("CounterEnchantment".equals(curse) && sa.isSpell() && host.isEnchantment()
|
||||
&& !sa.getHostCard().hasKeyword("CARDNAME can't be countered.")) {
|
||||
&& CardFactoryUtil.isCounterable(host)) {
|
||||
return true;
|
||||
} else if ("ChaliceOfTheVoid".equals(curse) && sa.isSpell() && !host.hasKeyword("CARDNAME can't be countered.")
|
||||
} else if ("ChaliceOfTheVoid".equals(curse) && sa.isSpell() && CardFactoryUtil.isCounterable(host)
|
||||
&& host.getCMC() == c.getCounters(CounterType.CHARGE)) {
|
||||
return true;
|
||||
} else if ("BazaarOfWonders".equals(curse) && sa.isSpell() && !host.hasKeyword("CARDNAME can't be countered.")) {
|
||||
} else if ("BazaarOfWonders".equals(curse) && sa.isSpell() && CardFactoryUtil.isCounterable(host)) {
|
||||
for (Card card : game.getCardsIn(ZoneType.Battlefield)) {
|
||||
if (!card.isToken() && card.getName().equals(host.getName())) {
|
||||
return true;
|
||||
@@ -793,7 +794,7 @@ public class AiController {
|
||||
}
|
||||
}
|
||||
// if the profile specifies it, deprioritize Storm spells in an attempt to build up storm count
|
||||
if (source.hasKeyword("Storm") && ai.getController() instanceof PlayerControllerAi) {
|
||||
if (source.hasKeyword(Keyword.STORM) && ai.getController() instanceof PlayerControllerAi) {
|
||||
p -= (((PlayerControllerAi) ai.getController()).getAi().getIntProperty(AiProps.PRIORITY_REDUCTION_FOR_STORM_SPELLS));
|
||||
}
|
||||
}
|
||||
@@ -1335,7 +1336,7 @@ public class AiController {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (sa.getHostCard().hasKeyword("Storm")
|
||||
if (sa.getHostCard().hasKeyword(Keyword.STORM)
|
||||
&& sa.getApi() != ApiType.Counter // AI would suck at trying to deliberately proc a Storm counterspell
|
||||
&& CardLists.filter(player.getCardsIn(ZoneType.Hand), Predicates.not(Predicates.or(CardPredicates.Presets.LANDS, CardPredicates.hasKeyword("Storm")))).size() > 0) {
|
||||
if (game.getView().getStormCount() < this.getIntProperty(AiProps.MIN_COUNT_FOR_STORM_SPELLS)) {
|
||||
@@ -1563,11 +1564,11 @@ public class AiController {
|
||||
// and exaclty one counter of the specifice type gets high priority to keep the card
|
||||
if (allies.contains(crd.getController())) {
|
||||
// except if its a Chronozoa, because it WANTS to be removed to make more
|
||||
if (crd.hasKeyword("Vanishing") && !"Chronozoa".equals(crd.getName())) {
|
||||
if (crd.hasKeyword(Keyword.VANISHING) && !"Chronozoa".equals(crd.getName())) {
|
||||
if (crd.getCounters(CounterType.TIME) == 1) {
|
||||
return CounterType.TIME;
|
||||
}
|
||||
} else if (crd.hasKeyword("Fading")) {
|
||||
} else if (crd.hasKeyword(Keyword.FADING)) {
|
||||
if (crd.getCounters(CounterType.FADE) == 1) {
|
||||
return CounterType.FADE;
|
||||
}
|
||||
|
||||
@@ -37,6 +37,7 @@ import forge.game.card.CardPredicates.Presets;
|
||||
import forge.game.combat.Combat;
|
||||
import forge.game.combat.CombatUtil;
|
||||
import forge.game.cost.*;
|
||||
import forge.game.keyword.Keyword;
|
||||
import forge.game.phase.PhaseHandler;
|
||||
import forge.game.phase.PhaseType;
|
||||
import forge.game.player.Player;
|
||||
@@ -789,7 +790,7 @@ public class ComputerUtil {
|
||||
}
|
||||
|
||||
if (destroy) {
|
||||
final CardCollection indestructibles = CardLists.getKeyword(remaining, "Indestructible");
|
||||
final CardCollection indestructibles = CardLists.getKeyword(remaining, Keyword.INDESTRUCTIBLE);
|
||||
if (!indestructibles.isEmpty()) {
|
||||
return indestructibles.get(0);
|
||||
}
|
||||
@@ -826,7 +827,7 @@ public class ComputerUtil {
|
||||
}
|
||||
|
||||
public static boolean canRegenerate(Player ai, final Card card) {
|
||||
if (card.hasKeyword("CARDNAME can't be regenerated.")) {
|
||||
if (!card.canBeShielded()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -939,7 +940,7 @@ public class ComputerUtil {
|
||||
}
|
||||
|
||||
// try not to cast Raid creatures in main 1 if an attack is likely
|
||||
if ("Count$AttackersDeclared".equals(card.getSVar("RaidTest")) && !card.hasKeyword("Haste")) {
|
||||
if ("Count$AttackersDeclared".equals(card.getSVar("RaidTest")) && !card.hasKeyword(Keyword.HASTE)) {
|
||||
for (Card potentialAtkr: ai.getCreaturesInPlay()) {
|
||||
if (ComputerUtilCard.doesCreatureAttackAI(ai, potentialAtkr)) {
|
||||
return false;
|
||||
@@ -951,12 +952,12 @@ public class ComputerUtil {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (card.isCreature() && !card.hasKeyword("Defender")
|
||||
&& (card.hasKeyword("Haste") || ComputerUtil.hasACardGivingHaste(ai, true) || sa.isDash())) {
|
||||
if (card.isCreature() && !card.hasKeyword(Keyword.DEFENDER)
|
||||
&& (card.hasKeyword(Keyword.HASTE) || ComputerUtil.hasACardGivingHaste(ai, true) || sa.isDash())) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (card.hasKeyword("Exalted")) {
|
||||
if (card.hasKeyword(Keyword.EXALTED)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -991,16 +992,16 @@ public class ComputerUtil {
|
||||
return true;
|
||||
}
|
||||
if (card.isCreature()) {
|
||||
if (buffedcard.hasKeyword("Soulbond") && !buffedcard.isPaired()) {
|
||||
if (buffedcard.hasKeyword(Keyword.SOULBOND) && !buffedcard.isPaired()) {
|
||||
return true;
|
||||
}
|
||||
if (buffedcard.hasKeyword("Evolve")) {
|
||||
if (buffedcard.hasKeyword(Keyword.EVOLVE)) {
|
||||
if (buffedcard.getNetPower() < card.getNetPower() || buffedcard.getNetToughness() < card.getNetToughness()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (card.hasKeyword("Soulbond") && buffedcard.isCreature() && !buffedcard.isPaired()) {
|
||||
if (card.hasKeyword(Keyword.SOULBOND) && buffedcard.isCreature() && !buffedcard.isPaired()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1248,7 +1249,7 @@ public class ComputerUtil {
|
||||
|
||||
// Special for Odric
|
||||
if (ai.isCardInPlay("Odric, Lunarch Marshal")
|
||||
&& !CardLists.getKeyword(all, "Haste").isEmpty()) {
|
||||
&& !CardLists.getKeyword(all, Keyword.HASTE).isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1317,7 +1318,7 @@ public class ComputerUtil {
|
||||
if ("Continuous".equals(params.get("Mode")) && params.containsKey("AddKeyword")
|
||||
&& params.get("AddKeyword").contains("Haste")) {
|
||||
|
||||
final ArrayList affected = Lists.newArrayList(params.get("Affected").split(","));
|
||||
final ArrayList<String> affected = Lists.newArrayList(params.get("Affected").split(","));
|
||||
if (affected.contains("Creature")) {
|
||||
return true;
|
||||
}
|
||||
@@ -1547,7 +1548,7 @@ public class ComputerUtil {
|
||||
final Card c = (Card) o;
|
||||
|
||||
// indestructible
|
||||
if (c.hasKeyword("Indestructible")) {
|
||||
if (c.hasKeyword(Keyword.INDESTRUCTIBLE)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -1594,7 +1595,7 @@ public class ComputerUtil {
|
||||
} else if (o instanceof Player) {
|
||||
final Player p = (Player) o;
|
||||
|
||||
if (source.hasKeyword("Infect")) {
|
||||
if (source.hasKeyword(Keyword.INFECT)) {
|
||||
if (ComputerUtilCombat.predictDamageTo(p, dmg, source, false) >= p.getPoisonCounters()) {
|
||||
threatened.add(p);
|
||||
}
|
||||
@@ -1615,14 +1616,14 @@ public class ComputerUtil {
|
||||
if (o instanceof Card) {
|
||||
final Card c = (Card) o;
|
||||
final boolean canRemove = (c.getNetToughness() <= dmg)
|
||||
|| (!c.hasKeyword("Indestructible") && c.getShieldCount() == 0 && (dmg >= ComputerUtilCombat.getDamageToKill(c)));
|
||||
|| (!c.hasKeyword(Keyword.INDESTRUCTIBLE) && c.getShieldCount() == 0 && (dmg >= ComputerUtilCombat.getDamageToKill(c)));
|
||||
if (!canRemove) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (saviourApi == ApiType.Pump || saviourApi == ApiType.PumpAll) {
|
||||
final boolean cantSave = c.getNetToughness() + toughness <= dmg
|
||||
|| (!c.hasKeyword("Indestructible") && c.getShieldCount() == 0 && !grantIndestructible
|
||||
|| (!c.hasKeyword(Keyword.INDESTRUCTIBLE) && c.getShieldCount() == 0 && !grantIndestructible
|
||||
&& (dmg >= toughness + ComputerUtilCombat.getDamageToKill(c)));
|
||||
if (cantSave && (tgt == null || !grantShroud)) {
|
||||
continue;
|
||||
@@ -1662,7 +1663,7 @@ public class ComputerUtil {
|
||||
if (o instanceof Card) {
|
||||
final Card c = (Card) o;
|
||||
// indestructible
|
||||
if (c.hasKeyword("Indestructible")) {
|
||||
if (c.hasKeyword(Keyword.INDESTRUCTIBLE)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
@@ -1081,7 +1081,7 @@ public class ComputerUtilCard {
|
||||
valueTempo *= 2; //deal with annoying things
|
||||
}
|
||||
if (!destination.equals(ZoneType.Graveyard) && //TODO:boat-load of "when blah dies" triggers
|
||||
c.hasKeyword("Persist") || c.hasKeyword("Undying") || c.hasKeyword("Modular")) {
|
||||
c.hasKeyword(Keyword.PERSIST) || c.hasKeyword(Keyword.UNDYING) || c.hasKeyword(Keyword.MODULAR)) {
|
||||
valueTempo *= 2;
|
||||
}
|
||||
if (destination.equals(ZoneType.Hand) && !c.isToken()) {
|
||||
@@ -1358,8 +1358,9 @@ public class ComputerUtilCard {
|
||||
|
||||
//1. save combatant
|
||||
if (ComputerUtilCombat.combatantWouldBeDestroyed(ai, c, combat) && !pumpedWillDie
|
||||
&& !c.hasKeyword("Indestructible")) { // hack because attackerWouldBeDestroyed() does not
|
||||
// check for Indestructible when computing lethal damage
|
||||
&& !c.hasKeyword(Keyword.INDESTRUCTIBLE)) {
|
||||
// hack because attackerWouldBeDestroyed()
|
||||
// does not check for Indestructible when computing lethal damage
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1396,17 +1397,17 @@ public class ComputerUtilCard {
|
||||
int poisonPumped = opp.canReceiveCounters(CounterType.POISON) ? ComputerUtilCombat.poisonIfUnblocked(pumped, ai) : 0;
|
||||
|
||||
// predict Infect
|
||||
if (pumpedDmg == 0 && c.hasKeyword("Infect")) {
|
||||
if (pumpedDmg == 0 && c.hasKeyword(Keyword.INFECT)) {
|
||||
if (poisonPumped > poisonOrig) {
|
||||
pumpedDmg = poisonPumped;
|
||||
}
|
||||
}
|
||||
|
||||
if (combat.isBlocked(c)) {
|
||||
if (!c.hasKeyword("Trample")) {
|
||||
if (!c.hasKeyword(Keyword.TRAMPLE)) {
|
||||
dmg = 0;
|
||||
}
|
||||
if (c.hasKeyword("Trample") || keywords.contains("Trample")) {
|
||||
if (c.hasKeyword(Keyword.TRAMPLE) || keywords.contains("Trample")) {
|
||||
for (Card b : combat.getBlockers(c)) {
|
||||
pumpedDmg -= ComputerUtilCombat.getDamageToKill(b);
|
||||
}
|
||||
@@ -1415,8 +1416,8 @@ public class ComputerUtilCard {
|
||||
}
|
||||
}
|
||||
if (pumpedDmg > dmg) {
|
||||
if ((!c.hasKeyword("Infect") && pumpedDmg >= opp.getLife())
|
||||
|| (c.hasKeyword("Infect") && opp.canReceiveCounters(CounterType.POISON) && pumpedDmg >= opp.getPoisonCounters())) {
|
||||
if ((!c.hasKeyword(Keyword.INFECT) && pumpedDmg >= opp.getLife())
|
||||
|| (c.hasKeyword(Keyword.INFECT) && opp.canReceiveCounters(CounterType.POISON) && pumpedDmg >= opp.getPoisonCounters())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -1425,7 +1426,7 @@ public class ComputerUtilCard {
|
||||
if (phase.is(PhaseType.COMBAT_DECLARE_BLOCKERS) && pumpedDmg > dmg) {
|
||||
int totalPowerUnblocked = 0;
|
||||
for (Card atk : combat.getAttackers()) {
|
||||
if (combat.isBlocked(atk) && !atk.hasKeyword("Trample")) {
|
||||
if (combat.isBlocked(atk) && !atk.hasKeyword(Keyword.TRAMPLE)) {
|
||||
continue;
|
||||
}
|
||||
if (atk == c) {
|
||||
@@ -1458,7 +1459,7 @@ public class ComputerUtilCard {
|
||||
}
|
||||
|
||||
//4. lifelink
|
||||
if (ai.canGainLife() && ai.getLife() > 0 && !c.hasKeyword("Lifelink") && keywords.contains("Lifelink")
|
||||
if (ai.canGainLife() && ai.getLife() > 0 && !c.hasKeyword(Keyword.LIFELINK) && keywords.contains("Lifelink")
|
||||
&& (combat.isAttacking(c) || combat.isBlocking(c))) {
|
||||
int dmg = pumped.getNetCombatDamage();
|
||||
//The actual dmg inflicted should be the sum of ComputerUtilCombat.predictDamageTo() for opposing creature
|
||||
@@ -1471,7 +1472,7 @@ public class ComputerUtilCard {
|
||||
List<Card> blockedBy = combat.getAttackersBlockedBy(c);
|
||||
boolean attackerHasTrample = false;
|
||||
for (Card b : blockedBy) {
|
||||
attackerHasTrample |= b.hasKeyword("Trample");
|
||||
attackerHasTrample |= b.hasKeyword(Keyword.TRAMPLE);
|
||||
}
|
||||
if (attackerHasTrample && (sa.isAbility() || ComputerUtilCombat.lifeInDanger(ai, combat))) {
|
||||
return true;
|
||||
@@ -1713,10 +1714,10 @@ public class ComputerUtilCard {
|
||||
}
|
||||
|
||||
public static boolean hasActiveUndyingOrPersist(final Card c) {
|
||||
if (c.hasKeyword("Undying") && c.getCounters(CounterType.P1P1) == 0) {
|
||||
if (c.hasKeyword(Keyword.UNDYING) && c.getCounters(CounterType.P1P1) == 0) {
|
||||
return true;
|
||||
}
|
||||
if (c.hasKeyword("Persist") && c.getCounters(CounterType.M1M1) == 0) {
|
||||
if (c.hasKeyword(Keyword.PERSIST) && c.getCounters(CounterType.M1M1) == 0) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
@@ -202,10 +202,10 @@ public class ComputerUtilCombat {
|
||||
}
|
||||
|
||||
damage += ComputerUtilCombat.predictPowerBonusOfAttacker(attacker, null, combat, withoutAbilities);
|
||||
if (!attacker.hasKeyword("Infect")) {
|
||||
if (!attacker.hasKeyword(Keyword.INFECT)) {
|
||||
sum = ComputerUtilCombat.predictDamageTo(attacked, damage, attacker, true);
|
||||
if (attacker.hasKeyword("Double Strike")) {
|
||||
sum += ComputerUtilCombat.predictDamageTo(attacked, damage, attacker, true);
|
||||
if (attacker.hasKeyword(Keyword.DOUBLE_STRIKE)) {
|
||||
sum *= 2;
|
||||
}
|
||||
}
|
||||
return sum;
|
||||
@@ -303,9 +303,9 @@ public class ComputerUtilCombat {
|
||||
|| attacker.hasKeyword("You may have CARDNAME assign its combat damage "
|
||||
+ "as though it weren't blocked.")) {
|
||||
unblocked.add(attacker);
|
||||
} else if (attacker.hasKeyword("Trample")
|
||||
} else if (attacker.hasKeyword(Keyword.TRAMPLE)
|
||||
&& (ComputerUtilCombat.getAttack(attacker) > ComputerUtilCombat.totalShieldDamage(attacker, blockers))) {
|
||||
if (!attacker.hasKeyword("Infect")) {
|
||||
if (!attacker.hasKeyword(Keyword.INFECT)) {
|
||||
damage += ComputerUtilCombat.getAttack(attacker) - ComputerUtilCombat.totalShieldDamage(attacker, blockers);
|
||||
}
|
||||
}
|
||||
@@ -332,6 +332,11 @@ public class ComputerUtilCombat {
|
||||
*/
|
||||
public static int resultingPoison(final Player ai, final Combat combat) {
|
||||
|
||||
// ai can't get poision counters, so the value can't change
|
||||
if (!ai.canReceiveCounters(CounterType.POISON)) {
|
||||
return ai.getPoisonCounters();
|
||||
}
|
||||
|
||||
int poison = 0;
|
||||
|
||||
final List<Card> attackers = combat.getAttackersOf(ai);
|
||||
@@ -578,7 +583,7 @@ public class ComputerUtilCombat {
|
||||
|
||||
int defenderDamage = predictDamageByBlockerWithoutDoubleStrike(attacker, defender);
|
||||
|
||||
if (defender.hasKeyword("Double Strike")) {
|
||||
if (defender.hasKeyword(Keyword.DOUBLE_STRIKE)) {
|
||||
defenderDamage += predictDamageTo(attacker, defenderDamage, defender, true);
|
||||
}
|
||||
|
||||
@@ -592,7 +597,7 @@ public class ComputerUtilCombat {
|
||||
* @return
|
||||
*/
|
||||
private static int predictDamageByBlockerWithoutDoubleStrike(final Card attacker, final Card defender) {
|
||||
if (attacker.getName().equals("Sylvan Basilisk") && !defender.hasKeyword("Indestructible")) {
|
||||
if (attacker.getName().equals("Sylvan Basilisk") && !defender.hasKeyword(Keyword.INDESTRUCTIBLE)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -923,9 +928,9 @@ public class ComputerUtilCombat {
|
||||
// if the attacker has first strike and wither the blocker will deal
|
||||
// less damage than expected
|
||||
if (dealsFirstStrikeDamage(attacker, withoutAbilities, null)
|
||||
&& (attacker.hasKeyword("Wither") || attacker.hasKeyword("Infect"))
|
||||
&& (attacker.hasKeyword(Keyword.WITHER) || attacker.hasKeyword(Keyword.INFECT))
|
||||
&& !dealsFirstStrikeDamage(blocker, withoutAbilities, null)
|
||||
&& !blocker.hasKeyword("CARDNAME can't have counters put on it.")) {
|
||||
&& !blocker.canReceiveCounters(CounterType.M1M1)) {
|
||||
power -= attacker.getNetCombatDamage();
|
||||
}
|
||||
|
||||
@@ -1266,7 +1271,7 @@ public class ComputerUtilCombat {
|
||||
if (ComputerUtilCombat.dealsFirstStrikeDamage(blocker, withoutAbilities, combat)
|
||||
&& (blocker.hasKeyword(Keyword.WITHER) || blocker.hasKeyword(Keyword.INFECT))
|
||||
&& !ComputerUtilCombat.dealsFirstStrikeDamage(attacker, withoutAbilities, combat)
|
||||
&& !attacker.hasKeyword("CARDNAME can't have counters put on it.")) {
|
||||
&& !attacker.canReceiveCounters(CounterType.M1M1)) {
|
||||
power -= blocker.getNetCombatDamage();
|
||||
}
|
||||
theTriggers.addAll(blocker.getTriggers());
|
||||
@@ -1493,7 +1498,7 @@ public class ComputerUtilCombat {
|
||||
} else if (params.containsKey("Affected") && params.get("Affected").contains("untapped")) {
|
||||
final String valid = TextUtil.fastReplace(params.get("Affected"), "untapped", "Creature");
|
||||
if (!attacker.isValid(valid, card.getController(), card, null)
|
||||
|| attacker.hasKeyword("Vigilance")) {
|
||||
|| attacker.hasKeyword(Keyword.VIGILANCE)) {
|
||||
continue;
|
||||
}
|
||||
// remove the bonus, because it will no longer be granted
|
||||
@@ -1646,7 +1651,7 @@ public class ComputerUtilCombat {
|
||||
if (blocker.isEquippedBy("Godsend")) {
|
||||
return true;
|
||||
}
|
||||
if (attacker.hasKeyword("Indestructible") || ComputerUtil.canRegenerate(attacker.getController(), attacker)) {
|
||||
if (attacker.hasKeyword(Keyword.INDESTRUCTIBLE) || ComputerUtil.canRegenerate(attacker.getController(), attacker)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1711,12 +1716,12 @@ public class ComputerUtilCombat {
|
||||
*/
|
||||
public static boolean attackerCantBeDestroyedInCombat(Player ai, final Card attacker) {
|
||||
// attacker is either indestructible or may regenerate
|
||||
if (attacker.hasKeyword("Indestructible") || (ComputerUtil.canRegenerate(ai, attacker))) {
|
||||
if (attacker.hasKeyword(Keyword.INDESTRUCTIBLE) || (ComputerUtil.canRegenerate(ai, attacker))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// attacker will regenerate
|
||||
if (attacker.getShieldCount() > 0 && !attacker.hasKeyword("CARDNAME can't be regenerated.")) {
|
||||
if (attacker.getShieldCount() > 0 && attacker.canBeShielded()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1829,7 +1834,7 @@ public class ComputerUtilCombat {
|
||||
final int attackerLife = ComputerUtilCombat.getDamageToKill(attacker)
|
||||
+ ComputerUtilCombat.predictToughnessBonusOfAttacker(attacker, blocker, combat, withoutAbilities, withoutAttackerStaticAbilities);
|
||||
|
||||
if (blocker.hasKeyword("Double Strike")) {
|
||||
if (blocker.hasKeyword(Keyword.DOUBLE_STRIKE)) {
|
||||
if (defenderDamage > 0 && (hasKeyword(blocker, "Deathtouch", withoutAbilities, combat) || attacker.hasSVar("DestroyWhenDamaged"))) {
|
||||
return true;
|
||||
}
|
||||
@@ -1839,7 +1844,8 @@ public class ComputerUtilCombat {
|
||||
|
||||
// Attacker may kill the blocker before he can deal normal
|
||||
// (secondary) damage
|
||||
if (dealsFirstStrikeDamage(attacker, withoutAbilities, combat) && !blocker.hasKeyword("Indestructible")) {
|
||||
if (dealsFirstStrikeDamage(attacker, withoutAbilities, combat)
|
||||
&& !blocker.hasKeyword(Keyword.INDESTRUCTIBLE)) {
|
||||
if (attackerDamage >= defenderLife) {
|
||||
return false;
|
||||
}
|
||||
@@ -1855,7 +1861,7 @@ public class ComputerUtilCombat {
|
||||
else { // no double strike for defender
|
||||
// Attacker may kill the blocker before he can deal any damage
|
||||
if (dealsFirstStrikeDamage(attacker, withoutAbilities, combat)
|
||||
&& !blocker.hasKeyword("Indestructible")
|
||||
&& !blocker.hasKeyword(Keyword.INDESTRUCTIBLE)
|
||||
&& !dealsFirstStrikeDamage(blocker, withoutAbilities, combat)) {
|
||||
|
||||
if (attackerDamage >= defenderLife) {
|
||||
@@ -2011,11 +2017,11 @@ public class ComputerUtilCombat {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (((blocker.hasKeyword("Indestructible") || (ComputerUtil.canRegenerate(ai, blocker) && !withoutAbilities)) && !(attacker
|
||||
.hasKeyword("Wither") || attacker.hasKeyword("Infect")))
|
||||
|| (blocker.hasKeyword("Persist") && !blocker.canReceiveCounters(CounterType.M1M1) && (blocker
|
||||
if (((blocker.hasKeyword(Keyword.INDESTRUCTIBLE) || (ComputerUtil.canRegenerate(ai, blocker) && !withoutAbilities)) && !(attacker
|
||||
.hasKeyword(Keyword.WITHER) || attacker.hasKeyword(Keyword.INFECT)))
|
||||
|| (blocker.hasKeyword(Keyword.PERSIST) && !blocker.canReceiveCounters(CounterType.M1M1) && (blocker
|
||||
.getCounters(CounterType.M1M1) == 0))
|
||||
|| (blocker.hasKeyword("Undying") && !blocker.canReceiveCounters(CounterType.P1P1) && (blocker
|
||||
|| (blocker.hasKeyword(Keyword.UNDYING) && !blocker.canReceiveCounters(CounterType.P1P1) && (blocker
|
||||
.getCounters(CounterType.P1P1) == 0))) {
|
||||
return false;
|
||||
}
|
||||
@@ -2065,7 +2071,7 @@ public class ComputerUtilCombat {
|
||||
final int attackerLife = ComputerUtilCombat.getDamageToKill(attacker)
|
||||
+ ComputerUtilCombat.predictToughnessBonusOfAttacker(attacker, blocker, combat, withoutAbilities, withoutAttackerStaticAbilities);
|
||||
|
||||
if (attacker.hasKeyword("Double Strike")) {
|
||||
if (attacker.hasKeyword(Keyword.DOUBLE_STRIKE)) {
|
||||
if (attackerDamage > 0 && (hasKeyword(attacker, "Deathtouch", withoutAbilities, combat) || blocker.hasSVar("DestroyWhenDamaged"))) {
|
||||
return true;
|
||||
}
|
||||
@@ -2075,7 +2081,8 @@ public class ComputerUtilCombat {
|
||||
|
||||
// Attacker may kill the blocker before he can deal normal
|
||||
// (secondary) damage
|
||||
if (dealsFirstStrikeDamage(blocker, withoutAbilities, combat) && !attacker.hasKeyword("Indestructible")) {
|
||||
if (dealsFirstStrikeDamage(blocker, withoutAbilities, combat)
|
||||
&& !attacker.hasKeyword(Keyword.INDESTRUCTIBLE)) {
|
||||
if (defenderDamage >= attackerLife) {
|
||||
return false;
|
||||
}
|
||||
@@ -2090,7 +2097,8 @@ public class ComputerUtilCombat {
|
||||
|
||||
else { // no double strike for attacker
|
||||
// Defender may kill the attacker before he can deal any damage
|
||||
if (dealsFirstStrikeDamage(blocker, withoutAbilities, combat) && !attacker.hasKeyword("Indestructible")
|
||||
if (dealsFirstStrikeDamage(blocker, withoutAbilities, combat)
|
||||
&& !attacker.hasKeyword(Keyword.INDESTRUCTIBLE)
|
||||
&& !dealsFirstStrikeDamage(attacker, withoutAbilities, combat)) {
|
||||
|
||||
if (defenderDamage >= attackerLife) {
|
||||
@@ -2137,7 +2145,7 @@ public class ComputerUtilCombat {
|
||||
return damageMap;
|
||||
}
|
||||
|
||||
final boolean hasTrample = attacker.hasKeyword("Trample");
|
||||
final boolean hasTrample = attacker.hasKeyword(Keyword.TRAMPLE);
|
||||
|
||||
if (block.size() == 1) {
|
||||
final Card blocker = block.getFirst();
|
||||
@@ -2239,11 +2247,11 @@ public class ComputerUtilCombat {
|
||||
final boolean noPrevention) {
|
||||
final int killDamage = c.isPlaneswalker() ? c.getCurrentLoyalty() : ComputerUtilCombat.getDamageToKill(c);
|
||||
|
||||
if (c.hasKeyword("Indestructible") || c.getShieldCount() > 0) {
|
||||
if (!(source.hasKeyword("Wither") || source.hasKeyword("Infect"))) {
|
||||
if (c.hasKeyword(Keyword.INDESTRUCTIBLE) || c.getShieldCount() > 0) {
|
||||
if (!(source.hasKeyword(Keyword.WITHER) || source.hasKeyword(Keyword.INFECT))) {
|
||||
return maxDamage + 1;
|
||||
}
|
||||
} else if (source.hasKeyword("Deathtouch")) {
|
||||
} else if (source.hasKeyword(Keyword.DEATHTOUCH)) {
|
||||
for (int i = 1; i <= maxDamage; i++) {
|
||||
if (noPrevention) {
|
||||
if (c.staticReplaceDamage(i, source, isCombat) > 0) {
|
||||
@@ -2406,7 +2414,7 @@ public class ComputerUtilCombat {
|
||||
|
||||
public final static boolean dealsFirstStrikeDamage(final Card combatant, final boolean withoutAbilities, final Combat combat) {
|
||||
|
||||
if (combatant.hasKeyword("Double Strike") || combatant.hasKeyword("First Strike")) {
|
||||
if (combatant.hasKeyword(Keyword.DOUBLE_STRIKE) || combatant.hasKeyword(Keyword.FIRST_STRIKE)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -2542,18 +2550,10 @@ public class ComputerUtilCombat {
|
||||
CardCollection withoutEvasion = new CardCollection();
|
||||
|
||||
for (Card atk : attackers) {
|
||||
boolean hasProtection = false;
|
||||
for (KeywordInterface inst : atk.getKeywords()) {
|
||||
String kw = inst.getOriginal();
|
||||
if (kw.startsWith("Protection")) {
|
||||
hasProtection = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (atk.hasKeyword("Flying") || atk.hasKeyword("Shadow")
|
||||
|| atk.hasKeyword("Horsemanship") || (atk.hasKeyword("Fear")
|
||||
|| atk.hasKeyword("Intimidate") || atk.hasKeyword("Skulk") || hasProtection)) {
|
||||
if (atk.hasKeyword(Keyword.FLYING) || atk.hasKeyword(Keyword.SHADOW)
|
||||
|| atk.hasKeyword(Keyword.HORSEMANSHIP) || (atk.hasKeyword(Keyword.FEAR)
|
||||
|| atk.hasKeyword(Keyword.INTIMIDATE) || atk.hasKeyword(Keyword.SKULK)
|
||||
|| atk.hasKeyword(Keyword.PROTECTION))) {
|
||||
withEvasion.add(atk);
|
||||
} else {
|
||||
withoutEvasion.add(atk);
|
||||
|
||||
@@ -9,6 +9,7 @@ import forge.game.GameActionUtil;
|
||||
import forge.game.ability.AbilityUtils;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.card.CardCollection;
|
||||
import forge.game.card.CardFactoryUtil;
|
||||
import forge.game.card.CardLists;
|
||||
import forge.game.card.CardPredicates.Presets;
|
||||
import forge.game.card.CounterType;
|
||||
@@ -446,7 +447,7 @@ public class ComputerUtilCost {
|
||||
// Check for stuff like Nether Void
|
||||
int extraManaNeeded = 0;
|
||||
if (sa instanceof Spell) {
|
||||
final boolean cannotBeCountered = sa.getHostCard().hasKeyword("CARDNAME can't be countered.");
|
||||
final boolean cannotBeCountered = CardFactoryUtil.isCounterable(sa.getHostCard());
|
||||
for (Card c : player.getGame().getCardsIn(ZoneType.Battlefield)) {
|
||||
final String snem = c.getSVar("AI_SpellsNeedExtraMana");
|
||||
if (!StringUtils.isBlank(snem)) {
|
||||
|
||||
@@ -84,7 +84,7 @@ public class CreatureEvaluator implements Function<Card, Integer> {
|
||||
if (power > 0) {
|
||||
if (c.hasKeyword(Keyword.DOUBLE_STRIKE)) {
|
||||
value += addValue(10 + (power * 15), "ds");
|
||||
} else if (c.hasKeyword("First Strike")) {
|
||||
} else if (c.hasKeyword(Keyword.FIRST_STRIKE)) {
|
||||
value += addValue(10 + (power * 5), "fs");
|
||||
}
|
||||
if (c.hasKeyword(Keyword.DEATHTOUCH)) {
|
||||
@@ -116,7 +116,7 @@ public class CreatureEvaluator implements Function<Card, Integer> {
|
||||
value += addValue(c.getKeywordMagnitude(Keyword.ABSORB) * 11, "absorb");
|
||||
|
||||
// Keywords that may produce temporary or permanent buffs over time
|
||||
if (c.hasKeyword("Prowess")) {
|
||||
if (c.hasKeyword(Keyword.PROWESS)) {
|
||||
value += addValue(5, "prowess");
|
||||
}
|
||||
if (c.hasKeyword(Keyword.OUTLAST)) {
|
||||
@@ -145,7 +145,7 @@ public class CreatureEvaluator implements Function<Card, Integer> {
|
||||
} else if (c.hasKeyword(Keyword.SHROUD)) {
|
||||
value += addValue(30, "shroud");
|
||||
}
|
||||
if (c.hasStartOfKeyword("Protection")) {
|
||||
if (c.hasKeyword(Keyword.PROTECTION)) {
|
||||
value += addValue(20, "protection");
|
||||
}
|
||||
|
||||
@@ -186,7 +186,7 @@ public class CreatureEvaluator implements Function<Card, Integer> {
|
||||
value -= subValue(30, "cupkeep");
|
||||
} else if (c.hasStartOfKeyword("UpkeepCost")) {
|
||||
value -= subValue(20, "sac-unless");
|
||||
} else if (c.hasStartOfKeyword("Echo") && c.cameUnderControlSinceLastUpkeep()) {
|
||||
} else if (c.hasKeyword(Keyword.ECHO) && c.cameUnderControlSinceLastUpkeep()) {
|
||||
value -= subValue(10, "echo-unpaid");
|
||||
}
|
||||
|
||||
|
||||
@@ -33,6 +33,7 @@ import forge.game.card.*;
|
||||
import forge.game.combat.Combat;
|
||||
import forge.game.combat.CombatUtil;
|
||||
import forge.game.cost.CostPart;
|
||||
import forge.game.keyword.Keyword;
|
||||
import forge.game.mana.ManaCostBeingPaid;
|
||||
import forge.game.phase.PhaseHandler;
|
||||
import forge.game.phase.PhaseType;
|
||||
@@ -206,7 +207,9 @@ public class SpecialCardAi {
|
||||
private static final int demonSacThreshold = Integer.MAX_VALUE; // if we're in dire conditions, sac everything from worst to best hoping to find an answer
|
||||
|
||||
public static boolean considerSacrificingCreature(final Player ai, final SpellAbility sa) {
|
||||
CardCollection flyingCreatures = CardLists.filter(ai.getCardsIn(ZoneType.Battlefield), Predicates.and(CardPredicates.Presets.UNTAPPED, Predicates.or(CardPredicates.hasKeyword("Flying"), CardPredicates.hasKeyword("Reach"))));
|
||||
CardCollection flyingCreatures = CardLists.filter(ai.getCardsIn(ZoneType.Battlefield),
|
||||
Predicates.and(CardPredicates.Presets.UNTAPPED, Predicates.or(
|
||||
CardPredicates.hasKeyword(Keyword.FLYING), CardPredicates.hasKeyword(Keyword.REACH))));
|
||||
boolean hasUsefulBlocker = false;
|
||||
|
||||
for (Card c : flyingCreatures) {
|
||||
@@ -329,7 +332,7 @@ public class SpecialCardAi {
|
||||
boolean oppHasFirstStrike = false;
|
||||
boolean oppCantDie = true;
|
||||
boolean unblocked = opposition.isEmpty();
|
||||
boolean canTrample = source.hasKeyword("Trample");
|
||||
boolean canTrample = source.hasKeyword(Keyword.TRAMPLE);
|
||||
|
||||
if (!isBlocking && combat.getDefenderByAttacker(source) instanceof Card) {
|
||||
int loyalty = ((Card)combat.getDefenderByAttacker(source)).getCounters(CounterType.LOYALTY);
|
||||
@@ -351,7 +354,7 @@ public class SpecialCardAi {
|
||||
}
|
||||
|
||||
for (Card c : opposition) {
|
||||
if (c.hasKeyword("First Strike") || c.hasKeyword("Double Strike")) {
|
||||
if (c.hasKeyword(Keyword.FIRST_STRIKE) || c.hasKeyword(Keyword.DOUBLE_STRIKE)) {
|
||||
oppHasFirstStrike = true;
|
||||
}
|
||||
if (!ComputerUtilCombat.attackerCantBeDestroyedInCombat(c.getController(), c)) {
|
||||
@@ -382,8 +385,8 @@ public class SpecialCardAi {
|
||||
// Already enough to kill the blockers and survive, don't overpump
|
||||
return false;
|
||||
}
|
||||
if (oppCantDie && !source.hasKeyword("Trample") && !source.hasKeyword("Wither")
|
||||
&& !source.hasKeyword("Infect") && predictedPT.getLeft() <= oppT) {
|
||||
if (oppCantDie && !source.hasKeyword(Keyword.TRAMPLE) && !source.hasKeyword(Keyword.WITHER)
|
||||
&& !source.hasKeyword(Keyword.INFECT) && predictedPT.getLeft() <= oppT) {
|
||||
// Can't kill or cripple anyone, as well as can't Trample over, so don't pump
|
||||
return false;
|
||||
}
|
||||
@@ -400,7 +403,7 @@ public class SpecialCardAi {
|
||||
CardCollection potentialBlockers = new CardCollection();
|
||||
|
||||
for (Card b : oppInPlay) {
|
||||
if (CombatUtil.canBlock(sa.getHostCard(), b)) {
|
||||
if (CombatUtil.canBlock(source, b)) {
|
||||
potentialBlockers.add(b);
|
||||
}
|
||||
}
|
||||
@@ -408,7 +411,7 @@ public class SpecialCardAi {
|
||||
Pair<Integer, Integer> predictedPT = getPumpedPT(ai, source.getNetCombatDamage(), source.getNetToughness());
|
||||
int oppT = Aggregates.sum(potentialBlockers, CardPredicates.Accessors.fnGetNetToughness);
|
||||
|
||||
if (potentialBlockers.isEmpty() || (sa.getHostCard().hasKeyword("Trample") && predictedPT.getLeft() - oppT >= oppLife)) {
|
||||
if (potentialBlockers.isEmpty() || (source.hasKeyword(Keyword.TRAMPLE) && predictedPT.getLeft() - oppT >= oppLife)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ import forge.game.combat.CombatUtil;
|
||||
import forge.game.cost.Cost;
|
||||
import forge.game.cost.CostPart;
|
||||
import forge.game.cost.CostSacrifice;
|
||||
import forge.game.keyword.Keyword;
|
||||
import forge.game.phase.PhaseHandler;
|
||||
import forge.game.phase.PhaseType;
|
||||
import forge.game.player.Player;
|
||||
@@ -94,7 +95,7 @@ public class AttachAi extends SpellAbilityAi {
|
||||
final CardCollection targets = CardLists.filter(list, new Predicate<Card>() {
|
||||
@Override
|
||||
public boolean apply(final Card c) {
|
||||
return !(c.hasProtectionFrom(source) || c.hasKeyword("Shroud") || c.hasKeyword("Hexproof"));
|
||||
return !(c.hasProtectionFrom(source) || c.hasKeyword(Keyword.SHROUD) || c.hasKeyword(Keyword.HEXPROOF));
|
||||
}
|
||||
});
|
||||
if (targets.isEmpty()) {
|
||||
@@ -255,7 +256,7 @@ public class AttachAi extends SpellAbilityAi {
|
||||
@Override
|
||||
public boolean apply(final Card c) {
|
||||
// Don't do Untapped Vigilance cards
|
||||
if (c.isCreature() && c.hasKeyword("Vigilance") && c.isUntapped()) {
|
||||
if (c.isCreature() && c.hasKeyword(Keyword.VIGILANCE) && c.isUntapped()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -397,7 +398,7 @@ public class AttachAi extends SpellAbilityAi {
|
||||
List<Card> evenBetterList = CardLists.filter(betterList, new Predicate<Card>() {
|
||||
@Override
|
||||
public boolean apply(final Card c) {
|
||||
return c.hasKeyword("Indestructible") || c.hasKeyword("Hexproof");
|
||||
return c.hasKeyword(Keyword.INDESTRUCTIBLE) || c.hasKeyword(Keyword.HEXPROOF);
|
||||
}
|
||||
});
|
||||
if (!evenBetterList.isEmpty()) {
|
||||
@@ -484,23 +485,23 @@ public class AttachAi extends SpellAbilityAi {
|
||||
for (Card card : list) {
|
||||
int cardPriority = 0;
|
||||
// Prefer Evasion
|
||||
if (card.hasKeyword("Trample")) {
|
||||
if (card.hasKeyword(Keyword.TRAMPLE)) {
|
||||
cardPriority += 10;
|
||||
}
|
||||
if (card.hasKeyword("Menace")) {
|
||||
if (card.hasKeyword(Keyword.MENACE)) {
|
||||
cardPriority += 10;
|
||||
}
|
||||
// Avoid this for Sleepers Robe?
|
||||
if (card.hasKeyword("Fear")) {
|
||||
if (card.hasKeyword(Keyword.FEAR)) {
|
||||
cardPriority += 15;
|
||||
}
|
||||
if (card.hasKeyword("Flying")) {
|
||||
if (card.hasKeyword(Keyword.FLYING)) {
|
||||
cardPriority += 20;
|
||||
}
|
||||
if (card.hasKeyword("Shadow")) {
|
||||
if (card.hasKeyword(Keyword.SHADOW)) {
|
||||
cardPriority += 30;
|
||||
}
|
||||
if (card.hasKeyword("Horsemanship")) {
|
||||
if (card.hasKeyword(Keyword.HORSEMANSHIP)) {
|
||||
cardPriority += 40;
|
||||
}
|
||||
if (card.hasKeyword("Unblockable")) {
|
||||
@@ -520,10 +521,10 @@ public class AttachAi extends SpellAbilityAi {
|
||||
if (card.getCurrentPower() <= 0) {
|
||||
cardPriority = -100;
|
||||
}
|
||||
if (card.hasKeyword("Defender")) {
|
||||
if (card.hasKeyword(Keyword.DEFENDER)) {
|
||||
cardPriority = -100;
|
||||
}
|
||||
if (card.hasKeyword("Indestructible")) {
|
||||
if (card.hasKeyword(Keyword.INDESTRUCTIBLE)) {
|
||||
cardPriority += 15;
|
||||
}
|
||||
if (cardPriority > priority) {
|
||||
@@ -725,7 +726,7 @@ public class AttachAi extends SpellAbilityAi {
|
||||
prefList = CardLists.filter(list, new Predicate<Card>() {
|
||||
@Override
|
||||
public boolean apply(final Card c) {
|
||||
if (!c.hasKeyword("Indestructible") && (c.getLethalDamage() <= Math.abs(tgh))) {
|
||||
if (!c.hasKeyword(Keyword.INDESTRUCTIBLE) && (c.getLethalDamage() <= Math.abs(tgh))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1012,7 +1013,7 @@ public class AttachAi extends SpellAbilityAi {
|
||||
if (isUsefulAttachKeyword(keyword, c, sa, pow)) {
|
||||
return true;
|
||||
}
|
||||
if (c.hasKeyword("Infect") && pow >= 2) {
|
||||
if (c.hasKeyword(Keyword.INFECT) && pow >= 2) {
|
||||
// consider +2 power a significant bonus on Infect creatures
|
||||
return true;
|
||||
}
|
||||
@@ -1040,7 +1041,7 @@ public class AttachAi extends SpellAbilityAi {
|
||||
prefList = CardLists.filter(prefList, new Predicate<Card>() {
|
||||
@Override
|
||||
public boolean apply(final Card c) {
|
||||
return !c.isEnchanted() || c.hasKeyword("Hexproof");
|
||||
return !c.isEnchanted() || c.hasKeyword(Keyword.HEXPROOF);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -1375,7 +1376,7 @@ public class AttachAi extends SpellAbilityAi {
|
||||
return false;
|
||||
}
|
||||
} else if (keyword.equals("First Strike")) {
|
||||
if (card.getNetCombatDamage() + powerBonus <= 0 || card.hasKeyword("Double Strike")
|
||||
if (card.getNetCombatDamage() + powerBonus <= 0 || card.hasKeyword(Keyword.DOUBLE_STRIKE)
|
||||
|| (!ComputerUtilCombat.canAttackNextTurn(card) && !CombatUtil.canBlock(card, true))) {
|
||||
return false;
|
||||
}
|
||||
@@ -1408,7 +1409,7 @@ public class AttachAi extends SpellAbilityAi {
|
||||
return false;
|
||||
}
|
||||
} else if (keyword.equals("Reach")) {
|
||||
if (card.hasKeyword("Flying") || !CombatUtil.canBlock(card, true)) {
|
||||
if (card.hasKeyword(Keyword.FLYING) || !CombatUtil.canBlock(card, true)) {
|
||||
return false;
|
||||
}
|
||||
} else if (keyword.endsWith("CARDNAME can block an additional creature each combat.")) {
|
||||
@@ -1417,11 +1418,11 @@ public class AttachAi extends SpellAbilityAi {
|
||||
return false;
|
||||
}
|
||||
} else if (keyword.equals("CARDNAME can attack as though it didn't have defender.")) {
|
||||
if (!card.hasKeyword("Defender") || card.getNetCombatDamage() + powerBonus <= 0) {
|
||||
if (!card.hasKeyword(Keyword.DEFENDER) || card.getNetCombatDamage() + powerBonus <= 0) {
|
||||
return false;
|
||||
}
|
||||
} else if (keyword.equals("Shroud") || keyword.equals("Hexproof")) {
|
||||
if (card.hasKeyword("Shroud") || card.hasKeyword("Hexproof")) {
|
||||
if (card.hasKeyword(Keyword.SHROUD) || card.hasKeyword(Keyword.HEXPROOF)) {
|
||||
return false;
|
||||
}
|
||||
} else if (keyword.equals("Defender")) {
|
||||
|
||||
@@ -7,6 +7,7 @@ import forge.game.Game;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.card.CardCollection;
|
||||
import forge.game.card.CardLists;
|
||||
import forge.game.keyword.Keyword;
|
||||
import forge.game.phase.PhaseType;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
@@ -30,7 +31,7 @@ public class BecomesBlockedAi extends SpellAbilityAi {
|
||||
CardCollection list = CardLists.filterControlledBy(game.getCardsIn(ZoneType.Battlefield), aiPlayer.getOpponents());
|
||||
list = CardLists.getValidCards(list, tgt.getValidTgts(), source.getController(), source, sa);
|
||||
list = CardLists.getTargetableCards(list, sa);
|
||||
list = CardLists.getNotKeyword(list, "Trample");
|
||||
list = CardLists.getNotKeyword(list, Keyword.TRAMPLE);
|
||||
|
||||
while (sa.getTargets().getNumTargeted() < tgt.getMaxTargets(source, sa)) {
|
||||
Card choice = null;
|
||||
|
||||
@@ -22,6 +22,7 @@ import forge.game.card.CardPredicates;
|
||||
import forge.game.card.CardPredicates.Presets;
|
||||
import forge.game.card.CounterType;
|
||||
import forge.game.combat.Combat;
|
||||
import forge.game.keyword.Keyword;
|
||||
import forge.game.phase.PhaseType;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.player.PlayerPredicates;
|
||||
@@ -126,8 +127,8 @@ public class ChooseCardAi extends SpellAbilityAi {
|
||||
} else if (aiLogic.equals("Duneblast")) {
|
||||
CardCollection aiCreatures = ai.getCreaturesInPlay();
|
||||
CardCollection oppCreatures = ComputerUtil.getOpponentFor(ai).getCreaturesInPlay();
|
||||
aiCreatures = CardLists.getNotKeyword(aiCreatures, "Indestructible");
|
||||
oppCreatures = CardLists.getNotKeyword(oppCreatures, "Indestructible");
|
||||
aiCreatures = CardLists.getNotKeyword(aiCreatures, Keyword.INDESTRUCTIBLE);
|
||||
oppCreatures = CardLists.getNotKeyword(oppCreatures, Keyword.INDESTRUCTIBLE);
|
||||
|
||||
// Use it as a wrath, when the human creatures threat the ai's life
|
||||
if (aiCreatures.isEmpty() && ComputerUtilCombat.sumDamageIfUnblocked(oppCreatures, ai) >= ai.getLife()) {
|
||||
@@ -261,7 +262,7 @@ public class ChooseCardAi extends SpellAbilityAi {
|
||||
}
|
||||
} else if (logic.equals("Duneblast")) {
|
||||
CardCollectionView aiCreatures = ai.getCreaturesInPlay();
|
||||
aiCreatures = CardLists.getNotKeyword(aiCreatures, "Indestructible");
|
||||
aiCreatures = CardLists.getNotKeyword(aiCreatures, Keyword.INDESTRUCTIBLE);
|
||||
|
||||
if (aiCreatures.isEmpty()) {
|
||||
return null;
|
||||
|
||||
@@ -27,6 +27,7 @@ import forge.game.card.CardCollection;
|
||||
import forge.game.card.CardCollectionView;
|
||||
import forge.game.card.CardLists;
|
||||
import forge.game.card.CounterType;
|
||||
import forge.game.keyword.Keyword;
|
||||
import forge.util.Aggregates;
|
||||
|
||||
|
||||
@@ -59,7 +60,7 @@ public abstract class CountersAi {
|
||||
if (type.equals("M1M1")) {
|
||||
// try to kill the best killable creature, or reduce the best one
|
||||
// but try not to target a Undying Creature
|
||||
final List<Card> killable = CardLists.getNotKeyword(CardLists.filterToughness(list, amount), "Undying");
|
||||
final List<Card> killable = CardLists.getNotKeyword(CardLists.filterToughness(list, amount), Keyword.UNDYING);
|
||||
if (killable.size() > 0) {
|
||||
choice = ComputerUtilCard.getBestCreatureAI(killable);
|
||||
} else {
|
||||
|
||||
@@ -8,6 +8,7 @@ import forge.ai.SpellAbilityAi;
|
||||
import forge.game.Game;
|
||||
import forge.game.ability.AbilityUtils;
|
||||
import forge.game.card.*;
|
||||
import forge.game.keyword.Keyword;
|
||||
import forge.game.phase.PhaseHandler;
|
||||
import forge.game.phase.PhaseType;
|
||||
import forge.game.player.Player;
|
||||
@@ -286,7 +287,7 @@ public class CountersMoveAi extends SpellAbilityAi {
|
||||
// do not steal a P1P1 from Undying if it would die
|
||||
// this way
|
||||
if (CounterType.P1P1.equals(cType) && srcCardCpy.getNetToughness() <= 0) {
|
||||
if (srcCardCpy.getCounters(cType) > 0 || !card.hasKeyword("Undying") || card.isToken()) {
|
||||
if (srcCardCpy.getCounters(cType) > 0 || !card.hasKeyword(Keyword.UNDYING) || card.isToken()) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
@@ -15,6 +15,7 @@ import forge.game.cost.Cost;
|
||||
import forge.game.cost.CostPart;
|
||||
import forge.game.cost.CostRemoveCounter;
|
||||
import forge.game.cost.CostSacrifice;
|
||||
import forge.game.keyword.Keyword;
|
||||
import forge.game.phase.PhaseHandler;
|
||||
import forge.game.phase.PhaseType;
|
||||
import forge.game.player.Player;
|
||||
@@ -154,7 +155,7 @@ public class CountersPutAi extends SpellAbilityAi {
|
||||
// receive counters, execpt it has undying
|
||||
CardCollection oppCreat = CardLists.getTargetableCards(ai.getOpponents().getCreaturesInPlay(), sa);
|
||||
CardCollection oppCreatM1 = CardLists.filter(oppCreat, CardPredicates.hasCounter(CounterType.M1M1));
|
||||
oppCreatM1 = CardLists.getNotKeyword(oppCreatM1, "Undying");
|
||||
oppCreatM1 = CardLists.getNotKeyword(oppCreatM1, Keyword.UNDYING);
|
||||
|
||||
oppCreatM1 = CardLists.filter(oppCreatM1, new Predicate<Card>() {
|
||||
@Override
|
||||
@@ -339,14 +340,8 @@ public class CountersPutAi extends SpellAbilityAi {
|
||||
|
||||
if ("Polukranos".equals(sa.getParam("AILogic"))) {
|
||||
|
||||
CardCollection humCreatures = CardLists.getTargetableCards(ai.getOpponents().getCreaturesInPlay(), sa);
|
||||
CardCollection targets = CardLists.getTargetableCards(ai.getOpponents().getCreaturesInPlay(), sa);
|
||||
|
||||
final CardCollection targets = CardLists.filter(humCreatures, new Predicate<Card>() {
|
||||
@Override
|
||||
public boolean apply(final Card c) {
|
||||
return !(c.hasProtectionFrom(source) || c.hasKeyword("Shroud") || c.hasKeyword("Hexproof"));
|
||||
}
|
||||
});
|
||||
if (!targets.isEmpty()){
|
||||
boolean canSurvive = false;
|
||||
for (Card humanCreature : targets) {
|
||||
@@ -792,12 +787,12 @@ public class CountersPutAi extends SpellAbilityAi {
|
||||
final List<Card> creats = player.getCreaturesInPlay();
|
||||
final int tributeAmount = AbilityUtils.calculateAmount(sa.getHostCard(), sa.getParam("CounterNum"), sa);
|
||||
|
||||
final boolean isHaste = source.hasKeyword("Haste");
|
||||
final boolean isHaste = source.hasKeyword(Keyword.HASTE);
|
||||
List<Card> threatening = CardLists.filter(creats, new Predicate<Card>() {
|
||||
@Override
|
||||
public boolean apply(Card c) {
|
||||
return CombatUtil.canBlock(source, c, !isHaste)
|
||||
&& (c.getNetToughness() > source.getNetPower() + tributeAmount || c.hasKeyword("DeathTouch"));
|
||||
&& (c.getNetToughness() > source.getNetPower() + tributeAmount || c.hasKeyword(Keyword.DEATHTOUCH));
|
||||
}
|
||||
});
|
||||
if (!threatening.isEmpty()) {
|
||||
@@ -814,7 +809,7 @@ public class CountersPutAi extends SpellAbilityAi {
|
||||
List<Card> canBlock = CardLists.filter(creats, new Predicate<Card>() {
|
||||
@Override
|
||||
public boolean apply(Card c) {
|
||||
return CombatUtil.canBlock(source, c) && (c.getNetToughness() > source.getNetPower() || c.hasKeyword("DeathTouch"));
|
||||
return CombatUtil.canBlock(source, c) && (c.getNetToughness() > source.getNetPower() || c.hasKeyword(Keyword.DEATHTOUCH));
|
||||
}
|
||||
});
|
||||
if (!canBlock.isEmpty()) {
|
||||
@@ -907,7 +902,7 @@ public class CountersPutAi extends SpellAbilityAi {
|
||||
final CardCollection persist = CardLists.filter(filtered, new Predicate<Card>() {
|
||||
@Override
|
||||
public boolean apply(Card input) {
|
||||
if (!input.hasKeyword("Persist"))
|
||||
if (!input.hasKeyword(Keyword.PERSIST))
|
||||
return false;
|
||||
return input.getCounters(CounterType.M1M1) <= amount;
|
||||
}
|
||||
@@ -920,7 +915,7 @@ public class CountersPutAi extends SpellAbilityAi {
|
||||
final CardCollection undying = CardLists.filter(filtered, new Predicate<Card>() {
|
||||
@Override
|
||||
public boolean apply(Card input) {
|
||||
if (!input.hasKeyword("Undying"))
|
||||
if (!input.hasKeyword(Keyword.UNDYING))
|
||||
return false;
|
||||
return input.getCounters(CounterType.P1P1) <= amount && input.getNetToughness() > amount;
|
||||
}
|
||||
@@ -945,7 +940,7 @@ public class CountersPutAi extends SpellAbilityAi {
|
||||
if (e instanceof Card) {
|
||||
Card c = (Card) e;
|
||||
if (c.getController().isOpponentOf(ai)) {
|
||||
if (options.contains(CounterType.M1M1) && !c.hasKeyword("Undying")) {
|
||||
if (options.contains(CounterType.M1M1) && !c.hasKeyword(Keyword.UNDYING)) {
|
||||
return CounterType.M1M1;
|
||||
}
|
||||
for (CounterType type : options) {
|
||||
|
||||
@@ -23,6 +23,7 @@ import forge.ai.SpellAbilityAi;
|
||||
import forge.game.Game;
|
||||
import forge.game.GlobalRuleChange;
|
||||
import forge.game.card.*;
|
||||
import forge.game.keyword.Keyword;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.player.PlayerController.BinaryChoiceType;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
@@ -56,7 +57,6 @@ public class CountersPutOrRemoveAi extends SpellAbilityAi {
|
||||
}
|
||||
|
||||
private boolean doTgt(Player ai, SpellAbility sa, boolean mandatory) {
|
||||
final Card source = sa.getHostCard();
|
||||
final Game game = ai.getGame();
|
||||
|
||||
final int amount = Integer.valueOf(sa.getParam("CounterNum"));
|
||||
@@ -71,7 +71,7 @@ public class CountersPutOrRemoveAi extends SpellAbilityAi {
|
||||
}
|
||||
|
||||
// Filter AI-specific targets if provided
|
||||
list = ComputerUtil.filterAITgts(sa, ai, (CardCollection)list, false);
|
||||
list = ComputerUtil.filterAITgts(sa, ai, list, false);
|
||||
|
||||
if (sa.hasParam("CounterType")) {
|
||||
// currently only Jhoira's Timebug
|
||||
@@ -125,7 +125,7 @@ public class CountersPutOrRemoveAi extends SpellAbilityAi {
|
||||
|
||||
CardCollection aiM1M1List = CardLists.filter(aiList, CardPredicates.hasCounter(CounterType.M1M1));
|
||||
|
||||
CardCollection aiPersistList = CardLists.getKeyword(aiM1M1List, "Persist");
|
||||
CardCollection aiPersistList = CardLists.getKeyword(aiM1M1List, Keyword.PERSIST);
|
||||
if (!aiPersistList.isEmpty()) {
|
||||
aiM1M1List = aiPersistList;
|
||||
}
|
||||
@@ -137,7 +137,7 @@ public class CountersPutOrRemoveAi extends SpellAbilityAi {
|
||||
|
||||
// do as P1P1 part
|
||||
CardCollection aiP1P1List = CardLists.filter(aiList, CardPredicates.hasCounter(CounterType.P1P1));
|
||||
CardCollection aiUndyingList = CardLists.getKeyword(aiM1M1List, "Undying");
|
||||
CardCollection aiUndyingList = CardLists.getKeyword(aiM1M1List, Keyword.UNDYING);
|
||||
|
||||
if (!aiUndyingList.isEmpty()) {
|
||||
aiP1P1List = aiUndyingList;
|
||||
@@ -226,9 +226,9 @@ public class CountersPutOrRemoveAi extends SpellAbilityAi {
|
||||
if (!ai.isCardInPlay("Marit Lage") || noLegendary) {
|
||||
return CounterType.ICE;
|
||||
}
|
||||
} else if (tgt.hasKeyword("Undying") && options.contains(CounterType.P1P1)) {
|
||||
} else if (tgt.hasKeyword(Keyword.UNDYING) && options.contains(CounterType.P1P1)) {
|
||||
return CounterType.P1P1;
|
||||
} else if (tgt.hasKeyword("Persist") && options.contains(CounterType.M1M1)) {
|
||||
} else if (tgt.hasKeyword(Keyword.PERSIST) && options.contains(CounterType.M1M1)) {
|
||||
return CounterType.M1M1;
|
||||
}
|
||||
|
||||
@@ -272,9 +272,9 @@ public class CountersPutOrRemoveAi extends SpellAbilityAi {
|
||||
if (!ai.isCardInPlay("Marit Lage") || noLegendary) {
|
||||
return false;
|
||||
}
|
||||
} else if (type.equals(CounterType.M1M1) && tgt.hasKeyword("Persist")) {
|
||||
} else if (type.equals(CounterType.M1M1) && tgt.hasKeyword(Keyword.PERSIST)) {
|
||||
return false;
|
||||
} else if (type.equals(CounterType.P1P1) && tgt.hasKeyword("Undying")) {
|
||||
} else if (type.equals(CounterType.P1P1) && tgt.hasKeyword(Keyword.UNDYING)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ import forge.game.Game;
|
||||
import forge.game.GlobalRuleChange;
|
||||
import forge.game.ability.AbilityUtils;
|
||||
import forge.game.card.*;
|
||||
import forge.game.keyword.Keyword;
|
||||
import forge.game.phase.PhaseHandler;
|
||||
import forge.game.phase.PhaseType;
|
||||
import forge.game.player.Player;
|
||||
@@ -188,7 +189,7 @@ public class CountersRemoveAi extends SpellAbilityAi {
|
||||
|
||||
CardCollection aiM1M1List = CardLists.filter(aiList, CardPredicates.hasCounter(CounterType.M1M1));
|
||||
|
||||
CardCollection aiPersistList = CardLists.getKeyword(aiM1M1List, "Persist");
|
||||
CardCollection aiPersistList = CardLists.getKeyword(aiM1M1List, Keyword.PERSIST);
|
||||
if (!aiPersistList.isEmpty()) {
|
||||
aiM1M1List = aiPersistList;
|
||||
}
|
||||
@@ -200,7 +201,7 @@ public class CountersRemoveAi extends SpellAbilityAi {
|
||||
|
||||
// do as P1P1 part
|
||||
CardCollection aiP1P1List = CardLists.filter(aiList, CardPredicates.hasCounter(CounterType.P1P1));
|
||||
CardCollection aiUndyingList = CardLists.getKeyword(aiM1M1List, "Undying");
|
||||
CardCollection aiUndyingList = CardLists.getKeyword(aiM1M1List, Keyword.UNDYING);
|
||||
|
||||
if (!aiUndyingList.isEmpty()) {
|
||||
aiP1P1List = aiUndyingList;
|
||||
@@ -230,7 +231,7 @@ public class CountersRemoveAi extends SpellAbilityAi {
|
||||
CardCollection aiList = CardLists.filterControlledBy(list, ai);
|
||||
aiList = CardLists.filter(aiList, CardPredicates.hasCounter(CounterType.M1M1, amount));
|
||||
|
||||
CardCollection aiPersist = CardLists.getKeyword(aiList, "Persist");
|
||||
CardCollection aiPersist = CardLists.getKeyword(aiList, Keyword.PERSIST);
|
||||
if (!aiPersist.isEmpty()) {
|
||||
aiList = aiPersist;
|
||||
}
|
||||
@@ -253,7 +254,7 @@ public class CountersRemoveAi extends SpellAbilityAi {
|
||||
// targeting ai creatures too
|
||||
CardCollection aiList = CardLists.filterControlledBy(list, ai);
|
||||
if (!aiList.isEmpty()) {
|
||||
CardCollection aiListUndying = CardLists.getKeyword(aiList, "Undying");
|
||||
CardCollection aiListUndying = CardLists.getKeyword(aiList, Keyword.UNDYING);
|
||||
if (!aiListUndying.isEmpty()) {
|
||||
aiList = aiListUndying;
|
||||
}
|
||||
@@ -266,7 +267,7 @@ public class CountersRemoveAi extends SpellAbilityAi {
|
||||
// need to target opponent creatures
|
||||
CardCollection oppList = CardLists.filterControlledBy(list, ai.getOpponents());
|
||||
if (!oppList.isEmpty()) {
|
||||
CardCollection oppListNotUndying = CardLists.getNotKeyword(oppList, "Undying");
|
||||
CardCollection oppListNotUndying = CardLists.getNotKeyword(oppList, Keyword.UNDYING);
|
||||
if (!oppListNotUndying.isEmpty()) {
|
||||
oppList = oppListNotUndying;
|
||||
}
|
||||
@@ -358,9 +359,9 @@ public class CountersRemoveAi extends SpellAbilityAi {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (options.contains(CounterType.M1M1) && target.hasKeyword("Persist")) {
|
||||
if (options.contains(CounterType.M1M1) && target.hasKeyword(Keyword.PERSIST)) {
|
||||
return CounterType.M1M1;
|
||||
} else if (options.contains(CounterType.P1P1) && target.hasKeyword("Undying")) {
|
||||
} else if (options.contains(CounterType.P1P1) && target.hasKeyword(Keyword.UNDYING)) {
|
||||
return CounterType.M1M1;
|
||||
}
|
||||
for (CounterType type : options) {
|
||||
|
||||
@@ -8,6 +8,7 @@ import forge.game.Game;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.card.CardCollectionView;
|
||||
import forge.game.card.CardPredicates;
|
||||
import forge.game.keyword.Keyword;
|
||||
import forge.game.phase.PhaseHandler;
|
||||
import forge.game.phase.PhaseType;
|
||||
import forge.game.player.Player;
|
||||
@@ -24,7 +25,8 @@ public abstract class DamageAiBase extends SpellAbilityAi {
|
||||
// Do not target a player if they aren't below 75% of our health.
|
||||
// Unless Lifelink will cancel the damage to us
|
||||
Card hostcard = sa.getHostCard();
|
||||
boolean lifelink = hostcard.hasKeyword("Lifelink");
|
||||
boolean lifelink = hostcard.hasKeyword(Keyword.LIFELINK);
|
||||
if (!lifelink) {
|
||||
for (Card ench : hostcard.getEnchantedBy(false)) {
|
||||
// Treat cards enchanted by older cards with "when enchanted creature deals damage, gain life" as if they had lifelink.
|
||||
if (ench.hasSVar("LikeLifeLink")) {
|
||||
@@ -33,6 +35,7 @@ public abstract class DamageAiBase extends SpellAbilityAi {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if ("SelfDamage".equals(sa.getParam("AILogic"))) {
|
||||
if (comp.getLife() * 0.75 < enemy.getLife()) {
|
||||
if (!lifelink) {
|
||||
|
||||
@@ -8,6 +8,7 @@ import forge.game.card.CardCollection;
|
||||
import forge.game.card.CardLists;
|
||||
import forge.game.card.CounterType;
|
||||
import forge.game.cost.Cost;
|
||||
import forge.game.keyword.Keyword;
|
||||
import forge.game.phase.PhaseType;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
@@ -268,7 +269,7 @@ public class DamageAllAi extends SpellAbilityAi {
|
||||
}
|
||||
};
|
||||
|
||||
list = CardLists.getNotKeyword(list, "Indestructible");
|
||||
list = CardLists.getNotKeyword(list, Keyword.INDESTRUCTIBLE);
|
||||
list = CardLists.filter(list, filterKillable);
|
||||
|
||||
return list;
|
||||
|
||||
@@ -10,6 +10,7 @@ import forge.game.ability.AbilityUtils;
|
||||
import forge.game.card.*;
|
||||
import forge.game.cost.Cost;
|
||||
import forge.game.cost.CostRemoveCounter;
|
||||
import forge.game.keyword.Keyword;
|
||||
import forge.game.phase.PhaseHandler;
|
||||
import forge.game.phase.PhaseType;
|
||||
import forge.game.player.Player;
|
||||
@@ -742,7 +743,7 @@ public class DamageDealAi extends DamageAiBase {
|
||||
if (o instanceof Card) {
|
||||
Card c = (Card) o;
|
||||
final int restDamage = ComputerUtilCombat.predictDamageTo(c, dmg, saMe.getHostCard(), false);
|
||||
if (!c.hasKeyword("Indestructible") && ComputerUtilCombat.getDamageToKill(c) <= restDamage) {
|
||||
if (!c.hasKeyword(Keyword.INDESTRUCTIBLE) && ComputerUtilCombat.getDamageToKill(c) <= restDamage) {
|
||||
if (c.getController().equals(ai)) {
|
||||
return false;
|
||||
} else {
|
||||
@@ -942,7 +943,7 @@ public class DamageDealAi extends DamageAiBase {
|
||||
for (Card c : creatures) {
|
||||
int power = c.getNetPower();
|
||||
int toughness = c.getNetToughness();
|
||||
boolean canDie = !(c.hasKeyword("Indestructible") || ComputerUtil.canRegenerate(c.getController(), c));
|
||||
boolean canDie = !(c.hasKeyword(Keyword.INDESTRUCTIBLE) || ComputerUtil.canRegenerate(c.getController(), c));
|
||||
|
||||
// Currently will target creatures with toughness 3+ (or power 5+)
|
||||
// and only if the creature can actually die, do not "underdrain"
|
||||
|
||||
@@ -8,6 +8,7 @@ import forge.game.card.*;
|
||||
import forge.game.cost.Cost;
|
||||
import forge.game.cost.CostPart;
|
||||
import forge.game.cost.CostSacrifice;
|
||||
import forge.game.keyword.Keyword;
|
||||
import forge.game.phase.PhaseHandler;
|
||||
import forge.game.phase.PhaseType;
|
||||
import forge.game.player.Player;
|
||||
@@ -109,7 +110,7 @@ public class DestroyAi extends SpellAbilityAi {
|
||||
return false;
|
||||
}
|
||||
for (Card c : list) {
|
||||
if (c.hasKeyword("Indestructible")) {
|
||||
if (c.hasKeyword(Keyword.INDESTRUCTIBLE)) {
|
||||
sa.getTargets().add(c);
|
||||
return true;
|
||||
}
|
||||
@@ -133,7 +134,7 @@ public class DestroyAi extends SpellAbilityAi {
|
||||
// Filter AI-specific targets if provided
|
||||
list = ComputerUtil.filterAITgts(sa, ai, (CardCollection)list, true);
|
||||
|
||||
list = CardLists.getNotKeyword(list, "Indestructible");
|
||||
list = CardLists.getNotKeyword(list, Keyword.INDESTRUCTIBLE);
|
||||
if (CardLists.getNotType(list, "Creature").isEmpty()) {
|
||||
list = ComputerUtilCard.prioritizeCreaturesWorthRemovingNow(ai, list, false);
|
||||
}
|
||||
@@ -160,7 +161,7 @@ public class DestroyAi extends SpellAbilityAi {
|
||||
return false;
|
||||
}
|
||||
//Check for undying
|
||||
return (!c.hasKeyword("Undying") || c.getCounters(CounterType.P1P1) > 0);
|
||||
return (!c.hasKeyword(Keyword.UNDYING) || c.getCounters(CounterType.P1P1) > 0);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -294,7 +295,7 @@ public class DestroyAi extends SpellAbilityAi {
|
||||
|
||||
if (list.isEmpty()
|
||||
|| !CardLists.filterControlledBy(list, ai).isEmpty()
|
||||
|| CardLists.getNotKeyword(list, "Indestructible").isEmpty()) {
|
||||
|| CardLists.getNotKeyword(list, Keyword.INDESTRUCTIBLE).isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -316,7 +317,7 @@ public class DestroyAi extends SpellAbilityAi {
|
||||
return false;
|
||||
}
|
||||
|
||||
CardCollection preferred = CardLists.getNotKeyword(list, "Indestructible");
|
||||
CardCollection preferred = CardLists.getNotKeyword(list, Keyword.INDESTRUCTIBLE);
|
||||
preferred = CardLists.filterControlledBy(preferred, ai.getOpponents());
|
||||
if (CardLists.getNotType(preferred, "Creature").isEmpty()) {
|
||||
preferred = ComputerUtilCard.prioritizeCreaturesWorthRemovingNow(ai, preferred, false);
|
||||
|
||||
@@ -7,6 +7,7 @@ import forge.game.card.Card;
|
||||
import forge.game.card.CardCollection;
|
||||
import forge.game.card.CardLists;
|
||||
import forge.game.cost.Cost;
|
||||
import forge.game.keyword.Keyword;
|
||||
import forge.game.phase.PhaseType;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
@@ -18,7 +19,7 @@ public class DestroyAllAi extends SpellAbilityAi {
|
||||
private static final Predicate<Card> predicate = new Predicate<Card>() {
|
||||
@Override
|
||||
public boolean apply(final Card c) {
|
||||
return !(c.hasKeyword("Indestructible") || c.getSVar("SacMe").length() > 0);
|
||||
return !(c.hasKeyword(Keyword.INDESTRUCTIBLE) || c.getSVar("SacMe").length() > 0);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ import forge.game.GlobalRuleChange;
|
||||
import forge.game.ability.ApiType;
|
||||
import forge.game.card.*;
|
||||
import forge.game.combat.CombatUtil;
|
||||
import forge.game.keyword.Keyword;
|
||||
import forge.game.phase.PhaseHandler;
|
||||
import forge.game.phase.PhaseType;
|
||||
import forge.game.player.Player;
|
||||
@@ -138,7 +139,8 @@ public class EffectAi extends SpellAbilityAi {
|
||||
final int count = CardLists.count(ai.getCardsIn(ZoneType.Hand), new Predicate<Card>() {
|
||||
@Override
|
||||
public boolean apply(final Card c) {
|
||||
return (c.isInstant() || c.isSorcery()) && !c.hasKeyword("Rebound") && ComputerUtil.hasReasonToPlayCardThisTurn(ai, c);
|
||||
return (c.isInstant() || c.isSorcery()) && !c.hasKeyword(Keyword.REBOUND)
|
||||
&& ComputerUtil.hasReasonToPlayCardThisTurn(ai, c);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ import forge.game.card.Card;
|
||||
import forge.game.card.CardCollection;
|
||||
import forge.game.card.CardCollectionView;
|
||||
import forge.game.card.CardLists;
|
||||
import forge.game.keyword.Keyword;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.spellability.AbilitySub;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
@@ -246,15 +247,17 @@ public class FightAi extends SpellAbilityAi {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean canKill(Card fighter, Card opponent, int pumpAttack) {
|
||||
if (opponent.getSVar("Targeting").equals("Dies")) {
|
||||
return true;
|
||||
}
|
||||
if (opponent.hasProtectionFrom(fighter) || !opponent.canBeDestroyed()
|
||||
|| opponent.getShieldCount() > 0 || ComputerUtil.canRegenerate(opponent.getController(), opponent)) {
|
||||
if (opponent.hasProtectionFrom(fighter) || !opponent.canBeDestroyed() || opponent.getShieldCount() > 0
|
||||
|| ComputerUtil.canRegenerate(opponent.getController(), opponent)) {
|
||||
return false;
|
||||
}
|
||||
if (fighter.hasKeyword("Deathtouch") || ComputerUtilCombat.getDamageToKill(opponent) <= fighter.getNetPower() + pumpAttack) {
|
||||
if (fighter.hasKeyword(Keyword.DEATHTOUCH)
|
||||
|| ComputerUtilCombat.getDamageToKill(opponent) <= fighter.getNetPower() + pumpAttack) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
@@ -6,6 +6,7 @@ import forge.game.Game;
|
||||
import forge.game.GameObject;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.card.CardPredicates;
|
||||
import forge.game.keyword.Keyword;
|
||||
import forge.game.phase.PhaseType;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
@@ -66,7 +67,7 @@ public class FogAi extends SpellAbilityAi {
|
||||
for (Card atk : game.getCombat().getAttackersOf(ai)) {
|
||||
if (game.getCombat().isUnblocked(atk)) {
|
||||
dmg += atk.getNetCombatDamage();
|
||||
} else if (atk.hasKeyword("Trample")) {
|
||||
} else if (atk.hasKeyword(Keyword.TRAMPLE)) {
|
||||
dmg += atk.getNetCombatDamage() - Aggregates.sum(game.getCombat().getBlockers(atk), CardPredicates.Accessors.fnGetNetToughness);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import forge.game.ability.AbilityUtils;
|
||||
import forge.game.card.*;
|
||||
import forge.game.cost.CostPart;
|
||||
import forge.game.cost.CostRemoveCounter;
|
||||
import forge.game.keyword.Keyword;
|
||||
import forge.game.phase.PhaseHandler;
|
||||
import forge.game.phase.PhaseType;
|
||||
import forge.game.player.Player;
|
||||
@@ -170,7 +171,7 @@ public class ManaEffectAi extends SpellAbilityAi {
|
||||
}
|
||||
testSaNoCost.setActivatingPlayer(ai);
|
||||
if (((PlayerControllerAi)ai.getController()).getAi().canPlaySa(testSaNoCost) == AiPlayDecision.WillPlay) {
|
||||
if (testSa.getHostCard().isPermanent() && !testSa.getHostCard().hasKeyword("Haste")
|
||||
if (testSa.getHostCard().isPermanent() && !testSa.getHostCard().hasKeyword(Keyword.HASTE)
|
||||
&& !ai.getGame().getPhaseHandler().is(PhaseType.MAIN2)) {
|
||||
// AI will waste a ritual in Main 1 unless the casted permanent is a haste creature
|
||||
continue;
|
||||
|
||||
@@ -80,7 +80,7 @@ public class PermanentCreatureAi extends PermanentAi {
|
||||
}
|
||||
|
||||
// save cards with flash for surprise blocking
|
||||
if (card.hasKeyword("Flash")
|
||||
if (card.withFlash(ai)
|
||||
&& (ai.isUnlimitedHandSize() || ai.getCardsIn(ZoneType.Hand).size() <= ai.getMaxHandSize()
|
||||
|| ph.getPhase().isBefore(PhaseType.END_OF_TURN))
|
||||
&& ai.getManaPool().totalMana() <= 0
|
||||
|
||||
@@ -11,6 +11,7 @@ import forge.game.card.Card;
|
||||
import forge.game.card.CardCollection;
|
||||
import forge.game.card.CardLists;
|
||||
import forge.game.cost.Cost;
|
||||
import forge.game.keyword.Keyword;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.player.PlayerActionConfirmMode;
|
||||
import forge.game.spellability.Spell;
|
||||
@@ -74,7 +75,7 @@ public class PlayAi extends SpellAbilityAi {
|
||||
return ComputerUtil.targetPlayableSpellCard(ai, cards, sa, sa.hasParam("WithoutManaCost"));
|
||||
}
|
||||
|
||||
if (source != null && source.hasKeyword("Hideaway") && source.hasRemembered()) {
|
||||
if (source != null && source.hasKeyword(Keyword.HIDEAWAY) && source.hasRemembered()) {
|
||||
// AI is not very good at playing non-permanent spells this way, at least yet
|
||||
// (might be possible to enable it for Sorceries in Main1/Main2 if target is available,
|
||||
// but definitely not for most Instants)
|
||||
|
||||
@@ -14,6 +14,7 @@ import forge.game.cost.Cost;
|
||||
import forge.game.cost.CostPart;
|
||||
import forge.game.cost.CostRemoveCounter;
|
||||
import forge.game.cost.CostTapType;
|
||||
import forge.game.keyword.Keyword;
|
||||
import forge.game.phase.PhaseHandler;
|
||||
import forge.game.phase.PhaseType;
|
||||
import forge.game.player.Player;
|
||||
@@ -35,12 +36,7 @@ public class PumpAi extends PumpAiBase {
|
||||
if (cost == null) {
|
||||
return true;
|
||||
}
|
||||
for (final CostPart part : cost.getCostParts()) {
|
||||
if (part instanceof CostTapType) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return cost.hasSpecificCostType(CostTapType.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -190,7 +186,7 @@ public class PumpAi extends PumpAiBase {
|
||||
srcCardCpy.setCounters(cType, srcCardCpy.getCounters(cType) - amount);
|
||||
|
||||
if (CounterType.P1P1.equals(cType) && srcCardCpy.getNetToughness() <= 0) {
|
||||
if (srcCardCpy.getCounters(cType) > 0 || !card.hasKeyword("Undying")
|
||||
if (srcCardCpy.getCounters(cType) > 0 || !card.hasKeyword(Keyword.UNDYING)
|
||||
|| card.isToken()) {
|
||||
return true;
|
||||
}
|
||||
@@ -243,7 +239,7 @@ public class PumpAi extends PumpAiBase {
|
||||
srcCardCpy.setCounters(cType, srcCardCpy.getCounters(cType) - amount);
|
||||
|
||||
if (CounterType.P1P1.equals(cType) && srcCardCpy.getNetToughness() <= 0) {
|
||||
if (srcCardCpy.getCounters(cType) > 0 || !card.hasKeyword("Undying")
|
||||
if (srcCardCpy.getCounters(cType) > 0 || !card.hasKeyword(Keyword.UNDYING)
|
||||
|| card.isToken()) {
|
||||
return true;
|
||||
}
|
||||
@@ -775,7 +771,7 @@ public class PumpAi extends PumpAiBase {
|
||||
|
||||
if ((sa.getTargetRestrictions() == null) || !sa.getTargetRestrictions().doesTarget()) {
|
||||
if (source.isCreature()) {
|
||||
if (!source.hasKeyword("Indestructible") && source.getNetToughness() + defense <= source.getDamage()) {
|
||||
if (!source.hasKeyword(Keyword.INDESTRUCTIBLE) && source.getNetToughness() + defense <= source.getDamage()) {
|
||||
return false;
|
||||
}
|
||||
if (source.getNetToughness() + defense <= 0) {
|
||||
@@ -864,7 +860,7 @@ public class PumpAi extends PumpAiBase {
|
||||
final Player defPlayer = combat.getDefendingPlayerRelatedTo(source);
|
||||
final boolean defTappedOut = ComputerUtilMana.getAvailableManaEstimate(defPlayer) == 0;
|
||||
|
||||
final boolean isInfect = source.hasKeyword("Infect"); // Flesh-Eater Imp
|
||||
final boolean isInfect = source.hasKeyword(Keyword.INFECT); // Flesh-Eater Imp
|
||||
int lethalDmg = isInfect ? 10 - defPlayer.getPoisonCounters() : defPlayer.getLife();
|
||||
|
||||
if (isInfect && !combat.getDefenderByAttacker(source).canReceiveCounters(CounterType.POISON)) {
|
||||
@@ -970,7 +966,7 @@ public class PumpAi extends PumpAiBase {
|
||||
final Player defPlayer = combat.getDefendingPlayerRelatedTo(source);
|
||||
final boolean defTappedOut = ComputerUtilMana.getAvailableManaEstimate(defPlayer) == 0;
|
||||
|
||||
final boolean isInfect = source.hasKeyword("Infect");
|
||||
final boolean isInfect = source.hasKeyword(Keyword.INFECT);
|
||||
int lethalDmg = isInfect ? 10 - defPlayer.getPoisonCounters() : defPlayer.getLife();
|
||||
|
||||
if (isInfect && !combat.getDefenderByAttacker(source).canReceiveCounters(CounterType.POISON)) {
|
||||
|
||||
@@ -12,6 +12,7 @@ import forge.game.Game;
|
||||
import forge.game.card.*;
|
||||
import forge.game.combat.Combat;
|
||||
import forge.game.combat.CombatUtil;
|
||||
import forge.game.keyword.Keyword;
|
||||
import forge.game.phase.PhaseHandler;
|
||||
import forge.game.phase.PhaseType;
|
||||
import forge.game.phase.Untap;
|
||||
@@ -135,7 +136,7 @@ public abstract class PumpAiBase extends SpellAbilityAi {
|
||||
|| card.getNetCombatDamage() <= 0
|
||||
|| ph.getPhase().isAfter(PhaseType.COMBAT_DECLARE_BLOCKERS)
|
||||
|| ph.getPhase().isBefore(PhaseType.MAIN1)
|
||||
|| CardLists.getNotKeyword(ai.getCreaturesInPlay(), "Defender").isEmpty())) {
|
||||
|| CardLists.getNotKeyword(ai.getCreaturesInPlay(), Keyword.DEFENDER).isEmpty())) {
|
||||
return false;
|
||||
}
|
||||
if (!ph.isPlayerTurn(ai) && (combat == null || !combat.isAttacking(card) || card.getNetCombatDamage() <= 0)) {
|
||||
@@ -193,21 +194,26 @@ public abstract class PumpAiBase extends SpellAbilityAi {
|
||||
return false;
|
||||
}
|
||||
} else if (keyword.endsWith("Flying")) {
|
||||
CardCollectionView attackingFlyer = CardCollection.EMPTY;
|
||||
if (combat != null) {
|
||||
attackingFlyer = CardLists.getKeyword(combat.getAttackers(), Keyword.FLYING);
|
||||
}
|
||||
|
||||
if (ph.isPlayerTurn(opp)
|
||||
&& ph.getPhase() == PhaseType.COMBAT_DECLARE_ATTACKERS
|
||||
&& !CardLists.getKeyword(game.getCombat().getAttackers(), "Flying").isEmpty()
|
||||
&& !card.hasKeyword("Reach")
|
||||
&& !attackingFlyer.isEmpty()
|
||||
&& !card.hasKeyword(Keyword.REACH)
|
||||
&& CombatUtil.canBlock(card)
|
||||
&& ComputerUtilCombat.lifeInDanger(ai, game.getCombat())) {
|
||||
return true;
|
||||
}
|
||||
Predicate<Card> flyingOrReach = Predicates.or(CardPredicates.hasKeyword("Flying"), CardPredicates.hasKeyword("Reach"));
|
||||
Predicate<Card> flyingOrReach = Predicates.or(CardPredicates.hasKeyword(Keyword.FLYING), CardPredicates.hasKeyword(Keyword.REACH));
|
||||
if (ph.isPlayerTurn(opp) && combat != null
|
||||
&& Iterables.any(combat.getAttackers(), CardPredicates.hasKeyword("Flying"))
|
||||
&& !attackingFlyer.isEmpty()
|
||||
&& CombatUtil.canBlock(card)) {
|
||||
// Use defensively to destroy the opposing Flying creature when possible, or to block with an indestructible
|
||||
// creature buffed with Flying
|
||||
for (Card c : CardLists.filter(combat.getAttackers(), CardPredicates.hasKeyword("Flying"))) {
|
||||
for (Card c : attackingFlyer) {
|
||||
if (!ComputerUtilCombat.attackerCantBeDestroyedInCombat(c.getController(), c)
|
||||
&& (card.getNetPower() >= c.getNetToughness() && card.getNetToughness() > c.getNetPower()
|
||||
|| ComputerUtilCombat.attackerCantBeDestroyedInCombat(ai, card))) {
|
||||
@@ -225,7 +231,7 @@ public abstract class PumpAiBase extends SpellAbilityAi {
|
||||
} else if (keyword.endsWith("Horsemanship")) {
|
||||
if (ph.isPlayerTurn(opp)
|
||||
&& ph.getPhase().equals(PhaseType.COMBAT_DECLARE_ATTACKERS)
|
||||
&& !CardLists.getKeyword(game.getCombat().getAttackers(), "Horsemanship").isEmpty()
|
||||
&& !CardLists.getKeyword(game.getCombat().getAttackers(), Keyword.HORSEMANSHIP).isEmpty()
|
||||
&& CombatUtil.canBlock(card)
|
||||
&& ComputerUtilCombat.lifeInDanger(ai, game.getCombat())) {
|
||||
return true;
|
||||
@@ -234,7 +240,7 @@ public abstract class PumpAiBase extends SpellAbilityAi {
|
||||
|| ph.getPhase().isAfter(PhaseType.COMBAT_DECLARE_ATTACKERS)
|
||||
|| newPower <= 0
|
||||
|| CardLists.getNotKeyword(CardLists.filter(opp.getCreaturesInPlay(), CardPredicates.possibleBlockers(card)),
|
||||
"Horsemanship").isEmpty()) {
|
||||
Keyword.HORSEMANSHIP).isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
} else if (keyword.endsWith("Intimidate")) {
|
||||
@@ -298,7 +304,7 @@ public abstract class PumpAiBase extends SpellAbilityAi {
|
||||
return false;
|
||||
}
|
||||
} else if (keyword.equals("First Strike")) {
|
||||
if (card.hasKeyword("Double Strike")) {
|
||||
if (card.hasKeyword(Keyword.DOUBLE_STRIKE)) {
|
||||
return false;
|
||||
}
|
||||
if (combat != null && combat.isBlocked(card) && !combat.getBlockers(card).isEmpty()) {
|
||||
@@ -338,7 +344,7 @@ public abstract class PumpAiBase extends SpellAbilityAi {
|
||||
|| newPower <= 0
|
||||
|| ph.getPhase().isAfter(PhaseType.COMBAT_DECLARE_ATTACKERS)
|
||||
|| CardLists.getNotKeyword(CardLists.filter(opp.getCreaturesInPlay(), CardPredicates.possibleBlockers(card)),
|
||||
"Flanking").isEmpty()) {
|
||||
Keyword.FLANKING).isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
} else if (keyword.startsWith("Trample")) {
|
||||
@@ -353,7 +359,7 @@ public abstract class PumpAiBase extends SpellAbilityAi {
|
||||
if (newPower <= 0) {
|
||||
return false;
|
||||
}
|
||||
if (combat != null && combat.isBlocking(card) && !card.hasKeyword("Wither")) {
|
||||
if (combat != null && combat.isBlocking(card) && !card.hasKeyword(Keyword.WITHER)) {
|
||||
return true;
|
||||
}
|
||||
if ((ph.isPlayerTurn(opp))
|
||||
@@ -362,7 +368,7 @@ public abstract class PumpAiBase extends SpellAbilityAi {
|
||||
return false;
|
||||
}
|
||||
} else if (keyword.endsWith("Wither")) {
|
||||
if (newPower <= 0 || card.hasKeyword("Infect")) {
|
||||
if (newPower <= 0 || card.hasKeyword(Keyword.INFECT)) {
|
||||
return false;
|
||||
}
|
||||
return combat != null && ( combat.isBlocking(card) || (combat.isAttacking(card) && combat.isBlocked(card)) );
|
||||
@@ -375,14 +381,14 @@ public abstract class PumpAiBase extends SpellAbilityAi {
|
||||
if (ph.isPlayerTurn(opp) || !CombatUtil.canAttack(card, opp)
|
||||
|| newPower <= 0
|
||||
|| ph.getPhase().isAfter(PhaseType.COMBAT_DECLARE_ATTACKERS)
|
||||
|| CardLists.getNotKeyword(opp.getCreaturesInPlay(), "Defender").isEmpty()) {
|
||||
|| CardLists.getNotKeyword(opp.getCreaturesInPlay(), Keyword.DEFENDER).isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
} else if (keyword.equals("Reach")) {
|
||||
if (ph.isPlayerTurn(ai)
|
||||
|| !ph.getPhase().equals(PhaseType.COMBAT_DECLARE_ATTACKERS)
|
||||
|| CardLists.getKeyword(game.getCombat().getAttackers(), "Flying").isEmpty()
|
||||
|| card.hasKeyword("Flying")
|
||||
|| CardLists.getKeyword(game.getCombat().getAttackers(), Keyword.FLYING).isEmpty()
|
||||
|| card.hasKeyword(Keyword.FLYING)
|
||||
|| !CombatUtil.canBlock(card)) {
|
||||
return false;
|
||||
}
|
||||
@@ -409,7 +415,7 @@ public abstract class PumpAiBase extends SpellAbilityAi {
|
||||
return false;
|
||||
}
|
||||
} else if (keyword.equals("Persist")) {
|
||||
if (card.getBaseToughness() <= 1 || card.hasKeyword("Undying")) {
|
||||
if (card.getBaseToughness() <= 1 || card.hasKeyword(Keyword.UNDYING)) {
|
||||
return false;
|
||||
}
|
||||
} else if (keyword.equals("Islandwalk")) {
|
||||
@@ -445,7 +451,7 @@ public abstract class PumpAiBase extends SpellAbilityAi {
|
||||
return false;
|
||||
}
|
||||
} else if (keyword.endsWith("CARDNAME can attack as though it didn't have defender.")) {
|
||||
if (!ph.isPlayerTurn(ai) || !card.hasKeyword("Defender")
|
||||
if (!ph.isPlayerTurn(ai) || !card.hasKeyword(Keyword.DEFENDER)
|
||||
|| ph.getPhase().isAfter(PhaseType.COMBAT_BEGIN)
|
||||
|| card.isTapped() || newPower <= 0) {
|
||||
return false;
|
||||
@@ -510,7 +516,7 @@ public abstract class PumpAiBase extends SpellAbilityAi {
|
||||
if (c.getSVar("Targeting").equals("Dies") || c.getNetToughness() <= -defense) {
|
||||
return true; // can kill indestructible creatures
|
||||
}
|
||||
return (ComputerUtilCombat.getDamageToKill(c) <= -defense && !c.hasKeyword("Indestructible"));
|
||||
return (ComputerUtilCombat.getDamageToKill(c) <= -defense && !c.hasKeyword(Keyword.INDESTRUCTIBLE));
|
||||
}
|
||||
}); // leaves all creatures that will be destroyed
|
||||
} // -X/-X end
|
||||
|
||||
@@ -14,6 +14,7 @@ import forge.game.card.CardCollection;
|
||||
import forge.game.card.CardLists;
|
||||
import forge.game.combat.Combat;
|
||||
import forge.game.cost.Cost;
|
||||
import forge.game.keyword.Keyword;
|
||||
import forge.game.phase.PhaseType;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
@@ -85,7 +86,7 @@ public class PumpAllAi extends PumpAiBase {
|
||||
if (c.getNetToughness() <= -defense) {
|
||||
return true; // can kill indestructible creatures
|
||||
}
|
||||
return ((ComputerUtilCombat.getDamageToKill(c) <= -defense) && !c.hasKeyword("Indestructible"));
|
||||
return ((ComputerUtilCombat.getDamageToKill(c) <= -defense) && !c.hasKeyword(Keyword.INDESTRUCTIBLE));
|
||||
}
|
||||
}); // leaves all creatures that will be destroyed
|
||||
human = CardLists.filter(human, new Predicate<Card>() {
|
||||
@@ -94,7 +95,7 @@ public class PumpAllAi extends PumpAiBase {
|
||||
if (c.getNetToughness() <= -defense) {
|
||||
return true; // can kill indestructible creatures
|
||||
}
|
||||
return ((ComputerUtilCombat.getDamageToKill(c) <= -defense) && !c.hasKeyword("Indestructible"));
|
||||
return ((ComputerUtilCombat.getDamageToKill(c) <= -defense) && !c.hasKeyword(Keyword.INDESTRUCTIBLE));
|
||||
}
|
||||
}); // leaves all creatures that will be destroyed
|
||||
} // -X/-X end
|
||||
|
||||
@@ -8,6 +8,7 @@ import forge.game.ability.AbilityUtils;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.card.CardLists;
|
||||
import forge.game.card.CardPredicates;
|
||||
import forge.game.keyword.Keyword;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.player.PlayerActionConfirmMode;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
@@ -82,7 +83,7 @@ public class SacrificeAi extends SpellAbilityAi {
|
||||
if (!destroy) {
|
||||
list = CardLists.filter(list, CardPredicates.canBeSacrificedBy(sa));
|
||||
} else {
|
||||
if (!CardLists.getKeyword(list, "Indestructible").isEmpty()) {
|
||||
if (!CardLists.getKeyword(list, Keyword.INDESTRUCTIBLE).isEmpty()) {
|
||||
// human can choose to destroy indestructibles
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ import forge.game.combat.Combat;
|
||||
import forge.game.cost.CostPart;
|
||||
import forge.game.cost.CostPutCounter;
|
||||
import forge.game.cost.CostRemoveCounter;
|
||||
import forge.game.keyword.Keyword;
|
||||
import forge.game.phase.PhaseHandler;
|
||||
import forge.game.phase.PhaseType;
|
||||
import forge.game.player.Player;
|
||||
@@ -201,7 +202,8 @@ public class TokenAi extends SpellAbilityAi {
|
||||
sa.getTargets().add(ai);
|
||||
} else {
|
||||
// Flash Foliage
|
||||
CardCollection list = CardLists.filterControlledBy(game.getCardsIn(ZoneType.Battlefield), ai.getOpponents());
|
||||
CardCollection list = CardLists.filterControlledBy(game.getCardsIn(ZoneType.Battlefield),
|
||||
ai.getOpponents());
|
||||
list = CardLists.getValidCards(list, tgt.getValidTgts(), source.getController(), source, sa);
|
||||
list = CardLists.getTargetableCards(list, sa);
|
||||
CardCollection betterList = CardLists.filter(list, new Predicate<Card>() {
|
||||
@@ -213,7 +215,7 @@ public class TokenAi extends SpellAbilityAi {
|
||||
if (!betterList.isEmpty()) {
|
||||
list = betterList;
|
||||
}
|
||||
betterList = CardLists.getNotKeyword(list, "Trample");
|
||||
betterList = CardLists.getNotKeyword(list, Keyword.TRAMPLE);
|
||||
if (!betterList.isEmpty()) {
|
||||
list = betterList;
|
||||
}
|
||||
@@ -222,7 +224,6 @@ public class TokenAi extends SpellAbilityAi {
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -140,13 +140,13 @@ public class SpellAbilityPicker {
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isSorcerySpeed(SpellAbility sa) {
|
||||
private static boolean isSorcerySpeed(SpellAbility sa, Player player) {
|
||||
// TODO: Can we use the actual rules engine for this instead of trying to do the logic ourselves?
|
||||
if (sa instanceof PlayLandAbility) {
|
||||
return false;
|
||||
}
|
||||
if (sa.isSpell()) {
|
||||
return !sa.getHostCard().isInstant() && !sa.getHostCard().hasKeyword("Flash");
|
||||
return !sa.getHostCard().isInstant() && !sa.getHostCard().withFlash(player);
|
||||
}
|
||||
if (sa.getRestrictions().isPwAbility()) {
|
||||
return !sa.getHostCard().hasKeyword("CARDNAME's loyalty abilities can be activated at instant speed.");
|
||||
@@ -167,7 +167,7 @@ public class SpellAbilityPicker {
|
||||
if (currentPhase.isBefore(PhaseType.COMBAT_DECLARE_BLOCKERS)) {
|
||||
List<SpellAbility> candidateSAs2 = new ArrayList<SpellAbility>();
|
||||
for (SpellAbility sa : candidateSAs) {
|
||||
if (!isSorcerySpeed(sa)) {
|
||||
if (!isSorcerySpeed(sa, player)) {
|
||||
System.err.println("Not sorcery: " + sa);
|
||||
candidateSAs2.add(sa);
|
||||
}
|
||||
|
||||
@@ -1,11 +1,22 @@
|
||||
package forge.util;
|
||||
|
||||
public abstract class Visitor<T> {
|
||||
/**
|
||||
* visit the object
|
||||
* the Visitor should return true it can be visit again
|
||||
* returning false means the outer function can stop
|
||||
*
|
||||
* @param object
|
||||
* @return boolean
|
||||
*/
|
||||
public abstract boolean visit(T object);
|
||||
|
||||
public void visitAll(Iterable<? extends T> objects) {
|
||||
public boolean visitAll(Iterable<? extends T> objects) {
|
||||
for (T obj : objects) {
|
||||
visit(obj);
|
||||
if (!visit(obj)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -527,25 +527,57 @@ public class Game {
|
||||
return cards;
|
||||
}
|
||||
|
||||
private static class CardStateVisitor extends Visitor<Card> {
|
||||
Card found = null;
|
||||
Card old = null;
|
||||
|
||||
private CardStateVisitor(final Card card) {
|
||||
this.old = card;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(Card object) {
|
||||
if (object.equals(old)) {
|
||||
found = object;
|
||||
}
|
||||
return found == null;
|
||||
}
|
||||
|
||||
public Card getFound() {
|
||||
return found == null ? found : old;
|
||||
}
|
||||
}
|
||||
|
||||
public Card getCardState(final Card card) {
|
||||
for (final Card c : getCardsInGame()) {
|
||||
if (card.equals(c)) {
|
||||
return c;
|
||||
}
|
||||
}
|
||||
return card;
|
||||
CardStateVisitor visit = new CardStateVisitor(card);
|
||||
this.forEachCardInGame(visit);
|
||||
return visit.getFound();
|
||||
}
|
||||
|
||||
// Allows visiting cards in game without allocating a temporary list.
|
||||
public void forEachCardInGame(Visitor<Card> visitor) {
|
||||
for (final Player player : getPlayers()) {
|
||||
visitor.visitAll(player.getZone(ZoneType.Graveyard).getCards());
|
||||
visitor.visitAll(player.getZone(ZoneType.Hand).getCards());
|
||||
visitor.visitAll(player.getZone(ZoneType.Library).getCards());
|
||||
visitor.visitAll(player.getZone(ZoneType.Battlefield).getCards(false));
|
||||
visitor.visitAll(player.getZone(ZoneType.Exile).getCards());
|
||||
visitor.visitAll(player.getZone(ZoneType.Command).getCards());
|
||||
visitor.visitAll(player.getInboundTokens());
|
||||
if (!visitor.visitAll(player.getZone(ZoneType.Graveyard).getCards())) {
|
||||
return;
|
||||
}
|
||||
if (!visitor.visitAll(player.getZone(ZoneType.Hand).getCards())) {
|
||||
return;
|
||||
}
|
||||
if (!visitor.visitAll(player.getZone(ZoneType.Library).getCards())) {
|
||||
return;
|
||||
}
|
||||
if (!visitor.visitAll(player.getZone(ZoneType.Battlefield).getCards(false))) {
|
||||
return;
|
||||
}
|
||||
if (!visitor.visitAll(player.getZone(ZoneType.Exile).getCards())) {
|
||||
return;
|
||||
}
|
||||
if (!visitor.visitAll(player.getZone(ZoneType.Command).getCards())) {
|
||||
return;
|
||||
}
|
||||
if (!visitor.visitAll(player.getInboundTokens())) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
visitor.visitAll(getStackZone().getCards());
|
||||
}
|
||||
|
||||
@@ -236,7 +236,11 @@ public class CardLists {
|
||||
return CardLists.filter(cardList, CardPredicates.isTargetableBy(source));
|
||||
}
|
||||
|
||||
public static CardCollection getKeyword(Iterable<Card> cardList, String keyword) {
|
||||
public static CardCollection getKeyword(Iterable<Card> cardList, final String keyword) {
|
||||
return CardLists.filter(cardList, CardPredicates.hasKeyword(keyword));
|
||||
}
|
||||
|
||||
public static CardCollection getKeyword(Iterable<Card> cardList, final Keyword keyword) {
|
||||
return CardLists.filter(cardList, CardPredicates.hasKeyword(keyword));
|
||||
}
|
||||
|
||||
@@ -244,6 +248,10 @@ public class CardLists {
|
||||
return CardLists.filter(cardList, Predicates.not(CardPredicates.hasKeyword(keyword)));
|
||||
}
|
||||
|
||||
public static CardCollection getNotKeyword(Iterable<Card> cardList, final Keyword keyword) {
|
||||
return CardLists.filter(cardList, Predicates.not(CardPredicates.hasKeyword(keyword)));
|
||||
}
|
||||
|
||||
public static int getAmountOfKeyword(final Iterable<Card> cardList, final String keyword) {
|
||||
int nKeyword = 0;
|
||||
for (final Card c : cardList) {
|
||||
|
||||
@@ -23,6 +23,7 @@ import com.google.common.base.Function;
|
||||
import com.google.common.base.Predicate;
|
||||
|
||||
import forge.game.combat.CombatUtil;
|
||||
import forge.game.keyword.Keyword;
|
||||
import forge.game.keyword.KeywordInterface;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
@@ -84,6 +85,15 @@ public final class CardPredicates {
|
||||
};
|
||||
}
|
||||
|
||||
public static final Predicate<Card> hasKeyword(final Keyword keyword) {
|
||||
return new Predicate<Card>() {
|
||||
@Override
|
||||
public boolean apply(final Card c) {
|
||||
return c.hasKeyword(keyword);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static final Predicate<Card> containsKeyword(final String keyword) {
|
||||
return new Predicate<Card>() {
|
||||
@Override
|
||||
|
||||
@@ -8,6 +8,7 @@ import forge.card.mana.ManaCost;
|
||||
import forge.game.Direction;
|
||||
import forge.game.GameEntityView;
|
||||
import forge.game.combat.Combat;
|
||||
import forge.game.keyword.Keyword;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.player.PlayerView;
|
||||
import forge.game.zone.ZoneType;
|
||||
@@ -978,11 +979,11 @@ public class CardView extends GameEntityView {
|
||||
}
|
||||
void updateKeywords(Card c, CardState state) {
|
||||
c.updateKeywordsCache(state);
|
||||
set(TrackableProperty.HasDeathtouch, c.hasKeyword("Deathtouch", state));
|
||||
set(TrackableProperty.HasHaste, c.hasKeyword("Haste", state));
|
||||
set(TrackableProperty.HasInfect, c.hasKeyword("Infect", state));
|
||||
set(TrackableProperty.HasStorm, c.hasKeyword("Storm", state));
|
||||
set(TrackableProperty.HasTrample, c.hasKeyword("Trample", state));
|
||||
set(TrackableProperty.HasDeathtouch, c.hasKeyword(Keyword.DEATHTOUCH, state));
|
||||
set(TrackableProperty.HasHaste, c.hasKeyword(Keyword.HASTE, state));
|
||||
set(TrackableProperty.HasInfect, c.hasKeyword(Keyword.INFECT, state));
|
||||
set(TrackableProperty.HasStorm, c.hasKeyword(Keyword.STORM, state));
|
||||
set(TrackableProperty.HasTrample, c.hasKeyword(Keyword.TRAMPLE, state));
|
||||
set(TrackableProperty.BlockAdditional, c.getAmountOfKeyword("CARDNAME can block an additional creature each combat.", state));
|
||||
updateAbilityText(c, state);
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import forge.game.card.Card;
|
||||
import forge.game.card.CardCollection;
|
||||
import forge.game.card.CardCollectionView;
|
||||
import forge.game.card.CardLists;
|
||||
import forge.game.keyword.Keyword;
|
||||
|
||||
public class AttackingBand {
|
||||
private CardCollection attackers = new CardCollection();
|
||||
@@ -32,7 +33,7 @@ public class AttackingBand {
|
||||
return false;
|
||||
}
|
||||
|
||||
int bandingCreatures = CardLists.getKeyword(band, "Banding").size();
|
||||
int bandingCreatures = CardLists.getKeyword(band, Keyword.BANDING).size();
|
||||
int neededBandingCreatures = shareDamage ? 1 : band.size() - 1;
|
||||
if (neededBandingCreatures <= bandingCreatures) {
|
||||
// For starting a band, only one can be non-Banding
|
||||
|
||||
@@ -659,7 +659,7 @@ public class CombatUtil {
|
||||
return 0;
|
||||
}
|
||||
// TODO: remove CantBeBlockedByAmount LT2
|
||||
if (attacker.hasKeyword("CantBeBlockedByAmount LT2") || attacker.hasKeyword("Menace")) {
|
||||
if (attacker.hasKeyword("CantBeBlockedByAmount LT2") || attacker.hasKeyword(Keyword.MENACE)) {
|
||||
return 2;
|
||||
} else if (attacker.hasKeyword("CantBeBlockedByAmount LT3")) {
|
||||
return 3;
|
||||
@@ -703,7 +703,7 @@ public class CombatUtil {
|
||||
for (final Card attacker : attackers) {
|
||||
if (CombatUtil.canBlock(attacker, blocker, combat)) {
|
||||
boolean must = true;
|
||||
if (attacker.hasStartOfKeyword("CantBeBlockedByAmount LT") || attacker.hasKeyword("Menace")) {
|
||||
if (attacker.hasStartOfKeyword("CantBeBlockedByAmount LT") || attacker.hasKeyword(Keyword.MENACE)) {
|
||||
final List<Card> possibleBlockers = Lists.newArrayList(defendersArmy);
|
||||
possibleBlockers.remove(blocker);
|
||||
if (!CombatUtil.canBeBlocked(attacker, possibleBlockers, combat)) {
|
||||
@@ -807,7 +807,7 @@ public class CombatUtil {
|
||||
for (final Card attacker : attackersWithLure) {
|
||||
if (CombatUtil.canBeBlocked(attacker, combat, defender) && CombatUtil.canBlock(attacker, blocker)) {
|
||||
boolean canBe = true;
|
||||
if (attacker.hasStartOfKeyword("CantBeBlockedByAmount LT") || attacker.hasKeyword("Menace")) {
|
||||
if (attacker.hasStartOfKeyword("CantBeBlockedByAmount LT") || attacker.hasKeyword(Keyword.MENACE)) {
|
||||
final List<Card> blockers = combat.getDefenderPlayerByAttacker(attacker).getCreaturesInPlay();
|
||||
blockers.remove(blocker);
|
||||
if (!CombatUtil.canBeBlocked(attacker, blockers, combat)) {
|
||||
@@ -825,7 +825,7 @@ public class CombatUtil {
|
||||
if (CombatUtil.canBeBlocked(attacker, combat, defender) && CombatUtil.canBlock(attacker, blocker)
|
||||
&& combat.isAttacking(attacker)) {
|
||||
boolean canBe = true;
|
||||
if (attacker.hasStartOfKeyword("CantBeBlockedByAmount LT") || attacker.hasKeyword("Menace")) {
|
||||
if (attacker.hasStartOfKeyword("CantBeBlockedByAmount LT") || attacker.hasKeyword(Keyword.MENACE)) {
|
||||
final List<Card> blockers = combat.getDefenderPlayerByAttacker(attacker).getCreaturesInPlay();
|
||||
blockers.remove(blocker);
|
||||
if (!CombatUtil.canBeBlocked(attacker, blockers, combat)) {
|
||||
@@ -1004,7 +1004,7 @@ public class CombatUtil {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((attacker.hasKeyword("Creatures with power greater than CARDNAME's power can't block it.") || attacker.hasKeyword("Skulk"))
|
||||
if ((attacker.hasKeyword("Creatures with power greater than CARDNAME's power can't block it.") || attacker.hasKeyword(Keyword.SKULK))
|
||||
&& attacker.getNetPower() < blocker.getNetPower()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@ import forge.game.combat.Combat;
|
||||
import forge.game.combat.CombatUtil;
|
||||
import forge.game.cost.Cost;
|
||||
import forge.game.event.*;
|
||||
import forge.game.keyword.Keyword;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.player.PlayerController.BinaryChoiceType;
|
||||
import forge.game.player.PlayerController.ManaPaymentPurpose;
|
||||
@@ -507,7 +508,7 @@ public class PhaseHandler implements java.io.Serializable {
|
||||
}
|
||||
|
||||
for (final Card attacker : combat.getAttackers()) {
|
||||
final boolean shouldTapForAttack = !attacker.hasKeyword("Vigilance") && !attacker.hasKeyword("Attacking doesn't cause CARDNAME to tap.");
|
||||
final boolean shouldTapForAttack = !attacker.hasKeyword(Keyword.VIGILANCE) && !attacker.hasKeyword("Attacking doesn't cause CARDNAME to tap.");
|
||||
if (shouldTapForAttack) {
|
||||
// set tapped to true without firing triggers because it may affect propaganda costs
|
||||
attacker.setTapped(true);
|
||||
|
||||
@@ -36,6 +36,7 @@ import forge.game.card.Card;
|
||||
import forge.game.card.CardCollection;
|
||||
import forge.game.card.CardLists;
|
||||
import forge.game.card.CardPredicates.Presets;
|
||||
import forge.game.keyword.Keyword;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.player.PlayerController.BinaryChoiceType;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
@@ -257,7 +258,7 @@ public class Untap extends Phase {
|
||||
|
||||
@Override
|
||||
public boolean apply(final Card c) {
|
||||
return ((c.isPhasedOut() && c.isDirectlyPhasedOut()) || c.hasKeyword("Phasing"));
|
||||
return ((c.isPhasedOut() && c.isDirectlyPhasedOut()) || c.hasKeyword(Keyword.PHASING));
|
||||
}
|
||||
});
|
||||
|
||||
@@ -268,7 +269,7 @@ public class Untap extends Phase {
|
||||
for (final Card c : list) {
|
||||
if (c.isPhasedOut()) {
|
||||
c.phase();
|
||||
} else if (c.hasKeyword("Phasing")) {
|
||||
} else if (c.hasKeyword(Keyword.PHASING)) {
|
||||
// 702.23g If an object would simultaneously phase out directly
|
||||
// and indirectly, it just phases out indirectly.
|
||||
if (c.isAura()) {
|
||||
|
||||
@@ -5,6 +5,7 @@ import java.util.Set;
|
||||
|
||||
import forge.card.ColorSet;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.keyword.Keyword;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
|
||||
//class for storing information during a game that is used at the end of the game to determine achievements
|
||||
@@ -28,7 +29,7 @@ public class AchievementTracker {
|
||||
|
||||
public void onSpellResolve(final SpellAbility spell) {
|
||||
final Card card = spell.getHostCard();
|
||||
if (card.hasKeyword("Epic")) {
|
||||
if (card.hasKeyword(Keyword.EPIC)) {
|
||||
challengesCompleted.add("Epic");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -530,7 +530,7 @@ public class Player extends GameEntity implements Comparable<Player> {
|
||||
//String additionalLog = "";
|
||||
source.addDealtDamageToPlayerThisTurn(getName(), amount);
|
||||
|
||||
boolean infect = source.hasKeyword("Infect")
|
||||
boolean infect = source.hasKeyword(Keyword.INFECT)
|
||||
|| hasKeyword("All damage is dealt to you as though its source had infect.");
|
||||
|
||||
if (infect) {
|
||||
|
||||
@@ -35,6 +35,7 @@ import forge.game.cost.Cost;
|
||||
import forge.game.cost.CostPart;
|
||||
import forge.game.cost.CostPartMana;
|
||||
import forge.game.cost.CostRemoveCounter;
|
||||
import forge.game.keyword.Keyword;
|
||||
import forge.game.mana.Mana;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.staticability.StaticAbility;
|
||||
@@ -1606,7 +1607,7 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
|
||||
public boolean tracksManaSpent() {
|
||||
if (hostCard == null || hostCard.getRules() == null) { return false; }
|
||||
|
||||
if (hostCard.hasKeyword("Sunburst")) {
|
||||
if (hostCard.hasKeyword(Keyword.SUNBURST)) {
|
||||
return true;
|
||||
}
|
||||
String text = hostCard.getRules().getOracleText();
|
||||
|
||||
@@ -22,6 +22,7 @@ import forge.StaticData;
|
||||
import forge.assets.FSkinProp;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.card.CardView.CardStateView;
|
||||
import forge.game.keyword.Keyword;
|
||||
import forge.gui.SOverlayUtils;
|
||||
import forge.item.PaperCard;
|
||||
import forge.toolbox.FOverlay;
|
||||
@@ -230,7 +231,7 @@ public enum CardZoomer {
|
||||
if (cardName.isEmpty()) { cardName = thisCard.getCard().getAlternateState().getName(); }
|
||||
|
||||
PaperCard pc = StaticData.instance().getCommonCards().getCard(cardName);
|
||||
boolean isAftermath = pc != null && Card.getCardForUi(pc).hasKeyword("Aftermath");
|
||||
boolean isAftermath = pc != null && Card.getCardForUi(pc).hasKeyword(Keyword.AFTERMATH);
|
||||
|
||||
return thisCard.getCard().isFaceDown() || isSplitRotated ? 0 : isAftermath ? 270 : 90; // rotate Aftermath splits the other way to correctly show the right split (graveyard) half
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ import forge.card.mana.ManaCost;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.card.CardView;
|
||||
import forge.game.card.CardView.CardStateView;
|
||||
import forge.game.keyword.Keyword;
|
||||
import forge.game.card.CounterType;
|
||||
import forge.gui.CardContainer;
|
||||
import forge.item.PaperCard;
|
||||
@@ -434,7 +435,7 @@ public class CardPanel extends SkinnedPanel implements CardContainer, IDisposabl
|
||||
} else {
|
||||
if (!card.isFaceDown()) { // no need to draw mana symbols on face down split cards (e.g. manifested)
|
||||
PaperCard pc = StaticData.instance().getCommonCards().getCard(card.getName());
|
||||
int ofs = pc != null && Card.getCardForUi(pc).hasKeyword("Aftermath") ? -12 : 12;
|
||||
int ofs = pc != null && Card.getCardForUi(pc).hasKeyword(Keyword.AFTERMATH) ? -12 : 12;
|
||||
|
||||
drawManaCost(g, card.getCurrentState().getManaCost(), ofs);
|
||||
drawManaCost(g, card.getAlternateState().getManaCost(), -ofs);
|
||||
|
||||
@@ -25,6 +25,7 @@ import forge.card.mana.ManaCost;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.card.CardView;
|
||||
import forge.game.card.CardView.CardStateView;
|
||||
import forge.game.keyword.Keyword;
|
||||
import forge.game.card.CounterType;
|
||||
import forge.item.IPaperCard;
|
||||
import forge.item.PaperCard;
|
||||
@@ -417,7 +418,7 @@ public class CardRenderer {
|
||||
float dy = manaSymbolSize / 2 + Utils.scale(5);
|
||||
|
||||
PaperCard pc = StaticData.instance().getCommonCards().getCard(card.getName());
|
||||
if (Card.getCardForUi(pc).hasKeyword("Aftermath")){
|
||||
if (Card.getCardForUi(pc).hasKeyword(Keyword.AFTERMATH)){
|
||||
dy *= -1; // flip card costs for Aftermath cards
|
||||
}
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@ import forge.game.card.CardPredicates.Presets;
|
||||
import forge.game.combat.AttackingBand;
|
||||
import forge.game.combat.Combat;
|
||||
import forge.game.combat.CombatUtil;
|
||||
import forge.game.keyword.Keyword;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.player.PlayerView;
|
||||
import forge.game.zone.ZoneType;
|
||||
@@ -313,7 +314,7 @@ public class InputAttack extends InputSyncronizedBase {
|
||||
private boolean isBandingPossible() {
|
||||
final CardCollectionView possibleAttackers = playerAttacks.getCardsIn(ZoneType.Battlefield);
|
||||
for (final Card c : possibleAttackers) {
|
||||
if ((c.hasKeyword("Banding") || c.hasStartOfKeyword("Bands with Other")) &&
|
||||
if ((c.hasKeyword(Keyword.BANDING) || c.hasStartOfKeyword("Bands with Other")) &&
|
||||
CombatUtil.canAttack(c, currentDefender)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -280,7 +280,7 @@ public class PlayerControllerHuman extends PlayerController implements IGameCont
|
||||
map.put(null, damageDealt);
|
||||
} else {
|
||||
final List<CardView> vBlockers = CardView.getCollection(blockers);
|
||||
if ((attacker.hasKeyword("Trample") && defender != null) || (blockers.size() > 1)) {
|
||||
if ((attacker.hasKeyword(Keyword.TRAMPLE) && defender != null) || (blockers.size() > 1)) {
|
||||
final CardView vAttacker = CardView.get(attacker);
|
||||
final GameEntityView vDefender = GameEntityView.get(defender);
|
||||
final Map<CardView, Integer> result = getGui().assignDamage(vAttacker, vBlockers, damageDealt,
|
||||
@@ -2296,7 +2296,7 @@ public class PlayerControllerHuman extends PlayerController implements IGameCont
|
||||
if (forgeCard.isPermanent() && !forgeCard.isAura()) {
|
||||
if (forgeCard.isCreature()) {
|
||||
if (!repeatLast) {
|
||||
if (forgeCard.hasKeyword("Haste")) {
|
||||
if (forgeCard.hasKeyword(Keyword.HASTE)) {
|
||||
lastSummoningSickness = true;
|
||||
} else {
|
||||
lastSummoningSickness = getGui().confirm(forgeCard.getView(),
|
||||
|
||||
Reference in New Issue
Block a user