rimlike/docs/implementation.md
megaproxy 836dfdd716 Phase 1 — 80² world, 6-layer TileMap, camera rig, tick loop, speed UI
World scene (scenes/world/world.{tscn,gd}):
- 6 TileMapLayer nodes per architecture.md split: Terrain (0), Floor (1),
  Wall (2), Designation (3), Roof (4, hidden), Fog (5, hidden).
- Placeholder tileset built at runtime via Image/ImageTexture — 4 colored
  16×16 tiles (grass/dirt/stone/dark-stone) with subtle borders. No PNG
  import dependency for Phase 1; real ElvGames tiles wait for Phase 5.
- Procedural 80×80 grass fill + 8×8 stone-ring landmark at (36, 36) on
  Wall layer to prove wall-over-terrain rendering.
- Calls camera_rig.set_world_bounds() once map dimensions known.
- ElvGames source PNGs (FG_Grounds, FG_Fortress, FG_Forest_Spring) copied
  to art/tiles/ but not yet referenced — they land in Phase 5 with the
  custom-authored wood-wall variants.

Camera rig (scenes/world/camera_rig.{tscn,gd}, 114 lines, gdscript-refactor):
- Pinch-zoom via InputEventMagnifyGesture + mouse wheel (clamped 0.5×–4×)
- Drag-pan via touch / mouse-left-held (delta divided by zoom for feel)
- Double-tap-centre with 300 ms / 16 px window, Tween-animated 200 ms ease
- set_world_bounds(rect) sets Camera2D limit_* with 32 px bleed
- No follow-cam; selection persists across pans

Tick loop (autoload/sim.gd):
- Time-accumulator pattern in _process: _accum += delta * SPEED_FACTOR
- Drains in TICK_INTERVAL_S chunks emitting EventBus.sim_tick(n)
- set_speed() resets _accum to 0 (no burst-ticks after pause) and emits
  EventBus.speed_changed(int). Boot default = NORMAL.
- Audit.log on every speed transition for runtime diagnostics.
- Early-return guard against redundant set_speed calls.

EventBus (autoload/event_bus.gd):
- New signals: sim_tick(tick_number: int), speed_changed(new_speed: int)

