Skip to content

Commit

Permalink
Merge pull request #4867 from MegaMek/infantry_weapon_modes
Browse files Browse the repository at this point in the history
Fixes for flame based infantry bugs
  • Loading branch information
neoancient authored Nov 9, 2023
2 parents 53877b3 + ca22a27 commit 5e796cb
Show file tree
Hide file tree
Showing 34 changed files with 385 additions and 105 deletions.
2 changes: 2 additions & 0 deletions megamek/i18n/megamek/common/options/messages.properties
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ GameOptionsInfo.option.rng_log.displayableName=RNG Log
GameOptionsInfo.option.rng_log.description=Whether or not to log the Random Number Generator.
GameOptionsInfo.option.flamer_heat.displayableName=Flamers per Battlemech Manual
GameOptionsInfo.option.flamer_heat.description=If checked, flamers deal both heat and damage. (BMM pg. 99) Otherwise, you have the option to choose heat or damage.
GameOptionsInfo.option.infantry_damage_heat.displayableName=(Unofficial) Infantry weapons like BMM flamers
GameOptionsInfo.option.infantry_damage_heat.description=If checked, flame-based infantry weapons will deal damage and heat like flamers.
GameOptionsInfo.option.indirect_fire.displayableName=Indirect fire
GameOptionsInfo.option.indirect_fire.description=If checked, LRMs may be fire indirectly, and players may choose to spot for indirect fire instead of attacking.\nChecked by default.
GameOptionsInfo.option.breeze.displayableName=Smoke Drift
Expand Down
4 changes: 2 additions & 2 deletions megamek/src/megamek/client/bot/princess/FireControl.java
Original file line number Diff line number Diff line change
Expand Up @@ -2166,7 +2166,7 @@ public boolean entityCanIndirectFireMissile(FireControlState fireControlState, E
}

