From 3eb1f7035114c1d1230a6819d06b3e5599ca8030 Mon Sep 17 00:00:00 2001
From: Agetian
Date: Sun, 2 Jul 2023 18:46:35 +0300
Subject: [PATCH] - Defensive damage assignment for Banding and Defensive
Formation
---
.../java/forge/ai/ComputerUtilCombat.java | 64 +++++++++++--------
.../java/forge/ai/PlayerControllerAi.java | 2 +-
2 files changed, 38 insertions(+), 28 deletions(-)
diff --git a/forge-ai/src/main/java/forge/ai/ComputerUtilCombat.java b/forge-ai/src/main/java/forge/ai/ComputerUtilCombat.java
index 4487a23b27c..023e63012d1 100644
--- a/forge-ai/src/main/java/forge/ai/ComputerUtilCombat.java
+++ b/forge-ai/src/main/java/forge/ai/ComputerUtilCombat.java
@@ -2023,6 +2023,8 @@ public class ComputerUtilCombat {
* distributeAIDamage.
*
*
+ * @param self
+ * a {@link forge.game.player.Player} object.
* @param attacker
* a {@link forge.game.card.Card} object.
* @param block
@@ -2031,16 +2033,18 @@ public class ComputerUtilCombat {
* @param defender
* @param overrideOrder overriding combatant order
*/
- public static Map distributeAIDamage(final Card attacker, final CardCollectionView block, final CardCollectionView remaining, int dmgCanDeal, GameEntity defender, boolean overrideOrder) {
+ public static Map distributeAIDamage(final Player self, final Card attacker, final CardCollectionView block, final CardCollectionView remaining, int dmgCanDeal, GameEntity defender, boolean overrideOrder) {
// TODO: Distribute defensive Damage (AI controls how damage is dealt to own cards) for Banding and Defensive Formation
Map damageMap = Maps.newHashMap();
Combat combat = attacker.getGame().getCombat();
boolean isAttacking = defender != null;
+ // TODO: is this enough to determine the fact that we're legally assigning combat damage to our creatures?
+ boolean isAttackingMe = isAttacking && combat.getDefenderPlayerByAttacker(attacker).equals(self); // Banding, Defensive Formation - the AI assigns combat damage to itself
final boolean hasTrample = attacker.hasKeyword(Keyword.TRAMPLE);
- if (combat != null && remaining != null && hasTrample && attacker.isAttacking()) {
+ if (combat != null && remaining != null && hasTrample && attacker.isAttacking() && !isAttackingMe) {
// if attacker has trample and some of its blockers are also blocking others it's generally a good idea
// to assign those without trample first so we can maximize the damage to the defender
for (final Card c : remaining) {
@@ -2061,7 +2065,7 @@ public class ComputerUtilCombat {
final Card blocker = block.getFirst();
int dmgToBlocker = dmgCanDeal;
- if (hasTrample && isAttacking) { // otherwise no entity to deliver damage via trample
+ if (hasTrample && isAttacking && !isAttackingMe) { // otherwise no entity to deliver damage via trample
dmgToBlocker = getEnoughDamageToKill(blocker, dmgCanDeal, attacker, true);
if (dmgCanDeal < dmgToBlocker) {
@@ -2078,32 +2082,38 @@ public class ComputerUtilCombat {
damageMap.put(blocker, dmgToBlocker);
} // 1 blocker
else {
- // Does the attacker deal lethal damage to all blockers
- //Blocking Order now determined after declare blockers
- Card lastBlocker = null;
- for (final Card b : block) {
- lastBlocker = b;
- final int dmgToKill = getEnoughDamageToKill(b, dmgCanDeal, attacker, true);
- if (dmgToKill <= dmgCanDeal) {
- damageMap.put(b, dmgToKill);
- dmgCanDeal -= dmgToKill;
- } else {
- // if it can't be killed choose the minimum damage
- int dmg = Math.min(b.getLethalDamage(), dmgCanDeal);
- damageMap.put(b, dmg);
- dmgCanDeal -= dmg;
- if (dmgCanDeal <= 0) {
- break;
+ if (!isAttackingMe) {
+ // Does the attacker deal lethal damage to all blockers
+ //Blocking Order now determined after declare blockers
+ Card lastBlocker = null;
+ for (final Card b : block) {
+ lastBlocker = b;
+ final int dmgToKill = getEnoughDamageToKill(b, dmgCanDeal, attacker, true);
+ if (dmgToKill <= dmgCanDeal) {
+ damageMap.put(b, dmgToKill);
+ dmgCanDeal -= dmgToKill;
+ } else {
+ // if it can't be killed choose the minimum damage
+ int dmg = Math.min(b.getLethalDamage(), dmgCanDeal);
+ damageMap.put(b, dmg);
+ dmgCanDeal -= dmg;
+ if (dmgCanDeal <= 0) {
+ break;
+ }
+ }
+ } // for
+
+ 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
+ damageMap.put(lastBlocker, dmgCanDeal + damageMap.get(lastBlocker));
}
}
- } // for
-
- 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
- damageMap.put(lastBlocker, dmgCanDeal + damageMap.get(lastBlocker));
- }
+ } else {
+ // In the event of Banding or Defensive Formation, assign max damage to a single blocker to lose
+ // as little as possible
+ damageMap.put(block.getFirst(), dmgCanDeal);
}
}
return damageMap;
diff --git a/forge-ai/src/main/java/forge/ai/PlayerControllerAi.java b/forge-ai/src/main/java/forge/ai/PlayerControllerAi.java
index d8b7f053ffd..5b2e1834c62 100644
--- a/forge-ai/src/main/java/forge/ai/PlayerControllerAi.java
+++ b/forge-ai/src/main/java/forge/ai/PlayerControllerAi.java
@@ -106,7 +106,7 @@ public class PlayerControllerAi extends PlayerController {
@Override
public Map assignCombatDamage(Card attacker, CardCollectionView blockers, CardCollectionView remaining, int damageDealt, GameEntity defender, boolean overrideOrder) {
- return ComputerUtilCombat.distributeAIDamage(attacker, blockers, remaining, damageDealt, defender, overrideOrder);
+ return ComputerUtilCombat.distributeAIDamage(player, attacker, blockers, remaining, damageDealt, defender, overrideOrder);
}
@Override