gauge wave P3: wire the combat scoring feed (KILLS tracks combat)
The Comm roster + score gauges read 0 for 4 independent reasons (none was handler logic -- the BTPlayer ScoreMessage/VehicleDead/ScoreInflicted handlers were already reconstructed). Mapped by the scoring-feed-decode workflow; fixed all 4: - NO PRODUCER: combat only dispatched Entity::TakeDamageMessage, never a scoring message. Added producers (btplayer.cpp bridges, called from mech4.cpp): per-hit ScoreInflicted at the beam (:2171) + projectile (:831) damage dispatch -> SCORE; KillScore at the TARGET-DESTROYED edge (:2198) -> KILLS. senderMechID = the VICTIM (so the local player is credited via the !=our-mech branch, not the suicide-negate branch); dispatched to application->GetMissionPlayer(). - CROSS-FAMILY MECH OFFSETS: MECH_OWNING_PLAYER/TONNAGE/DAMAGE_BIAS read raw binary offsets (garbage in our 0x638 Mech). MECH_OWNING_PLAYER -> Entity::GetPlayerLink() (NULL for the ownerless dummy); tonnage/bias stubbed 1.0/0.0 (bring-up). - NULL scenarioRole: the BTMission role registry has no WinTesla analog, so every award path NULL-deref'd. CalcInflictedScore returns the neutral (damage+bias) when scenarioRole==0; DamageReceived guarded. - NULL-owner dummy: the KillScore sender-owner increment + StatusMessage now guard GetPlayerLink() (the dummy has none) so a solo kill credits only the local player. - DATABINDING: the PilotList read KILLS/DEATHS at raw offsets (pilot+0x27c/+0x200) that don't match our compiled layout -> silent 0. Repointed to the compiled members via bridges (BTPilotKills/BTPilotDeaths -> BTPlayer::GetKillCount/GetDeaths). ⚠ ROOT-CAUSE of the empty scoreboard: the PilotList SELECT-TARGET highlight did a raw-offset deref (*(local+0x284) objectiveMech, then *(tgt+0x190)) that AV'd on our layout -- caught silently by the SEH GuardedExecute, aborting Execute BEFORE the KILLS/DEATHS draw. Replaced with BTPilotIsSelected (accessors + GetPlayerLink). - SCORE persistence: PlayerSimulation's 10s flush zeroed currentScore (the binary's console delta); now only flushes/zeros when a console host exists (solo keeps the running score for the gauge + CalcRanking). Verified DBASE+dev gauges (BT_SCORE_LOG): SCORE climbs +5.83/hit -> 164; on the kill KILLS 0->1, and the Comm roster RENDERS KILLS=1 (screenshot). No crash. Follow-ups (noted): DEATHS=1 is a pre-existing spawn/respawn-handshake artifact (not from the producers -- the dummy's GetPlayerLink is NULL so no VehicleDead is posted; confirmed killCount=1 not 2); RANK=-1 is CalcRanking's non-scoring mark (Plasma serial surface, invisible on dev); MP VehicleDead/tonnage/role registry remain per the plan. Diagnostics kept: BT_SCORE_LOG. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
f998f2a023
commit
34aaa7dda4
@@ -187,6 +187,11 @@ Logical
|
||||
// the bridge (mechmppr.cpp, a complete-Mech TU) resolves it -- this TU only sees
|
||||
// the pilot as an opaque pointer read at BTPlayer raw offsets.
|
||||
extern void *BTResolveRosterPilot(int slot);
|
||||
// gauge scoring wave: read a roster pilot's scoreboard via its compiled BTPlayer
|
||||
// members (btplayer.cpp), not raw offsets (which don't match our layout).
|
||||
extern int BTPilotKills(void *pilot);
|
||||
extern int BTPilotDeaths(void *pilot);
|
||||
extern int BTPilotIsSelected(void *pilot, void *local_player); // SELECT-TARGET highlight (safe)
|
||||
|
||||
// App+0xC8 player-name-bitmap cache is not wired in the port (same deferral
|
||||
// PlayerStatus uses for its nameImage) -> NULL routes DrawMechIcon to the binary's
|
||||
@@ -344,6 +349,11 @@ void
|
||||
PilotList::Execute()
|
||||
{
|
||||
void *local = BTResolveRosterPilot(0); // slot-0 (local) pilot gates the body
|
||||
{
|
||||
static int s_pl = 0;
|
||||
if (getenv("BT_SCORE_LOG") && (s_pl++ % 120) == 0)
|
||||
DEBUG_STREAM << "[score] PilotList::Execute local=" << local << "\n" << std::flush;
|
||||
}
|
||||
if (local == NULL)
|
||||
return;
|
||||
|
||||
@@ -375,8 +385,11 @@ void
|
||||
}
|
||||
else
|
||||
{
|
||||
void *tgt = *(void **)((char *)local + 0x284); // local pilot's target (objectiveMech)
|
||||
int selected = (tgt != NULL) && (pilot == *(void **)((char *)tgt + 0x190)); // target's owningPlayer
|
||||
// SELECT-TARGET highlight -- via the safe bridge (accessors), NOT raw offsets:
|
||||
// the old `*(local+0x284)` objectiveMech + deref `*(tgt+0x190)` read garbage in
|
||||
// our compiled layout and AV'd inside the SEH-guarded Execute, silently aborting
|
||||
// it before the KILLS/DEATHS draw below (= the empty scoreboard).
|
||||
int selected = BTPilotIsSelected(pilot, local);
|
||||
if ((int)(size_t)pilot != e.resolvedMech || e.selected != selected)
|
||||
{
|
||||
localView.MoveToAbsolute(e.x, e.y); // vtbl+0x24
|
||||
@@ -385,13 +398,16 @@ void
|
||||
// (byte-faithful: the binary does NOT latch e.resolvedMech here, so the
|
||||
// icon redraws each occupied frame.)
|
||||
}
|
||||
// KILLS = killCount@0x27c (real; ScoreMessageHandler kill branch increments it).
|
||||
e.nameDisplay->Draw(&localView, (Scalar)(*(int *)((char *)pilot + 0x27c)));
|
||||
// DEATHS: the binary reads pad_0x280 (NO writer anywhere -> perpetual 0 = a
|
||||
// shipped dead field). FIX (correctness deviation, per "if it doesn't work,
|
||||
// fix it"): read the real deaths counter Player::deathCount@0x200
|
||||
// (VehicleDeadMessageHandler increments it), so DEATHS is meaningful in MP.
|
||||
e.mechDisplay->Draw(&localView, (Scalar)(*(int *)((char *)pilot + 0x200)));
|
||||
// KILLS / DEATHS -- gauge scoring wave (Step 1, the databinding fix): read the
|
||||
// pilot's COMPILED named members via the BTPlayer bridge, NOT raw binary offsets.
|
||||
// The earlier pilot+0x27c / +0x200 reads assumed the 1995 layout; our compiled
|
||||
// BTPlayer/Player layout differs (engine base != binary), so those offsets read a
|
||||
// stale/garbage slot -> a silent 0 even after the handlers increment the members.
|
||||
// The bridge reads the SAME members the ScoreMessageHandler/VehicleDead handlers
|
||||
// write (killCount / Player::deathCount). (DEATHS uses the real deathCount, not
|
||||
// the binary's dead pad_0x280.)
|
||||
e.nameDisplay->Draw(&localView, (Scalar)BTPilotKills(pilot)); // KILLS (killCount)
|
||||
e.mechDisplay->Draw(&localView, (Scalar)BTPilotDeaths(pilot)); // DEATHS (Player::deathCount)
|
||||
}
|
||||
|
||||
++currentSlot;
|
||||
|
||||
Reference in New Issue
Block a user