gauge wave P3: fix the DEATHS runaway + RANK=-1 (solo scoreboard correct)

RANK=-1: Player::DefaultFlags creates every player NonScoring (CalcRanking only
ranks IsScoringPlayer()==true, else -1), and the bring-up never cleared it.  Call
SetScoringPlayerFlag() in DropZoneReplyMessageHandler's mech branch (a piloted mech
IS an active scoring combatant; the camera-ship branch stays non-scoring).  -> RANK=0.

DEATHS runaway (climbed ~1/s, not the "1" first seen): @004c012c -- which our
MESSAGE_ENTRY binds as the VehicleDead handler -- is actually the drop-zone
RESPAWN-RETRY helper.  The engine's RequestDropZone (PLAYER.cpp:400) posts a
2s-delayed VehicleDead(deathCount=-2) as a "did the drop zone reply?" timer; our
handler ++deathCount + RE-POSTED it UNCONDITIONALLY -> an infinite loop, because the
drop-zone handshake never "completes" in the bring-up (no console/drop-zone system).
Traced it to a pure boot artifact (13 fires with zero combat; the only VehicleDead
producers are the re-post + my kill-producer, and the kill fires AFTER the loop
starts).  FIX (correct respawn-retry semantics): if the player already has a vehicle
the drop zone WAS acquired (the DropZoneReply created it), so the retry is moot --
return without counting a death or re-posting.  -> DEATHS=0 in solo, loop gone.

Verified DBASE+dev gauges: the Comm roster shows KILLS=1 / DEATHS=0; the KILL log
reads killCount=1 deaths=0 score=164 rank=0; 0 crashes.  (MP real-death DEATHS -- a
destroyed vehicle via the separate @004c05c4 path -- remains deferred per the plan;
solo never dies so the retry gate is correct there.)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
arcattack
2026-07-07 19:19:00 -05:00
co-authored by Claude Opus 4.8
parent 99e2078b83
commit a9210e038a
+32
View File
@@ -237,6 +237,28 @@ void
Check(this); Check(this);
Check(message); Check(message);
//
// scoring wave (DEATHS fix): @004c012c is the drop-zone RESPAWN-RETRY helper --
// the engine's RequestDropZone (PLAYER.cpp:400) posts a 2s-delayed VehicleDead
// (deathCount=-2) as a "did the drop zone reply?" timer. If the player already
// has a vehicle, the drop zone WAS acquired (the DropZoneReply created it) -> the
// retry is moot: don't count a death and don't re-post. The prior unconditional
// ++deathCount + re-post was an INFINITE loop in solo (the drop-zone handshake
// never "completes" in the bring-up), climbing deathCount ~1/s. (A real MP death
// -- vehicle destroyed -> playerVehicle cleared/the @004c05c4 path -- is deferred.)
//
if (playerVehicle != 0)
{
deathPending = 0; // this+0x290
suppressConsole = 0; // this+0x258
Check_Fpu();
return;
}
if (getenv("BT_SCORE_LOG"))
DEBUG_STREAM << "[score] VehicleDeadMessageHandler: deathCount " << deathCount
<< " -> " << (deathCount + 1) << "\n" << std::flush;
// //
// One more death; tell the message (so replicants stay in sync) and // One more death; tell the message (so replicants stay in sync) and
// debit a life against the scoring role. // debit a life against the scoring role.
@@ -823,6 +845,11 @@ void
Check(this); Check(this);
Check(message); Check(message);
if (getenv("BT_SCORE_LOG"))
DEBUG_STREAM << "[score] DropZoneReply: hasVehicle=" << (playerVehicle != 0)
<< " deathCount=" << deathCount << " msgDeath=" << message->deathCount
<< " state=" << (int)GetSimulationState() << "\n" << std::flush;
if (!playerVehicle) // param_1[0x7f] == 0 if (!playerVehicle) // param_1[0x7f] == 0
{ {
CreatePlayerVehicle(message->dropZoneLocation); // (**vtable+0x40)(origin) CreatePlayerVehicle(message->dropZoneLocation); // (**vtable+0x40)(origin)
@@ -836,6 +863,11 @@ void
if (playerVehicle->GetClassID() == Mech::MechClassID) // 0xbb9 if (playerVehicle->GetClassID() == Mech::MechClassID) // 0xbb9
{ {
SetPerformance(&BTPlayer::PlayerSimulation); // perf @00513058 SetPerformance(&BTPlayer::PlayerSimulation); // perf @00513058
// scoring wave (RANK fix): an active mech combatant is a SCORING player.
// Player::DefaultFlags creates every player NonScoring (rank -1 until
// activated); CalcRanking only ranks IsScoringPlayer()==true. The camera
// ship (below) stays non-scoring; a piloted mech becomes scoring here.
SetScoringPlayerFlag(); // clear NonScoringPlayerFlag
} }
else // camera ship (0x45) else // camera ship (0x45)
{ {