for (Mounted weapon : shooter.getWeaponList()) {
if (weapon.getType().hasModeType(Weapon.MODE_MISSILE_INDIRECT)) {
if (weapon.hasModeType(Weapon.MODE_MISSILE_INDIRECT) || weapon.hasModeType(Weapon.MODE_INDIRECT_HEAT)) {
fireControlState.getEntityIDFStates().put(shooter.getId(), true);
return true;
}
Expand Down Expand Up @@ -3244,7 +3244,7 @@ private int switchMissileMode(Mounted weapon) {
// check that we're operating a missile weapon that can switch direct/indirect modes
// don't bother checking non-missile weapons
if (weapon.getType().hasFlag(Weapon.F_MISSILE) &&
weapon.getType().hasModeType(Weapon.MODE_MISSILE_INDIRECT)) {
(weapon.hasModeType(Weapon.MODE_MISSILE_INDIRECT) || weapon.hasModeType(Weapon.MODE_INDIRECT_HEAT))) {

// if we are able to switch the weapon to indirect fire mode, do so and try again
if (!weapon.curMode().equals(Weapon.MODE_MISSILE_INDIRECT)) {
Expand Down
2 changes: 1 addition & 1 deletion megamek/src/megamek/client/commands/FireCommand.java
Original file line number Diff line number Diff line change
Expand Up @@ -274,7 +274,7 @@ private String calculateToHit(int weaponId, Targetable target) {
str += " Can't shoot: "
+ Messages.getString("FiringDisplay.alreadyFired");
} else if ((m.getType().hasFlag(WeaponType.F_AUTO_TARGET) && !m.curMode().equals(Weapon.MODE_AMS_MANUAL))
|| (m.getType().hasModes() && m.curMode().equals("Point Defense"))) {
|| (m.hasModes() && m.curMode().equals("Point Defense"))) {
str += " Can't shoot: "
+ Messages.getString("FiringDisplay.autoFiringWeapon");
} else if (getClient().getGame().getPhase().isFiring() && m.isInBearingsOnlyMode()) {
Expand Down
6 changes: 3 additions & 3 deletions megamek/src/megamek/client/ui/swing/FiringDisplay.java
Original file line number Diff line number Diff line change
Expand Up @@ -1006,7 +1006,7 @@ protected void changeMode(boolean forward) {

// If the weapon does not have modes, just exit.
Mounted m = ce().getEquipment(wn);
if ((m == null) || !m.getType().hasModes()) {
if ((m == null) || !m.hasModes()) {
return;
}

Expand Down Expand Up @@ -1960,7 +1960,7 @@ public void updateTarget() {
setFireEnabled(false);
} else if ((m.getType().hasFlag(WeaponType.F_AUTO_TARGET)
&& !m.curMode().equals(Weapon.MODE_AMS_MANUAL))
|| (m.getType().hasModes() && m.curMode().equals("Point Defense"))) {
|| (m.hasModes() && m.curMode().equals("Point Defense"))) {
clientgui.getUnitDisplay().wPan.setToHit(Messages.getString("FiringDisplay.autoFiringWeapon"));
setFireEnabled(false);
} else if (m.isInBearingsOnlyMode()) {
Expand Down Expand Up @@ -2395,7 +2395,7 @@ protected void setFireModeEnabled(boolean enabled) {
* @param m The active weapon
*/
protected void adaptFireModeEnabled(Mounted m) {
setFireModeEnabled(m.isModeSwitchable() & m.getType().hasModes());
setFireModeEnabled(m.isModeSwitchable() && m.hasModes());
}

protected void setFireCalledEnabled(boolean enabled) {
Expand Down
4 changes: 2 additions & 2 deletions megamek/src/megamek/client/ui/swing/MapMenu.java
Original file line number Diff line number Diff line change
Expand Up @@ -1405,8 +1405,8 @@ private JMenu createModeMenu() {
int weaponNum = gui.getUnitDisplay().wPan.getSelectedWeaponNum();
Mounted mounted = myEntity.getEquipment(weaponNum);

if ((mounted != null) && mounted.getType().hasModes()) {
for (int pos = 0; pos < mounted.getType().getModesCount(); pos++) {
if ((mounted != null) && mounted.hasModes()) {
for (int pos = 0; pos < mounted.getModesCount(); pos++) {
menu.add(createModeJMenuItem(mounted, pos));
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -716,7 +716,7 @@ private void changeMode(boolean forward) {

// If the weapon does not have modes, just exit.
Mounted m = ce().getEquipment(wn);
if ((m == null) || !m.getType().hasModes()) {
if ((m == null) || !m.hasModes()) {
return;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -569,7 +569,7 @@ void toggleHotLoad(Collection<Entity> entities, boolean hotLoadOn) {
// TODO: The following should ideally be part of setHotLoad in Mounted
if (hotLoadOn) {
m.setMode("HotLoad");
} else if (m.getType().hasModeType("HotLoad")) {
} else if (m.hasModeType("HotLoad")) {
m.setMode("");
}
updateCandidates.add(entity);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -415,7 +415,7 @@ private String getMountedDisplay(Mounted m, int loc, CriticalSlot cs) {
sb.append(hotLoaded);
}

if (m.getType().hasModes()) {
if (m.hasModes()) {
if (!m.curMode().getDisplayableName().isEmpty()) {
sb.append(" (");
sb.append(m.curMode().getDisplayableName());
Expand Down Expand Up @@ -451,7 +451,7 @@ public void itemStateChanged(ItemEvent ev) {
&& (ev.getStateChange() == ItemEvent.SELECTED)) {
Mounted m = getSelectedEquipment();
CriticalSlot cs = getSelectedCritical();
if ((m != null) && m.getType().hasModes()) {
if ((m != null) && m.hasModes()) {
int nMode = m_chMode.getSelectedIndex();
if (nMode >= 0) {

Expand Down Expand Up @@ -760,7 +760,7 @@ public void valueChanged(ListSelectionEvent event) {
int round = client.getGame().getRoundCount();
boolean inSquadron = ((en instanceof Aero) && ((Aero) en)
.isInASquadron());
if ((m != null) && bOwner && m.getType().hasModes()) {
if ((m != null) && bOwner && m.hasModes()) {
if (!m.isInoperable() && !m.isDumping()
&& (en.isActive() || en.isActive(round) || inSquadron)
&& m.isModeSwitchable()) {
Expand Down
14 changes: 7 additions & 7 deletions megamek/src/megamek/client/ui/swing/unitDisplay/WeaponPanel.java
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,7 @@ public String getElementAt(int index) {
}

// Fire Mode - lots of things have variable modes
if (wtype.hasModes()) {
if (mounted.hasModes()) {
wn.append(' ');

wn.append(mounted.curMode().getDisplayableName());
Expand Down Expand Up @@ -1749,7 +1749,7 @@ private void displaySelected() {
}
wDamR.setText(damage.toString());
} else if (wtype.hasFlag(WeaponType.F_ENERGY)
&& wtype.hasModes()
&& mounted.hasModes()
&& (unitDisplay.getClientGUI() != null) && unitDisplay.getClientGUI().getClient().getGame().getOptions().booleanOption(
OptionsConstants.ADVCOMBAT_TACOPS_ENERGY_WEAPONS)) {
if (mounted.hasChargedCapacitor() != 0) {
Expand Down Expand Up @@ -1786,7 +1786,7 @@ private void displaySelected() {
extremeR = wtype.getWExtremeRange();
} else if (wtype.hasFlag(WeaponType.F_PDBAY)) {
//Point Defense bays have a variable range, depending on the mode they're in
if (wtype.hasModes() && mounted.curMode().equals("Point Defense")) {
if (mounted.hasModes() && mounted.curMode().equals("Point Defense")) {
shortR = 1;
wShortR.setText("1");
} else {
Expand Down Expand Up @@ -2369,7 +2369,7 @@ private void updateAttackValues(Mounted weapon, Mounted ammo) {
wShortR.setText("1-12");
} else if (wtype.hasFlag(WeaponType.F_PDBAY)) {
//Point Defense bays have a variable range too, depending on the mode they're in
if (wtype.hasModes() && weapon.curMode().equals("Point Defense")) {
if (weapon.hasModes() && weapon.curMode().equals("Point Defense")) {
wShortR.setText("1");
} else {
wShortR.setText("1-6");
Expand Down Expand Up @@ -2594,13 +2594,13 @@ private void compileWeaponBay(Mounted weapon, boolean isCapital) {
}
// check for bracketing
double mult = 1.0;
if (wtype.hasModes() && weapon.curMode().equals("Bracket 80%")) {
if (weapon.hasModes() && weapon.curMode().equals("Bracket 80%")) {
mult = 0.8;
}
if (wtype.hasModes() && weapon.curMode().equals("Bracket 60%")) {
if (weapon.hasModes() && weapon.curMode().equals("Bracket 60%")) {
mult = 0.6;
}
if (wtype.hasModes() && weapon.curMode().equals("Bracket 40%")) {
if (weapon.hasModes() && weapon.curMode().equals("Bracket 40%")) {
mult = 0.4;
}
avShort = mult * avShort;
Expand Down
4 changes: 2 additions & 2 deletions megamek/src/megamek/common/Compute.java
Original file line number Diff line number Diff line change
Expand Up @@ -6619,7 +6619,7 @@ public static int dialDownDamage(Mounted weapon, WeaponType wtype) {
public static int dialDownDamage(Mounted weapon, WeaponType wtype, int range) {
int toReturn = wtype.getDamage(range);

if (!wtype.hasModes()) {
if (!weapon.hasModes()) {
return toReturn;
}

Expand Down Expand Up @@ -6658,7 +6658,7 @@ public static int dialDownHeat(Mounted weapon, WeaponType wtype) {
public static int dialDownHeat(Mounted weapon, WeaponType wtype, int range) {
int toReturn = wtype.getHeat();

if (!wtype.hasModes()) {
if (!weapon.hasModes()) {
return toReturn;
}

Expand Down
12 changes: 5 additions & 7 deletions megamek/src/megamek/common/Entity.java
Original file line number Diff line number Diff line change
Expand Up @@ -3953,7 +3953,7 @@ public boolean isWeaponValidForPhase(Mounted mounted) {
&& (!(mounted.getType().hasFlag(WeaponType.F_AMS) && mounted.curMode().equals(Weapon.MODE_AMS_ON)))
&& (!(mounted.getType().hasFlag(WeaponType.F_AMS) && mounted.curMode().equals(Weapon.MODE_AMS_OFF)))
&& (!mounted.getType().hasFlag(WeaponType.F_AMSBAY))
&& (!(mounted.getType().hasModes() && mounted.curMode().equals("Point Defense")))
&& (!(mounted.hasModes() && mounted.curMode().equals("Point Defense")))
&& ((mounted.getLinked() == null)
|| mounted.getLinked().getType().hasFlag(MiscType.F_AP_MOUNT)
|| (mounted.getLinked().getUsableShotsLeft() > 0))) {
Expand Down Expand Up @@ -4375,7 +4375,7 @@ public int countWorkingMisc(BigInteger flag, int location) {
OUTER: for (Mounted m : getMisc()) {
if (!m.isInoperable() && m.getType().hasFlag(flag)
&& ((location == -1) || (m.getLocation() == location))) {
if (m.getType().hasModes()) {
if (m.hasModes()) {
for (Enumeration<EquipmentMode> e = m.getType().getModes(); e.hasMoreElements();) {
if (e.nextElement().equals("On") && !m.curMode().equals("On")) {
continue OUTER;
Expand All @@ -4393,7 +4393,7 @@ public int countWorkingMisc(String internalName, int location) {
OUTER: for (Mounted m : getMisc()) {
if (!m.isInoperable() && m.getType().getInternalName().equalsIgnoreCase(internalName)
&& ((location == -1) || (m.getLocation() == location))) {
if (m.getType().hasModes()) {
if (m.hasModes()) {
for (Enumeration<EquipmentMode> e = m.getType().getModes(); e.hasMoreElements();) {
if (e.nextElement().equals("On") && !m.curMode().equals("On")) {
continue OUTER;
Expand Down Expand Up @@ -11433,7 +11433,7 @@ public boolean hasLinkedMGA(Mounted mounted) {
&& m.getType().hasFlag(WeaponType.F_MGA)
&& !(m.isDestroyed() || m.isBreached())
&& m.getBayWeapons().contains(getEquipmentNum(mounted))
&& m.getType().hasModes() && m.curMode().equals("Linked")) {
&& m.hasModes() && m.curMode().equals("Linked")) {
return true;
}
}
Expand Down Expand Up @@ -11887,9 +11887,7 @@ public void setGameOptions() {
}

for (Mounted mounted : getWeaponList()) {
if (mounted.getType() instanceof Weapon) {
((Weapon) mounted.getType()).adaptToGameOptions(game.getOptions());
}
mounted.adaptToGameOptions(game.getOptions());
mounted.setModesForMapType();
}

Expand Down
10 changes: 10 additions & 0 deletions megamek/src/megamek/common/EquipmentMode.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@

package megamek.common;

import megamek.common.weapons.Weapon;

import java.util.Hashtable;
import java.util.Objects;

Expand Down Expand Up @@ -115,4 +117,12 @@ public boolean equals(String modeName) {
public String toString() {
return getName();
}

public boolean isHeat() {
return name.equals(Weapon.MODE_FLAMER_HEAT) || name.equals(Weapon.MODE_INDIRECT_HEAT);
}

public boolean isIndirect() {
return name.equals(Weapon.MODE_MISSILE_INDIRECT) || name.equals(Weapon.MODE_INDIRECT_HEAT);
}
}
118 changes: 118 additions & 0 deletions megamek/src/megamek/common/InfantryWeaponMounted.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
/*
* MegaMek - Copyright (C) 2023 - The MegaMek Team
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*/

package megamek.common;

import megamek.common.options.GameOptions;
import megamek.common.weapons.Weapon;
import megamek.common.weapons.infantry.InfantryWeapon;

import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;

/**
* Specialized mount used for infantry units with primary and secondary weapons.
* This handles special features of both weapons. Infantry units that only have
* a single weapon should use {@link Mounted}.
*/
public class InfantryWeaponMounted extends Mounted {

transient private InfantryWeapon otherWeapon;
private final String typeName;

transient private List<EquipmentMode> modes;

/**
* Creates a Mounted that deals with combined feature of a primary and a secondary weapon.
* Infantry units with a single type of weapon should use {@link Mounted}
*
* @param entity The infantry unit
* @param rangeWeapon The weapon used to calculate range.
* @param otherWeapon The other weapon
*/
public InfantryWeaponMounted(Entity entity, EquipmentType rangeWeapon, InfantryWeapon otherWeapon) {
super(entity, rangeWeapon);
this.typeName = otherWeapon.getInternalName();
this.otherWeapon = otherWeapon;
rebuildModeList();
}

public InfantryWeapon getOtherWeapon() {
if (otherWeapon == null) {
otherWeapon = (InfantryWeapon) EquipmentType.get(typeName);
}
return otherWeapon;
}

public void rebuildModeList() {
modes = new ArrayList<>();
for (Enumeration<EquipmentMode> e = getType().getModes(); e.hasMoreElements(); ) {
modes.add(e.nextElement());
}
for (Enumeration<EquipmentMode> e = getOtherWeapon().getModes(); e.hasMoreElements(); ) {
EquipmentMode mode = e.nextElement();
if (!modes.contains(mode)) {
modes.add(mode);
}
}
}

private List<EquipmentMode> getModes() {
if (modes == null) {
rebuildModeList();
}
return modes;
}

@Override
public int getModesCount() {
return getModes().size();
}

@Override
protected EquipmentMode getMode(int mode) {
return getModes().get(mode);
}

@Override
public boolean hasModes() {
return !getModes().isEmpty();
}

@Override
public boolean hasModeType(String mode) {
return modes.contains(EquipmentMode.getMode(mode));
}

@Override
public boolean canInstantSwitch(int newMode) {
if (getType().hasModes() && (getType().getMode(newMode) != null)) {
return super.canInstantSwitch(newMode);
} else {
String newModeName = getOtherWeapon().getMode(newMode).getName();
String curModeName = curMode().getName();
return getType().hasInstantModeSwitch()
&& !getOtherWeapon().isNextTurnModeSwitch(newModeName)
&& !getOtherWeapon().isNextTurnModeSwitch(curModeName);
}
}

@Override
public void adaptToGameOptions(GameOptions options) {
((Weapon) getOtherWeapon()).adaptToGameOptions(options);
super.adaptToGameOptions(options);
rebuildModeList();
}
}
Loading

0 comments on commit 5e796cb

Please sign in to comment.