From a9210e038a5035c7bbc1902308a7bf523217f41a Mon Sep 17 00:00:00 2001 From: arcattack Date: Tue, 7 Jul 2026 19:19:00 -0500 Subject: [PATCH] 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) --- game/reconstructed/btplayer.cpp | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/game/reconstructed/btplayer.cpp b/game/reconstructed/btplayer.cpp index 64206fa..42ac51a 100644 --- a/game/reconstructed/btplayer.cpp +++ b/game/reconstructed/btplayer.cpp @@ -237,6 +237,28 @@ void Check(this); 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 // debit a life against the scoring role. @@ -823,6 +845,11 @@ void Check(this); 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 { CreatePlayerVehicle(message->dropZoneLocation); // (**vtable+0x40)(origin) @@ -836,6 +863,11 @@ void if (playerVehicle->GetClassID() == Mech::MechClassID) // 0xbb9 { 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) {