Add slice 3 execution plan
This commit is contained in:
453
SLICE3.MD
Normal file
453
SLICE3.MD
Normal file
@@ -0,0 +1,453 @@
|
|||||||
|
# Add Debug Foundation
|
||||||
|
|
||||||
|
This ExecPlan is a living document. The sections `Progress`, `Surprises & Discoveries`, `Decision Log`, and `Outcomes & Retrospective` must be kept up to date as work proceeds.
|
||||||
|
|
||||||
|
This plan follows `PLANS.md` in the repository root. It is intentionally self-contained so a developer with only this repository and this file can implement Slice 3 without reading prior chat history.
|
||||||
|
|
||||||
|
## Purpose / Big Picture
|
||||||
|
|
||||||
|
After this slice, the project will have shared debug tools that later hero, weapon, enemy, cluster, mission, boss, and highscore slices can reuse. A developer will be able to boot directly into a debug sandbox, pause and resume time, step one frame, change time scale, switch difficulty, set seed, toggle test flags, request actor spawns by content id, jump to a timeline marker, restart the sandbox, reload the scene, and see the active debug state in an overlay.
|
||||||
|
|
||||||
|
The observable result is concrete: from `D:\Code\zfxaction26_1`, `dotnet test SideScrollerGame.sln` validates the debug command service, `dotnet build SideScrollerGame.sln` succeeds, and `.\godot --headless --path godot -- --debug-boot=debug-sandbox --debug-script=foundation-smoke --seed=333` boots a Godot debug sandbox, runs a scripted sequence of debug commands, prints the commands and resulting state, prints `Debug foundation smoke succeeded`, and exits with code 0.
|
||||||
|
|
||||||
|
This slice does not implement the real hero, weapons, enemies, clusters, or mission runner. It creates the control surface and fake sandbox targets that later slices can replace with real gameplay handlers while keeping the same debug command names and fast testing workflow.
|
||||||
|
|
||||||
|
## Progress
|
||||||
|
|
||||||
|
- [x] (2026-04-21 17:43Z) Read repository rules, Windows rules, `PLANS.md`, `CODE.md`, `SLICE2.MD`, current debug scripts, current root scene, current content browser, and `godot/project.godot`.
|
||||||
|
- [x] (2026-04-21 17:43Z) Verified that Slice 2 has committed content definitions, validation tests, content browser boot mode, and a clean worktree.
|
||||||
|
- [x] (2026-04-21 17:43Z) Created this Slice 3 ExecPlan.
|
||||||
|
- [ ] Implement pure debug runtime state and command service.
|
||||||
|
- [ ] Add unit tests for debug commands and validation behavior.
|
||||||
|
- [ ] Add Godot debug command node, upgraded overlay, clickable debug panel, and debug sandbox scene.
|
||||||
|
- [ ] Add debug sandbox boot mode and input actions.
|
||||||
|
- [ ] Add headless debug foundation smoke script.
|
||||||
|
- [ ] Run formatting for touched C# files with `jb cleanupcode --build=False`.
|
||||||
|
- [ ] Validate with .NET tests, .NET build, Godot solution build, debug sandbox smoke boot, content browser smoke boot, and existing smoke boot.
|
||||||
|
- [ ] Commit the completed slice.
|
||||||
|
|
||||||
|
## Surprises & Discoveries
|
||||||
|
|
||||||
|
- Observation: The active Godot project is under `godot/`, not at the repository root.
|
||||||
|
Evidence: `godot/project.godot` is the configured project file, and all successful Godot commands use `.\godot --path godot ...` from `D:\Code\zfxaction26_1`.
|
||||||
|
|
||||||
|
- Observation: The current debug overlay only shows boot mode, seed, scene id, and debug build state.
|
||||||
|
Evidence: `godot/scripts/debug/DebugOverlay.cs` contains `SetStatus(DebugSettings settings, string loadedSceneId)` and a single label.
|
||||||
|
|
||||||
|
- Observation: The current root boot modes are `Menu`, `Smoke`, and `ContentBrowser`.
|
||||||
|
Evidence: `godot/scripts/debug/DebugBootMode.cs` contains those three enum values, and `godot/scripts/bootstrap/GameRoot.cs` switches between menu, smoke, and content browser scenes.
|
||||||
|
|
||||||
|
- Observation: Content definitions and tests are available for difficulty ids, enemy ids, and mission timeline marker names.
|
||||||
|
Evidence: `godot/scripts/content/samples/SampleContent.cs` creates `difficulty.easy`, `difficulty.normal`, `difficulty.hard`, `enemy.serial`, `enemy.parallel`, and mission markers such as `cluster.opening`.
|
||||||
|
|
||||||
|
## Decision Log
|
||||||
|
|
||||||
|
- Decision: Implement debug command rules as plain C# first, with a Godot node wrapper for engine effects.
|
||||||
|
Rationale: Command validity, flags, selected difficulty, time scale values, spawn requests, and marker jumps are pure rules that can be tested quickly with `dotnet test`. Godot-specific work such as pausing the scene tree and reloading scenes belongs in the node wrapper.
|
||||||
|
Date/Author: 2026-04-21 / Codex.
|
||||||
|
|
||||||
|
- Decision: Use one shared debug command service instead of wiring shortcuts directly inside the sandbox scene.
|
||||||
|
Rationale: Later slices need the same commands from keyboard shortcuts, clickable debug UI, tests, future automation, and gameplay sandboxes. A shared service avoids duplicating behavior in every scene.
|
||||||
|
Date/Author: 2026-04-21 / Codex.
|
||||||
|
|
||||||
|
- Decision: Make Slice 3 prove spawn and timeline commands with fake sandbox objects.
|
||||||
|
Rationale: Real enemies, projectiles, clusters, and mission timeline systems do not exist yet. A fake actor and fake marker jump still prove the debug command surface and preserve the same handler interfaces for later real systems.
|
||||||
|
Date/Author: 2026-04-21 / Codex.
|
||||||
|
|
||||||
|
- Decision: Add `DebugSandbox` as a boot mode instead of expanding `ContentBrowser`.
|
||||||
|
Rationale: The content browser is for inspecting definitions. The debug sandbox is for exercising runtime commands. Keeping them separate prevents the content browser from becoming a mixed-purpose tool.
|
||||||
|
Date/Author: 2026-04-21 / Codex.
|
||||||
|
|
||||||
|
## Outcomes & Retrospective
|
||||||
|
|
||||||
|
Not started. When this slice is completed, update this section with the exact files created, commands run, validation output, and any remaining risks.
|
||||||
|
|
||||||
|
## Context and Orientation
|
||||||
|
|
||||||
|
The repository root is `D:\Code\zfxaction26_1`. The Godot project root is `D:\Code\zfxaction26_1\godot`. The repo-local wrapper `D:\Code\zfxaction26_1\godot.cmd` launches Godot 4.5.1 .NET, so Godot commands are run from the repository root with `.\godot --path godot ...`.
|
||||||
|
|
||||||
|
Current important files are:
|
||||||
|
|
||||||
|
- `godot/project.godot`: Godot project configuration. It sets `run/main_scene="res://scenes/bootstrap/GameRoot.tscn"`, the C# assembly name, and input actions for movement, primary fire, secondary fire, special fire, pause, debug overlay, and quick restart.
|
||||||
|
- `SideScrollerGame.sln`: Visual Studio solution. It contains the Godot C# project and the `tests/SideScrollerGame.Content.Tests` xUnit project.
|
||||||
|
- `godot/scripts/bootstrap/GameRoot.cs`: root scene script. It reads `DebugSettings`, seeds Godot randomness, prints boot mode and seed, and loads a scene based on `DebugBootMode`.
|
||||||
|
- `godot/scenes/bootstrap/GameRoot.tscn`: main scene. It wires `MenuScene`, `SmokeScene`, `ContentBrowserScene`, and a `DebugOverlay` child.
|
||||||
|
- `godot/scripts/debug/DebugBootMode.cs`: debug boot enum. It currently contains `Menu`, `Smoke`, and `ContentBrowser`.
|
||||||
|
- `godot/scripts/debug/DebugSettings.cs`: reads `--debug-boot=<value>` and `--seed=<integer>` from user command-line arguments. It already supports hyphenated boot names such as `content-browser`.
|
||||||
|
- `godot/scripts/debug/DebugOverlay.cs`: small overlay label. It must become a reusable debug status overlay in this slice.
|
||||||
|
- `godot/scripts/debug/ContentBrowserController.cs`: existing debug content browser that can headlessly validate sample content.
|
||||||
|
- `godot/scripts/content/samples/SampleContent.cs`: theme-neutral sample content. Slice 3 should use it to validate difficulty ids, actor definition ids, and mission marker names.
|
||||||
|
- `tests/SideScrollerGame.Content.Tests/`: xUnit tests. Slice 3 can add debug tests here or rename the project later, but the simplest implementation is to add debug tests to this existing test project.
|
||||||
|
|
||||||
|
Definitions used in this plan:
|
||||||
|
|
||||||
|
- A debug command is a named action used only during development, such as `Pause`, `FrameStep`, `SetTimeScale`, `SpawnActor`, or `JumpToMarker`.
|
||||||
|
- A debug command service is a C# object that receives debug commands, updates debug state, and notifies registered handlers. A handler is a callback function that a scene provides when it knows how to do a command-specific action, such as spawning a fake actor in the sandbox.
|
||||||
|
- Debug state is the current shared debug information, such as whether the game is paused, the selected difficulty, current time scale, random seed, active timeline marker, and toggled flags.
|
||||||
|
- A debug sandbox is a Godot scene made only for development. It contains visible placeholder nodes and buttons so commands can be tested without real gameplay systems.
|
||||||
|
- A frame step means advancing the game by one rendered/process frame while the game is otherwise paused.
|
||||||
|
- Time scale means Godot's global simulation speed multiplier. `1.0` is normal speed, `0.5` is half speed, and `2.0` is double speed.
|
||||||
|
- Headless mode means Godot runs without opening a window. This plan uses headless mode to prove the debug sandbox from the command line.
|
||||||
|
|
||||||
|
## Plan of Work
|
||||||
|
|
||||||
|
First, add pure debug command types under `godot/scripts/debug/commands/`. Add `DebugCommandId`, an enum for commands such as `ToggleOverlay`, `Pause`, `Resume`, `TogglePause`, `FrameStep`, `SetTimeScale`, `ReloadScene`, `RestartMission`, `SetDifficulty`, `SetSeed`, `SpawnActor`, `JumpToMarker`, `ToggleCollisionShapes`, `ToggleGameplayBounds`, `ToggleInvulnerability`, `ToggleInfiniteSpecialAmmo`, and `ToggleNoEnemyFire`. Add `DebugCommandResult`, a small result object with `Succeeded`, `Message`, and optional `CommandId`. Add `DebugRuntimeState`, a plain C# class or record that stores overlay visibility, pause state, time scale, active difficulty id, seed, last spawned actor id, spawned actor count, current marker id, collision shape visibility flag, gameplay bounds flag, invulnerability flag, infinite special ammo flag, no enemy fire flag, reload request count, restart request count, and frame step request count.
|
||||||
|
|
||||||
|
Next, add `DebugCommandService` as a pure C# service in `godot/scripts/debug/commands/`. It should accept a `ContentRegistry` in its constructor so it can validate difficulty ids, actor ids, and mission marker ids against `SampleContent.CreateRegistry()`. It should expose `DebugRuntimeState State`, `DebugCommandResult Execute(DebugCommandId commandId, string? argument = null)`, and events such as `StateChanged` and `CommandExecuted`. It should allow handler registration for sandbox-specific actions: a spawn handler for actor ids, a timeline jump handler for marker ids, a reload handler, and a restart handler. If no handler is registered, the command should still update the request count and return a clear message rather than crashing. Unknown difficulty ids, unknown actor ids, and unknown marker ids should fail with clear messages.
|
||||||
|
|
||||||
|
Then add unit tests in `tests/SideScrollerGame.Content.Tests/DebugCommandServiceTests.cs`. These tests should cover at least: pause/resume/toggle pause updates state, time scale accepts the supported values `0.25`, `0.5`, `1`, `2`, and `4`, invalid time scale fails, difficulty can switch to `difficulty.hard`, missing difficulty fails, seed can be set, toggle flags work, spawn actor validates `enemy.serial`, missing actor fails, marker jump validates `cluster.opening`, missing marker fails, restart request increments the restart counter, and command events fire. These tests should not launch Godot.
|
||||||
|
|
||||||
|
Next, add a Godot wrapper node `godot/scripts/debug/DebugCommandNode.cs`. It should extend `Node`, own one `DebugCommandService`, and apply engine-level effects. When the service state says paused, set `GetTree().Paused`. When time scale changes, set `Engine.TimeScale`. When frame step is requested, briefly unpause for one process frame and then pause again. When reload is requested, call `GetTree().ReloadCurrentScene()` unless the current scene is the root scene and the command came from the headless smoke script; in the smoke script, record the reload request without disrupting the script. Keep the wrapper small and let the pure service own validation and state.
|
||||||
|
|
||||||
|
Update `godot/scripts/bootstrap/GameRoot.cs` and `godot/scenes/bootstrap/GameRoot.tscn` so the root scene owns one `DebugCommandNode` child and passes the debug settings seed into it. Expose the command node to loaded scenes by node path, for example `/root/GameRoot/DebugCommandNode`. Do not make it a global autoload in this slice; the root already exists and can own shared runtime services.
|
||||||
|
|
||||||
|
Upgrade `godot/scripts/debug/DebugOverlay.cs`. It should be toggleable and should display active boot mode, loaded scene id, seed, active difficulty, paused state, time scale, current marker, spawned actor count, and flags for invulnerability, infinite special ammo, no enemy fire, collision shapes, and gameplay bounds. Add a method such as `Bind(DebugCommandService service, DebugSettings settings, string loadedSceneId)`. The overlay should subscribe to `StateChanged` and refresh itself. Keep `SetStatus` only if it remains useful as a compatibility wrapper.
|
||||||
|
|
||||||
|
Add a clickable debug panel for the sandbox. Create `godot/scripts/debug/DebugPanelController.cs` and use a simple `Control` node in the sandbox scene. The panel should have buttons or option controls for pause/resume, frame step, time scale choices, difficulty choices, spawn `enemy.serial`, spawn `enemy.parallel`, jump `intro`, jump `cluster.opening`, toggle invulnerability, toggle infinite special ammo, toggle no enemy fire, toggle collision shapes, toggle gameplay bounds, restart sandbox, and reload scene. If building all buttons as `.tscn` children is verbose, the controller may create buttons in `_Ready()` in code for this slice.
|
||||||
|
|
||||||
|
Add `godot/scenes/debug/DebugSandbox.tscn` and `godot/scripts/debug/DebugSandboxController.cs`. The sandbox should be a `Node2D` or `Control` scene with a title, a simple playfield area, the debug panel, and a log label. It should find the root `DebugCommandNode`, register spawn, marker jump, reload, and restart handlers, and update the log whenever a command executes. The spawn handler should create a visible placeholder node with the requested actor id as text and increment a visible count. The marker jump handler should update a visible marker label. The restart handler should clear spawned actors and reset the marker label. The reload handler may just log in the sandbox because the root command wrapper owns actual scene reload behavior.
|
||||||
|
|
||||||
|
Add headless smoke support to the sandbox. If Godot is headless and the user command-line contains `--debug-script=foundation-smoke`, the sandbox controller should run a deterministic command sequence after one process frame: pause, set time scale to `0.5`, set difficulty to `difficulty.hard`, set seed to the command-line seed, spawn `enemy.serial`, jump to `cluster.opening`, toggle invulnerability, toggle infinite special ammo, toggle no enemy fire, toggle collision shapes, toggle gameplay bounds, restart sandbox, and finally spawn `enemy.parallel`. It should verify the resulting state and printed log, print `Debug foundation smoke succeeded`, and quit with exit code 0. If any command fails unexpectedly, print `Debug foundation smoke failed` and quit with exit code 1.
|
||||||
|
|
||||||
|
Update debug boot wiring. Add `DebugSandbox` to `DebugBootMode`. Add an exported `PackedScene? DebugSandboxScene` to `GameRoot`. Add the `res://scenes/debug/DebugSandbox.tscn` reference to `godot/scenes/bootstrap/GameRoot.tscn`. Update `GameRoot.LoadBootScene` so `--debug-boot=debug-sandbox` loads the sandbox.
|
||||||
|
|
||||||
|
Update input actions in `godot/project.godot`. Keep existing actions and add these actions: `debug_pause`, `debug_frame_step`, `debug_time_slower`, `debug_time_faster`, `debug_spawn_actor`, `debug_jump_marker`, `debug_toggle_invulnerability`, `debug_toggle_infinite_special_ammo`, `debug_toggle_no_enemy_fire`, `debug_toggle_collision_shapes`, and `debug_toggle_gameplay_bounds`. The sandbox controller should handle these actions through `_UnhandledInput` and call `DebugCommandNode.Execute(...)` so keyboard and clickable panel use the same service.
|
||||||
|
|
||||||
|
Finally, update this ExecPlan as work proceeds. Do not edit `DESIGN.md` unless the user explicitly asks for design changes. If `SideScrollerGame.sln`, `godot/SideScrollerGame.Godot.csproj`, or test project files have unrelated user edits, preserve them and stage only Slice 3 changes.
|
||||||
|
|
||||||
|
## Concrete Steps
|
||||||
|
|
||||||
|
Run all commands from `D:\Code\zfxaction26_1`.
|
||||||
|
|
||||||
|
1. Confirm the current state before editing:
|
||||||
|
|
||||||
|
git status --short
|
||||||
|
dotnet test SideScrollerGame.sln
|
||||||
|
dotnet build SideScrollerGame.sln
|
||||||
|
.\godot --headless --path godot -- --debug-boot=content-browser --content-validate-only
|
||||||
|
|
||||||
|
Expected result: the worktree should be clean unless the user has made new edits. Tests should report 8 or more passing tests. The content browser should print loaded definitions and `Content validation succeeded`.
|
||||||
|
|
||||||
|
2. Create the debug command folder:
|
||||||
|
|
||||||
|
godot/scripts/debug/commands
|
||||||
|
|
||||||
|
Creating a directory that already exists is safe.
|
||||||
|
|
||||||
|
3. Add pure debug command files under `godot/scripts/debug/commands/`:
|
||||||
|
|
||||||
|
DebugCommandId.cs
|
||||||
|
DebugCommandResult.cs
|
||||||
|
DebugRuntimeState.cs
|
||||||
|
DebugCommandService.cs
|
||||||
|
|
||||||
|
Keep these files free of Godot node dependencies. They may use `System`, `System.Collections.Generic`, and the content registry types.
|
||||||
|
|
||||||
|
4. Add tests in:
|
||||||
|
|
||||||
|
tests/SideScrollerGame.Content.Tests/DebugCommandServiceTests.cs
|
||||||
|
|
||||||
|
Use `SampleContent.CreateRegistry()` as the registry input. The tests must prove the service works without launching Godot.
|
||||||
|
|
||||||
|
5. Add Godot debug runtime scripts:
|
||||||
|
|
||||||
|
godot/scripts/debug/DebugCommandNode.cs
|
||||||
|
godot/scripts/debug/DebugPanelController.cs
|
||||||
|
godot/scripts/debug/DebugSandboxController.cs
|
||||||
|
|
||||||
|
The command node bridges service state to Godot pause and time scale. The panel and sandbox call into the command node.
|
||||||
|
|
||||||
|
6. Add the sandbox scene:
|
||||||
|
|
||||||
|
godot/scenes/debug/DebugSandbox.tscn
|
||||||
|
|
||||||
|
The scene should contain the sandbox controller, a label for current marker, a label or container for spawned actors, a log label, and a simple panel node for debug buttons.
|
||||||
|
|
||||||
|
7. Update existing debug boot and root files:
|
||||||
|
|
||||||
|
godot/scripts/debug/DebugBootMode.cs
|
||||||
|
godot/scripts/bootstrap/GameRoot.cs
|
||||||
|
godot/scripts/debug/DebugOverlay.cs
|
||||||
|
godot/scenes/bootstrap/GameRoot.tscn
|
||||||
|
|
||||||
|
Wire the `DebugSandbox` boot mode, add the command node child, bind the overlay to the command service, and preserve `Menu`, `Smoke`, and `ContentBrowser` boot behavior.
|
||||||
|
|
||||||
|
8. Update `godot/project.godot` input actions. Add only missing debug actions. Preserve existing actions and settings.
|
||||||
|
|
||||||
|
9. Format touched C# files. Include every C# file touched by this slice. Use separate path arguments:
|
||||||
|
|
||||||
|
jb cleanupcode --build=False godot\scripts\debug\commands\DebugCommandId.cs godot\scripts\debug\commands\DebugCommandResult.cs godot\scripts\debug\commands\DebugRuntimeState.cs godot\scripts\debug\commands\DebugCommandService.cs godot\scripts\debug\DebugCommandNode.cs godot\scripts\debug\DebugPanelController.cs godot\scripts\debug\DebugSandboxController.cs godot\scripts\debug\DebugOverlay.cs godot\scripts\debug\DebugBootMode.cs godot\scripts\bootstrap\GameRoot.cs tests\SideScrollerGame.Content.Tests\DebugCommandServiceTests.cs
|
||||||
|
|
||||||
|
Adjust the file list to include the real touched files. If `jb cleanupcode` is unavailable, record the exact error in `Surprises & Discoveries`, keep formatting manually consistent, and continue validation.
|
||||||
|
|
||||||
|
10. Validate the slice:
|
||||||
|
|
||||||
|
dotnet test SideScrollerGame.sln
|
||||||
|
dotnet build SideScrollerGame.sln
|
||||||
|
.\godot --headless --path godot --build-solutions --quit
|
||||||
|
.\godot --headless --path godot -- --debug-boot=debug-sandbox --debug-script=foundation-smoke --seed=333
|
||||||
|
.\godot --headless --path godot -- --debug-boot=content-browser --content-validate-only
|
||||||
|
.\godot --headless --path godot -- --debug-boot=smoke --seed=12345
|
||||||
|
|
||||||
|
Expected result: tests pass, the solution builds with 0 errors, the Godot solution build exits without a stuck process, the debug sandbox smoke prints `Debug foundation smoke succeeded`, the content browser still validates sample content, and the existing smoke scene still prints `Smoke scene loaded`.
|
||||||
|
|
||||||
|
11. Check the diff:
|
||||||
|
|
||||||
|
git status --short
|
||||||
|
git diff -- SLICE3.MD godot/project.godot godot/scripts/debug godot/scripts/bootstrap godot/scenes/debug godot/scenes/bootstrap tests
|
||||||
|
|
||||||
|
Expected result: the diff includes only Slice 3 files and minimal edits to shared debug/root files. User changes outside the slice must not be reverted.
|
||||||
|
|
||||||
|
12. Commit this slice after validation:
|
||||||
|
|
||||||
|
git add SLICE3.MD godot/project.godot godot/scripts/debug godot/scripts/bootstrap godot/scenes/debug godot/scenes/bootstrap tests
|
||||||
|
git commit -m "Add debug foundation"
|
||||||
|
|
||||||
|
## Validation and Acceptance
|
||||||
|
|
||||||
|
This slice is accepted when the following observable behaviors are true.
|
||||||
|
|
||||||
|
Running tests succeeds:
|
||||||
|
|
||||||
|
dotnet test SideScrollerGame.sln
|
||||||
|
|
||||||
|
Expected final output should include a passed test summary and no failed tests. The exact number of tests may change, but it should include the existing content validation tests plus new debug command service tests.
|
||||||
|
|
||||||
|
Building from the repository root succeeds:
|
||||||
|
|
||||||
|
dotnet build SideScrollerGame.sln
|
||||||
|
|
||||||
|
Expected final lines include:
|
||||||
|
|
||||||
|
Build succeeded.
|
||||||
|
0 Error(s)
|
||||||
|
|
||||||
|
Godot can build the C# solution and exit:
|
||||||
|
|
||||||
|
.\godot --headless --path godot --build-solutions --quit
|
||||||
|
|
||||||
|
The command should exit without a stuck Godot process. If it prints warnings that do not fail the process, record them in this plan.
|
||||||
|
|
||||||
|
The debug sandbox can be booted directly and can exercise the command service headlessly:
|
||||||
|
|
||||||
|
.\godot --headless --path godot -- --debug-boot=debug-sandbox --debug-script=foundation-smoke --seed=333
|
||||||
|
|
||||||
|
Expected output includes:
|
||||||
|
|
||||||
|
Debug boot: DebugSandbox
|
||||||
|
Seed: 333
|
||||||
|
Debug foundation smoke loaded
|
||||||
|
Command executed: Pause
|
||||||
|
Command executed: SetTimeScale 0.5
|
||||||
|
Command executed: SetDifficulty difficulty.hard
|
||||||
|
Actor spawned: enemy.serial
|
||||||
|
Timeline marker: cluster.opening
|
||||||
|
Command executed: RestartMission
|
||||||
|
Actor spawned: enemy.parallel
|
||||||
|
Debug foundation smoke succeeded
|
||||||
|
|
||||||
|
Existing direct boots must still work:
|
||||||
|
|
||||||
|
.\godot --headless --path godot -- --debug-boot=content-browser --content-validate-only
|
||||||
|
|
||||||
|
Expected output includes:
|
||||||
|
|
||||||
|
Content validation succeeded
|
||||||
|
|
||||||
|
And:
|
||||||
|
|
||||||
|
.\godot --headless --path godot -- --debug-boot=smoke --seed=12345
|
||||||
|
|
||||||
|
Expected output includes:
|
||||||
|
|
||||||
|
Debug boot: Smoke
|
||||||
|
Seed: 12345
|
||||||
|
Smoke scene loaded
|
||||||
|
|
||||||
|
Opening the editor should show the project and root scene:
|
||||||
|
|
||||||
|
.\godot --editor --path godot
|
||||||
|
|
||||||
|
In the editor, running with `-- --debug-boot=debug-sandbox` should show the sandbox. The overlay should be visible by default, the buttons should change the overlay state, keyboard shortcuts should call the same commands, spawned fake actors should appear in the sandbox, restart should clear them, and the reload command should reload the scene.
|
||||||
|
|
||||||
|
## Idempotence and Recovery
|
||||||
|
|
||||||
|
The implementation is mostly additive and safe to repeat. Creating directories that already exist should do nothing. Re-running tests, builds, and smoke commands should produce the same result.
|
||||||
|
|
||||||
|
If `godot/project.godot` already contains one of the planned debug input actions, keep the existing action and add only missing actions. Do not duplicate action blocks.
|
||||||
|
|
||||||
|
If `.\godot --headless --path godot --build-solutions --quit` generates new `.cs.uid` files for scripts, keep them if Godot generated them during the project scan. Do not hand-edit generated UID values.
|
||||||
|
|
||||||
|
If the debug sandbox smoke command hangs, inspect running processes and stop only the Godot process whose command line contains `--debug-boot=debug-sandbox --debug-script=foundation-smoke` and this repository path. Record the command and process detail in `Surprises & Discoveries`, then fix the smallest affected script.
|
||||||
|
|
||||||
|
If frame-step behavior is unreliable in headless mode, keep unit tests focused on the frame step request counter and validate the visual one-frame advance manually in the editor. Record the limitation in `Surprises & Discoveries` and `Outcomes & Retrospective`.
|
||||||
|
|
||||||
|
If user changes appear in files outside this slice, leave them untouched. If user changes conflict with `godot/project.godot`, `GameRoot.tscn`, or shared debug scripts, read the file and merge only the smallest required change.
|
||||||
|
|
||||||
|
Do not use `git restore`, `git checkout --`, reset commands, or equivalent rollback commands to discard local changes. If generated files are unwanted, leave them unstaged unless the user explicitly asks for cleanup.
|
||||||
|
|
||||||
|
## Artifacts and Notes
|
||||||
|
|
||||||
|
Current successful Slice 2 commands before Slice 3 implementation:
|
||||||
|
|
||||||
|
dotnet test SideScrollerGame.sln
|
||||||
|
Passed! - Failed: 0, Passed: 8, Skipped: 0, Total: 8
|
||||||
|
|
||||||
|
.\godot --headless --path godot -- --debug-boot=content-browser --content-validate-only
|
||||||
|
Debug boot: ContentBrowser
|
||||||
|
Seed: 1
|
||||||
|
Loaded content definitions:
|
||||||
|
behavior.enemy.parallel
|
||||||
|
behavior.enemy.serial
|
||||||
|
camera.test.path
|
||||||
|
cluster.opening
|
||||||
|
collectible.points.small
|
||||||
|
collectible.squadron.orbit
|
||||||
|
difficulty.easy
|
||||||
|
difficulty.hard
|
||||||
|
difficulty.normal
|
||||||
|
enemy.parallel
|
||||||
|
enemy.serial
|
||||||
|
layer.background.stars
|
||||||
|
layer.foreground.clouds
|
||||||
|
mission.test
|
||||||
|
squadron.orbit
|
||||||
|
weapon.primary.basic
|
||||||
|
weapon.secondary.vertical
|
||||||
|
weapon.special.bomb
|
||||||
|
Content validation succeeded
|
||||||
|
|
||||||
|
The debug sandbox smoke output after this slice should resemble:
|
||||||
|
|
||||||
|
Debug boot: DebugSandbox
|
||||||
|
Seed: 333
|
||||||
|
Debug foundation smoke loaded
|
||||||
|
Command executed: Pause
|
||||||
|
Command executed: SetTimeScale 0.5
|
||||||
|
Command executed: SetDifficulty difficulty.hard
|
||||||
|
Command executed: SpawnActor enemy.serial
|
||||||
|
Actor spawned: enemy.serial
|
||||||
|
Command executed: JumpToMarker cluster.opening
|
||||||
|
Timeline marker: cluster.opening
|
||||||
|
Command executed: ToggleInvulnerability
|
||||||
|
Command executed: ToggleInfiniteSpecialAmmo
|
||||||
|
Command executed: ToggleNoEnemyFire
|
||||||
|
Command executed: ToggleCollisionShapes
|
||||||
|
Command executed: ToggleGameplayBounds
|
||||||
|
Command executed: RestartMission
|
||||||
|
Command executed: SpawnActor enemy.parallel
|
||||||
|
Actor spawned: enemy.parallel
|
||||||
|
Debug foundation smoke succeeded
|
||||||
|
|
||||||
|
## Interfaces and Dependencies
|
||||||
|
|
||||||
|
The project uses Godot 4.5.1 .NET through `Godot.NET.Sdk/4.5.1` and targets `net8.0` for desktop builds. New Godot-facing scripts must use the `Godot` namespace and partial Godot classes where required by the engine. Pure debug command types should avoid Godot node dependencies so xUnit tests can run without launching the engine.
|
||||||
|
|
||||||
|
At the end of this slice, these public C# types should exist. The exact supporting members may be adjusted during implementation, but the names, responsibilities, and observable behaviors should remain stable.
|
||||||
|
|
||||||
|
In `godot/scripts/debug/commands/DebugCommandId.cs`:
|
||||||
|
|
||||||
|
namespace SideScrollerGame.Debug.Commands;
|
||||||
|
|
||||||
|
public enum DebugCommandId
|
||||||
|
{
|
||||||
|
ToggleOverlay,
|
||||||
|
Pause,
|
||||||
|
Resume,
|
||||||
|
TogglePause,
|
||||||
|
FrameStep,
|
||||||
|
SetTimeScale,
|
||||||
|
ReloadScene,
|
||||||
|
RestartMission,
|
||||||
|
SetDifficulty,
|
||||||
|
SetSeed,
|
||||||
|
SpawnActor,
|
||||||
|
JumpToMarker,
|
||||||
|
ToggleCollisionShapes,
|
||||||
|
ToggleGameplayBounds,
|
||||||
|
ToggleInvulnerability,
|
||||||
|
ToggleInfiniteSpecialAmmo,
|
||||||
|
ToggleNoEnemyFire
|
||||||
|
}
|
||||||
|
|
||||||
|
In `godot/scripts/debug/commands/DebugRuntimeState.cs`:
|
||||||
|
|
||||||
|
namespace SideScrollerGame.Debug.Commands;
|
||||||
|
|
||||||
|
public sealed class DebugRuntimeState
|
||||||
|
{
|
||||||
|
public bool OverlayVisible { get; }
|
||||||
|
public bool IsPaused { get; }
|
||||||
|
public double TimeScale { get; }
|
||||||
|
public string ActiveDifficultyId { get; }
|
||||||
|
public int Seed { get; }
|
||||||
|
public string CurrentMarkerId { get; }
|
||||||
|
public string LastSpawnedActorId { get; }
|
||||||
|
public int SpawnedActorCount { get; }
|
||||||
|
public bool ShowCollisionShapes { get; }
|
||||||
|
public bool ShowGameplayBounds { get; }
|
||||||
|
public bool Invulnerable { get; }
|
||||||
|
public bool InfiniteSpecialAmmo { get; }
|
||||||
|
public bool NoEnemyFire { get; }
|
||||||
|
public int ReloadSceneRequestCount { get; }
|
||||||
|
public int RestartMissionRequestCount { get; }
|
||||||
|
public int FrameStepRequestCount { get; }
|
||||||
|
}
|
||||||
|
|
||||||
|
The implementation may use mutable properties if that keeps the code simple, but callers should treat the state as read-only outside the service.
|
||||||
|
|
||||||
|
In `godot/scripts/debug/commands/DebugCommandService.cs`:
|
||||||
|
|
||||||
|
namespace SideScrollerGame.Debug.Commands;
|
||||||
|
|
||||||
|
public sealed class DebugCommandService
|
||||||
|
{
|
||||||
|
public DebugCommandService(ContentRegistry registry, int seed);
|
||||||
|
public DebugRuntimeState State { get; }
|
||||||
|
public DebugCommandResult Execute(DebugCommandId commandId, string? argument = null);
|
||||||
|
public event Action<DebugRuntimeState>? StateChanged;
|
||||||
|
public event Action<DebugCommandResult>? CommandExecuted;
|
||||||
|
}
|
||||||
|
|
||||||
|
The service should validate difficulty ids against `registry.Difficulties`, actor ids against `registry.EnemyTypes`, and marker ids against mission `mission.test` timeline markers. If a later slice changes the active mission, it can extend the service to select a different mission id.
|
||||||
|
|
||||||
|
In `godot/scripts/debug/DebugCommandNode.cs`:
|
||||||
|
|
||||||
|
namespace SideScrollerGame.Debug;
|
||||||
|
|
||||||
|
public partial class DebugCommandNode : Node
|
||||||
|
{
|
||||||
|
public DebugCommandService Service { get; }
|
||||||
|
public void Initialize(DebugSettings settings);
|
||||||
|
public DebugCommandResult Execute(DebugCommandId commandId, string? argument = null);
|
||||||
|
}
|
||||||
|
|
||||||
|
In `godot/scripts/debug/DebugOverlay.cs`, add:
|
||||||
|
|
||||||
|
public void Bind(DebugCommandService service, DebugSettings settings, string loadedSceneId);
|
||||||
|
|
||||||
|
The overlay should refresh when the service state changes and should hide or show itself based on the overlay visibility flag.
|
||||||
|
|
||||||
|
In `godot/scripts/debug/DebugSandboxController.cs`:
|
||||||
|
|
||||||
|
namespace SideScrollerGame.Debug;
|
||||||
|
|
||||||
|
public partial class DebugSandboxController : Node
|
||||||
|
{
|
||||||
|
public override void _Ready();
|
||||||
|
public override void _UnhandledInput(InputEvent @event);
|
||||||
|
}
|
||||||
|
|
||||||
|
The sandbox should use `DebugCommandNode.Execute(...)` for both keyboard and button commands. It should not maintain separate command rules.
|
||||||
|
|
||||||
|
In `godot/scripts/debug/DebugBootMode.cs`, extend the enum to include:
|
||||||
|
|
||||||
|
DebugSandbox
|
||||||
|
|
||||||
|
In `godot/scripts/bootstrap/GameRoot.cs`, extend boot loading so:
|
||||||
|
|
||||||
|
--debug-boot=debug-sandbox
|
||||||
|
|
||||||
|
loads `res://scenes/debug/DebugSandbox.tscn`.
|
||||||
|
|
||||||
|
Revision note 2026-04-21: Created this ExecPlan from the current Slice 2 project state. The plan scopes Slice 3 to shared debug command infrastructure, overlay upgrades, keyboard and clickable sandbox controls, and headless smoke validation while leaving real gameplay systems to later slices.
|
||||||
Reference in New Issue
Block a user