#45/#134 authentic score/death report tail -- replaces the scoring stand-ins
Reconstructs the dark-gap tail of Mech::TakeDamageMessageHandler
(@0x4a02f4-0x4a0890, raw disasm): the three id-0x16 score reports (kill to
the shooter's player / type-0 wire-fidelity / received to the victim's
player) and the BT 0x38-byte VehicleDeadMessage extension {killed-by player,
kill zone} dispatched from the death tail. Retires BTPostDamageScore /
BTPostKillScore and the per-hit inflicted credit (never existed in 1995:
@0x4c0200 is bound in no handler-table entry -- byte-scan receipt in
decomp-reference). Suicides now dispatch and the handler negates the award
(the #134 panic penalty). Collision divert falls through to the death tail
per @0x4a0375 (wall deaths respawn + blast; no score). ScoreMessage fields
renamed to decoded truth (vitalHit/zoneIndex/subsysID) + wire asserts;
console VTVDamaged points_transfered corrected (Round(award), not Now()).
Benches: scorekill.sh cross-node kill (kills=1 award=4.88, killedBy=2:1
zone=3, single death cycle), scoreself.sh suicide (type=2 award=-39.00
kills=0), deathblast2.sh re-verified (72 bursts at ~9u).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
06a8edbff2
commit
91bd28669e
+70
-41
@@ -1063,24 +1063,40 @@ void
|
||||
(float)(message->damageData.impactPoint.z - localOrigin.linearPosition.z));
|
||||
}
|
||||
|
||||
//
|
||||
// Death-edge latch, MOVED 2026-08-05 to the authentic position (binary
|
||||
// @0x4a0303: captured at handler ENTRY, before the collision divert). It
|
||||
// arms the whole death tail -- kill report, VehicleDead, death blast.
|
||||
// Capturing it after the divert (where #89 first placed it) meant a
|
||||
// COLLISION death could never arm the tail.
|
||||
//
|
||||
const int deathBlastArmed = !IsMechDestroyed(); // [ebp-0x10], inverted
|
||||
//
|
||||
// The zone the reports + VehicleDead carry: msg+0x24 as of loop entry.
|
||||
// The binary never rewrites msg+0x24 after the initial cylinder resolve;
|
||||
// our loop-exit matchlog write (damageZone = LAST burst's zone) must not
|
||||
// leak into them.
|
||||
//
|
||||
int reportZone = message->damageZone;
|
||||
DamageLookupTable *table = (DamageLookupTable *)damageLookupTable; // named member (Wword absorbs!)
|
||||
|
||||
//
|
||||
// @0x4a0368 -- the COLLISION DIVERT (#83, closing the #82 chain). Damage
|
||||
// type 0 == the mover's collision damage (what the crash response policy
|
||||
// forwards). It NEVER reaches the zone/armor loop below: it is priced +
|
||||
// distributed as INTERNAL RATTLE by DistributeCollisionDamage (@0049ffcc)
|
||||
// and the handler is done. This was the missing half of the crash
|
||||
// self-damage reconstruction -- without the divert, the raw kinetic-energy
|
||||
// amounts (a 60-ton mech prices ~1000+ per wall tap) fell through into the
|
||||
// WEAPON loop and a single bump killed the mech (run-14 field report).
|
||||
// distributed as INTERNAL RATTLE by DistributeCollisionDamage (@0049ffcc).
|
||||
// CORRECTED 2026-08-05: the binary does NOT return here -- it JUMPS TO THE
|
||||
// DEATH TAIL (raw disasm @0x4a0375: jmp 0x4a07b5), so a mech that dies of
|
||||
// the rattle still posts VehicleDead (the respawn trigger) and still
|
||||
// blasts. The early `return` this block shipped with was a latent
|
||||
// "wall-death strands the pilot" hazard. No score reports on this path --
|
||||
// the jump bypasses them; a collision death credits no one. Authentic.
|
||||
//
|
||||
if (message->damageData.damageType == 0)
|
||||
{
|
||||
DistributeCollisionDamage(&message->damageData);
|
||||
Check_Fpu();
|
||||
return;
|
||||
goto death_tail;
|
||||
}
|
||||
|
||||
DamageLookupTable *table = (DamageLookupTable *)damageLookupTable; // named member (Wword absorbs!)
|
||||
// #92: when this gate fails the zone keeps whatever the message carried --
|
||||
// which for an unaimed weapon hit is 0, i.e. EVERY shot lands on zone 0.
|
||||
if (getenv("BT_DMGTABLE_LOG"))
|
||||
@@ -1105,6 +1121,7 @@ void
|
||||
<< message->damageData.impactPoint.z << ")\n" << std::flush;
|
||||
}
|
||||
}
|
||||
reportZone = message->damageZone; // post-resolve (@0x4a0396 write)
|
||||
//
|
||||
// #80 -- the faithful application loop (binary @0x4a0423-0x4a04d8), which
|
||||
// REPLACES the engine-base single application. Three things the base
|
||||
@@ -1126,11 +1143,6 @@ void
|
||||
// nothing (matches ENTITY.cpp:878; the binary would deref -1 -- it can't
|
||||
// happen there because every mech ships a lookup table).
|
||||
//
|
||||
// #89 DEATH-BLAST edge arm: the binary's tail (@0x4a07b8) gates on the
|
||||
// victim ENTERING dead/eject during THIS message (entry test edx = the
|
||||
// pre-application disabled state). Capture the pre-state here.
|
||||
const int deathBlastArmed = !IsMechDestroyed();
|
||||
|
||||
if (damageZones != 0 && message->damageZone >= 0
|
||||
&& message->damageZone < damageZoneCount)
|
||||
{
|
||||
@@ -1181,14 +1193,20 @@ void
|
||||
}
|
||||
message->damageZone = zoneIndex; // matchlog sees the LAST zone
|
||||
|
||||
// Binary tail (@0x4a04da-0x4a07b2, decoded + deferred): builds id-0x16
|
||||
// damage/kill REPORT messages -- {tally, zone, zoneDestroyed flag,
|
||||
// inflicting subsystem, victim name} -- to the shooter's player (with a
|
||||
// kill-flagged variant when this damage NEWLY disabled the mech) and to
|
||||
// the victim's player. That is the authentic stats plumbing (#45); the
|
||||
// port's matchlog + BTPostDamageScore cover the bookkeeping today [T3].
|
||||
(void)zoneDestroyed;
|
||||
(void)damageTally;
|
||||
//
|
||||
// THE REPORT TAIL (@0x4a04da-0x4a07b2), reconstructed 2026-08-05 [T1]:
|
||||
// the kill / inflicted / received score reports to the shooter's and
|
||||
// victim's players. Sender lives in btplayer.cpp (complete-BTPlayer
|
||||
// TU, per the databinding rule); it receives the APPLIED tally, the
|
||||
// vital-wreck flag, and the loop-ENTRY zone. This retires
|
||||
// BTPostDamageScore/BTPostKillScore -- see the btplayer.cpp tombstone.
|
||||
//
|
||||
{
|
||||
extern void BTMechPostCombatReports(void *, void *, float, int, int, int);
|
||||
BTMechPostCombatReports((void *)this, (void *)message,
|
||||
(float)damageTally, zoneDestroyed, reportZone,
|
||||
deathBlastArmed && IsMechDestroyed());
|
||||
}
|
||||
}
|
||||
|
||||
// MP MATCH FORENSICS (matchlog.hpp): the victim-side authoritative damage
|
||||
@@ -1218,30 +1236,41 @@ void
|
||||
(float)message->damageData.impactPoint.z);
|
||||
}
|
||||
|
||||
// #89 DEATH BLAST (raw disasm @0x4a07b8-0x4a0bda -- the un-exported tail
|
||||
// of THIS handler; found by call-scanning Explosion::SplashDamage
|
||||
// @0042fad0, which has exactly TWO callers: Missile::Perform and here).
|
||||
// The binary, when the victim ENTERS dead(9)/eject(10) during the
|
||||
// applications above:
|
||||
// 1. self-dispatches SetBurningState (id 0x17, Damage payload, zone -1)
|
||||
// -- the burning wreck. DEFERRED here: our 0x17 handler is not yet
|
||||
// reconstructed; the port's wreck-fire visuals come from the death
|
||||
// effect layer meanwhile.
|
||||
// 2. spawns the death Explosion (Explosion::Make model 0x31 at the mech
|
||||
// origin) -- the port's kill path already fires the authored death
|
||||
// list visuals; not double-spawned.
|
||||
// 3. calls SplashDamage on it, gated on the owning player's
|
||||
// advancedDamageOn (+0x264) AND NOT suppressConsole (+0x258 -- set
|
||||
// by the eject bookkeeping: an EJECTED pilot's mech never blasts):
|
||||
// THE DEATH TAIL (@0x4a07b5-0x4a0bda, raw disasm -- the un-exported tail
|
||||
// of THIS handler). Gate [T1]: mech was ALIVE at entry ([ebp-0x10]) and
|
||||
// is dead(9)/eject(10) now. Sequence:
|
||||
// 1. Player::VehicleDeadMessage (id 0x17; the BT 0x38-byte extension
|
||||
// carrying killed-by player + kill zone) -> the mech's OWN player:
|
||||
// the respawn-cycle trigger. CORRECTED 2026-08-05: the #89 banner
|
||||
// previously read this build as "SetBurningState (mech id 0x17)" --
|
||||
// it is a PLAYER-table id: the binary's BTPlayer handler table (file
|
||||
// 0x112dxx) binds 0x17 -> @0x4c05c4 VehicleDeadMessageHandler; the
|
||||
// mech-table 0x17 (@0x49f674) is unrelated. Dispatched from HERE --
|
||||
// this is the decoded sender the mech4 transition site's
|
||||
// [T3 sender-undecoded] flag was waiting for.
|
||||
// 2. the death Explosion (id 3 MakeMessage, 0x5C bytes, model 0x31 at
|
||||
// the mech origin, @0x4a08bd) -- the port fires the authored per-mech
|
||||
// death list from the death transition instead (#42 history): same
|
||||
// once-per-death edge, not double-spawned.
|
||||
// 3. SplashDamage (#89), gated advancedDamageOn (+0x264) AND NOT
|
||||
// suppressConsole (+0x258 -- ejected pilots' mechs never blast):
|
||||
// Damage{type=2 Explosive, amount=deathSplashDamage@0x520,
|
||||
// impact=mech origin,
|
||||
// burstCount=round(0.001 * moverMass@0x20c * 15.0)}
|
||||
// radius=deathSplashRadius@0x524, excluded=the dying mech.
|
||||
// Splash falloff (bursts / dist^1.25) applies inside per victim.
|
||||
// 4. ForceUpdate(1). (A score/console post also rides this tail --
|
||||
// the deferred id-0x16 family, tracked with #134.)
|
||||
// radius=deathSplashRadius@0x524, excluded=the dying mech;
|
||||
// falloff bursts/dist^1.25 per victim inside the shared core.
|
||||
// 4. ForceUpdate(1).
|
||||
// The gate predicate here is IsMechDestroyed() (graphicAlarm >= 9, the
|
||||
// structural flag @0x49fb54) -- the binary tests movementMode 9|10, but
|
||||
// the death transition sets mode 9 synchronously with the structural flag
|
||||
// on every path through here, so the edges coincide; this exact predicate
|
||||
// is the one the #89 blast benches verified both ways.
|
||||
death_tail:
|
||||
if (deathBlastArmed && IsMechDestroyed())
|
||||
{
|
||||
extern void BTMechPostVehicleDead(void *, void *, int);
|
||||
BTMechPostVehicleDead((void *)this, (void *)message, reportZone);
|
||||
|
||||
extern void BTApplyDeathSplash(void *mech_v);
|
||||
BTApplyDeathSplash((void *)this);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user