predictDamage methods moved to ComputerUtilCombat, since used by AI only and it's not a core game entity property

distubute combat damage for AI moved from Combat.java to ComputerUtilCombat
This commit is contained in:
Maxmtg
2013-02-10 19:19:13 +00:00
parent eee197879c
commit a7397fda01
19 changed files with 365 additions and 350 deletions

View File

@@ -7705,92 +7705,6 @@ public class Card extends GameEntity implements Comparable<Card> {
this.dealtDamageToPlayerThisTurn.clear();
}
// how much damage is enough to kill the creature (for AI)
/**
* <p>
* getEnoughDamageToKill.
* </p>
*
* @param maxDamage
* a int.
* @param source
* a {@link forge.Card} object.
* @param isCombat
* a boolean.
* @return a int.
*/
public final int getEnoughDamageToKill(final int maxDamage, final Card source, final boolean isCombat) {
return this.getEnoughDamageToKill(maxDamage, source, isCombat, false);
}
/**
* <p>
* getEnoughDamageToKill.
* </p>
*
* @param maxDamage
* a int.
* @param source
* a {@link forge.Card} object.
* @param isCombat
* a boolean.
* @param noPrevention
* a boolean.
* @return a int.
*/
public final int getEnoughDamageToKill(final int maxDamage, final Card source, final boolean isCombat,
final boolean noPrevention) {
final int killDamage = this.getKillDamage();
if (this.hasKeyword("Indestructible") || (this.getShield() > 0)) {
if (!(source.hasKeyword("Wither") || source.hasKeyword("Infect"))) {
return maxDamage + 1;
}
} else if (source.hasKeyword("Deathtouch")) {
for (int i = 1; i <= maxDamage; i++) {
if (noPrevention) {
if (this.staticReplaceDamage(i, source, isCombat) > 0) {
return i;
}
} else if (this.predictDamage(i, source, isCombat) > 0) {
return i;
}
}
}
for (int i = 1; i <= maxDamage; i++) {
if (noPrevention) {
if (this.staticReplaceDamage(i, source, isCombat) >= killDamage) {
return i;
}
} else {
if (this.predictDamage(i, source, isCombat) >= killDamage) {
return i;
}
}
}
return maxDamage + 1;
}
// the amount of damage needed to kill the creature (for AI)
/**
* <p>
* getKillDamage.
* </p>
*
* @return a int.
*/
public final int getKillDamage() {
int killDamage = this.getLethalDamage() + this.getPreventNextDamage();
if ((killDamage > this.getPreventNextDamage())
&& this.hasStartOfKeyword("When CARDNAME is dealt damage, destroy it.")) {
killDamage = 1 + this.getPreventNextDamage();
}
return killDamage;
}
// this is the minimal damage a trampling creature has to assign to a
// blocker
/**
@@ -7935,34 +7849,6 @@ public class Card extends GameEntity implements Comparable<Card> {
}
}
// This function helps the AI calculate the actual amount of damage an
// effect would deal
/**
* <p>
* predictDamage.
* </p>
*
* @param damage
* a int.
* @param possiblePrevention
* a int.
* @param source
* a {@link forge.Card} object.
* @param isCombat
* a boolean.
* @return a int.
*/
public final int predictDamage(final int damage, final int possiblePrevention, final Card source,
final boolean isCombat) {
int restDamage = damage;
restDamage = this.staticReplaceDamage(restDamage, source, isCombat);
restDamage = this.staticDamagePrevention(restDamage, possiblePrevention, source, isCombat);
return restDamage;
}
// This should be also usable by the AI to forecast an effect (so it must
// not change the game state)
/**

View File

@@ -123,30 +123,6 @@ public abstract class GameEntity extends MyObservable {
*/
public abstract boolean addDamageAfterPrevention(final int damage, final Card source, final boolean isCombat);
/**
* <p>
* predictDamage.
* </p>
*
* @param damage
* a int.
* @param source
* a {@link forge.Card} object.
* @param isCombat
* a boolean.
* @return a int.
*/
// This function helps the AI calculate the actual amount of damage an
// effect would deal
public int predictDamage(final int damage, final Card source, final boolean isCombat) {
int restDamage = damage;
restDamage = this.staticReplaceDamage(restDamage, source, isCombat);
restDamage = this.staticDamagePrevention(restDamage, source, isCombat);
return restDamage;
}
// This should be also usable by the AI to forecast an effect (so it must
// not change the game state)

View File

