Cross-pod beams: replicate emitter discharge via subsystem update records
User report: lasers only visible on the window firing them. The peer's replicant emitters never learned the master fired. THE AUTHENTIC PIPE (decomp-verified): - FUN_0041c350 (the "beam keepalive" ServiceDischarge/ContinueDischarge call) does TWO things: queue the LOCAL deferred beam-effect callback (@0x4bac0c) on the app+0x34 manager -- our per-weapon render walk already plays that role -- and set the subsystem DIRTY bit, which maps to the 2007 engine's updateModel / ForceUpdate(). - Replication rides SUBSYSTEM UPDATE RECORDS inside the mech's update message: the roster walk already hands the entity's stream to every subsystem's PerformAndWatch; Simulation::WriteSimulationUpdate serializes each requested updateModel bit; Entity::UpdateMessageHandler routes received records by subsystemID to the subsystem's ReadUpdateRecord. All engine machinery -- the missing pieces were the Emitter's serialize/apply pair + the triggers. CORRECTIONS to the dormant task-33-era transcriptions (never exercised -- nothing ever set updateModel -- so the latent misreads never surfaced): - The weapon-family VTABLE SLOT MAP was swapped: slot 6 = ReadUpdateRecord, 7 = WriteUpdateRecord, 9 = TakeDamage (evidence: Mech hierarchy symmetry + body semantics; @004ba568 resolves an EntityID at rec+0x30 through the entity index -- record semantics, not Damage). Renamed across MechWeapon / Emitter / ProjectileWeapon; the real Emitter::TakeDamage @004bafc8 is undecoded (inherits MechWeapon for now). - Emitter/MechWeapon Write: `*record = 0x38/0x18` is the record LENGTH, not recordID; rec+0x30 is the TARGET's EntityID (GetEntityID()), not a colour -- the old `CopyColor(targetEntity+0x184)` was also a databinding trap. - OVERRIDE-SIGNATURE TRAP: the decls used each class's own shadowing UpdateRecord typedef as the param type, silently NOT overriding the engine virtual (the base ran instead; nothing would ever have serialized). Base-typed params (Simulation__UpdateRecord*), casts inside. - Emitter::ReadUpdateRecord reconstructed (@004ba568): target EntityID resolve (drop unknown non-null targets), MechWeapon alarm apply chain, beam fields. - ServiceDischarge/ContinueDischarge: ForceUpdate() per keepalive tick + one final record at beam end (turns the peer's beam off). - Mech::DrawWeaponBeams extracted from the player-only drive block so the walk runs for REPLICANTS (+ per-mech gun-port cache -- the process-wide statics would have served the player's segment pointers as the replicant's muzzles). VERIFIED 2-node: A fires 57 volleys -> 225 emitter records -> B applies all 225 -> B draws 414 beams (PPC blue / laser red, from A's replicant's own gun ports). Solo un-regressed (150 beams, kill chain, no crash). Also preserved: the full Mech::WriteUpdateRecord @0x4a0c2c recovery (reference/decomp/mech_writeupdate_004a0c2c.disasm.txt) with all 9 record types decoded (pose/alarm/leg-state+heat with the body-channel write-through re-sync, knockdown, death, impact, movementMode) -- transcription deferred; it replicates remote knockdown/death/heat and was not needed for beams. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
a110041a70
commit
29035028fd
@@ -233,59 +233,73 @@ Logical
|
||||
//
|
||||
|
||||
//
|
||||
// @004b964c -- slot 6. Apply damage through the base, then set the weapon's
|
||||
// display alarm: a destroyed weapon (damage->destroyed flag, +0x14) clears the
|
||||
// alarm to 0, otherwise the alarm tracks the incoming damage level (+0x10).
|
||||
// @004b96d4 -- slot 9 (TASK #51 RENAME: this body was mislabeled slot-6
|
||||
// ReadUpdateRecord; @004b96d4 chains @004b0efc = PoweredSubsystem::TakeDamage,
|
||||
// which dispatches on *param==4 == Damage::damageType -- damage semantics).
|
||||
// The PoweredSubsystem::TakeDamage body (electrical-short handling) is not yet
|
||||
// reconstructed; chain to the engine Subsystem base, and keep the damage-zone
|
||||
// alarm tracking this port has shipped with (best-effort; the binary's alarm
|
||||
// display on weapon damage flows through the unreconstructed @004b0efc chain).
|
||||
//
|
||||
void
|
||||
MechWeapon::TakeDamage(Damage &damage)
|
||||
{
|
||||
Subsystem::TakeDamage(damage); // FUN_0041bd34
|
||||
Subsystem::TakeDamage(damage); // engine base (real chain: @004b0efc)
|
||||
|
||||
// A destroyed weapon clears its display alarm; otherwise the alarm tracks
|
||||
// the damage-zone state set by the base TakeDamage above. (The decomp read
|
||||
// the post-damage destroyed/level flags out of the damage record; here we
|
||||
// read them back off the resolved damage zone.)
|
||||
// Read the ENGINE Subsystem::damageZone (the real built zone), qualified -- the
|
||||
// nearer MechSubsystem::damageZone is a ReconDamageZone-shim SHADOW of it (exposed
|
||||
// once HeatableSubsystem re-based onto MechSubsystem). TODO: remove that shadow.
|
||||
// Port behavior (kept): a destroyed weapon clears its display alarm;
|
||||
// otherwise the alarm tracks the resolved damage-zone state.
|
||||
::DamageZone *dz = this->Subsystem::damageZone;
|
||||
if (dz != 0
|
||||
&& dz->GetGraphicState() == DamageZone::DestroyedGraphicState)
|
||||
{
|
||||
weaponAlarm.SetLevel(0); // FUN_0041bbd8(this+0xd4, 0)
|
||||
weaponAlarm.SetLevel(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
weaponAlarm.SetLevel(
|
||||
(dz != 0 && dz->damageLevel > 0.0f) ? 1 : 0
|
||||
); // FUN_0041bbd8(this+0xd4, level)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// @004b9690 -- slot 7. Append the weapon's replication fields to the update
|
||||
// record: record type 0x18, the alarm state (this+0x364), and a "ready/idle"
|
||||
// flag derived from this+0x360.
|
||||
// record. DISASM CORRECTIONS (task #51): `*message = 0x18` writes the record
|
||||
// LENGTH (record[0] = recordLength), not recordID; and message[5] derives from
|
||||
// this+0x360 == weaponAlarm+0x10 (LevelCountB), not from the level. BASE-TYPED
|
||||
// param: with the class's own UpdateRecord typedef this silently failed to
|
||||
// override the engine virtual, so the engine base ran and no weapon field
|
||||
// ever serialized.
|
||||
//
|
||||
void
|
||||
MechWeapon::WriteUpdateRecord(UpdateRecord *message, int update_model)
|
||||
MechWeapon::WriteUpdateRecord(Simulation__UpdateRecord *message, int update_model)
|
||||
{
|
||||
Subsystem::WriteUpdateRecord(message, update_model); // FUN_0041c500
|
||||
Subsystem::WriteUpdateRecord(message, update_model); // FUN_0041c500 (header + subsystemID)
|
||||
|
||||
message->recordID = 0x18; // *message = 0x18
|
||||
message->alarmState = weaponAlarm.GetLevel(); // message[4] = *(this+0x364)
|
||||
message->weaponIdle = (weaponAlarm.GetLevel() == 0) ? 1 : 0; // message[5]
|
||||
MechWeapon__UpdateRecord *rec = (MechWeapon__UpdateRecord *)message;
|
||||
rec->recordLength = sizeof(MechWeapon__UpdateRecord); // *message = 0x18
|
||||
rec->alarmState = weaponAlarm.GetLevel(); // message[4] = *(this+0x364)
|
||||
rec->weaponIdle = (weaponAlarm.LevelCountB() == 0) ? 1 : 0; // message[5] = (*(this+0x360)==0)
|
||||
}
|
||||
|
||||
//
|
||||
// @004b96d4 -- slot 9. MechWeapon adds nothing of its own; defers to the
|
||||
// PoweredSubsystem implementation (paired with WriteUpdateRecord above).
|
||||
// @004b964c -- slot 6 (TASK #51 RENAME: was mislabeled TakeDamage). Apply a
|
||||
// weapon update record on the REPLICANT: chain the Simulation base
|
||||
// (@0041bd34 -- lastUpdate + simulationState), then the weapon alarm --
|
||||
// reset-through-0 first when the record's weaponIdle flag is set, then the
|
||||
// replicated alarm level.
|
||||
//
|
||||
void
|
||||
MechWeapon::ReadUpdateRecord(UpdateRecord *message)
|
||||
MechWeapon::ReadUpdateRecord(Simulation__UpdateRecord *message)
|
||||
{
|
||||
PoweredSubsystem::ReadUpdateRecord(message); // FUN_004b0efc
|
||||
Simulation::ReadUpdateRecord(message); // FUN_0041bd34
|
||||
|
||||
MechWeapon__UpdateRecord *rec = (MechWeapon__UpdateRecord *)message;
|
||||
if (rec->weaponIdle != 0) // record+0x14
|
||||
{
|
||||
weaponAlarm.SetLevel(0);
|
||||
}
|
||||
weaponAlarm.SetLevel(rec->alarmState); // record+0x10
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
Reference in New Issue
Block a user