diff --git a/restoration/source410/BT/EMITTER.CPP b/restoration/source410/BT/EMITTER.CPP index 70484296..a22b6d73 100644 --- a/restoration/source410/BT/EMITTER.CPP +++ b/restoration/source410/BT/EMITTER.CPP @@ -190,6 +190,29 @@ void // PoweredSubsystem::PoweredSubsystemSimulation(time_slice); + // + // Hard failure (@4baab9): weapon DESTROYED or its own sink at FailureHeat + // -> drop the beam state and the charge, and hold there. Unlike the + // ballistic roach-motel this recovers by itself: once conduction cools the + // sink below the failure threshold the gate stops firing and the weapon + // resumes from Loading. (The owning-mech-disabled half joins with the + // damage wave.) + // + if (GetSimulationState() == 1 || GetHeatState() == FailureHeat) + { + if (getenv("BT_MECH_LOG") && GetWeaponState() != LoadingState) + { + DEBUG_STREAM << "[fire] '" << GetName() + << "' THERMAL SHUTDOWN (T=" << CurrentTemperatureOf() + << ")" << endl << flush; + } + ResetFiringState(); + chargeLevel = 0.0f; + ComputeOutputVoltage(); + Check_Fpu(); + return; + } + { static int forceFire = -1; if (forceFire < 0) diff --git a/restoration/source410/BT/HEAT.NOTES.md b/restoration/source410/BT/HEAT.NOTES.md index 1afcb523..e05f93b3 100644 --- a/restoration/source410/BT/HEAT.NOTES.md +++ b/restoration/source410/BT/HEAT.NOTES.md @@ -81,9 +81,30 @@ threshold: the authentic fire-discipline game. SRM salvos spike +641K and cross degradation after three. Condensers absorb and pass to the central bank. Neutral run: sensor Ready/100%, mech holds, zero Fail. +## Heat consequences (Phase 5.3.11, 2026-07-22) + +The weapon-side consequences are wired and VERIFIED under sustained fire: + +- **Ballistic gate 1** (@4bbd36): weapon destroyed-state or its own sink at + FailureHeat pins full recoil + the unavailable alarm (the NoAmmo roach-motel + re-asserts). (Mech-disabled half joins the damage wave.) +- **CheckForJam** (@4bbfcc): `p = 0.41·T/failT` clamped [minJamChance, 1.0], + rolled per granted shot against the MUNGA uniform `Random`. Interim gate: + heat-degraded only (stands in for the deferred `LiveFireEnabled` novice + switch AND the trivially-true `heatLoad>0` early-out — the exact spurious- + cold-jam trap the BT411 port documented). Verified: SRM6s jam at + T≈1200-1320 (24%+ rolls past the 1000° threshold), stay jammed. +- **Emitter hard gate** (@4baab9): FailureHeat drops the beam state + charge + each frame; SELF-RECOVERING — conduction cools the sink below 2000° and the + weapon resumes from Loading. Verified: the PPC settles into an emergent + thermal duty cycle (fire at ~1930° → spike 2562° → shutdown → cool → refire); + the lasers oscillate around ~2200°. + ## Still deferred -UpdateCoolant / BalanceCoolant (coolant depletion + venting), the HeatModelOff -experience gate (novice mode), the electrical charge model (TrackSeekVoltage / -voltage sag / I²R), gate 1 + the heat-scaled jam roll in the weapon FSMs, the -central sink's forward-linked drain, Condenser MoveValve handling. +UpdateCoolant / BalanceCoolant (coolant depletion + venting), the +HeatModelOff / LiveFireEnabled experience gates (novice mode), the electrical +charge model (TrackSeekVoltage / voltage sag / I²R), the mech-disabled halves +of the weapon gates (damage wave), the central sink's forward-linked drain, +Condenser MoveValve handling, jam recovery via ResetToInitialState (mission +reset path). diff --git a/restoration/source410/BT/PROJWEAP.CPP b/restoration/source410/BT/PROJWEAP.CPP index 0bde9cca..31736b21 100644 --- a/restoration/source410/BT/PROJWEAP.CPP +++ b/restoration/source410/BT/PROJWEAP.CPP @@ -23,6 +23,10 @@ # include #endif +#if !defined(RANDOM_HPP) +# include +#endif + Derivation ProjectileWeapon::ClassDerivations( MechWeapon::ClassDerivations, @@ -159,6 +163,48 @@ void } } +// +//############################################################################# +// CheckForJam -- the heat-scaled jam roll (binary @4bbfcc): +// p = 0.41 * currentTemperature / failureTemperature, +// clamped to [minJamChance, 1.0], rolled against a uniform random. +// A jammed launcher clears only on ResetToInitialState (mission reset). +// +// TWO interim gates stand in for deferred systems (the BT411 port hit exactly +// this): the authentic leading gate is LiveFireEnabled() -- the player +// experience switch (novice mode = no jams), which needs the player-link +// accessor wave -- and the binary's own `heatLoad <= 0` early-out is trivially +// true once the heat model runs. Until LiveFireEnabled lands, gate the roll +// on the sink actually being heat-degraded, so cold weapons fire reliably and +// jams appear exactly when the heat economy says the weapon is cooking. +//############################################################################# +// +Logical + ProjectileWeapon::CheckForJam() +{ + Check(this); + + if (GetHeatState() < DegradationHeat) // interim LiveFireEnabled stand-in + { + return False; + } + + Scalar p = (0.41f * CurrentTemperatureOf()) / failureTemperature; + if (minJamChance <= p) + { + if (p > 1.0f) + { + p = 1.0f; + } + } + else + { + p = minJamChance; + } + + return (p > (Scalar)Random) ? True : False; +} + // //############################################################################# // ProjectileWeaponSimulation The ballistic per-frame fire state machine @@ -209,6 +255,24 @@ void // Logical trigger = CheckFireEdge(); + // + // Gate 1 (@4bbd36): weapon DESTROYED or its own sink at FailureHeat -> pin + // full recoil + the unavailable alarm. No return -- the frame continues + // into the state machine (whose NoAmmo case re-asserts). (The owning-mech- + // disabled half joins with the damage wave.) + // + if (GetSimulationState() == 1 || GetHeatState() == FailureHeat) + { + if (getenv("BT_MECH_LOG") && GetWeaponState() != NoAmmoState) + { + DEBUG_STREAM << "[fire] '" << GetName() + << "' -> UNAVAILABLE (gate1: heatState=" << GetHeatState() + << " T=" << CurrentTemperatureOf() << ")" << endl << flush; + } + recoil = rechargeRate; + weaponAlarm.SetLevel(NoAmmoState); + } + // // Gate 2: the linked AmmoBin. Empty (or missing) -> pin unavailable EVERY // frame while dry. @@ -244,6 +308,16 @@ void { weaponAlarm.SetLevel(NoAmmoState); } + else if (CheckForJam()) + { + weaponAlarm.SetLevel(JammedState); + if (getenv("BT_MECH_LOG")) + { + DEBUG_STREAM << "[fire] '" << GetName() + << "' JAMMED (T=" << CurrentTemperatureOf() + << ")" << endl << flush; + } + } else { weaponAlarm.SetLevel(LoadingState); diff --git a/restoration/source410/BT/PROJWEAP.HPP b/restoration/source410/BT/PROJWEAP.HPP index 7b005eb5..f4f495e0 100644 --- a/restoration/source410/BT/PROJWEAP.HPP +++ b/restoration/source410/BT/PROJWEAP.HPP @@ -77,6 +77,8 @@ void ProjectileWeaponSimulation(Scalar time_slice); + Logical + CheckForJam(); public: typedef ProjectileWeapon__SubsystemResource SubsystemResource;