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

Add VTOL/WiGE exemption for AAA and LAA missiles #6611

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
129 changes: 71 additions & 58 deletions megamek/src/megamek/client/bot/princess/FireControl.java
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,8 @@ public class FireControl {
static final TargetRollModifier TH_AIR_STRIKE_PATH = new TargetRollModifier(TargetRoll.IMPOSSIBLE,
"target not under flight path");
static final TargetRollModifier TH_AIR_STRIKE = new TargetRollModifier(2, "strike attack");
static final TargetRollModifier TH_AAA_AT_GROUND_TARGET = new TargetRollModifier(+4, "AAA/LAA vs non-flying ground target");
static final TargetRollModifier TH_AAA_TOO_LOW = new TargetRollModifier(+4, "AAA/LAA from below 4 Altitude");
private static final TargetRollModifier TH_STABLE_WEAPON = new TargetRollModifier(1, "stabilized weapon quirk");
private static final TargetRollModifier TH_PHY_LARGE = new TargetRollModifier(-2, "target large vehicle");
static final TargetRollModifier TH_NO_TARGETS_IN_BLAST = new TargetRollModifier(TargetRoll.IMPOSSIBLE, "no targets in blast zone!");
Expand Down Expand Up @@ -927,74 +929,85 @@ ToHitData guessToHitModifierForWeapon(final Entity shooter,
// ammo mods
if (AmmoType.T_NA != weaponType.getAmmoType()
&& (null != firingAmmo)
&& (firingAmmo.getType() instanceof AmmoType)) {
final AmmoType ammoType = (AmmoType) firingAmmo.getType();
if (null != ammoType) {
// Set of munitions we'll consider for Flak targeting
EnumSet<AmmoType.Munitions> aaMunitions = EnumSet.of(
AmmoType.Munitions.M_CLUSTER,
AmmoType.Munitions.M_FLAK);
EnumSet<AmmoType.Munitions> ArtyOnlyMunitions = EnumSet.of(
AmmoType.Munitions.M_FLECHETTE,
AmmoType.Munitions.M_FAE);
EnumSet<AmmoType.Munitions> homingMunitions = EnumSet.of(
AmmoType.Munitions.M_HOMING);
if (0 != ammoType.getToHitModifier()) {
toHit.addModifier(ammoType.getToHitModifier(), TH_AMMO_MOD);
&& (firingAmmo.getType() instanceof AmmoType ammoType)) {
// Set of munitions we'll consider for Flak targeting
EnumSet<AmmoType.Munitions> aaMunitions = EnumSet.of(
AmmoType.Munitions.M_CLUSTER,
AmmoType.Munitions.M_FLAK);
EnumSet<AmmoType.Munitions> ArtyOnlyMunitions = EnumSet.of(
AmmoType.Munitions.M_FLECHETTE,
AmmoType.Munitions.M_FAE);
EnumSet<AmmoType.Munitions> homingMunitions = EnumSet.of(
AmmoType.Munitions.M_HOMING);
if (0 != ammoType.getToHitModifier()) {
toHit.addModifier(ammoType.getToHitModifier(), TH_AMMO_MOD);
}
// Air-defense Arrow IV handling; can only fire at airborne targets
if (ammoType.getMunitionType().contains(AmmoType.Munitions.M_ADA)) {
if (target.isAirborne() || target.isAirborneVTOLorWIGE()) {
toHit.addModifier(TH_WEAPON_ADA);
} else {
toHit.addModifier(TH_WEAPON_CANNOT_FIRE);
}
// Air-defense Arrow IV handling; can only fire at airborne targets
if (ammoType.getMunitionType().contains(AmmoType.Munitions.M_ADA)) {
if (target.isAirborne() || target.isAirborneVTOLorWIGE()) {
toHit.addModifier(TH_WEAPON_ADA);
} else {
toHit.addModifier(TH_WEAPON_CANNOT_FIRE);
}
}
// Handle cluster, flak, AAA vs Airborne, Arty-only vs Airborne
if (target.isAirborne() || target.isAirborneVTOLorWIGE()) {
if (ammoType.getMunitionType().stream().anyMatch(aaMunitions::contains)
|| ammoType.countsAsFlak()) {
toHit.addModifier(TH_WEAPON_FLAK);
} else if (ammoType.getMunitionType().stream().anyMatch(ArtyOnlyMunitions::contains)) {
toHit.addModifier(TH_WEAPON_CANNOT_FIRE);
}
// Handle cluster, flak, AAA vs Airborne, Arty-only vs Airborne
if (target.isAirborne() || target.isAirborneVTOLorWIGE()) {
if (ammoType.getMunitionType().stream().anyMatch(aaMunitions::contains)
|| ammoType.countsAsFlak()) {
toHit.addModifier(TH_WEAPON_FLAK);
} else if (ammoType.getMunitionType().stream().anyMatch(ArtyOnlyMunitions::contains)) {
toHit.addModifier(TH_WEAPON_CANNOT_FIRE);
}
// Handle homing munitions
if (ammoType.getMunitionType().stream().anyMatch(homingMunitions::contains)) {
if (game.getPhase().isOffboard()) {
final StringBuilder msg = new StringBuilder("Estimating to-hit for Homing artillery fire by ")
.append(shooter.getDisplayName());

// Check all friends with TAG for proximity to enemies.
Entity friendlySpotter = Compute.findTAGSpotter(game, shooter, target, true);

if (friendlySpotter != null) {
// If we've got one friendly TAGger in range, roll them bones!
msg.append("\nSurvey says we've got friendly TAG unit ")
.append(friendlySpotter.getDisplayName())
.append(" nearby; fingers crossed!");
toHit.addModifier(TH_HOMING_TARGET_TAGGED);
} else {
// Can't hit without TAG support on-site!
msg.append("\nUnfortunately we have no friends near the target...");
toHit.addModifier(TH_HOMING_TARGET_UNTAGGED);
}

logger.debug(msg.toString());
}
// Handle homing munitions
if (ammoType.getMunitionType().stream().anyMatch(homingMunitions::contains)) {
if (game.getPhase().isOffboard()) {
final StringBuilder msg = new StringBuilder("Estimating to-hit for Homing artillery fire by ")
.append(shooter.getDisplayName());

// Check all friends with TAG for proximity to enemies.
Entity friendlySpotter = Compute.findTAGSpotter(game, shooter, target, true);

if (friendlySpotter != null) {
// If we've got one friendly TAGger in range, roll them bones!
msg.append("\nSurvey says we've got friendly TAG unit ")
.append(friendlySpotter.getDisplayName())
.append(" nearby; fingers crossed!");
toHit.addModifier(TH_HOMING_TARGET_TAGGED);
} else {
// Can't hit without TAG support on-site!
msg.append("\nUnfortunately we have no friends near the target...");
toHit.addModifier(TH_HOMING_TARGET_UNTAGGED);
}
}

logger.debug(msg.toString());
}
// Guesstimate Heat-Seeking Ammo mods
if (ammoType.getMunitionType().contains(AmmoType.Munitions.M_HEAT_SEEKING)) {
if (targetState.getHeat() > 0) {
// Hot target good
toHit.addModifier(-targetState.getHeat() / 5, TH_AMMO_MOD);
} else {
// Cold target bad
toHit.addModifier(1, TH_AMMO_MOD);
}
}

// Guesstimate Heat-Seeking Ammo mods
if (ammoType.getMunitionType().contains(AmmoType.Munitions.M_HEAT_SEEKING)) {
if (targetState.getHeat() > 0) {
// Hot target good
toHit.addModifier(-targetState.getHeat() / 5, TH_AMMO_MOD);
} else {
// Cold target bad
toHit.addModifier(1, TH_AMMO_MOD);
// Guesstimate Air-to-Air missile mods; targetState values are less accurate but workable
if ((ammoType.getAmmoType() == AmmoType.T_AAA_MISSILE) || (ammoType.getAmmoType() == AmmoType.T_LAA_MISSILE)) {
if (!targetState.isAirborneAero()) {
// Mod for firing at non-flying ground
if (!targetState.isAirborne()) {
toHit.addModifier(TH_AAA_AT_GROUND_TARGET);
}
if (shooter.getAltitude() < 4) {
toHit.addModifier(TH_AAA_TOO_LOW);
}
}
}

}

// targeting computer
Expand Down
9 changes: 6 additions & 3 deletions megamek/src/megamek/common/actions/WeaponAttackAction.java
Original file line number Diff line number Diff line change
Expand Up @@ -3363,8 +3363,11 @@ private static ToHitData compileAmmoToHitMods(Game game, Entity ae, Targetable t
// Air-to-air Arrow and Light Air-to-air missiles
if (((atype.getAmmoType() == AmmoType.T_AAA_MISSILE) || (atype.getAmmoType() == AmmoType.T_LAA_MISSILE))
&& Compute.isAirToGround(ae, target)) {
// +4 penalty if trying to use one against a ground target
toHit.addModifier(+4, Messages.getString("WeaponAttackAction.AaaGroundAttack"));
// +4 penalty if trying to use one against a ground target that is not flying
// (Errata: https://bg.battletech.com/forums/index.php?topic=87401.msg2060972#msg2060972 )
if (!target.isAirborneVTOLorWIGE()) {
toHit.addModifier(+4, Messages.getString("WeaponAttackAction.AaaGroundAttack"));
}
// +3 additional if the attacker is flying at Altitude 3 or less
if (ae.getAltitude() < 4) {
toHit.addModifier(+3, Messages.getString("WeaponAttackAction.AaaLowAlt"));
Expand Down Expand Up @@ -3844,7 +3847,7 @@ private static ToHitData compileAeroAttackerToHitMods(Game game, Entity ae, Targ
if (ae.isAero()) {
IAero aero = (IAero) ae;

// check for heavy gauss rifle on fighter of small craft
// check for heavy gauss rifle on fighter or small craft
// Arguably a weapon effect, except that it only applies when used by a fighter
// (isn't recoil fun?)
// So it's here instead of with other weapon mods that apply across the board
Expand Down