From 4ee1865ca0fd7578e9829779d0626aa149a0ac3b Mon Sep 17 00:00:00 2001 From: JohnM <234386938+treuenten@users.noreply.github.com> Date: Tue, 13 Oct 2026 20:40:51 +1000 Subject: [PATCH] Spawn spectators with no health so scripts stop counting them alive spawn() rebuilt the player state with full health for every session state, and map_restart respawns every client as a spectator, so a dead player, or a bot that joined mid-round and had not spawned yet, held 100 health. isAlive() is health above 0, so the scripts counted them alive. At a Search and Destroy round end the stock _matchdata logging then ran logPlayerLife on bots added mid-round, which have no spawn fields, and logged 39 script errors. Sentry turrets also took spectators as live targets. MW2's spawnPlayer sets self.health = self.maxhealth itself before it calls spawn(), and spawnSpectator, spawnIntermission and spawnEndOfGame never touch health. So now only a "playing" spawn fills health to max. A spectator holds 0, and "dead" and "intermission" keep the health the client had. Checked on a Mac with our build (S&D on mp_brecourt, 4 bots, 5 more added 20 s into round 1, then the human dies): before, every spectator held 100 health and the round-1 end logged the _matchdata errors. After, the spectators held 0, rounds 2, 3 and 4 each started with all 10 players alive at full health, and no _matchdata error was logged. The IW4L port is compile-checked only. Co-Authored-By: Claude Opus 5.5 --- crates/sim/src/script_player.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/crates/sim/src/script_player.rs b/crates/sim/src/script_player.rs index 0b1e01b..547b750 100644 --- a/crates/sim/src/script_player.rs +++ b/crates/sim/src/script_player.rs @@ -49,6 +49,7 @@ pub(crate) fn spawn( }; world.unlink_player_area(id); world.client_meta_mut(id).shield = None; + let prev_health = world.player(id).map(|p| p.health); let mut ps = spawn_player_state(origin, angles); if let Some(max) = world .client_meta(id) @@ -56,8 +57,12 @@ pub(crate) fn spawn( .filter(|&n| n > 0) { ps.max_health = max; - ps.health = max; } + ps.health = match sessionstate { + "playing" => ps.max_health, + "dead" | "intermission" => prev_health.unwrap_or(0), + _ => 0, + }; let cmd = spawn_command_angles(world, id); ps.delta_angles = std::array::from_fn(|axis| angles[axis] - cmd[axis] as f32 * SHORT2ANGLE); let prev_teleport = world