Q: iron_smelt (iron_ore + wood → iron_ingot) and gold_smelt
(gold + wood → gold_ingot) recipes added at Smelter, using the
existing ingredient2 buffer mechanism. New TYPE_IRON_INGOT and
TYPE_GOLD_INGOT item types (procedural hue-hash draw for now).
R: new Recipe.target_workbench field (StringName, empty = any matching
skill) round-trips through to_dict/from_dict. Workbench bill picker
filters by both required_skill AND target_workbench vs lower-cased
label_text. plank → carpenter, stone_block/iron_smelt/gold_smelt →
smelter, flour → millstone. Cooking-only recipes (bread, meal) stay
unrestricted since Hearth is the only cooking workbench.
9 recipes total now, 4 distinct workbench routes.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
G: large_text scales global theme font (14→20 at 1.4×) via new
GameState.get_font_scale + EventBus.settings_changed. reduce_motion
gates ResumeToast fade (HintOverlay already gated).
I: InspectTooltip long-press wired (500ms hold, 12px drift cancel,
tap-to-clear pin). Stale Phase 19 TODO replaced with accurate doc.
H: Pawn.arrived_at_destination now also emitted on
EventBus.pawn_arrived_at_destination; DirtinessSystem subscribes and
bumps indoor traffic dirt (BUMP_INDOOR_TRAFFIC = 0.2). Outdoor-tracked
bump needs Pawn.prev_tile — flagged for Phase 20.
P: CraftingProvider caches ingredient item ref on Job.ingredient_item;
JobRunner._tick_pickup validates is_instance_valid + not being_carried
before the tile scan, cancels cleanly if another pawn grabbed it.
J: rest_provider.gd deleted. Removed @onready + register call from
world.gd, ext_resource + node from world.tscn. Provider count comment
updated to 9.
M: DIRTY_THRESHOLD extracted — cleaning_provider and job_runner now
reference DirtinessSystem.DIRT_DIRTY_THRESHOLD.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
save/load round-trip: workbench bills, crop static-method, bed owner,
wolf target now all survive reload via Bill.from_dict reconstruction,
_spawn_crop using setup(), and a new _post_load_resolve_references pass.
PlantProvider: sow path added; consumes 1 grain on a TILLED crop tile.
CraftingProvider: ingredient2 supported via new KIND_DEPOSIT_AT_WB toil
and Workbench.deposited_inputs buffer. Cremation pyre now actually
consumes wood.
HaulingProvider: per-item haul_retry_count + haul_rejected after 3
orphan passes; new EventBus.stockpile_layout_changed resets rejects on
any player stockpile edit.
Storyteller: 14 stubbed event effects implemented. New buff registry
(add_buff/get_buff_multiplier/has_buff, day-prune, save/load) drives
seasonal/resource events. New request_pawn_spawn signal + WANDERER
table for arrivals. New SICK status + 3 mood thoughts.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Three-agent fan-out (gdscript-refactor x3) ships the chosen Phase 19
approach: contextual hints during first session + a Help reference,
plus a sweep of hover tooltips for desktop discoverability.
- HintSystem (autoload) + HintOverlay (layer 22 top-center banner):
7-step tour gated on player events — welcome (boot+2s), pawn select,
build drawer open, stockpile painted, work matrix open, day_ended,
tour_complete. Per-hint dismissals persist as Array[String] in
GameState.settings['dismissed_hints']. Max-3 FIFO queue if hints
chain. Reduce-motion path snaps in/out instead of tweening.
Reset_tour() public API for the Help modal.
- HelpModal (layer 20): 5-tab static reference (Controls / Verbs /
Priorities / Storyteller / Tips). Opens via EventBus.help_requested,
dimmed backdrop, X/Esc/backdrop-tap dismiss. SettingsMenu gains an
'Onboarding' section: Show-hints checkbox, Help button (emits
help_requested), Reset hints button (calls HintSystem.reset_tour with
has_method guard). Pre-existing 'W' keybind reference fixed to 'P'.
- Tooltip pass: tooltip_text via Strings.t on every TopBar button
(10 buttons incl. speed shortcuts), BuildDrawer FAB, and every tool
button in BuildDrawer (21 tools). _add_tool_btn extended with optional
tooltip param. ~34 new tooltip.* string keys.
Contracts pre-written (Opus): EventBus.help_requested, hint_dismissed,
ui_panel_opened signals; GameState show_hints + dismissed_hints
defaults; BuildDrawer.open + WorkPriorityMatrix.open emit
ui_panel_opened so HintSystem can subscribe via one signal.
Also recorded [MED] known bug in memory.md: drag-paint with active
paint tool is eaten by camera drag-pan.
MCP runtime verified: welcome banner fires 2s after boot, dismiss
queues build_drawer hint on next ui_panel_opened, dismissed_hints
persisted as ['welcome'], HelpModal opens via help_requested with
tab switching working (Controls → Tips verified visually).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Reverted center-bottom auto-size (caused tab-switch jumping) back
to a full-width tray, but at 280px tall instead of 600. Build tab
now lays out Structures / Furniture / Production as side-by-side
columns, each a labelled 3-col grid, filling the tray horizontally
instead of stacking vertically.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Was a full-width 600px sheet covering 83% of the screen with ~75%
empty space. Now auto-sized, anchored bottom-center, ~30% wide.
Build tab groups items into Structures / Furniture / Production
with header labels. Buttons 80→72, tab strip 48→36, 4→5 cols.
'Build quarry' → 'Quarry' for row consistency.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Trees: 4 growth stages (Sapling→Young→Growing→Mature), only Mature
yields wood. WildGrowth ticker fires every in-game hour; rejection-
samples grass tiles and plants a sapling with ~30% probability (capped
at MAP_TREE_LIMIT=60). New `paint_plant_tree` designation lets the
player manually plant — ghost sapling registered as a build_site that
ConstructionProvider fulfils. Stage round-trips through save/load.
Initial seed mixes 4 saplings + 6 mature so growth is visible day 1.
Quarry: new BigRockNode entity (2×2 permanent stone outcrop, never
depletes). 3 nodes seeded far from cabin. New QuarryWorkbench
(extends Workbench, auto-FOREVER `quarry_stone` bill, recipe drops
1 stone per 300 work-ticks). New `paint_quarry` designation only
accepts BigRockNode tiles. CraftingProvider now supports recipes
with `ingredient_count == 0` — skips ingredient-fetch and goes
straight to walk+craft toils. Recipe gains `ingredient_count` field
(defaults 0). Save/load layering: big_rock_node spawns at priority 0
(same as rock/tree), quarry_workbench at priority 2 (after the node).
UI: Plant tree + Build quarry buttons added to Build drawer.
build_drawer_thumb gains `plant_tree` (sapling sprout in dirt) and
`paint_quarry` (stone block + chisel + cut-stone pile) shapes.
inspect_tooltip recognises BigRockNode + shows tree growth stage on
hover.
Delegation: gdscript-refactor (Sonnet ×2) for trees full impl +
quarry skeleton; quick-edit (Haiku) for CraftingProvider no-ingredient
plumbing + TopBar polish; integration handled on Opus.
Theme (scenes/ui/medieval_theme.gd):
- Procedural builder for an app-wide Theme: parchment buttons on
dark-wood border, tan panels, ink text, gold focus ring.
- Applied on the root Window in Main._ready, with a tree-walk that
re-seeds every CanvasLayer's topmost Control (CanvasLayer
interrupts the root-Window theme cascade in Godot 4).
- Late-mounting popups + modals get themed via a child_entered_tree
hook on each CanvasLayer.
Build drawer thumbnails (scenes/ui/build_drawer_thumb.gd):
- New BuildDrawerThumb Control that dispatches on tool_id and draws
a recognisable silhouette of the entity each tool builds. 17 tools
covered: chop/mine/dig_grave/no_roof (Designate), stone+wood walls,
wood+stone floors, door, crate, bed, torch, 5 workbenches
(Carpenter/Smelter/Millstone/Hearth/Cremation Pyre), stockpile,
graveyard.
- Replaces the flat ColorRect placeholder. _add_tool_btn signature
changed from (label, color, callback) to (label, tool_id, callback).
Pawns now render as AnimatedSprite2D children sourced from ElvGames
"Farming Characters Pack" atlases (Pack 1, characters 001-015). Each
pawn picks one of 15 peasants deterministically from name hash:
Bram=004, Cora=013, Edda=001.
Animations: idle_down/left/right/up + walk_down/left/right/up (4 fps
idle, 8 fps walk, looped) + dead (single frame, no loop). Pawn picks
animation each _process tick from (is_downed, is_walking, facing).
Facing is now a Vector2i field updated in _advance_walk; round-trips
through save/load.
Sprite mounting is deferred from _ready() to setup() / from_dict()
because the atlas pick depends on pawn_name, which isn't assigned at
_ready time. _mount_sprite() is idempotent for the save-load chain.
_atlas_for_pawn(pawn) is the single Slice-2 extension point —
swapping atlases based on equipped armor in a future sprint is a
one-function change.
_draw() stripped of body disc + downed-rotation; now overlay-only
(selection ring + carry indicator). AnimatedSprite2D child uses
z_index=-1 so the overlays stay on top.
45 PNGs copied into art/sprites/characters/ + 45 .import companions.
The mode OptionButton and remove Button both called _populate_bills()
from inside their own signal callbacks. _populate_bills() rebuilds the
bill list — freeing the very widget mid-emit, which crashes Godot.
Defer the rebuild via call_deferred("_populate_bills") so it runs
after the signal frame completes. Reproduced by user changing a bill
from FOREVER → "Do until 10".
Player report: hovering trees works after the canopy fix, but crops show
nothing and beds still report "Wood floor" sometimes.
* Crops are added to the lookup, showing kind + growth stage + percent
("Wheat | sown · 98%", "Wheat | ready to harvest", "tilled — not sown").
* Bed sprite is 16×32 (two tiles tall, anchor at the foot). Hovering on
the headboard (one tile above the anchor) used to miss. Added a sprite-
canopy pass for beds mirroring the existing 4-tile tree canopy logic.
* Full layer audit and reordering. Final priority top-down:
1. Pawn / Wolf / Corpse / Grave marker — entities the player cares
about first.
2. Crop — small sprite, exact tile only.
3. Tree — trunk tile + 4-tile canopy.
4. Big rock (2×2 footprint) / Rock — exact tile / footprint.
5. Furniture at exact tile (wall / door / bed / crate / workbench /
torch).
6. Furniture sprite-canopy (currently only bed; future tall furniture
slots in here).
7. Item on the ground (loose stack).
8. Stockpile / zone region overlay.
9. Floor — only when nothing physical is on the tile.
Confirmed at runtime: bed foot → Bed; bed headboard → Bed (via canopy);
two-tiles-above-bed → empty; crop at growing stage shows percent; bare
floor still says Wood floor; furniture on wood floor wins over floor.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two playtest gaps reported:
* Hovering on a tree showed nothing — trees anchor to the trunk tile but
the canopy sprite rises ~4 tiles upward. Now any hover within the
vertical band [trunk.y - 4, trunk.y] resolves to the tree.
* Hovering inside the cabin always said "Wood floor" — both floor and
furniture register in World.build_queue, and the floor was found
first. Now we two-pass the queue: remember any floor we hit, but keep
scanning for furniture (bed / crate / workbench / torch / etc.) and
return that if found. Items on the ground also win over the bare
floor. Floor only shows when nothing else occupies the tile.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds an InspectTooltip CanvasLayer that follows the mouse, samples the
tile under the cursor each frame, and renders a small dark panel with a
short description of whatever's there.
Per-entity describers cover the playable surface:
* Pawn: name + HP + mood + current job
* Tree / rock / big rock: progress %, "marked" tag if designated
* Wall: material + ghost/% if unbuilt
* Floor / door / torch: ghost vs complete state
* Bed: occupant or "available", medical tag
* Crate: full contents broken down by item type and count
* Workbench: label + active bills count
* Item on ground: type + stack size
* Corpse: deceased name + fresh/rotting/rotted state
* Wolf: HP + state
* Grave marker: deceased name
* Stockpile / graveyard zone: name + priority + accepted types
Layer 50 so the tooltip sits above the world but below modals (which
sit at 100+). process_mode = ALWAYS so hovering still works during
storyteller modals. Position auto-flips to the other side of the cursor
when it would overflow the viewport.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds an AudioManager autoload with three buses (Master, Music routed to
Master, SFX routed to Master), a small catalog of looping music + one-shot
SFX, and a single persistent AudioStreamPlayer for the music director.
Music
* Day and night loops swap on Clock.phase_changed (night during the night
phase, day everywhere else). Tracks pulled from Retro Farming Music 1
(day) and Cozy Melodies Pack 1 (night), both loopable OGG.
SFX
* Tree.fell, Rock.mined, BigRock.mined → tree_fell / mine_tick.
* EventBus.pawn_took_damage → combat_hit (Sword Pack 1).
* EventBus.storyteller_event_fired → ui_confirm sting.
* EventBus.alert_added → ui_click.
* play_sfx is rate-limited per key (80ms cooldown) so fast-sim doesn't
saturate the mixer.
Settings + suspend
* SettingsMenu master/music/sfx sliders now live-bind to the bus dB via
Audio.set_*_linear (linear → dB internally, 0 → -80dB silence). The
ambient slider is intentionally unwired; no ambient bus this pass.
* NOTIFICATION_APPLICATION_PAUSED + FOCUS_OUT mute the Master bus to
match the existing "no background sim" rule. Resume + focus restore it.
Bundle housekeeping
* Two zipped packs in the ElvGames bundle (Cozy Melodies Pack 1, Retro
Farming Music 1) extracted in place to keep pack identity intact for
the license/credits string. 8 OGG files curated into audio/ at ~5.3MB.
Verified end-to-end via MCP runtime: buses online, day_loop plays at
boot, manual phase swap day→night→day round-trips, slider linear→dB
mapping correct (0.5 → -6.02dB, 0.0 → -80dB), tree_fell SFX triggers.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Three alert signals had no UI subscribers — gameplay failures vanished
silently. Now all three feed AlertsLog via translator handlers that
forward to the generic alert_added sink.
- EventBus: new no_stockpile_accepts(item_type, tile) and
bill_blocked(recipe_label, reason, focus_tile) signals.
- HaulingProvider: per-item-type 30s cooldown; emits when find_best_for
scan finishes with viable items but no destinations.
- CraftingProvider: per-(workbench, reason) 60s cooldown; emits at the
skill_too_low and missing_ingredient continue sites. no_workbench
reason declared for future use but not emitted (the iteration shape
has no natural site for it).
- AlertsLog: connect + disconnect for all three signals using the same
has_signal-guarded pattern; translator handlers convert to localized
alert_added(severity, text, focus_tile).
- AlertsLog catch-up: room_too_large emits during World init, before
this CanvasLayer mounts. _catch_up_room_too_large() in _ready scans
World.rooms for rooms > ROOM_AUTOROOF_CAP and replays them, so the
pre-built cabin's 24-tile-too-large warning lands in the log on every
boot. Hauling/bill signals fire at runtime so they need no catch-up.
Verified runtime: cabin warning shows up in AlertsLog with severity
'warn' and focus_tile (45, 24) — the cabin top-left.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds full PC keyboard+mouse support on top of existing touch controls. Touch
paths untouched. All input goes through named actions in project.godot.
Bindings:
- WASD / arrows: camera pan (speed scales with zoom)
- = / -: keyboard zoom in/out
- C / Home: center on selected pawn
- Tab / Shift+Tab: cycle through pawns (pans camera to selection)
- B / L / P / ,: toggle BuildDrawer / AlertsLog / WorkPriorityMatrix / Settings
- Escape: cancel active designation tool > close topmost panel > deselect pawn
- Right-click: cancel active tool or deselect pawn (RTS convention)
- F: speed_cycle (action restored; handler still TODO)
- pawn_prev action removed; Shift+Tab read via event.shift_pressed inline
Escape priority enforced by Designation._input running before _unhandled_input
plus each panel consuming its own cancel action when visible.
Also fixes a pre-existing pre-Phase-17 bug: WorkPriorityMatrix, AlertsLog,
StorytellerModal, LoadMenu, and SettingsMenu had MOUSE_FILTER_STOP Controls
(Backdrop / Dim) that remained input-active when the panel was "closed" —
their open/close paths only toggled _root.visible / _panel.visible, never
CanvasLayer.visible. World mouse events (right-click deselect, left-click
pawn-select) were silently eaten. Now each _set_visible / open / close
toggles self.visible (the CanvasLayer) so input dispatch shuts off properly.
Verified end-to-end via MCP runtime: WASD pan, zoom keys, Tab+Shift+Tab
cycle, B-open + Escape-close, right-click deselect, left-click pawn-select
all working in sequence with no input bleed.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Three gdscript-refactor agents in parallel; Opus integrated and verified
the day-night transition + torch lighting via MCP runtime + screenshot.
Clock autoload (Agent A, autoload/clock.gd, ~138 lines):
- TICKS_PER_DAY = 4800 → 4 min/day at 1× / 48 s at Fast / 20 s at Ultra
- TICKS_PER_HOUR = 200 (so 60 min × ~3 ticks per minute)
- 4-phase day: night → dawn (5–7) → day (7–19) → dusk (19–22) → night
- darkness_factor() returns 0..1 with linear ramps across dawn/dusk
- phase_changed signal fires on phase transitions
- save_dict / apply_dict for save round-trip
- Boots at Day 1, 06:00 (mid-dawn for atmospheric start)
- Registered in project.godot autoload list (Opus)
Top-bar clock UI (Agent A):
- ClockLabel added to top_bar.tscn (center-anchored at ±80 px)
- _on_clock_refresh in top_bar.gd; early-out string compare to skip text
assignments when unchanged (cheap per-tick)
Torch entity + lights registry (Agent B, scenes/entities/torch.{gd,tscn} +
workbench.gd + world.gd, ~210 lines):
- class Torch: buildable furniture, BUILD_TICKS=30, LIGHT_RADIUS=6
- Procedural radial gradient texture (64×64) generated at runtime with
smoothstep falloff → no PNG dependency
- PointLight2D child with the gradient texture, warm fire tint, energy 1.2
- is_on / get_light_tile / get_light_radius duck-typed interface; same
shape exposed by Workbench when label_text='Hearth' (HEARTH_LIGHT_RADIUS=5)
- World.light_sources registry + register/unregister + is_tile_lit(tile)
(Manhattan distance, no occlusion — Phase 13 may add wall-occlusion)
CanvasModulate darkness + in_darkness thought (Agent C, ~30 lines mod +
new factory):
- DarkOverlay CanvasModulate node added to world.tscn (first child of
World root so it tints all sibling layers + entities)
- world.gd._update_dark_overlay lerps DAY_TINT (white) ↔ NIGHT_TINT
(0.20, 0.22, 0.40 deep cool blue) by Clock.darkness_factor() each tick
- ThoughtCatalog.in_darkness(): persistent, -3 mood, fires when
darkness > 0.3 AND World.is_tile_lit(pawn.tile) is false
- Pawn._process_thoughts syncs in_darkness alongside hungry/tired
Opus integration:
- project.godot: Clock autoload registered
- world.tscn: DarkOverlay CanvasModulate node, plus the agent additions
- Demo seed: 2 torches inside cabin at (46, 26) + (49, 26), pre-built
- MCP-driven runtime test verified day→night transition + lighting
effects:
- Noon: world bright green, torches barely visible (over-bright at noon
is minor polish — Phase 17 may scale torch energy by darkness)
- Midnight: world deep blue/green tinted, torches cast yellow halos,
Hearth ember glows orange, cabin interior warmly lit, exterior dark
- top_bar clock label updates each sim tick (early-out on no-change)
Phase 11 followups for later phases:
- Torch energy should scale with darkness — visible halos at noon are
silly. Phase 17 will likely tie PointLight2D.energy to clamp(darkness,
0.2, 1.0) so they're invisible at midday
- Wall-occlusion for light_map — Phase 13's room-detection BFS could
treat completed wall tiles as occluders so light doesn't bleed through
- 'In darkness' thought currently treats ALL unlit cells as darkness;
Phase 13's roof flag could differentiate 'indoors-dark' (different
thought) from 'outdoors-dark'
- Light source visibility through CanvasModulate works correctly thanks
to PointLight2D's additive blend mode
Acceptance — MCP-verified via play_scene + get_game_screenshot:
- ✅ Day → Dusk → Night cycle visible (Clock.current_phase emits events)
- ✅ CanvasModulate tints world deep blue at night
- ✅ Torches cast visible yellow halos via PointLight2D additive blend
- ✅ Hearth opts-in as a light source via label_text='Hearth' check
- ✅ Top-bar clock shows 'Day N, HH:MM' format and updates each tick
- ✅ in_darkness thought wires through _process_thoughts (would fire if
a pawn were standing in an unlit night tile — demo didn't capture this
specifically but the code path is verified)
Delegation report this phase:
- Agent A: Clock autoload + 4-phase day cycle + top-bar UI extension
- Agent B: Torch entity + PointLight2D + procedural radial texture +
Workbench Hearth opt-in + World.light_sources registry
- Agent C: CanvasModulate world.tscn node + day/night colour lerp +
in_darkness ThoughtCatalog entry + Pawn persistent thought sync
- Opus: Clock autoload registration in project.godot + 2 torches in
demo seed + MCP runtime verification at midnight vs noon
~75% of Phase 11 GDScript was subagent-authored.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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>
Project scaffold:
- project.godot at repo root, GL Compatibility renderer (max mobile reach),
pixel-snap on, texture filter nearest, sensor_landscape orientation
- 7 autoloads: World, Sim, GameState, EventBus, Strings, Audit, SaveSystem
- scenes/main/main.{tscn,gd} smoke-test scene with autoload assertions
- Folder layout matches tavernkeep idiom: autoload/ at root, scripts
co-located with scenes/ (not the scripts/autoloads/ mirror originally
sketched in implementation.md)
- Input map: pause, speed_cycle, speed_normal/fast/ultra, confirm, cancel.
Mobile gestures (pinch/drag/long-press) handled at script level via
Godot's InputEventScreenTouch/Drag/MagnifyGesture.
- SaveSystem skeleton: SAVE_VERSION=1, JSON to user://save_slot.json,
version-mismatch warning. Phase 3 expands to real entity state.
- icon.svg placeholder (cabin silhouette on dark green field)
- README.md points at memory.md / implementation.md / docs/
Headless verification: 'godot --headless --path . --quit' exits 0,
'[main] Phase 0 smoke test online.' prints, no errors. Editor-side
green-dot check still pending — needs human launch of editor.
Asset audit (researcher Haiku, 2026-05-10):
- FG_Houses.png NOT autotile-solvable — pre-built decorative house
compositions, 4 distinct roof palettes, no modular wall family.
~½–1 day per material to author terrain bits on top.
- FG_Fortress.png IS autotile-solvable — ~20–30 modular tan-stone
pieces. Wang-style Godot 4 terrain works with minimal extra art.
Iconic Homestead $19.99 fallback not needed.
- No wolf sprite anywhere in the bundle. EvoMonster packs all
cute/fantasy. Need commission, CC0 source, or Ventilatore check.
- Retro Graveyard 16x16 [Kingdom Explorer] confirmed in Tier 3 with
full graveyard suite — direct use in Phase 14.
New open questions surfaced in memory.md:
- Player-built wall material strategy (3 options laid out)
- Wolf sprite acquisition path (Phase 10 blocker)
Project move:
- Repo physical location moved from ~/claude/projects/rimlike to
/mnt/d/godot/rimlike (D: drive, fast for Windows-side editor).
- Symlink at the original WSL path preserves the home-CLAUDE.md
layout convention. Mirrors tavernkeep's pattern.
- Set core.filemode=false to silence DrvFs's everything-is-0777
false-positive on git diff.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>