fix(runner): link monsters into the area tree so hitscan connects - #18
Merged
Conversation
Shots (and every SV_Move query) passed straight through all 55 monsters in lq_e0m1: they fired and consumed ammo but never dealt damage. Root cause was two coupled defects in how spawned entities enter the collision area tree. 1. setsize was a no-op. QC monsters set solid=SOLID_SLIDEBOX then call setmodel + setsize. Their alias .mdl bbox does not resolve in setmodel, so setmodel skips their area-tree link (only SOLID_BSP doors, whose submodel bbox resolves, were linked). setsize -- which in vanilla (SV_SetSize -> SV_LinkEdict) writes the entity's mins/maxs/size AND relinks it -- did nothing here, so monsters got neither a collision bbox nor a tree entry. Implement it. 2. setmodel/setsize keyed the area tree by the SECOND return of ResolvePointer, which is the byte OFFSET within the field block, not the edict index. A setmodel(self,...) / setsize(self,...) pointer addresses field-offset 0, so that value is 0 for every entity -- collapsing them all onto world.Key(0) (the world slot the query skips). Key by the edict's arena slot via Arena.NumFor(ent), matching how host.SetOrigin already links. With both fixed, traceline's candidate query returns the monsters and the swept-bbox clip connects, so the QC's TraceAttack -> T_Damage path lands. Verified in-browser (Firefox/Playwright, lq_e0m1) via a temporary area-tree census + weapon-independent hitscan probe (both since removed): linked solids went 11 (0 slidebox) -> 76 (55 slidebox = all 55 monster edicts), the monster bbox reads back [-16 -16 -24]/[16 16 40], and a point-blank trace through the nearest monster's centre returns that monster's slot. runner physics probe still 8/8 (no spawn/view regression). Adds runner/setsize_test.go covering the bbox write, correct-slot linking (the world.Key(0) regression guard), and the SOLID_NOT unlink case. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
In lq_e0m1 the player fires and consumes ammo, but shots never damage any of the 55 monsters — hitscan passes straight through them.
Root cause — two coupled defects in area-tree linking
setsizewas a no-op. QC monsters setsolid=SOLID_SLIDEBOX, then callsetmodel+setsize. Their alias.mdlbbox doesn't resolve insetmodel, sosetmodelskips their area-tree link (onlySOLID_BSPdoors, whose submodel bbox resolves, got linked). Vanillasetsize(SV_SetSize→SV_LinkEdict) writes the entity'smins/maxs/sizeand relinks it — here it did nothing, so monsters had neither a collision bbox nor a tree entry. Now implemented.Wrong area-tree key.
setmodel/setsizekeyed the tree byResolvePointer's second return — the byte offset within the field block, not the edict index. Asetsize(self,…)pointer addresses field-offset 0, so that value is0for every entity → all collapsed ontoworld.Key(0)(the world slot the query skips). Now keyed byArena.NumFor(ent), matching howhost.SetOriginalready links.With both fixed,
traceline's candidate query returns the monsters and the swept-bbox clip connects, so the QCTraceAttack→T_Damagepath lands.Verification (autonomous, Firefox/Playwright, lq_e0m1)
Via a temporary area-tree census + weapon-independent hitscan probe (since removed):
[-16 -16 -24]/[16 16 40]Physics probe still 8/8 (no spawn/view regression). Adds
runner/setsize_test.gocovering the bbox write, correct-slot linking (theworld.Key(0)regression guard), and theSOLID_NOTunlink case.🤖 Generated with Claude Code