Decouple gameplay from presentation and preserve signed movement fractions. Separate cinematic camera ownership with safe body handoff and transactional current-room restarts. Resample input latches on focus regain, discard modal timing debt, and attach cinematic lighting to the view to match editor preview. Verify editor 137/137 and ASM 288/288, cinematic traces, RGB bake and IPC framebuffer parity without changing project assets.
182 lines
11 KiB
Markdown
182 lines
11 KiB
Markdown
# Stabilization roadmap
|
|
|
|
Stages are delivered separately. Each completed stage is tested, committed and
|
|
pushed before work stops for confirmation. Editor and runtime UI redesign will
|
|
use an Unreal Engine 5-inspired visual style; stage 1 does not redesign UI.
|
|
|
|
## Stage 1 — correctness and data safety
|
|
|
|
Implemented:
|
|
|
|
- `DrawRectOutline` keeps loop counters in a callee-saved register, not the
|
|
volatile registers overwritten by `DrawPixel`. Nonpositive sizes draw nothing.
|
|
- `TraceShotWall` traverses current map cells independently of rendering. Distances
|
|
are 24.8 world units; closed doors, glass, portals and out-of-map cells block
|
|
shots. Open doors do not. Corner-adjacent walls block diagonal seam shots.
|
|
- Hitscan chooses the nearest live guard intersecting the forward billboard
|
|
hitbox. Half-width is 16 world units times authored scale (zero scale means
|
|
legacy 1.0), not a constant number of screen pixels.
|
|
- `IsGameplayBlocked` centralizes gameplay gating. `UpdatePlayer` latches blocked
|
|
ticks before processing cinematic state. AI, pickups and weapon timers freeze
|
|
during active/pending cutscenes and room fades. END/skip and instant transitions
|
|
cannot resume AI or fire stale input halfway through a tick. Death blocks player
|
|
movement, turning and firing; lethal damage stops the remaining entity pass.
|
|
- Packed entity metadata remains binary-compatible. Only `flags == 0` enables
|
|
legacy texture-based archetypes; any nonzero flags explicitly encode the low
|
|
type byte, including Prop. Health uses one byte: 1..255, zero = legacy 100 HP.
|
|
The C++ setter clamps before narrowing, and the inspector exposes 1..255.
|
|
- IDE source writes use a unique sibling temporary file, checked full write,
|
|
flush, close and atomic replacement. Failure preserves disk contents and dirty
|
|
buffer. Loading publishes a new document only after a complete valid text read.
|
|
File switches and builds stop on failed autosave. Saves target the file actually
|
|
opened, not a newly resolved fallback path.
|
|
- Compiler output is drained even with the IDE hidden. Process startup uses the
|
|
system command processor with quoting for project paths containing spaces.
|
|
|
|
### Verification
|
|
|
|
Windows commands:
|
|
|
|
```cmd
|
|
build.bat
|
|
editor\build_editor.bat
|
|
python tools\test_regressions.py
|
|
```
|
|
|
|
Results: editor **112/112**, ASM **148/148**. The suite also checks live x86/x64
|
|
preview IPC, DX11 pixel equality, cinematic trace equality and unchanged project
|
|
assets. New tests exercise production gameplay and IDE procedures, including
|
|
failed atomic replacement under a Windows sharing lock and hidden compiler output
|
|
exceeding pipe capacity. Interactive UI playtesting remains separate.
|
|
|
|
## Preparation for stage 2 — integration with current main
|
|
|
|
Integrated server history through `2bac213` with stage-1 commit `f42679f` in an
|
|
isolated worktree, preserving both histories and the original working directory.
|
|
Retained map v5, preview IPC v2, authored lighting, three weapons, explosive
|
|
barrels, modal dialogues, peaceful mode and the updated studio UI.
|
|
|
|
Compatibility adjustments:
|
|
|
|
- Pause and modal dialogue join the shared gameplay gate, including direct calls
|
|
to gameplay procedures. Blocked player updates cannot activate portal triggers.
|
|
- Knife/pistol/shotgun keep their range, damage, ammunition and reserved light
|
|
behavior. World-space half-widths are respectively 24/16/32 times sprite scale;
|
|
the shotgun remains a single-target wider hit test, not three separate rays.
|
|
- Barrel texture fallback is enabled only with zero flags in both editor and ASM.
|
|
Explicit Prop overrides barrel artwork and cannot explode.
|
|
- A new explosion regression reproduced access violation `0xC0000005`: the AOE
|
|
loop kept a sprite pointer in EDX and destroyed it with CDQ. The pointer now
|
|
stays in EBX. A nearby guard receives blast damage without crashing.
|
|
- IDE tab navigation, API catalog and docked view retain stage-1 safe writes.
|
|
|
|
Verification: both production builds succeeded; editor **137/137**, ASM
|
|
**191/191**. All 1024 baked RGB cells match between C++ and ASM; classic and lit
|
|
preview frames match direct ASM rendering, including the x86/x64/DX11 path.
|
|
Project assets remain byte-for-byte unchanged. The original working directory
|
|
still has only its pre-existing `editor/imgui.ini` modification. No separate
|
|
interactive UI playtest was performed during this preparation.
|
|
|
|
### Stage 2 implementation contract
|
|
|
|
1. Separate message/input polling, fixed simulation ticks and drawing/presentation.
|
|
Use QPC and a 60 Hz accumulator with bounded catch-up. Consume edge-triggered
|
|
commands once, not once per catch-up tick. Reset timing debt after pause/focus
|
|
stalls; advance world timers/lights only on the appropriate simulation tick.
|
|
2. Keep movement fractions in runtime state without changing serialized raw-unit
|
|
coordinates. Cancel opposing inputs, normalize combined forward/strafe input,
|
|
and use symmetric signed accumulation. Clear fractions on load/teleport/restart.
|
|
3. Check the full collision body on each axis and preserve wall sliding; prevent
|
|
corner clipping and occupied-door closure. Keep portals intentionally walkable.
|
|
4. Centralize Playing/Cutscene/Transition/Dead/Paused/Dialog update rules, including
|
|
`Game_OnTick`, dynamic lights, fade, toasts and modal dismissal. Render routines
|
|
must not advance those timers or perform input-driven state changes.
|
|
5. Separate cinematic camera pose from gameplay/player position. Define END/skip
|
|
handoff explicitly and validate the restored gameplay collision body, including
|
|
paths crossing walls. Keep C++ preview and ASM cinematic traces aligned.
|
|
6. Track the current room path and its loaded entry spawn separately from startup
|
|
map. Restart restores that room and entry pose, resets gameplay transients and
|
|
fractional movement, and preserves project mode/settings. Failed reload must
|
|
not partially reset the current room. Make death-R and pause restart share it.
|
|
|
|
Acceptance checks: identical simulation after 60 ticks under different render
|
|
schedules; single consumption of edge inputs during catch-up; symmetric movement
|
|
for all quadrants and equal cardinal/diagonal speed; collision corner/slide cases;
|
|
no simulation advancement during pause/dialog; no pickups/AI along camera paths;
|
|
safe cinematic handoff; restart after a room transition; failed restart rollback;
|
|
unchanged v1..v5 loading, lighting bake parity and IPC v2 framebuffer parity.
|
|
|
|
## Stage 2 — predictable simulation
|
|
|
|
Implemented without changing EBLN v1..v5, sprite coordinates or preview IPC v2:
|
|
|
|
- `include/engine_simulation.inc` owns QPC scheduling and `Engine_Tick`. The
|
|
accumulator stores elapsed counts multiplied by 60, avoiding rounded tick
|
|
periods. Each pump runs at most eight ticks; stalls over 250 ms, backwards
|
|
counter samples, pause/resume, focus changes, modal boundaries and successful
|
|
restarts discard timing debt. Drawing runs independently, without interpolation.
|
|
Under overload the engine drops debt rather than fast-forwarding.
|
|
- `PollInput` records held movement and pending command edges. An edge survives
|
|
render-only frames and is consumed after one simulation tick, including blocked
|
|
ticks. On focus regain physical latches are resampled and new edges discarded;
|
|
movement may resume while held, but actions require release and a fresh press.
|
|
F12 remains a presentation-only screenshot request.
|
|
- Movement uses signed 16.16 runtime remainders with truncation toward zero.
|
|
Opposing axes cancel before rounding; combined forward/strafe uses 46341/65536
|
|
normalization. Collision checks the axis-aligned body against every overlapped
|
|
cell, stepping one world unit per axis with wall sliding (maximum 64 substeps
|
|
per tick). Blocked axes discard their remainder. Portals remain walkable; open
|
|
doors cannot close over the body. Fractions reset on room load and camera handoff.
|
|
- State precedence is Unfocused, Paused, Dialog, Transition, Dead, Cutscene,
|
|
Playing. Playing updates player, entities, combat, `Game_OnTick`, lights and
|
|
toast lifetime in that order, rechecking the shared gameplay gate between
|
|
subsystems and entities. Cutscene updates camera/track and lights only;
|
|
transition advances only its fade/load sequencer. Death accepts R; pause accepts
|
|
menu commands; dialog accepts E/Space, Enter or attack dismissal. Other world
|
|
timers freeze in those states. END/skip and transition completion never resume
|
|
gameplay partway through the same tick.
|
|
- `Engine_Frame` and HUD/overlay procedures do not update gameplay timers, dismiss
|
|
dialogs or load rooms. `UpdateScreenFade` is the tick sequencer;
|
|
`ApplyScreenFade` is only a framebuffer operation.
|
|
- Cinematics own `csCameraX/Y/Angle`; rendering selects `viewX/Y/Angle` without
|
|
moving the player's body. Wall samples and camera-attached flashlight use the
|
|
cinematic view, matching the editor preview. Paths may cross walls. END/Tab
|
|
transfers position and angle only if the entire body is walkable at the camera
|
|
pose; otherwise the previous safe player pose remains. No path search or
|
|
collision correction is attempted. Existing sound/overlay cleanup remains.
|
|
- `RestartCurrentRoom` reloads the remembered room path with its actual entry
|
|
spawn (including transition overrides), then resets entities, health/ammo,
|
|
keys/score, combat flashes/cooldown, dialog/toast, fades, pending transition and
|
|
movement fractions. Authored lighting and original room data are reloaded;
|
|
game genre, weapon/HUD configuration and selected weapon remain unchanged.
|
|
Procedural startup fallback restarts procedurally. Failed file reload leaves
|
|
the active room and gameplay intact; it does not revive a dead player or resume
|
|
a paused game. Death R and pause-menu item 4 use this same procedure.
|
|
|
|
Verification commands are the same as stage 1. New production ASM checks cover
|
|
different render schedules, catch-up limits and 64-bit QPC arithmetic, pending
|
|
edges and focus regain, pause/modal timing, all 512 movement directions, diagonal
|
|
normalization, sprint, body corners/sliding/doors, high-speed wall collision,
|
|
wall-crossing cutscene skip/END, lit camera framebuffer parity, both restart entry
|
|
points and failed-reload rollback. The runner's lighting/cutscene labels now follow
|
|
the actual ASM call order rather than mislabelling failures. Interactive keyboard
|
|
and UI playtesting remains separate; no UI redesign is included in this stage.
|
|
|
|
Verification (2026-10-31): production engine and editor builds succeeded; editor
|
|
**137/137**, ASM **288/288**. All 320 projection angles, frame-by-frame C++/ASM
|
|
cinematic traces and 1024 baked RGB cells agree. Classic/lit IPC v2 frames match
|
|
direct ASM rendering through the x86/x64/DX11 path. Project assets remain
|
|
byte-for-byte unchanged. No manual interactive playtest was performed.
|
|
|
|
## Remaining stages
|
|
|
|
3. Honest editor/gameplay: complete undo, keys/switches and supported interactions;
|
|
UE5-inspired editor/runtime UI redesign.
|
|
4. Project lifecycle and validated, safe standalone export.
|
|
5. Measurements and targeted renderer/editor optimizations.
|
|
6. Finished multi-room demo game and release verification.
|
|
7. Data-driven extensions and subsequent rendering features.
|
|
|
|
Stages 1 and 2 do not claim that all audit findings are fixed: AI visibility/melee
|
|
through walls, full undo and exporter safety remain assigned to subsequent
|
|
stages. Existing map and texture assets are not migrated or rewritten.
|