From 683fc9114e936f214fbc9a6af06141bbf89e262c Mon Sep 17 00:00:00 2001 From: sleet01 Date: Sat, 23 Sep 2023 17:33:41 -0700 Subject: [PATCH 1/3] Fix for #4801: NPE while Princess moves BAs This fixes an issue introduced by my attempt to add Field Gun / Field Artillery expected damage calculations for infantry units under Princess. Unfortunately, I didn't realize that the same code path was used by all non-infantry units when Princess projects how much damage _any_ unit will take; in this case, BA attackers trying to compute expected damage against non-enemy targets. The root of the issue is trying to dereference linked ammo for BA weapons, which don't have that concept in some cases. This throws an NPE, which gets caught, causing a null 'mp' object to be returned, which creates a _new_ NPE. Fix is to clean up that area of code and restrict the special weapon handling to infantry only. All others, including BAs, will go back to the previous method of projecting expected damage. Testing: - Re-ran reporter's attached saved game. - Confirmed the update for field guns / field artillery still works correctly. - Ran all unit tests. --- .../bot/princess/InfantryFireControl.java | 29 ++++++++++--------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/megamek/src/megamek/client/bot/princess/InfantryFireControl.java b/megamek/src/megamek/client/bot/princess/InfantryFireControl.java index 89b1d6b57b7..851a3785a96 100644 --- a/megamek/src/megamek/client/bot/princess/InfantryFireControl.java +++ b/megamek/src/megamek/client/bot/princess/InfantryFireControl.java @@ -67,8 +67,7 @@ public double getMaxDamageAtRange(final MovePath shooterPath, final MovePath tar boolean shooterIsActualInfantry = shooter.hasETypeFlag(Entity.ETYPE_INFANTRY) && !shooter.hasETypeFlag(Entity.ETYPE_BATTLEARMOR); // field guns can't fire if the unit in question moved - boolean fieldGunsDoDamage = (shooterIsActualInfantry && shooterPath.getMpUsed() == 0) - || !shooterIsActualInfantry; + boolean otherWeaponsMayShoot = !shooterIsActualInfantry || shooterPath.getMpUsed() == 0; boolean inBuilding = Compute.isInBuilding(target.getGame(), targetPath.getFinalElevation(), targetPath.getFinalCoords()); boolean inOpen = ServerHelper.infantryInOpen(target, targetHex, target.getGame(), targetIsPlatoon, false, @@ -96,8 +95,8 @@ public double getMaxDamageAtRange(final MovePath shooterPath, final MovePath tar // weapon damage, multiply by building dmg reduction. // 4. Shooter is non-infantry, target is infantry in "cover". Use // "directBlowInfantryDamage". - // 5. Shooter is non-infantry, target is non-infantry. Use base - // class. + // 5. Shooter is infantry with field gun / field artillery and needs special damage calc. + // 6. Shooter is non-infantry, target is non-infantry. Use base class. // case 1 if (weaponType.hasFlag(WeaponType.F_INFANTRY)) { @@ -113,7 +112,7 @@ public double getMaxDamageAtRange(final MovePath shooterPath, final MovePath tar * infantryCount; // field guns can't fire if the infantry unit has done anything // other than turning - } else if (targetIsActualInfantry && fieldGunsDoDamage) { + } else if (targetIsActualInfantry && otherWeaponsMayShoot) { double damage = 0; // if we're outside, use the direct blow infantry damage @@ -134,15 +133,17 @@ public double getMaxDamageAtRange(final MovePath shooterPath, final MovePath tar } maxFGDamage += damage; - // field guns can't fire if the infantry unit has done anything - // other than turning - // case 5 - } else if (fieldGunsDoDamage) { - // Some FGs are artillery, so check both damage/shot * shot num _and_ rack size. - AmmoType ammoType = (AmmoType) weapon.getLinked().getType(); - int wDamage = (weaponType.hasFlag(F_ARTILLERY) ? weaponType.rackSize : weaponType.getDamage()); - int ammoDamage = ammoType.getDamagePerShot()*ammoType.getShots(); - maxFGDamage += Math.max(wDamage, ammoDamage); + } else if (otherWeaponsMayShoot) { + // case 5 + if (shooter.isInfantry()) { + // field guns can't fire if the infantry unit has done anything + // other than turning, so we only get here if infantry has not used MP. + // All valid Infantry Field Weapons can consider rackSize as their damage. + maxFGDamage += weaponType.rackSize; + // Case 6: all other unit types / weapons + } else { + maxFGDamage += weaponType.getDamage(); + } } } From 92a928b9a053f294abc27145704507a61dee2302 Mon Sep 17 00:00:00 2001 From: sleet01 Date: Sat, 23 Sep 2023 17:59:30 -0700 Subject: [PATCH 2/3] Fix 4801: replace call with existing boolean --- .../src/megamek/client/bot/princess/InfantryFireControl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/megamek/src/megamek/client/bot/princess/InfantryFireControl.java b/megamek/src/megamek/client/bot/princess/InfantryFireControl.java index 851a3785a96..18d6b020f75 100644 --- a/megamek/src/megamek/client/bot/princess/InfantryFireControl.java +++ b/megamek/src/megamek/client/bot/princess/InfantryFireControl.java @@ -135,7 +135,7 @@ public double getMaxDamageAtRange(final MovePath shooterPath, final MovePath tar maxFGDamage += damage; } else if (otherWeaponsMayShoot) { // case 5 - if (shooter.isInfantry()) { + if (shooterIsActualInfantry) { // field guns can't fire if the infantry unit has done anything // other than turning, so we only get here if infantry has not used MP. // All valid Infantry Field Weapons can consider rackSize as their damage. From c7fad9f6ea21aef4dfda29e8c8680e9b71743623 Mon Sep 17 00:00:00 2001 From: sleet01 Date: Sat, 23 Sep 2023 18:59:00 -0700 Subject: [PATCH 3/3] Final bit of cleanup based on IDE warnings. --- .../megamek/client/bot/princess/InfantryFireControl.java | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/megamek/src/megamek/client/bot/princess/InfantryFireControl.java b/megamek/src/megamek/client/bot/princess/InfantryFireControl.java index 18d6b020f75..0afeba12f48 100644 --- a/megamek/src/megamek/client/bot/princess/InfantryFireControl.java +++ b/megamek/src/megamek/client/bot/princess/InfantryFireControl.java @@ -22,8 +22,6 @@ import java.util.ArrayList; import java.util.List; -import static megamek.common.WeaponType.F_ARTILLERY; - /** * This class is intended to help the bot calculate firing plans for infantry * units. @@ -110,10 +108,8 @@ public double getMaxDamageAtRange(final MovePath shooterPath, final MovePath tar maxInfantryWeaponDamage += ((InfantryWeapon) weaponType).getInfantryDamage() * infantryCount; - // field guns can't fire if the infantry unit has done anything - // other than turning } else if (targetIsActualInfantry && otherWeaponsMayShoot) { - double damage = 0; + double damage; // if we're outside, use the direct blow infantry damage // calculation