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

View File

@@ -368,7 +368,7 @@ public class AiBlockController {
if (firstStrikeBlockers.size() > 1) {
CardLists.sortByPowerDesc(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);
// if the total damage of the blockgang was not enough
// 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 absorbedDamage2 = ComputerUtilCombat.getEnoughDamageToKill(blocker, attacker.getNetCombatDamage(), attacker, true);
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);
if ((damageNeeded > currentDamage || CombatUtil.getMinNumBlockersForAttacker(attacker, ai) > blockGang.size())
&& !(damageNeeded > currentDamage + additionalDamage)
@@ -486,7 +486,7 @@ public class AiBlockController {
final int additionalDamage2 = ComputerUtilCombat.dealsDamageAsBlocker(attacker, secondBlocker);
final int absorbedDamage2 = ComputerUtilCombat.getEnoughDamageToKill(secondBlocker, attacker.getNetCombatDamage(), attacker, true);
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);
List<Card> usableBlockersAsThird = new ArrayList<>(usableBlockers);
@@ -774,7 +774,7 @@ public class AiBlockController {
if (blockers.size() > 0) {
safeBlockers = getSafeBlockers(combat, attacker, blockers);
for (final Card blocker : safeBlockers) {
final int damageNeeded = ComputerUtilCombat.getDamageToKill(attacker)
final int damageNeeded = ComputerUtilCombat.getDamageToKill(attacker, false)
+ ComputerUtilCombat.predictToughnessBonusOfAttacker(attacker, blocker, combat, false);
// Add an additional blocker if the current blockers are not
// enough and the new one would deal additional damage
@@ -801,7 +801,7 @@ public class AiBlockController {
}
for (final Card blocker : safeBlockers) {
final int damageNeeded = ComputerUtilCombat.getDamageToKill(attacker)
final int damageNeeded = ComputerUtilCombat.getDamageToKill(attacker, false)
+ ComputerUtilCombat.predictToughnessBonusOfAttacker(attacker, blocker, combat, false);
// Add an additional blocker if the current blockers are not
// 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) {
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)
|| (!grantIndestructible && !grantShroud && !canSave)) {
continue;
@@ -1695,14 +1695,14 @@ public class ComputerUtil {
}
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) {
continue;
}
}
// cannot protect against source
if (saviourApi == ApiType.Protection && (ProtectAi.toProtectFrom(source, saviour) == null)) {
if (saviourApi == ApiType.Protection && ProtectAi.toProtectFrom(source, saviour) == null) {
continue;
}
@@ -1712,7 +1712,7 @@ public class ComputerUtil {
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);
}
} else if (o instanceof Player) {
@@ -1739,7 +1739,7 @@ public class ComputerUtil {
if (o instanceof Card) {
final Card c = (Card) o;
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) {
continue;
}
@@ -1747,7 +1747,7 @@ public class ComputerUtil {
if (saviourApi == ApiType.Pump || saviourApi == ApiType.PumpAll) {
final boolean cantSave = c.getNetToughness() + toughness <= dmg
|| (!c.hasKeyword(Keyword.INDESTRUCTIBLE) && c.getShieldCount() == 0 && !grantIndestructible
&& (dmg >= toughness + ComputerUtilCombat.getDamageToKill(c)));
&& (dmg >= toughness + ComputerUtilCombat.getDamageToKill(c, false)));
if (cantSave && (!topStack.usesTargeting() || !grantShroud)) {
continue;
}

View File

@@ -1498,7 +1498,7 @@ public class ComputerUtilCard {
}
if (c.hasKeyword(Keyword.TRAMPLE) || keywords.contains("Trample")) {
for (Card b : combat.getBlockers(c)) {
pumpedDmg -= ComputerUtilCombat.getDamageToKill(b);
pumpedDmg -= ComputerUtilCombat.getDamageToKill(b, false);
}
} else {
pumpedDmg = 0;
@@ -1526,7 +1526,7 @@ public class ComputerUtilCard {
if (combat.isBlocked(atk)) {
// consider Trample damage properly for a blocked creature
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
@@ -619,7 +619,7 @@ public class ComputerUtilCombat {
if (flankingMagnitude >= defender.getNetToughness()) {
return 0;
}
if ((flankingMagnitude >= (defender.getNetToughness() - defender.getDamage()))
if (flankingMagnitude >= (defender.getNetToughness() - defender.getDamage())
&& !defender.hasKeyword(Keyword.INDESTRUCTIBLE)) {
return 0;
}
@@ -698,7 +698,7 @@ public class ComputerUtilCombat {
final int defBushidoMagnitude = blocker.getKeywordMagnitude(Keyword.BUSHIDO);
final int defenderDefense = (blocker.getLethalDamage() - flankingMagnitude) + defBushidoMagnitude;
final int defenderDefense = blocker.getLethalDamage() - flankingMagnitude + defBushidoMagnitude;
return defenderDefense;
} // shieldDamage
@@ -751,10 +751,10 @@ public class ComputerUtilCombat {
// Consider first strike and 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?
@@ -1611,7 +1611,7 @@ public class ComputerUtilCombat {
}
//Check triggers that deal damage or shrink the attacker
if (getDamageToKill(attacker)
if (getDamageToKill(attacker, false)
+ predictToughnessBonusOfAttacker(attacker, blocker, combat, withoutAbilities) <= 0) {
return true;
}
@@ -1763,9 +1763,9 @@ public class ComputerUtilCombat {
return false;
}
final int defenderLife = getDamageToKill(blocker)
final int defenderLife = getDamageToKill(blocker, false)
+ predictToughnessBonusOfBlocker(attacker, blocker, withoutAbilities);
final int attackerLife = getDamageToKill(attacker)
final int attackerLife = getDamageToKill(attacker, false)
+ predictToughnessBonusOfAttacker(attacker, blocker, combat, withoutAbilities, withoutAttackerStaticAbilities);
if (blocker.hasKeyword(Keyword.DOUBLE_STRIKE)) {
@@ -1790,13 +1790,11 @@ public class ComputerUtilCombat {
return true;
}
} // defender double strike
else { // no double strike for defender
// Attacker may kill the blocker before he can deal any damage
if (dealsFirstStrikeDamage(attacker, withoutAbilities, combat)
&& !blocker.hasKeyword(Keyword.INDESTRUCTIBLE)
&& !dealsFirstStrikeDamage(blocker, withoutAbilities, combat)) {
if (attackerDamage >= defenderLife) {
return false;
}
@@ -1810,7 +1808,6 @@ public class ComputerUtilCombat {
}
return defenderDamage >= attackerLife;
} // defender no double strike
return false;// should never arrive here
} // canDestroyAttacker
@@ -1856,7 +1853,7 @@ public class ComputerUtilCombat {
if (flankingMagnitude >= blocker.getNetToughness()) {
return true;
}
if ((flankingMagnitude >= getDamageToKill(blocker))
if (flankingMagnitude >= getDamageToKill(blocker, false)
&& !blocker.hasKeyword(Keyword.INDESTRUCTIBLE)) {
return true;
}
@@ -1867,7 +1864,7 @@ public class ComputerUtilCombat {
return false;
}
if (getDamageToKill(blocker)
if (getDamageToKill(blocker, false)
+ predictToughnessBonusOfBlocker(attacker, blocker, withoutAbilities) <= 0) {
return true;
}
@@ -1996,9 +1993,9 @@ public class ComputerUtilCombat {
}
}
final int defenderLife = getDamageToKill(blocker)
final int defenderLife = getDamageToKill(blocker, false)
+ predictToughnessBonusOfBlocker(attacker, blocker, withoutAbilities);
final int attackerLife = getDamageToKill(attacker)
final int attackerLife = getDamageToKill(attacker, false)
+ predictToughnessBonusOfAttacker(attacker, blocker, combat, withoutAbilities, withoutAttackerStaticAbilities);
if (attacker.hasKeyword(Keyword.DOUBLE_STRIKE)) {
@@ -2128,7 +2125,7 @@ public class ComputerUtilCombat {
if (dmgCanDeal > 0 ) { // if any damage left undistributed,
if (hasTrample && isAttacking) // if you have trample, deal damage to defending entity
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));
}
}
@@ -2171,7 +2168,7 @@ public class ComputerUtilCombat {
*/
public static final int getEnoughDamageToKill(final Card c, final int maxDamage, final Card source, final boolean isCombat,
final boolean noPrevention) {
final int killDamage = getDamageToKill(c);
final int killDamage = getDamageToKill(c, false);
if (c.hasKeyword(Keyword.INDESTRUCTIBLE) || c.getShieldCount() > 0) {
if (!(source.hasKeyword(Keyword.WITHER) || source.hasKeyword(Keyword.INFECT))) {
@@ -2212,8 +2209,8 @@ public class ComputerUtilCombat {
*
* @return a int.
*/
public final static int getDamageToKill(final Card c) {
int damageShield = c.getPreventNextDamageTotalShields();
public final static int getDamageToKill(final Card c, boolean withShields) {
int damageShield = withShields ? c.getPreventNextDamageTotalShields() : 0;
int killDamage = (c.isPlaneswalker() ? c.getCurrentLoyalty() : c.getLethalDamage()) + damageShield;
if (killDamage > damageShield

View File

@@ -256,7 +256,7 @@ public class DamageAllAi extends SpellAbilityAi {
final Predicate<Card> filterKillable = new Predicate<Card>() {
@Override
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) {
Card c = (Card) o;
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)) {
return false;
} else {

View File

@@ -70,14 +70,11 @@ public class FightAi extends SpellAbilityAi {
}
Card fighter1 = fighter1List.get(0);
for (Card humanCreature : humCreatures) {
if (ComputerUtilCombat.getDamageToKill(humanCreature) <= fighter1.getNetPower()
&& humanCreature.getNetPower() < ComputerUtilCombat.getDamageToKill(fighter1)) {
if (canKill(fighter1, humanCreature, 0)
&& !canKill(humanCreature, fighter1, 0)) {
// todo: check min/max targets; see if we picked the best matchup
sa.getTargets().add(humanCreature);
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
@@ -87,16 +84,12 @@ public class FightAi extends SpellAbilityAi {
if (!(humCreatures.isEmpty() && aiCreatures.isEmpty())) {
for (Card humanCreature : humCreatures) {
for (Card aiCreature : aiCreatures) {
if (ComputerUtilCombat.getDamageToKill(humanCreature) <= aiCreature.getNetPower()
&& humanCreature.getNetPower() < ComputerUtilCombat.getDamageToKill(aiCreature)) {
if (canKill(aiCreature, humanCreature, 0)
&& !canKill(humanCreature, aiCreature, 0)) {
// todo: check min/max targets; see if we picked the best matchup
sa.getTargets().add(humanCreature);
sa.getTargets().add(aiCreature);
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)) {
continue;
}
if (ComputerUtilCombat.getDamageToKill(creature1) <= creature2.getNetPower()
&& creature1.getNetPower() >= ComputerUtilCombat.getDamageToKill(creature2)) {
if (canKill(creature1, creature2, 0)
&& canKill(creature2, creature1, 0)) {
// todo: check min/max targets; see if we picked the best matchup
sa.getTargets().add(creature1);
sa.getTargets().add(creature2);
@@ -152,14 +145,14 @@ public class FightAi extends SpellAbilityAi {
if (sa.hasParam("Defined")) {
Card aiCreature = AbilityUtils.getDefinedCards(source, sa.getParam("Defined"), sa).get(0);
for (Card humanCreature : humCreatures) {
if (ComputerUtilCombat.getDamageToKill(humanCreature) <= aiCreature.getNetPower()
if (canKill(aiCreature, humanCreature, 0)
&& ComputerUtilCard.evaluateCreature(humanCreature) > ComputerUtilCard.evaluateCreature(aiCreature)) {
sa.getTargets().add(humanCreature);
return true;
}
}
for (Card humanCreature : humCreatures) {
if (ComputerUtilCombat.getDamageToKill(aiCreature) > humanCreature.getNetPower()) {
if (!canKill(humanCreature, aiCreature, 0)) {
sa.getTargets().add(humanCreature);
return true;
}
@@ -312,7 +305,7 @@ public class FightAi extends SpellAbilityAi {
return false;
}
if (fighter.hasKeyword(Keyword.DEATHTOUCH)
|| ComputerUtilCombat.getDamageToKill(opponent) <= fighter.getNetPower() + pumpAttack) {
|| ComputerUtilCombat.getDamageToKill(opponent, true) <= fighter.getNetPower() + pumpAttack) {
return true;
}
return false;

View File

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

View File

@@ -90,7 +90,7 @@ public class PumpAllAi extends PumpAiBase {
if (c.getNetToughness() <= -defense) {
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
human = CardLists.filter(human, new Predicate<Card>() {
@@ -99,7 +99,7 @@ public class PumpAllAi extends PumpAiBase {
if (c.getNetToughness() <= -defense) {
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
} // -X/-X end

View File

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

View File

@@ -11,7 +11,7 @@ ALTERNATE
Name:Revenge
ManaCost:4 W B
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:X:Count$YourLifeTotal/Twice
SVar:Y:Count$TargetedLifeTotal/HalfUp