diff --git a/megamek/src/megamek/client/bot/princess/MultiTargetFireControl.java b/megamek/src/megamek/client/bot/princess/MultiTargetFireControl.java index 4b7e9736b58..7963399234a 100644 --- a/megamek/src/megamek/client/bot/princess/MultiTargetFireControl.java +++ b/megamek/src/megamek/client/bot/princess/MultiTargetFireControl.java @@ -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; @@ -259,6 +260,26 @@ FiringPlan calculatePerArcFiringPlan(Entity shooter, List shotLi // damage values by arc: arc #, arc damage Map 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()){ + + } + 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())); @@ -267,44 +288,51 @@ FiringPlan calculatePerArcFiringPlan(Entity shooter, List 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>> 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)); + // 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]; @@ -315,7 +343,7 @@ FiringPlan calculatePerArcFiringPlan(Entity shooter, List shotLi // make sure we don't accidentally update the cell we're examining List 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 @@ -336,7 +364,8 @@ FiringPlan calculatePerArcFiringPlan(Entity shooter, List 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)); } } diff --git a/megamek/src/megamek/client/ui/swing/unitDisplay/WeaponPanel.java b/megamek/src/megamek/client/ui/swing/unitDisplay/WeaponPanel.java index db9153b3e45..7d05c009f53 100644 --- a/megamek/src/megamek/client/ui/swing/unitDisplay/WeaponPanel.java +++ b/megamek/src/megamek/client/ui/swing/unitDisplay/WeaponPanel.java @@ -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(); @@ -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); diff --git a/megamek/src/megamek/common/Entity.java b/megamek/src/megamek/common/Entity.java index 9ec423ce322..70e0808c493 100644 --- a/megamek/src/megamek/common/Entity.java +++ b/megamek/src/megamek/common/Entity.java @@ -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; diff --git a/megamek/src/megamek/common/actions/WeaponAttackAction.java b/megamek/src/megamek/common/actions/WeaponAttackAction.java index 1876be5a9cc..42a7ee83390 100644 --- a/megamek/src/megamek/common/actions/WeaponAttackAction.java +++ b/megamek/src/megamek/common/actions/WeaponAttackAction.java @@ -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 @@ -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) { @@ -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 diff --git a/megamek/src/megamek/common/weapons/WeaponHandler.java b/megamek/src/megamek/common/weapons/WeaponHandler.java index dedba430824..bbad25266e0 100644 --- a/megamek/src/megamek/common/weapons/WeaponHandler.java +++ b/megamek/src/megamek/common/weapons/WeaponHandler.java @@ -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()) { @@ -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 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; + } } } } @@ -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; } /** @@ -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());