diff --git a/megamek/src/megamek/client/ui/swing/OffBoardTargetOverlay.java b/megamek/src/megamek/client/ui/swing/OffBoardTargetOverlay.java index 2a5bd5838db..9f908b18b24 100644 --- a/megamek/src/megamek/client/ui/swing/OffBoardTargetOverlay.java +++ b/megamek/src/megamek/client/ui/swing/OffBoardTargetOverlay.java @@ -315,7 +315,7 @@ private void handleButtonClick(OffBoardDirection direction) { JOptionPane.QUESTION_MESSAGE, null, SharedUtility .getDisplayArray(eligibleTargets), null); choice = SharedUtility.getTargetPicked(eligibleTargets, input); - } else if (eligibleTargets.size() == 1) { + } else if ((eligibleTargets.size() == 1) && (eligibleTargets.get(0) != null)) { choice = eligibleTargets.get(0); } else { return; diff --git a/megamek/src/megamek/client/ui/swing/boardview/BoardView1.java b/megamek/src/megamek/client/ui/swing/boardview/BoardView1.java index 4e8a5fc3651..4fb2376de1a 100644 --- a/megamek/src/megamek/client/ui/swing/boardview/BoardView1.java +++ b/megamek/src/megamek/client/ui/swing/boardview/BoardView1.java @@ -6132,13 +6132,15 @@ public String getHexTooltip(MouseEvent e) { return txt.toString(); } - private ArrayList getArtilleryAttacksAtLocation( - Coords c) { + private ArrayList getArtilleryAttacksAtLocation(Coords c) { ArrayList v = new ArrayList(); + for (Enumeration attacks = game .getArtilleryAttacks(); attacks.hasMoreElements(); ) { ArtilleryAttackAction a = attacks.nextElement(); - if (a.getTarget(game).getPosition().equals(c)) { + Targetable target = a.getTarget(game); + + if ((target != null) && c.equals(target.getPosition())) { v.add(a); } } diff --git a/megamek/src/megamek/common/Targetable.java b/megamek/src/megamek/common/Targetable.java index febace6a64e..22eb911cfb1 100644 --- a/megamek/src/megamek/common/Targetable.java +++ b/megamek/src/megamek/common/Targetable.java @@ -17,6 +17,8 @@ import java.io.Serializable; import java.util.Map; +import megamek.common.annotations.Nullable; + public interface Targetable extends Serializable { public static final int TYPE_ENTITY = 0; public static final int TYPE_HEX_CLEAR = 1; @@ -151,4 +153,17 @@ default boolean tracksHeat() { @Override int hashCode(); + + /** + * Utility function used to safely tell whether two Targetables are in the same hex. + * Does not throw exceptions in case of nulls. + */ + public static boolean areAtSamePosition(@Nullable Targetable first, @Nullable Targetable second) { + if ((first == null) || (second == null) || + (first.getPosition() == null) || (second.getPosition() == null)) { + return false; + } + + return first.getPosition().equals(second.getPosition()); + } } diff --git a/megamek/src/megamek/common/weapons/ArtilleryWeaponIndirectFireHandler.java b/megamek/src/megamek/common/weapons/ArtilleryWeaponIndirectFireHandler.java index 8dca1e2debe..c7061a8aa57 100644 --- a/megamek/src/megamek/common/weapons/ArtilleryWeaponIndirectFireHandler.java +++ b/megamek/src/megamek/common/weapons/ArtilleryWeaponIndirectFireHandler.java @@ -396,7 +396,11 @@ else if ((null != bestSpotter) && !(this instanceof ArtilleryWeaponDirectFireHan AreaEffectHelper.clearMineFields(targetPos, Minefield.CLEAR_NUMBER_WEAPON, ae, vPhaseReport, game, server); } - if(aaa.getTarget(game).isOffBoard()) { + Targetable updatedTarget = aaa.getTarget(game); + + // the attack's target may have been destroyed or fled since the attack was generated + // so we need to carry out offboard/null checks against the "current" version of the target. + if ((updatedTarget != null) && updatedTarget.isOffBoard()) { DamageFalloff df = AreaEffectHelper.calculateDamageFallOff(atype, shootingBA, mineClear); int actualDamage = df.damage - (df.falloff * targetPos.distance(target.getPosition())); Coords effectiveTargetPos = aaa.getCoords(); @@ -406,7 +410,7 @@ else if ((null != bestSpotter) && !(this instanceof ArtilleryWeaponDirectFireHan } if(actualDamage > 0) { - AreaEffectHelper.artilleryDamageEntity((Entity) aaa.getTarget(game), actualDamage, null, 0, false, asfFlak, isFlak, altitude, + AreaEffectHelper.artilleryDamageEntity((Entity) updatedTarget, actualDamage, null, 0, false, asfFlak, isFlak, altitude, effectiveTargetPos, atype, targetPos, false, ae, null, altitude, vPhaseReport, server); } } else { diff --git a/megamek/src/megamek/server/Server.java b/megamek/src/megamek/server/Server.java index ffdc8c6bf0b..e670bc50162 100644 --- a/megamek/src/megamek/server/Server.java +++ b/megamek/src/megamek/server/Server.java @@ -13481,14 +13481,16 @@ private void processAttack(Entity entity, Vector vector) { boolean firingAtNewHex = false; final ArtilleryAttackAction aaa = (ArtilleryAttackAction) ea; final Entity firingEntity = game.getEntity(aaa.getEntityId()); + Targetable attackTarget = aaa.getTarget(game); + for (Enumeration j = game.getAttacks(); !firingAtNewHex && j.hasMoreElements(); ) { WeaponHandler wh = (WeaponHandler) j.nextElement(); if (wh.waa instanceof ArtilleryAttackAction) { ArtilleryAttackAction oaaa = (ArtilleryAttackAction) wh.waa; + if ((oaaa.getEntityId() == aaa.getEntityId()) - && !oaaa.getTarget(game).getPosition() - .equals(aaa.getTarget(game).getPosition())) { + && !Targetable.areAtSamePosition(oaaa.getTarget(game), attackTarget)) { firingAtNewHex = true; } }