diff --git a/megamek/src/megamek/common/Aero.java b/megamek/src/megamek/common/Aero.java index 2a07d334dee..d75745fa1b7 100644 --- a/megamek/src/megamek/common/Aero.java +++ b/megamek/src/megamek/common/Aero.java @@ -1566,6 +1566,16 @@ public int getHeatCapacity(boolean includeRadicalHeatSink) { return capacity; } + @Override + public String formatHeat() { + int capacity = (getHeatSinks() * (getHeatType() + 1)); + if (hasWorkingMisc(MiscType.F_RADICAL_HEATSINK)) { + return "%d [%d]".formatted(capacity, capacity + getHeatSinks()); + } else { + return Integer.toString(capacity); + } + } + // If the aero is in the water, it is dead so no worries @Override public int getHeatCapacityWithWater() { @@ -1804,7 +1814,7 @@ public int getCriticalEffect(int roll, int target) { } /** - + */ @Override public void setOmni(boolean omni) { diff --git a/megamek/src/megamek/common/Entity.java b/megamek/src/megamek/common/Entity.java index e8e54d8c4af..5b82f97c97c 100644 --- a/megamek/src/megamek/common/Entity.java +++ b/megamek/src/megamek/common/Entity.java @@ -4826,6 +4826,17 @@ public int getHeatCapacity() { public abstract int getHeatCapacity(boolean radicalHeatSink); + /** + * Pretty-prints the heat capacity of a unit, including optional heat sinking systems. + * Typically, this is equivalent to {@link #getHeatCapacity()}, + * but in the presence of Radical Heat Sinks, Coolant Pods, or the RISC Emergency Coolant System, + * produces strings like "24 [36]" or "12 [+MoS]". + * @return The formatted heat capacity + */ + public String formatHeat() { + return Integer.toString(getHeatCapacity(true)); + } + /** * Returns the amount of heat that the entity can sink each turn, factoring * in whether the entity is standing in water. diff --git a/megamek/src/megamek/common/Mek.java b/megamek/src/megamek/common/Mek.java index c1f2beacccc..69093c05aec 100644 --- a/megamek/src/megamek/common/Mek.java +++ b/megamek/src/megamek/common/Mek.java @@ -34,6 +34,7 @@ import megamek.common.cost.MekCostCalculator; import megamek.common.enums.AimingMode; import megamek.common.enums.MPBoosters; +import megamek.common.equipment.AmmoMounted; import megamek.common.equipment.MiscMounted; import megamek.common.loaders.MtfFile; import megamek.common.options.IBasicOption; @@ -1637,6 +1638,36 @@ && hasWorkingMisc(MiscType.F_RADICAL_HEATSINK)) { return Math.max(capacity, 0); } + @Override + public String formatHeat() { + StringBuilder sb = new StringBuilder(); + int capacity = getHeatCapacity(true, false); + sb.append(capacity); + + // Radical Heatsinks + if (hasWorkingMisc(MiscType.F_RADICAL_HEATSINK)) { + capacity += getActiveSinks(); + sb.append(" [").append(capacity).append("]"); + } + + // Coolant Pod + for (AmmoMounted m : getAmmo()) { + if (m.getType().ammoType == AmmoType.T_COOLANT_POD) { + capacity += getActiveSinks(); + sb.append(" [").append(capacity).append("]"); + break; + } + } + + for (MiscMounted m : getMisc()) { + if (m.getType().hasFlag(MiscType.F_EMERGENCY_COOLANT_SYSTEM)) { + sb.append(" [+MoS]"); + } + } + + return sb.toString(); + } + /** * Returns the about of heat that the entity can sink each turn, factoring * for water. diff --git a/megamek/src/megamek/common/MekView.java b/megamek/src/megamek/common/MekView.java index 0937660df71..db6026dfaab 100644 --- a/megamek/src/megamek/common/MekView.java +++ b/megamek/src/megamek/common/MekView.java @@ -50,7 +50,7 @@ * The information is encoded in a series of classes that implement a common * {@link ViewElement} * interface, which can format the element either in html or in plain text. - * + * * @author Ryan McConnell * @since January 20, 2003 */ @@ -452,7 +452,7 @@ public MekView(final Entity entity, final boolean showDetail, final boolean useA } if (a.getHeatCapacity() > a.getHeatSinks()) { hsString.append(" [") - .append(a.getHeatCapacity()).append("]"); + .append(a.formatHeat()).append("]"); } if (a.getHeatSinkHits() > 0) { hsString.append(warningStart()).append(" (").append(a.getHeatSinkHits()) @@ -469,7 +469,7 @@ public MekView(final Entity entity, final boolean showDetail, final boolean useA StringBuilder hsString = new StringBuilder(); hsString.append(aMek.heatSinks()); if (aMek.getHeatCapacity() > aMek.heatSinks()) { - hsString.append(" [").append(aMek.getHeatCapacity()).append("]"); + hsString.append(" [").append(aMek.formatHeat()).append("]"); } if (aMek.damagedHeatSinks() > 0) { hsString.append(" ").append(warningStart()).append("(") @@ -640,7 +640,7 @@ private String getReadout(List section) { /** * The head section includes the title (unit name), tech level and availability, * tonnage, bv, and cost. - * + * * @return The data from the head section. */ public String getMekReadoutHead() { @@ -651,7 +651,7 @@ public String getMekReadoutHead() { * The basic section includes general details such as movement, system equipment * (cockpit, gyro, etc.) * and armor. - * + * * @return The data from the basic section */ public String getMekReadoutBasic() { @@ -660,7 +660,7 @@ public String getMekReadoutBasic() { /** * The invalid section includes reasons why the unit is invalid - * + * * @return The data from the invalid section */ public String getMekReadoutInvalid() { @@ -670,7 +670,7 @@ public String getMekReadoutInvalid() { /** * The loadout includes weapons, ammo, and other equipment broken down by * location. - * + * * @return The data from the loadout section. */ public String getMekReadoutLoadout() { @@ -681,7 +681,7 @@ public String getMekReadoutLoadout() { * The fluff section includes fluff details like unit history and deployment * patterns * as well as quirks. - * + * * @return The data from the fluff section. */ public String getMekReadoutFluff() { @@ -1683,7 +1683,7 @@ public String toDiscord() { * Marks warning text; in html the text is displayed in red. In plain text it is * preceded and followed * by an asterisk. - * + * * @return A String that is used to mark the beginning of a warning. */ private String warningStart() { @@ -1701,7 +1701,7 @@ private String warningStart() { /** * Returns the end element of the warning text. - * + * * @return A String that is used to mark the end of a warning. */ private String warningEnd() { @@ -1721,7 +1721,7 @@ private String warningEnd() { * Marks the beginning of a section of italicized text if using html output. For * plain text * returns an empty String. - * + * * @return The starting element for italicized text. */ private String italicsStart() { @@ -1739,7 +1739,7 @@ private String italicsStart() { /** * Marks the end of a section of italicized text. - * + * * @return The ending element for italicized text. */ private String italicsEnd() { diff --git a/megamek/testresources/megamek/common/units/Sagittaire SGT-14D.mtf b/megamek/testresources/megamek/common/units/Sagittaire SGT-14D.mtf new file mode 100644 index 00000000000..672d46057b6 --- /dev/null +++ b/megamek/testresources/megamek/common/units/Sagittaire SGT-14D.mtf @@ -0,0 +1,174 @@ +generator:MegaMek Suite 0.50.1-SNAPSHOT on 2024-09-23 +chassis:Sagittaire +model:SGT-14D +mul id:6835 + +Config:Biped +techbase:Inner Sphere +era:3134 +source:Record Sheets: 3145 New Tech New Upgrades +rules level:3 +role:Juggernaut + +quirk:difficult_maintain +quirk:weak_head_1 +quirk:imp_target_short + +mass:95 +engine:285 Light Engine(IS) +structure:IS Endo-Composite +myomer:Standard +cockpit:Small Cockpit +gyro:Standard Gyro + +heat sinks:14 IS Double +walk mp:3 +jump mp:3 + +armor:Standard(Inner Sphere) +LA armor:31 +RA armor:31 +LT armor:30 +RT armor:30 +CT armor:44 +HD armor:9 +LL armor:39 +RL armor:39 +RTL armor:10 +RTR armor:10 +RTC armor:15 + +Weapons:7 +Large Re-engineered Laser, Left Arm +Medium VSP Laser, Left Arm +Medium VSP Laser, Left Arm +Large Re-engineered Laser, Right Arm +Medium VSP Laser, Right Arm +Medium X-Pulse Laser, Right Arm +ER Large Laser, Left Torso + +Left Arm: +Shoulder +Upper Arm Actuator +Large Re-engineered Laser +Large Re-engineered Laser +Large Re-engineered Laser +Large Re-engineered Laser +Large Re-engineered Laser +ISMediumVSPLaser +ISMediumVSPLaser +ISMediumVSPLaser +ISMediumVSPLaser +-Empty- + +Right Arm: +Shoulder +Upper Arm Actuator +Large Re-engineered Laser +Large Re-engineered Laser +Large Re-engineered Laser +Large Re-engineered Laser +Large Re-engineered Laser +ISMediumVSPLaser +ISMediumVSPLaser +ISMediumXPulseLaser +-Empty- +-Empty- + +Left Torso: +Fusion Engine +Fusion Engine +ISDoubleHeatSink +ISDoubleHeatSink +ISDoubleHeatSink +ISERLargeLaser +ISERLargeLaser +IS Endo-Composite +IS Endo-Composite +IS Endo-Composite +-Empty- +-Empty- + +Right Torso: +Fusion Engine +Fusion Engine +ISDoubleHeatSink +ISDoubleHeatSink +ISDoubleHeatSink +ISDoubleHeatSink +ISDoubleHeatSink +ISDoubleHeatSink +Radical Heat Sink System +Radical Heat Sink System +Radical Heat Sink System +IS Endo-Composite + +Center Torso: +Fusion Engine +Fusion Engine +Fusion Engine +Gyro +Gyro +Gyro +Gyro +Fusion Engine +Fusion Engine +Fusion Engine +Jump Jet +IS Endo-Composite + +Head: +Life Support +Sensors +Cockpit +Sensors +ISC3BoostedSystemSlaveUnit +ISC3BoostedSystemSlaveUnit +-Empty- +-Empty- +-Empty- +-Empty- +-Empty- +-Empty- + +Left Leg: +Hip +Upper Leg Actuator +Lower Leg Actuator +Foot Actuator +Jump Jet +IS Endo-Composite +-Empty- +-Empty- +-Empty- +-Empty- +-Empty- +-Empty- + +Right Leg: +Hip +Upper Leg Actuator +Lower Leg Actuator +Foot Actuator +Jump Jet +IS Endo-Composite +-Empty- +-Empty- +-Empty- +-Empty- +-Empty- +-Empty- + +overview:The Sagittaire is built to fight in the confines of an urban battlefield. +capabilities:The laser-heavy Sagittaire is powered by a Pitban extralight engine and has a top speed of 54 kph, which is comparable to 'Mechs of similar weight. The HildCo lifters fitted in the legs and midline, however, make the Sagittaire one of the few assault 'Mechs with jumping capacity, offering it a great deal of tactical versatility. That, of course, does not account for the psychological impact of witnessing a 95-ton BattleMech fly through the air on inexperienced troops. As if that weren't enough, the Sagittaire's pulse-technology lasers combined with the targeting computer enable a MechWarrior to retain a high rate of movement while still outperforming most other BattleMechs. With eighteen tons of armor, the need for such a strategy is questionable. The design is truly deadly when the Sagittaire's feet are firmly planted on the earth. +deployment:This variant uses an Endo-Composite structure and Small Cockpit. This frees enough weight to replace the weapons with two Large Re-Engineered Lasers, an ER Large Laser, three Medium Variable Speed Pulse Lasers, and a Medium X-Pulse Laser. A C3 Boosted Slave is mounted in the head. Fourteen double heat sinks are boosted by a Radical Heat Sink System. This Sagittaire can also cover 90 meters in a single jump. +history:When it was originally deployed from Robinson BattleWorks in 3063, the machine saw considerable battle against House Kurita as Duke James Sandoval dedicated heavy resources to taking and retaining a half-dozen Combine worlds. The Sagittaire was crucial in the attack on Proserpina, where one of these robots slaughtered a company of Ninth Benjamin Regulars samurai in a series of ill-fated one-on-one duels. Later, following House Kurita's successful counter-assault, the Dragon made a deliberate effort to acquire or salvage multiple Sagittaire-designed 'Mechs for its own usage. Duke Sandoval later permitted additional of these vehicles to be sold into her loyalist army as Katherine Steiner-Davion tightened her grip on the Draconis March. This was short-lived, since Tancred Sandoval removed his father as March Lord not long after. He then directed the entire Sagittaire line to reinforce his own soldiers as well as the regiments supporting Victor Steiner-Davion. As an illustration of the Sagittaire's broad appeal, an allied company and a loyalist company were stationed on New Avalon, each with a lance of Sagittaires. During the battle for the continental capital of Flensburg, only three allied robots escaped under their own power. They were all Sagittarius. They were responsible for eight "kills" out of twelve enemy 'Mechs. +manufacturer:Robinson Standard BattleWorks +primaryfactory:Robinson +systemmanufacturer:CHASSIS:Skuel Heavy TRQ EC +systemmanufacturer:ENGINE:Pitban 285 Light +systemmanufacturer:ARMOR:Starshield Special Heavy +systemmanufacturer:JUMPJET:HildCo LFT 9-X +systemmanufacturer:COMMUNICATIONS:Sony MSF-31 +systemmanufacturer:TARGETING:Federated Stalker with Targeting Module + diff --git a/megamek/unittests/megamek/common/EntityTest.java b/megamek/unittests/megamek/common/EntityTest.java index 76401cd6b40..d1f3d6bd7e5 100644 --- a/megamek/unittests/megamek/common/EntityTest.java +++ b/megamek/unittests/megamek/common/EntityTest.java @@ -111,6 +111,25 @@ void testCalculateWeight() { } } + @Test + void testFormatHeat() { + File f; + MekFileParser mfp; + Entity e; + String expectedHeat, computedHeat; + + try { + f = new File("testresources/megamek/common/units/Sagittaire SGT-14D.mtf"); + mfp = new MekFileParser(f); + e = mfp.getEntity(); + expectedHeat = "28 [42]"; + computedHeat = e.formatHeat(); + assertEquals(expectedHeat, computedHeat); + } catch (Exception ex) { + fail(ex.getMessage()); + } + } + /** * Verify that if a unit's name appears in the list of canon unit names, it is * canon