# MECHSUB — reconstruction notes (phase 2 kickoff, 2026-07-19) **Status: base-class ANALYSIS done; the reconstruction must be done FRESH against the 4.10 structure, NOT backdated from BT411 (its base class + damage model diverge). Key findings below de-risk the actual code.** MechSubsystem is the base of the ~30-class subsystem roster (sensor, gyro, torso, hud, generator, myomers, weapons…) the Mech ctor's segment-table walk instantiates. The staged MECHSUB.HPP is interface-only (one `Mech *owner` member, no bodies). Reconstructing the base is phase-2 step 1. ## Dependencies (all survive in the 4.10 archive) - `Subsystem` (CODE/RP/MUNGA/SUBSYSTM.HPP) — the base. - `DamageZone` (CODE/RP/MUNGA/DAMAGE.HPP/.CPP) — the engine damage zone. - `Mech__DamageZone : public DamageZone` (CODE/BT/BT/MECHDMG.HPP) + the BT damage model (MechCriticalSubsystem, redirect tables, critical hits). - `AlarmIndicator` (just reconstructed, MUNGA/ALARM.HPP) — statusAlarm. ## Finding 1 — the base Subsystem DECLARES the zone but does NOT create it (RESOLVED) SUBSYSTM.HPP:181 declares `DamageZone *damageZone;` in the **base** Subsystem and its TakeDamage (SUBSYSTM.HPP:177-178) delegates `damageZone->TakeDamage(damage)`. The base ctors ARE reconstructed — **staged source410/MUNGA/SUBSYSTM.CPP** (built, in munga.lib; 38 Subsystem:: syms in the map). Both base ctors set `damageZone = NULL` (resource ctor line 63) — the base does **not** allocate the zone. So the DERIVED MechSubsystem creates the `Mech__DamageZone` (BT411's zone-creation is structurally right here) — but the ARMOUR it seeds must follow Finding 2 (per-type streamed), not BT411's per-facing hand-seed. ## Finding 2 — CRITICAL: the damage model diverged The 4.10 base DamageZone (DAMAGE.HPP/CPP) is a **per-damage-TYPE** model: - members: `defaultArmorPoints` (Scalar) + `damageScale[Damage::DamageTypeCount]` where DamageTypeCount = 5 = {Collision, Ballistic, Explosive, Laser, Energy}. - TakeDamage: `damageLevel += damage.damageAmount * damageScale[damage.damageType]` (DAMAGE.CPP:413); the zone STREAMS its own armour (`*stream >> defaultArmorPoints; *stream >> damageScale[ii]`, DAMAGE.CPP:335,340). BT411's reconstruction is a **per-FACING** model: `armorByFacing[5]` (front/back/ left/right/top) + `structureReference`, **manually seeded** by MechSubsystem's resource ctor with an armour→absorption-coefficient inversion. Those field names (`armour[5]`, `structureReference`) exist only in BT411's `ReconDamageZone` proxy — NOT in the surviving 4.10 DamageZone/Mech__DamageZone. The staged `MechSubsystem__SubsystemResource` (armorByFacing[5]+structureReference) was copied from BT411 and is likewise suspect for 4.10. **Consequence:** BT411's MechSubsystem ctor (heat.cpp/mechsub.cpp) CANNOT be backdated verbatim — it would install a damage model the 4.10 engine's DamageZone doesn't implement, corrupting every subsystem's damage behaviour. The 4.10 MechSubsystem must let the base Subsystem + DamageZone stream ctor handle armour (per-type), not hand-seed per-facing values. ## Corrected reconstruction approach (fully scoped — ready to write) 1. ~~Pin the base Subsystem ctor~~ **DONE (Finding 1)** — staged SUBSYSTM.CPP sets `damageZone = NULL`; MechSubsystem creates the `Mech__DamageZone`. 2. Reconstruct MechSubsystem: chain the base Subsystem, `new Mech__DamageZone(this, index)`, add the mech-specific members (statusAlarm = AlarmIndicator, vital flag, alarmModel, criticalReference, collisionCriticalHitWeight, printSimulationState, configureActivePress) and the TakeDamage override, using the 4.10 **per-type** damage model — let the zone stream its own armour (defaultArmorPoints + damageScale[5]); do NOT hand-seed BT411's per-facing armorByFacing[5]/structureReference. 3. Re-derive `MechSubsystem__SubsystemResource` against the 4.10 DamageZone stream format (the staged struct's per-facing fields are BT411-copied and suspect). 4. Then the roster (GAUSS/PPC/SENSOR source + .TCP partials + decomp). Analysis complete; step 2 is the next code to write. This worksheet is the deliverable that keeps the subsystem foundation on the 4.10 damage model rather than BT411's divergent one. ## Progress + roster hierarchy findings (2026-07-19) **DONE:** `MechSubsystem` base (MECHSUB.HPP/.CPP) and `HeatableSubsystem` (HEAT.HPP/.CPP) reconstructed + compile-verified. Verified vertical slice so far: `Subsystem → MechSubsystem → HeatableSubsystem`. **The subsystem hierarchy is DEEPER than the staged headers show.** From BT411: ``` MechSubsystem (done) └ HeatableSubsystem (done; ctor chains MechSubsystem + ResetToInitialState -> currentTemperature=300, heatLoad=0) └ HeatSink (MISSING from the staged tree -- not in source410 nor CODE; VDATA HeatSinkClassID=0xBC5. Must be added.) └ PoweredSubsystem (staged POWERSUB.HPP wrongly derives it from HeatableSubsystem -- real base is HeatSink; FIX the staged header. Also its ctor has embedded AlarmIndicators (electricalStateAlarm 5-level, modeAlarm 3-level) and a voltageSource SlotOf.) └ Sensor (surviving header CODE/BT/BT/SENSOR.HPP; leaf) └ Generator, ... (other leaves) ``` **PoweredSubsystem has inter-subsystem wiring** — its ctor resolves a voltage source (generator) by indexing the OWNER MECH's subsystem roster (`owner->GetSubsystem(res->voltageSourceIndex)`), not the skeleton. So powered subsystems must be created AFTER the generator in the segment-walk order, and the Mech must expose GetSubsystemCount()/GetSubsystem(). This ordering constraint is a segment-walk (phase-4 ctor) concern; note it there. ## Complete subsystem family tree (mapped 2026-07-19) + build status ``` MechSubsystem DONE (MECHSUB.HPP/.CPP) ├─ HeatableSubsystem DONE (HEAT) │ └─ HeatSink DONE (HEAT) │ ├─ PoweredSubsystem DONE (POWERSUB) │ │ ├─ Sensor DONE (SENSOR; interface survives in CODE) │ │ └─ Myomers todo (myomers.hpp) │ ├─ Generator DONE (POWERSUB; source, GNRATOR.TCP partial) │ └─ Condenser todo (heat.hpp) └─ HeatWatcher (: MechSubsystem) todo -- sibling branch to HeatableSubsystem └─ PowerWatcher (: HeatWatcher) todo ├─ Torso todo (torso.hpp) -- Mech sinkSourceSubsystem ├─ HUD todo (hud.hpp) -- Mech hudSubsystem └─ Gyroscope todo (gyro.hpp) -- Mech gyroSubsystem ``` Plus the WEAPON family (separate subtree): MechWeapon → {PPC, Gauss, ProjectileWeapon → Missile/…}. PPC/GAUSS source survives in CODE/BT/BT; AMMOBIN/EMITTER/MISSILE/ PROJTILE/PROJWEAP have .TCP partials; the rest decomp-only. **Pattern established** (6 classes verified): each = statics (ClassDerivations(parent, "Name") + DefaultData reusing Subsystem::MessageHandlers/ AttributeIndex/StateCount), ctor chaining the parent + member init from the resource, dtor, TestClass/TestInstance/ResetToInitialState real, per-frame Simulation method + CreateStreamedSubsystem staged with Fail. Embedded alarms = AlarmIndicator(N); filters = AverageOf; owner->GetInstance() != ReplicantInstance gates the master performance install. ## ROSTER CORE COMPLETE (2026-07-20) — 16 classes verified All committed + compile-verified: MechSubsystem, HeatableSubsystem, HeatSink, HeatWatcher, PowerWatcher, PoweredSubsystem, Generator, Condenser, Sensor, Myomers, Gyroscope, HUD, Torso, MechWeapon, Emitter (+ surviving PPC/GaussRifle), ProjectileWeapon, MissileLauncher, AmmoBin. Tree links clean. ## Phase-4 blueprint: the Mech-ctor segment-walk factory (mech.cpp:1146-1330) The Mech ctor allocates `subsystemArray[subsystemCount]` (+GetSubsystem/Count), then loops segments (from id 2) switching on `seg->classID` to `new (this, id, seg)`, caching the key ones. **The VDATA ClassID enum names are MISLABELED vs the real class** (BT411's mapping): | ClassID | enum name | REAL class | built? | |---|---|---|---| | 0xBBD | CockpitClassID | Condenser | ✅ | | 0xBBE | SensorClassID | HeatSink bank | ~ (HeatSink ✅; bank variant TODO) | | 0xBC0 | CondenserClassID | **Reservoir** | ❌ TODO | | 0xBC1 | GeneratorClassID | Generator | ✅ | | 0xBC2 | PoweredSubsystemClassID | PoweredSubsystem | ✅ | | 0xBC3 | MyomersClassID | Sensor | ✅ | | 0xBC4 | GyroClassID | Gyroscope | ✅ (cache gyroSubsystem) | | 0xBC5 | SinkSourceClassID | Torso | ✅ (cache sinkSourceSubsystem) | | 0xBC6 | ActuatorClassID | Myomers | ✅ | | 0xBC8 | WeaponEmitterClassID | Emitter | ✅ (weaponCount++) | | 0xBCB | JumpJetClassID | AmmoBin | ✅ | | 0xBCD | MechWeaponClassID | ProjectileWeapon | ✅ | | 0xBCE | MissileWeaponClassID | GaussRifle | ✅ | | 0xBD0 | BallisticWeaponClassID | MissileLauncher | ✅ | | 0xBD3 | SubsystemMessageManagerID | **SubsystemMessageManager** | ❌ TODO | | 0xBD4 | GaussWeaponClassID | Emitter (PPC) | ✅ | | 0xBD6 | MechTechClassID | HUD | ✅ (cache hudSubsystem) | | 0xBD8 | LegSubsystemClassID | **Searchlight** (: PowerWatcher) | ❌ TODO | | 0xBDC | HeatableClassID | **MechTech** | ❌ TODO | | 0xBDE | DisplayClassID | **ThermalSight** (: PowerWatcher) | ❌ TODO | Remaining subsystem classes for a full walk: Reservoir, HeatSink-bank variant, Searchlight, MechTech, ThermalSight, SubsystemMessageManager, Actuator (fallback). Then the Mech member layout (subsystemArray/subsystemCount + the capability chains controllable/heatable/weaponRoster/damageable + the alarms/reticle/state-indicators /SequenceControllers/NameFilter/CStrings embedded members) must be named in MECH.HPP for the ctor to set them. ## 5.3.17 — the subsystem side of the damage cascade - The subsystem's PRIVATE zone is now ARMED (binary @4abf28): owned by the SUBSYSTEM (`new DamageZone(this, 0)` — Joint.cpp:512 proves subsystems are valid zone owners), named, `defaultArmorPoints = structureReference`, `damageScale[5] = armorByFacing[]` normalized with the hull-zone rule (1/(raw×points); guard: structureReference<=0 leaves the zone inert). This makes crits MEASURABLE. - `ApplyDamageAndMeasure` (@4ac07c): TakeDamage + return the zone-level delta (the crit accountant charges it). - `ForceCriticalFailure` now also sets `SetSimulationState(DestroyedState)` — the subsystem SIMULATION state enum (Destroyed=1/Exploding=2, from the binary PrintState string pool) added to MECHSUB.HPP; both weapon FSMs' `GetSimulationState() == 1` hard gate is now live (destroyed weapons stop firing — fight-verified). - `IsVitalSubsystem()` accessor (vital crit death = mech kill in SendSubsystemDamage).