Files
TeslaRel410/restoration/source410/BT/MECHSUB.NOTES.md
T
CydandClaude Fable 5 d4f0aef6e1 BT410 Phase 5.3.17: the destruction cascade -- zone deaths DESTROY things
Mech::DamageZone::TakeDamage is the full binary cascade (@0049c690): LOD
artifact router with same-attacker clustering (@0049c40c, hysteresis 0.33),
artifact parent level = mean of children, Energy-hit generator shorts
through the crit plug binding (novice-exempt), weighted CriticalHit
(@0049ccc4), zone-death allotment push (SendSubsystemDamage @0049c9a8) and
the segment-table destruction descent (RecurseSegmentTable @0049cad4 --
SIBS + DESCEND via the engine EntitySegment iterators).

Supporting bricks: Mech's OWN handler table with the TakeDamage override
(latches lastInflictingID @0x43c; gyro feed staged; cylinder table
deferred); friend class Mech__DamageZone (authentic); subsystem private
zones ARMED (structureReference + armorByFacing[5] normalized -- crits are
measurable); ApplyDamageAndMeasure; ForceCriticalFailure sets
DestroyedState so the weapon FSMs' GetSimulationState()==1 hard gate went
live; Generator::ForceShort + PoweredSubsystem::ForceShortRecovery.

Fight-verified (mutual BT_SPAWN_ENEMY + BT_FORCE_ZONE=8 concentrated fire):
arm zone to 1.0 -> sibling searchlight zone dies (Searchlight2/ThermalSight
crits destroyed, HUD degrading) -> DESCEND kills the gun zone -> PPC_2 +
ERMLaser_2 + Condenser6 DESTROYED and FALL SILENT (0 shots after death;
undamaged PPC_1 keeps firing); vital-zone hit = MECH KILL; PPC hit shorted
GeneratorD. This art has zero artifact zones (redirect=0 across all 20 --
router verified dormant-correct). Smoke + novice regressions clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-24 11:13:28 -05:00

11 KiB
Raw Blame History

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 <Class>(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).