Merge branch 'cleanup' into 'master'

Cleanup predictDamageTo

See merge request core-developers/forge!5413
This commit is contained in:
Michael Kamensky
2021-09-22 17:46:55 +00:00
14 changed files with 121 additions and 187 deletions

View File

@@ -78,7 +78,6 @@ public class AiProfileUtil {
List<String> lines = FileUtil.readFile(buildFileName(profileName)); List<String> lines = FileUtil.readFile(buildFileName(profileName));
for (String line : lines) { for (String line : lines) {
if (line.startsWith("#") || (line.length() == 0)) { if (line.startsWith("#") || (line.length() == 0)) {
continue; continue;
} }

View File

@@ -576,7 +576,7 @@ public class ComputerUtil {
int count = 0; int count = 0;
while (count < amount) { while (count < amount) {
Card prefCard = ComputerUtil.getCardPreference(ai, source, "SacCost", typeList); Card prefCard = getCardPreference(ai, source, "SacCost", typeList);
if (prefCard == null) { if (prefCard == null) {
prefCard = ComputerUtilCard.getWorstAI(typeList); prefCard = ComputerUtilCard.getWorstAI(typeList);
} }
@@ -1051,7 +1051,7 @@ public class ComputerUtil {
} }
if (card.isCreature() && !cardState.hasKeyword(Keyword.DEFENDER) if (card.isCreature() && !cardState.hasKeyword(Keyword.DEFENDER)
&& (cardState.hasKeyword(Keyword.HASTE) || ComputerUtil.hasACardGivingHaste(ai, true) || sa.isDash())) { && (cardState.hasKeyword(Keyword.HASTE) || hasACardGivingHaste(ai, true) || sa.isDash())) {
return true; return true;
} }
@@ -1556,7 +1556,7 @@ public class ComputerUtil {
sub = sub.getSubAbility(); sub = sub.getSubAbility();
} }
if (sa == null || (sa != spell && sa != sub)) { if (sa == null || (sa != spell && sa != sub)) {
Iterables.addAll(objects, ComputerUtil.predictThreatenedObjects(ai, sa, spell)); Iterables.addAll(objects, predictThreatenedObjects(ai, sa, spell));
} }
if (top) { if (top) {
break; // only evaluate top-stack break; // only evaluate top-stack
@@ -1892,7 +1892,7 @@ public class ComputerUtil {
} }
} }
Iterables.addAll(threatened, ComputerUtil.predictThreatenedObjects(aiPlayer, saviour, topStack.getSubAbility())); Iterables.addAll(threatened, predictThreatenedObjects(aiPlayer, saviour, topStack.getSubAbility()));
return threatened; return threatened;
} }
@@ -1927,7 +1927,7 @@ public class ComputerUtil {
} }
} }
} }
willDieFromSpell = !noStackCheck && ComputerUtil.predictThreatenedObjects(creature.getController(), excludeSa).contains(creature); willDieFromSpell = !noStackCheck && predictThreatenedObjects(creature.getController(), excludeSa).contains(creature);
return willDieInCombat || willDieFromSpell; return willDieInCombat || willDieFromSpell;
} }
@@ -1950,7 +1950,7 @@ public class ComputerUtil {
List<Card> willBeKilled = CardLists.filter(list, new Predicate<Card>() { List<Card> willBeKilled = CardLists.filter(list, new Predicate<Card>() {
@Override @Override
public boolean apply(Card card) { public boolean apply(Card card) {
return card.isCreature() && ComputerUtil.predictCreatureWillDieThisTurn(ai, card, excludeSa); return card.isCreature() && predictCreatureWillDieThisTurn(ai, card, excludeSa);
} }
}); });
list.removeAll(willBeKilled); list.removeAll(willBeKilled);

View File

@@ -375,13 +375,13 @@ public class ComputerUtilCard {
public static Card getBestAI(final Iterable<Card> list) { public static Card getBestAI(final Iterable<Card> list) {
// Get Best will filter by appropriate getBest list if ALL of the list is of that type // Get Best will filter by appropriate getBest list if ALL of the list is of that type
if (Iterables.all(list, CardPredicates.Presets.CREATURES)) { if (Iterables.all(list, CardPredicates.Presets.CREATURES)) {
return ComputerUtilCard.getBestCreatureAI(list); return getBestCreatureAI(list);
} }
if (Iterables.all(list, CardPredicates.Presets.LANDS)) { if (Iterables.all(list, CardPredicates.Presets.LANDS)) {
return getBestLandAI(list); return getBestLandAI(list);
} }
// TODO - Once we get an EvaluatePermanent this should call getBestPermanent() // TODO - Once we get an EvaluatePermanent this should call getBestPermanent()
return ComputerUtilCard.getMostExpensivePermanentAI(list); return getMostExpensivePermanentAI(list);
} }
/** /**
@@ -422,7 +422,7 @@ public class ComputerUtilCard {
int biggestvalue = -1; int biggestvalue = -1;
for (Card card : CardLists.filter(list, CardPredicates.Presets.CREATURES)) { for (Card card : CardLists.filter(list, CardPredicates.Presets.CREATURES)) {
int newvalue = ComputerUtilCard.evaluateCreature(card); int newvalue = evaluateCreature(card);
newvalue += card.isToken() ? tokenBonus : 0; // raise the value of tokens newvalue += card.isToken() ? tokenBonus : 0; // raise the value of tokens
if (biggestvalue < newvalue) { if (biggestvalue < newvalue) {
@@ -453,7 +453,7 @@ public class ComputerUtilCard {
* @return a {@link forge.game.card.Card} object. * @return a {@link forge.game.card.Card} object.
*/ */
public static Card getWorstAI(final Iterable<Card> list) { public static Card getWorstAI(final Iterable<Card> list) {
return ComputerUtilCard.getWorstPermanentAI(list, false, false, false, false); return getWorstPermanentAI(list, false, false, false, false);
} }
/** /**
@@ -956,43 +956,43 @@ public class ComputerUtilCard {
final String logic = sa.getParam("AILogic"); final String logic = sa.getParam("AILogic");
if (logic.equals("MostProminentInHumanDeck")) { if (logic.equals("MostProminentInHumanDeck")) {
chosen.add(ComputerUtilCard.getMostProminentColor(CardLists.filterControlledBy(game.getCardsInGame(), opp), colorChoices)); chosen.add(getMostProminentColor(CardLists.filterControlledBy(game.getCardsInGame(), opp), colorChoices));
} }
else if (logic.equals("MostProminentInComputerDeck")) { else if (logic.equals("MostProminentInComputerDeck")) {
chosen.add(ComputerUtilCard.getMostProminentColor(CardLists.filterControlledBy(game.getCardsInGame(), ai), colorChoices)); chosen.add(getMostProminentColor(CardLists.filterControlledBy(game.getCardsInGame(), ai), colorChoices));
} }
else if (logic.equals("MostProminentDualInComputerDeck")) { else if (logic.equals("MostProminentDualInComputerDeck")) {
List<String> prominence = ComputerUtilCard.getColorByProminence(CardLists.filterControlledBy(game.getCardsInGame(), ai)); List<String> prominence = getColorByProminence(CardLists.filterControlledBy(game.getCardsInGame(), ai));
chosen.add(prominence.get(0)); chosen.add(prominence.get(0));
chosen.add(prominence.get(1)); chosen.add(prominence.get(1));
} }
else if (logic.equals("MostProminentInGame")) { else if (logic.equals("MostProminentInGame")) {
chosen.add(ComputerUtilCard.getMostProminentColor(game.getCardsInGame(), colorChoices)); chosen.add(getMostProminentColor(game.getCardsInGame(), colorChoices));
} }
else if (logic.equals("MostProminentHumanCreatures")) { else if (logic.equals("MostProminentHumanCreatures")) {
CardCollectionView list = opp.getCreaturesInPlay(); CardCollectionView list = opp.getCreaturesInPlay();
if (list.isEmpty()) { if (list.isEmpty()) {
list = CardLists.filter(CardLists.filterControlledBy(game.getCardsInGame(), opp), CardPredicates.Presets.CREATURES); list = CardLists.filter(CardLists.filterControlledBy(game.getCardsInGame(), opp), CardPredicates.Presets.CREATURES);
} }
chosen.add(ComputerUtilCard.getMostProminentColor(list, colorChoices)); chosen.add(getMostProminentColor(list, colorChoices));
} }
else if (logic.equals("MostProminentComputerControls")) { else if (logic.equals("MostProminentComputerControls")) {
chosen.add(ComputerUtilCard.getMostProminentColor(ai.getCardsIn(ZoneType.Battlefield), colorChoices)); chosen.add(getMostProminentColor(ai.getCardsIn(ZoneType.Battlefield), colorChoices));
} }
else if (logic.equals("MostProminentHumanControls")) { else if (logic.equals("MostProminentHumanControls")) {
chosen.add(ComputerUtilCard.getMostProminentColor(opp.getCardsIn(ZoneType.Battlefield), colorChoices)); chosen.add(getMostProminentColor(opp.getCardsIn(ZoneType.Battlefield), colorChoices));
} }
else if (logic.equals("MostProminentPermanent")) { else if (logic.equals("MostProminentPermanent")) {
chosen.add(ComputerUtilCard.getMostProminentColor(game.getCardsIn(ZoneType.Battlefield), colorChoices)); chosen.add(getMostProminentColor(game.getCardsIn(ZoneType.Battlefield), colorChoices));
} }
else if (logic.equals("MostProminentAttackers") && game.getPhaseHandler().inCombat()) { else if (logic.equals("MostProminentAttackers") && game.getPhaseHandler().inCombat()) {
chosen.add(ComputerUtilCard.getMostProminentColor(game.getCombat().getAttackers(), colorChoices)); chosen.add(getMostProminentColor(game.getCombat().getAttackers(), colorChoices));
} }
else if (logic.equals("MostProminentInActivePlayerHand")) { else if (logic.equals("MostProminentInActivePlayerHand")) {
chosen.add(ComputerUtilCard.getMostProminentColor(game.getPhaseHandler().getPlayerTurn().getCardsIn(ZoneType.Hand), colorChoices)); chosen.add(getMostProminentColor(game.getPhaseHandler().getPlayerTurn().getCardsIn(ZoneType.Hand), colorChoices));
} }
else if (logic.equals("MostProminentInComputerDeckButGreen")) { else if (logic.equals("MostProminentInComputerDeckButGreen")) {
List<String> prominence = ComputerUtilCard.getColorByProminence(CardLists.filterControlledBy(game.getCardsInGame(), ai)); List<String> prominence = getColorByProminence(CardLists.filterControlledBy(game.getCardsInGame(), ai));
if (prominence.get(0).equals(MagicColor.Constant.GREEN)) { if (prominence.get(0).equals(MagicColor.Constant.GREEN)) {
chosen.add(prominence.get(1)); chosen.add(prominence.get(1));
} else { } else {
@@ -1097,7 +1097,7 @@ public class ComputerUtilCard {
for (Card attacker : currCombat.getAttackersBlockedBy(c)) { for (Card attacker : currCombat.getAttackersBlockedBy(c)) {
if (attacker.getShieldCount() == 0 && ComputerUtilCombat.attackerWouldBeDestroyed(ai, attacker, currCombat)) { if (attacker.getShieldCount() == 0 && ComputerUtilCombat.attackerWouldBeDestroyed(ai, attacker, currCombat)) {
CardCollection blockers = currCombat.getBlockers(attacker); CardCollection blockers = currCombat.getBlockers(attacker);
ComputerUtilCard.sortByEvaluateCreature(blockers); sortByEvaluateCreature(blockers);
Combat combat = new Combat(ai); Combat combat = new Combat(ai);
combat.addAttacker(attacker, opp); combat.addAttacker(attacker, opp);
for (Card blocker : blockers) { for (Card blocker : blockers) {
@@ -1188,7 +1188,7 @@ public class ComputerUtilCard {
float threat = 0; float threat = 0;
if (c.isCreature()) { if (c.isCreature()) {
// the base value for evaluate creature is 100 // the base value for evaluate creature is 100
threat += (-1 + 1.0f * ComputerUtilCard.evaluateCreature(c) / 100) / costRemoval; threat += (-1 + 1.0f * evaluateCreature(c) / 100) / costRemoval;
if (ai.getLife() > 0 && ComputerUtilCombat.canAttackNextTurn(c)) { if (ai.getLife() > 0 && ComputerUtilCombat.canAttackNextTurn(c)) {
Combat combat = game.getCombat(); Combat combat = game.getCombat();
threat += 1.0f * ComputerUtilCombat.damageIfUnblocked(c, opp, combat, true) / ai.getLife(); threat += 1.0f * ComputerUtilCombat.damageIfUnblocked(c, opp, combat, true) / ai.getLife();
@@ -1335,7 +1335,7 @@ public class ComputerUtilCard {
&& phase.isPlayerTurn(ai) && phase.isPlayerTurn(ai)
&& SpellAbilityAi.isSorcerySpeed(sa) || main1Preferred && SpellAbilityAi.isSorcerySpeed(sa) || main1Preferred
&& power > 0 && power > 0
&& ComputerUtilCard.doesCreatureAttackAI(ai, c)) { && doesCreatureAttackAI(ai, c)) {
return true; return true;
} }
@@ -1360,7 +1360,7 @@ public class ComputerUtilCard {
//create and buff attackers //create and buff attackers
if (phase.getPhase().isBefore(PhaseType.COMBAT_DECLARE_ATTACKERS) && phase.isPlayerTurn(ai) && opp.getLife() > 0) { if (phase.getPhase().isBefore(PhaseType.COMBAT_DECLARE_ATTACKERS) && phase.isPlayerTurn(ai) && opp.getLife() > 0) {
//1. become attacker for whatever reason //1. become attacker for whatever reason
if (!ComputerUtilCard.doesCreatureAttackAI(ai, c) && ComputerUtilCard.doesSpecifiedCreatureAttackAI(ai, pumped)) { if (!doesCreatureAttackAI(ai, c) && doesSpecifiedCreatureAttackAI(ai, pumped)) {
float threat = 1.0f * ComputerUtilCombat.damageIfUnblocked(pumped, opp, combat, true) / opp.getLife(); float threat = 1.0f * ComputerUtilCombat.damageIfUnblocked(pumped, opp, combat, true) / opp.getLife();
if (CardLists.filter(oppCreatures, CardPredicates.possibleBlockers(pumped)).isEmpty()) { if (CardLists.filter(oppCreatures, CardPredicates.possibleBlockers(pumped)).isEmpty()) {
threat *= 2; threat *= 2;
@@ -1407,7 +1407,7 @@ public class ComputerUtilCard {
} }
} }
// combat Haste: only grant it if the creature will attack // combat Haste: only grant it if the creature will attack
if (ComputerUtilCard.doesSpecifiedCreatureAttackAI(ai, pumped)) { if (doesSpecifiedCreatureAttackAI(ai, pumped)) {
combatChance += 0.5f + (0.5f * ComputerUtilCombat.damageIfUnblocked(pumped, opp, combat, true) / opp.getLife()); combatChance += 0.5f + (0.5f * ComputerUtilCombat.damageIfUnblocked(pumped, opp, combat, true) / opp.getLife());
} }
chance += nonCombatChance + combatChance; chance += nonCombatChance + combatChance;
@@ -1416,7 +1416,7 @@ public class ComputerUtilCard {
//3. grant evasive //3. grant evasive
if (!CardLists.filter(oppCreatures, CardPredicates.possibleBlockers(c)).isEmpty()) { if (!CardLists.filter(oppCreatures, CardPredicates.possibleBlockers(c)).isEmpty()) {
if (CardLists.filter(oppCreatures, CardPredicates.possibleBlockers(pumped)).isEmpty() if (CardLists.filter(oppCreatures, CardPredicates.possibleBlockers(pumped)).isEmpty()
&& ComputerUtilCard.doesSpecifiedCreatureAttackAI(ai, pumped)) { && doesSpecifiedCreatureAttackAI(ai, pumped)) {
chance += 0.5f * ComputerUtilCombat.damageIfUnblocked(pumped, opp, combat, true) / opp.getLife(); chance += 0.5f * ComputerUtilCombat.damageIfUnblocked(pumped, opp, combat, true) / opp.getLife();
} }
} }
@@ -1714,7 +1714,7 @@ public class ComputerUtilCard {
} }
final long timestamp2 = c.getGame().getNextTimestamp(); //is this necessary or can the timestamp be re-used? final long timestamp2 = c.getGame().getNextTimestamp(); //is this necessary or can the timestamp be re-used?
pumped.addChangedCardKeywordsInternal(toCopy, null, false, false, timestamp2, 0, true); pumped.addChangedCardKeywordsInternal(toCopy, null, false, false, timestamp2, 0, true);
ComputerUtilCard.applyStaticContPT(ai.getGame(), pumped, new CardCollection(c)); applyStaticContPT(ai.getGame(), pumped, new CardCollection(c));
return pumped; return pumped;
} }
@@ -1794,7 +1794,7 @@ public class ComputerUtilCard {
} }
} }
if (!threatenedTargets.isEmpty()) { if (!threatenedTargets.isEmpty()) {
ComputerUtilCard.sortByEvaluateCreature(threatenedTargets); sortByEvaluateCreature(threatenedTargets);
for (Card c : threatenedTargets) { for (Card c : threatenedTargets) {
if (sa.canAddMoreTarget()) { if (sa.canAddMoreTarget()) {
sa.getTargets().add(c); sa.getTargets().add(c);
@@ -1953,7 +1953,7 @@ public class ComputerUtilCard {
// A special case which checks that this creature will attack if it's the AI's turn // A special case which checks that this creature will attack if it's the AI's turn
if (needsToPlay.equalsIgnoreCase("WillAttack")) { if (needsToPlay.equalsIgnoreCase("WillAttack")) {
if (sa != null && game.getPhaseHandler().isPlayerTurn(sa.getActivatingPlayer())) { if (sa != null && game.getPhaseHandler().isPlayerTurn(sa.getActivatingPlayer())) {
return ComputerUtilCard.doesSpecifiedCreatureAttackAI(sa.getActivatingPlayer(), card) ? return doesSpecifiedCreatureAttackAI(sa.getActivatingPlayer(), card) ?
AiPlayDecision.WillPlay : AiPlayDecision.BadEtbEffects; AiPlayDecision.WillPlay : AiPlayDecision.BadEtbEffects;
} else { } else {
return AiPlayDecision.WillPlay; // not our turn, skip this check for the possible Flash use etc. return AiPlayDecision.WillPlay; // not our turn, skip this check for the possible Flash use etc.

View File

@@ -86,7 +86,7 @@ public class ComputerUtilCombat {
final Iterable<GameEntity> defenders = CombatUtil.getAllPossibleDefenders(attacker.getController()); final Iterable<GameEntity> defenders = CombatUtil.getAllPossibleDefenders(attacker.getController());
return Iterables.any(defenders, new Predicate<GameEntity>() { return Iterables.any(defenders, new Predicate<GameEntity>() {
@Override public boolean apply(final GameEntity input) { @Override public boolean apply(final GameEntity input) {
return ComputerUtilCombat.canAttackNextTurn(attacker, input); return canAttackNextTurn(attacker, input);
} }
}); });
} }
@@ -162,7 +162,7 @@ public class ComputerUtilCombat {
} }
}); });
return ComputerUtilCombat.totalDamageOfBlockers(attacker, list); return totalDamageOfBlockers(attacker, list);
} }
@@ -212,9 +212,9 @@ public class ComputerUtilCombat {
return 0; return 0;
} }
damage += ComputerUtilCombat.predictPowerBonusOfAttacker(attacker, null, combat, withoutAbilities); damage += predictPowerBonusOfAttacker(attacker, null, combat, withoutAbilities);
if (!attacker.hasKeyword(Keyword.INFECT)) { if (!attacker.hasKeyword(Keyword.INFECT)) {
sum = ComputerUtilCombat.predictDamageTo(attacked, damage, attacker, true); sum = predictDamageTo(attacked, damage, attacker, true);
if (attacker.hasKeyword(Keyword.DOUBLE_STRIKE)) { if (attacker.hasKeyword(Keyword.DOUBLE_STRIKE)) {
sum *= 2; sum *= 2;
} }
@@ -237,9 +237,9 @@ public class ComputerUtilCombat {
public static int poisonIfUnblocked(final Card attacker, final Player attacked) { public static int poisonIfUnblocked(final Card attacker, final Player attacked) {
int damage = attacker.getNetCombatDamage(); int damage = attacker.getNetCombatDamage();
int poison = 0; int poison = 0;
damage += ComputerUtilCombat.predictPowerBonusOfAttacker(attacker, null, null, false); damage += predictPowerBonusOfAttacker(attacker, null, null, false);
if (attacker.hasKeyword(Keyword.INFECT)) { if (attacker.hasKeyword(Keyword.INFECT)) {
int pd = ComputerUtilCombat.predictDamageTo(attacked, damage, attacker, true); int pd = predictDamageTo(attacked, damage, attacker, true);
poison += pd; poison += pd;
if (attacker.hasKeyword(Keyword.DOUBLE_STRIKE)) { if (attacker.hasKeyword(Keyword.DOUBLE_STRIKE)) {
poison += pd; poison += pd;
@@ -265,7 +265,7 @@ public class ComputerUtilCombat {
public static int sumDamageIfUnblocked(final Iterable<Card> attackers, final Player attacked) { public static int sumDamageIfUnblocked(final Iterable<Card> attackers, final Player attacked) {
int sum = 0; int sum = 0;
for (final Card attacker : attackers) { for (final Card attacker : attackers) {
sum += ComputerUtilCombat.damageIfUnblocked(attacker, attacked, null, false); sum += damageIfUnblocked(attacker, attacked, null, false);
} }
return sum; return sum;
} }
@@ -284,7 +284,7 @@ public class ComputerUtilCombat {
public static int sumPoisonIfUnblocked(final List<Card> attackers, final Player attacked) { public static int sumPoisonIfUnblocked(final List<Card> attackers, final Player attacked) {
int sum = 0; int sum = 0;
for (final Card attacker : attackers) { for (final Card attacker : attackers) {
sum += ComputerUtilCombat.poisonIfUnblocked(attacker, attacked); sum += poisonIfUnblocked(attacker, attacked);
} }
return sum; return sum;
} }
@@ -313,14 +313,14 @@ public class ComputerUtilCombat {
+ "as though it weren't blocked.")) { + "as though it weren't blocked.")) {
unblocked.add(attacker); unblocked.add(attacker);
} else if (attacker.hasKeyword(Keyword.TRAMPLE) } else if (attacker.hasKeyword(Keyword.TRAMPLE)
&& (ComputerUtilCombat.getAttack(attacker) > ComputerUtilCombat.totalShieldDamage(attacker, blockers))) { && (getAttack(attacker) > totalShieldDamage(attacker, blockers))) {
if (!attacker.hasKeyword(Keyword.INFECT)) { if (!attacker.hasKeyword(Keyword.INFECT)) {
damage += ComputerUtilCombat.getAttack(attacker) - ComputerUtilCombat.totalShieldDamage(attacker, blockers); damage += getAttack(attacker) - totalShieldDamage(attacker, blockers);
} }
} }
} }
damage += ComputerUtilCombat.sumDamageIfUnblocked(unblocked, ai); damage += sumDamageIfUnblocked(unblocked, ai);
if (!ai.canLoseLife()) { if (!ai.canLoseLife()) {
damage = 0; damage = 0;
@@ -358,9 +358,9 @@ public class ComputerUtilCombat {
+ " as though it weren't blocked.")) { + " as though it weren't blocked.")) {
unblocked.add(attacker); unblocked.add(attacker);
} else if (attacker.hasKeyword(Keyword.TRAMPLE) } else if (attacker.hasKeyword(Keyword.TRAMPLE)
&& (ComputerUtilCombat.getAttack(attacker) > ComputerUtilCombat.totalShieldDamage(attacker, blockers))) { && (getAttack(attacker) > totalShieldDamage(attacker, blockers))) {
if (attacker.hasKeyword(Keyword.INFECT)) { if (attacker.hasKeyword(Keyword.INFECT)) {
poison += ComputerUtilCombat.getAttack(attacker) - ComputerUtilCombat.totalShieldDamage(attacker, blockers); poison += getAttack(attacker) - totalShieldDamage(attacker, blockers);
} }
if (attacker.hasKeyword(Keyword.POISONOUS)) { if (attacker.hasKeyword(Keyword.POISONOUS)) {
poison += attacker.getKeywordMagnitude(Keyword.POISONOUS); poison += attacker.getKeywordMagnitude(Keyword.POISONOUS);
@@ -368,7 +368,7 @@ public class ComputerUtilCombat {
} }
} }
poison += ComputerUtilCombat.sumPoisonIfUnblocked(unblocked, ai); poison += sumPoisonIfUnblocked(unblocked, ai);
return ai.getPoisonCounters() + poison; return ai.getPoisonCounters() + poison;
} }
@@ -456,12 +456,12 @@ public class ComputerUtilCombat {
maxTreshold--; maxTreshold--;
} }
if (ComputerUtilCombat.lifeThatWouldRemain(ai, combat) - payment < Math.min(threshold, ai.getLife()) if (lifeThatWouldRemain(ai, combat) - payment < Math.min(threshold, ai.getLife())
&& !ai.cantLoseForZeroOrLessLife()) { && !ai.cantLoseForZeroOrLessLife()) {
return true; return true;
} }
return (ComputerUtilCombat.resultingPoison(ai, combat) > Math.max(7, ai.getPoisonCounters())); return resultingPoison(ai, combat) > Math.max(7, ai.getPoisonCounters());
} }
// Checks if the life of the attacked Player would be reduced // Checks if the life of the attacked Player would be reduced
@@ -475,7 +475,7 @@ public class ComputerUtilCombat {
* @return a boolean. * @return a boolean.
*/ */
public static boolean wouldLoseLife(final Player ai, final Combat combat) { public static boolean wouldLoseLife(final Player ai, final Combat combat) {
return (ComputerUtilCombat.lifeThatWouldRemain(ai, combat) < ai.getLife()); return lifeThatWouldRemain(ai, combat) < ai.getLife();
} }
// Checks if the life of the attacked Player/Planeswalker is in danger // Checks if the life of the attacked Player/Planeswalker is in danger
@@ -497,7 +497,7 @@ public class ComputerUtilCombat {
return false; return false;
} }
final List<Card> threateningCommanders = ComputerUtilCombat.getLifeThreateningCommanders(ai, combat); final List<Card> threateningCommanders = getLifeThreateningCommanders(ai, combat);
// check for creatures that must be blocked // check for creatures that must be blocked
final List<Card> attackers = combat.getAttackersOf(ai); final List<Card> attackers = combat.getAttackersOf(ai);
@@ -515,11 +515,11 @@ public class ComputerUtilCombat {
} }
} }
if (ComputerUtilCombat.lifeThatWouldRemain(ai, combat) - payment < 1 && !ai.cantLoseForZeroOrLessLife()) { if (lifeThatWouldRemain(ai, combat) - payment < 1 && !ai.cantLoseForZeroOrLessLife()) {
return true; return true;
} }
return (ComputerUtilCombat.resultingPoison(ai, combat) > 9); return resultingPoison(ai, combat) > 9;
} }
@@ -543,7 +543,7 @@ public class ComputerUtilCombat {
} }
for (final Card defender : defenders) { for (final Card defender : defenders) {
damage += ComputerUtilCombat.dealsDamageAsBlocker(attacker, defender); damage += dealsDamageAsBlocker(attacker, defender);
} }
return damage; return damage;
} }
@@ -561,7 +561,7 @@ public class ComputerUtilCombat {
} }
for (final Card defender : defenders) { for (final Card defender : defenders) {
damage += ComputerUtilCombat.predictDamageByBlockerWithoutDoubleStrike(attacker, defender); damage += predictDamageByBlockerWithoutDoubleStrike(attacker, defender);
} }
return damage; return damage;
} }
@@ -620,9 +620,9 @@ public class ComputerUtilCombat {
int defenderDamage; int defenderDamage;
if (defender.toughnessAssignsDamage()) { if (defender.toughnessAssignsDamage()) {
defenderDamage = defender.getNetToughness() + ComputerUtilCombat.predictToughnessBonusOfBlocker(attacker, defender, true); defenderDamage = defender.getNetToughness() + predictToughnessBonusOfBlocker(attacker, defender, true);
} else { } else {
defenderDamage = defender.getNetPower() + ComputerUtilCombat.predictPowerBonusOfBlocker(attacker, defender, true); defenderDamage = defender.getNetPower() + predictPowerBonusOfBlocker(attacker, defender, true);
} }
// consider static Damage Prevention // consider static Damage Prevention
@@ -646,7 +646,7 @@ public class ComputerUtilCombat {
int defenderDefense = 0; int defenderDefense = 0;
for (final Card defender : defenders) { for (final Card defender : defenders) {
defenderDefense += ComputerUtilCombat.shieldDamage(attacker, defender); defenderDefense += shieldDamage(attacker, defender);
} }
return defenderDefense; return defenderDefense;
@@ -666,7 +666,7 @@ public class ComputerUtilCombat {
* @return a int. * @return a int.
*/ */
public static int shieldDamage(final Card attacker, final Card blocker) { public static int shieldDamage(final Card attacker, final Card blocker) {
if (ComputerUtilCombat.canDestroyBlockerBeforeFirstStrike(blocker, attacker, false)) { if (canDestroyBlockerBeforeFirstStrike(blocker, attacker, false)) {
return 0; return 0;
} }
@@ -705,10 +705,10 @@ public class ComputerUtilCombat {
*/ */
public static boolean combatantWouldBeDestroyed(Player ai, final Card combatant, Combat combat) { public static boolean combatantWouldBeDestroyed(Player ai, final Card combatant, Combat combat) {
if (combat.isAttacking(combatant)) { if (combat.isAttacking(combatant)) {
return ComputerUtilCombat.attackerWouldBeDestroyed(ai, combatant, combat); return attackerWouldBeDestroyed(ai, combatant, combat);
} }
if (combat.isBlocking(combatant)) { if (combat.isBlocking(combatant)) {
return ComputerUtilCombat.blockerWouldBeDestroyed(ai, combatant, combat); return blockerWouldBeDestroyed(ai, combatant, combat);
} }
return false; return false;
} }
@@ -729,7 +729,7 @@ public class ComputerUtilCombat {
int firstStrikeBlockerDmg = 0; int firstStrikeBlockerDmg = 0;
for (final Card defender : blockers) { for (final Card defender : blockers) {
if (ComputerUtilCombat.canDestroyAttacker(ai, attacker, defender, combat, true) if (canDestroyAttacker(ai, attacker, defender, combat, true)
&& !(defender.hasKeyword(Keyword.WITHER) || defender.hasKeyword(Keyword.INFECT))) { && !(defender.hasKeyword(Keyword.WITHER) || defender.hasKeyword(Keyword.INFECT))) {
return true; return true;
} }
@@ -740,10 +740,10 @@ public class ComputerUtilCombat {
// Consider first strike and double strike // Consider first strike and double strike
if (attacker.hasKeyword(Keyword.FIRST_STRIKE) || attacker.hasKeyword(Keyword.DOUBLE_STRIKE)) { if (attacker.hasKeyword(Keyword.FIRST_STRIKE) || attacker.hasKeyword(Keyword.DOUBLE_STRIKE)) {
return firstStrikeBlockerDmg >= ComputerUtilCombat.getDamageToKill(attacker); return firstStrikeBlockerDmg >= getDamageToKill(attacker);
} }
return ComputerUtilCombat.totalDamageOfBlockers(attacker, blockers) >= ComputerUtilCombat.getDamageToKill(attacker); return totalDamageOfBlockers(attacker, blockers) >= getDamageToKill(attacker);
} }
// Will this trigger trigger? // Will this trigger trigger?
@@ -956,7 +956,7 @@ public class ComputerUtilCombat {
for (final Trigger trigger : theTriggers) { for (final Trigger trigger : theTriggers) {
final Card source = trigger.getHostCard(); final Card source = trigger.getHostCard();
if (!ComputerUtilCombat.combatTriggerWillTrigger(attacker, blocker, trigger, null)) { if (!combatTriggerWillTrigger(attacker, blocker, trigger, null)) {
continue; continue;
} }
@@ -1074,7 +1074,7 @@ public class ComputerUtilCombat {
for (final Trigger trigger : theTriggers) { for (final Trigger trigger : theTriggers) {
final Card source = trigger.getHostCard(); final Card source = trigger.getHostCard();
if (!ComputerUtilCombat.combatTriggerWillTrigger(attacker, blocker, trigger, null)) { if (!combatTriggerWillTrigger(attacker, blocker, trigger, null)) {
continue; continue;
} }
@@ -1089,7 +1089,7 @@ public class ComputerUtilCombat {
continue; continue;
} }
int damage = AbilityUtils.calculateAmount(source, sa.getParam("NumDmg"), sa); int damage = AbilityUtils.calculateAmount(source, sa.getParam("NumDmg"), sa);
toughness -= predictDamageTo(blocker, damage, 0, source, false); toughness -= predictDamageTo(blocker, damage, source, false);
} else } else
// -1/-1 PutCounter triggers // -1/-1 PutCounter triggers
@@ -1216,9 +1216,9 @@ public class ComputerUtilCombat {
// if the defender has first strike and wither the attacker will deal // if the defender has first strike and wither the attacker will deal
// less damage than expected // less damage than expected
if (null != blocker) { if (null != blocker) {
if (ComputerUtilCombat.dealsFirstStrikeDamage(blocker, withoutAbilities, combat) if (dealsFirstStrikeDamage(blocker, withoutAbilities, combat)
&& (blocker.hasKeyword(Keyword.WITHER) || blocker.hasKeyword(Keyword.INFECT)) && (blocker.hasKeyword(Keyword.WITHER) || blocker.hasKeyword(Keyword.INFECT))
&& !ComputerUtilCombat.dealsFirstStrikeDamage(attacker, withoutAbilities, combat) && !dealsFirstStrikeDamage(attacker, withoutAbilities, combat)
&& !attacker.canReceiveCounters(CounterEnumType.M1M1)) { && !attacker.canReceiveCounters(CounterEnumType.M1M1)) {
power -= blocker.getNetCombatDamage(); power -= blocker.getNetCombatDamage();
} }
@@ -1250,7 +1250,7 @@ public class ComputerUtilCombat {
for (final Trigger trigger : theTriggers) { for (final Trigger trigger : theTriggers) {
final Card source = trigger.getHostCard(); final Card source = trigger.getHostCard();
if (!ComputerUtilCombat.combatTriggerWillTrigger(attacker, blocker, trigger, combat)) { if (!combatTriggerWillTrigger(attacker, blocker, trigger, combat)) {
continue; continue;
} }
@@ -1451,7 +1451,7 @@ public class ComputerUtilCombat {
for (final Trigger trigger : theTriggers) { for (final Trigger trigger : theTriggers) {
final Card source = trigger.getHostCard(); final Card source = trigger.getHostCard();
if (!ComputerUtilCombat.combatTriggerWillTrigger(attacker, blocker, trigger, combat)) { if (!combatTriggerWillTrigger(attacker, blocker, trigger, combat)) {
continue; continue;
} }
@@ -1472,7 +1472,7 @@ public class ComputerUtilCombat {
} }
int damage = AbilityUtils.calculateAmount(source, sa.getParam("NumDmg"), sa); int damage = AbilityUtils.calculateAmount(source, sa.getParam("NumDmg"), sa);
toughness -= predictDamageTo(attacker, damage, 0, source, false); toughness -= predictDamageTo(attacker, damage, source, false);
continue; continue;
} else if (ApiType.Pump.equals(sa.getApi())) { } else if (ApiType.Pump.equals(sa.getApi())) {
if (sa.hasParam("Cost")) { if (sa.hasParam("Cost")) {
@@ -1600,8 +1600,8 @@ public class ComputerUtilCombat {
} }
//Check triggers that deal damage or shrink the attacker //Check triggers that deal damage or shrink the attacker
if (ComputerUtilCombat.getDamageToKill(attacker) if (getDamageToKill(attacker)
+ ComputerUtilCombat.predictToughnessBonusOfAttacker(attacker, blocker, combat, withoutAbilities) <= 0) { + predictToughnessBonusOfAttacker(attacker, blocker, combat, withoutAbilities) <= 0) {
return true; return true;
} }
@@ -1613,7 +1613,7 @@ public class ComputerUtilCombat {
for (Trigger trigger : theTriggers) { for (Trigger trigger : theTriggers) {
final Card source = trigger.getHostCard(); final Card source = trigger.getHostCard();
if (!ComputerUtilCombat.combatTriggerWillTrigger(attacker, blocker, trigger, null)) { if (!combatTriggerWillTrigger(attacker, blocker, trigger, null)) {
continue; continue;
} }
SpellAbility sa = trigger.ensureAbility(); SpellAbility sa = trigger.ensureAbility();
@@ -1735,17 +1735,17 @@ public class ComputerUtilCombat {
int attackerDamage; int attackerDamage;
if (blocker.toughnessAssignsDamage()) { if (blocker.toughnessAssignsDamage()) {
defenderDamage = blocker.getNetToughness() defenderDamage = blocker.getNetToughness()
+ ComputerUtilCombat.predictToughnessBonusOfBlocker(attacker, blocker, withoutAbilities); + predictToughnessBonusOfBlocker(attacker, blocker, withoutAbilities);
} else { } else {
defenderDamage = blocker.getNetPower() defenderDamage = blocker.getNetPower()
+ ComputerUtilCombat.predictPowerBonusOfBlocker(attacker, blocker, withoutAbilities); + predictPowerBonusOfBlocker(attacker, blocker, withoutAbilities);
} }
if (attacker.toughnessAssignsDamage()) { if (attacker.toughnessAssignsDamage()) {
attackerDamage = attacker.getNetToughness() attackerDamage = attacker.getNetToughness()
+ ComputerUtilCombat.predictToughnessBonusOfAttacker(attacker, blocker, combat, withoutAbilities, withoutAttackerStaticAbilities); + predictToughnessBonusOfAttacker(attacker, blocker, combat, withoutAbilities, withoutAttackerStaticAbilities);
} else { } else {
attackerDamage = attacker.getNetPower() attackerDamage = attacker.getNetPower()
+ ComputerUtilCombat.predictPowerBonusOfAttacker(attacker, blocker, combat, withoutAbilities, withoutAttackerStaticAbilities); + predictPowerBonusOfAttacker(attacker, blocker, combat, withoutAbilities, withoutAttackerStaticAbilities);
} }
int possibleDefenderPrevention = 0; int possibleDefenderPrevention = 0;
@@ -1762,10 +1762,10 @@ public class ComputerUtilCombat {
return false; return false;
} }
final int defenderLife = ComputerUtilCombat.getDamageToKill(blocker) final int defenderLife = getDamageToKill(blocker)
+ ComputerUtilCombat.predictToughnessBonusOfBlocker(attacker, blocker, withoutAbilities); + predictToughnessBonusOfBlocker(attacker, blocker, withoutAbilities);
final int attackerLife = ComputerUtilCombat.getDamageToKill(attacker) final int attackerLife = getDamageToKill(attacker)
+ ComputerUtilCombat.predictToughnessBonusOfAttacker(attacker, blocker, combat, withoutAbilities, withoutAttackerStaticAbilities); + predictToughnessBonusOfAttacker(attacker, blocker, combat, withoutAbilities, withoutAttackerStaticAbilities);
if (blocker.hasKeyword(Keyword.DOUBLE_STRIKE)) { if (blocker.hasKeyword(Keyword.DOUBLE_STRIKE)) {
if (defenderDamage > 0 && (hasKeyword(blocker, "Deathtouch", withoutAbilities, combat) || attacker.hasSVar("DestroyWhenDamaged"))) { if (defenderDamage > 0 && (hasKeyword(blocker, "Deathtouch", withoutAbilities, combat) || attacker.hasSVar("DestroyWhenDamaged"))) {
@@ -1832,7 +1832,7 @@ public class ComputerUtilCombat {
final List<Card> attackers = combat.getAttackersBlockedBy(blocker); final List<Card> attackers = combat.getAttackersBlockedBy(blocker);
for (Card attacker : attackers) { for (Card attacker : attackers) {
if (ComputerUtilCombat.canDestroyBlocker(ai, blocker, attacker, combat, true) if (canDestroyBlocker(ai, blocker, attacker, combat, true)
&& !(attacker.hasKeyword(Keyword.WITHER) || attacker.hasKeyword(Keyword.INFECT))) { && !(attacker.hasKeyword(Keyword.WITHER) || attacker.hasKeyword(Keyword.INFECT))) {
return true; return true;
} }
@@ -1856,7 +1856,7 @@ public class ComputerUtilCombat {
if (flankingMagnitude >= blocker.getNetToughness()) { if (flankingMagnitude >= blocker.getNetToughness()) {
return true; return true;
} }
if ((flankingMagnitude >= ComputerUtilCombat.getDamageToKill(blocker)) if ((flankingMagnitude >= getDamageToKill(blocker))
&& !blocker.hasKeyword(Keyword.INDESTRUCTIBLE)) { && !blocker.hasKeyword(Keyword.INDESTRUCTIBLE)) {
return true; return true;
} }
@@ -1867,8 +1867,8 @@ public class ComputerUtilCombat {
return false; return false;
} }
if (ComputerUtilCombat.getDamageToKill(blocker) if (getDamageToKill(blocker)
+ ComputerUtilCombat.predictToughnessBonusOfBlocker(attacker, blocker, withoutAbilities) <= 0) { + predictToughnessBonusOfBlocker(attacker, blocker, withoutAbilities) <= 0) {
return true; return true;
} }
@@ -1880,7 +1880,7 @@ public class ComputerUtilCombat {
for (Trigger trigger : theTriggers) { for (Trigger trigger : theTriggers) {
final Card source = trigger.getHostCard(); final Card source = trigger.getHostCard();
if (!ComputerUtilCombat.combatTriggerWillTrigger(attacker, blocker, trigger, null)) { if (!combatTriggerWillTrigger(attacker, blocker, trigger, null)) {
continue; continue;
} }
SpellAbility sa = trigger.ensureAbility(); SpellAbility sa = trigger.ensureAbility();
@@ -1956,17 +1956,17 @@ public class ComputerUtilCombat {
int attackerDamage; int attackerDamage;
if (blocker.toughnessAssignsDamage()) { if (blocker.toughnessAssignsDamage()) {
defenderDamage = blocker.getNetToughness() defenderDamage = blocker.getNetToughness()
+ ComputerUtilCombat.predictToughnessBonusOfBlocker(attacker, blocker, withoutAbilities); + predictToughnessBonusOfBlocker(attacker, blocker, withoutAbilities);
} else { } else {
defenderDamage = blocker.getNetPower() defenderDamage = blocker.getNetPower()
+ ComputerUtilCombat.predictPowerBonusOfBlocker(attacker, blocker, withoutAbilities); + predictPowerBonusOfBlocker(attacker, blocker, withoutAbilities);
} }
if (attacker.toughnessAssignsDamage()) { if (attacker.toughnessAssignsDamage()) {
attackerDamage = attacker.getNetToughness() attackerDamage = attacker.getNetToughness()
+ ComputerUtilCombat.predictToughnessBonusOfAttacker(attacker, blocker, combat, withoutAbilities, withoutAttackerStaticAbilities); + predictToughnessBonusOfAttacker(attacker, blocker, combat, withoutAbilities, withoutAttackerStaticAbilities);
} else { } else {
attackerDamage = attacker.getNetPower() attackerDamage = attacker.getNetPower()
+ ComputerUtilCombat.predictPowerBonusOfAttacker(attacker, blocker, combat, withoutAbilities, withoutAttackerStaticAbilities); + predictPowerBonusOfAttacker(attacker, blocker, combat, withoutAbilities, withoutAttackerStaticAbilities);
} }
int possibleDefenderPrevention = 0; int possibleDefenderPrevention = 0;
@@ -1991,15 +1991,15 @@ public class ComputerUtilCombat {
if (combat != null) { if (combat != null) {
for (Card atkr : combat.getAttackersBlockedBy(blocker)) { for (Card atkr : combat.getAttackersBlockedBy(blocker)) {
if (!atkr.equals(attacker)) { if (!atkr.equals(attacker)) {
attackerDamage += predictDamageTo(blocker, atkr.getNetCombatDamage(), 0, atkr, true); attackerDamage += predictDamageTo(blocker, atkr.getNetCombatDamage(), atkr, true);
} }
} }
} }
final int defenderLife = ComputerUtilCombat.getDamageToKill(blocker) final int defenderLife = getDamageToKill(blocker)
+ ComputerUtilCombat.predictToughnessBonusOfBlocker(attacker, blocker, withoutAbilities); + predictToughnessBonusOfBlocker(attacker, blocker, withoutAbilities);
final int attackerLife = ComputerUtilCombat.getDamageToKill(attacker) final int attackerLife = getDamageToKill(attacker)
+ ComputerUtilCombat.predictToughnessBonusOfAttacker(attacker, blocker, combat, withoutAbilities, withoutAttackerStaticAbilities); + predictToughnessBonusOfAttacker(attacker, blocker, combat, withoutAbilities, withoutAttackerStaticAbilities);
if (attacker.hasKeyword(Keyword.DOUBLE_STRIKE)) { if (attacker.hasKeyword(Keyword.DOUBLE_STRIKE)) {
if (attackerDamage > 0 && (hasKeyword(attacker, "Deathtouch", withoutAbilities, combat) || blocker.hasSVar("DestroyWhenDamaged"))) { if (attackerDamage > 0 && (hasKeyword(attacker, "Deathtouch", withoutAbilities, combat) || blocker.hasSVar("DestroyWhenDamaged"))) {
@@ -2082,8 +2082,7 @@ public class ComputerUtilCombat {
// trample // trample
if (hasTrample) { if (hasTrample) {
int dmgToKill = getEnoughDamageToKill(blocker, dmgCanDeal, attacker, true);
int dmgToKill = ComputerUtilCombat.getEnoughDamageToKill(blocker, dmgCanDeal, attacker, true);
if (dmgCanDeal < dmgToKill) { if (dmgCanDeal < dmgToKill) {
dmgToKill = Math.min(blocker.getLethalDamage(), dmgCanDeal); dmgToKill = Math.min(blocker.getLethalDamage(), dmgCanDeal);
@@ -2113,7 +2112,7 @@ public class ComputerUtilCombat {
Card lastBlocker = null; Card lastBlocker = null;
for (final Card b : block) { for (final Card b : block) {
lastBlocker = b; lastBlocker = b;
final int dmgToKill = ComputerUtilCombat.getEnoughDamageToKill(b, dmgCanDeal, attacker, true); final int dmgToKill = getEnoughDamageToKill(b, dmgCanDeal, attacker, true);
if (dmgToKill <= dmgCanDeal) { if (dmgToKill <= dmgCanDeal) {
damageMap.put(b, dmgToKill); damageMap.put(b, dmgToKill);
dmgCanDeal -= dmgToKill; dmgCanDeal -= dmgToKill;
@@ -2175,7 +2174,7 @@ public class ComputerUtilCombat {
*/ */
public static final int getEnoughDamageToKill(final Card c, final int maxDamage, final Card source, final boolean isCombat, public static final int getEnoughDamageToKill(final Card c, final int maxDamage, final Card source, final boolean isCombat,
final boolean noPrevention) { final boolean noPrevention) {
final int killDamage = c.isPlaneswalker() ? c.getCurrentLoyalty() : ComputerUtilCombat.getDamageToKill(c); final int killDamage = c.isPlaneswalker() ? c.getCurrentLoyalty() : getDamageToKill(c);
if (c.hasKeyword(Keyword.INDESTRUCTIBLE) || c.getShieldCount() > 0) { if (c.hasKeyword(Keyword.INDESTRUCTIBLE) || c.getShieldCount() > 0) {
if (!(source.hasKeyword(Keyword.WITHER) || source.hasKeyword(Keyword.INFECT))) { if (!(source.hasKeyword(Keyword.WITHER) || source.hasKeyword(Keyword.INFECT))) {
@@ -2241,70 +2240,10 @@ public class ComputerUtilCombat {
* a boolean. * a boolean.
* @return a int. * @return a int.
*/ */
public final static int predictDamageTo(final GameEntity target, final int damage, final Card source, final boolean isCombat) {
public final static int predictDamageTo(final Player target, final int damage, final Card source, final boolean isCombat) {
final Game game = target.getGame();
int restDamage = damage;
restDamage = target.staticReplaceDamage(restDamage, source, isCombat);
// Predict replacement effects
for (final Card ca : game.getCardsIn(ZoneType.STATIC_ABILITIES_SOURCE_ZONES)) {
for (final ReplacementEffect re : ca.getReplacementEffects()) {
if (!re.getMode().equals(ReplacementType.DamageDone) ||
(!re.hasParam("PreventionEffect") && !re.hasParam("Prevent"))) {
continue;
}
// Immortal Coil prevents the damage but has a similar negative effect
if ("Immortal Coil".equals(ca.getName())) {
continue;
}
if (!re.matchesValidParam("ValidSource", source)) {
continue;
}
if (!re.matchesValidParam("ValidTarget", target)) {
continue;
}
if (re.hasParam("IsCombat")) {
if (re.getParam("IsCombat").equals("True") != isCombat) {
continue;
}
}
if (re.hasParam("Prevent")) {
return 0;
} else if (re.getOverridingAbility() != null) {
SpellAbility repSA = re.getOverridingAbility();
if (repSA.getApi() == ApiType.ReplaceDamage) {
return Math.max(0, restDamage - AbilityUtils.calculateAmount(ca, repSA.getParam("Amount"), repSA));
}
}
return 0;
}
}
restDamage = target.staticDamagePrevention(restDamage, 0, source, isCombat);
return restDamage;
}
/**
* <p>
* predictDamage.
* </p>
*
* @param damage
* a int.
* @param source
* a {@link forge.game.card.Card} object.
* @param isCombat
* a boolean.
* @return a int.
*/
public final static int predictDamageTo(final Card target, final int damage, final Card source, final boolean isCombat) {
return predictDamageTo(target, damage, 0, source, isCombat); return predictDamageTo(target, damage, 0, source, isCombat);
} }
// This function helps the AI calculate the actual amount of damage an // This function helps the AI calculate the actual amount of damage an
// effect would deal // effect would deal
/** /**
@@ -2322,7 +2261,7 @@ public class ComputerUtilCombat {
* a boolean. * a boolean.
* @return a int. * @return a int.
*/ */
public final static int predictDamageTo(final Card target, final int damage, final int possiblePrevention, final Card source, final boolean isCombat) { public final static int predictDamageTo(final GameEntity target, final int damage, final int possiblePrevention, final Card source, final boolean isCombat) {
int restDamage = damage; int restDamage = damage;
restDamage = target.staticReplaceDamage(restDamage, source, isCombat); restDamage = target.staticReplaceDamage(restDamage, source, isCombat);
@@ -2529,13 +2468,13 @@ public class ComputerUtilCombat {
if (combat.isBlocked(c)) { if (combat.isBlocked(c)) {
for (Card blk : combat.getBlockers(c)) { for (Card blk : combat.getBlockers(c)) {
if (ComputerUtilCombat.blockerWouldBeDestroyed(ai, blk, combat)) { if (blockerWouldBeDestroyed(ai, blk, combat)) {
return true; return true;
} }
} }
} else if (combat.isBlocking(c)) { } else if (combat.isBlocking(c)) {
for (Card atk : combat.getAttackersBlockedBy(c)) { for (Card atk : combat.getAttackersBlockedBy(c)) {
if (ComputerUtilCombat.attackerWouldBeDestroyed(ai, atk, combat)) { if (attackerWouldBeDestroyed(ai, atk, combat)) {
return true; return true;
} }
} }

View File

@@ -63,7 +63,7 @@ public class ComputerUtilMana {
return payManaCost(sa, ai, false, 0, true); return payManaCost(sa, ai, false, 0, true);
} }
private static boolean payManaCost(final SpellAbility sa, final Player ai, final boolean test, final int extraMana, boolean checkPlayable) { private static boolean payManaCost(final SpellAbility sa, final Player ai, final boolean test, final int extraMana, boolean checkPlayable) {
ManaCostBeingPaid cost = ComputerUtilMana.calculateManaCost(sa, test, extraMana); ManaCostBeingPaid cost = calculateManaCost(sa, test, extraMana);
return payManaCost(cost, sa, ai, test, checkPlayable); return payManaCost(cost, sa, ai, test, checkPlayable);
} }
@@ -81,7 +81,7 @@ public class ComputerUtilMana {
* Return the number of colors used for payment for Converge * Return the number of colors used for payment for Converge
*/ */
public static int getConvergeCount(final SpellAbility sa, final Player ai) { public static int getConvergeCount(final SpellAbility sa, final Player ai) {
ManaCostBeingPaid cost = ComputerUtilMana.calculateManaCost(sa, true, 0); ManaCostBeingPaid cost = calculateManaCost(sa, true, 0);
if (payManaCost(cost, sa, ai, true, true)) { if (payManaCost(cost, sa, ai, true, true)) {
return cost.getSunburst(); return cost.getSunburst();
} }
@@ -608,7 +608,7 @@ public class ComputerUtilMana {
} }
// arrange all mana abilities by color produced. // arrange all mana abilities by color produced.
final ListMultimap<Integer, SpellAbility> manaAbilityMap = ComputerUtilMana.groupSourcesByManaColor(ai, true); final ListMultimap<Integer, SpellAbility> manaAbilityMap = groupSourcesByManaColor(ai, true);
if (manaAbilityMap.isEmpty()) { if (manaAbilityMap.isEmpty()) {
refundMana(manaSpentToPay, ai, sa); refundMana(manaSpentToPay, ai, sa);
@@ -617,7 +617,7 @@ public class ComputerUtilMana {
} }
// select which abilities may be used for each shard // select which abilities may be used for each shard
Multimap<ManaCostShard, SpellAbility> sourcesForShards = ComputerUtilMana.groupAndOrderToPayShards(ai, manaAbilityMap, cost); Multimap<ManaCostShard, SpellAbility> sourcesForShards = groupAndOrderToPayShards(ai, manaAbilityMap, cost);
sortManaAbilities(sourcesForShards, sa); sortManaAbilities(sourcesForShards, sa);
@@ -919,7 +919,7 @@ public class ComputerUtilMana {
final SpellAbility sa, final Player ai, final boolean test, final boolean checkPlayable, final SpellAbility sa, final Player ai, final boolean test, final boolean checkPlayable,
List<Mana> manaSpentToPay, final boolean hasConverge, final boolean ignoreColor, final boolean ignoreType) { List<Mana> manaSpentToPay, final boolean hasConverge, final boolean ignoreColor, final boolean ignoreType) {
// arrange all mana abilities by color produced. // arrange all mana abilities by color produced.
final ListMultimap<Integer, SpellAbility> manaAbilityMap = ComputerUtilMana.groupSourcesByManaColor(ai, checkPlayable); final ListMultimap<Integer, SpellAbility> manaAbilityMap = groupSourcesByManaColor(ai, checkPlayable);
if (manaAbilityMap.isEmpty()) { if (manaAbilityMap.isEmpty()) {
// no mana abilities, bailing out // no mana abilities, bailing out
refundMana(manaSpentToPay, ai, sa); refundMana(manaSpentToPay, ai, sa);
@@ -931,7 +931,7 @@ public class ComputerUtilMana {
} }
// select which abilities may be used for each shard // select which abilities may be used for each shard
ListMultimap<ManaCostShard, SpellAbility> sourcesForShards = ComputerUtilMana.groupAndOrderToPayShards(ai, manaAbilityMap, cost); ListMultimap<ManaCostShard, SpellAbility> sourcesForShards = groupAndOrderToPayShards(ai, manaAbilityMap, cost);
if (hasConverge) { if (hasConverge) {
// add extra colors for paying converge // add extra colors for paying converge
final int unpaidColors = cost.getUnpaidColors() + cost.getColorsPaid() ^ ManaCostShard.COLORS_SUPERPOSITION; final int unpaidColors = cost.getUnpaidColors() + cost.getColorsPaid() ^ ManaCostShard.COLORS_SUPERPOSITION;
@@ -1540,7 +1540,7 @@ public class ComputerUtilMana {
for (int i = 0; i < 10; i++) { for (int i = 0; i < 10; i++) {
mCost = ManaCost.combine(mCost, mkCost); mCost = ManaCost.combine(mCost, mkCost);
ManaCostBeingPaid mcbp = new ManaCostBeingPaid(mCost); ManaCostBeingPaid mcbp = new ManaCostBeingPaid(mCost);
if (!ComputerUtilMana.canPayManaCost(mcbp, sa, sa.getActivatingPlayer())) { if (!canPayManaCost(mcbp, sa, sa.getActivatingPlayer())) {
sa.getHostCard().setSVar("NumTimes", "Number$" + i); sa.getHostCard().setSVar("NumTimes", "Number$" + i);
break; break;
} }

View File

@@ -26,7 +26,6 @@ public class CreatureEvaluator implements Function<Card, Integer> {
public int evaluateCreature(final Card c) { public int evaluateCreature(final Card c) {
return evaluateCreature(c, true, true); return evaluateCreature(c, true, true);
} }
public int evaluateCreature(final Card c, final boolean considerPT, final boolean considerCMC) { public int evaluateCreature(final Card c, final boolean considerPT, final boolean considerCMC) {
int value = 80; int value = 80;
if (!c.isToken()) { if (!c.isToken()) {

View File

@@ -70,7 +70,7 @@ public abstract class GameEntity extends GameObject implements IIdentifiable {
// This should be also usable by the AI to forecast an effect (so it must // This should be also usable by the AI to forecast an effect (so it must
// not change the game state) // not change the game state)
public int staticDamagePrevention(final int damage, final int possiblePrevention, final Card source, final boolean isCombat) { public int staticDamagePrevention(int damage, final int possiblePrevention, final Card source, final boolean isCombat) {
if (damage <= 0) { if (damage <= 0) {
return 0; return 0;
} }
@@ -88,6 +88,10 @@ public abstract class GameEntity extends GameObject implements IIdentifiable {
(!re.hasParam("PreventionEffect") && !re.hasParam("Prevent"))) { (!re.hasParam("PreventionEffect") && !re.hasParam("Prevent"))) {
continue; continue;
} }
// Immortal Coil prevents the damage but has a similar negative effect
if ("Immortal Coil".equals(ca.getName())) {
continue;
}
if (!re.matchesValidParam("ValidSource", source)) { if (!re.matchesValidParam("ValidSource", source)) {
continue; continue;
} }
@@ -104,12 +108,13 @@ public abstract class GameEntity extends GameObject implements IIdentifiable {
} else if (re.getOverridingAbility() != null) { } else if (re.getOverridingAbility() != null) {
SpellAbility repSA = re.getOverridingAbility(); SpellAbility repSA = re.getOverridingAbility();
if (repSA.getApi() == ApiType.ReplaceDamage) { if (repSA.getApi() == ApiType.ReplaceDamage) {
return Math.max(0, damage - AbilityUtils.calculateAmount(ca, repSA.getParam("Amount"), repSA)); damage = Math.max(0, damage - AbilityUtils.calculateAmount(ca, repSA.getParam("Amount"), repSA));
}
} }
} else {
return 0; return 0;
} }
} }
}
return Math.max(0, damage - possiblePrevention); return Math.max(0, damage - possiblePrevention);
} }

View File

@@ -5239,13 +5239,13 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
} }
} else if (c.getName().equals("Furnace of Rath")) { } else if (c.getName().equals("Furnace of Rath")) {
if (isCreature()) { if (isCreature()) {
restDamage += restDamage; restDamage *= 2;
} }
} else if (c.getName().equals("Dictate of the Twin Gods")) { } else if (c.getName().equals("Dictate of the Twin Gods")) {
restDamage += restDamage; restDamage += restDamage;
} else if (c.getName().equals("Gratuitous Violence")) { } else if (c.getName().equals("Gratuitous Violence")) {
if (c.getController().equals(source.getController()) && source.isCreature() && isCreature()) { if (c.getController().equals(source.getController()) && source.isCreature() && isCreature()) {
restDamage += restDamage; restDamage *= 2;
} }
} else if (c.getName().equals("Fire Servant")) { } else if (c.getName().equals("Fire Servant")) {
if (c.getController().equals(source.getController()) && source.isRed() if (c.getController().equals(source.getController()) && source.isRed()
@@ -5419,7 +5419,6 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
return (c != null ? c.getImageKey() : ""); return (c != null ? c.getImageKey() : "");
} }
public final boolean isTributed() { return tributed; } public final boolean isTributed() { return tributed; }
public final void setTributed(final boolean b) { public final void setTributed(final boolean b) {

View File

@@ -742,10 +742,10 @@ public class Player extends GameEntity implements Comparable<Player> {
restDamage += 2; restDamage += 2;
} }
} else if (c.getName().equals("Furnace of Rath") || c.getName().equals("Dictate of the Twin Gods")) { } else if (c.getName().equals("Furnace of Rath") || c.getName().equals("Dictate of the Twin Gods")) {
restDamage += restDamage; restDamage *= 2;
} else if (c.getName().equals("Gratuitous Violence")) { } else if (c.getName().equals("Gratuitous Violence")) {
if (c.getController().equals(source.getController()) && source.isCreature()) { if (c.getController().equals(source.getController()) && source.isCreature()) {
restDamage += restDamage; restDamage *= 2;
} }
} else if (c.getName().equals("Fire Servant")) { } else if (c.getName().equals("Fire Servant")) {
if (c.getController().equals(source.getController()) && source.isRed() if (c.getController().equals(source.getController()) && source.isRed()

View File

@@ -139,7 +139,6 @@ public class ReplaceDamage extends ReplacementEffect {
return true; return true;
} }
/* (non-Javadoc) /* (non-Javadoc)
* @see forge.card.replacement.ReplacementEffect#setReplacingObjects(java.util.HashMap, forge.card.spellability.SpellAbility) * @see forge.card.replacement.ReplacementEffect#setReplacingObjects(java.util.HashMap, forge.card.spellability.SpellAbility)
*/ */

View File

@@ -66,8 +66,6 @@ public class ReplaceDraw extends ReplacementEffect {
return true; return true;
} }
/* (non-Javadoc) /* (non-Javadoc)
* @see forge.card.replacement.ReplacementEffect#setReplacingObjects(java.util.HashMap, forge.card.spellability.SpellAbility) * @see forge.card.replacement.ReplacementEffect#setReplacingObjects(java.util.HashMap, forge.card.spellability.SpellAbility)
*/ */

View File

@@ -61,8 +61,6 @@ public class ReplaceDrawCards extends ReplacementEffect {
return true; return true;
} }
/* (non-Javadoc) /* (non-Javadoc)
* @see forge.card.replacement.ReplacementEffect#setReplacingObjects(java.util.HashMap, forge.card.spellability.SpellAbility) * @see forge.card.replacement.ReplacementEffect#setReplacingObjects(java.util.HashMap, forge.card.spellability.SpellAbility)
*/ */

View File

@@ -51,8 +51,6 @@ public class ReplaceMill extends ReplacementEffect {
return true; return true;
} }
/* (non-Javadoc) /* (non-Javadoc)
* @see forge.card.replacement.ReplacementEffect#setReplacingObjects(java.util.HashMap, forge.card.spellability.SpellAbility) * @see forge.card.replacement.ReplacementEffect#setReplacingObjects(java.util.HashMap, forge.card.spellability.SpellAbility)
*/ */