Fix miscalculation of damage shields

This commit is contained in:
tool4EvEr
2021-10-21 22:50:11 +02:00
parent 1deb890f93
commit f1467d5a3e
13 changed files with 49 additions and 59 deletions

View File

@@ -1063,7 +1063,7 @@ public class AiAttackController {
final Card attacker = attackersLeft.get(i); final Card attacker = attackersLeft.get(i);
if (this.aiAggression < 5 && !attacker.hasFirstStrike() && !attacker.hasDoubleStrike() if (this.aiAggression < 5 && !attacker.hasFirstStrike() && !attacker.hasDoubleStrike()
&& ComputerUtilCombat.getTotalFirstStrikeBlockPower(attacker, this.defendingOpponent) && ComputerUtilCombat.getTotalFirstStrikeBlockPower(attacker, this.defendingOpponent)
>= ComputerUtilCombat.getDamageToKill(attacker)) { >= ComputerUtilCombat.getDamageToKill(attacker, false)) {
continue; continue;
} }
@@ -1086,7 +1086,7 @@ public class AiAttackController {
} }
} }
// if enough damage: switch to next planeswalker // if enough damage: switch to next planeswalker
if (damage >= ComputerUtilCombat.getDamageToKill((Card) defender)) { if (damage >= ComputerUtilCombat.getDamageToKill((Card) defender, true)) {
break; break;
} }
} }

View File

