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>
65 lines
3.8 KiB
Markdown
65 lines
3.8 KiB
Markdown
# 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. Installs `ConsolidateAndSendDamage` as 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 `DamageInformation` plug KEYED BY DAMAGE AMOUNT (the
|
||
chain key is read back with `GetValue()` at consolidation).
|
||
- **ConsolidateAndSendDamage @0049b784**: wire format pinned by the ENGINE'S
|
||
OWN PARSER (`Entity::TakeDamageStreamMessageHandler`, RP ENTITY.CPP:855):
|
||
a `Receiver::DynamicMessage(TakeDamageStreamMessageID == 0x13, sizeof ==
|
||
0x34)` with header {impactPoint@+0x1c, damageZoneIndex@+0x28,
|
||
shootingEntity@+0x2c}, then `<< count`, then `<< type << amount <<
|
||
subsystemID` per record. Explosion bundling reads the FIRING weapon's
|
||
resource (binary: blind `roster[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 with `DeletePlugs()` (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 at `CreationEventPriority(3)` to `application->GetRegistry()` with
|
||
delivery `Now() + 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.
|