SubsystemMessageManager reconstructed from the raw decomp with the wire format pinned by the engine's own parser (TakeDamageStreamMessageHandler: count, then type/amount/subsystemID per record -- the chain KEY carries the damage amount, which is why AddDamageMessage keys its plugs by it). AddDamageMessage @0049b6d8: first hit of the frame wins the common record. ConsolidateAndSendDamage @0049b784: one DynamicMessage(0x13, 0x34) at the hit entity, the firing weapon's explosion resource bundled per record (binary reads roster[id]+0x3e4 blind; [T1] guarded here), DeletePlugs teardown, commons reset. CreateWeaponExplosions @0049baa0 under its AUTHENTIC surviving-header signature (the donor's 'SendQueuedExplosions' rename was drift): per queued resource an Explosion::MakeMessage -- MakeMessageID 3, ExplosionClassID 0x31 (our VDATA enum ordinal matches the binary exactly), flags 0x100, identity-rotation Origin at the impact point -- posted to the Registry at CreationEventPriority, staggered 0.1s apart. The 1995 archaeology: the binary computes a terrain-hit flag from a Derivation at 0x4e6f54 (unresolved BSS class) and passes it to a parameter THE SHIPPED BODY NEVER READS; terrainHitExplosionID is resourced by the CSS and consumed nowhere. A declared, streamed, abandoned feature -- shipped anyway. We pass False [T3]; behavior is byte-identical. Ctor corrections vs our stub: weaponExplosions is UNIQUE-keyed (binary chain ctor flag 1), the Performance registers, resource fields copy from the stream. MechWeapon grows a public ExplosionResourceIDOf() accessor. The 0x160-byte engine helper at +0xE0 stays staged [T3]; the CSS stays in the authoring-family batch. No feeders call AddDamageMessage yet -- the consolidator idles empty per frame, soak-verified live (no faults, wheel turning). Stubs: 13 across 10 files. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3.8 KiB
MESSMGR.CPP — SubsystemMessageManager reconstruction notes
The per-mech damage/explosion hub: subsystems queue damage during a frame
(AddDamageMessage), the Performance (ConsolidateAndSendDamage) folds the
queue into ONE TakeDamageStreamMessage for the hit entity and flushes the
bundled weapon explosions (CreateWeaponExplosions). The surviving
MESSMGR.HPP (CODE/BT/BT) is authentic and untouched.
Reconstructed 2026-08-04 (5.3.115), decomp-verified (part_012.c)
- ctor @0049bca4:
weaponExplosions(NULL, True)— the binary constructs the explosion chain UNIQUE-keyed (0049bfde(.., 0, 1)); the damage chain allows duplicate keys. InstallsConsolidateAndSendDamageas the Performance (member-pointer triple @0050b6fc); copies the two streamed resource fields (res+0x30/+0x34); zeros the common record. [T3] The binary also allocates a 0x160-byte engine-internal helper at this+0xE0 (FUN_0041de1c) — not declared in the surviving header, untouched by the reconstructed paths, STAGED until identified. - AddDamageMessage @0049b6d8: first hit of the frame wins each common
field (entity / impact point via epsilon-against-origin / zone); every
message queues a
DamageInformationplug KEYED BY DAMAGE AMOUNT (the chain key is read back withGetValue()at consolidation). - ConsolidateAndSendDamage @0049b784: wire format pinned by the ENGINE'S
OWN PARSER (
Entity::TakeDamageStreamMessageHandler, RP ENTITY.CPP:855): aReceiver::DynamicMessage(TakeDamageStreamMessageID == 0x13, sizeof == 0x34)with header {impactPoint@+0x1c, damageZoneIndex@+0x28, shootingEntity@+0x2c}, then<< count, then<< type << amount << subsystemIDper record. Explosion bundling reads the FIRING weapon's resource (binary: blindroster[subsystemID]+0x3e4); [T1] guarded here (range + IsDerivedFrom(MechWeapon)) — the binary is unguarded and relies on well-formed feeders. Dispatches the stream directly at the hit entity, flushes explosions, purges withDeletePlugs()(binary FUN_00417858(,1) — the explicit teardown), resets the commons. - CreateWeaponExplosions @0049baa0: the authentic HEADER signature
(terrain_hit, entity_hit, explode_position) — the BT411 donor's
"SendQueuedExplosions" rename was port drift. Per queued resource:
Explosion::MakeMessage(MakeMessageID(3), sizeof(0x5c), RegisteredClass::ExplosionClassID(0x31 — our VDATA enum ordinal matches the binary byte-for-byte), no owner, resourceID, instanceFlags 0x100, Origin{impact position, identity rotation}, entity_hit, owner mech), posted atCreationEventPriority(3)toapplication->GetRegistry()with deliveryNow() + 0.1s × n(impacts in one frame stagger). Then purge.
The inert terrain-hit flag (recorded, not tuned)
The binary computes terrain_hit = entityHit->IsDerivedFrom(<Derivation @ 0x4e6f54>) before calling CreateWeaponExplosions. The class at 0x4e6f54 is
UNRESOLVED (BSS object; no static-ctor reference survives in the decomp
parts). It does not matter: @0049baa0 NEVER READS its first argument, and
terrainHitExplosionID is never consumed anywhere in the shipped cluster —
a declared, resourced, abandoned 1995 feature. We pass False [T3];
shipped behavior is preserved exactly. If the constant is ever resolved
(candidates: a terrain/cultural class), only the dead flag changes.
Status / staging
CreateStreamedSubsystem@0049be10 remains a Fail stub — the authoring- side CSS family (8 fns) is batched separately; missions load prebuilt streams from BTL4.RES and never call it.- No feeders yet: nothing in source410 calls
AddDamageMessage(the weapon damage paths still dispatch TakeDamage directly), so the consolidator runs empty every frame — verified live (5-period soak, wheel turning, no faults). When the weapon-fire damage path is rebased onto the manager, this TU is ready for it.