@@ -368,7 +368,7 @@ public class AiBlockController {
if (firstStrikeBlockers.size() > 1) { if (firstStrikeBlockers.size() > 1) {
CardLists.sortByPowerDesc(firstStrikeBlockers); CardLists.sortByPowerDesc(firstStrikeBlockers);
for (final Card blocker : firstStrikeBlockers) { for (final Card blocker : firstStrikeBlockers) {
final int damageNeeded = ComputerUtilCombat.getDamageToKill(attacker) final int damageNeeded = ComputerUtilCombat.getDamageToKill(attacker, false)
+ ComputerUtilCombat.predictToughnessBonusOfAttacker(attacker, blocker, combat, false); + ComputerUtilCombat.predictToughnessBonusOfAttacker(attacker, blocker, combat, false);
// if the total damage of the blockgang was not enough // if the total damage of the blockgang was not enough
// without but is enough with this blocker finish the blockgang // without but is enough with this blocker finish the blockgang
@@ -446,7 +446,7 @@ public class AiBlockController {
final int additionalDamage = ComputerUtilCombat.dealsDamageAsBlocker(attacker, blocker); final int additionalDamage = ComputerUtilCombat.dealsDamageAsBlocker(attacker, blocker);
final int absorbedDamage2 = ComputerUtilCombat.getEnoughDamageToKill(blocker, attacker.getNetCombatDamage(), attacker, true); final int absorbedDamage2 = ComputerUtilCombat.getEnoughDamageToKill(blocker, attacker.getNetCombatDamage(), attacker, true);
final int addedValue = ComputerUtilCard.evaluateCreature(blocker); final int addedValue = ComputerUtilCard.evaluateCreature(blocker);
final int damageNeeded = ComputerUtilCombat.getDamageToKill(attacker) final int damageNeeded = ComputerUtilCombat.getDamageToKill(attacker, false)
+ ComputerUtilCombat.predictToughnessBonusOfAttacker(attacker, blocker, combat, false); + ComputerUtilCombat.predictToughnessBonusOfAttacker(attacker, blocker, combat, false);
if ((damageNeeded > currentDamage || CombatUtil.getMinNumBlockersForAttacker(attacker, ai) > blockGang.size()) if ((damageNeeded > currentDamage || CombatUtil.getMinNumBlockersForAttacker(attacker, ai) > blockGang.size())
&& !(damageNeeded > currentDamage + additionalDamage) && !(damageNeeded > currentDamage + additionalDamage)
@@ -486,7 +486,7 @@ public class AiBlockController {
final int additionalDamage2 = ComputerUtilCombat.dealsDamageAsBlocker(attacker, secondBlocker); final int additionalDamage2 = ComputerUtilCombat.dealsDamageAsBlocker(attacker, secondBlocker);
final int absorbedDamage2 = ComputerUtilCombat.getEnoughDamageToKill(secondBlocker, attacker.getNetCombatDamage(), attacker, true); final int absorbedDamage2 = ComputerUtilCombat.getEnoughDamageToKill(secondBlocker, attacker.getNetCombatDamage(), attacker, true);
final int addedValue2 = ComputerUtilCard.evaluateCreature(secondBlocker); final int addedValue2 = ComputerUtilCard.evaluateCreature(secondBlocker);
final int damageNeeded = ComputerUtilCombat.getDamageToKill(attacker) final int damageNeeded = ComputerUtilCombat.getDamageToKill(attacker, false)
+ ComputerUtilCombat.predictToughnessBonusOfAttacker(attacker, secondBlocker, combat, false); + ComputerUtilCombat.predictToughnessBonusOfAttacker(attacker, secondBlocker, combat, false);
List<Card> usableBlockersAsThird = new ArrayList<>(usableBlockers); List<Card> usableBlockersAsThird = new ArrayList<>(usableBlockers);
@@ -774,7 +774,7 @@ public class AiBlockController {
if (blockers.size() > 0) { if (blockers.size() > 0) {
safeBlockers = getSafeBlockers(combat, attacker, blockers); safeBlockers = getSafeBlockers(combat, attacker, blockers);
for (final Card blocker : safeBlockers) { for (final Card blocker : safeBlockers) {
final int damageNeeded = ComputerUtilCombat.getDamageToKill(attacker) final int damageNeeded = ComputerUtilCombat.getDamageToKill(attacker, false)
+ ComputerUtilCombat.predictToughnessBonusOfAttacker(attacker, blocker, combat, false); + ComputerUtilCombat.predictToughnessBonusOfAttacker(attacker, blocker, combat, false);
// Add an additional blocker if the current blockers are not // Add an additional blocker if the current blockers are not
// enough and the new one would deal additional damage // enough and the new one would deal additional damage
@@ -801,7 +801,7 @@ public class AiBlockController {
} }
for (final Card blocker : safeBlockers) { for (final Card blocker : safeBlockers) {
final int damageNeeded = ComputerUtilCombat.getDamageToKill(attacker) final int damageNeeded = ComputerUtilCombat.getDamageToKill(attacker, false)
+ ComputerUtilCombat.predictToughnessBonusOfAttacker(attacker, blocker, combat, false); + ComputerUtilCombat.predictToughnessBonusOfAttacker(attacker, blocker, combat, false);
// Add an additional blocker if the current blockers are not // Add an additional blocker if the current blockers are not
// enough and the new one would deal the remaining damage // enough and the new one would deal the remaining damage

View File

@@ -1687,7 +1687,7 @@ public class ComputerUtil {
} }
if (saviourApi == ApiType.Pump || saviourApi == ApiType.PumpAll) { if (saviourApi == ApiType.Pump || saviourApi == ApiType.PumpAll) {
boolean canSave = ComputerUtilCombat.predictDamageTo(c, dmg - toughness, source, false) < ComputerUtilCombat.getDamageToKill(c); boolean canSave = ComputerUtilCombat.predictDamageTo(c, dmg - toughness, source, false) < ComputerUtilCombat.getDamageToKill(c, false);
if ((!topStack.usesTargeting() && !grantIndestructible && !canSave) if ((!topStack.usesTargeting() && !grantIndestructible && !canSave)
|| (!grantIndestructible && !grantShroud && !canSave)) { || (!grantIndestructible && !grantShroud && !canSave)) {
continue; continue;
@@ -1695,14 +1695,14 @@ public class ComputerUtil {
} }
if (saviourApi == ApiType.PutCounter || saviourApi == ApiType.PutCounterAll) { if (saviourApi == ApiType.PutCounter || saviourApi == ApiType.PutCounterAll) {
boolean canSave = ComputerUtilCombat.predictDamageTo(c, dmg - toughness, source, false) < ComputerUtilCombat.getDamageToKill(c); boolean canSave = ComputerUtilCombat.predictDamageTo(c, dmg - toughness, source, false) < ComputerUtilCombat.getDamageToKill(c, false);
if (!canSave) { if (!canSave) {
continue; continue;
} }
} }
// cannot protect against source // cannot protect against source
if (saviourApi == ApiType.Protection && (ProtectAi.toProtectFrom(source, saviour) == null)) { if (saviourApi == ApiType.Protection && ProtectAi.toProtectFrom(source, saviour) == null) {
continue; continue;
} }
@@ -1712,7 +1712,7 @@ public class ComputerUtil {
continue; continue;
} }
if (ComputerUtilCombat.predictDamageTo(c, dmg, source, false) >= ComputerUtilCombat.getDamageToKill(c)) { if (ComputerUtilCombat.predictDamageTo(c, dmg, source, false) >= ComputerUtilCombat.getDamageToKill(c, false)) {
threatened.add(c); threatened.add(c);
} }
} else if (o instanceof Player) { } else if (o instanceof Player) {
@@ -1739,7 +1739,7 @@ public class ComputerUtil {
if (o instanceof Card) { if (o instanceof Card) {
final Card c = (Card) o; final Card c = (Card) o;
final boolean canRemove = (c.getNetToughness() <= dmg) final boolean canRemove = (c.getNetToughness() <= dmg)
|| (!c.hasKeyword(Keyword.INDESTRUCTIBLE) && c.getShieldCount() == 0 && (dmg >= ComputerUtilCombat.getDamageToKill(c))); || (!c.hasKeyword(Keyword.INDESTRUCTIBLE) && c.getShieldCount() == 0 && (dmg >= ComputerUtilCombat.getDamageToKill(c, false)));
if (!canRemove) { if (!canRemove) {
continue; continue;
} }
@@ -1747,7 +1747,7 @@ public class ComputerUtil {
if (saviourApi == ApiType.Pump || saviourApi == ApiType.PumpAll) { if (saviourApi == ApiType.Pump || saviourApi == ApiType.PumpAll) {
final boolean cantSave = c.getNetToughness() + toughness <= dmg final boolean cantSave = c.getNetToughness() + toughness <= dmg
|| (!c.hasKeyword(Keyword.INDESTRUCTIBLE) && c.getShieldCount() == 0 && !grantIndestructible || (!c.hasKeyword(Keyword.INDESTRUCTIBLE) && c.getShieldCount() == 0 && !grantIndestructible
&& (dmg >= toughness + ComputerUtilCombat.getDamageToKill(c))); && (dmg >= toughness + ComputerUtilCombat.getDamageToKill(c, false)));
if (cantSave && (!topStack.usesTargeting() || !grantShroud)) { if (cantSave && (!topStack.usesTargeting() || !grantShroud)) {
continue; continue;
} }

View File

@@ -1498,7 +1498,7 @@ public class ComputerUtilCard {
} }
if (c.hasKeyword(Keyword.TRAMPLE) || keywords.contains("Trample")) { if (c.hasKeyword(Keyword.TRAMPLE) || keywords.contains("Trample")) {
for (Card b : combat.getBlockers(c)) { for (Card b : combat.getBlockers(c)) {
pumpedDmg -= ComputerUtilCombat.getDamageToKill(b); pumpedDmg -= ComputerUtilCombat.getDamageToKill(b, false);
} }
} else { } else {
pumpedDmg = 0; pumpedDmg = 0;
@@ -1526,7 +1526,7 @@ public class ComputerUtilCard {
if (combat.isBlocked(atk)) { if (combat.isBlocked(atk)) {
// consider Trample damage properly for a blocked creature // consider Trample damage properly for a blocked creature
for (Card blk : combat.getBlockers(atk)) { for (Card blk : combat.getBlockers(atk)) {
totalPowerUnblocked -= ComputerUtilCombat.getDamageToKill(blk); totalPowerUnblocked -= ComputerUtilCombat.getDamageToKill(blk, false);
} }
} }
} }

View File

@@ -164,7 +164,7 @@ public class ComputerUtilCombat {
} }
}); });
return totalDamageOfBlockers(attacker, list); return totalFirstStrikeDamageOfBlockers(attacker, list);
} }
// This function takes Doran and Double Strike into account // This function takes Doran and Double Strike into account
@@ -619,7 +619,7 @@ public class ComputerUtilCombat {
if (flankingMagnitude >= defender.getNetToughness()) { if (flankingMagnitude >= defender.getNetToughness()) {
return 0; return 0;
} }
if ((flankingMagnitude >= (defender.getNetToughness() - defender.getDamage())) if (flankingMagnitude >= (defender.getNetToughness() - defender.getDamage())
&& !defender.hasKeyword(Keyword.INDESTRUCTIBLE)) { && !defender.hasKeyword(Keyword.INDESTRUCTIBLE)) {
return 0; return 0;
} }
@@ -698,7 +698,7 @@ public class ComputerUtilCombat {
final int defBushidoMagnitude = blocker.getKeywordMagnitude(Keyword.BUSHIDO); final int defBushidoMagnitude = blocker.getKeywordMagnitude(Keyword.BUSHIDO);
final int defenderDefense = (blocker.getLethalDamage() - flankingMagnitude) + defBushidoMagnitude; final int defenderDefense = blocker.getLethalDamage() - flankingMagnitude + defBushidoMagnitude;
return defenderDefense; return defenderDefense;
} // shieldDamage } // shieldDamage
@@ -751,10 +751,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 >= getDamageToKill(attacker); return firstStrikeBlockerDmg >= getDamageToKill(attacker, true);
} }
return totalDamageOfBlockers(attacker, blockers) >= getDamageToKill(attacker); return totalDamageOfBlockers(attacker, blockers) >= getDamageToKill(attacker, false);
} }
// Will this trigger trigger? // Will this trigger trigger?
@@ -1611,7 +1611,7 @@ public class ComputerUtilCombat {
} }
//Check triggers that deal damage or shrink the attacker //Check triggers that deal damage or shrink the attacker
if (getDamageToKill(attacker) if (getDamageToKill(attacker, false)
+ predictToughnessBonusOfAttacker(attacker, blocker, combat, withoutAbilities) <= 0) { + predictToughnessBonusOfAttacker(attacker, blocker, combat, withoutAbilities) <= 0) {
return true; return true;
} }
@@ -1763,9 +1763,9 @@ public class ComputerUtilCombat {
return false; return false;
} }
final int defenderLife = getDamageToKill(blocker) final int defenderLife = getDamageToKill(blocker, false)
+ predictToughnessBonusOfBlocker(attacker, blocker, withoutAbilities); + predictToughnessBonusOfBlocker(attacker, blocker, withoutAbilities);
final int attackerLife = getDamageToKill(attacker) final int attackerLife = getDamageToKill(attacker, false)
+ predictToughnessBonusOfAttacker(attacker, blocker, combat, withoutAbilities, withoutAttackerStaticAbilities); + predictToughnessBonusOfAttacker(attacker, blocker, combat, withoutAbilities, withoutAttackerStaticAbilities);
if (blocker.hasKeyword(Keyword.DOUBLE_STRIKE)) { if (blocker.hasKeyword(Keyword.DOUBLE_STRIKE)) {
@@ -1790,13 +1790,11 @@ public class ComputerUtilCombat {
return true; return true;
} }
} // defender double strike } // defender double strike
else { // no double strike for defender else { // no double strike for defender
// Attacker may kill the blocker before he can deal any damage // Attacker may kill the blocker before he can deal any damage
if (dealsFirstStrikeDamage(attacker, withoutAbilities, combat) if (dealsFirstStrikeDamage(attacker, withoutAbilities, combat)
&& !blocker.hasKeyword(Keyword.INDESTRUCTIBLE) && !blocker.hasKeyword(Keyword.INDESTRUCTIBLE)
&& !dealsFirstStrikeDamage(blocker, withoutAbilities, combat)) { && !dealsFirstStrikeDamage(blocker, withoutAbilities, combat)) {
if (attackerDamage >= defenderLife) { if (attackerDamage >= defenderLife) {
return false; return false;
} }
@@ -1810,7 +1808,6 @@ public class ComputerUtilCombat {
} }
return defenderDamage >= attackerLife; return defenderDamage >= attackerLife;
} // defender no double strike } // defender no double strike
return false;// should never arrive here return false;// should never arrive here
} // canDestroyAttacker } // canDestroyAttacker
@@ -1856,7 +1853,7 @@ public class ComputerUtilCombat {
if (flankingMagnitude >= blocker.getNetToughness()) { if (flankingMagnitude >= blocker.getNetToughness()) {
return true; return true;
} }
if ((flankingMagnitude >= getDamageToKill(blocker)) if (flankingMagnitude >= getDamageToKill(blocker, false)
&& !blocker.hasKeyword(Keyword.INDESTRUCTIBLE)) { && !blocker.hasKeyword(Keyword.INDESTRUCTIBLE)) {
return true; return true;
} }
@@ -1867,7 +1864,7 @@ public class ComputerUtilCombat {
return false; return false;
} }
if (getDamageToKill(blocker) if (getDamageToKill(blocker, false)
+ predictToughnessBonusOfBlocker(attacker, blocker, withoutAbilities) <= 0) { + predictToughnessBonusOfBlocker(attacker, blocker, withoutAbilities) <= 0) {
return true; return true;
} }
@@ -1996,9 +1993,9 @@ public class ComputerUtilCombat {
} }
} }
final int defenderLife = getDamageToKill(blocker) final int defenderLife = getDamageToKill(blocker, false)
+ predictToughnessBonusOfBlocker(attacker, blocker, withoutAbilities); + predictToughnessBonusOfBlocker(attacker, blocker, withoutAbilities);
final int attackerLife = getDamageToKill(attacker) final int attackerLife = getDamageToKill(attacker, false)
+ predictToughnessBonusOfAttacker(attacker, blocker, combat, withoutAbilities, withoutAttackerStaticAbilities); + predictToughnessBonusOfAttacker(attacker, blocker, combat, withoutAbilities, withoutAttackerStaticAbilities);
if (attacker.hasKeyword(Keyword.DOUBLE_STRIKE)) { if (attacker.hasKeyword(Keyword.DOUBLE_STRIKE)) {
@@ -2128,7 +2125,7 @@ public class ComputerUtilCombat {
if (dmgCanDeal > 0 ) { // if any damage left undistributed, if (dmgCanDeal > 0 ) { // if any damage left undistributed,
if (hasTrample && isAttacking) // if you have trample, deal damage to defending entity if (hasTrample && isAttacking) // if you have trample, deal damage to defending entity
damageMap.put(null, dmgCanDeal); damageMap.put(null, dmgCanDeal);
else if ( lastBlocker != null ) { // otherwise flush it into last blocker else if (lastBlocker != null) { // otherwise flush it into last blocker
damageMap.put(lastBlocker, dmgCanDeal + damageMap.get(lastBlocker)); damageMap.put(lastBlocker, dmgCanDeal + damageMap.get(lastBlocker));
} }
} }
@@ -2171,7 +2168,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 = getDamageToKill(c); final int killDamage = getDamageToKill(c, false);
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))) {
@@ -2212,8 +2209,8 @@ public class ComputerUtilCombat {
* *
* @return a int. * @return a int.
*/ */
public final static int getDamageToKill(final Card c) { public final static int getDamageToKill(final Card c, boolean withShields) {
int damageShield = c.getPreventNextDamageTotalShields(); int damageShield = withShields ? c.getPreventNextDamageTotalShields() : 0;
int killDamage = (c.isPlaneswalker() ? c.getCurrentLoyalty() : c.getLethalDamage()) + damageShield; int killDamage = (c.isPlaneswalker() ? c.getCurrentLoyalty() : c.getLethalDamage()) + damageShield;
if (killDamage > damageShield if (killDamage > damageShield

View File

@@ -256,7 +256,7 @@ public class DamageAllAi extends SpellAbilityAi {
final Predicate<Card> filterKillable = new Predicate<Card>() { final Predicate<Card> filterKillable = new Predicate<Card>() {
@Override @Override
public boolean apply(final Card c) { public boolean apply(final Card c) {
return (ComputerUtilCombat.predictDamageTo(c, dmg, source, false) >= ComputerUtilCombat.getDamageToKill(c)); return (ComputerUtilCombat.predictDamageTo(c, dmg, source, false) >= ComputerUtilCombat.getDamageToKill(c, false));
} }
}; };

View File

@@ -807,7 +807,7 @@ public class DamageDealAi extends DamageAiBase {
if (o instanceof Card) { if (o instanceof Card) {
Card c = (Card) o; Card c = (Card) o;
final int restDamage = ComputerUtilCombat.predictDamageTo(c, dmg, saMe.getHostCard(), false); final int restDamage = ComputerUtilCombat.predictDamageTo(c, dmg, saMe.getHostCard(), false);
if (!c.hasKeyword(Keyword.INDESTRUCTIBLE) && ComputerUtilCombat.getDamageToKill(c) <= restDamage) { if (!c.hasKeyword(Keyword.INDESTRUCTIBLE) && ComputerUtilCombat.getDamageToKill(c, false) <= restDamage) {
if (c.getController().equals(ai)) { if (c.getController().equals(ai)) {
return false; return false;
} else { } else {

View File

@@ -70,14 +70,11 @@ public class FightAi extends SpellAbilityAi {
} }
Card fighter1 = fighter1List.get(0); Card fighter1 = fighter1List.get(0);
for (Card humanCreature : humCreatures) { for (Card humanCreature : humCreatures) {
if (ComputerUtilCombat.getDamageToKill(humanCreature) <= fighter1.getNetPower() if (canKill(fighter1, humanCreature, 0)
&& humanCreature.getNetPower() < ComputerUtilCombat.getDamageToKill(fighter1)) { && !canKill(humanCreature, fighter1, 0)) {
// todo: check min/max targets; see if we picked the best matchup // todo: check min/max targets; see if we picked the best matchup
sa.getTargets().add(humanCreature); sa.getTargets().add(humanCreature);
return true; return true;
} else if (humanCreature.getSVar("Targeting").equals("Dies")) {
sa.getTargets().add(humanCreature);
return true;
} }
} }
return false; // bail at this point, otherwise the AI will overtarget and waste the activation return false; // bail at this point, otherwise the AI will overtarget and waste the activation
@@ -87,16 +84,12 @@ public class FightAi extends SpellAbilityAi {
if (!(humCreatures.isEmpty() && aiCreatures.isEmpty())) { if (!(humCreatures.isEmpty() && aiCreatures.isEmpty())) {
for (Card humanCreature : humCreatures) { for (Card humanCreature : humCreatures) {
for (Card aiCreature : aiCreatures) { for (Card aiCreature : aiCreatures) {
if (ComputerUtilCombat.getDamageToKill(humanCreature) <= aiCreature.getNetPower() if (canKill(aiCreature, humanCreature, 0)
&& humanCreature.getNetPower() < ComputerUtilCombat.getDamageToKill(aiCreature)) { && !canKill(humanCreature, aiCreature, 0)) {
// todo: check min/max targets; see if we picked the best matchup // todo: check min/max targets; see if we picked the best matchup
sa.getTargets().add(humanCreature); sa.getTargets().add(humanCreature);
sa.getTargets().add(aiCreature); sa.getTargets().add(aiCreature);
return true; return true;
} else if (humanCreature.getSVar("Targeting").equals("Dies")) {
sa.getTargets().add(humanCreature);
sa.getTargets().add(aiCreature);
return true;
} }
} }
} }
@@ -111,8 +104,8 @@ public class FightAi extends SpellAbilityAi {
if (sa.hasParam("TargetsWithoutSameCreatureType") && creature1.sharesCreatureTypeWith(creature2)) { if (sa.hasParam("TargetsWithoutSameCreatureType") && creature1.sharesCreatureTypeWith(creature2)) {
continue; continue;
} }
if (ComputerUtilCombat.getDamageToKill(creature1) <= creature2.getNetPower() if (canKill(creature1, creature2, 0)
&& creature1.getNetPower() >= ComputerUtilCombat.getDamageToKill(creature2)) { && canKill(creature2, creature1, 0)) {
// todo: check min/max targets; see if we picked the best matchup // todo: check min/max targets; see if we picked the best matchup
sa.getTargets().add(creature1); sa.getTargets().add(creature1);
sa.getTargets().add(creature2); sa.getTargets().add(creature2);
@@ -152,14 +145,14 @@ public class FightAi extends SpellAbilityAi {
if (sa.hasParam("Defined")) { if (sa.hasParam("Defined")) {
Card aiCreature = AbilityUtils.getDefinedCards(source, sa.getParam("Defined"), sa).get(0); Card aiCreature = AbilityUtils.getDefinedCards(source, sa.getParam("Defined"), sa).get(0);
for (Card humanCreature : humCreatures) { for (Card humanCreature : humCreatures) {
if (ComputerUtilCombat.getDamageToKill(humanCreature) <= aiCreature.getNetPower() if (canKill(aiCreature, humanCreature, 0)
&& ComputerUtilCard.evaluateCreature(humanCreature) > ComputerUtilCard.evaluateCreature(aiCreature)) { && ComputerUtilCard.evaluateCreature(humanCreature) > ComputerUtilCard.evaluateCreature(aiCreature)) {
sa.getTargets().add(humanCreature); sa.getTargets().add(humanCreature);
return true; return true;
} }
} }
for (Card humanCreature : humCreatures) { for (Card humanCreature : humCreatures) {
if (ComputerUtilCombat.getDamageToKill(aiCreature) > humanCreature.getNetPower()) { if (!canKill(humanCreature, aiCreature, 0)) {
sa.getTargets().add(humanCreature); sa.getTargets().add(humanCreature);
return true; return true;
} }
@@ -312,7 +305,7 @@ public class FightAi extends SpellAbilityAi {
return false; return false;
} }
if (fighter.hasKeyword(Keyword.DEATHTOUCH) if (fighter.hasKeyword(Keyword.DEATHTOUCH)
|| ComputerUtilCombat.getDamageToKill(opponent) <= fighter.getNetPower() + pumpAttack) { || ComputerUtilCombat.getDamageToKill(opponent, true) <= fighter.getNetPower() + pumpAttack) {
return true; return true;
} }
return false; return false;

View File

@@ -136,7 +136,7 @@ public class LifeSetAi extends SpellAbilityAi {
if (tgt.canOnlyTgtOpponent()) { if (tgt.canOnlyTgtOpponent()) {
sa.getTargets().add(opponent); sa.getTargets().add(opponent);
} else { } else {
if ((amount > myLife) && (myLife <= 10)) { if (amount > myLife && myLife <= 10) {
sa.getTargets().add(ai); sa.getTargets().add(ai);
} else if (hlife > amount) { } else if (hlife > amount) {
sa.getTargets().add(opponent); sa.getTargets().add(opponent);

View File

@@ -467,7 +467,7 @@ public abstract class PumpAiBase extends SpellAbilityAi {
if (c.getSVar("Targeting").equals("Dies") || c.getNetToughness() <= -defense) { if (c.getSVar("Targeting").equals("Dies") || c.getNetToughness() <= -defense) {
return true; // can kill indestructible creatures return true; // can kill indestructible creatures
} }
return (ComputerUtilCombat.getDamageToKill(c) <= -defense && !c.hasKeyword(Keyword.INDESTRUCTIBLE)); return ComputerUtilCombat.getDamageToKill(c, false) <= -defense && !c.hasKeyword(Keyword.INDESTRUCTIBLE);
} }
}); // leaves all creatures that will be destroyed }); // leaves all creatures that will be destroyed
} // -X/-X end } // -X/-X end

View File

@@ -90,7 +90,7 @@ public class PumpAllAi extends PumpAiBase {
if (c.getNetToughness() <= -defense) { if (c.getNetToughness() <= -defense) {
return true; // can kill indestructible creatures return true; // can kill indestructible creatures
} }
return ((ComputerUtilCombat.getDamageToKill(c) <= -defense) && !c.hasKeyword(Keyword.INDESTRUCTIBLE)); return ComputerUtilCombat.getDamageToKill(c, false) <= -defense && !c.hasKeyword(Keyword.INDESTRUCTIBLE);
} }
}); // leaves all creatures that will be destroyed }); // leaves all creatures that will be destroyed
human = CardLists.filter(human, new Predicate<Card>() { human = CardLists.filter(human, new Predicate<Card>() {
@@ -99,7 +99,7 @@ public class PumpAllAi extends PumpAiBase {
if (c.getNetToughness() <= -defense) { if (c.getNetToughness() <= -defense) {
return true; // can kill indestructible creatures return true; // can kill indestructible creatures
} }
return ((ComputerUtilCombat.getDamageToKill(c) <= -defense) && !c.hasKeyword(Keyword.INDESTRUCTIBLE)); return ComputerUtilCombat.getDamageToKill(c, false) <= -defense && !c.hasKeyword(Keyword.INDESTRUCTIBLE);
} }
}); // leaves all creatures that will be destroyed }); // leaves all creatures that will be destroyed
} // -X/-X end } // -X/-X end

View File

@@ -24,14 +24,14 @@ public class LifeSetEffect extends SpellAbilityEffect {
if (redistribute) { if (redistribute) {
for (final Player p : getTargetPlayers(sa)) { for (final Player p : getTargetPlayers(sa)) {
if ((tgt == null) || p.canBeTargetedBy(sa)) { if (tgt == null || p.canBeTargetedBy(sa)) {
lifetotals.add(p.getLife()); lifetotals.add(p.getLife());
} }
} }
} }
for (final Player p : getTargetPlayers(sa)) { for (final Player p : getTargetPlayers(sa)) {
if ((tgt == null) || p.canBeTargetedBy(sa)) { if (tgt == null || p.canBeTargetedBy(sa)) {
if (!redistribute) { if (!redistribute) {
p.setLife(lifeAmount, sa.getHostCard()); p.setLife(lifeAmount, sa.getHostCard());
} else { } else {

View File

@@ -11,7 +11,7 @@ ALTERNATE
Name:Revenge Name:Revenge
ManaCost:4 W B ManaCost:4 W B
Types:Sorcery Types:Sorcery
A:SP$ SetLife | LifeAmount$ X | SubAbility$ DBLoseHalf | SpellDescription$ Double your life total. Target opponent loses half their life, rounded up. A:SP$ SetLife | LifeAmount$ X | Defined$ You | SubAbility$ DBLoseHalf | SpellDescription$ Double your life total. Target opponent loses half their life, rounded up.
SVar:DBLoseHalf:DB$ LoseLife | ValidTgts$ Opponent | LifeAmount$ Y SVar:DBLoseHalf:DB$ LoseLife | ValidTgts$ Opponent | LifeAmount$ Y
SVar:X:Count$YourLifeTotal/Twice SVar:X:Count$YourLifeTotal/Twice
SVar:Y:Count$TargetedLifeTotal/HalfUp SVar:Y:Count$TargetedLifeTotal/HalfUp