Files
zfxaction26_1/SLICE2.MD

445 lines
30 KiB
Markdown

# Add Core Data Definitions
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 2 without reading prior chat history.
## Purpose / Big Picture
After this slice, the project will have a small content definition layer for the side-scrolling shooter. A developer will be able to describe a mission, difficulty, camera path, parallax layers, enemies, behavior tracks, clusters, collectibles, weapons, special weapons, and squadron mate types without hardcoding those choices into gameplay scenes.
The observable result is concrete: from `D:\Code\zfxaction26_1`, `dotnet test SideScrollerGame.sln` validates the sample content and intentionally broken content, `dotnet build SideScrollerGame.sln` succeeds, and `.\godot --headless --path godot -- --debug-boot=content-browser --content-validate-only` loads the project, validates the sample registry, prints a short list of loaded definition ids, prints `Content validation succeeded`, and exits with code 0.
This slice does not implement gameplay movement, enemy spawning, weapons, or the mission runner. It creates the contracts and validation needed so those later systems can load data confidently and fail fast when sample content is incomplete or contradictory.
## Progress
- [x] (2026-04-21 17:04Z) Read repository rules, Windows rules, `PLANS.md`, `DESIGN.md`, `CODE.md`, `SLICE1.MD`, current Godot scripts, and `godot/project.godot`.
- [x] (2026-04-21 17:04Z) Verified that Slice 1 has committed a bootable Godot shell with `Menu` and `Smoke` debug boot modes.
- [x] (2026-04-21 17:04Z) Created this Slice 2 ExecPlan.
- [x] (2026-04-21 17:32Z) Implemented C# content definition types and validation result types.
- [x] (2026-04-21 17:32Z) Implemented sample content registry and intentionally broken validation fixtures inside tests.
- [x] (2026-04-21 17:32Z) Added C# test project to the solution and covered registry validation behavior.
- [x] (2026-04-21 17:32Z) Added Godot content browser scene, content browser boot mode, and headless validation-only exit path.
- [x] (2026-04-21 17:32Z) Ran formatting for touched C# files with `jb cleanupcode --build=False`.
- [x] (2026-04-21 17:32Z) Validated with .NET tests, .NET build, Godot solution build, and headless content browser boot.
- [x] (2026-04-21 17:32Z) 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, the wrapper command is run from the repository root as `.\godot --path godot ...`, and Slice 1 validation uses that layout.
- Observation: The current boot shell has only `Menu` and `Smoke` debug boot modes.
Evidence: `godot/scripts/debug/DebugBootMode.cs` contains only `Menu` and `Smoke`, and `godot/scripts/bootstrap/GameRoot.cs` switches every non-smoke mode to the menu placeholder.
- Observation: There are no existing gameplay content definitions or tests in the repository.
Evidence: `rg --files` shows only bootstrap, menu, debug smoke scripts and scenes under `godot/`; there is no `tests/` directory.
- Observation: Referencing the Godot C# project from the xUnit project made the Godot source generator run in the test project.
Evidence: the first `dotnet test SideScrollerGame.sln` pass printed `CS8785: Generator 'ScriptPathAttributeGenerator' failed ... Property 'GodotProjectDir' is null or empty`. Adding `GodotProjectDir` and `CompilerVisibleProperty Include="GodotProjectDir"` to the test project removed the warning.
- Observation: `.\godot --headless --path godot --build-solutions --quit` generated `.cs.uid` files for every new C# script, including plain content definitions.
Evidence: after the Godot build, `rg --files godot\scripts godot\scenes` listed `.uid` files beside all new content and validation C# files. These are kept because Godot generated them for the project scan.
## Decision Log
- Decision: Implement Slice 2 definitions as plain C# records and small enums inside the existing Godot C# project first, not as Godot `Resource` assets.
Rationale: Plain C# records are fast to write, easy to unit test without launching Godot, easy to refactor during the jam, and can later be backed by Godot resources or JSON if editor authoring becomes more important than code-side iteration.
Date/Author: 2026-04-21 / Codex.
- Decision: Add a separate .NET test project for content validation.
Rationale: Definition validation is pure rule logic. Running it through `dotnet test` is faster and more reliable than launching the Godot editor for every validation case.
Date/Author: 2026-04-21 / Codex.
- Decision: Add a Godot content browser debug boot mode even though the unit tests cover validation.
Rationale: Later slices need a fast in-engine way to confirm that designers can see loaded content ids and validation errors before entering gameplay. This also proves the definitions can be consumed from Godot scenes, not only from tests.
Date/Author: 2026-04-21 / Codex.
- Decision: Keep sample definitions theme-neutral.
Rationale: The jam topic is unknown. Stable ids such as `mission.test`, `enemy.serial`, and `weapon.primary.basic` prove system wiring without committing to a visual or story theme.
Date/Author: 2026-04-21 / Codex.
- Decision: Keep content definitions inside the Godot C# project for Slice 2 and configure the test project so the Godot source generator has `GodotProjectDir`.
Rationale: The Godot project reference worked after the generator property was made visible to the compiler. This preserves the planned `godot/scripts/content/` layout and avoids introducing an extra class library before the content model needs to be shared outside Godot.
Date/Author: 2026-04-21 / Codex.
## Outcomes & Retrospective
Implemented the core content definition layer under `godot/scripts/content/`, including definitions, registry lookup, validation messages, validation rules, and theme-neutral sample content. Added `tests/SideScrollerGame.Content.Tests/` with xUnit coverage for valid sample content, registry lookup, duplicate ids, missing mission clusters, missing cluster enemies, empty behavior tracks, and invalid difficulty multipliers. Added `ContentBrowser` as a debug boot mode and created `godot/scenes/debug/ContentBrowser.tscn` for in-engine content inspection and headless validation.
Validation completed:
dotnet test SideScrollerGame.sln
Passed! - Failed: 0, Passed: 8, Skipped: 0, Total: 8
dotnet build SideScrollerGame.sln
Build succeeded.
0 Warning(s)
0 Error(s)
.\godot --headless --path godot --build-solutions --quit
Exited successfully.
.\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
Remaining risk: the content browser visual layout was validated through headless boot and compilation, not manually inspected in the editor window.
## 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 currently sets `run/main_scene="res://scenes/bootstrap/GameRoot.tscn"` and `project/assembly_name="SideScrollerGame.Godot"`.
- `godot/SideScrollerGame.Godot.csproj`: the Godot C# project. It currently targets `net8.0`, uses `Godot.NET.Sdk/4.5.1`, and builds `SideScrollerGame.Godot.dll`.
- `SideScrollerGame.sln`: the Visual Studio solution. It currently contains the Godot C# project.
- `godot/scripts/bootstrap/GameRoot.cs`: the root scene script. It reads `DebugSettings`, seeds Godot randomness, prints the boot mode and seed, then loads either the menu placeholder or smoke scene.
- `godot/scripts/debug/DebugBootMode.cs`: the enum for debug boot modes. It currently contains `Menu` and `Smoke`.
- `godot/scripts/debug/DebugSettings.cs`: command-line and project-setting reader for `--debug-boot=<value>` and `--seed=<integer>`.
- `godot/scripts/debug/DebugOverlay.cs`: tiny top-left debug label used by the root scene.
- `godot/scenes/bootstrap/GameRoot.tscn`: main scene with `GameRoot.cs`, `MenuScene`, `SmokeScene`, and a `DebugOverlay` child.
- `CODE.md`: the broader implementation plan. Its Slice 2 section asks for definitions, sample data, a content registry, validation methods, and a debug content browser.
- `DESIGN.md`: the systems design source. It describes the data-driven content objects and rules that Slice 2 must represent. Do not rewrite this file in Slice 2 unless the user explicitly asks for design changes.
- `SLICE1.MD`: the previous ExecPlan. It documents the boot shell and validation commands used by this repo.
Definitions used in this plan:
- A definition is immutable content data that describes what something is, such as a weapon id, enemy health, cluster spawn time, or difficulty multiplier. A definition should not own live scene nodes or mutable gameplay state.
- A stable id is a string that other definitions can reference. Stable ids should use lowercase dotted names, such as `mission.test` or `weapon.primary.basic`, so sample content is easy to search and compare.
- A content registry is a central object that exposes all known definitions by stable id. Later gameplay code will ask the registry for definitions instead of constructing content directly.
- Validation means checking content for broken references and invalid numbers before gameplay starts. For example, a mission that references `cluster.missing` should produce a clear validation error naming that missing id.
- A content browser is a debug scene that lists loaded definition ids and validation messages. It is not the final game UI; it is a fast testing tool.
- Headless mode means Godot runs without opening a window. This plan uses headless mode to prove the content browser and validation can run from the command line.
## Plan of Work
First, create a pure content model under `godot/scripts/content/`. Use subfolders so the file locations communicate intent: `godot/scripts/content/definitions/` for definition records and enums, `godot/scripts/content/validation/` for validation results, and `godot/scripts/content/samples/` for hardcoded sample content. Keep one primary public type per file, with class names matching filenames.
Add definition records for the Slice 2 objects named in `CODE.md` and `DESIGN.md`: `MissionDefinition`, `DifficultyDefinition`, `CameraPathDefinition`, `LevelLayerDefinition`, `EnemyTypeDefinition`, `EnemyBehaviorDefinition`, `EnemyClusterDefinition`, `CollectibleDefinition`, `WeaponDefinition`, `SpecialWeaponDefinition`, and `SquadronMateTypeDefinition`. Also add small supporting records and enums where they keep the main definitions readable, such as `DefinitionId`, `SpawnScheduleEntryDefinition`, `CameraPathPointDefinition`, `LayerKind`, `BehaviorTrackMode`, `BehaviorEventKind`, `CollectibleKind`, `WeaponKind`, `SpecialWeaponKind`, `SquadronMateFormationKind`, `ClusterEscapeRule`, and `DifficultyModifierSet`.
Keep the data minimal but useful. Do not model every future parameter in detail. Each definition should contain the stable id, display name or debug name, and the fields needed to validate cross-references and starter balance. For example, `EnemyClusterDefinition` should include an id, a reward score, an escape rule, and spawn entries that reference enemy type ids. `MissionDefinition` should include an id, a default difficulty id, camera path id, background and foreground layer ids, cluster ids, collectible ids, special weapon ids, and timeline marker names. `WeaponDefinition` should include kind, damage, fire cadence seconds, projectile speed, projectile count, and whether it consumes enemy projectiles.
Next, implement validation in `godot/scripts/content/validation/`. Add `ContentValidationSeverity` with `Info`, `Warning`, and `Error`. Add `ContentValidationMessage` with severity, code, message, and optional definition id. Add `ContentValidationResult` with an `IReadOnlyList<ContentValidationMessage>`, a `HasErrors` property, and helper constructors. Add `ContentValidator` with a method `Validate(ContentRegistry registry)` that checks every sample definition collection. Validation should report missing ids, duplicate ids, empty required lists, invalid timings, invalid multipliers, invalid health and damage values, invalid weapon slots, broken mission references, cluster spawn entries that reference missing enemies, behavior tracks with no events, event durations below zero, and difficulties with non-positive multipliers.
Then add `ContentRegistry` under `godot/scripts/content/`. It should own read-only dictionaries keyed by stable id. It should expose typed lookup methods such as `TryGetMission`, `TryGetDifficulty`, and `TryGetEnemyType`, plus an `AllDefinitionIds()` method that returns stable ids grouped or sorted for display. Add `SampleContent.CreateRegistry()` in `godot/scripts/content/samples/` to create one test mission and enough referenced content for validation to pass.
The sample content must prove the real vertical slice shape without implementing gameplay. Include at least these ids:
- Difficulty ids: `difficulty.easy`, `difficulty.normal`, `difficulty.hard`.
- Camera path id: `camera.test.path`.
- Layer ids: `layer.background.stars`, `layer.foreground.clouds`.
- Enemy behavior ids: `behavior.enemy.serial`, `behavior.enemy.parallel`.
- Enemy type ids: `enemy.serial`, `enemy.parallel`.
- Cluster id: `cluster.opening`.
- Collectible ids: `collectible.points.small`, `collectible.squadron.orbit`.
- Primary weapon id: `weapon.primary.basic`.
- Secondary weapon id: `weapon.secondary.vertical`.
- Special weapon id: `weapon.special.bomb`.
- Squadron mate type id: `squadron.orbit`.
- Mission id: `mission.test`.
Add an intentionally broken sample builder, such as `BrokenSampleContent.CreateRegistryWithMissingEnemyReference()`, that is used only by tests. This avoids weakening the main sample content while proving validation catches bad data.
Next, add a test project under `tests/SideScrollerGame.Content.Tests/`. Use xUnit or another standard .NET test framework already available through NuGet. If no test framework is already present, choose xUnit because it works cleanly with `dotnet test` and does not require Godot to launch. Add the test project to `SideScrollerGame.sln`. The test project should reference `godot/SideScrollerGame.Godot.csproj` or, if that proves awkward because of Godot SDK behavior, create a small plain class library `src/SideScrollerGame.Content/` and move the pure content model there. Prefer the smallest working approach, but record any project-structure decision in this plan.
Cover these behaviors with tests:
- `SampleContent.CreateRegistry()` validates with no errors.
- Duplicate ids produce an error naming the duplicated id.
- A mission with a missing cluster id produces an error naming the mission and missing cluster.
- A cluster with a missing enemy type id produces an error naming the cluster and missing enemy.
- A behavior track with no events produces an error naming the behavior.
- A difficulty with a non-positive multiplier produces an error naming the difficulty and field.
- The registry can look up `mission.test`, `enemy.serial`, `weapon.primary.basic`, and `difficulty.normal`.
Then add a Godot content browser. Create `godot/scenes/debug/ContentBrowser.tscn` and `godot/scripts/debug/ContentBrowserController.cs`. The scene should be a `Control` with a title label and a multiline text label. The controller should create the sample registry, validate it, list definitions by id, and show validation messages. If Godot is headless and the command line contains `--content-validate-only`, it should print all loaded definition ids, print either `Content validation succeeded` or `Content validation failed`, then quit with exit code 0 for success and 1 for validation errors.
Update `godot/scripts/debug/DebugBootMode.cs` to add `ContentBrowser`. Update `godot/scripts/bootstrap/GameRoot.cs` to export a `PackedScene? ContentBrowserScene` and load it when the boot mode is `ContentBrowser`. Update `godot/scenes/bootstrap/GameRoot.tscn` to reference `res://scenes/debug/ContentBrowser.tscn`. Keep existing `Menu` and `Smoke` behavior unchanged.
Finally, update this ExecPlan as work proceeds. Do not edit `DESIGN.md` as part of this slice. If `SideScrollerGame.sln` or `godot/SideScrollerGame.Godot.csproj` have unrelated user edits, preserve them and stage only the minimal solution or project changes required for tests.
## Concrete Steps
Run all commands from `D:\Code\zfxaction26_1`.
1. Confirm the current state before editing:
git status --short
rg --files godot
dotnet build SideScrollerGame.sln
.\godot --headless --path godot -- --debug-boot=smoke --seed=12345
Expected result: `git status --short` should show only changes the user intentionally left in the worktree, if any. The .NET build should end with `Build succeeded`. The smoke boot should print `Debug boot: Smoke`, `Seed: 12345`, and `Smoke scene loaded`.
2. Create content folders:
godot/scripts/content
godot/scripts/content/definitions
godot/scripts/content/validation
godot/scripts/content/samples
Creating directories that already exist is safe.
3. Add the definition records and enums described in the Plan of Work. Keep the public namespace `SideScrollerGame.Content.Definitions` for definition types. Use `SideScrollerGame.Content.Validation` for validation types. Use `SideScrollerGame.Content.Samples` for sample builders.
4. Add `godot/scripts/content/ContentRegistry.cs`. It should accept all definition collections in its constructor, build dictionaries keyed by id, and expose read-only collections for validators and the debug browser. It should not depend on Godot scene nodes.
5. Add `godot/scripts/content/validation/ContentValidator.cs`. It should validate a whole registry and return `ContentValidationResult`. Do not throw exceptions for normal content mistakes; return validation messages so the browser and tests can display them.
6. Add `godot/scripts/content/samples/SampleContent.cs` with one valid test mission and all referenced definitions. Add broken sample helpers in test code or in `godot/scripts/content/samples/BrokenSampleContent.cs` if shared fixtures reduce duplication.
7. Add a test project:
dotnet new xunit -n SideScrollerGame.Content.Tests -o tests\SideScrollerGame.Content.Tests
dotnet sln SideScrollerGame.sln add tests\SideScrollerGame.Content.Tests\SideScrollerGame.Content.Tests.csproj
dotnet add tests\SideScrollerGame.Content.Tests\SideScrollerGame.Content.Tests.csproj reference godot\SideScrollerGame.Godot.csproj
If referencing the Godot project makes `dotnet test` fail because of Godot-specific build behavior, create `src\SideScrollerGame.Content\SideScrollerGame.Content.csproj`, move the pure content files there, reference that project from both the Godot project and the test project, and record the reason in `Surprises & Discoveries` and `Decision Log`.
8. Add tests for validation and registry lookup behavior. Use test names that describe the expected behavior, such as `SampleContent_ValidatesWithoutErrors` and `Validate_ClusterWithMissingEnemyType_ReportsMissingEnemy`.
9. Add `godot/scenes/debug/ContentBrowser.tscn` and `godot/scripts/debug/ContentBrowserController.cs`. The browser should display all ids from `SampleContent.CreateRegistry()` and validation messages from `ContentValidator`.
10. Extend debug boot:
godot/scripts/debug/DebugBootMode.cs
godot/scripts/bootstrap/GameRoot.cs
godot/scenes/bootstrap/GameRoot.tscn
Add the `ContentBrowser` enum value, export and wire `ContentBrowserScene`, and load it when requested by `--debug-boot=content-browser`.
11. Format touched C# files. Include every C# file touched by this slice. Use separate path arguments because quoting a semicolon-separated list can be interpreted as one path by JetBrains Cleanup Code:
jb cleanupcode --build=False godot\scripts\content\ContentRegistry.cs godot\scripts\content\definitions\MissionDefinition.cs godot\scripts\content\validation\ContentValidator.cs godot\scripts\content\samples\SampleContent.cs godot\scripts\debug\ContentBrowserController.cs godot\scripts\debug\DebugBootMode.cs godot\scripts\bootstrap\GameRoot.cs tests\SideScrollerGame.Content.Tests\ContentValidationTests.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.
12. Validate the slice:
dotnet test SideScrollerGame.sln
dotnet build SideScrollerGame.sln
.\godot --headless --path godot --build-solutions --quit
.\godot --headless --path godot -- --debug-boot=content-browser --content-validate-only
Expected result: tests pass, the solution builds with 0 errors, the Godot solution build exits without a stuck process, and the content browser prints loaded ids plus `Content validation succeeded`.
13. Check the diff:
git status --short
git diff -- SLICE2.MD godot/scripts/content godot/scripts/debug godot/scripts/bootstrap godot/scenes/debug godot/scenes/bootstrap SideScrollerGame.sln tests src
Expected result: the diff includes only Slice 2 files and the minimal solution/project updates required for tests. User changes outside the slice must not be reverted.
14. Commit this slice after validation:
git add SLICE2.MD godot/scripts/content godot/scripts/debug godot/scripts/bootstrap godot/scenes/debug godot/scenes/bootstrap SideScrollerGame.sln tests src
git commit -m "Add core content definitions"
If `src` is not created, omit it. If `SideScrollerGame.sln` is dirty from user work unrelated to the test project addition, stage only the hunks that add the test project.
## Validation and Acceptance
This slice is accepted when the following observable behaviors are true.
Running content 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 tests for valid sample content, missing references, duplicate ids, invalid difficulty multipliers, invalid behavior tracks, and registry lookup.
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 content browser can be booted directly and used as a validation smoke test:
.\godot --headless --path godot -- --debug-boot=content-browser --content-validate-only
Expected output includes:
Debug boot: ContentBrowser
Loaded content definitions:
mission.test
difficulty.normal
enemy.serial
cluster.opening
weapon.primary.basic
Content validation succeeded
Opening the editor should still show the project and root scene:
.\godot --editor --path godot
In the editor, running with no command-line arguments should still show the menu placeholder. Running with `-- --debug-boot=content-browser` should show the content browser with loaded ids and validation status. Running with `-- --debug-boot=smoke --seed=12345` should still print `Smoke scene loaded` and exit in headless mode.
## Idempotence and Recovery
The implementation is mostly additive and safe to repeat. Creating directories that already exist should do nothing. Re-running `dotnet test`, `dotnet build`, Godot solution build, and content browser smoke boot should produce the same result.
If test project creation fails because files already exist, inspect `tests/SideScrollerGame.Content.Tests/SideScrollerGame.Content.Tests.csproj` and continue from the existing project instead of deleting it. If the solution already contains the test project, do not add it twice.
If referencing `godot/SideScrollerGame.Godot.csproj` from tests causes restore or build problems, move pure content definitions into a plain class library under `src/SideScrollerGame.Content/` and reference that library from both the Godot project and tests. This is a safe course change because it keeps Godot scene scripts in the Godot project while making pure rules easy to test.
If `.\godot --headless --path godot --build-solutions --quit` hangs, stop only the stuck Godot process whose command line contains this repository path and this exact command. Record the command and process details in `Surprises & Discoveries`. Validate with `dotnet build SideScrollerGame.sln` plus the content browser smoke boot before continuing.
If user changes appear in files outside this slice, leave them untouched. If user changes conflict with `SideScrollerGame.sln`, `godot/SideScrollerGame.Godot.csproj`, or `godot/project.godot`, read the current 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 a generated file is unwanted, leave it unstaged unless the user explicitly asks for cleanup.
## Artifacts and Notes
Current project shape before Slice 2 implementation:
godot\project.godot
godot\SideScrollerGame.Godot.csproj
godot\scenes\bootstrap\GameRoot.tscn
godot\scenes\debug\SmokeScene.tscn
godot\scenes\menu\MenuPlaceholder.tscn
godot\scripts\bootstrap\GameRoot.cs
godot\scripts\debug\DebugBootMode.cs
godot\scripts\debug\DebugOverlay.cs
godot\scripts\debug\DebugSettings.cs
godot\scripts\debug\SmokeSceneController.cs
godot\scripts\menu\MenuPlaceholder.cs
Current successful Slice 1 smoke command:
.\godot --headless --path godot -- --debug-boot=smoke --seed=12345
Debug boot: Smoke
Seed: 12345
Smoke scene loaded
The content browser output after this slice should resemble:
Debug boot: ContentBrowser
Seed: 1
Loaded content definitions:
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
## 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 content model types should avoid Godot dependencies unless an engine type clearly saves complexity.
At the end of this slice, these public C# types should exist. The exact supporting fields may be adjusted during implementation, but the names, responsibilities, and validation behaviors should remain stable.
In `godot/scripts/content/ContentRegistry.cs`:
namespace SideScrollerGame.Content;
public sealed class ContentRegistry
{
public IReadOnlyDictionary<string, MissionDefinition> Missions { get; }
public IReadOnlyDictionary<string, DifficultyDefinition> Difficulties { get; }
public IReadOnlyDictionary<string, CameraPathDefinition> CameraPaths { get; }
public IReadOnlyDictionary<string, LevelLayerDefinition> LevelLayers { get; }
public IReadOnlyDictionary<string, EnemyTypeDefinition> EnemyTypes { get; }
public IReadOnlyDictionary<string, EnemyBehaviorDefinition> EnemyBehaviors { get; }
public IReadOnlyDictionary<string, EnemyClusterDefinition> EnemyClusters { get; }
public IReadOnlyDictionary<string, CollectibleDefinition> Collectibles { get; }
public IReadOnlyDictionary<string, WeaponDefinition> Weapons { get; }
public IReadOnlyDictionary<string, SpecialWeaponDefinition> SpecialWeapons { get; }
public IReadOnlyDictionary<string, SquadronMateTypeDefinition> SquadronMateTypes { get; }
public IEnumerable<string> AllDefinitionIds();
}
In `godot/scripts/content/validation/ContentValidator.cs`:
namespace SideScrollerGame.Content.Validation;
public sealed class ContentValidator
{
public ContentValidationResult Validate(ContentRegistry registry);
}
In `godot/scripts/content/validation/ContentValidationResult.cs`:
namespace SideScrollerGame.Content.Validation;
public sealed class ContentValidationResult
{
public IReadOnlyList<ContentValidationMessage> Messages { get; }
public bool HasErrors { get; }
}
In `godot/scripts/content/samples/SampleContent.cs`:
namespace SideScrollerGame.Content.Samples;
public static class SampleContent
{
public static ContentRegistry CreateRegistry();
}
In `godot/scripts/debug/ContentBrowserController.cs`:
namespace SideScrollerGame.Debug;
public partial class ContentBrowserController : Control
{
public override void _Ready();
}
The content browser controller must print validation results and quit when headless and `--content-validate-only` is present in `OS.GetCmdlineUserArgs()`.
In `godot/scripts/debug/DebugBootMode.cs`, extend the enum to include:
ContentBrowser
In `godot/scripts/bootstrap/GameRoot.cs`, extend boot loading so:
--debug-boot=content-browser
loads `res://scenes/debug/ContentBrowser.tscn`.
Revision note 2026-04-21: Created this ExecPlan from the current Slice 1 project shell. The plan scopes Slice 2 to pure data definitions, validation, sample content, tests, and an in-engine content browser smoke path while leaving gameplay runtime systems to later slices.