Skip to content

Commit

Permalink
Issue 4259: Fixing Dropship Heat-by-Arc, updated Princess and forced …
Browse files Browse the repository at this point in the history
…grounded dropships to use individual weapons with heat-by-arc
  • Loading branch information
psikomonkie committed Dec 15, 2024
1 parent ae8cbb8 commit f974aa8
Show file tree
Hide file tree
Showing 5 changed files with 117 additions and 43 deletions.
51 changes: 40 additions & 11 deletions megamek/src/megamek/client/bot/princess/MultiTargetFireControl.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@

import megamek.common.Entity;
import megamek.common.Game;
import megamek.common.Mounted;
import megamek.common.Targetable;
import megamek.common.equipment.AmmoMounted;
import megamek.common.equipment.WeaponMounted;
Expand Down Expand Up @@ -259,6 +260,26 @@ FiringPlan calculatePerArcFiringPlan(Entity shooter, List<WeaponFireInfo> shotLi
// damage values by arc: arc #, arc damage
Map<Integer, Double> arcDamage = new HashMap<>();

int heatCapacity = shooter.getHeatCapacity();
shooter.getWeapons();
shooter.getWeaponList();
logger.error(String.format("Weapons Length: %s", shooter.getWeaponList().size()));
for ( WeaponMounted weapon : shooter.getWeaponList()){

Check notice

Code scanning / CodeQL

Unread local variable Note

Variable 'WeaponMounted weapon' is never read.

}
for ( WeaponMounted weapon : shooter.getWeaponList().stream().filter(Mounted::isUsedThisRound).toList()){
logger.error(String.format("Weapon in list: %s", weapon.getName()));
int arc = weapon.isRearMounted() ? -shooter.getWeaponArc(weapon.getLocation()) : shooter.getWeaponArc(weapon.getLocation());
if (!arcShots.containsKey(arc)) {
heatCapacity -= shooter.getHeatInArc(weapon.getLocation(), weapon.isRearMounted());
arcShots.put(arc, new ArrayList<>());
arcHeat.put(arc, 0);
arcDamage.put(arc, 1.0);

logger.error(String.format("Arc: %s Heat: %s", arc, arcHeat.get(arc)));
}
}

// assemble the data we'll need to solve the backpack problem
for (WeaponFireInfo shot : shotList) {
int arc = shooter.getWeaponArc(shooter.getEquipmentNum(shot.getWeapon()));
Expand All @@ -267,44 +288,51 @@ FiringPlan calculatePerArcFiringPlan(Entity shooter, List<WeaponFireInfo> shotLi
arc = -arc;
}

logger.error(String.format("Shot: %s Rear: %s Arc: %s Raw arc: %s", shot.getWeapon().getName(), shot.getWeapon().isRearMounted(), arc,shooter.getWeaponArc(shooter.getEquipmentNum(shot.getWeapon())) ));

if (!arcShots.containsKey(arc)) {
arcShots.put(arc, new ArrayList<>());
arcHeat.put(arc,
shooter.getHeatInArc(shot.getWeapon().getLocation(), shot.getWeapon().isRearMounted()));
arcDamage.put(arc, 0.0);

logger.error(String.format("Arc: %s Heat: %s", arc, arcHeat.get(arc)));
}

arcShots.get(arc).add(shot);
arcDamage.put(arc, arcDamage.get(arc) + shot.getExpectedDamage());
logger.error(String.format("Arc: %s Damage: %s", arc, arcDamage.get(arc)));
}

// initialize the backpack
Map<Integer, Map<Integer, List<Integer>>> arcBackpack = new HashMap<>();
for (int x = 0; x < arcShots.keySet().size(); x++) {
for (int x = 0; x <= arcShots.keySet().size(); x++) {
arcBackpack.put(x, new HashMap<>());

for (int y = 0; y < shooter.getHeatCapacity(); y++) {
for (int y = 0; y <= heatCapacity; y++) {
arcBackpack.get(x).put(y, new ArrayList<>());
}
}

double[][] damageBackpack = new double[arcShots.keySet().size()][shooter.getHeatCapacity()];
double[][] damageBackpack = new double[arcShots.keySet().size() + 1][heatCapacity + 1];
Integer[] arcHeatKeyArray = new Integer[arcHeat.keySet().size()];
System.arraycopy(arcHeat.keySet().toArray(), 0, arcHeatKeyArray, 0, arcHeat.keySet().size());

logger.error(String.format("Heat Key Array Length: %s Attempted Output: %s", arcHeatKeyArray.length, arcHeatKeyArray));

Check notice

Code scanning / CodeQL

Implicit conversion from array to string Note

Implicit conversion from Array to String.

// now, we essentially solve the backpack problem, where the arcs are the items:
// arc expected damage is the "value", and arc heat is the "weight", while the
// backpack capacity is the unit's heat capacity.
// while we're at it, we assemble the list of arcs fired for each cell
for (int arcIndex = 0; arcIndex < arcHeatKeyArray.length; arcIndex++) {
for (int heatIndex = 0; heatIndex < shooter.getHeatCapacity(); heatIndex++) {
int previousArc = arcIndex > 0 ? arcHeatKeyArray[arcIndex - 1] : 0;
for (int arcIndex = 0; arcIndex <= arcHeatKeyArray.length; arcIndex++) {
for (int heatIndex = 0; heatIndex <= heatCapacity; heatIndex++) {
int currentArc = arcIndex > 0 ? arcHeatKeyArray[arcIndex - 1] : 0;

if (arcIndex == 0 || heatIndex == 0) {
damageBackpack[arcIndex][heatIndex] = 0;
} else if (arcHeat.get(previousArc) <= heatIndex) {
int previousHeatIndex = heatIndex - arcHeat.get(previousArc);
double currentArcDamage = arcDamage.get(previousArc)
} else if (arcHeat.get(currentArc) <= heatIndex) {
int previousHeatIndex = heatIndex - arcHeat.get(currentArc);
double currentArcDamage = arcDamage.get(currentArc)
+ damageBackpack[arcIndex - 1][previousHeatIndex];
double accumulatedPreviousArcDamage = damageBackpack[arcIndex - 1][heatIndex];

Expand All @@ -315,7 +343,7 @@ FiringPlan calculatePerArcFiringPlan(Entity shooter, List<WeaponFireInfo> shotLi
// make sure we don't accidentally update the cell we're examining
List<Integer> appendedArcList = new ArrayList<>(
arcBackpack.get(arcIndex - 1).get(previousHeatIndex));
appendedArcList.add(previousArc);
appendedArcList.add(currentArc);
arcBackpack.get(arcIndex).put(heatIndex, appendedArcList);
} else {
// we *can* add this arc to the list, but it won't take us past the damage
Expand All @@ -336,7 +364,8 @@ FiringPlan calculatePerArcFiringPlan(Entity shooter, List<WeaponFireInfo> shotLi
// solution
// unless there is no firing solution at all, in which case we skip this part
if (!arcBackpack.isEmpty()) {
for (int arc : arcBackpack.get(arcBackpack.size() - 1).get(shooter.getHeatCapacity() - 1)) {
for (int arc : arcBackpack.get(arcBackpack.size() - 1).get(heatCapacity - 1)) {
logger.error(String.format("final arc: %s", arc));
retVal.addAll(arcShots.get(arc));
}
}
Expand Down
19 changes: 11 additions & 8 deletions megamek/src/megamek/client/ui/swing/unitDisplay/WeaponPanel.java
Original file line number Diff line number Diff line change
Expand Up @@ -1195,12 +1195,10 @@ public void displayMek(Entity en) {
&& game.getPhase().isFiring()) {
hasFiredWeapons = true;
// add heat from weapons fire to heat tracker
if (entity.usesWeaponBays()) {
if (entity.isLargeCraft()) {
// if using bay heat option then don't add total arc
if (game.getOptions().booleanOption(OptionsConstants.ADVAERORULES_HEAT_BY_BAY)) {
for (WeaponMounted weapon : mounted.getBayWeapons()) {
currentHeatBuildup += weapon.getCurrentHeat();
}
currentHeatBuildup += mounted.getCurrentHeat();
} else {
// check whether arc has fired
int loc = mounted.getLocation();
Expand Down Expand Up @@ -1257,17 +1255,22 @@ public void displayMek(Entity en) {

// change what is visible based on type
if (entity.usesWeaponBays()) {
wArcHeatL.setVisible(true);
wArcHeatR.setVisible(true);
m_chBayWeapon.setVisible(true);
wBayWeapon.setVisible(true);
} else {
wArcHeatL.setVisible(false);
wArcHeatR.setVisible(false);
m_chBayWeapon.setVisible(false);
wBayWeapon.setVisible(false);
}

if (entity.isLargeCraft()) {
wArcHeatL.setVisible(true);
wArcHeatR.setVisible(true);
}
else {
wArcHeatL.setVisible(false);
wArcHeatR.setVisible(false);
}

wDamageTrooperL.setVisible(false);
wDamageTrooperR.setVisible(false);
wInfantryRange0L.setVisible(false);
Expand Down
1 change: 1 addition & 0 deletions megamek/src/megamek/common/Entity.java
Original file line number Diff line number Diff line change
Expand Up @@ -11636,6 +11636,7 @@ public int getHeatInArc(int location, boolean rearMount) {
if ((mounted.getLocation() == location)
&& (mounted.isRearMounted() == rearMount)) {
arcHeat += mounted.getCurrentHeat();
logger.error(String.format("Location: %s Rear Mount: %s Weapon: %s Heat: %s Arc Heat %s", location, rearMount, mounted.getName(), mounted.getCurrentHeat(), arcHeat));
}
}
return arcHeat;
Expand Down
50 changes: 31 additions & 19 deletions megamek/src/megamek/common/actions/WeaponAttackAction.java
Original file line number Diff line number Diff line change
Expand Up @@ -1486,28 +1486,30 @@ private static String toHitIsImpossible(Game game, Entity ae, int attackerId, Ta

// limit large craft to zero net heat and to heat by arc
final int heatCapacity = ae.getHeatCapacity();
if (ae.usesWeaponBays() && (weapon != null) && !weapon.getBayWeapons().isEmpty()) {
if (ae.isLargeCraft() && (weapon != null)) {
int totalHeat = 0;

// first check to see if there are any usable weapons
boolean usable = false;
for (WeaponMounted m : weapon.getBayWeapons()) {
WeaponType bayWType = m.getType();
boolean bayWUsesAmmo = (bayWType.getAmmoType() != AmmoType.T_NA);
if (m.canFire()) {
if (bayWUsesAmmo) {
if ((m.getLinked() != null) && (m.getLinked().getUsableShotsLeft() > 0)) {
// first check to see if there are any usable bay weapons
if (!weapon.getBayWeapons().isEmpty()) {
boolean usable = false;
for (WeaponMounted m : weapon.getBayWeapons()) {
WeaponType bayWType = m.getType();
boolean bayWUsesAmmo = (bayWType.getAmmoType() != AmmoType.T_NA);
if (m.canFire()) {
if (bayWUsesAmmo) {
if ((m.getLinked() != null) && (m.getLinked().getUsableShotsLeft() > 0)) {
usable = true;
break;
}
} else {
usable = true;
break;
}
} else {
usable = true;
break;
}
}
}
if (!usable) {
return Messages.getString("WeaponAttackAction.BayNotReady");
if (!usable) {
return Messages.getString("WeaponAttackAction.BayNotReady");
}
}

// create an array of booleans of locations
Expand All @@ -1534,8 +1536,13 @@ private static String toHitIsImpossible(Game game, Entity ae, int attackerId, Ta
int loc = prevWeapon.getLocation();
boolean rearMount = prevWeapon.isRearMounted();
if (game.getOptions().booleanOption(OptionsConstants.ADVAERORULES_HEAT_BY_BAY)) {
for (WeaponMounted bWeapon : prevWeapon.getBayWeapons()) {
totalHeat += bWeapon.getCurrentHeat();
if (!weapon.getBayWeapons().isEmpty()) {
for (WeaponMounted bWeapon : prevWeapon.getBayWeapons()) {
totalHeat += bWeapon.getCurrentHeat();
}
}
else {
totalHeat += prevWeapon.getCurrentHeat();
}
} else {
if (!rearMount) {
Expand All @@ -1560,8 +1567,13 @@ private static String toHitIsImpossible(Game game, Entity ae, int attackerId, Ta
int currentHeat = ae.getHeatInArc(loc, rearMount);
if (game.getOptions().booleanOption(OptionsConstants.ADVAERORULES_HEAT_BY_BAY)) {
currentHeat = 0;
for (WeaponMounted bWeapon : weapon.getBayWeapons()) {
currentHeat += bWeapon.getCurrentHeat();
if (!weapon.getBayWeapons().isEmpty()) {
for (WeaponMounted bWeapon : weapon.getBayWeapons()) {
currentHeat += bWeapon.getCurrentHeat();
}
}
else {
currentHeat += weapon.getCurrentHeat();
}
}
// check to see if this is currently the only arc being fired
Expand Down
39 changes: 34 additions & 5 deletions megamek/src/megamek/common/weapons/WeaponHandler.java
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ public int getSalvoBonus() {
* TeleMissile entity in the physical phase
*/
protected int getLargeCraftHeat(Entity e) {
int totalheat = 0;
int totalHeat = 0;
if (e.hasETypeFlag(Entity.ETYPE_DROPSHIP)
|| e.hasETypeFlag(Entity.ETYPE_JUMPSHIP)) {
if (e.usesWeaponBays()) {
Expand All @@ -163,7 +163,35 @@ protected int getLargeCraftHeat(Entity e) {
if (prevAttack.getEntityId() == e.getId()) {
WeaponMounted prevWeapon = (WeaponMounted) e.getEquipment(prevAttack.getWeaponId());
for (WeaponMounted bayW : prevWeapon.getBayWeapons()) {
totalheat += bayW.getCurrentHeat();
totalHeat += bayW.getCurrentHeat();
}
}
}
} else if (!game.getOptions().booleanOption(OptionsConstants.ADVAERORULES_HEAT_BY_BAY)) {
// create an array of booleans of locations
boolean[] usedFrontArc = new boolean[ae.locations()];
boolean[] usedRearArc = new boolean[ae.locations()];
for (int i = 0; i < ae.locations(); i++) {
usedFrontArc[i] = false;
usedRearArc[i] = false;
}
for (Enumeration<AttackHandler> i = game.getAttacks(); i.hasMoreElements();) {
AttackHandler ah = i.nextElement();
WeaponAttackAction prevAttack = ah.getWaa();
if (prevAttack.getEntityId() == e.getId()) {
WeaponMounted prevWeapon = (WeaponMounted) e.getEquipment(prevAttack.getWeaponId());
boolean rearMount = prevWeapon.isRearMounted();
int loc = prevWeapon.getLocation();
if (!rearMount) {
if (!usedFrontArc[loc]) {
totalHeat += ae.getHeatInArc(loc, rearMount);
usedFrontArc[loc] = true;
}
} else {
if (!usedRearArc[loc]) {
totalHeat += ae.getHeatInArc(loc, rearMount);
usedRearArc[loc] = true;
}
}
}
}
Expand All @@ -173,12 +201,12 @@ protected int getLargeCraftHeat(Entity e) {
WeaponAttackAction prevAttack = ah.getWaa();
if (prevAttack.getEntityId() == e.getId()) {
Mounted<?> prevWeapon = e.getEquipment(prevAttack.getWeaponId());
totalheat += prevWeapon.getCurrentHeat();
totalHeat += prevWeapon.getCurrentHeat();
}
}
}
}
return totalheat;
return totalHeat;
}

/**
Expand Down Expand Up @@ -1903,12 +1931,13 @@ protected void addHeat() {
return;
}
if (!(toHit.getValue() == TargetRoll.IMPOSSIBLE)) {
if (ae.usesWeaponBays() && !game.getOptions().booleanOption(OptionsConstants.ADVAERORULES_HEAT_BY_BAY)) {
if (ae.isLargeCraft() && !game.getOptions().booleanOption(OptionsConstants.ADVAERORULES_HEAT_BY_BAY)) {
int loc = weapon.getLocation();
boolean rearMount = weapon.isRearMounted();
if (!ae.hasArcFired(loc, rearMount)) {
ae.heatBuildup += ae.getHeatInArc(loc, rearMount);
ae.setArcFired(loc, rearMount);
logger.error(String.format("Arc Heat: %s Buildup: %s", ae.getHeatInArc(loc, rearMount), ae.heatBuildup));
}
} else {
ae.heatBuildup += (weapon.getCurrentHeat());
Expand Down

0 comments on commit f974aa8

Please sign in to comment.