From 4ac8a77e8e7ba12fbb3de58ed6eb4e96fa8c2764 Mon Sep 17 00:00:00 2001 From: Frank Tovar Date: Thu, 14 May 2026 03:06:12 +0200 Subject: [PATCH] Refresh implementation task backlog --- TASKS.md | 369 +++++++++++++++++++++++++++---------------------------- 1 file changed, 179 insertions(+), 190 deletions(-) diff --git a/TASKS.md b/TASKS.md index cd169ba..452e1a0 100644 --- a/TASKS.md +++ b/TASKS.md @@ -1,205 +1,194 @@ -# Reactor Maintenance Tasks +# Reactor Maintenance Tasks -## Godot Frontend Integration +This backlog tracks what must change so the implementation matches `docs/design.md`, `docs/UX.md`, and `docs/CAMPAIGN.md`. -The Godot frontend (src/ReactorMaintenance.Godot) has a complete UX scaffold (splash to main menu to campaign intro to level screen to win/loss overlays) and reusable controls (StateBadge, PrimaryButton, CellInspector, ForecastList, InventoryStrip, ConfirmDialog, OutcomeOverlay) but no connection to the simulation core. This section covers bridging the gap. +## Audit Snapshot -### Phase 1: Simulation Bridge (Critical Path) +- Current simulation tests pass: `33/33` in `tests/ReactorMaintenance.Simulation.Tests`. +- Existing tests cover older behavior. They are useful regression scaffolding, but they do not prove the latest design rules. +- The simulation has a working `SimulationEngine`, network propagation, consumers, structural integrity, leaks, reactor readiness, forecasts, serialization, editor helpers, and Godot session bridge. +- Godot currently has a usable UX scaffold and grid renderer, but it still exposes older interaction concepts such as a player-facing `EndTurn` action and always-available underground layer controls. +- Existing campaign data is the older three-level placeholder set: `coolant_restart.json`, `fuel_bleed.json`, and `black_start.json`. These levels and manifest entries must be replaced by the tutorial plus six-group campaign from `docs/CAMPAIGN.md`. +- Unrelated Godot metadata or generated `.uid` files are not part of this backlog unless a later implementation task intentionally touches them. -**Goal:** Connect LevelScreen to SimulationEngine so gameplay state flows between simulation and UI. +## P0 Simulation Contract -- [x] **Task 1.1: Create GameSession class** - - Location: src/ReactorMaintenance.Godot/Data/GameSession.cs - - Wraps SimulationEngine and holds the current LevelState - - Loads LevelState from JSON via LevelSerializer.Deserialize() using Godot FileAccess - - Exposes current state properties: RobotPosition, GlobalState, Terrain, Surface, UndergroundLayers, Props, Leaks, Forecasts - - Fires events on state changes: OnLevelStateChanged, OnRobotMoved, OnTurnAdvanced, OnLevelWon, OnLevelLost - - Exposes action methods: MoveRobot, EndTurn, InteractProp, InteractLeak, ApplyHeatShield, ActivateReactor - - Validates actions before committing (rejects invalid moves) - - Handles level start snapshot for retry +- [ ] Rename the public environment tick model from turn-based `AdvanceTurn`/`EndTurn` semantics to `Pulse` semantics. + - One accepted `LengthyAction` triggers exactly one `Pulse`. + - One `Pulse` contains a fixed balance-defined number of deterministic `Step`s. + - The fixed step count must not vary by action type, forecast result, or danger level. + - Keep isolated step or pulse advancement available only for tests, editor tooling, or debug use. +- [ ] Remove player-facing wait/fast-forward behavior from normal gameplay. + - `MoveRobot`, selection, inspection, and terminal layer viewing remain `QuickAction`s. + - `InteractProp`, `InteractLeak`, `ApplyHeatShield`, and accepted powered-prop no-op interactions are `LengthyAction`s. + - `ActivateReactor` wins when `ReactorReadiness` is true and must not require a separate wait. +- [ ] Add `IsolationValveProp` as a first-class prop. + - Store carrier binding and `Open`/`Closed` state. + - Block intentional underground propagation across the authored branch boundary. + - Preserve downstream starvation/readiness consequences for consumers and reactor feed. + - Add editor placement, validation, serialization, and rendering support. +- [ ] Add `SprinklerControlProp` and wall-mounted `CoolantSprinklerValve`. + - A `SprinklerControlProp` links to exactly one `CoolantSprinklerValve`. + - The valve is wall-mounted, not directly interactive, and has exactly one outlet/access floor face. + - Discharge creates `SprinklerWater` only while linked control is `Enabled` and the coolant branch is fed. + - Discharge applies deterministic local coolant pressure debt. + - Add editor placement/linking, validation, serialization, and rendering support. +- [ ] Rework surface coolant into `SprinklerWater`. + - Rename code-facing concepts where practical; otherwise make names and UI text consistently mean sprinkler water, not a generic coolant hazard. + - `Coolant` pipe failures should inject `SprinklerWater`, not a second damaging liquid. + - `SprinklerWater` alone must not cause `UnsafeEntryLoss`. +- [ ] Update leak injection. + - Leaks inject only when the underground leak cell has positive amount and positive pressure/voltage after propagation. + - Isolating a leak stops fresh injection without repairing the underlying fault. + - Repair restores structural integrity and stops future injection but does not clean existing surface values. +- [ ] Implement the approved surface interaction order. + - Resolve leak/sprinkler injection per `Step`. + - Resolve coolant mitigation before ignition and electrical spread. + - Implement `Dilute`, `Quench`, value-based `Evaporate`, wet-electric `Conduct`, and `Ignite`. + - Preserve deterministic same-cell and adjacent-cell delta accumulation. + - Closed powered doors and remedy blocks must gate only the interactions they explicitly block. +- [ ] Implement value-based evaporation. + - Add balance values for ambient evaporation, heat-driven evaporation, and evaporation cooling. + - Hot cells should evaporate `SprinklerWater` faster than cold cells. + - Evaporation happens during useful action pulses; there is no campaign wait command. +- [ ] Implement `Unsafe` as derived movement safety. + - `Unsafe` is recalculated after authored setup and after each `Pulse`. + - `Unsafe` is caused by unsafe `Heat`, unsafe `LeakedElectricity`, or the wet-electric unsafe rule. + - `LeakedFuel` alone and `SprinklerWater` alone are not `Unsafe`. + - `UnsafeEntryLoss` happens only when `MoveRobot` enters an `Unsafe` destination without applicable protection. + - A `Pulse` must not kill a stationary robot just because the current cell becomes `Unsafe`. +- [ ] Implement powered prop behavior. + - `DoorProp` and `AllSeeingEyeTerminal` require positive local electricity amount and voltage for their interactions to take effect. + - Interacting with an unpowered `PoweredProp` is accepted as a `LengthyAction`, changes no prop state, reveals no terminal information, and still triggers one `Pulse`. + - Powered doors keep their last physical open/closed state when power is lost. +- [ ] Gate all-seeing-eye information. + - Underground topology, numeric underground values, and `Forecast` output are visible only while the robot is at an active and powered `AllSeeingEyeTerminal`. + - Terminal access is local and does not persist after the robot leaves. + - Forecasts are systemic simulations over copied state, never authored level prose. +- [ ] Update `ReactorReadiness` checks as the invariant source of victory. + - Every network present beneath `ReactorControlProp` must have positive amount and intensity. + - Required per-carrier consumer counts must be `Enabled` and `Producing`. + - Missing readiness blocks `Ready` but does not directly cause `Lost`. -- [x] **Task 1.2: Create LevelStateLoader helper** - - Location: src/ReactorMaintenance.Godot/Data/LevelStateLoader.cs - - Static helper that takes a res://Data/Levels/... path - - Uses Godot FileAccess to read JSON string - - Calls LevelSerializer.Deserialize() and returns LevelState - - Throws descriptive exceptions for missing files or schema errors - - Supports both res:// and user:// paths +## P0 Simulation Tests -- [x] **Task 1.3: Wire LevelScreen to GameSession** - - Update LevelScreen.Configure() to accept GameSession instead of raw CampaignLevel - - Replace placeholder grid with real viewport - - Wire CellInspector.SetCellInfo() to display live selected cell data - - Wire ForecastList.SetForecasts() to display live simulation forecasts - - Connect action bar buttons to GameSession action methods - - Update LevelHeader with live global state badge (Stable/Caution/Critical/Ready) - - Update InventoryStrip with live remedy/heat shield counts +- [ ] Update existing tests so their names and assertions use `Pulse`, `Step`, `SprinklerWater`, `Unsafe`, and `ReactorReadiness` terminology. +- [ ] Add tests for fixed pulse length. + - Every accepted `LengthyAction` advances one `Pulse`. + - Each pulse resolves the configured number of `Step`s. + - `MoveRobot` and inspection-like calls do not resolve a pulse. +- [ ] Add tests for no normal wait/fast-forward dependency. + - Player-facing session/action APIs should not require `EndTurn` to reach readiness after a lengthy action. + - Debug/test-only pulse advancement, if retained, must be clearly named and excluded from campaign UI. +- [ ] Add tests for `IsolationValveProp`. + - Open valve allows branch propagation. + - Closed valve isolates damaged branches and can starve downstream consumers/reactor feed. + - Toggling a valve triggers exactly one `Pulse`. +- [ ] Add tests for `SprinklerControlProp` and `CoolantSprinklerValve`. + - Valve discharge requires a linked enabled control and fed coolant branch. + - Direct valve interaction is invalid or unavailable. + - Discharge creates `SprinklerWater` at the authored outlet and applies pressure debt. + - Disabling the linked control or isolating the sprinkler branch stops fresh discharge. +- [ ] Add tests for updated leak injection. + - Fed fuel, coolant, and electricity leaks inject to the correct access face. + - Isolated leaks stop new surface injection. + - Repairs restore structural integrity without cleaning existing surface values. +- [ ] Add tests for surface interactions. + - `SprinklerWater` dilutes `LeakedFuel`. + - `SprinklerWater` quenches `Heat`. + - Evaporation removes water and cools heat according to balance values. + - Wet cells conduct electricity faster than dry cells. + - Fuel plus electricity or heat can ignite and create heat while consuming fuel. + - Closed doors block designed surface propagation paths. +- [ ] Add tests for `Unsafe`. + - Moving into unsafe heat loses without active heat protection. + - Moving into unsafe electricity loses. + - Moving into wet-electric unsafe cells loses. + - Moving into fuel-only or sprinkler-water-only cells does not lose. + - A pulse that makes the robot's current cell unsafe does not immediately lose. +- [ ] Add tests for powered props. + - Powered door toggles and blocks/unblocks surface propagation. + - Unpowered door interaction changes no door state but still triggers a pulse. + - Powered terminal interaction enables local visibility and forecasts. + - Unpowered terminal interaction reveals nothing and still triggers a pulse. + - Leaving the terminal removes underground/forecast visibility. +- [ ] Add tests for campaign authoring invariants. + - Every campaign level loads and validates. + - Tutorial is solvable with exactly one `LengthyAction` before `ActivateReactor`. + - Every non-tutorial level has at least two valid first `LengthyAction` choices. + - No campaign level requires a wait/fast-forward command. + - No level before 3-2 requires underground visibility to understand the intended first decisions. + - Campaign manifest order matches `docs/CAMPAIGN.md`. -- [x] **Task 1.4: Create res://Data/Levels/ directory and level files** - - Create placeholder level files for the 3 campaign levels - - Use Win2D editor to export v3 JSON schema for each level - - Files: coolant_restart.json, fuel_bleed.json, black_start.json - - Verify round-trip: load -> serialize -> deserialize -> validate +## P1 Editor, Schema, And Level Data -### Phase 2: Grid Viewport Rendering (Critical Path) +- [ ] Bump the level schema version when adding new prop/link/outlet state. +- [ ] Update serialization round trips for new fields. + - `IsolationValveProp` carrier and open/closed state. + - `SprinklerControlProp` enabled/disabled state and linked valve id or position. + - `CoolantSprinklerValve` wall position, outlet/access face, linked control, and coolant connection. + - Powered terminal active state if it becomes serialized runtime state. +- [ ] Update `LevelEditor` tools. + - Add isolation valve placement. + - Add sprinkler control placement. + - Add wall-mounted sprinkler valve placement and outlet/access cycling. + - Keep electricity leak wall access cycling. + - Prevent invalid prop placement on walls except designed wall-mounted sprinkler valves. +- [ ] Update `LevelValidator`. + - Validate powered doors have valid geometry and local electricity. + - Validate terminal power requirements where needed. + - Validate wall-mounted sprinkler valve geometry, outlet/access face, coolant connection, and exactly one linked control. + - Validate isolation valves sit on exactly one matching underground carrier. + - Warn on initially unready reactors, initially starved required consumers, unused supplies, and sprinkler valves with no useful suppression or pressure tradeoff. +- [ ] Replace placeholder campaign files. + - Create tutorial plus Groups 1-6 from `docs/CAMPAIGN.md`. + - Update `default_campaign_manifest.json` to the final order. + - Remove or demote old placeholder levels so they are not presented as campaign content. + - Add stable ids and short flavor text for every authored level. +- [ ] Add test/build helpers for level construction. + - Prefer shared builders for linear networks, forks, leaks, wall electricity faces, doors, controls, consumers, and reactors. + - Avoid duplicating low-level array setup across tests. -**Goal:** Replace the grid placeholder with a functional, interactive tile-based viewport. +## P1 Godot UX Integration -- [x] **Task 2.1: Create GridViewport control** - - Location: src/ReactorMaintenance.Godot/Controls/GridViewport.cs - - Custom Control that renders the level grid via CanvasItem._draw() - - Supports configurable tile size (default 48px) - - Handles viewport scrolling and zoom - - Exposes SelectedCell, RobotPosition, HoveredCell properties - - Fires OnCellSelected, OnCellHovered, OnGridClicked signals +- [ ] Remove player-facing `End Turn` from the normal action bar. + - Replace it with contextual `LengthyAction` commands and `ActivateReactor` when ready. + - Keep any debug pulse control behind an explicit development-only path. +- [ ] Update `GameSession` API names and events to match pulse semantics. + - Use `PulseAdvanced` or equivalent instead of `TurnAdvanced`. + - Ensure accepted no-op powered-prop interactions still notify pulse playback. + - Ensure refused invalid actions do not mutate state or trigger pulse playback. +- [ ] Animate `Pulse` playback as a short cascade of `Step`s. + - Show leak growth, sprinkler discharge, evaporation, quenching, ignition, wet conduction, and readiness updates. + - Disable conflicting inputs during playback. + - End playback on the final post-pulse decision state. +- [ ] Gate underground layer controls and forecasts. + - Hide or disable layer toggles unless the robot is at an active and powered `AllSeeingEyeTerminal`. + - Show why terminal access is unavailable when unpowered or away from the terminal. + - Use `Pulse +N` wording in forecast UI. +- [ ] Update grid rendering and inspector text. + - Render `SprinklerWater` separately from underground `Coolant`. + - Render `Unsafe` cells with a distinct movement warning treatment. + - Render isolation valve state, sprinkler control state, wall-mounted sprinkler outlet, powered door state, and powered terminal state. + - Inspector should display visible surface values, prop state, consumer per-carrier state, and underground values only when terminal access allows it. +- [ ] Update action affordances. + - Movement remains direct and quick. + - Prop interactions should explain unavailable power, invalid position, missing remedy, depleted supply, and reactor-not-ready causes. + - Disabled actions remain inspectable so players can understand why an action is unavailable. -- [x] **Task 2.2: Implement terrain rendering** - - Draw floor tiles with graphite/steel palette per ART.md - - Draw wall tiles with darker steel, reinforced silhouettes - - Apply subtle hue shifts between adjacent floor plates (no checkerboard) - - Support layer opacity rules (surface full / underground 25% or 50%) +## P2 Polish And Release Tasks -- [x] **Task 2.3: Implement prop rendering** - - Draw props at cell centers with semantic colors - - Show state indicators (enabled/disabled, producing/starved) - - Use generated asset textures where available, fall back to procedural badges - - Support prop highlight for selected cell +- [ ] Add concise pulse-result feedback for major outcomes: isolated leak, restored pressure, downstream starvation, reactor ready, wet-electric risk, and terminal heat danger. +- [ ] Add campaign completion flow for the final Group 6 level. +- [ ] Add loading and malformed-level error states for campaign level loading. +- [ ] Verify Win2D editor export and Godot loader compatibility for the new schema. +- [ ] Revisit art labels/icons for `SprinklerWater`, `Unsafe`, powered props, and sprinkler controls after mechanics are implemented. -- [x] **Task 2.4: Implement robot marker** - - Draw robot sprite at current RobotPosition - - Show direction indicator - - Use maintenance_robot.png from Assets/Characters - - Animate on position change (optional, low priority) - -- [x] **Task 2.5: Implement hazard rendering** - - Draw surface hazards on affected floor cells - - Use semantic colors: fuel (red slicks), coolant (blue-cyan), electricity (yellow arcs), heat (orange-white) - - Show hazard intensity via saturation/opacity - -- [x] **Task 2.6: Implement cell selection highlight** - - Draw selection rectangle around selected cell - - Draw reachable/actionable hints on valid move targets - - Support hover highlight on non-selected cells - -- [x] **Task 2.7: Implement underground network visualization** - - Draw carrier-colored network lines between adjacent cells with positive flow - - Fuel = red, Coolant = blue, Electricity = yellow - - Render sources as large centered dots - - Support layer toggle: show/hide each underground layer independently - - Apply opacity rules per design doc - -### Phase 3: Action Input System - -**Goal:** Enable player interaction with the simulation through grid input and keyboard. - -- [ ] **Task 3.1: Implement grid click-to-select** - - Left-click on grid cell -> select cell, update CellInspector - - Right-click -> context menu for available actions (repair, interact, etc.) - - Click on robot position -> select robot cell - -- [ ] **Task 3.2: Implement keyboard movement** - - WASD / Arrow keys -> move robot to adjacent floor cell - - Validate against SimulationEngine.MoveRobot before committing - - Show rejected movement as brief toast/negative feedback - - Tab -> cycle focus between grid, inspector, action bar buttons - -- [ ] **Task 3.3: Implement action bar integration** - - Move button -> enter move mode (highlight reachable cells) - - Interact button -> execute prop interaction at robot position - - Repair button -> repair reachable leak at robot position - - Trigger Win / Trigger Lose -> remove for release, keep for dev testing - - Disable buttons when action is invalid; keep them inspectable - -- [ ] **Task 3.4: Implement Enter/Space action confirmation** - - Enter -> confirm current action (interact, repair, activate) - - Space -> quick action (move to adjacent in facing direction) - - Escape -> cancel current mode, deselect - -### Phase 4: UI Polish and Data Flow - -**Goal:** Make all UI controls display live simulation data and feel responsive. - -- [ ] **Task 4.1: Complete CellInspector data binding** - - Display: position, terrain type, prop type, prop state - - Display: surface hazards (type, amount, band) - - Display: underground state (when all-seeing-eye active) - - Update on every simulation step - -- [ ] **Task 4.2: Complete ForecastList data binding** - - Map EForecastKind values to display strings - - Show turn offset (+1, +2, etc.) and severity band - - Order from soonest to latest - - Update on every simulation step - -- [ ] **Task 4.3: Complete InventoryStrip data binding** - - Display remedy counts (fuel/coolant/electricity remedy types) - - Display heat shield count - - Show depleted/empty states - -- [ ] **Task 4.4: Complete LevelHeader data binding** - - Display global state badge (Stable/Caution/Critical/Ready/Lost/Won) - - Display reactor heat level with band coloring - - Display campaign progress (Level X / Y) - -- [ ] **Task 4.5: Add turn transition feedback** - - Brief visual indicator when a turn is resolved - - Show outcome of lengthy actions (prop toggled, leak repaired, etc.) - - Toast/negative feedback for rejected actions - -### Phase 5: Testing and Verification - -**Goal:** Ensure simulation -> Godot data flow is correct and reliable. - -- [ ] **Task 5.1: Write GameSession unit tests** - - Load level from JSON -> verify LevelState matches expected - - Execute actions -> verify state mutations match simulation engine - - Test retry restores start snapshot - - Test invalid action rejection - -- [ ] **Task 5.2: Write LevelStateLoader tests** - - Load existing level files -> verify successful deserialization - - Load missing file -> verify descriptive exception - - Load malformed JSON -> verify schema error message - -- [ ] **Task 5.3: End-to-end integration verification** - - Run Godot editor, load campaign, play through one full level - - Verify all props, leaks, hazards render correctly - - Verify cell inspector shows accurate data - - Verify forecasts match simulation output - - Verify win/loss overlays trigger at correct simulation states - -- [ ] **Task 5.4: Verify Win2D to Godot level compatibility** - - Export a level from Win2D editor -> load in Godot frontend - - Verify all terrain, props, underground data, robot state, and inventory transfer correctly - - Verify simulation engine produces identical results regardless of source - -### Phase 6: Underground Layer Toggle (Medium Priority) - -**Goal:** Allow the player to inspect underground networks when at an all-seeing-eye terminal. - -- [ ] **Task 6.1: Implement all-seeing-eye viewing mode** - - When robot is at an all-seeing-eye terminal, enable underground visibility - - Show all three underground layers simultaneously with reduced opacity - - Highlight the carrier of the active underground layer at full opacity - -- [ ] **Task 6.2: Implement per-layer underground toggle** - - Allow player to cycle through Fuel/Coolant/Electricity underground views - - Each view shows only that carrier network at full opacity - - Other layers at 25% opacity - -### Phase 7: Release Cleanup (Low Priority) - -**Goal:** Prepare the Godot frontend for release. - -- [ ] **Task 7.1: Remove dev-only buttons** - - Remove Trigger Win and Trigger Lose buttons from action bar - - Replace with End Turn / Wait button - -- [ ] **Task 7.2: Add loading state for level transitions** - - Show loading indicator when loading level JSON - - Handle slow load gracefully - -- [ ] **Task 7.3: Export configuration** - - Verify Godot export template for Windows works - - Test standalone build with all assets bundled +## Verification Rules +- [ ] After each implementation iteration, run the focused simulation tests that cover the changed system. +- [ ] Run full `dotnet test` before committing simulation or serializer changes. +- [ ] For code changes on Windows, run the repo-required CRLF normalization and cleanup steps after implementation. +- [ ] For documentation-only iterations, no cleanup pass is required. +- [ ] Keep documentation aligned whenever code changes terminology or behavior. +- [ ] Commit each completed iteration with a brief summary.