Top bar (scenes/ui/top_bar.{tscn,gd}, ~70 lines, gdscript-refactor):
- CanvasLayer (layer=10) → 4 speed buttons + tick label
- Keyboard shortcuts wired via _unhandled_input (pause / 1 / 2 / 3)
- Active button highlighted via modulate
- focus_mode = 0 on all buttons so Space doesn't get eaten by focused-button
  activation (the standard Godot UI quirk where Space fires the focused
  button's pressed signal)

i18n (autoload/strings.gd):
- 5 new keys: speed.pause/normal/fast/ultra, hud.tick (template with {n})

Main bootstrap (scenes/main/main.{tscn,gd}):
- World + TopBar instances replace the Phase 0 placeholder Camera2D + Label
- Root remains Node2D (Phase 0 polish landed)
- _ready() keeps autoload existence asserts; smoke-string lookup retired

Indoor tint shader (art/shaders/indoor_tint.gdshader):
- Stub: tint_strength = 0 pass-through. Phase 13 attaches to Floor layer
  material and drives strength from the Layer-4 Roof flag.

Acceptance: MCP-verified via play_scene + get_game_screenshot. 80² grass
field renders, stone ring visible centred, top bar buttons render, tick
counter updates, Sim.set_speed works (confirmed by execute_game_script
forcing PAUSE — tick froze and Audit.log emitted the transition line).

Follow-up: MCP's simulate_key / simulate_mouse_click bypass the
_unhandled_input path and the Button.pressed signal — events don't reach
the handler. Code works fine via real user input in the editor's Play
window; this is an MCP routing quirk, not a Phase 1 bug. Documented as
a known limitation when scripting input tests.

Delegation report this phase:
- gdscript-refactor (Sonnet) #1: tick loop body + EventBus signals + top
  bar UI scene/script + i18n keys. ~3 file mods + 2 new files. Headless-
  validated by the subagent.
- gdscript-refactor (Sonnet) #2: camera rig scene + script. 2 new files,
  114 lines GDScript. Headless-validated by the subagent.
- Opus: world scene + procedural tileset + map fill + integration into
  main.tscn + MCP-driven runtime verification.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-10 20:37:02 +01:00

33 KiB
Raw Blame History

Implementation plan — rimlike

Phased build plan from clean-slate to MVP. Phases are ordered by dependency, and each ends with a runnable demo state so the project never sits in "everything's stubbed" territory for long.

Effort estimates are wall-time at focused solo pace. Scale up generously for context-switches, life, and the occasional rabbit hole. Ranges are deliberately wide.

Status Phase
done — green dot up, smoke scene runs, MCP plugin self-installed 3 runtime services Phase 0 — Project scaffold & foundations
done — 80² map renders, walls/terrain/UI layers, camera rig, tick loop, speed UI all live Phase 1 — World, tilemap, camera
next Phase 2 — Pawn skeleton, pathfinding, movement

Use this doc as a checklist: tick boxes as items complete, and update the Status row above whenever a phase rolls over. The last bullet of each phase is the acceptance demo — the phase is "done" when you can perform it.

Refs to docs/ files are linked so each item lands in the right spec.


Pre-implementation audit (~75 min)

The five items from memory.md Open questions / Audit. None of these need code, but several of them gate Phase 1+ (autotile drives the wall pipeline; aesthetic harmony decides whether Ventilatore stays in active use). Knock these out before Phase 0.

  • Aesthetic harmony test — ElvGames Forest vs Ventilatore tile, side-by-side. Decide use-both or drop-Ventilatore. (~15 min) — needs your eye
  • ElvGames autotile audit — done 2026-05-10. Findings (visual inspection):
    • FG_Houses.png is NOT autotile-solvable as-is. Pieces are pre-built decorative house compositions (4 distinct roof palettes), not modular wall variants. ~½1 day per material to author terrain bits on top.
    • FG_Fortress.png IS autotile-solvable. 2030 modular tan-stone-with-dark-mortar pieces — straight, corners, caps. Wang-style Godot 4 terrain works with minimal extra art.
    • Recommendation: make Fortress stone the primary player wall material. Defer custom-authored Houses walls to v2 OR keep Houses as static prebuilt-shelter art only.
    • Iconic Homestead $19.99 fallback not needed.
  • Wolf sprite source — done 2026-05-10. No wolf in the bundle. EvoMonster packs are all cute/fantasy creatures (slimes, ghosts, dragons), Turn-Based RPG Monsters are humanoid-style. No 4-legged canine predator anywhere in the bundle. Action: commission a 16×16 wolf (idle + 24-frame walk × 4 directions) OR check Ventilatore bundle OR find a CC0 sprite. Open in memory.md.
  • Grave marker source — done 2026-05-10. Retro Graveyard 16x16 Tileset [Kingdom Explorer] confirmed in Tier 3, full graveyard suite (tombstones, crosses, mounds, crypts).
  • License compilation start — kick off the credits string list now; add to it as packs come in. (open across phases)

Phase 0 — Project scaffold & foundations (~1 week)

Goal: a Godot project that opens cleanly, has all the autoloads and folder structure committed, and runs an empty test scene.

  • project.godot at repo root. GL Compatibility renderer (max mobile reach; Forward+ would lock out older devices). Pixel-snap on, texture filter = nearest. Landscape sensor orientation. Viewport 1280×720 (canvas_items stretch, keep aspect).
  • Re-copy addons/godot_mcp/ from /mnt/d/godot/mcp/addons/godot_mcp and enable in project.godot [editor_plugins]. Editor-side green-dot check pending — needs you to open the editor once.
  • Folder layout (cribbed from tavernkeep's idiomatic Godot pattern — co-located scripts in scenes/, autoload/ at root):
    • autoload/ — singletons (world.gd, sim.gd, game_state.gd, event_bus.gd, strings.gd, audit.gd, save_system.gd)
    • scenes/ (main/, world/, pawn/, ui/, entities/, effects/)
    • data/ (recipes/, thoughts/, events/, weather/, pawns/)
    • art/ (tiles/, sprites/, ui/, fx/)
    • audio/ (sfx/, music/)
    • tests/, tools/
  • Autoloads (stubs; real bodies land in later phases):
    • World — entity registry, tile state, signals
    • Sim — tick loop owner, speed/pause state, Speed enum + factor table
    • GameState — current map, session timestamp, save_dict() / apply_dict()
    • EventBus — global signal hub (no signals yet — added per-phase)
    • Strings — i18n string table (Strings.t(key) lookup; const dict for now, .tres later)
    • Audit — debug-only logging gate
    • SaveSystemwrite_save() / read_save(), version + path, JSON
  • Input map: pause, speed_cycle, speed_normal, speed_fast, speed_ultra, confirm, cancel. Mobile gestures (pinch / drag / long-press) handled at script level, not as input actions — Godot's InputEventScreenTouch / InputEventScreenDrag / InputEventMagnifyGesture cover them.
  • SaveSystem skeleton: version field (SAVE_VERSION = 1), user://save_slot.json, JSON serialize, mismatch warning. Smoke-test payload only; Phase 3 expands.
  • .gitignore covers .godot/, .import/, addons/godot_mcp/, exports — verified.
  • Smoke-test scene: scenes/main/main.tscn (Node + Camera2D + Label). main.gd._ready() asserts every autoload alive and shows the i18n-resolved hello string.
  • Acceptance (headless): godot --headless --path . --quit exits 0, [main] Phase 0 smoke test online. prints, no errors. Confirmed.
  • Acceptance (editor): open project.godot in Godot 4.6, hit Play — see "Phase 0 — autoloads online." rendered at 32,32. MCP Pro bottom panel shows green dot. Needs your hand.

Phase 1 — World, tilemap, camera (~2 weeks)

Goal: an 80×80 map with the locked camera UX. No pawns yet; just a navigable empty world.

  • 6 TileMapLayer nodes in scenes/world/world.tscn (Godot 4.4+ idiom — supersedes the multi-layer TileMap): 0 Terrain · 1 Floor · 2 Wall · 3 Designation · 4 Roof (hidden) · 5 Fog (hidden). Z-indices set, layers can hold different sources independently.
  • Placeholder tileset built at runtime (no PNG import dependency for Phase 1). 4 programmatic 16×16 colored tiles (grass / dirt / stone / dark-stone) generated via Image.create() + ImageTexture.create_from_image(). Real ElvGames PNGs (FG_Grounds.png, FG_Fortress.png, FG_Forest_Spring.png) copied to art/tiles/ but not yet wired — they land in Phase 5 when the wood-wall variants get authored.
  • 80×80 map filled with grass on the Terrain layer, plus an 8×8 stone-ring landmark at (36, 36) on the Wall layer to prove the wall layer renders correctly on top of terrain.
  • Tick loop in autoload/sim.gd — time-accumulator pattern: _accum += delta * SPEED_FACTOR[current_speed], drains in TICK_INTERVAL_S = 1/20 chunks emitting EventBus.sim_tick. Default boot speed = NORMAL. set_speed() resets _accum to 0 to avoid burst-ticks after pause.
  • Speed control top bar (scenes/ui/top_bar.tscn) — CanvasLayer (layer 10) → 4 buttons (Pause / 1× / Fast / Ultra) + tick label. Keyboard shortcuts: pause, speed_normal/fast/ultra (keys Space, 1, 2, 3). Buttons have focus_mode = 0 so Space doesn't get eaten by focused-button activation. Active speed highlighted via modulate.
  • Camera rig (scenes/world/camera_rig.tscn) per ui.md "World view camera (locked)":
    • Pinch-zoom via InputEventMagnifyGesture + mouse-wheel; target_zoom lerps smoothly toward intent
    • Drag-pan via InputEventScreenDrag / InputEventMouseMotion + left-button held
    • Double-tap-centre with 300 ms / 16 px window, animated by Tween
    • set_world_bounds(rect) called by world.gd once map is built — sets Camera2D limit_* with 32 px bleed
    • No follow-cam
  • Indoor tint shader skeleton at art/shaders/indoor_tint.gdshader (tint_strength = 0 pass-through). Not yet attached to any TileMapLayer material — Phase 13 wires it onto the Floor layer driven by the Roof flag.
  • Acceptance (visual, MCP-verified): 80² grass field renders, 8×8 stone ring landmark visible at centre, 4 speed buttons render top-left, tick counter updates top-right, Sim.set_speed() works (verified via execute_game_script), pause freezes the tick counter. Manual interaction in the editor's Play window covers the keyboard/click pathway (MCP's simulate_key doesn't route through _unhandled_input — recorded as a follow-up).

Phase 2 — Pawn skeleton, pathfinding, movement (~2 weeks)

Goal: 3 pawns on the map, click-to-move them around. No AI yet.

  • Pawn scene: CharacterBody2D (or Node2D if we don't use built-in physics for movement — TBD), sprite, name label, debug state badge
  • Pawn registry on World: pawns: Array[Pawn]
  • AStarGrid2D over the 80² map; walkable derived from terrain passability + furniture occupancy. Update affected cells on build/destroy/door-state-change (one cell per change → O(1)).
  • Walk-to-tile: pawn requests a path, follows, lerps between sim ticks for smooth render
  • Click-to-move test: tap empty tile while pawn selected → pawn walks there
  • Spike (~30 min): AStarGrid2D path-query timing at 80² with 6 pawns simultaneously requesting paths. Confirm sub-millisecond per query.
  • Acceptance: spawn 3 pawns, tap-select, tap-destination, watch them path around walls and reach the spot. Smooth motion at all speeds.

Phase 3 — AI core: Decision → WorkProvider → JobRunner (~3 weeks)

Goal: the 5-layer pipeline from architecture.md is real, but with one dummy work category. Save round-trip for JobRunner mid-toil state is required to land in this phase, not later.

  • Decision layer: priority-ordered checks (incapacitation → forced job → status interrupt → work → idle)
  • WorkProvider interface — find_best_for(pawn) -> Job?
  • Job + JobRunner — multi-step toils, each toil is {action, predicate, on_complete}
  • Player overrides: forced job (e.g. "go here") preempts work
  • Status interrupts skeleton — only Bleeding for now (rest land at Phase 9)
  • Idle behavior: stand still or wander locally (per architecture.md:72, idle is v2; MVP just stands)
  • First WorkProvider: RestProvider — sends pawn to a hardcoded "rest tile". Just a smoke test for the pipeline.
  • Save round-trip: kill the app mid-toil-2-of-4, reopen, pawn resumes the same toil at the same position
  • Acceptance: 3 pawns idle around a rest tile; force-move one with a tap-and-hold-issue-order; suspend mid-walk and resume seamlessly.

Phase 4 — First verbs: chop, mine, hauling, stockpiles (~3 weeks)

Goal: the foundational gameplay loop — pawns harvest things and pile them up.

  • Tree entity (chop → 3 logs drop), stone-tile mining (mine → 1 stone drop), iron-ore-tile mining
  • Item entity: position, type, stack size, on-floor sprite
  • ChopProvider, MineProvider (subset of Construction work)
  • Hauling:
    • HaulingProvider + Hauling job toils (walk → pick → walk → deposit)
    • items_needing_haul dirty set on World (per architecture.md:243)
    • StorageDestination interface (zones first; containers Phase 5)
    • No-destination fallback (locked decision): drop after 3 retry passes + passive No stockpile accepts X alert
  • Floor stockpile zones:
    • Zone-paint UI (designation paint mode reuses Phase 5 paint controller — for now, a quick zone-paint button)
    • 16-chip filter grid (Wd/St/Ir/Cu/Ag/Au/Cl/Veg/Mt/Gr/Ck/Md/Tl/Wp/Ar/Co)
    • 5-priority cycle (Critical/High/Normal/Low/Off) on the zone
    • One-stack-per-tile, one-type-per-tile rule
  • Carry capacity = 1 stack, 1 type (multi-type carry is v2)
  • Spike (~1 hr): 16-chip grid mockup on a 720×1280 viewport — does it cram? Adjust before building.
  • Acceptance: 3 pawns chop / mine / haul to a stockpile. Set the stockpile to wood-only — pawns leave stone alone. Set a second stockpile to higher priority and watch wood flow upward.

Phase 5 — Building, walls, floors, containers (~2.53.5 weeks; was ~23, bumped for wood-wall art authoring)

Goal: the player can shape the world. End of phase: build a functional wooden cabin, with stone fortress walls available as the upgrade material.

  • Designation paint mode (controller reused later by stockpile-paint, no-roof, etc.) — drag-paints ghosts on Layer 3, green-if-placeable / red-if-blocked
  • BuildJob queue on World, with material requirements
  • Construction WorkProvider: nearest-job-first, hauls materials → walks to ghost → works N ticks → swaps Layer 3 ghost for real Layer 2 wall (autotile fixes neighbours) → updates pathfinder
  • Walls — wood (locked-in via 2026-05-10 audit):
    • Art: author corner / T-junction / cap / cross variants on top of FG_Houses.png warm-brown timber + blue-roof palette (~½ day pixel art). Bundle has the visual language; the modular pieces don't exist yet.
    • TileSet: Wang-style terrain definition in Godot, hand-painted variant assignment.
  • Walls — stone (autotile-solvable as-is):
    • Import FG_Fortress.png tan stonework directly. Build TileSet terrain (~few hours, mostly assembly).
  • WallMaterial enum / data path: wood vs stone is a tag on the BuildJob; the construction pipeline is identical for both. Wood unlocked from start; stone unlocked once player has stone resource (one-step craft from raw stone? — TBD in Phase 6, not here).
  • Floors: wood plank, stone, dirt-cleared
  • Doors: simple swing-open furniture; pawns walk through; pathfinder treats as walkable, walls don't
  • Containers (crates): furniture entity, 4 stacks, 16-chip filter, 5 priorities, all-neighbours-blocked fallback (locked: hold then drop after ~5 sim sec)
  • Deconstruction (reverse build job)
  • Acceptance: Player paints a 6×4 cabin outline → pawns haul wood → walls go up → floor + door → drop a crate inside → set crate filter to "tools" → tools auto-flow into it.

Phase 6 — Production: workbenches, recipes, bills, quality (~3 weeks)

Goal: crafting chains end-to-end with the full Rimworld bill semantics.

  • 5 workbenches: carpenter, smelter, smithy, cooking hearth, millstone
  • Recipe registrydata/recipes/*.tres, ~22 recipes per design.md
  • Recipe DSL: ingredients (with optional quality filter), product (count, type), workTime, skill (Crafting or Cooking), skillThreshold
  • Bill semantics:
    • Modes: one-shot count / forever / until-N-in-stockpile
    • Ingredient quality minimum filter (e.g. "Excellent+ iron ingots only")
    • Skill threshold gate
    • Bill round-trip in saves
    • "Bill blocked" alert when no ingredients qualify (open question — drives Phase 17 alerts)
  • CraftingProvider + CookingProvider (per architecture.md:559 9-list)
  • Quality system (Shoddy/Normal/Excellent/Masterwork/Legendary) — additive: skill × 0.04 + RNG; multiplicative stat bonus; quality stamped on every crafted item
  • Spike (~2 hr): prototype the recipe-as-Resource format. Does the bill UI fit in a single bottom-sheet? Adjust before authoring 22 recipes.
  • Acceptance: smelt iron, smith a sword. Watch quality vary by smith skill. Set a bill "until 5 swords in stockpile" — pawns stop at 5, restart when one is taken.

Phase 7 — Plants, cooking, hunger (~2 weeks)

Goal: food loop from seed to belly.

  • 34 crops: wheat, potato, berry-bush, hop (final picks TBD; ElvGames bundle pickings audit-driven)
  • Plant tile state machine: tilled → sown → growing (4 stages) → ready
  • Plant WorkProvider: till + sow + harvest (matches the 9-category list)
  • "Plants don't grow indoors" rule — depends on Layer-4 Roof flag, which doesn't exist yet at this phase. Stub it (always-outdoor) and revisit in Phase 13.
  • Cooking: hearth recipes (raw ingredient → meal), shelf-life on meals
  • Eating: pawn walks to nearest meal, consumes, hunger drops
  • Hunger need + thought (design.md mood section)
  • Acceptance: full grain → flour (millstone) → bread (hearth) → eat loop. Hungry pawn auto-prioritises eating.

Phase 8 — Sleep, mood, thoughts (~3 weeks)

Goal: pawns have an interior life. Mood swings drive behaviour.

  • Beds (furniture, "owned by pawn", quality affects sleep)
  • Sleep need + sleep-mood gradient (placeholder numbers +5/+0/2/5/8 from design.md Tunables)
  • Tired status, decision-pipeline override (sleep when low)
  • Mood thought registry — data-driven, ~13 thoughts per design.md
  • Mood compute (architecture.md:318): base 50 + sum(modifier × min(stacks, MAX_STACKS_PER_THOUGHT)). MAX_STACKS_PER_THOUGHT = 5 (locked).
  • Thoughts: persistent (state-driven) or event (decay over hours), with stacking rules
  • Soft breaks — Sulking and Wandering, fire when mood < 25 sustained for 30 in-game min, recover at mood ≥ 35
  • Mood UI: small mood bar on pawn portrait, breakdown in pawn-detail (Phase 17)
  • Acceptance: Force-create misery (cold, hungry, no bed, sees corpse): mood plummets, pawn enters Sulking, meet needs, recovers.

Phase 9 — Status effects + Medicine (~23 weeks)

Goal: the full status-driven drama from design.md Health section.

  • Status registry: Hungry, Tired, Bleeding, Sick, Downed, Wet (Damp/Soaked), Cold
  • Each status: trigger condition, decay rate, gameplay effect (move-speed, work-speed, mood thought, threshold-flips)
  • Bleed-out timer (BLEED_OUT_TICKS = 6 in-game hours from design.md:418, locked)
  • Doctor work category (already in 9-list; provider lands here)
  • Medical bed furniture
  • Treatment job: walk → fetch supplies → walk → treat (timed by Medicine skill)
  • Doctor interrupt prioritization — Combat=Off doctors still volunteer for medicine
  • Downed → rescue model: Downed pawns get a timer; doctor walks them to medical bed; if timer expires before bed, pawn dies
  • Acceptance: Wound a pawn → bleeds → second pawn breaks off work → carries to medical bed → treats → recovery thoughts fire. Try with no doctor available — pawn dies, watch the death pipeline run (Phase 14 closes the loop).

Phase 10 — Combat + Wolves (~3 weeks)

Goal: real threat, real defense, real consequences.

  • 3 weapons: sword (melee), axe (melee, slow), bow (ranged) — stats per design.md
  • 3 armor slots: helm, cuirass, boots
  • Equipment system on pawn (carry + active slots)
  • Hit math: two-roll resolution (hit roll → damage roll, armor reduces damage). Bonuses: skill ×5%, range ×5%, cover 40/20%. Numbers placeholder, tune in Phase 20.
  • Cover: walls = 40%, trees = 20%. Cover lookup is per-shooter, per-target line.
  • Combat priority semantics: Off ≠ "won't fight" — Off = "defends if cornered, won't volunteer" (per design.md:64)
  • Friendly fire ON — bow pawns can hit teammates in line
  • Wolf entity: Animal class, 4-state machine (APPROACH → ENGAGE → FLEE → DEAD per architecture.md:700)
  • Wolf spawn: storyteller-driven, at map edge, in packs of 14, night only, season-weighted (more in winter)
  • Spike (~half day): combat feel — 3 pawns vs 3 wolves on a small map. Does the two-roll resolution feel good? If not, dial numbers before Phase 20.
  • Acceptance: Wolf raid at night — pawns auto-fight (or flee per priorities) — some get downed — doctor saves who they can — bodies on the field.

Phase 11 — Day/night + Lighting (~12 weeks)

Goal: the world has a rhythm; night feels different.

  • Time-of-day clock (already in tick loop — surface to UI)
  • Top-bar clock + day counter ("Day 14, 6 AM")
  • Light sources: torch furniture, hearth furniture (already exists for cooking), candle. Each has radius (max 8) and on/off state.
  • light_map compute (per architecture.md:366) — recompute on light-source-change only, not every tick. Touches ≤ 200 cells per change.
  • Night shader: sample light_map, brighten lit tiles, darken unlit. Smooth dawn/dusk transition (~30 in-game min each)
  • "In darkness" mood thought integration — fires for pawns standing in unlit cells at night
  • Acceptance: Day → dusk → night → dawn cycle visible. Indoor lit areas glow; outdoor unlit areas are dim. Pawn in a dark room at midnight gets the mood thought.

Phase 12 — Seasons + Weather (~12 weeks)

Goal: 48-day year cycle with daily weather variety.

  • 48-day year: 4 seasons × 12 days. Seasonal palette modulate on tilemap (subtle).
  • Daily weather roll (design.md:582): clear / rain / storm / cold-snap, season-weighted (placeholder weights, tune Phase 20)
  • Rain visual + ambient sfx (sourced from bundle SFX packs)
  • Storm = rain + lightning flashes + dampness rate ×2
  • Cold snap = winter-only, applies Cold status faster
  • Wet status accumulation outdoors in rain (Damp at 25, Soaked at 60), decays indoors. Mood thought tiers.
  • Cold status in winter outdoors, slower decay than wet
  • Season indicator UI (top bar): "Spring 4/12", tap → forecast tooltip
  • Acceptance: Run 1 in-game year, see all 4 seasons cycle. Trigger rain — watch outdoor pawns get Damp, then Soaked. They head indoors and dry off.

Phase 13 — Rooms, roofing, beauty, dirtiness, cleaning (~23 weeks)

Goal: built-environment systems — your cabin matters now.

  • EnclosureDetector + RoomDetector (per architecture.md:967 and 982)
  • Auto-roof BFS (≤8 cells, per architecture.md:983) — sets Layer-4 Roof flag
  • No-Roof designation (paint mode) — courtyards stay open
  • room_too_large signal when BFS hits the cap on an enclosed area (locked decision from this session)
  • DECIDE: big-room UX (open question in memory.md):
    • (a) Keep ≤8 cap, surface "split with an interior wall" banner — minimal scope
    • (b) Bump cap to ~16, banner at the new threshold
    • (c) Detect any enclosed area regardless of size — bigger architectural shift
    • Recommendation lands here; deferring past Phase 13 means bugs.
  • Indoor tint driven by Roof flag — wires to the shader skeleton from Phase 1
  • Plants-don't-grow-indoors rule wires up properly (was stubbed in Phase 7)
  • Beauty score per cell, derived from nearby furniture × Quality multiplier
  • Dirtiness accumulation, traffic-weighted, spike events (blood from combat = +20, corpse decay = +5/h)
  • Cleaning WorkProvider (the 9-list category — earlier doc text said "8th"; that's stale)
  • Room thoughts: clean/dirty, beautiful/ugly, ate-without-table, slept-in-room
  • Spike (~1 hr): room detection on a stress map (50+ rooms). Does it stutter on rebuild?
  • Acceptance: Build a kitchen → mood reflects it (table, beauty). Bloody combat in bedroom → room turns ugly until cleaned. Build a 12-cell enclosed room → big-room banner fires (per (a)/(b)/(c) decision).

Phase 14 — Death, corpses, burial (~12 weeks)

Goal: close the death loop properly.

  • Corpse entity + decay timer: 050 fresh / 50100 rotting / 100 rotted
  • Graveyard stockpile — special filter: Corpses-only chip
  • Grave dig job (Manual Labor) — produces a grave slot
  • Permanent grave marker entity: tap → opens deceased-pawn detail (Phase 17)
  • Cremation pyre furniture + recipe (1 corpse + 5 wood → ash + brief mood thought for pawns nearby)
  • Mood thoughts: "saw corpse", "buried friend", "cremated friend", "rotting body in colony" (severity scales)
  • Death triggers in pawn pipeline (already wired in Phase 9) end here — corpse drops, hauler fires.
  • Acceptance: Pawn dies (combat or untreated illness) → corpse on the floor → graveyard zone painted → hauler takes corpse to grave slot → digger digs → marker placed. Tap marker, see deceased pawn's portrait + 1-line backstory + mood-thought legacy.

Phase 15 — Storyteller (~23 weeks)

Goal: the world prods the player without overwhelming them.

  • Event registry: 25 prompts authored in design.md ported to data/events/*.tres
  • Daily 6 AM roll — picks one event from a weighted pool
  • Weighted pool builder: trigger predicate, per-event AND per-category cooldowns (locked: both gates must pass), tension modifier
  • Cooldowns: per-event from event def; per-category from CATEGORY_COOLDOWN (3 days threats, 5 days wanderers, etc.)
  • Tension model: running tension score (0100), high tension reduces threat weight (×0.3), low tension boosts (×2.0)
  • State-triggered events ("First Beds" while no beds exist) at higher weight than random
  • Banner UI (ambient, dismissible, no pause) for nudges/seasonal/lore
  • Modal auto-pause for wanderer/threat/disease/milestone (player choice)
  • "Go there" jump-to-alert integration — every alert/banner includes the camera-pan tap (locked)
  • Ghost state + Wanderer event recovery — when all colonists dead/gone, sim half-speed, wanderer fires in 35 days
  • Acceptance: Play a full season, all event categories fire at least once. Trigger ghost state by killing all 3 pawns — wanderer arrives within the window.

Phase 16 — Save/load full coverage (~12 weeks)

Goal: the save round-trip from Phase 3 expanded to every system. Mid-tick suspend safe.

  • All entity types serialize (pawn, item, furniture, container, corpse, wolf, plant tile)
  • Tilemap layers serialize via get_used_cells_by_id
  • Storyteller state (current tension, recent-fired log per event + per category, scheduled events)
  • Bill states (mid-fetch, mid-craft)
  • Pawn deep state: thoughts, statuses, equipment, current job + JobRunner toil index
  • Autosave on suspend (mobile platforms — NOTIFICATION_APPLICATION_PAUSED)
  • "You've been away X minutes" toast on resume (no fast-forward in MVP)
  • Slot management: single slot for MVP, manual save + autosave file
  • Save version number; load barfs gracefully on mismatch
  • Acceptance: Kill the app mid-anything (mid-haul, mid-craft, mid-bleed-out, mid-storyteller-modal). Reopen. Everything resumes seamlessly. No exceptions, no visual desync.

Phase 17 — Touch UX completion (~34 weeks)

Goal: every interaction has a touch path. No desktop-only gestures.

  • Work-priority matrix (9 cols × N pawns, sticky pawn-name column, horizontal scroll on phone, tap-to-cycle priority, long-press 5-chip picker, swipe-column bulk-set)
  • Per-pawn / per-job views layered on the matrix
  • Stockpile / container UI — 4×4 chip grid, priority cycle, allow/forbid all
  • Build drawer — bottom-sheet tabs (Walls / Floors / Furniture / Production / Designate). Material-pick UI when multiple materials match.
  • Storyteller event modal vs ambient banner UX
  • Pawn detail screen (bottom-sheet, full-height): needs bars, status effects, current job, equipment, mood thoughts breakdown, skill table, deceased-state
  • Settings — speeds, auto-pause toggles, audio volumes, accessibility
  • Day-summary card — recap at end-of-day, gives short sessions a stopping point (ui.md:620)
  • Alerts log + storyteller event history
  • Bill UI for workbenches (created in Phase 6 stub; full UX here)
  • "No stockpile accepts X" alert from Phase 4 hauling fallback wires up here
  • "Bill blocked" alert from Phase 6 quality-filter wires up here
  • Acceptance: every screen in ui.md "Screens still to design" exists and is touch-driven. Hand the device to someone who's never played — they can navigate without instruction.

Phase 18 — Audio (~1 week)

Goal: the game has soundscape; not silent.

  • Ambient day loop, ambient night loop (bundle music packs)
  • UI clicks (tap, long-press confirm, error)
  • Combat stings: hit, miss, downed, kill
  • Alert stings: storyteller modal, ambient banner, raid warning, pawn-down
  • Volume sliders in Settings (master / music / sfx / ambient)
  • Audio mute on suspend; fade in on resume
  • Acceptance: play through a normal in-game day. Sounds fire at the right moments, mute toggles work.

Phase 19 — Onboarding & first-60-seconds (~12 weeks)

Goal: resolve the open question in memory.md — a new mobile player gets productive in <60 seconds.

  • DECIDE: approach (open question, not enough thinking yet):
    • (a) Hint system — contextual tooltips during first session, dismissible, no replay
    • (b) Guided first-day — scripted storyteller events on day 1 walking through chop / haul / build / sleep
    • (c) Tutorial scene — separate from main game, opt-in
    • Recommendation lands when this phase begins.
  • First-time-player flag persisted
  • Acceptance: hand the game to a tester cold. They are doing useful colony work within 60 seconds without you saying anything.

Phase 20 — Balance, polish, export (~24 weeks)

Goal: ship-ready.

  • Tune all placeholders (memory.md Tunable list):
    • Sleep mood gradient +5/+0/2/5/8
    • Wet thresholds 25 / 60 + accumulation rates
    • Season weather weights
    • Hit-chance bonuses (skill ×5%, range ×5%, cover 40/20%)
    • Bleed-out timer (6h)
    • Mood thought magnitudes + decay times
  • iOS export setup (needs Mac/Xcode — this is the long tail)
  • Android export from Linux
  • Steam Deck input parity — open question (memory.md): gamepad-cursor or D-pad menus or both?
  • Credits screen — every art pack, every audio pack, every font (compiled across all phases)
  • Bug pass — known issues from each phase's parking lot
  • Performance pass — profile on a real low-end Android device
  • Acceptance: TestFlight build for iOS, signed APK for Android, Steam Deck verified launch. Credits screen complete.

Out of scope (v2+ / explicit cuts)

These are not in MVP. Pulling any of them in adds weeks. Each is a known v2 candidate.

  • Procgen maps (MVP: fixed seed)
  • Multiple biomes (MVP: temperate forest only)
  • Bandit raids (MVP: wolves only)
  • Butchering animals for meat
  • Surgery / limb damage / specific body parts (MVP: single HP + status)
  • Background simulation when app is backgrounded
  • Fast-forward on long absence
  • Tech / research progression tree
  • Pawn name / backstory generator (MVP: hand-curated list)
  • Multi-pawn carry, multi-type carry
  • Per-bench ingredient radius restriction
  • Localization beyond English (architecture supports it; content stays EN)
  • Post-launch monetization decisions (premium / PWYW / free)
  • Multiplayer in any form
  • Pets / tame animals
  • Trade caravans
  • Drugs / alcohol / festivals
  • Prisoner mechanics

Scope-cut levers

If 612 months calendar is too long, these are sane reductions ranked by gameplay-cost-per-week-saved:

Cut Saves Cost
Drop seasons & weather (keep day/night) ~2 wk Big — kills atmospheric variety
Drop dirtiness + Cleaning category ~1 wk Small — but loses one mood lever
Drop combat entirely (no wolves) ~3 wk Huge — only quiet events left
Drop quality system (everything is Normal) ~1 wk Medium — flattens late game
Drop cremation, keep burial only 0.5 wk Tiny
Reduce skills 5 → 3 (Labor, Combat, Medicine) 0.5 wk Tiny — tightens design
Drop room beauty score (rooms still detected) 0.5 wk Small — loses one mood lever
Single workbench instead of 5 ~1.5 wk Big — collapses production design
Drop ranged weapons (sword/axe only) ~0.5 wk Small — loses cover-relevance

De-risking spikes (run before the relevant phase)

Spike Phase Effort Question to answer
AStarGrid2D timing at 80² with 6 concurrent path queries Phase 2 ~30 min Sub-millisecond per query?
16-chip filter UI mockup on phone viewport Phase 4 ~1 hr Does it cram or fit cleanly?
Recipe-as-Resource format prototype Phase 6 ~2 hr Does the bill UI fit a single bottom-sheet?
Combat feel test (3 vs 3 on small map) Phase 10 ~half day Does two-roll resolution feel good?
Room detection on stress map (50+ rooms) Phase 13 ~1 hr Does rebuild stutter?

How to use this doc going forward

  • At session start: check the Status row at top, then jump to the current phase. Read its goal + current open boxes.
  • During a session: tick boxes as they complete. If the work uncovers something not on the list, add it as a new box (don't silently expand scope across phases).
  • At session end: if a phase rolled over, update the Status row and add a ### YYYY-MM-DD entry to memory.md Session log noting what landed.
  • DECIDE points (currently in Phases 13 and 19): when you hit one, propose options + pick before continuing past it. Don't paper over.
  • Spikes: treat the de-risking spikes as bona-fide tasks, not optional. Skipping them invites the cost-per-week to balloon.

What lives elsewhere

  • Game design / mechanics — see design.md.
  • Tech / engine layout / pawn AI — see architecture.md.
  • Touch UI / camera — see ui.md.
  • Tilesets / art / license — see art.md.
  • Decisions index / open questions — see memory.md.