@@ -93,7 +93,7 @@ public class ChooseSourceAi extends SpellAiLogic {
return false;
}
int dmg = AbilityFactory.calculateAmount(threatSource, topStack.getParam("NumDmg"), topStack);
if (ai.predictDamage(dmg, threatSource, false) <= 0) {
if (ComputerUtilCombat.predictDamageTo(ai, dmg, threatSource, false) <= 0) {
return false;
}
return true;

View File

@@ -9,6 +9,7 @@ import forge.CardPredicates;
import forge.Singletons;
import forge.card.abilityfactory.SpellAiLogic;
import forge.card.spellability.SpellAbility;
import forge.game.ai.ComputerUtilCombat;
import forge.game.phase.PhaseType;
import forge.game.player.Player;
import forge.game.zone.ZoneType;
@@ -26,7 +27,7 @@ public abstract class DamageAiBase extends SpellAiLogic {
}
if (!noPrevention) {
restDamage = enemy.predictDamage(restDamage, sa.getSourceCard(), false);
restDamage = ComputerUtilCombat.predictDamageTo(enemy, restDamage, sa.getSourceCard(), false);
} else {
restDamage = enemy.staticReplaceDamage(restDamage, sa.getSourceCard(), false);
}

View File

@@ -15,6 +15,7 @@ import forge.card.cardfactory.CardFactoryUtil;
import forge.card.cost.Cost;
import forge.card.spellability.SpellAbility;
import forge.card.spellability.Target;
import forge.game.ai.ComputerUtilCombat;
import forge.game.ai.ComputerUtilCost;
import forge.game.ai.ComputerUtilMana;
import forge.game.player.AIPlayer;
@@ -69,7 +70,7 @@ public class DamageAllAi extends SpellAiLogic {
// TODO: if damage is dependant on mana paid, maybe have X be human's max life
// Don't kill yourself
if (validP.contains("Each") && (ai.getLife() <= ai.predictDamage(dmg, source, false))) {
if (validP.contains("Each") && (ai.getLife() <= ComputerUtilCombat.predictDamageTo(ai, dmg, source, false))) {
return false;
}
@@ -80,7 +81,7 @@ public class DamageAllAi extends SpellAiLogic {
// if we can kill human, do it
if ((validP.contains("Each") || validP.contains("EachOpponent"))
&& (opp.getLife() <= opp.predictDamage(dmg, source, false))) {
&& (opp.getLife() <= ComputerUtilCombat.predictDamageTo(opp, dmg, source, false))) {
return true;
}
@@ -133,13 +134,13 @@ public class DamageAllAi extends SpellAiLogic {
computerList.clear();
}
// Don't get yourself killed
if (validP.contains("Each") && (ai.getLife() <= ai.predictDamage(dmg, source, false))) {
if (validP.contains("Each") && (ai.getLife() <= ComputerUtilCombat.predictDamageTo(ai, dmg, source, false))) {
return false;
}
// if we can kill human, do it
if ((validP.contains("Each") || validP.contains("EachOpponent") || validP.contains("Targeted"))
&& (enemy.getLife() <= enemy.predictDamage(dmg, source, false))) {
&& (enemy.getLife() <= ComputerUtilCombat.predictDamageTo(enemy, dmg, source, false))) {
return true;
}
@@ -177,7 +178,7 @@ public class DamageAllAi extends SpellAiLogic {
final Predicate<Card> filterKillable = new Predicate<Card>() {
@Override
public boolean apply(final Card c) {
return (c.predictDamage(dmg, source, false) >= c.getKillDamage());
return (ComputerUtilCombat.predictDamageTo(c, dmg, source, false) >= ComputerUtilCombat.getDamageToKill(c));
}
};
@@ -222,13 +223,13 @@ public class DamageAllAi extends SpellAiLogic {
return true;
}
// Don't get yourself killed
if (validP.contains("Each") && (ai.getLife() <= ai.predictDamage(dmg, source, false))) {
if (validP.contains("Each") && (ai.getLife() <= ComputerUtilCombat.predictDamageTo(ai, dmg, source, false))) {
return false;
}
// if we can kill human, do it
if ((validP.contains("Each") || validP.contains("EachOpponent") || validP.contains("Targeted"))
&& (enemy.getLife() <= enemy.predictDamage(dmg, source, false))) {
&& (enemy.getLife() <= ComputerUtilCombat.predictDamageTo(enemy, dmg, source, false))) {
return true;
}

View File

@@ -16,6 +16,7 @@ import forge.card.spellability.SpellAbility;
import forge.card.spellability.Target;
import forge.card.spellability.TargetSelection;
import forge.game.ai.ComputerUtil;
import forge.game.ai.ComputerUtilCombat;
import forge.game.ai.ComputerUtilCost;
import forge.game.ai.ComputerUtilMana;
import forge.game.phase.PhaseHandler;
@@ -102,7 +103,7 @@ public class DamageDealAi extends DamageAiBase {
final boolean noPrevention = sa.hasParam("NoPrevention");
final ArrayList<Card> cards = tgt.getTargetCards();
for (final Card c : cards) {
final int adjDamage = c.getEnoughDamageToKill(dmg, source, false, noPrevention);
final int adjDamage = ComputerUtilCombat.getEnoughDamageToKill(c, dmg, source, false, noPrevention);
if ((adjDamage > actualPay) && (adjDamage <= dmg)) {
actualPay = adjDamage;
}
@@ -156,7 +157,7 @@ public class DamageDealAi extends DamageAiBase {
final List<Card> killables = CardLists.filter(hPlay, new Predicate<Card>() {
@Override
public boolean apply(final Card c) {
return (c.getEnoughDamageToKill(d, source, false, noPrevention) <= d) && !ComputerUtil.canRegenerate(ai, c)
return (ComputerUtilCombat.getEnoughDamageToKill(c, d, source, false, noPrevention) <= d) && !ComputerUtil.canRegenerate(ai, c)
&& !(c.getSVar("SacMe").length() > 0);
}
});
@@ -322,7 +323,7 @@ public class DamageDealAi extends DamageAiBase {
// Card c = (Card)o;
} else if (o instanceof Player) {
final Player p = (Player) o;
final int restDamage = p.predictDamage(dmg, saMe.getSourceCard(), false);
final int restDamage = ComputerUtilCombat.predictDamageTo(p, dmg, saMe.getSourceCard(), false);
if (p == ai && p.canLoseLife() && ((restDamage + 3) >= p.getLife()) && (restDamage > 0)) {
// from this spell will kill me
return false;
@@ -413,7 +414,7 @@ public class DamageDealAi extends DamageAiBase {
actualPay = dmg;
}
for (final Card c : cards) {
final int adjDamage = c.getEnoughDamageToKill(dmg, source, false, noPrevention);
final int adjDamage = ComputerUtilCombat.getEnoughDamageToKill(c, dmg, source, false, noPrevention);
if (adjDamage > actualPay) {
actualPay = adjDamage;
}

View File

@@ -10,6 +10,7 @@ import forge.CardLists;
import forge.card.abilityfactory.SpellAiLogic;
import forge.card.spellability.SpellAbility;
import forge.card.spellability.Target;
import forge.game.ai.ComputerUtilCombat;
import forge.game.player.AIPlayer;
import forge.util.MyRandom;
@@ -44,8 +45,8 @@ public class FightAi extends SpellAiLogic {
if (humCreatures.size() > 0 && aiCreatures.size() > 0) {
for (Card humanCreature : humCreatures) {
for (Card aiCreature : aiCreatures) {
if (humanCreature.getKillDamage() <= aiCreature.getNetAttack()
&& humanCreature.getNetAttack() < aiCreature.getKillDamage()) {
if (ComputerUtilCombat.getDamageToKill(humanCreature) <= aiCreature.getNetAttack()
&& humanCreature.getNetAttack() < ComputerUtilCombat.getDamageToKill(aiCreature)) {
// todo: check min/max targets; see if we picked the best matchup
tgt.addTarget(humanCreature);
tgt.addTarget(aiCreature);
@@ -69,8 +70,8 @@ public class FightAi extends SpellAiLogic {
&& creature1.sharesCreatureTypeWith(creature2)) {
continue;
}
if (creature1.getKillDamage() <= creature2.getNetAttack()
&& creature1.getNetAttack() >= creature2.getKillDamage()) {
if (ComputerUtilCombat.getDamageToKill(creature1) <= creature2.getNetAttack()
&& creature1.getNetAttack() >= ComputerUtilCombat.getDamageToKill(creature2)) {
// todo: check min/max targets; see if we picked the best matchup
tgt.addTarget(creature1);
tgt.addTarget(creature2);

View File

@@ -502,7 +502,7 @@ public abstract class PumpAiBase extends SpellAiLogic {
if (c.getNetDefense() <= -defense) {
return true; // can kill indestructible creatures
}
return ((c.getKillDamage() <= -defense) && !c.hasKeyword("Indestructible"));
return ((ComputerUtilCombat.getDamageToKill(c) <= -defense) && !c.hasKeyword("Indestructible"));
}
}); // leaves all creatures that will be destroyed
} // -X/-X end

View File

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

View File

@@ -167,7 +167,7 @@ public class ChooseSourceEffect extends SpellEffect {
break;
}
int dmg = AbilityFactory.calculateAmount(source, topStack.getParam("NumDmg"), topStack);
if (ai.predictDamage(dmg, source, false) <= 0) {
if (ComputerUtilCombat.predictDamageTo(ai, dmg, source, false) <= 0) {
break;
}
chosen.add(topStack.getSourceCard());

View File

@@ -50,6 +50,7 @@ import forge.card.trigger.Trigger;
import forge.card.trigger.TriggerHandler;
import forge.control.input.Input;
import forge.control.input.InputSelectManyCards;
import forge.game.ai.ComputerUtilCombat;
import forge.game.player.Player;
import forge.game.zone.PlayerZone;
import forge.game.zone.Zone;
@@ -232,7 +233,7 @@ public class CardFactoryCreatures {
final List<Card> damageableWolves = CardLists.filter(wolves, new Predicate<Card>() {
@Override
public boolean apply(final Card c) {
return (c.predictDamage(target.getNetAttack(), target, false) > 0);
return (ComputerUtilCombat.predictDamageTo(c, target.getNetAttack(), target, false) > 0);
}
});
@@ -253,8 +254,8 @@ public class CardFactoryCreatures {
wolvesLeft = CardLists.filter(wolvesLeft, new Predicate<Card>() {
@Override
public boolean apply(final Card c) {
return (c.getKillDamage() > 0)
&& ((c.getKillDamage() <= target.getNetAttack()) || target
return (ComputerUtilCombat.getDamageToKill(c) > 0)
&& ((ComputerUtilCombat.getDamageToKill(c) <= target.getNetAttack()) || target
.hasKeyword("Deathtouch"));
}
});
@@ -263,7 +264,7 @@ public class CardFactoryCreatures {
if (wolvesLeft.size() > 0) {
final Card best = CardFactoryUtil.getBestCreatureAI(wolvesLeft);
best.addDamage(1, target);
if ((best.getKillDamage() <= 0) || target.hasKeyword("Deathtouch")) {
if ((ComputerUtilCombat.getDamageToKill(best) <= 0) || target.hasKeyword("Deathtouch")) {
wolvesLeft.remove(best);
}
} else {

View File

@@ -786,7 +786,7 @@ public class AiAttackController {
final Card attacker = attackersLeft.get(i);
if (this.aiAggression < 5 && !attacker.hasFirstStrike() && !attacker.hasDoubleStrike()
&& ComputerUtilCombat.getTotalFirstStrikeBlockPower(attacker, ai.getOpponent())
>= attacker.getKillDamage()) {
>= ComputerUtilCombat.getDamageToKill(attacker)) {
continue;
}

View File

@@ -1150,21 +1150,21 @@ public class ComputerUtil {
// don't bounce or blink a permanent that the human
// player owns or is a token
if (saviourApi == ApiType.ChangeZone && (c.getOwner().isHuman() || c.isToken())) {
if (saviourApi == ApiType.ChangeZone && (c.getOwner().isOpponentOf(aiPlayer) || c.isToken())) {
continue;
}
if (c.predictDamage(dmg, source, false) >= c.getKillDamage()) {
if (ComputerUtilCombat.predictDamageTo(c, dmg, source, false) >= ComputerUtilCombat.getDamageToKill(c)) {
threatened.add(c);
}
} else if (o instanceof Player) {
final Player p = (Player) o;
if (source.hasKeyword("Infect")) {
if (p.predictDamage(dmg, source, false) >= p.getPoisonCounters()) {
if (ComputerUtilCombat.predictDamageTo(p, dmg, source, false) >= p.getPoisonCounters()) {
threatened.add(p);
}
} else if (p.predictDamage(dmg, source, false) >= p.getLife()) {
} else if (ComputerUtilCombat.predictDamageTo(p, dmg, source, false) >= p.getLife()) {
threatened.add(p);
}
}
@@ -1224,7 +1224,7 @@ public class ComputerUtil {
// don't bounce or blink a permanent that the human
// player owns or is a token
if (saviourApi == ApiType.ChangeZone && (c.getOwner().isHuman() || c.isToken())) {
if (saviourApi == ApiType.ChangeZone && (c.getOwner().isOpponentOf(aiPlayer) || c.isToken())) {
continue;
}

View File

@@ -426,7 +426,7 @@ public class ComputerUtilBlock {
if (firstStrikeBlockers.size() > 1) {
CardLists.sortAttack(firstStrikeBlockers);
for (final Card blocker : firstStrikeBlockers) {
final int damageNeeded = attacker.getKillDamage()
final int damageNeeded = ComputerUtilCombat.getDamageToKill(attacker)
+ 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
@@ -482,7 +482,7 @@ public class ComputerUtilBlock {
final Card leader = CardFactoryUtil.getBestCreatureAI(usableBlockers);
blockGang.add(leader);
usableBlockers.remove(leader);
absorbedDamage = leader.getEnoughDamageToKill(attacker.getNetCombatDamage(), attacker, true);
absorbedDamage = ComputerUtilCombat.getEnoughDamageToKill(leader, attacker.getNetCombatDamage(), attacker, true);
currentValue = CardFactoryUtil.evaluateCreature(leader);
for (final Card blocker : usableBlockers) {
@@ -490,10 +490,9 @@ public class ComputerUtilBlock {
// enough and the new one would deal the remaining damage
final int currentDamage = ComputerUtilCombat.totalDamageOfBlockers(attacker, blockGang);
final int additionalDamage = ComputerUtilCombat.dealsDamageAsBlocker(attacker, blocker);
final int absorbedDamage2 = blocker
.getEnoughDamageToKill(attacker.getNetCombatDamage(), attacker, true);
final int absorbedDamage2 = ComputerUtilCombat.getEnoughDamageToKill(blocker, attacker.getNetCombatDamage(), attacker, true);
final int addedValue = CardFactoryUtil.evaluateCreature(blocker);
final int damageNeeded = attacker.getKillDamage()
final int damageNeeded = ComputerUtilCombat.getDamageToKill(attacker)
+ ComputerUtilCombat.predictToughnessBonusOfAttacker(attacker, blocker, combat, false);
if ((damageNeeded > currentDamage || CombatUtil.needsBlockers(attacker) > blockGang.size())
&& !(damageNeeded > currentDamage + additionalDamage)
@@ -666,7 +665,7 @@ public class ComputerUtilBlock {
// Try to use safe blockers first
safeBlockers = ComputerUtilBlock.getSafeBlockers(ai, attacker, blockers, combat);
for (final Card blocker : safeBlockers) {
final int damageNeeded = attacker.getKillDamage()
final int damageNeeded = ComputerUtilCombat.getDamageToKill(attacker)
+ 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
@@ -690,7 +689,7 @@ public class ComputerUtilBlock {
}
for (final Card blocker : safeBlockers) {
final int damageNeeded = attacker.getKillDamage()
final int damageNeeded = ComputerUtilCombat.getDamageToKill(attacker)
+ 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

@@ -32,12 +32,14 @@ import forge.card.TriggerReplacementBase;
import forge.card.abilityfactory.AbilityFactory;
import forge.card.abilityfactory.ApiType;
import forge.card.cardfactory.CardFactoryUtil;
import forge.card.replacement.ReplacementEffect;
import forge.card.spellability.AbilityActivated;
import forge.card.spellability.SpellAbility;
import forge.card.staticability.StaticAbility;
import forge.card.trigger.Trigger;
import forge.card.trigger.TriggerHandler;
import forge.card.trigger.TriggerType;
import forge.game.GameState;
import forge.game.GlobalRuleChange;
import forge.game.phase.Combat;
import forge.game.phase.CombatUtil;
@@ -124,9 +126,9 @@ public class ComputerUtilCombat {
}
damage += ComputerUtilCombat.predictPowerBonusOfAttacker(attacker, null, combat, false);
if (!attacker.hasKeyword("Infect")) {
sum = attacked.predictDamage(damage, attacker, true);
sum = ComputerUtilCombat.predictDamageTo(attacked, damage, attacker, true);
if (attacker.hasKeyword("Double Strike")) {
sum += attacked.predictDamage(damage, attacker, true);
sum += ComputerUtilCombat.predictDamageTo(attacked, damage, attacker, true);
}
}
return sum;
@@ -151,9 +153,9 @@ public class ComputerUtilCombat {
int poison = 0;
damage += ComputerUtilCombat.predictPowerBonusOfAttacker(attacker, null, null, false);
if (attacker.hasKeyword("Infect")) {
poison += attacked.predictDamage(damage, attacker, true);
poison += ComputerUtilCombat.predictDamageTo(attacked, damage, attacker, true);
if (attacker.hasKeyword("Double Strike")) {
poison += attacked.predictDamage(damage, attacker, true);
poison += ComputerUtilCombat.predictDamageTo(attacked, damage, attacker, true);
}
}
if (attacker.hasKeyword("Poisonous") && (damage > 0)) {
@@ -442,10 +444,10 @@ public class ComputerUtilCombat {
}
// consider static Damage Prevention
defenderDamage = attacker.predictDamage(defenderDamage, defender, true);
defenderDamage = predictDamageTo(attacker, defenderDamage, defender, true);
if (defender.hasKeyword("Double Strike")) {
defenderDamage += attacker.predictDamage(defenderDamage, defender, true);
defenderDamage += predictDamageTo(attacker, defenderDamage, defender, true);
}
return defenderDamage;
@@ -555,7 +557,7 @@ public class ComputerUtilCombat {
}
}
return ComputerUtilCombat.totalDamageOfBlockers(attacker, blockers) >= attacker.getKillDamage();
return ComputerUtilCombat.totalDamageOfBlockers(attacker, blockers) >= ComputerUtilCombat.getDamageToKill(attacker);
}
// Will this trigger trigger?
@@ -850,7 +852,7 @@ public class ComputerUtilCombat {
// can't parse the number (X for example)
continue;
}
toughness -= defender.predictDamage(damage, 0, source, false);
toughness -= predictDamageTo(defender, damage, 0, source, false);
continue;
}
@@ -1157,7 +1159,7 @@ public class ComputerUtilCombat {
// can't parse the number (X for example)
continue;
}
toughness -= attacker.predictDamage(damage, 0, source, false);
toughness -= predictDamageTo(attacker, damage, 0, source, false);
continue;
}
@@ -1409,12 +1411,12 @@ public class ComputerUtilCombat {
}
// consider Damage Prevention/Replacement
defenderDamage = attacker.predictDamage(defenderDamage, possibleAttackerPrevention, defender, true);
attackerDamage = defender.predictDamage(attackerDamage, possibleDefenderPrevention, attacker, true);
defenderDamage = predictDamageTo(attacker, defenderDamage, possibleAttackerPrevention, defender, true);
attackerDamage = predictDamageTo(defender, attackerDamage, possibleDefenderPrevention, attacker, true);
final int defenderLife = defender.getKillDamage()
final int defenderLife = ComputerUtilCombat.getDamageToKill(defender)
+ ComputerUtilCombat.predictToughnessBonusOfBlocker(attacker, defender, withoutAbilities);
final int attackerLife = attacker.getKillDamage()
final int attackerLife = ComputerUtilCombat.getDamageToKill(attacker)
+ ComputerUtilCombat.predictToughnessBonusOfAttacker(attacker, defender, combat, withoutAbilities);
if (defender.hasKeyword("Double Strike")) {
@@ -1522,7 +1524,7 @@ public class ComputerUtilCombat {
if (flankingMagnitude >= defender.getNetDefense()) {
return true;
}
if ((flankingMagnitude >= defender.getKillDamage()) && !defender.hasKeyword("Indestructible")) {
if ((flankingMagnitude >= ComputerUtilCombat.getDamageToKill(defender)) && !defender.hasKeyword("Indestructible")) {
return true;
}
} // flanking
@@ -1559,20 +1561,20 @@ public class ComputerUtilCombat {
}
// consider Damage Prevention/Replacement
defenderDamage = attacker.predictDamage(defenderDamage, possibleAttackerPrevention, defender, true);
attackerDamage = defender.predictDamage(attackerDamage, possibleDefenderPrevention, attacker, true);
defenderDamage = predictDamageTo(attacker, defenderDamage, possibleAttackerPrevention, defender, true);
attackerDamage = predictDamageTo(defender, attackerDamage, possibleDefenderPrevention, attacker, true);
if (combat != null) {
for (Card atkr : combat.getAttackersBlockedBy(defender)) {
if (!atkr.equals(attacker)) {
attackerDamage += defender.predictDamage(atkr.getNetCombatDamage(), 0, atkr, true);
attackerDamage += predictDamageTo(defender, atkr.getNetCombatDamage(), 0, atkr, true);
}
}
}
final int defenderLife = defender.getKillDamage()
final int defenderLife = ComputerUtilCombat.getDamageToKill(defender)
+ ComputerUtilCombat.predictToughnessBonusOfBlocker(attacker, defender, withoutAbilities);
final int attackerLife = attacker.getKillDamage()
final int attackerLife = ComputerUtilCombat.getDamageToKill(attacker)
+ ComputerUtilCombat.predictToughnessBonusOfAttacker(attacker, defender, combat, withoutAbilities);
if (attacker.hasKeyword("Double Strike")) {
@@ -1626,4 +1628,293 @@ public class ComputerUtilCombat {
} // attacker no double strike
return false; // should never arrive here
} // canDestroyBlocker
/**
* <p>
* distributeAIDamage.
* </p>
*
* @param attacker
* a {@link forge.Card} object.
* @param block
* a {@link forge.CardList} object.
* @param damage
* a int.
*/
public static void distributeAIDamage(final Card attacker, final List<Card> block, int damage, Combat combat) {
final Card c = attacker;
if (attacker.hasKeyword("You may have CARDNAME assign its combat damage as though it weren't blocked.")
|| attacker.hasKeyword("CARDNAME assigns its combat damage as though it weren't blocked.")) {
combat.addDefendingDamage(damage, attacker);
return;
}
final boolean hasTrample = attacker.hasKeyword("Trample");
if (block.size() == 1) {
final Card blocker = block.get(0);
// trample
if (hasTrample) {
int damageNeeded = 0;
// TODO if the human can be killed distribute only the minimum
// of damage to the blocker
damageNeeded = ComputerUtilCombat.getEnoughDamageToKill(blocker, damage, attacker, true);
if (damageNeeded > damage) {
damageNeeded = Math.min(blocker.getLethalDamage(), damage);
} else {
damageNeeded = Math.max(blocker.getLethalDamage(), damageNeeded);
}
final int trample = damage - damageNeeded;
// If Extra trample damage, assign to defending
// player/planeswalker
if (0 < trample) {
combat.addDefendingDamage(trample, attacker);
}
blocker.addAssignedDamage(damageNeeded, attacker);
} else {
blocker.addAssignedDamage(damage, attacker);
}
} // 1 blocker
else {
boolean killsAllBlockers = true;
// Does the attacker deal lethal damage to all blockers
//Blocking Order now determined after declare blockers
Card lastBlocker = null;
for (final Card b : block) {
final int enoughDamageToKill = ComputerUtilCombat.getEnoughDamageToKill(b, damage, attacker, true);
if (enoughDamageToKill <= damage) {
damage -= enoughDamageToKill;
final List<Card> cl = new ArrayList<Card>();
cl.add(attacker);
b.addAssignedDamage(enoughDamageToKill, c);
} else {
killsAllBlockers = false;
}
lastBlocker = b;
} // for
if (killsAllBlockers && damage > 0) {
// if attacker has no trample, and there's damage left, assign the rest to the last blocker
if (!hasTrample && lastBlocker != null) {
lastBlocker.addAssignedDamage(damage, c);
damage = 0;
} else if (hasTrample) {
combat.addDefendingDamage(damage, c);
}
}
}
} // setAssignedDamage()
// how much damage is enough to kill the creature (for AI)
/**
* <p>
* getEnoughDamageToKill.
* </p>
*
* @param maxDamage
* a int.
* @param source
* a {@link forge.Card} object.
* @param isCombat
* a boolean.
* @return a int.
*/
public final static int getEnoughDamageToKill(final Card c, final int maxDamage, final Card source, final boolean isCombat) {
return getEnoughDamageToKill(c, maxDamage, source, isCombat, false);
}
/**
* <p>
* getEnoughDamageToKill.
* </p>
*
* @param maxDamage
* a int.
* @param source
* a {@link forge.Card} object.
* @param isCombat
* a boolean.
* @param noPrevention
* a boolean.
* @return a int.
*/
public static final int getEnoughDamageToKill(final Card c, final int maxDamage, final Card source, final boolean isCombat,
final boolean noPrevention) {
final int killDamage = ComputerUtilCombat.getDamageToKill(c);
if (c.hasKeyword("Indestructible") || (c.getShield() > 0)) {
if (!(source.hasKeyword("Wither") || source.hasKeyword("Infect"))) {
return maxDamage + 1;
}
} else if (source.hasKeyword("Deathtouch")) {
for (int i = 1; i <= maxDamage; i++) {
if (noPrevention) {
if (c.staticReplaceDamage(i, source, isCombat) > 0) {
return i;
}
} else if (predictDamageTo(c, i, source, isCombat) > 0) {
return i;
}
}
}
for (int i = 1; i <= maxDamage; i++) {
if (noPrevention) {
if (c.staticReplaceDamage(i, source, isCombat) >= killDamage) {
return i;
}
} else {
if (predictDamageTo(c, i, source, isCombat) >= killDamage) {
return i;
}
}
}
return maxDamage + 1;
}
// the amount of damage needed to kill the creature (for AI)
/**
* <p>
* getKillDamage.
* </p>
*
* @return a int.
*/
public final static int getDamageToKill(final Card c) {
int killDamage = c.getLethalDamage() + c.getPreventNextDamage();
if ((killDamage > c.getPreventNextDamage())
&& c.hasStartOfKeyword("When CARDNAME is dealt damage, destroy it.")) {
killDamage = 1 + c.getPreventNextDamage();
}
return killDamage;
}
/**
* <p>
* predictDamage.
* </p>
*
* @param damage
* a int.
* @param source
* a {@link forge.Card} object.
* @param isCombat
* a boolean.
* @return a int.
*/
public final static int predictDamageTo(final Player target, final int damage, final Card source, final boolean isCombat) {
final GameState game = Singletons.getModel().getGame();
int restDamage = damage;
restDamage = target.staticReplaceDamage(restDamage, source, isCombat);
// Predict replacement effects
for (final Card ca : game.getCardsIn(ZoneType.Battlefield)) {
for (final ReplacementEffect re : ca.getReplacementEffects()) {
HashMap<String, String> params = re.getMapParams();
if (!"DamageDone".equals(params.get("Event")) || !params.containsKey("PreventionEffect")) {
continue;
}
if (params.containsKey("ValidSource")
&& !source.isValid(params.get("ValidSource"), ca.getController(), ca)) {
continue;
}
if (params.containsKey("ValidTarget")
&& !target.isValid(params.get("ValidTarget"), ca.getController(), ca)) {
continue;
}
if (params.containsKey("IsCombat")) {
if (params.get("IsCombat").equals("True")) {
if (!isCombat) {
continue;
}
} else {
if (isCombat) {
continue;
}
}
}
return 0;
}
}
restDamage = target.staticDamagePrevention(restDamage, source, isCombat);
return restDamage;
}
/**
* <p>
* predictDamage.
* </p>
*
* @param damage
* a int.
* @param source
* a {@link forge.Card} object.
* @param isCombat
* a boolean.
* @return a int.
*/
// This function helps the AI calculate the actual amount of damage an
// effect would deal
public final static int predictDamageTo(final Card target, final int damage, final Card source, final boolean isCombat) {
int restDamage = damage;
restDamage = target.staticReplaceDamage(restDamage, source, isCombat);
restDamage = target.staticDamagePrevention(restDamage, source, isCombat);
return restDamage;
}
// This function helps the AI calculate the actual amount of damage an
// effect would deal
/**
* <p>
* predictDamage.
* </p>
*
* @param damage
* a int.
* @param possiblePrevention
* a int.
* @param source
* a {@link forge.Card} object.
* @param isCombat
* a boolean.
* @return a int.
*/
public final static int predictDamageTo(final Card target, final int damage, final int possiblePrevention, final Card source, final boolean isCombat) {
int restDamage = damage;
restDamage = target.staticReplaceDamage(restDamage, source, isCombat);
restDamage = target.staticDamagePrevention(restDamage, possiblePrevention, source, isCombat);
return restDamage;
}
}

View File

@@ -160,7 +160,7 @@ public class ComputerUtilCost {
for (final CostPart part : cost.getCostParts()) {
if (part instanceof CostDamage) {
final CostDamage pay = (CostDamage) part;
int realDamage = ai.predictDamage(pay.convertAmount(), source, false);
int realDamage = ComputerUtilCombat.predictDamageTo(ai, pay.convertAmount(), source, false);
if (ai.getLife() - realDamage < remainingLife
&& realDamage > 0 && !ai.cantLoseForZeroOrLessLife()
&& ai.canLoseLife()) {

View File

@@ -33,6 +33,7 @@ import forge.CardPredicates;
import forge.GameEntity;
import forge.Singletons;
import forge.card.trigger.TriggerType;
import forge.game.ai.ComputerUtilCombat;
import forge.game.event.BlockerAssignedEvent;
import forge.game.player.Player;
import forge.game.zone.ZoneType;
@@ -290,8 +291,8 @@ public class Combat {
final GameEntity ge = this.getDefenderByAttacker(source);
if (ge instanceof Card) {
final Card pw = (Card) ge;
pw.addAssignedDamage(n, source);
final Card planeswalker = (Card) ge;
planeswalker.addAssignedDamage(n, source);
return;
}
@@ -692,18 +693,16 @@ public class Combat {
final int damage = blocker.getNetCombatDamage();
if (attackers.size() == 0) {
// Just in case it was removed or something
} else {
if (!attackers.isEmpty()) {
assignedDamage = true;
if (this.getAttackingPlayer().isComputer()) { // ai attacks
if (blocker.getController().isHuman()) { // who is defending matters
if (attackers.size() > 1) {
CMatchUI.SINGLETON_INSTANCE.assignDamage(blocker, attackers, damage, null);
} else {
attackers.get(0).addAssignedDamage(damage, blocker);
}
} else { // computer attacks
this.distributeAIDamage(blocker, attackers, damage);
ComputerUtilCombat.distributeAIDamage(blocker, attackers, damage, this);
}
}
}
@@ -742,8 +741,10 @@ public class Combat {
if (blockers.size() == 0) {
if (trampler || this.isUnblocked(attacker)) {
this.addDefendingDamage(damageDealt, attacker);
} else {
// Else no damage can be dealt anywhere
continue;
}
// Else no damage can be dealt anywhere
} else {
if (this.getAttackingPlayer().isHuman()) { // human attacks
if (assignDamageAsIfNotBlocked(attacker)) {
@@ -756,7 +757,7 @@ public class Combat {
}
}
} else { // computer attacks
this.distributeAIDamage(attacker, blockers, damageDealt);
ComputerUtilCombat.distributeAIDamage(attacker, blockers, damageDealt, this);
}
} // if !hasFirstStrike ...
} // for
@@ -769,93 +770,6 @@ public class Combat {
return assignedDamage;
}
/**
* <p>
* distributeAIDamage.
* </p>
*
* @param attacker
* a {@link forge.Card} object.
* @param block
* a {@link forge.CardList} object.
* @param damage
* a int.
*/
private void distributeAIDamage(final Card attacker, final List<Card> block, int damage) {
final Card c = attacker;
if (attacker.hasKeyword("You may have CARDNAME assign its combat damage as though it weren't blocked.")
|| attacker.hasKeyword("CARDNAME assigns its combat damage as though it weren't blocked.")) {
this.addDefendingDamage(damage, attacker);
return;
}
final boolean hasTrample = attacker.hasKeyword("Trample");
if (block.size() == 1) {
final Card blocker = block.get(0);
// trample
if (hasTrample) {
int damageNeeded = 0;
// TODO if the human can be killed distribute only the minimum
// of damage to the blocker
damageNeeded = blocker.getEnoughDamageToKill(damage, attacker, true);
if (damageNeeded > damage) {
damageNeeded = Math.min(blocker.getLethalDamage(), damage);
} else {
damageNeeded = Math.max(blocker.getLethalDamage(), damageNeeded);
}
final int trample = damage - damageNeeded;
// If Extra trample damage, assign to defending
// player/planeswalker
if (0 < trample) {
this.addDefendingDamage(trample, attacker);
}
blocker.addAssignedDamage(damageNeeded, attacker);
} else {
blocker.addAssignedDamage(damage, attacker);
}
} // 1 blocker
else {
boolean killsAllBlockers = true;
// Does the attacker deal lethal damage to all blockers
//Blocking Order now determined after declare blockers
Card lastBlocker = null;
for (final Card b : block) {
final int enoughDamageToKill = b.getEnoughDamageToKill(damage, attacker, true);
if (enoughDamageToKill <= damage) {
damage -= enoughDamageToKill;
final List<Card> cl = new ArrayList<Card>();
cl.add(attacker);
b.addAssignedDamage(enoughDamageToKill, c);
} else {
killsAllBlockers = false;
}
lastBlocker = b;
} // for
if (killsAllBlockers && damage > 0) {
// if attacker has no trample, and there's damage left, assign the rest to the last blocker
if (!hasTrample && lastBlocker != null) {
lastBlocker.addAssignedDamage(damage, c);
damage = 0;
} else if (hasTrample) {
this.addDefendingDamage(damage, c);
}
}
}
} // setAssignedDamage()
/**
* <p>
* dealAssignedDamage.

View File

@@ -44,6 +44,7 @@ import forge.control.input.InputSelectManyCards;
import forge.game.GameActionUtil;
import forge.game.GameState;
import forge.game.ai.ComputerUtil;
import forge.game.ai.ComputerUtilCombat;
import forge.game.ai.ComputerUtilCost;
import forge.game.ai.ComputerUtilMana;
import forge.game.player.AIPlayer;
@@ -401,7 +402,7 @@ public class Upkeep extends Phase {
GameActionUtil.payManaDuringAbilityResolve(sb.toString(), upkeepCost, paidCommand, unpaidCommand);
} else { // computers
if (ComputerUtilCost.canPayCost(aiPaid, controller)
&& (controller.predictDamage(upkeepDamage, c, false) > 0)) {
&& (ComputerUtilCombat.predictDamageTo(controller, upkeepDamage, c, false) > 0)) {
ComputerUtil.playNoStack((AIPlayer)controller, aiPaid, game);
} else {
controller.addDamage(upkeepDamage, c);

View File

@@ -47,7 +47,6 @@ import forge.card.SpellManaCost;
import forge.card.cardfactory.CardFactoryUtil;
import forge.card.cost.Cost;
import forge.card.mana.ManaPool;
import forge.card.replacement.ReplacementEffect;
import forge.card.replacement.ReplacementResult;
import forge.card.spellability.Ability;
import forge.card.spellability.Spell;
@@ -653,62 +652,6 @@ public abstract class Player extends GameEntity implements Comparable<Player> {
return true;
}
/**
* <p>
* predictDamage.
* </p>
*
* @param damage
* a int.
* @param source
* a {@link forge.Card} object.
* @param isCombat
* a boolean.
* @return a int.
*/
@Override
public final int predictDamage(final int damage, final Card source, final boolean isCombat) {
int restDamage = damage;
restDamage = this.staticReplaceDamage(restDamage, source, isCombat);
// Predict replacement effects
for (final Card ca : game.getCardsIn(ZoneType.Battlefield)) {
for (final ReplacementEffect re : ca.getReplacementEffects()) {
HashMap<String, String> params = re.getMapParams();
if (!"DamageDone".equals(params.get("Event")) || !params.containsKey("PreventionEffect")) {
continue;
}
if (params.containsKey("ValidSource")
&& !source.isValid(params.get("ValidSource"), ca.getController(), ca)) {
continue;
}
if (params.containsKey("ValidTarget")
&& !this.isValid(params.get("ValidTarget"), ca.getController(), ca)) {
continue;
}
if (params.containsKey("IsCombat")) {
if (params.get("IsCombat").equals("True")) {
if (!isCombat) {
continue;
}
} else {
if (isCombat) {
continue;
}
}
}
return 0;
}
}
restDamage = this.staticDamagePrevention(restDamage, source, isCombat);
return restDamage;
}
// This should be also usable by the AI to forecast an effect (so it must
// not change the game state)
/**