diff --git a/docs/architecture.md b/docs/architecture.md index 1a6cc44..4a1370f 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -97,15 +97,17 @@ Multipliers drive sim tick stepping per render frame. At 60 Hz render with 20 Hz - Ultra (12×): 240 ticks/sec (4 ticks per render frame) - Pause: zero ticks queued -Per-tick budget at Ultra: ~4 ms to stay in 60 fps render. Achievable for our scope (3–6 pawns, < 500 entities, ≤ 60×60 tiles). +Per-tick budget at Ultra: ~4 ms to stay in 60 fps render. Achievable for our scope (3–6 pawns, < 500 entities, **80×80 MVP map; sized to a 120² ceiling**). At 80² the AStarGrid2D has 6.4k nodes; longest path ≤ 160 tiles; full A* runs sub-millisecond on a mid-range phone. At 120² (22.5k nodes) it's still well under a millisecond per query. ### Pawn movement -| Speed | Real-time crossing of 40-tile map | Feel | +| Speed | Real-time crossing of 80-tile map (corner-to-corner) | Feel | |---|---|---| -| 1× (1 tile / 0.5 s) | ~20 s | Watchable, deliberate | -| Fast (5×) | ~4 s | Snappy, default cadence | -| Ultra (12×) | ~1.7 s | Almost teleporting | +| 1× (1 tile / 0.5 s) | ~80 s | Watchable, deliberate | +| Fast (5×) | ~16 s | Snappy, default cadence | +| Ultra (12×) | ~7 s | Almost teleporting | + +Travel times are roughly 2× the original 40-tile estimates. A wolf spawning at the map edge gives ~20 s real-time lead at Fast — meaningful warning, not tedious. This mostly absorbs the "walking-speed problem" surfaced earlier — Fast as default keeps a 5-min day from feeling like watching paint dry. @@ -260,6 +262,10 @@ walk_to(item.cell) → pick_up(item, capped_at_carry_capacity) Carry capacity capped at one stack of one type. Multi-type carry is v2. +**No-destination fallback.** If `find_best_destination_for(item)` returns null on three consecutive periodic-flow passes (~15 sim seconds), the item is dropped where it lies, taken off the dirty set, and a passive "*No stockpile accepts X*" alert is queued. This prevents `_dirty_items` from cycling forever when the player has zero matching storage. The alert clears the moment a matching destination opens up (it re-enters the dirty set on the next storage-change event). + +**Container all-neighbors-blocked.** `best_drop_position(from)` returns the closest free 4-neighbor of the container. If all four are blocked, the hauler holds the carry and re-tries on the next pass; if still blocked after ~5 sim seconds the carry gets dropped on the hauler's current tile (same fallback as above) so it doesn't deadlock. + ### Container build flow 1. Player taps Build → Furniture → Crate. @@ -322,6 +328,8 @@ func compute_mood(p: Pawn) -> float: return clamp(m, 0, 100) ``` +`MAX_STACKS_PER_THOUGHT = 5`. Caps "saw corpse" / "ate without table" / "slept on floor" pile-ups so a single thought type can't deterministically tank mood by itself; the player still loses points from variety, which matches the Rimworld feel. + Thought registry (data file) drives content. Each thought entry knows: - Trigger: what game event creates it (saw_corpse from EntityProximity, slept_well from JobComplete, hungry from NeedThreshold, …) - Lifetime: persistent (driven by ongoing state) or event (decays after N hours) @@ -375,7 +383,7 @@ func recompute_light(affected_cells: Array[Vector2i]): Recomputed only when a light source is added/removed/state-changes (turned off due to fuel, etc.). Cheap. -**Render side:** at night, world rendered with darkness shader; the shader samples `light_map` to brighten tiles. Phone GPU runs this comfortably for 60×60 tiles. +**Render side:** at night, world rendered with darkness shader; the shader samples `light_map` to brighten tiles. Phone GPU runs this comfortably for the MVP 80×80 map (and the 120² ceiling) — light recompute touches only cells inside the affected light's radius (max_radius=8 → ≤ 200 cells), not the whole map. **Mood integration:** `is_lit(cell)` returns `light_map[cell] > 0.2`. Fires "In darkness" thought for pawns in unlit tiles at night. @@ -450,10 +458,6 @@ Decay: zero — dirtiness only goes down via Cleaning. 8th WorkProvider in the existing pawn AI. Scans dirty tiles in/near rooms with priority bias toward indoor tiles. Job toils: walk → clean (timed by Manual Labor skill) → set dirtiness to 0. -### Updated WorkProvider list (8) - -Construction · Mining · Hauling · **Cleaning** · Cooking · Plant · Doctor · Combat - ## Production: workbenches, recipes, bills For player rules and recipe lists, see [`design.md`](./design.md). This section is data + AI plug-in. @@ -889,6 +893,9 @@ func build_weighted_pool() -> Dictionary: var pool = {} for ev in EVENT_REGISTRY.values(): if not ev.trigger.matches(World.state, current_day): continue + # Both gates must pass: per-event AND per-category cooldown. + # Per-event prevents the *same* event repeating within its individual cooldown. + # Per-category enforces the design.md pacing rule (no two threats within 3 days, etc.). if recently_fired(ev.id, ev.cooldown_days): continue if recently_fired_category(ev.category, CATEGORY_COOLDOWN[ev.category]): continue var w = ev.weight * tension_modifier(ev.category) @@ -982,6 +989,8 @@ func on_wall_change(cell: Vector2i): `bfs_finds_exit(start)`: flood from `start`, treating walls as blockers; return true if we reach the map edge or a No-Roof designated cell within 8 steps. Fast at our scale (8² = 64 cells worst case per BFS, called on each candidate within the affected radius — still microseconds). +**Big-room feedback.** When `bfs_finds_exit` returns true *because the BFS ran out of steps without escaping or roofing* (i.e. the area is enclosed but larger than the cap), the EnclosureDetector emits a `room_too_large` signal carrying the centroid cell. The UI raises a one-shot ambient banner: "*This area is too large to roof. Split it with an interior wall.*" Threshold and exact UX wording TBD — see `memory.md` Open questions. + When a wall is destroyed: candidates are re-evaluated; some may flip from roof to no-roof. ### No-Roof designation @@ -1070,7 +1079,7 @@ Children of a `World` node, tile-aligned positions: ### Pathfinding -`AStarGrid2D`, one grid the size of the map. Walkable derived from TileMap data + furniture occupancy. Update affected cells on build/destroy/door-state-change. Microseconds at 60×60. +`AStarGrid2D`, one grid the size of the map. Walkable derived from TileMap data + furniture occupancy. Update affected cells on build/destroy/door-state-change (one cell per change → O(1)). Sub-millisecond per path query at 80²; still well under a millisecond at the 120² ceiling. ### Saving @@ -1078,7 +1087,7 @@ Per layer, `tilemap.get_used_cells_by_id(layer)` → JSON. Plus furniture entiti ### Performance -60×60 × 5 layers = 18k tiles, GPU-batched in one draw call per layer. ~100–500 entity nodes at peak. Comfortable 60fps on a mid-range phone with headroom. iOS export needs Mac/Xcode; Android export from Linux is fine. +80×80 × 5 layers = 32k tiles (MVP); 120×120 × 5 = 72k tiles (ceiling). GPU-batched in one draw call per layer regardless of cell count, so the bump is essentially free for the renderer. ~100–500 entity nodes at peak. Comfortable 60 fps on a mid-range phone with headroom. iOS export needs Mac/Xcode; Android export from Linux is fine. ## What lives elsewhere diff --git a/docs/design.md b/docs/design.md index b443d21..b96db6a 100644 --- a/docs/design.md +++ b/docs/design.md @@ -175,10 +175,6 @@ Floor tiles accumulate `dirtiness: float (0..100)`: Mostly low-skill chore work — gives Manual-Labor pawns something to do when not building/mining/hauling. -### Updated work category list (now 8) - -Construction · Mining · Hauling · **Cleaning** · Cooking · Plant · Doctor · Combat - ## Production chains & workbenches 2-step chains where it makes medieval sense; 1-step where simpler is fine. Going Medieval / Banished flow without runaway recipe authoring. @@ -540,6 +536,8 @@ When walls form an enclosed area ≤ 8 cells across, the interior **auto-roofs** Matches the player's mental model: *"I built four walls, now there's a room."* +**Big-room UX (open):** the ≤8-cell cap silently fails on rooms larger than 8×8. When the player encloses a too-large area, we should surface "this area is too large to roof — split it with an interior wall." Exact threshold + UI treatment is TBD (see `memory.md` Open questions). + ### "No-roof" designation Designation type: paint cells inside an enclosure to forbid auto-roofing. Useful for courtyards, fire pits, gardens. Same paint UX as stockpile zones. diff --git a/docs/ui.md b/docs/ui.md index 675e85a..1919f4b 100644 --- a/docs/ui.md +++ b/docs/ui.md @@ -610,11 +610,24 @@ Top bar also shows season + day-of-season ("Spring 4/12"). Tap → seasonal fore Wet pawns have a subtle drip particle + slight sprite tint until they dry off. Players can quickly spot "Bob is soaked, why?" without opening the pawn detail screen. +## World view camera (locked) + +The camera/navigation model on the main world view, decided 2026-05-10 alongside the bump from a 40² to an 80² map. + +- **Pinch-zoom**: between a "strategic" zoom (whole-map-ish on tablet, ~1/4 of the map on phone) and a "close" zoom (~16 tiles wide on phone, sprite-readable). No fixed zoom levels — smooth. +- **Drag-pan**: one-finger drag on empty world tiles. Drag on a pawn = select-and-drag-issue-order (long-press-then-drag for multi-select rectangle). +- **Double-tap to centre**: double-tap on a pawn portrait, alert banner, or the world centres the camera there with a brief animated pan. +- **No follow-camera**: selecting a pawn does **not** lock the view to them. Selection persists across pans so the player can scroll to a build site and issue an order without losing their pawn. +- **Jump-to-alert**: every storyteller alert / banner / event modal includes a *Go there* tap that pans-and-centres the camera on the relevant tile (raid spawn, downed pawn, fire, etc.). Replaces the "where is this happening?" minimap need. +- **No minimap in MVP**. Reasoning: phone screen real estate is precious, and *Jump-to-alert* + double-tap-on-portrait covers the navigation needs at 80². Revisit if playtest shows people getting lost. +- **Speed / pause buttons** stay fixed at the top regardless of camera state. + +Layered on the world view: select (tap empty pawn), inspect (long-press anything), build mode (bottom-sheet → paint), designation paint (bottom-sheet → designate type → paint). + ## Screens still to design These are open work for future sessions. Listed roughly in importance. -- **World view** — camera/select/orders. The screen the player sees most. Tap targets, long-press menus, pinch-zoom, multi-select (for multi-pawn orders). - **Build drawer** — bottom-sheet tabs (Walls / Floors / Furniture / Production / Designate). Designation paint mode. Material-pick UI when a build can use multiple materials. - **Alerts / storyteller event** — modal vs. ambient, dismissal, history of past prompts. - **Day-summary / end-of-day** — a recap card showing what changed today; gives short sessions a stopping point. diff --git a/memory.md b/memory.md index 4b81fd6..819fea6 100644 --- a/memory.md +++ b/memory.md @@ -37,7 +37,7 @@ Distilled from the brainstorm. Each lock has a "why" — change with deliberate | **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, 30×30 to 60×60 tile maps | Readable on a phone; cheap to simulate. | +| **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. | @@ -72,6 +72,7 @@ Distilled from the brainstorm. Each lock has a "why" — change with deliberate - **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 @@ -94,6 +95,7 @@ These are concrete checks to run before serious construction begins. Total ~75 m ### Design topics still open +- [ ] **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. @@ -120,7 +122,7 @@ These are concrete checks to run before serious construction begins. Total ~75 m 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**, ~40×40 tiles, fixed seed for now. +- **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). @@ -143,6 +145,17 @@ Same scope as locked in `~/claude/ideas/rimlike/plan.md`. Realistic timeline 3 ### 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. ## External references