# memory — rimlike Durable memory for this project. Read at session start, update before session end. Date format: `YYYY-MM-DD`. A 2D, tile-based **cute-farming-RPG-meets-colony-sim** — Rimworld DNA, Going Medieval × Stardew lodestars — shaped for mobile and handheld. Promoted from `~/claude/ideas/rimlike` on 2026-05-10 after a single deep brainstorm session. Realistic MVP timeline: **3–6 months solo**. ## How to read this project `memory.md` is the index of decisions and open questions. The deep specs live alongside in `docs/`: | File | Contents | |---|---| | [`docs/design.md`](./docs/design.md) | Game design — core loop, simplifications, skills, statuses, mood, weather, stockpiles, production, combat, death/burial, storyteller corpus | | [`docs/architecture.md`](./docs/architecture.md) | Tech — pawn AI / job system, time/tick model, Godot 4 engine layout, TileMap split, all subsystems (mood, lighting, rooms, hauling, production, combat, storyteller) | | [`docs/ui.md`](./docs/ui.md) | Touch UX — work-priority matrix, stockpile/container screens, mood/lighting/rooms cues, combat banners, storyteller event UI, screens still to design | | [`docs/art.md`](./docs/art.md) | Owned assets (ElvGames bundle primary, Ventilatore secondary), license, autotile gotcha, audit list, candidates kept for record | | [`docs/implementation.md`](./docs/implementation.md) | **Phased build plan** — 21 phases (audit → P0 scaffold → … → P20 export). Checklists, acceptance demos, scope-cut levers, de-risking spikes. Track progress here. | When working on a feature, read `memory.md` first, then the relevant `docs/` file(s). For "what do I build next?" check the **Status** row at the top of `docs/implementation.md`. ## Decisions & rationale Distilled from the brainstorm. Each lock has a "why" — change with deliberate intent. ### Pillars | Decision | Choice | Why | |---|---|---| | **View** | Top-down grid for gameplay (pathfinding, designation, floor) **+ 3/4-perspective rendering for vertical structures** (walls, doors, furniture). | Re-decided 2026-05-10 after exhausting the asset library: every wall pack we own is RPG-style perspective (Stardew / Going Medieval style), not Rimworld-style top-down. Pivoting the renderer (Y-sorted entity sprites for walls) makes the entire library usable as-is and replaces the "we need to author or commission" bottleneck. Gameplay grid + pathfinding stay identical; only the rendering of vertical structures shifts. | | **Wall layer rendering** | Walls are **entity sprites** (Sprite2D / Y-sorted Node2D), not TileMap cells. `Wall` TileMap layer (Layer 2) becomes data-only — used for pathfinding-impassable + room-detection BFS, but doesn't render. | Same source as the view-style pivot above. Consequence: doors, crates, furniture all live as entities with Y-sort; floor and designation-paint still tile-based. Architecture.md TileMap-layer section needs an annotation. | | **Primary platforms** | iOS + Android touch, then Steam Deck / ROG Ally gamepad. Desktop falls out for free. | Mobile is the hard constraint; Deck inherits. | | **Ambition** | itch.io + TestFlight release. Real artifact, small audience. | Drives engine choice; no app-store polish-tax. | | **Engine** | Godot 4 (GDScript) | 2D-first, free, exports everywhere we need, fast iteration. | | **Tile size / style** | 16×16 pixel art, **cute-farming-RPG primary** (ElvGames bundle), Ventilatore as medieval accent | ElvGames "Ultimate Farming RPG" Humble bundle owned (~2.8 GB, 70+ packs). Tone: Stardew × Going Medieval × Rimworld. | | **Setting** | Medieval fantasy with cute palette | Owned-art alignment + clearer scope (no electricity / hydroponics / energy weapons) + underexplored on mobile. | | **Run shape** | Open-ended sandbox, autosave, persistent world | Player picks up where they left off. | | **Session length** | 5–15 min target | Drives every UI and pacing choice. | | **Default speed** | Fast (5×), with auto-pause on event | 1 in-game day ≈ 5 min real time. Solves the "watch a pawn walk for 60s" problem; 1× exists for combat / fine work. | | **Goal scaffolding** | Light storyteller prompts | Soft, dismissible nudges give each short session shape without forcing scenarios. | | **Combat** | Realtime with auto-pause on threat | Rimworld-feel; touch-friendly. | | **Health** | Single HP per pawn + status effects (Bleeding/Sick/Tired/Hungry/Wet/Cold/Downed/...) | ~80% of the drama for ~5% of Rimworld's health-system code. | | **Scale** | 3–6 pawns, **80×80** MVP map (architecture sized to ~120² ceiling) | Roughly Stardew-farm size; readable when zoomed in, doesn't fit a phone screen — forces the world-view camera (pinch / pan / jump-to-alert) rather than strategic-overview-on-phone. | | **Priority levels** | 5 (Critical / High / Normal / When idle / Off) | Matches Rimworld + Going Medieval contract. | | **Failure state** | Ghost colony — no game over; storyteller drops wanderer in 3–5 days | Mobile-friendly; preserves player investment. | | **Engine version** | Godot **4.6.2 stable** (Win64 binary at `D:\godot\Godot_v4.6.2-stable_win64.exe`) | Locked for reproducibility; pinned in `project.godot` features. | | **Renderer** | GL Compatibility (mobile + desktop), **not** Forward+ | Max device reach; Forward+ would lock out older phones. | | **Repo location** | Physical: `/mnt/d/godot/rimlike/` (D: drive, fast for Windows-side editor). Symlink: `~/claude/projects/rimlike` → physical. | Mirrors tavernkeep's pattern. Both WSL and Windows access without crossing the WSL net bridge. | | **Player walls** | **Wood + stone via Pixel Crawler `Walls.png`** (entity sprites, Y-sorted; no autotile in Phase 5). Single-sprite-per-material is the Phase 5 ship; per-direction variants are a polish item. | After the rendering pivot to 3/4 perspective, the Pixel Crawler Walls.png pack becomes directly usable. It has 4 wood materials + a sandstone variant with clear corner/edge pieces. Phase 5 ships with one sprite per material (uniform-looking walls); Phase 17 can add per-direction variants if playtest reveals the visuals feel flat. Stardew-cabin warmth restored without authoring or commission. | ### Architecture (tech) - **Sim tick 20 Hz, render 60 Hz, decoupled.** Pawn positions lerp between sim ticks. (`docs/architecture.md` Time / tick model) - **Pawn AI: 5-layer pipeline** (Decision → WorkProvider → Job + JobRunner → Status interrupts → Player overrides). Slimmer than Rimworld's ThinkTree. ~800–1500 LOC GDScript for full MVP. - **TileMap layers**: 0 Terrain · 1 Floor · 2 Wall · 3 Designation · 4 Roof · 5 Fog. Furniture / Pawn / Item / EffectFX as scene-instanced entities (not TileMap). - **Pathfinding**: `AStarGrid2D` (built-in), updated on wall/door/furniture changes. - **No background simulation** — app backgrounded = sim paused. Avoids "lost colony to a raid while at work." - **Save format**: between sim ticks only; JobRunner mid-toil state round-trips from day one. ### Game design - **5 skills** (Manual Labor / Crafting / Cooking / Medicine / Combat), 0–10 each, level by use, multiplicative speed/quality bonus. Skills modify duration and quality, never permission. - **9 work categories** (Construction / Mining / Hauling / Cleaning / Crafting / Cooking / Plant / Doctor / Combat). 5-level priority matrix per pawn. - **Storage**: floor zones AND independent crate furniture (4 stacks each), unified by `StorageDestination` interface. **16 filter chips** (Wd/St/Ir/Cu/Ag/Au/Cl/Veg/Mt/Gr/Ck/Md/Tl/Wp/Ar/Co), 5 priorities with Rimworld flow semantics. One stack per tile, one type per tile. - **Production**: 2-step where medieval-sense (Iron→Ingot→Weapon, Grain→Flour→Bread); 1-step otherwise. 5 workbenches (Carpenter, Smelter, Smithy, Cooking hearth, Millstone), ~22 recipes. Full Rimworld bill semantics (one-shot count / forever / until-N + ingredient-quality filter + skill threshold). - **Quality system** (Shoddy/Normal/Excellent/Masterwork/Legendary) on every crafted item; multiplicative stat bonus. Quality from crafter skill + RNG only (inputs are just resources). - **Mood**: ~13 thoughts (data-driven registry, mix persistent + event-driven). Soft breaks at sustained mood < 25 for 30 in-game min — Sulking or Wandering, recover at mood ≥ 35. - **Lighting** (real shader at night), **auto-detected rooms** (named by furniture, scored for beauty + dirtiness), **dirtiness + Cleaning** (8th work category), **beauty score** with Quality multiplier. - **Roofing**: indoor = Layer-4 Roof flag, sim-data only (no rendering, just an indoor tint on floors). Auto-roof when walls enclose ≤8 cells (BFS); No-Roof designation for courtyards. Plants don't grow indoors. - **Weather**: 4 types (Clear / Rain / Storm / Cold snap), daily roll, season-weighted. Wet status accumulates outside in rain, decays indoors. 4 seasons × 12 days = 48-day year. - **Combat**: 3 weapons (sword/axe/bow), 3 armor slots (helm/cuirass/boots). Walls + trees provide cover. Two-roll resolution (hit, then damage with armor reduction). Downed-then-rescue death model; doctors auto-prioritize. Combat=Off "defends if cornered, won't volunteer." Friendly fire ON. - **Death / corpses**: Both burial AND cremation. Graveyard = special stockpile (Corpses-only); pawns dig graves, place permanent grave markers (tap → deceased pawn-detail). Cremation pyre = furniture with single recipe (1 corpse + 5 wood). Corpses decay 0–50 fresh / 50–100 rotting (no butcher) / 100 rotted. - **Storyteller**: 25 prompts written in `docs/design.md`. Daily 6am roll, weighted pool, per-category cooldowns, tension model alternates quiet/threat per Tynan Sylvester pacing. Ambient banner for low-stakes, modal auto-pause for choice events. ### Touch UX - **Bottom-sheet menus** instead of right-side panels. **Long-press** = inspect/context. **Tap world** = select. Speed/pause buttons fixed top. - **Work-priority matrix**: 9 columns × N pawns, sticky pawn-name column, horizontal scroll on phone. Tap-to-cycle priority, long-press for 5-chip picker, swipe column for bulk-set. Per-pawn and per-job views layered on top. - **Stockpile/container UI**: 4×4 chip grid for the 16 filter categories; same UI for floor zones and crates. - **Storyteller events**: ambient banners for nudges/seasonal/lore; modal auto-pause for wanderer/threat/disease/milestone. Events log + "while you were away" digest at resume. - **Indoor tint** marks "this is inside" without needing roof rendering — matches Stardew/Rimworld convention. - **World-view camera**: pinch-zoom + drag-pan + double-tap-to-center; selected pawn does **not** force-follow. Storyteller alerts/banners include a *Go there* tap that pans the camera to the event tile. **No minimap in MVP** — revisit if playtest shows people getting lost on the 80² map. ### Art strategy - **ElvGames "Ultimate Farming RPG" Humble bundle** is the primary art (`/mnt/d/godot/assets/humble set new/`, ~2.8 GB, 70+ packs, all 16×16, ElvGames license: commercial OK with credit). - **Ventilatore Fantasy Tileset Complete Bundle** stays as medieval accent for biome variety / decorative props / animated water. - Mana Seed series considered but **dropped** from buy plan (the bundle covers what we'd planned). - **Authoring still required**: designation overlays (~2 hrs custom), possibly autotile bits on bundle wall sheets, possibly wolf sprite, possibly grave marker. ## Known bugs / triage backlog Reported from playtest. Triaged but not yet fixed. Plan: knock out as a bug-triage patch out-of-phase before Phase 18 (Audio) — same shape as the 2026-05-12 PC-controls patch. - [x] **[HIGH] Torches don't build when placed.** Reported 2026-05-15, fixed 2026-05-15. Root cause: Torch/Bed/Crate/Workbench/CremationPyre didn't call `World.register_build_site(self)` in `_ready` (only Wall/Floor/Door/GraveSlot did). ConstructionProvider iterates `World.build_queue` so the unregistered entities were never offered as jobs. The seeded cabin pre-built everything via `_spawn_complete_*` helpers, masking the gap until a player painted a fresh furniture designation. Fix: added register_build_site / unregister_build_site to all five entity types. - [x] **[HIGH] Two of three pawns sit idle, one does all work.** Reported 2026-05-15, fixed 2026-05-15. Root cause: pawns get stranded on tiles that became impassable mid-walk. Sequence: pawn-A walks a path computed pre-wall; pawn-B builds a wall on a tile in pawn-A's path; pawn-A keeps stepping along the now-stale path and lands on the wall tile; pathfinder requires a walkable START, so all subsequent find_path calls from pawn-A return empty → pawn-A is idle forever. The Phase 6 wall-trap-bug fix only protected the BUILDING pawn (adjacent-stand); bystanders + walk-through cases weren't covered. Two-part fix: (a) Wall._complete dislodges any pawn standing on the tile via new `Pathfinder.find_nearest_walkable` BFS helper; (b) Pawn._advance_walk re-checks `next_tile` walkability before snapping `tile`, aborts walk + cancels job + lets Decision reroute. Defense in depth. - [x] **[MED] Pawns render behind floor tiles.** Reported 2026-05-15, fixed 2026-05-15. Root cause: Floor entity anchored origin at tile-center (`tile.y*16+8`), same Y as Pawn — Y-sort tiebreak fell to scene-tree order, Floor (spawned later) drew over Pawn. Fix: moved Floor origin to top-of-tile (`tile.y*16`) so Floor.y < Pawn.y under Y-sort; _draw offsets compensate (rect spans y=0..TILE_SIZE_PX instead of -half..+half). Older bugs noted in passing but never fixed: - [ ] **[LOW] Bed-claim failure for 2/3 pawns when beds are free** (logged 2026-05-11). `Bram bed claim failed at /root/Main/World/Bed — sleeping on floor` even when beds free. Doesn't gate progress; needs sleep-system audit. - [ ] **[LOW] Save mid-INTERACT/mid-BUILD restarts toil from 0 on load** (Phase 16 known acceptable gap). Walk toil round-trips; multi-step interact does not. Tolerable per Phase 20 tuning note. ## Open questions / TODOs ### Audit / unblock-the-prototype action items Total ~75 min. **3 of 5 closed on 2026-05-10**; see session log + `docs/art.md` for findings. Two open. - [ ] **Aesthetic harmony test** — needs your eye. ElvGames Forest tile vs Ventilatore tile, side-by-side. Decides whether Ventilatore stays as accent or gets shelved. ~15 min. - [x] **ElvGames autotile audit** — done. **`FG_Fortress.png` IS autotile-solvable** (tan stone, ~20–30 modular pieces); **`FG_Houses.png` is NOT** (pre-built decorative house compositions, not modular wall variants). Iconic Homestead $19.99 fallback **not needed**. New decision required — see Open questions below. - [x] **Wolf sprite source** — done. **No wolf in the bundle anywhere.** Need a custom commission, a CC0 sprite, or a Ventilatore find. New decision required — see Open questions below. - [x] **Grave marker source** — done. `Retro Graveyard 16x16 Tileset [Kingdom Explorer]` confirmed in Tier 3, full graveyard suite. Direct use, no custom work. - [ ] **License compilation** — maintain credits string for every pack used (ElvGames + Ventilatore + Kingdom Explorer + any others). Display in game's credits screen. Confirm specific terms per pack before any commercial release. ### Design topics still open **Resolved: wall material strategy.** Lock = **custom-author wood + stone walls** (option b). Player builds wood-cabin walls from day one (corner/T/cap variants authored on top of `FG_Houses.png` wood-and-blue-roof material, ~½ day work) then upgrades to stone fortress walls later (`FG_Fortress.png` autotile-solvable as-is, ~few hours). Phase 5 slips ~3 days but the Stardew-cabin-warmth aesthetic survives, which was the original anchor. See `docs/art.md` Wall-material decision. - [ ] **Wolf sprite acquisition** (NEW from 2026-05-10 audit): bundle has nothing canine-predator; **Ventilatore checked 2026-05-10, also nothing** (Ventilatore is character + terrain + decoration; no animal/creature sprites at all besides player + slimes). Remaining options: (a) commission a 16×16 wolf (idle + 2–4-frame walk × 4 directions; ~$30–60 paid commission); (b) source a CC0 wolf from OpenGameArt; (c) reskin a bundle animal placeholder for MVP (e.g. recolor a dog from `Animal Sprites Pixelart`) and replace later. Phase 10 blocker — can defer until then; placeholder from option (c) unblocks the combat/AI work meanwhile. - [ ] **Auto-roof big-room UX**: the ≤8-cell BFS cap silently fails on rooms larger than 8×8. Decide whether to (a) keep the cap and surface "this area is too large to roof — split with an interior wall" hint when the player encloses one, (b) bump the cap to ~16 with the same hint at the new threshold, or (c) detect any enclosed area regardless of size. Affects `EnclosureDetector` + a new room-feedback UI. - [ ] **Onboarding / first 60 seconds**: mobile-specific. Don't copy Rimworld's tutorial. - [ ] **Touch UI for non-priority screens**: world view, build drawer, alerts, day-summary, pawn detail. See `docs/ui.md` "Screens still to design." - [ ] **Background time / "while you were away" mechanic** — locked to no background simulation in MVP; revisit if it feels bad in playtest. - [ ] **Audio direction** — who/where for SFX + ambient track? Bundle has 11 music + 8 SFX packs covering most needs. - [ ] **Steam Deck input parity** — gamepad-driven cursor, or full menu navigation by D-pad? Probably both. - [ ] **Localization stance** — English-only for MVP, but architect strings for i18n (already locked in CLAUDE.md). - [ ] **Pawn name/backstory generation** — hand-curated list vs simple generator. - [ ] **Naming the game** — "rimlike" is a working title. - [ ] **Monetization stance** — free? PWYW? Premium? - [ ] **Tech / research progression** — medieval tech tree shape. - [ ] **Map / world generation** — fixed seed for slice; procgen later. ### Tunable in prototype (not real "open Qs", just numbers to playtest) - Sleep mood gradient values (`+5/+0/−2/−5/−8`) - Wet status thresholds (25 / 60) and accumulation rates - Season weather weights (Spring/Summer/Autumn/Winter distributions) - Hit-chance bonuses (skill ×5%, range ×5%, cover 40/20%) - Bleed-out timer (6 in-game hours) - Various mood thought magnitudes and decay times ## Vertical slice (MVP target) Same scope as locked in `~/claude/ideas/rimlike/plan.md`. Realistic timeline 3–6 months solo for an MVP this rich. - **1 biome** (temperate forest, ElvGames Forest Tileset 4 Seasons; Ventilatore foliage accents). - **1 map**, **80×80 tiles**, fixed seed for now. - **3 starting pawns** ("settlers" / "villagers"), each with name + portrait + one-sentence backstory. - **Verbs**: chop wood, mine stone & ore, build walls/floors/furniture/crates, plant/harvest crops (3–4 types), cook meals (recipes), haul, repair, clean. - **Needs**: hunger, sleep, mood (~13 distinct thoughts, soft breaks at sustained mood < 25). - **Status effects**: Hungry, Tired, Bleeding, Sick, Downed, Wet (Damp/Soaked), Cold. - **Storage**: floor zones AND containers, 16 filter chips, 5 priorities. - **Quality system** on all crafted items. - **Lighting** — torches + hearths emit light; visual darkness at night. - **Rooms** — auto-detected, named by contents, beauty + dirtiness scored. - **Cleaning** as 8th work category. - **Day/night cycle**, ~5 min per day at default speed. **Seasons** (Spring/Summer/Autumn/Winter, 12 days each). - **Weather**: Clear / Rain / Storm / Cold snap. - **One disaster type**: wolves at night (bandit raids deferred). - **One storyteller**: random quiet/threat alternation, 25 prompt corpus. - **Save/load**, autosave on suspend, single slot. - **Touch UI** end-to-end (no desktop-only gestures). - **Sound**: minimal — UI clicks, ambient day/night loop, alert sting. ## Session log ### 2026-05-10 - Promoted from `~/claude/ideas/rimlike/` (single multi-hour brainstorm session). - Scaffolded `projects/rimlike/` from `_templates/project/`. Customized `CLAUDE.md`. Distilled `plan.md` into this `memory.md`. Moved companion files (design / architecture / ui / art) into `docs/`. `git init`, first commit "Initial scaffold". Created Forgejo repo `rimlike` (private), set HTTPS remote, pushed `main`. Archived idea folder to `~/claude/archive/ideas/rimlike/`. - Wired Godot MCP Pro: `.mcp.json`, `.claude/settings.local.json` allowlist, three project-local subagents (`quick-edit`, `researcher`, `gdscript-refactor`). Added the MCP-Pro and tiered-delegation sections to `CLAUDE.md`. Editor plugin / `addons/godot_mcp/` re-copy still pending Godot project scaffold. - Reviewed `docs/` against `memory.md` and `CLAUDE.md`. Decisions made: - **Map size bumped from 40² → 80²** for the MVP slice; architecture sized to ~120² ceiling. Pawn count stays at 3 start / 6 cap (frontier feel was rejected in favor of "split-the-difference" sizing — Stardew-farm scale, not Going-Medieval scale). - **World-view camera** = pinch-zoom + drag-pan + double-tap-to-center; storyteller alerts include a *Go there* tap. No minimap in MVP. No follow-cam (avoids fighting the player when issuing build orders across the map). - Storyteller cooldown semantics: **per-event AND per-category, both gates must pass** (resolves the design.md vs architecture.md disagreement). - `MAX_STACKS_PER_THOUGHT = 5` (was named-but-unset). - Hauling-loop fallback: items that fail to find any valid destination after 3 retry passes drop on the floor and surface as a passive "No stockpile accepts X" alert. - Cleaned up doc rot in `docs/design.md` and `docs/architecture.md` — both had stale "8 work categories" intermediate sections still next to the canonical "9" lists. Removed the (8) snapshots. - Updated `docs/architecture.md` perf assumptions (60² → 80² with 120² ceiling). - `docs/ui.md` got a new **World view camera (locked)** section; removed the world-view bullet from "Screens still to design". - Open: auto-roof big-room UX (added to TODOs above) — the ≤8-cell BFS cap silently fails on bigger rooms; player feedback path needs a decision before EnclosureDetector lands. - Wrote `docs/implementation.md` — 21-phase build plan with checkboxes, acceptance demos, scope-cut levers, and de-risking spikes. Status row at the top of that file is the canonical "where are we now" pointer. - **Project location moved** from `/home/megaproxy/claude/projects/rimlike/` to `/mnt/d/godot/rimlike/` (D: drive). Symlink at the original WSL path preserves the home-CLAUDE.md layout convention. Set `git config core.filemode false` to silence DrvFs's everything-is-0777 false-positive. Mirrors tavernkeep's pattern; both WSL and Windows access without crossing the WSL net bridge. - **Phase 0 scaffold landed.** `project.godot` + 7 autoloads + smoke-test scene + addons re-copy + folder layout. Used **GL Compatibility renderer** (not Forward+) for max mobile reach. Folder layout matches tavernkeep (`autoload/` at root, scripts co-located in `scenes/`) — *not* the `scripts/autoloads/` mirror layout originally sketched in `implementation.md`. Headless verification: `godot --headless --path . --quit` exits 0 with the smoke-test message. Editor-side green-dot check pending — needs you to open the editor once. - **Asset audit ran via researcher subagent (Haiku).** 3 of 5 items closed. Findings: - `FG_Fortress.png` autotile-solvable (tan stone). `FG_Houses.png` NOT autotile-solvable (pre-built decorative compositions). Iconic Homestead fallback not needed. **Opens a new wall-material decision** (see Open questions). - No wolf sprite anywhere in the bundle. **Opens a wolf-acquisition decision** (see Open questions). - `Retro Graveyard 16x16 Tileset [Kingdom Explorer]` confirmed in Tier 3 — full graveyard suite, ready for direct use in Phase 14. ### 2026-05-11 - **Phases 1–11 all shipped between 2026-05-10 and 2026-05-11** (the in-context session log lagged behind the commits). See `docs/implementation.md` Status table for canonical phase-by-phase state; below is just the headline. - **Phase 9 (Status + Medicine) + Phase 10 (Combat + Wolves) shipped as the "drama pair"** via 3-agent fan-out (Agent A: HP/Status on pawn, Agent B: DoctorProvider + medical bed + Rescue/Treat toils, Agent C: Wolf entity + WolfSpawner). Opus integrated into `scenes/world/world.tscn` + `world.gd` (middle bed at (47,24) marked `is_medical=true`). - **MCP runtime verified both phases.** Phase 9: injected 75 dmg + Bleeding(2) into Bram → went Downed (hp 25) → Edda+Cora both volunteered `doctor → 'Rescue Bram → bed at (47, 24)'` → treated → Bram healed to 94.2 hp, statuses cleared, returned to work. Phase 10: `[wolf] RAID: 1 wolf(ves) spawned at [(53, 2)]` fired at day 3 22:00 (darkness ≥ 0.8); 4 wolves alive across raid cycles by day 4 01:51. Screenshot confirms medical-bed red-cross marker and wolf silhouettes at night. - **Phase 10 deliberately partial** — wolf-side combat (two-roll, bleed on hit) shipped, but pawn-side weapons/armor/cover/friendly-fire deferred. Acceptance demo's full chain (wolf → bites → pawn bleeds → doctor saves) awaits player weapons. WolfSpawner pack size 1–2 vs design target 1–4 — tune up post Phase 20 numbers pass. - **Bleed-out timer shipped at demo value** `BLEED_OUT_TICKS = 1200` (~ minutes) instead of design value `432000` (6 in-game hours). Documented in `status_catalog.gd`; flip on first time-balance pass. Recorded so it doesn't ship to a release at the demo value. - **Bed-claim bug noticed in passing** during ULTRA-speed run: `Bram bed claim failed at /root/Main/World/Bed — sleeping on floor` for two of three pawns even when beds free. Doesn't gate Phase 12; logged for separate triage. - **Phase 12 (Seasons + Weather) shipped same day.** Three-agent fan-out (Agent A = clock seasons + season top-bar + terrain palette; Agent B = Weather autoload + procedural rain overlay + storm flash; Agent C = Wet/Cold statuses + mood thoughts + shelter check). Opus prepped contracts up front (event_bus signals, Clock season constants, Weather autoload stub) so all three could run fully parallel. - **Pre-fan-out contract pattern was the key.** By writing the public API surface to disk *before* dispatching agents, each agent's slice compiled standalone and integrated on first try. Worth repeating for any future "multi-system phase" — pay the 5-minute scaffolding cost, save the round-trip merge fixup. - One quick-edit fixup needed: Variant inference errors on `var old_sev := s.severity` (untyped Array loop var). Same trap as the Phase 7 crop fix. Pattern: always explicit-type any `:=` declaration assigned from a non-typed-Array element. - **MCP runtime verified all paths.** Top-bar shows "Spring 1/12", rain droplets render across screen, storm white-flash caught mid-animation, wet status flipped 0→Damp(26)→Soaked(65) with mood thought sync, cold status fired on cold_snap with -4 mood. `_is_sheltered()` proxy (has floor) works for v1; Phase 13 Room BFS replaces it. - Next: Phase 13 (Rooms, roofing, beauty, dirtiness, cleaning) is the natural follow-up — it pays the `_is_sheltered()` debt and unlocks beauty/dirty mood thoughts. - **Phase 13 (Rooms + Beauty + Dirtiness + Cleaning) shipped same day.** Three-agent fan-out reusing the Phase 12 contracts-first pattern (`Room` class, `World.rooms`/`room_at_tile`/`is_indoor`, 4 EventBus signals pre-written by Opus before dispatch). - **DECISION: Big-room UX = bump cap to 16, banner above.** Auto-roof activates for rooms ≤16 interior tiles; rooms above that emit `room_too_large` with no roof. Cabin (24 tiles) intentionally exceeds cap so the warning path is exercised at boot. Test shed (3×3 = 9 tiles) exercises the roof path. - **Agent C wiring loss + recovery.** Agent C reported wiring `BeautySystem` / `DirtinessSystem` / `CleaningProvider` / `IndoorTintOverlay` into `world.gd` but the instantiation code never landed — only the autoload field declarations and the entity-side `register_furniture` hooks survived. Opus added the missing 4 instantiations + work-provider registration + pre-built furniture seed in a follow-up edit. Recovery time ~5 min via runtime probe. Pattern: **trust but verify agent reports against actual file state**, especially for "I wired this into the scene/autoload" claims. Don't repeat by asking for "show me the diff" alongside the report. - **Demo seed extended.** Added `_prestamp_test_shed_for_room_detector` + `_spawn_complete_wall` / `_spawn_complete_floor` helpers — instantiates entities in completed state for a 5×5 walled shed at (34, 23). Demonstrates the auto-roof path side-by-side with the cabin's over-cap path. - **Sheltering proxy debt paid.** `Pawn._is_sheltered()` now reads `World.is_indoor(tile)` first, falling back to the floor-has-cell proxy for graceful degradation while RoomDetector populates. - Phase 10 wolves' raid cooldown is set to 4800 ticks (1 in-game day). Combined with `darkness_factor ≥ 0.8` trigger gate, wolves continue to spawn nightly. No tuning required this phase. - Next: Phase 14 (Death + corpses + burial) — closes the death loop. Pairs naturally with Phase 13's `DirtinessSystem.bump()` API for combat-blood spikes. - **Phase 14 (Death + corpses + burial) shipped same day.** Three-agent fan-out (A: death+corpse spawn, B: graveyard+grave_slot+marker+haul, C: cremation pyre+ash+4 mood thoughts). Opus pre-wrote Corpse class with `setup()`/save round-trip + 5 EventBus signals + World.corpses/grave_markers registries before dispatch. Pattern proven for the third time — this is the way. - **Bleed-out timeout shipped at design value** (`BLEED_OUT_TICKS = 432000` = 6 in-game hours at 20 Hz). Previous memory entry's claim of "demo value 1200" was based on a misread; the constant has been at the design value since Phase 9 status_catalog.gd landed. - **Throughput tuning surfaced as a real concern.** At ULTRA speed (12×), corpses decay (DECAY_PER_TICK=0.05) hit the 100-rotted threshold before HaulingProvider's priority-3 corpse-haul scheduling could chain pawn→pickup→graveyard. Pipeline is correct; numbers need a Phase 20 pass. Workaround for testing: pause + manual job assignment, or boost corpse-haul to priority 4+. - **`recipe.gd` extended with `ingredient2_type/count`** for the cremation recipe (1 corpse + 5 wood). CraftingProvider's pickup step still only enforces ingredient1 — documented gap. Cremation pyre works as a corpse-only recipe today. - **HaulingProvider corpse path uses Node metadata** (set_meta on the carrying pawn) to track the carried corpse. Agent B did this to keep Corpse class (Agent A's territory) untouched. If a proper `Pawn.carrying_corpse` field lands later, replace the metadata. - **MCP runtime verified:** DEMO_PHASE14_AUTOKILL toggle force-kills first pawn at tick 50 — Bram DIED → corpse spawned → Cora's saw_corpse (-3) thought fired. Grave dig + designation paint chain verified to completion. Bleed-out + Wet status overlap firing correctly during ULTRA runs. Screenshot shows fresh corpse silhouette at cabin doorway. - Next: Phase 15 (Storyteller) — the world prods the player. 25-event registry, daily 6 AM roll, per-event + per-category cooldowns (both gates locked), banner/modal UI, ghost-state recovery. - **Phase 15 (Storyteller) shipped same day.** Three-agent fan-out (A: EventCatalog with all 25 events authored from design.md, B: Storyteller autoload daily roll + cooldowns + tension + ghost state, C: BannerUI + ModalUI + go-there pan). Opus pre-wrote EventDef class + 5 EventBus signals + Storyteller autoload stub before dispatch. Contracts-first pattern proven for the 4th time. - **Code-as-data choice for events.** EventCatalog is GDScript factories rather than `.tres` resources. Trade-off: faster iteration in MVP (no Inspector dance, no resource UID drift), but `.tres` is on the table for Phase 17 if hot-reload becomes a workflow need. - **MCP runtime verified across two boots** (different daily rolls per boot — RNG fresh per session): - Boot 1: `lone_wolf` THREAT → modal "A starving wolf circles your livestock." with Prepare/Dismiss buttons + sim auto-paused. Resolve → tension bumped 27→42, sim resumed. - Boot 2: `an_old_map` LORE → top-center banner, non-blocking, 6-sec auto-dismiss. - **Quick-edit fixup mid-flight:** modal didn't auto-hide when `Storyteller.resolve_current` was called externally (only on internal button click). Added a `storyteller_event_resolved` subscriber to the modal — one method. Phase 17 polish will sweep similar UX gaps. - **Wolf-spawn integration deferred to Phase 17** — Agent A noted `EventBus.request_wolf_spawn(count)` signal not yet declared; threat events that spawn wolves currently log-stub. Pattern recorded: when a Phase-N agent surfaces a missing cross-Phase signal, declare it on EventBus in the next contracts-prep pass rather than spreading the fix across agents. - **All categories used** in the 25-event corpus: nudge×4, seasonal×4, wanderer×4, threat×4, disease×3, resource×3, lore×2, milestone×1. Total cooldowns lock the per-day pool to roughly 3-6 eligible events on a typical day. - Next: Phase 16 (Save/load full coverage) — pays the partial-save debt accumulated since Phase 3. All entity types (pawn, item, furniture, container, corpse, wolf, plant, grave_marker), TileMap layers via `get_used_cells_by_id`, Storyteller state round-trip, Bill mid-fetch states. Phase 16 is the integration phase — fewer new files, more save-seam plumbing. - **Phase 16 (Save/load full coverage) shipped same day.** Three-agent fan-out (A: class_id tagging + missing to_dict/from_dict on 6 entities + tilemap helpers + beauty/dirt map serialization; B: SaveSystem v2 rewrite with per-class factory registry + clear-and-respawn apply_save + slot API; C: Autosave autoload + Save/Load TopBar buttons + LoadMenu + ResumeToast). Opus pre-wrote World.clear_all + 4 EventBus signals before dispatch. Pattern proven for the 5th time. - **User explicitly wanted autosave + manual save/load UI** — both shipped: Save (💾) + Load buttons in TopBar; Load opens slot picker; Autosave fires periodically (every 6000 ticks = ~5 in-game min) and on app pause / focus loss. - **clear-and-respawn pattern works.** `World.clear_all()` wipes all entity registries + queue_frees the Nodes; SaveSystem.apply_save then iterates `payload.entities` and dispatches to per-class factories. Verified: 113 entities saved at tick 1137, sim advanced to tick 4600 at ULTRA, load restored tick=1137 + all 3 pawns + all furniture in correct positions with 0 errors. Round-trip clean. - **Wolf target re-resolution** — wolf.from_dict stores target_pawn as a name string; Agent A's note says Agent B's apply_save should re-resolve names against `World.pawns` after all pawns are restored. Verify on next save-load cycle. - **Save version bumped to 2.** v1→v2 mismatch shows a warning dialog in LoadMenu; player can continue or cancel. Future bumps follow the same pattern. - **Known acceptable gaps:** Pawn JobRunner mid-INTERACT/mid-BUILD restarts from toil 0 on reload (walk toil round-trips; multi-step interact does not). Workbench bill mid-craft fetch state isn't fully serialized. Both are tolerable — pawns just redo a few seconds of work. Document as Phase 20 tuning. - **Three new autoloads now: Autosave (Phase 16) + Storyteller (Phase 15) + Weather (Phase 12).** All registered in project.godot in dependency order (Sim/Clock first, then Weather/Storyteller/Autosave). - Next: Phase 17 (Touch UX completion). The biggest deferred-polish bucket — work-priority matrix UI, bills UI, alerts log, pawn detail panel, build drawer, settings menu, all the touch-first interaction layer that's been stubbed. Several Phase 14/15 effects (wanderer recruit, resource buffs, wolf-spawn signal) wire in here too. - **Phase 17 (Touch UX completion, MVP-cut) shipped same day.** Three-agent fan-out: A = PawnDetailPanel + SettingsMenu, B = BuildDrawer (with 12 new Designation tools), C = WorkPriorityMatrix + AlertsLog + Decision Layer 4 per-pawn priority refactor + EventBus.request_wolf_spawn wiring. Opus pre-wrote 6 EventBus signals + Pawn.work_priorities Dictionary stub before dispatch. Pattern proven for the 6th time. - **TopBar now has 10 buttons:** ‖ / 1× / 5× / 12× / Save / Load / Settings / Build / Work / Log[N]. + a floating ⊕ FAB at bottom-right (BuildDrawer quick-open). Crowded but functional; mobile-polish pass can group under a hamburger. - **Decision Layer 4 refactor.** Pawn.work_priorities (Dict cat→int 0..4, 0=OFF, default 3=NORMAL) is now respected. Needs categories (rest/eat/sleep) BYPASS the filter so a pawn can't accidentally starve from misconfiguration. Doctor IS in the matrix (players can opt a pawn out of doctor duty). Audit log now prefixes work decisions with `(pri=N)`. - **Mouse drag-paint works as-is.** User specifically asked about this. Existing Selection + Designation tools read `_unhandled_input` events for mouse motion + button-held state, so drag-painting walls/floors/designations works in the editor without further work. - **Pattern proven 6th time:** "pre-write contracts to disk before fan-out". This session has been a single 1-day sprint shipping 6 phases (12 through 17) end-to-end via this pattern. Cost discipline: every phase = 3 Sonnet agents in parallel + Opus pre-write contracts + Opus integration + Opus MCP runtime verify. - **MCP runtime verified all 4 new UI surfaces:** Bram's detail panel shows Crafting=8 / Cooking=2 / Manual=0 matching seed; BuildDrawer Designate tab shows 4 tools with procedural icons; WorkPriorityMatrix shows 3 pawns × 8 categories grid with default "3" cells; AlertsLog shows 4 entries with mixed severity icons + "Spring Awakens" from the boot storyteller roll. - **Deferred to Phase 17.5 polish pass:** Per-pawn/per-job view layers, stockpile 4×4 chip grid, Bill UI, "no stockpile accepts X" / "bill blocked" emit wiring, DaySummaryCard visual. - Next: Phase 18 (Audio). Music + SFX + ambient + volume sliders + mute-on-suspend. Smaller scope than 17 — 1 week target. ElvGames + Ventilatore bundles include music/SFX packs we can source from. ### 2026-05-12 - **PC controls patch shipped** (out-of-phase, for desktop testing + Steam Deck prep). New input actions: WASD/arrow pan, `=`/`-` zoom, `C`/`Home` center, `Tab`/`Shift+Tab` pawn cycle, `B`/`L`/`P`/`,` panel toggles, `Escape` priority stack (cancel tool → close topmost panel → deselect pawn), right-click cancel/deselect (RTS convention). `F` is the new `speed_cycle` binding (handler still TODO). Touch paths untouched. Commit `0b2e0fc`. - **Pattern note: pawn cycling Shift+Tab.** First attempt used `Input.is_key_pressed(KEY_SHIFT)` to detect modifier on `pawn_next` action — this works with real keyboard but fails for MCP synthetic input (the singleton keystate isn't updated by injected events). Fix: read `event.shift_pressed` on the InputEventKey directly. More reliable AND works in tests. **Use `event._pressed`, not `Input.is_key_pressed(KEY_*)`, when reading modifiers tied to a specific key event.** - **Latent pre-Phase-17 bug surfaced and fixed in same commit.** WorkPriorityMatrix, AlertsLog, StorytellerModal, LoadMenu, SettingsMenu all had `MOUSE_FILTER_STOP` Backdrop/Dim Controls that stayed input-active when the panel was "closed". Their open/close only toggled `_root.visible` / `_panel.visible` — never `CanvasLayer.visible` (self). World mouse events (right-click deselect, left-click pawn-select) silently eaten. Each `_set_visible`/`open`/`close` now toggles `self.visible` so input dispatch shuts off. **Why it went undetected since Phase 17:** Tab-select and direct script invocation bypass `_unhandled_input`; the runtime verify in Phase 17 used those paths. First proper mouse-world test (this session) exposed it. - **Pattern recorded:** for any CanvasLayer-based UI panel with full-screen backdrop, always toggle `self.visible` (CanvasLayer) in addition to inner Control visibility, OR use `mouse_filter=PASS` on the backdrop. Apply when adding future modals. - Delegation report: PC controls drafted by `gdscript-refactor` (Sonnet, 2 dispatches — spec implementation + polish pass). Backdrop-bug discovery + fix done on Opus during MCP runtime verification (already in context). Headless + runtime verify all green. - **Visual sprite upgrade pass (play → fix → play loop).** Three procedural draws replaced with ElvGames atlas sprites this session: - **Walls** — stone-fill was at FG_Fortress (1,1), which is a tan stone *floor* tile (no brick texture, no cap). Swapped to (13,4) — middle column of a 3-tile-wide capped autotile, used as a non-autotile single sprite. Reads as a proper brick wall with a depth cap. Commit before context summary. - **Items on ground** — five most-spawned types (stone, iron_ore, gold, wood, plank) now render from FG_Abandoned_Mines coords with a quality border + stack-count badge composited on top in `_draw()`. Other types still use the original hue-hashed square fallback. Commit `7274ada`. - **Doors** — first attempt used FG_Fortress 32×32 closed-gate at (4,19); user rejected as "a door for a entrance to a castle" (commit `ac21443`). Pivoted to **FG_Village (3, 24)**, a 1-tile-wide olive-wood cabin door with U-handle extracted from the red-roofed cottage template. Commit `745ab29`. FG_Village.png copied into `art/tiles/`. - **Tileset survey notes (for future visual passes).** FG_Fortress = ALL doors are 2-tile-wide castle gates. FG_Houses = component-style 2-wide doors (cottage but still big). **FG_Village** = full-house templates with 1-tile-wide cabin doors baked into the bottom row (the one we used) — good source for residential furniture, signs, windows. FG_Interior = wardrobes/dressers/wallpaper, no standalone door entities. FG_Marketplace = no doors at all. - **MCP `execute_game_script` quirk:** scripts with 3+ statements fail `Parse error`. Workaround: pack with `;` or use `for x in y: a; b; c` (compound for-body line). Affected this session's visual probes. - **Pattern recorded:** when copying a new texture into `art/tiles/`, run `/mnt/d/godot/Godot_v4.6.2-stable_win64.exe --editor --headless --quit` to force generation of the `.import` file before any scene can `preload()` it. Skipping this step yields a silent null preload at runtime. - Delegation report this session: **No delegation — handled on Opus** for sprite-atlas surveys, GDScript refactors, MCP runtime verification. Pure visual / MCP-tool work; subagents would have re-read the same files repeatedly. ### 2026-05-15 - **Bug-triage patch shipped** out-of-phase, before Phase 18 (Audio) starts. Same pattern as the 2026-05-12 PC-controls patch. Three playtest-reported bugs investigated, root-caused, fixed, and MCP-runtime-verified end-to-end. See **Known bugs / triage backlog** above for per-bug detail. - **Pattern recorded — "pre-built seed masks downstream wiring gaps".** Five entity types (Torch / Bed / Crate / Workbench / CremationPyre) had been missing `World.register_build_site(self)` since Phase 17 landed. Nobody noticed because the cabin demo seed calls `_spawn_complete_*` helpers (insta-builds via direct on_build_tick loop), so no player ever painted a fresh furniture designation. Lesson: when adding a new entity type to a build-queue iterating system, always playtest the FRESH-PAINT path, not just the pre-built seed. - **Pattern recorded — "pathfinder mutation can strand walking pawns".** When set_cell_walkable(false) fires, pawns already mid-walk on a path through that cell will keep stepping along the stale path and land on the now-impassable tile. AStarGrid2D requires walkable START, so the pawn idles forever. Defense-in-depth fix: Wall._complete dislodges-on-tile + Pawn._advance_walk re-checks walkability before stepping. Generalises to any future system that mutates pathfinder walkability (e.g. doors, big_rock, future fortifications). - **Pattern recorded — "Y-sort equal-Y ambiguity".** Two entities at same Y-sort root with same position.y fall back to scene-tree order, which is fragile. Always set distinct Y-positions for distinct visual layers (ground vs. above-ground). Ground-tier entities use top-of-tile anchor; above-ground entities use bottom-of-tile anchor (Wall/Bed/Torch/Workbench pattern). Pawn at mid-tile sits cleanly between. - Delegation report: `researcher` (Haiku, 1 dispatch) mapped three bug surfaces (~600 word digest with file:line citations). All fixes + MCP runtime verification handled on Opus — fix sites were already in context from the researcher report, so re-dispatching to Sonnet would have been pure overhead. - **Job system audit + designation-gate fix shipped same day** in the same bug-triage patch session. Player report: "I set some trees to be cut but no one is doing it". Root cause via `researcher` (Haiku, 1 dispatch, ~900 word digest mapping all 11 WorkProviders): ChopProvider and MineProvider iterated `World.trees` / `World.rocks` unconditionally — `is_choppable()` / `is_mineable()` only check progress, not designation. The `&"chop"` and `&"mine"` cases in `world.gd._on_designation_added` stashed a `null` sentinel and never touched the entity, so paint was purely cosmetic. Pawns were auto-chopping the nearest unfelled tree regardless of what the player painted. - **Fix**: added `chop_designated: bool` to Tree, `mine_designated: bool` to Rock + BigRock (BigRock flag covers entire 2×2 footprint via `footprint_tiles()` membership check), gated both providers, wired designation paint/cancel to flip the flag, auto-designated SAMPLE_TREES / SAMPLE_ROCKS / SAMPLE_BIG_ROCKS at boot so the demo still runs end-to-end. Rimworld parity restored — pawns no longer auto-chop / auto-mine. - **Also from same audit**: added reachability pre-checks (mirroring HaulingProvider) to DoctorProvider and EatProvider — they were issuing jobs to unreachable targets, then relying on JobRunner walk-cancel to clean up. The pre-check avoids the busy-spin between Decision and JobRunner. - **Pattern recorded — "iteration-source bugs hide behind boot-seed shortcuts".** The Phase 4 seed `SAMPLE_TREES` / `SAMPLE_ROCKS` was always being chopped/mined because providers auto-picked. Player only noticed when they painted designations and the behavior didn't change. Boot seeds that mirror "default OK" state can mask gate logic; when adding any new gate (designation, skill, capability), playtest the FRESH state, not just the seeded state. - **Other audit findings (deferred, not fixed this session)**: CraftingProvider has an ingredient re-scan race (ingredient may disappear between lines 73 and 91); PlantProvider only handles harvest (sow stubbed for Phase 17); RestProvider is a Phase 3 smoke-test leftover that could be deprecated. All low priority. - **ConstructionProvider reachability gate** added later same day after player report: chop designations weren't being honored. Root cause: a leftover `build_door` designation at the test shed wall (36, 27) was unreachable (wall in the way), but ConstructionProvider (priority 6) kept offering it to every pawn every tick, and Decision picked it over chop (priority 5). JobRunner cancelled the doomed walk each tick — busy-spin starved chop. Same fix shape as Doctor/Eat: pre-check `pathfinder.find_path` before issuing a job; for blocking sites (walls) probe from an adjacent walkable cell. - **Door-replaces-wall replacement build** added by user request. Painting `build_door` on a tile occupied by a Wall (ghost OR completed) atomically demolishes the wall in place and spawns the door ghost. Reverses Wall._complete cleanly: unstamps wall_layer, marks pathfinder walkable, recomputes rooms. Source of truth is World.build_queue so the rule covers both designation-painted walls AND pre-built seeds (cabin, test shed) which self-register via Wall._ready. Other replacement combos (floor-on-floor, wall-on-door) NOT in scope; would need separate refund/destroy rules. - **Phase 18 (Audio) shipped same day** — bug-triage sprint extended into Phase 18 once the job system was solid. Scope: - 8 curated OGG files copied into `audio/{music,sfx}/` from the ElvGames bundle (Retro Farming Music 1, Cozy Melodies Pack 1, UI Pack 1, Woodcutting & Mining 1, Sword Pack 1). Two source zips (Cozy Melodies Pack 1, Retro Farming Music 1) were extracted in-place inside the bundle's Tier 1 dir. - New `autoload/audio.gd` singleton: 3 buses (Master, Music routed to Master, SFX routed to Master), lazy stream loading with per-key cache, rate-limited play_sfx (80ms cooldown to prevent chop-tick spam at ULTRA), single persistent AudioStreamPlayer for the music director, automatic music swap on `Clock.phase_changed` (day_loop ↔ night_loop), mute on `NOTIFICATION_APPLICATION_PAUSED` + `FOCUS_OUT`. - SettingsMenu sliders (master/music/sfx) live-bound via `value_changed.connect(Audio.set_*_linear)`. Ambient slider intentionally unwired — no ambient bus this pass. - SFX wiring: `Tree.fell` → tree_fell, `Rock.mined` / `BigRock.mined` → mine_tick, EventBus.pawn_took_damage → combat_hit, storyteller_event_fired → ui_confirm sting, alert_added → ui_click. - MCP runtime verified all paths: buses online, music swap day→night→day round-trips, volume sliders correctly map linear→dB (0.5 → -6.02dB, 0.0 → -80dB silent), SFX trigger on demand. - **Pattern recorded — "rate-limit per-key SFX or the mixer chokes at fast sim speed".** First instinct was per-chop-tick SFX from `Tree.on_chop_tick`; at ULTRA (12× speed, ~12 chop ticks/sec/pawn × 3 pawns) that would saturate the audio mixer. Moved to completion-only SFX (Tree.fell, Rock.mined) plus an 80ms per-key cooldown in `play_sfx`. Future ambient/tick-based SFX should reuse this throttle. - **Pattern recorded — "extract zipped asset packs into their own named folder, in place".** The bundle ships some packs as zips (Cozy Melodies Pack 1, Retro Farming Music 1) and others as already-extracted folders. Each zip's top-level entry was a folder matching its name, so `unzip -q` in-place created the expected `Cozy Melodies Pack 1/` directory alongside the .zip without flattening. Don't extract to a generic name like `audio_extracted/` — preserve the pack identity so licensing/credits stay traceable. - **Hover-inspect tooltip shipped** in the same out-of-phase polish sprint as the audio + designation-gate work. New `scenes/ui/inspect_tooltip.gd` CanvasLayer at z=50, `PROCESS_MODE_ALWAYS` so it works during pause. Priority order is: pawn → wolf → corpse → grave marker → crop (exact tile) → tree (4-tile canopy) → big rock / rock → furniture at exact tile → furniture sprite-canopy (bed +1 tile up) → item → stockpile region → floor. Iterated three times against player feedback: trees not showing (needed canopy box), beds covered by floor (needed sprite-canopy fallback because bed sprite spans 16×32), crops not showing on the exact-tile pass. - **Pattern recorded — "tall sprites need sprite-canopy fallback in pickers".** Bed sits 32px tall but its `tile` is only the foot row; hovering the headboard row hit the floor first because exact-tile match prefers furniture-on-this-tile. Fix: after the exact-tile pass fails, retry with `tile + Vector2i(0, 1)` for sprite-tall entities. Same trick will be needed for any future 2-tile-tall furniture (wardrobe, double bed, statue). - **Bed sprite rebuilt procedural after atlas misidentification.** The 2026-05-12 visual pass used FG_Interior atlas coords for "beds" — those coords were actually chairs-with-cushions. User caught it as "not good and confusing where the bed is". Replaced with a procedural top-down draw: wood frame + striped blanket + pillow + foot board, 16×32, three deterministic colour variants seeded by tile coord. The cabin was 8×6 = beds slipped into the north wall; bumped to 8×7 at origin (44, 22) so the headboard row is interior floor. - **Pattern recorded — "visually verify atlas coords during survey, not just by filename hint".** FG_Interior has a band of chair sprites at the same atlas band where beds would naturally live; previous pass logged them as beds without zooming in. Future atlas surveys should ALWAYS render a `_zoom.png` with red gridlines and visually confirm before committing to a coord constant. - **Designation tile-highlight leaked across job completion.** Painting chop / mine / build left the orange/blue overlay on the tile forever, even after the job finished. Root cause: nothing called `designation_ctl.clear_cell()` from entity completion handlers. Fix: added `World.designation_ctl` field + `clear_designation_at(tile)` helper, wired into completion handlers of Wall, Floor, Door, Bed, Torch, Workbench, Crate, Tree, Rock, BigRock (iterates footprint), GraveSlot. Commit `f67c12c`. World boot wires the controller (`World.designation_ctl = designation_ctl`). - **Crop sprites rebuilt with ElvGames atlas + four growable kinds.** Replaced procedural stem-and-circle draw with per-kind 16×32 Sprite2D anchored bottom-of-tile (matches tree pattern). Four kinds wired: wheat, potato, corn, strawberry — first two from 4-stage sheets, last two from 5-stage sheets (we slice the first 4 cols; the 5th is a post-harvest regrow frame we ignore). Stage-to-sprite mapping: TILLED=no plant, SOWN→col 0, G1→col 1, G2→col 2, G3 + READY both→col 3 (harvest highlight is the cue, not the sprite). Boot demo plants one column per kind east of the cabin. Harvest outputs: wheat/corn → grain, potato/strawberry → vegetable. Commit `c93f889`. - **Pattern recorded — ".import auto-generation via `godot --headless --import`".** Writing `*.png.import` companion files by hand with placeholder paths works but Godot rewrites them with proper UIDs on the first import scan. Reliable workflow: copy PNG into `art/`, write minimal .import (UID will be ignored), then run `/mnt/d/godot/Godot_v4.6.2-stable_win64_console.exe --headless --import` once to generate the .ctex binaries. Headless boot fails until the import scan runs. Same pattern from 2026-05-12 confirmed. - Delegation report (whole-day 05-15 polish sprint): **No delegation — all on Opus.** Sprite-atlas surveys, MCP runtime verification (camera positioning, dialog dismissal, stage forcing), and the iterative play→fix→play loop kept the same files hot in context; Haiku/Sonnet handoffs would have re-read every time. The `researcher` (Haiku) dispatch from earlier in the day for the job-system audit is the only delegated work. - **Workbenches rebuilt procedural after second atlas-misidentification.** The 2026-05-12 atlas-sprite pass for Carpenter/Smelter/Hearth/Millstone again hit the same trap as Bed: the picked tiles read as chest-of-drawers (Carpenter, FG_Interior 24,20), candle base (Smelter, FG_Marketplace 8,30), 2-burner stove (Hearth, FG_Interior 16,32), and cushion stack (Millstone, FG_Interior 17,40). User: "the carpenter is a chest of draws? no idea what the mill is, looks like a bucket". Survey of all 7 plausible source tilesets (Interior / Marketplace / Houses / Village / Fortress / Mines / Dark Castle / Extras) found a 2×2 medieval stone fireplace at FG_Interior (12,13)..(13,14) for Hearth but **2-wide tiles don't fit the current single-column sprite system**, and the other three had no clean replacement. Decision: go procedural for all four, following `CremationPyre._draw_pyre` as precedent. New methods `_draw_carpenter / _draw_smelter / _draw_hearth / _draw_millstone` in workbench.gd; `_VARIANT_SPRITES` table + `_build_sprite()` Sprite2D path removed; `_complete()` no longer touches a sprite child. Hearth is the only h=2 variant — draws stone surround + mantle + arched opening + log fire + flame teardrop extending into the tile north (Y-sort handles occlusion). Commit `c97ada8`. - **Pattern recorded — "atlas-misidentification trap, second occurrence — prefer procedural for named furniture".** Pixel-art tile atlases pack hundreds of squat 16×16 items at low resolution; without zoomed-in inspection it's easy to mistake a furniture silhouette for what its name implies. Both Bed (2026-05-12) and the four workbenches (2026-05-15) hit this failure mode. Going forward: for furniture whose silhouette is **definitional** (workbench-with-tools, bed-with-pillow, millstone-as-grindstone), **start with procedural draws** and only swap in an atlas tile after verifying via 16× zoom that the tile actually depicts the named object. Atlas-sourced sprites are still the right call for generic decorative variation (trees, walls, floors, crops with growth stages). - **Tree variety added via 3-season palette swap.** Copied `FG_Tree_Summer.png` + `FG_Tree_Fall.png` from the Grasslands tier-1 pack into `art/sprites/` (Spring was already there). 4 silhouettes × 3 palettes = 12 visual variants; `tree.gd` hash mixes silhouette and season independently so neighbouring tiles don't all share the same palette. Winter omitted — snowy trees would look out of place in the current biome. When a season-cycle system lands (future phase), the active texture can be swapped globally by season instead of per-tree. No growth stage / sapling system yet — bundle has no growth frames for trees, and the user's "regrow / auto-grow" ask is explicit future scope (deferred to a later phase that also covers planting designation). Commit `c97ada8`. ### 2026-05-16 - **Pre-made SW stockpiles + crates removed** so items sit where they're produced until the player paints storage (matches Rimworld parity). Deleted `_spawn_sample_stockpiles()` (two zones at (15,55) and (15,62)) and the two SW haul-target crates at (17,60)/(18,60). Kept the interior cabin crate at (50,23) as a starting amenity. Bills' `UNTIL_N` mode counts items anywhere in world (not just stockpiles) so the Smelter's "5 stone blocks" bill still terminates. All `World.stockpiles` consumers iterate the list — empty-list no-op, no crashes. Commit `c81e817`. - **Workbench Bill Editor plan drafted** for the deferred Phase 17 item ("Bill UI for workbenches"). 10-step plan saved to user-memory at `~/.claude/projects/-mnt-d-godot-rimlike/memory/plan_bill_ui.md` (resilient to fresh sessions). Without this UI, every player-built workbench is functionally dead — build drawer can paint them but bills can only be set in code. Recipe.id field existence needs verification in Step 4. Starting execution 2026-05-16. - **Workbench Bill Editor shipped same day** (commit `bdd4352`). Tap a workbench → right-side panel with bill rows (mode toggle, target spin, pause, remove) + Add-bill popup filtered by `accepted_skill`. Mirrors `PawnDetailPanel` exactly: layer 18, right-anchored 360 px, procedural `_build_ui()`, sim_tick refresh of the current-bill status only (bill list rebuilt on add/remove/select to preserve scroll). Selection chain extended in `selection.gd`: pawn-first then workbench, mutual-exclusion via new `EventBus.workbench_selected/deselected` signals. Closes Phase 17 deferred item. Code-level verification: headless boot clean, no runtime errors. Visual MCP verification still pending (need editor running). - **Delegation report — bill UI sprint.** Steps 1, 3, 4, 6 (~5 mechanical edits across 5 files) → `quick-edit` (Haiku, 1 dispatch). Step 5 (new ~432-LOC WorkbenchPanel script needing full mirror of PawnDetailPanel + 6 source files) → `gdscript-refactor` (Sonnet, 1 dispatch). Steps 2, 7 (selection chain + main mount, needed design judgment about mutual-exclusion and main.gd's typed-var pattern) handled on Opus. Strings table follow-up (`ui.bill.until_count` key missed by Sonnet, fallback was a hardcoded literal) caught and fixed on Opus before commit. - **Pattern recorded — "main.gd typed-var pattern requires CanvasLayer.new() + set_script(), not SCRIPT.new()".** First mount attempt used `WORKBENCH_PANEL_SCRIPT.new()` — Godot 4's parser refused with "Cannot infer the type" because `Script.new()` returns generic `Object`. Switched to `var x := CanvasLayer.new(); x.set_script(SCRIPT)` matching the rest of main.gd. Cheap parse error to surface via `--headless --quit`, but worth noting: subagents writing UI mount glue need this idiom explicitly. - **Pattern recorded — "never free a widget from within its own signal callback".** Bill editor crashed when user changed mode FOREVER → UNTIL_N. Root cause: OptionButton.item_selected lambda called `_populate_bills()` directly, which clears + rebuilds all bill rows — freeing the very OptionButton whose signal was still emitting. Same pattern in the Remove button. Fix: `call_deferred("_populate_bills")` so the rebuild runs on the next idle frame after the signal frame completes. Commit `4e09dea`. Applies to any UI where a child Control's signal handler mutates a parent container — always defer rebuilds. - **Pawn reskin Slice 1 shipped** (commit `b4c9541`). Pawns now render as AnimatedSprite2D children sourced from ElvGames Farming Characters Pack (Pack 1, chars 001-015). 9 anims per pawn: idle×4dirs + walk×4dirs + dead. Atlas pick is `(absi(pawn_name.hash()) % 15) + 1` — Bram=004, Cora=013, Edda=001, all visually distinct. New `_atlas_for_pawn(pawn)` helper is the **single extension point** for the future Slice 2 (armor → atlas swap when equipment+combat phase ships). Slice 1 plan archived to user-memory at `~/.claude/projects/-mnt-d-godot-rimlike/memory/plan_pawn_reskin_slice1.md`. 45 PNGs + .import companions copied to `art/sprites/characters/`. New `scenes/pawn/pawn_sprite_frames.gd` builds the SpriteFrames from a {idle, walk, dead} atlas trio. Code-level verification clean; visual MCP verification still pending (needs editor running). - **Pattern recorded — "class_name lookups aren't reliable during cold-start parse; use preload() for sibling scripts".** First attempt referenced `PawnSpriteFrames.build()` in pawn.gd via the global class_name registry. Headless parse failed with "Identifier 'PawnSpriteFrames' not declared in the current scope" because pawn.gd was loaded before pawn_sprite_frames.gd registered its class_name globally. Fix: `const _PAWN_SPRITE_FRAMES = preload("res://scenes/pawn/pawn_sprite_frames.gd")` at the top of pawn.gd, then call `_PAWN_SPRITE_FRAMES.build(atlases)`. Class_name is fine for public API documentation; preload is the reliable runtime resolver. - **Pattern recorded — "deferred-init data must be wired AFTER setup(), not in _ready()".** First sprite mount happened in `_ready()` and read `pawn_name` — but pawn_name is empty at _ready time (assigned later in setup()). All three pawns got atlas idx 1 (hash of empty string). Fix: moved sprite mount to `_mount_sprite()` called from setup() AND from_dict(), both of which assign pawn_name first. Idempotent (frees prior sprite). Same shape will recur for any future render that depends on per-instance saved state — always check whether the data the renderer reads is available at _ready vs after setup/from_dict. - **Delegation report — pawn reskin Slice 1.** `quick-edit` (Haiku, 1 dispatch) handled the mechanical edits across pawn.gd (facing field, save/load, `_canonical_facing`, `_atlas_for_pawn`). `gdscript-refactor` (Sonnet, 1 dispatch) wrote the new `pawn_sprite_frames.gd` helper (~50 LOC) and wired the AnimatedSprite2D into Pawn._ready (z_index, anim switching, _facing_suffix). Opus handled the asset copy + headless --import, the two parse/runtime fixes (preload + setup-not-ready), the hash-distribution audit, and the commit. The two patterns above were caught on Opus during verification. ## External references - **Forgejo repo:** https://git.rdx4.com/megaproxy/rimlike (private) - **Owned art bundle (primary):** ElvGames "Ultimate Farming RPG" Humble bundle, local at `/mnt/d/godot/assets/humble set new/`. License: ElvGames terms (). Commercial OK with credit; no NFT/crypto/resale. - **Owned art bundle (secondary):** Ventilatore Fantasy Tileset Complete Bundle — - **Mana Seed catalog (fallback if bundle gaps surface):** - **Iconic Homestead (autotile-ready wall fallback, $19.99):** - **Lodestars:** Rimworld (Tynan Sylvester / Ludeon Studios), Going Medieval (Foxy Voxel), Stardew Valley (ConcernedApe). - **Original brainstorm history:** archived to `~/claude/archive/ideas/rimlike/` after promotion. The session log there captures every decision's rationale. - **Brainstorming-mode preference:** see `~/.claude/projects/-home-megaproxy-claude-ideas/memory/brainstorm-ask-dont-decide.md` (not directly applicable to projects but captures interaction style).