MP live-play wave: collision economy, missiles, radar transform, panel polarity, comm ticker
The interactive 2-node playtest wave -- every fix decomp-grounded and live-verified: COLLISION ECONOMY (the ram one-shot): StaticBounce mutates worldLinearVelocity per contact and ProcessCollisionList walks EVERY touched solid per frame; with 2007 terrain-as-solids the reflections compounded x4-x40 within one frame and a walking bump one-shot a pristine mech for 112,375 pts (62-pt authentic economy). Fix: frameEntryWorldVelocity restore per contact (damage always priced at the real approach speed -- all the binary's physics ever saw); Mech::Reset zeroes the mover motion (respawn = teleport); [collide-tx]/[mp-hdlr] telemetry. Gotcha #16 (engine-facility drift class). MISSILES: peer-visible salvos (the launcher record extension carries a salvo counter + aim point; ForceUpdate actually enqueues it -- the dirty flag alone never serialized), the authentic arc (authored MuzzleVelocity vector + the Seeker's 200m/0.1/300 loft + gain-4 steering, decoded from @004beae4/@004bef78), world-impact bursts (rounds detonate on cave geometry instead of phasing through), contact-only damage (flight-cap expiry = fizzle, no more teleport damage), live re-lead, and ballistic (unguided) shells for autocannons. projweap's stale BTPushProjectile extern (the /FORCE signature trap, gotcha #6 corollary) crashed the Avatar's first AFC100 shot -- fixed + sweep rule recorded. RADAR: two transcription bugs made the scope permanently empty -- FUN_0040b244 is the affine INVERSE (not a copy) and FUN_0040adec writes ONLY the 3x3 rotation (never the translation); worldToView now Invert(view) built rotation-first. CulturalIcons sorted out of the moving grid (the phantom red pips were map props), visible-radius culls on all three draw passes, live pip verified at |delta| x ppm px. Gotcha #17 (verify the FUN_ body, not its call shape). WEAPON PANELS (the frozen-dial hunt): the binary's *(subsystem+0x40) means FAILED -- the recon's 'operating' name was backwards, inverting the destroyed-X lamps, the panel look, the children enable and the ready-lamp gate (which had NEVER executed). Polarity chain corrected end-to-end (failedState, fed by real damage saturation). Root cause of the freezes: MFD page-mode gating -- the dev composite shows ALL pages at once, so off-page dials legitimately stopped; under BT_DEV_GAUGES the 15 page-plane bits stay active (the exclusive secondary trio untouched). The SEH gauge guard now names its kills; repaint-heal resets the incremental arc after panel repaints; [panel]/[arc] probes added. COMM/SCORE: MessageBoard LIVE (the engine already shipped the whole Player__StatusMessage queue; wired the binary's one producer -- the kill branch, victim's name, 6s -- plus the consumer bridge and a lazy source bind); MP DEATHS counted via the observed-death tally (each node scores every pilot from locally observed events, the same model as the KILLS credit) and the -2/-1 engine seed clamped for display. DEV UX: node-tagged window titles (-net port), gauge panel reworked (1320x480, true 4:3 MFD cells, the portrait secondary UNROTATED upright, linear filtering, BT_GAUGE_SCALE), fixed close spawns via BT_SPAWN_XZ, Boreas flies an Avatar (first second-chassis live outing). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
dd27238ceb
commit
bb795e2805
@@ -669,23 +669,24 @@ void
|
||||
}
|
||||
|
||||
//
|
||||
// Pop a "destroyed by <killer>" status message onto the pilot HUD.
|
||||
// Gated on a real attacker player (+ the status pool, a NULL stub in
|
||||
// bring-up), so the ownerless-dummy solo path skips it (no player to name).
|
||||
// Pop a "destroyed" status message naming the victim onto the comm
|
||||
// ticker (MessageBoard feed, LIVE 2026-07-12). ALLOCATION NOTE: the
|
||||
// binary drew from the MemoryBlock pool @00512f6c and spliced the BT
|
||||
// vtable; our engine's StatusMessageUpdate reclaims each expired
|
||||
// message with Unregister_Object + plain delete (PLAYER.cpp:864), so
|
||||
// the matching allocation here is the engine ctor + Register_Object
|
||||
// -- same record {playerInvolved, messageType 0, 6.0s}, same queue.
|
||||
// Gated on a real named player (the ownerless dummy has none).
|
||||
//
|
||||
if (sender_owner && StatusMessagePool)
|
||||
if (sender_owner)
|
||||
{
|
||||
StatusMessage *status = (StatusMessage *)StatusMessagePool->New(); // FUN_00402f74(0x512f6c)
|
||||
if (status)
|
||||
{
|
||||
new (status) Player__StatusMessage(
|
||||
sender_owner, // *(sender+0x190)
|
||||
0,
|
||||
STATUS_DISPLAY_TIME // 0x40c00000 == 6.0f
|
||||
);
|
||||
*(int *)((char *)status + 0x18) = 0; // status[6] = 0
|
||||
AddStatusMessage(status); // FUN_0042e580
|
||||
}
|
||||
Player__StatusMessage *status = new Player__StatusMessage(
|
||||
sender_owner, // *(sender+0x190) -- the victim's player
|
||||
0, // message strip 0 (the kill cell)
|
||||
STATUS_DISPLAY_TIME // 0x40c00000 == 6.0f
|
||||
);
|
||||
Register_Object(status);
|
||||
AddStatusMessage(status); // FUN_0042e580
|
||||
}
|
||||
|
||||
//
|
||||
@@ -1359,7 +1360,41 @@ int BTPilotKills(void *pilot)
|
||||
DEBUG_STREAM << "[score] gauge read: pilot=" << pilot << " kills=" << k << "\n" << std::flush;
|
||||
return k;
|
||||
}
|
||||
int BTPilotDeaths(void *pilot) { return pilot ? ((BTPlayer *)pilot)->GetDeaths() : 0; }
|
||||
// DEATHS display (MP DEATHS fix): the engine seeds Player::deathCount at -2
|
||||
// (PLAYER.cpp:759) and only the LOCAL vehicle-acquire path zeroes it -- a REMOTE
|
||||
// player's copy on this node keeps the raw pre-acquire value (-2/-1) forever,
|
||||
// which the scoreboard showed verbatim ("DEATHS -1", user-reported). Those
|
||||
// negatives are the engine's internal spawn-cycle convention, not real deaths;
|
||||
// clamp them to the observable truth: no deaths yet = 0.
|
||||
int BTPilotDeaths(void *pilot)
|
||||
{
|
||||
if (pilot == 0)
|
||||
return 0;
|
||||
int deaths = ((BTPlayer *)pilot)->GetDeaths();
|
||||
return (deaths < 0) ? 0 : deaths;
|
||||
}
|
||||
|
||||
// OBSERVED-DEATH tally (MP DEATHS fix): each node maintains every player's
|
||||
// score from LOCALLY OBSERVED events -- exactly how the KILLS credit already
|
||||
// works (the victim's ScoreMessage handler ++s the SHOOTER's local Player
|
||||
// copy). This is the symmetric half: when a REPLICANT mech dies here (the
|
||||
// once-per-death transition runs on every node), tally the death onto its
|
||||
// owning player's LOCAL copy. The owner's own node counts through its real
|
||||
// VehicleDead(-1) path, so the caller gates this to replicant mechs -- each
|
||||
// node then counts every pilot self-consistently. Normalize the -2/-1
|
||||
// pre-acquire seed first so death #1 reads 1.
|
||||
void BTPlayerCountObservedDeath(void *player)
|
||||
{
|
||||
if (player == 0)
|
||||
return;
|
||||
BTPlayer *p = (BTPlayer *)player;
|
||||
if (p->deathCount < 0)
|
||||
p->deathCount = 0;
|
||||
++p->deathCount;
|
||||
if (getenv("BT_SCORE_LOG"))
|
||||
DEBUG_STREAM << "[score] observed death -> pilot " << player
|
||||
<< " deaths=" << p->deathCount << "\n" << std::flush;
|
||||
}
|
||||
|
||||
// Is `pilot` the owner of `local_player`'s current target? (the SELECT-TARGET row
|
||||
// highlight). Computed here via accessors -- the PilotList's raw-offset version
|
||||
@@ -1388,11 +1423,37 @@ int BTPilotIsSelected(void *pilot, void *local_player)
|
||||
// compiled BTPlayer/Player__StatusMessage members (decode AddStatusMessage @0042e580).
|
||||
// A real (stub) definition is REQUIRED or the /FORCE link AVs MessageBoard::Execute's call.
|
||||
class BitMap;
|
||||
// MessageBoard feed (LIVE 2026-07-12): the board is the LOCAL cockpit's comm
|
||||
// ticker -- read the mission player's engine status queue. statusMessagePointer
|
||||
// is maintained per frame by Player::StatusMessageUpdate [T0 PLAYER.cpp:520/822]:
|
||||
// it holds the newest queued Player__StatusMessage until its 6s displayTime runs
|
||||
// out. The name bitmap resolves through the SAME small-raster chain the radar
|
||||
// labels use (Mission::GetSmallNameBitmap keyed by the involved player's egg
|
||||
// bitmapindex).
|
||||
Logical BTResolveMessageBoard(Entity * /*tracked_mech*/, int *messageId, BitMap **nameBitmap)
|
||||
{
|
||||
*messageId = -1;
|
||||
*nameBitmap = 0;
|
||||
return False;
|
||||
BTPlayer *local_player = (application != 0)
|
||||
? (BTPlayer *)application->GetMissionPlayer() : 0;
|
||||
if (local_player == 0)
|
||||
{
|
||||
return False;
|
||||
}
|
||||
Player::StatusMessage *m = local_player->statusMessagePointer;
|
||||
if (m == 0)
|
||||
{
|
||||
return False;
|
||||
}
|
||||
*messageId = m->messageType;
|
||||
Player *involved = m->playerInvolved;
|
||||
if (involved != 0)
|
||||
{
|
||||
Mission *mission = application->GetCurrentMission();
|
||||
if (mission != 0)
|
||||
*nameBitmap = mission->GetSmallNameBitmap(involved->playerBitmapIndex);
|
||||
}
|
||||
return True;
|
||||
}
|
||||
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Reference in New Issue
Block a user