Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow dropship nose LRM indirect fire #4612

Merged
merged 2 commits into from
Jul 10, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions megamek/src/megamek/client/ui/swing/GameOptionsDialog.java
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
30 changes: 21 additions & 9 deletions megamek/src/megamek/common/Compute.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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
22 changes: 10 additions & 12 deletions megamek/src/megamek/common/actions/WeaponAttackAction.java
Original file line number Diff line number Diff line change
Expand Up @@ -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");
}
}
Expand Down Expand Up @@ -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

if ((Compute.useSpheroidAtmosphere(game, ae) ||
(ae.isAero() && ((IAero) ae).isSpheroid() && (ae.getAltitude() == 0) && game.getBoard().onGround()))
&& weapon != null) {
int altDif = target.getAltitude() - ae.getAltitude();
&& (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 ((weapon.getLocation() == Aero.LOC_NOSE) && (altDif < 1)
&& 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
Expand Down Expand Up @@ -2196,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)) {
Expand Down Expand Up @@ -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");
}

Expand Down