Need help getting started? Try the player guide. These notes include older work; the game overview explains what’s ready now.
PC enemy crowd movement
The playable frontend enables local body sliding for CD and CE..D8 walking controllers. A blocked enemy moves along the blocking enemy's square collision face, retaining its chosen side across frames so it can get around the body instead of steering immediately back into it. Centered head-on pairs choose opposite world directions. If a wall blocks that side, the other side is tried. The original navigation, turning, speed, attack logic and Time Strike freeze gate remain in place. This is a requested PC behavior extension, not original ROM behavior or a new matching function.
ssa_enemy_walk_world_sliding uses the recovered walking routine's existing
movement callback. The movement-call counter and vertical physics still run
once per walk, including blocked walks. Horizontal displacement is split into
steps of at most four units per axis. A lateral step uses the larger requested
component, so its length cannot exceed that requested step's length. Every
candidate goes through world entity contacts, static walls, sector clearance,
steps and normal spatial unlink/relink. It moves only the current enemy and
does not push other bodies, clear collision flags, or exclude a neighbor.
Already overlapping enemy pairs can move gradually apart. The PC query permits this only when they were already colliding, neither absolute separation shrinks, at least one grows, and neither center is crossed. Other bodies are checked independently: a previously clear pair cannot gain overlap under this exception. The exception does not apply to players. Player blockages retain the original blocked result for contact attacks; failed side probes restore the original enemy blocker instead of leaving a later probe's wall/player result behind. For Prion melee, crowd movement additionally records the first real enemy contact before trying sidesteps. This transient contact resets on every move; it is distinct from the persistent side-preference blocker. Combat consumes it within the same controller call, allowing melee when either body is converted even if the attacker subsequently slides successfully. It is never reused as a target on a later frame or dereferenced after actor retirement.
The PC query extends entity-cell searches by128 units to cover all original
enemy radii, including Gunrilla's90 units across cell boundaries. Original
ssa_world_query/try_move retain their32-unit search margin and exact recovered
contact behavior. The world structure and original API layouts are unchanged.
The crowd API's coverage contract is original enemy radii up to128 units.
Zero-initialize one SsaEnemyCrowd per enemy lifetime. Its cached blocker is an
identity for the side preference only and is never dereferenced. Generic runtime
bindings reset this state on slot reuse; CD initialization clears its own state.
crowd_slide defaults off in both runtime adapters. The native frontend enables
the generic adapter and game.enemy_crowd_slide, which propagates to newly bound
CD actors. Shared recovered controller/motion entry points stay unchanged.
State/world query guards reject reentry; callbacks must honor existing world
ownership and synchronous-special contracts. Stable storage remains required.
Validation commands:
wsl -d Ubuntu --exec .venv-linux/bin/python tests/test_enemy_crowd.py
powershell -ExecutionPolicy Bypass -File scripts/test-enemy-crowd.ps1
The dedicated fixtures cover a stationary blocker, two head-on enemies, coincident spawns with monotonic separation checks, wall-side reversal, a fully blocked corner, player contacts, one-call gravity, large-displacement collision substeps, large-radius cell coverage and reentry guards. Diagnostic room fixtures use the real world/geometry code. Separate walking fixtures cover all16 original maps and all enemy type IDs; a512-tick first-level native input run verifies live AI movement and automatic CD policy binding. These are focused integration fixtures, not an uninterrupted campaign completion proof.
Reports: enemy-crowd-validation.json and windows-enemy-crowd-validation.json.
Original world and enemy-motion ROM comparisons remain separate regressions.
Current dedicated result:20,456 ASan/UBSan/leak assertions,1,503 diagnostic crowd frames,158 overlap separation checks,6,144 real-map walks and512 native input ticks with274 observed enemy moves. MSVC and GCC agree. Original motion still passes88,226 ROM comparisons; original world predicates/queries/moves, synchronous pickups, spatial lists and player/jump/H integration still pass. The Windows special-weapons checker also retains22,349 passing checks.
The normal-input first-level run now finishes in5,110 frames with24,000 score, eight pickups,19 spawns and100 health, using13 pistol and21 shotgun shots. Its earlier28,000 minimum score failed despite a successful exit: crowd motion changes how many kamikazes detonate without player credit. The completion test now requires positive earned score in original2,000-point increments, zero living enemies, zero outstanding script groups, all19 spawns, survival, pickups and the actual VM exit. It neither grants score nor changes the input route. Windows compares every completion field against the current source-hashed sanitizer run. The profile completion/save test uses that same current result instead of its old4,977-frame assumption. The full nine-map campaign remains historical evidence and was not replayed for this movement change.
PC player clearance
Campaign probing exposed the same32-unit broadphase limit in player movement.
A Gunrilla's90-unit radius can overlap Sam before its center enters that search.
Once the overlap is detected, the original predicate can reject retreat as well.
ssa_world_try_move_player searches128 units for enemy centers and permits only
monotonic separation of an already overlapping player/enemy pair: neither axis
distance may decrease, at least one must increase, and centers cannot cross.
New contacts, walls, floor/ceiling checks and spatial relinking remain active.
The enemy is never moved or made nonsolid.
Only CD..D8 bodies participate in the additional cells. In particular, E1
pickups retain their original cell-based touch range and script timing. The
original query, try_move, horizontal and player-motion entry points retain their
ROM behavior. ssa_world_horizontal_step_player and
ssa_player_motion_world_step_pc bind the policy to the existing PC player
runtime without changing public structure layouts.
The12 focused scenarios now include reproducing the missed Gunrilla contact, blocked approaches, gradual retreat, a second enemy obstructing retreat, center crossing, wall limits, unchanged pickup coverage and invalid/reentrant calls. An isolated player-runtime fixture runs16 movement/jump/H frames out of a controlled overlapping spawn with presentation/hazard boundaries controlled; the separate512-frame full-game fixture still runs real native AI. GCC ASan/UBSan/leaks and MSVC agree on20,456 checks. Original world tests and 8,672 original player-motion comparisons also pass. Windows gameplay smoke, both startup renderers and the normal5110-frame first-level completion pass.
The tenth campaign map also completes from its original Normal-difficulty
standalone loadout with the real mixer and both renderers. This is a separate
development proof, not campaign admission. Reproduce it with
tests/test_campaign.py --fresh 9 or
ssa_native_campaign_check.exe --fresh 9. The default Python command still
requires an uninterrupted campaign run and writes a different report. The
current full replay reaches the ninth map, where the bot dies; the frontend
therefore remains capped at nine maps. See campaign-standalone-validation.json.