Debug menu, reorganized game scene
This commit is contained in:
145
docs/superpowers/specs/2026-04-05-f1-debug-menu-design.md
Normal file
145
docs/superpowers/specs/2026-04-05-f1-debug-menu-design.md
Normal file
@@ -0,0 +1,145 @@
|
||||
# F1 Debug Menu Design
|
||||
|
||||
## Overview
|
||||
|
||||
An F1-toggled debug menu overlay that works regardless of the current scene. Introduces a new root scene (`game.tscn`) that hosts both the debug menu and the active game scene as children. The game pauses underneath while the debug menu is open.
|
||||
|
||||
## Scene Tree Structure
|
||||
|
||||
```
|
||||
Game (Node, process_mode = PROCESS_MODE_ALWAYS)
|
||||
├── DebugMenu (CanvasLayer, layer 100)
|
||||
│ ├── Panel (PanelContainer)
|
||||
│ │ ├── SceneList (VBoxContainer of Buttons)
|
||||
│ │ └── CommandInput (LineEdit)
|
||||
│ └── ResultLabel (Label, brief flash for command output)
|
||||
└── ActiveSceneContainer (Node)
|
||||
└── <currently loaded scene>
|
||||
```
|
||||
|
||||
- `Game` is the new main scene, replacing `strategy_phase.tscn` as the entry point in `project.godot`.
|
||||
- `DebugMenu` uses a high `CanvasLayer` (layer 100) so it always renders above the active scene.
|
||||
- `ActiveSceneContainer` is a plain `Node` whose child gets swapped when changing scenes.
|
||||
- `Game` has `process_mode = PROCESS_MODE_ALWAYS` so it can receive F1 input even while the tree is paused.
|
||||
|
||||
## Responsibilities
|
||||
|
||||
### Game (`game.gd`)
|
||||
|
||||
Thin shell only:
|
||||
|
||||
- F1 input toggles `DebugMenu` visibility.
|
||||
- When debug menu opens: sets `ActiveSceneContainer.process_mode = PROCESS_MODE_DISABLED`.
|
||||
- When debug menu closes: sets `ActiveSceneContainer.process_mode = PROCESS_MODE_INHERIT`.
|
||||
- Loads the default scene (strategy_phase) into `ActiveSceneContainer` at startup.
|
||||
- Passes a reference to `ActiveSceneContainer` to `DebugMenu` at `_ready()`.
|
||||
|
||||
### DebugMenu (`debug_menu.gd`)
|
||||
|
||||
Owns all debug functionality:
|
||||
|
||||
- **Scene registry**: hardcoded array of scene entries.
|
||||
- **Scene swapping**: frees old child of `ActiveSceneContainer`, instantiates new scene, applies setup if present.
|
||||
- **Command console**: parses input, dispatches to registered commands or falls back to expression eval.
|
||||
- **Command registration**: holds an explicit `Array[ConsoleCommand]` populated at `_ready()`.
|
||||
|
||||
## Scene Swapping
|
||||
|
||||
### Registry Format
|
||||
|
||||
A hardcoded array in `DebugMenu`:
|
||||
|
||||
```gdscript
|
||||
var scene_registry: Array = [
|
||||
{ "name": "Strategy Phase", "path": "res://scenes/strategy_phase.tscn" },
|
||||
{ "name": "Strategy Phase (Test Data)", "path": "res://scenes/strategy_phase.tscn", "setup": "test_data" },
|
||||
{ "name": "Main Menu", "path": "res://scenes/main_menu.tscn" },
|
||||
{ "name": "Visual Novel", "path": "res://scenes/vn_scene.tscn" },
|
||||
{ "name": "Dialogue", "path": "res://scenes/dialogue_scene.tscn" },
|
||||
]
|
||||
```
|
||||
|
||||
### Swap Flow
|
||||
|
||||
1. Free the current child of `ActiveSceneContainer`.
|
||||
2. Instantiate the new scene from `path` and add as child.
|
||||
3. If the entry has a `"setup"` key, call a setup method on `DebugMenu` that applies that configuration to the new scene instance.
|
||||
4. Close the debug menu and signal `Game` to unpause.
|
||||
|
||||
### Setup Hooks
|
||||
|
||||
Setup functions live in `DebugMenu`, dispatched by matching the `"setup"` string key. This keeps test configurations self-contained within the debug system.
|
||||
|
||||
## Command Console
|
||||
|
||||
### Input
|
||||
|
||||
A `LineEdit` at the bottom of the debug panel. On Enter:
|
||||
|
||||
1. Split input text — first token is the command name, rest are arguments.
|
||||
2. Check against registered commands by matching `get_command_name()`.
|
||||
3. If found, call `run()` with args and context.
|
||||
4. If no match, fall back to Godot's `Expression` class (evaluated with the active scene as base instance).
|
||||
5. Show the result (or error) in `ResultLabel`, then hide it after 2 seconds using a `Timer` or `create_tween()`.
|
||||
|
||||
### Command Pattern
|
||||
|
||||
Base class:
|
||||
|
||||
```gdscript
|
||||
class_name ConsoleCommand
|
||||
|
||||
func get_command_name() -> String:
|
||||
return ""
|
||||
|
||||
func get_help_text() -> String:
|
||||
return ""
|
||||
|
||||
func run(args: Array, context: Dictionary) -> String:
|
||||
return ""
|
||||
```
|
||||
|
||||
Each command extends `ConsoleCommand` and overrides these three methods. The `context` dictionary provides access to the active scene container, debug menu reference, and other shared state without coupling commands to specific nodes.
|
||||
|
||||
Commands are explicitly registered in an `Array[ConsoleCommand]` in `DebugMenu._ready()`.
|
||||
|
||||
### Starter Commands
|
||||
|
||||
- **`help`** — lists all registered commands with their help text.
|
||||
- **`swap <name>`** — scene swap by name (matches against registry entry names).
|
||||
- **`list_scenes`** — prints available scene registry entries.
|
||||
|
||||
### Expression Eval Fallback
|
||||
|
||||
Uses Godot's `Expression` class. The expression executes with the current active scene as the base instance, allowing direct access to nodes and properties in the loaded scene. Errors are displayed in `ResultLabel`.
|
||||
|
||||
## File Organization
|
||||
|
||||
```
|
||||
nodes/
|
||||
├── game.gd # Thin shell
|
||||
├── debug_menu.gd # Scene registry, command console, swap logic
|
||||
|
||||
resources/resource_definitions/
|
||||
├── console_command.gd # Base class
|
||||
|
||||
resources/console_commands/
|
||||
├── swap_command.gd
|
||||
├── help_command.gd
|
||||
├── list_scenes_command.gd
|
||||
|
||||
scenes/
|
||||
├── game.tscn # New root scene (new main_scene in project.godot)
|
||||
|
||||
prefabs/
|
||||
├── debug_menu.tscn # CanvasLayer with panel, buttons, input, result label
|
||||
```
|
||||
|
||||
Existing scenes are unchanged — they are loaded as children of `ActiveSceneContainer` instead of being the root.
|
||||
|
||||
## Key Decisions
|
||||
|
||||
- **No autoload**: The debug menu is a UI node, not a headless service. The `XXXServer` autoload pattern is reserved for game services like a future `SceneManagerServer`.
|
||||
- **Explicit command registration**: Commands are listed in an array rather than auto-discovered from a folder. Simpler and more readable for a small number of commands. Can be swapped to auto-discovery later if needed.
|
||||
- **Single-line output**: `ResultLabel` shows one result at a time with a brief display. No scrollable history for now.
|
||||
- **Hardcoded scene list**: Entries can carry setup data/context, which auto-discovery from `.tscn` files wouldn't support.
|
||||
Reference in New Issue
Block a user