Stop a co-op companion killing its own team mate - #260
Conversation
Two human sessions on e1m2, two deaths, two different mechanisms. Both are ours, not stock friendly fire being unlucky. IT SHOOTS BACK. The 2026-09-05 session ended eighteen seconds in, with health going 100, 76, 64, 48, 24, 4 across five hits. The tape holds the cause: both parties took exactly 24 damage within one second of each other, Carmack at t 13.3 and the player at t 13.5. Shane shot Carmack, and Argus_Pain fights back against "any live player, human or bot" with no co-op test, so it made its own team mate the enemy. In co-op that branch can ONLY ever fire on a team mate, because monsters do not carry classname "player". IT SHOOTS THROUGH YOU. The earlier 157 s session died at '425 -469 320' to "chewed on Carmack's boomstick". That one never targeted the player at all: two engage events in the whole session, both engage monster. The bot was in pursue, firing at a monster's last known position during the three second sight loss hold, and the human crossed the line. It pushed GOAL_REVIVE_SUPPORT the moment he died and came to help. Two guards, both gated on coop so deathmatch cannot reach them. Argus_Pain does not retaliate against a team mate, and the fire gate traces the shot it is about to take and holds when a team mate is what it reaches. Splash is deliberately not covered: a rocket landing beside a team mate still hurts them, and that wants its own look if a session shows it. ALREADY SAFE, recorded because it reads like a hole and is not one. Argus_CoopFindThreat requires FL_MONSTER so it cannot return a player, and Argus_Perceive returns before its player scan whenever coop is on, so a companion cannot acquire a team mate through perception. A guard was written there first and then removed as dead code once the early return was found. VERIFICATION, honestly. This cannot be shown headless. A co-op botmatch runs one bot, and adding more with impulse 101 does not help because the perception early return means bots never target each other in co-op anyway. Two ally tests were built and both came back null for exactly that reason, on e1m2 and on a monster free dm4 co-op: zero bot versus bot engagements in either arm, every death world or a spawn telefrag. The retaliation path needs a player to damage a bot, and the netclient can walk and send impulses but cannot fire. So the ladder can only show absence of harm, and does. dm4 at parity, 73 cells and 17 stalls against 74 to 75 and 4 to 18 on recent tapes. dm2 at 129 cells. On a single bot tape the guards are provably inert: Argus_IsAlly can only return FALSE with no second player, and the fire trace never runs without an engage event. The e1m2 tape in this ladder pinned in water at 6 cells, which is the pre-existing stochastic pin filed separately, on a tape with zero engage events where none of this code executed. The real verification is a human session where the companion is shot and does not shoot back. Refs #258, #259 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 87999cde35
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| // shot we are about to take, and if a team mate is what it | ||
| // reaches, wait. Splash is not covered here: a rocket landing | ||
| // beside a team mate still hurts them. | ||
| traceline (self.origin + '0 0 22', aimpt, FALSE, self); |
There was a problem hiding this comment.
Trace the direction the weapon will actually fire
Trace along self.v_angle rather than directly toward aimpt. The preceding code overwrites self.v_angle_y with self.angles_y, and the trigger permits that yaw to differ from the desired aim by up to 12 degrees, while bot weapons such as W_FireShotgun fire using self.v_angle. During normal aim convergence, a teammate can therefore be directly in the actual firing line while this trace follows a different line and allows the shot, leaving the co-op team-kill regression unfixed.
Useful? React with 👍 / 👎.
| traceline (self.origin + '0 0 22', aimpt, FALSE, self); | ||
| if (Argus_IsAlly (trace_ent)) |
There was a problem hiding this comment.
Check the full shotgun spread before firing
Account for the weapon's firing envelope instead of checking only one center ray. FireBullets sends six or fourteen randomized traces with nonzero horizontal and vertical spread, extending 2048 units, so a teammate who is within the shotgun or super-shotgun pellet cone but not intersecting this exact ray—including one behind the intended target—can still be hit repeatedly and killed. This is especially relevant because the reported co-op death was caused by the bot's boomstick.
Useful? React with 👍 / 👎.
Two human sessions on e1m2, two deaths, two different mechanisms. Both are ours, not stock friendly fire being unlucky.
It shoots back
The 2026-09-05 session ended eighteen seconds in, health going 100, 76, 64, 48, 24, 4 across five hits in five seconds.
The tape holds the cause. Both parties took exactly 24 damage within one second of each other:
Shane shot Carmack, and
Argus_Painfights back against "any live player, human or bot" with no co-op test, so it made its own team mate the enemy. In co-op that branch can only ever fire on a team mate, because monsters do not carry classnameplayer.It shoots through you
The earlier 157 s session died at
425 -469 320to "chewed on Carmack's boomstick".That one never targeted the player at all: two engage events in the whole session, both
engage monster. The bot was inpursue, firing at a monster's last known position during the three second sight loss hold, and the human crossed the line. It pushedGOAL_REVIVE_SUPPORTthe moment he died and came to help.The fix
Two guards, both gated on
coopso deathmatch cannot reach them.Argus_Paindoes not retaliate against a team mate.Splash is deliberately not covered: a rocket landing beside a team mate still hurts them, and that wants its own look if a session shows it.
Already safe
Recorded because it reads like a hole and is not one.
Argus_CoopFindThreatrequiresFL_MONSTERso it cannot return a player, andArgus_Perceivereturns before its player scan whenevercoop > 0, so a companion cannot acquire a team mate through perception. A guard was written there first and removed as dead code once the early return was found.Verification, honestly
This cannot be shown headless. A co-op botmatch runs one bot, and adding more with impulse 101 does not help, because the perception early return means bots never target each other in co-op anyway. Two ally tests were built and both came back null for exactly that reason, on e1m2 and on a monster free dm4 co-op: zero bot versus bot engagements in either arm, every death
worldor a spawn telefrag. The retaliation path needs a player to damage a bot, and the netclient can walk and send impulses but cannot fire (#259).So the ladder can only show absence of harm, and does:
On a single bot tape the guards are provably inert:
Argus_IsAllycan only return FALSE with no second player, and the fire trace never runs without an engage event. The e1m2 tape in this ladder pinned in water at 6 cells, which is the pre-existing stochastic pin filed as #256, on a tape with zero engage events where none of this code executed.The real verification is a human session where the companion is shot and does not shoot back.
Refs #258, #259
🤖 Generated with Claude Code