Group level edits across inspector and canvas gestures, preserve legacy maps, and expose opt-in switch/key door bindings without changing wire layouts. Refresh studio and pause UI with UE5-inspired surfaces and content drawer. Verify 162 editor and 307 ASM checks plus renderer parity.
236 lines
15 KiB
Markdown
236 lines
15 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.
|
||
|
||
## Stage 3 — authored history, interactions and studio UI
|
||
|
||
Implemented:
|
||
|
||
- Nested `LevelEdit` scopes compare complete level snapshots around production UI
|
||
edits. Inspector transforms, sprite texture/type/AI parameters, spawn/angle,
|
||
palette/presets, portal filenames/destinations/fade, light settings/sources and
|
||
cinematic events/strings participate in Undo/Redo. Existing explicit `PushUndo`
|
||
calls are suppressed inside these scopes, avoiding duplicate entries. A stable
|
||
active widget or canvas stroke groups continuous edits; release-time baking and
|
||
timeline normalization belong to the same gesture. Selection/view changes and
|
||
clicks without authored changes create no history. New edits clear Redo; history
|
||
is limited to 64 snapshots. Successful loads clear history, failed loads preserve
|
||
it; creating a default level is undoable. Undo/Redo clear active widgets, drag
|
||
state and selection, restore RGB bake/signature and rescrub cinematic preview.
|
||
- Sprite drags clamp to map bounds. Inspector duplication returns immediately,
|
||
avoiding references invalidated by vector growth. Releasing a light drag outside
|
||
the canvas clears its radius/position drag state.
|
||
- A switch can opt into one authored door target without changing EBLN v5 or IPC
|
||
v2 layouts. Flags low byte is 5; byte 1 is `0xA8` (switch-only door) or `0xA9`
|
||
(also unlockable with a key), bytes 2/3 are door cell X/Y. Other switch flags
|
||
retain legacy hook/toggle behavior. Each door has at most one bound switch;
|
||
C++ and ASM loaders reject non-door targets, out-of-bounds targets and duplicate
|
||
bindings before replacing the room. Painting away a bound door leaves a visible
|
||
inspector error and blocks save/F5 until the target is repaired or unbound.
|
||
- Ordinary doors remain free. Bound doors require their switch, or one collected
|
||
key if explicitly enabled. First direct unlock consumes one key and persists
|
||
until room reinitialization; subsequent opens/closes are free. Switch use itself
|
||
never consumes a key. `ToggleDoorCell` shares full-player-body closure protection
|
||
between direct and remote interactions. An occupied/invalid target does not
|
||
change switch state. `E` prioritizes the facing door/portal, then the first nearby
|
||
switch in authored order, consuming the action once. Custom switch hooks run
|
||
before default door behavior and may handle the action themselves. This stage
|
||
does not add generic proximity dialogs or switch line-of-sight tests.
|
||
- Removed fake per-entity dialogue fields and the nonfunctional insertion promise.
|
||
UI explicitly identifies dialogue as an `engine_gameplay.inc` code hook, not map
|
||
data. Texture/audio import and project settings remain external file operations,
|
||
not part of level Undo; IDE text history is separate.
|
||
- UE5-inspired studio uses charcoal surfaces, compact square panels, Outliner and
|
||
Details headers, a two-row horizontally scrollable toolbar, resizable views and
|
||
a bottom Content Drawer (`Ctrl+Space`). Minimum desktop size is 1000x680; narrow
|
||
mode shortens viewport tabs. No docking framework or alternative renderer was
|
||
introduced. Workspace geometry ignores stale ImGui layout entries; no existing
|
||
`imgui.ini` is rewritten by the implementation. F5/F6/F7 remain; F focuses, G
|
||
fills, resolving the previous F conflict. Runtime pause menu uses the same
|
||
restrained charcoal/blue visual language with five clearly separated rows.
|
||
|
||
Verification (2026-10-31): production builds succeed; editor **162/162**, ASM
|
||
**307/307**. New checks exercise actual ImGui spawn/sprite/portal inputs and palette
|
||
preset buttons, history grouping/branching/capacity, workspace/content drawer at
|
||
1000/1360/1920 widths, real C++/ASM binding validation, key consumption, legacy
|
||
compatibility, single-action priority and remotely occupied doors. All 320 ray
|
||
angles, cinematic traces, 1024 RGB bake cells and classic/lit IPC frames still
|
||
agree. Project assets remain byte-for-byte unchanged. No manual visual/keyboard
|
||
playtest was performed; automated UI checks do not replace it.
|
||
|
||
## Remaining stages
|
||
|
||
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–3 do not claim that all audit findings are fixed: AI visibility/melee
|
||
through walls and exporter safety remain open. Existing map and texture assets
|
||
are not migrated or rewritten.
|