From caa8feeea8069de22f56281081e2a62d9b6f1248 Mon Sep 17 00:00:00 2001 From: NickAragua Date: Sat, 8 Jul 2023 22:58:05 -0400 Subject: [PATCH 1/2] Allow dropship nose LRM indirect fire --- .../client/ui/swing/GameOptionsDialog.java | 1 + megamek/src/megamek/common/Compute.java | 30 +++++++++++++------ .../common/actions/WeaponAttackAction.java | 24 +++++++-------- 3 files changed, 33 insertions(+), 22 deletions(-) diff --git a/megamek/src/megamek/client/ui/swing/GameOptionsDialog.java b/megamek/src/megamek/client/ui/swing/GameOptionsDialog.java index a8613ae613c..dc1184ea8a4 100644 --- a/megamek/src/megamek/client/ui/swing/GameOptionsDialog.java +++ b/megamek/src/megamek/client/ui/swing/GameOptionsDialog.java @@ -356,6 +356,7 @@ private void addSearchPanel() { lblSearch.setLabelFor(txtSearch); lblSearch.setToolTipText(Messages.getString("GameOptionsDialog.SearchToolTip")); txtSearch.setToolTipText(Messages.getString("GameOptionsDialog.SearchToolTip")); + txtSearch.setColumns(20); txtSearch.getDocument().addDocumentListener(new DocumentListener() { @Override public void changedUpdate(DocumentEvent e) { diff --git a/megamek/src/megamek/common/Compute.java b/megamek/src/megamek/common/Compute.java index 4176ee72551..9f4c91d35c4 100644 --- a/megamek/src/megamek/common/Compute.java +++ b/megamek/src/megamek/common/Compute.java @@ -21,7 +21,6 @@ import megamek.common.enums.AimingMode; import megamek.common.enums.BasementType; import megamek.common.enums.IlluminationLevel; -import megamek.common.options.Option; import megamek.common.options.OptionsConstants; import megamek.common.weapons.InfantryAttack; import megamek.common.weapons.Weapon; @@ -1338,15 +1337,10 @@ public static ToHitData getRangeMods(Game game, Entity ae, int weaponId, if (isIndirect) { c3spotter = ae; // no c3 when using indirect fire } - if (isIndirect - && game.getOptions().booleanOption(OptionsConstants.BASE_INDIRECT_FIRE) - && !game.getOptions().booleanOption(OptionsConstants.ADVCOMBAT_INDIRECT_ALWAYS_POSSIBLE) - && LosEffects.calculateLOS(game, ae, target).canSee() - && (!game.getOptions().booleanOption(OptionsConstants.ADVANCED_DOUBLE_BLIND) || Compute - .canSee(game, ae, target)) - && !(wtype instanceof MekMortarWeapon)) { + + if (isIndirect && indirectAttackImpossible(game, ae, target, wtype, weapon)) { return new ToHitData(TargetRoll.IMPOSSIBLE, - "Indirect fire impossible with direct LOS"); + Messages.getString("WeaponAttackAction.NoIndirectWithLOS")); } int c3dist = Compute.effectiveDistance(game, c3spotter, target, false); @@ -7187,5 +7181,23 @@ public static boolean useSpheroidAtmosphere(Game game, Entity en) { // are we in atmosphere? return en.isAirborne(); } + + /** + * Worker function that checks if an indirect attack is impossible for the given passed-in arguments + */ + public static boolean indirectAttackImpossible(Game game, Entity ae, Targetable target, WeaponType wtype, Mounted weapon) { + boolean isLandedSpheroid = ae.isAero() && ((IAero) ae).isSpheroid() && (ae.getAltitude() == 0) && game.getBoard().onGround(); + int altDif = target.getAltitude() - ae.getAltitude(); + boolean noseWeaponAimedAtGroundTarget = (weapon != null) && (weapon.getLocation() == Aero.LOC_NOSE) && (altDif < 1); + + return game.getOptions().booleanOption(OptionsConstants.BASE_INDIRECT_FIRE) + && !game.getOptions().booleanOption(OptionsConstants.ADVCOMBAT_INDIRECT_ALWAYS_POSSIBLE) + && LosEffects.calculateLOS(game, ae, target).canSee() + && (!game.getOptions().booleanOption(OptionsConstants.ADVANCED_DOUBLE_BLIND) + || Compute.canSee(game, ae, target)) + && !(wtype instanceof ArtilleryCannonWeapon) + && !wtype.hasFlag(WeaponType.F_MORTARTYPE_INDIRECT) + && !(isLandedSpheroid && noseWeaponAimedAtGroundTarget); + } } // End public class Compute diff --git a/megamek/src/megamek/common/actions/WeaponAttackAction.java b/megamek/src/megamek/common/actions/WeaponAttackAction.java index 255b72c99fb..c318e518a73 100644 --- a/megamek/src/megamek/common/actions/WeaponAttackAction.java +++ b/megamek/src/megamek/common/actions/WeaponAttackAction.java @@ -1148,7 +1148,7 @@ private static String toHitIsImpossible(Game game, Entity ae, int attackerId, Ta // Infantry can't clear woods. if (isAttackerInfantry && (Targetable.TYPE_HEX_CLEAR == target.getTargetType())) { Hex hexTarget = game.getBoard().getHex(target.getPosition()); - if (hexTarget.containsTerrain(Terrains.WOODS)) { + if ((hexTarget != null) && hexTarget.containsTerrain(Terrains.WOODS)) { return Messages.getString("WeaponAttackAction.NoInfantryWoodsClearing"); } } @@ -1508,21 +1508,25 @@ private static String toHitIsImpossible(Game game, Entity ae, int attackerId, Ta // for spheroid dropships in atmosphere (and on ground), the rules about // firing arcs are more complicated // TW errata 2.1 + boolean isLandedSpheroid = ae.isAero() && ((IAero) ae).isSpheroid() && (ae.getAltitude() == 0) && game.getBoard().onGround(); + int altDif = target.getAltitude() - ae.getAltitude(); + boolean noseWeaponAimedAtGroundTarget = (weapon != null) && (weapon.getLocation() == Aero.LOC_NOSE) && (altDif < 1); + if ((Compute.useSpheroidAtmosphere(game, ae) || - (ae.isAero() && ((IAero) ae).isSpheroid() && (ae.getAltitude() == 0) && game.getBoard().onGround())) - && weapon != null) { - int altDif = target.getAltitude() - ae.getAltitude(); + isLandedSpheroid) + && (weapon != null)) { int range = Compute.effectiveDistance(game, ae, target, false); // Only aft-mounted weapons can be fired at range 0 (targets directly underneath) if (!ae.isAirborne() && (range == 0) && (weapon.getLocation() != Aero.LOC_AFT)) { return Messages.getString("WeaponAttackAction.OnlyAftAtZero"); } // Nose-mounted weapons can only be fired at targets at least 1 altitude higher - if ((weapon.getLocation() == Aero.LOC_NOSE) && (altDif < 1) + if (noseWeaponAimedAtGroundTarget && wtype != null // Unless the weapon is used as artillery && (!(wtype instanceof ArtilleryWeapon || wtype.hasFlag(WeaponType.F_ARTILLERY) - || (ae.getAltitude() == 0 && wtype instanceof CapitalMissileWeapon)))) { + || (ae.getAltitude() == 0 && wtype instanceof CapitalMissileWeapon) + || isIndirect))) { return Messages.getString("WeaponAttackAction.TooLowForNose"); } // Front-side-mounted weapons can only be fired at targets at the same altitude or higher @@ -2277,13 +2281,7 @@ private static String toHitIsImpossible(Game game, Entity ae, int attackerId, Ta // Indirect Fire (LRMs) // Can't fire Indirect LRM with direct LOS - if (isIndirect && game.getOptions().booleanOption(OptionsConstants.BASE_INDIRECT_FIRE) - && !game.getOptions().booleanOption(OptionsConstants.ADVCOMBAT_INDIRECT_ALWAYS_POSSIBLE) - && LosEffects.calculateLOS(game, ae, target).canSee() - && (!game.getOptions().booleanOption(OptionsConstants.ADVANCED_DOUBLE_BLIND) - || Compute.canSee(game, ae, target)) - && !(wtype instanceof ArtilleryCannonWeapon) - && !wtype.hasFlag(WeaponType.F_MORTARTYPE_INDIRECT)) { + if (isIndirect && Compute.indirectAttackImpossible(game, ae, target, wtype, weapon)) { return Messages.getString("WeaponAttackAction.NoIndirectWithLOS"); } From b6eefa1254c92c9ef60a45c1c6b90733833d0c61 Mon Sep 17 00:00:00 2001 From: NickAragua Date: Sat, 8 Jul 2023 23:06:54 -0400 Subject: [PATCH 2/2] revert unnecessary change for efficiency --- .../megamek/common/actions/WeaponAttackAction.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/megamek/src/megamek/common/actions/WeaponAttackAction.java b/megamek/src/megamek/common/actions/WeaponAttackAction.java index c318e518a73..e62a089f850 100644 --- a/megamek/src/megamek/common/actions/WeaponAttackAction.java +++ b/megamek/src/megamek/common/actions/WeaponAttackAction.java @@ -1508,20 +1508,20 @@ private static String toHitIsImpossible(Game game, Entity ae, int attackerId, Ta // for spheroid dropships in atmosphere (and on ground), the rules about // firing arcs are more complicated // TW errata 2.1 - boolean isLandedSpheroid = ae.isAero() && ((IAero) ae).isSpheroid() && (ae.getAltitude() == 0) && game.getBoard().onGround(); - int altDif = target.getAltitude() - ae.getAltitude(); - boolean noseWeaponAimedAtGroundTarget = (weapon != null) && (weapon.getLocation() == Aero.LOC_NOSE) && (altDif < 1); if ((Compute.useSpheroidAtmosphere(game, ae) || - isLandedSpheroid) + (ae.isAero() && ((IAero) ae).isSpheroid() && (ae.getAltitude() == 0) && game.getBoard().onGround())) && (weapon != null)) { int range = Compute.effectiveDistance(game, ae, target, false); // Only aft-mounted weapons can be fired at range 0 (targets directly underneath) if (!ae.isAirborne() && (range == 0) && (weapon.getLocation() != Aero.LOC_AFT)) { return Messages.getString("WeaponAttackAction.OnlyAftAtZero"); } + + int altDif = target.getAltitude() - ae.getAltitude(); + // Nose-mounted weapons can only be fired at targets at least 1 altitude higher - if (noseWeaponAimedAtGroundTarget + if ((weapon.getLocation() == Aero.LOC_NOSE) && (altDif < 1) && wtype != null // Unless the weapon is used as artillery && (!(wtype instanceof ArtilleryWeapon || wtype.hasFlag(WeaponType.F_ARTILLERY) @@ -2200,7 +2200,7 @@ private static String toHitIsImpossible(Game game, Entity ae, int attackerId, Ta return Messages.getString("WeaponAttackAction.InvalidForFirefighting"); } Hex hexTarget = game.getBoard().getHex(target.getPosition()); - if (!hexTarget.containsTerrain(Terrains.FIRE)) { + if ((hexTarget != null) && !hexTarget.containsTerrain(Terrains.FIRE)) { return Messages.getString("WeaponAttackAction.TargetNotBurning"); } } else if (wtype.hasFlag(WeaponType.F_EXTINGUISHER)) {