mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-16 10:48:00 +00:00
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:
@@ -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)
|
||||
/**
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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()) {
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user