diff --git a/README.md b/README.md new file mode 100644 index 0000000..49371fd --- /dev/null +++ b/README.md @@ -0,0 +1,29 @@ +# rimlike + +A 2D, tile-based **cute-farming-RPG-meets-colony-sim** for mobile and handheld. Rimworld DNA, Going Medieval × Stardew lodestars. Built on Godot 4. + +Working title `rimlike` — rename TBD. + +## Working in this repo + +- **Project rules:** [`CLAUDE.md`](./CLAUDE.md) +- **Decisions index + open questions:** [`memory.md`](./memory.md) +- **Phased build plan (the checklist):** [`docs/implementation.md`](./docs/implementation.md) +- **Game design:** [`docs/design.md`](./docs/design.md) +- **Tech / engine layout:** [`docs/architecture.md`](./docs/architecture.md) +- **Touch UX:** [`docs/ui.md`](./docs/ui.md) +- **Art / tilesets / license:** [`docs/art.md`](./docs/art.md) + +## On a fresh clone + +The proprietary **Godot MCP Pro** addon is gitignored. Re-copy from the local install before opening the project: + +```bash +cp -r /mnt/d/godot/mcp/addons/godot_mcp ./addons/ +``` + +Then open `project.godot` in Godot 4.6+. Project → Project Settings → Plugins → enable **Godot MCP Pro**. Look for the green dot in the "MCP Pro" bottom panel. + +## Engine + +Godot **4.6.2 stable**, GDScript only, GL Compatibility renderer (max mobile reach), 16×16 pixel art (texture filter = nearest, pixel snap on). diff --git a/art/fx/.gitkeep b/art/fx/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/art/sprites/.gitkeep b/art/sprites/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/art/tiles/.gitkeep b/art/tiles/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/art/ui/.gitkeep b/art/ui/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/audio/music/.gitkeep b/audio/music/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/audio/sfx/.gitkeep b/audio/sfx/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/autoload/audit.gd b/autoload/audit.gd new file mode 100644 index 0000000..21b36dc --- /dev/null +++ b/autoload/audit.gd @@ -0,0 +1,12 @@ +extends Node +## Debug-only logging + perf counters. No-op in release builds. +## +## Use Audit.log("category", "message") instead of print() — gives us a single +## switch to silence everything before ship. + +var enabled: bool = OS.is_debug_build() + + +func log(category: String, message: String) -> void: + if enabled: + print("[%s] %s" % [category, message]) diff --git a/autoload/event_bus.gd b/autoload/event_bus.gd new file mode 100644 index 0000000..e27834d --- /dev/null +++ b/autoload/event_bus.gd @@ -0,0 +1,9 @@ +extends Node +## Pure signal hub — notification-only, no state, no callbacks back into singletons. +## +## Subsystems mutate themselves; this bus only spreads the news. Add signals as +## features land — keep this file readable. See docs/architecture.md. + +# Phase 0 placeholder — no signals yet. +# Phase 1 will add tick / speed-change / pause signals. +# Phase 2 will add pawn-state signals (selected, deselected, walking, …). diff --git a/autoload/game_state.gd b/autoload/game_state.gd new file mode 100644 index 0000000..bd51622 --- /dev/null +++ b/autoload/game_state.gd @@ -0,0 +1,28 @@ +extends Node +## Top-level mutable game state. SaveSystem serializes via save_dict() / apply_dict(). +## +## Holds session-wide flags and the "what map are we on" pointer. Per-entity state +## lives on the entities themselves; per-tile state on the TileMap or World. +## See docs/architecture.md. + +var current_map_id: StringName = &"slice_temperate_forest" +var session_started_at_unix: int = 0 # for "you've been away X minutes" toast + + +func _ready() -> void: + session_started_at_unix = int(Time.get_unix_time_from_system()) + + +# Phase 16 expands these into real save round-trip. +func save_dict() -> Dictionary: + return { + "current_map_id": String(current_map_id), + "session_started_at_unix": session_started_at_unix, + } + + +func apply_dict(d: Dictionary) -> void: + if d.has("current_map_id"): + current_map_id = StringName(d["current_map_id"]) + if d.has("session_started_at_unix"): + session_started_at_unix = int(d["session_started_at_unix"]) diff --git a/autoload/save_system.gd b/autoload/save_system.gd new file mode 100644 index 0000000..aa6c052 --- /dev/null +++ b/autoload/save_system.gd @@ -0,0 +1,45 @@ +extends Node +## Save/load skeleton — version field, file IO, save-between-ticks contract. +## +## Saves only happen between sim ticks (Sim owns the loop). JobRunner mid-toil +## state must round-trip from day one — see docs/architecture.md. +## +## Phase 0: file-IO smoke test only. Phase 3 expands to real entity state. +## Phase 16 closes coverage of every system. + +const SAVE_VERSION: int = 1 +const SAVE_PATH: String = "user://save_slot.json" + + +func write_save() -> bool: + # Smoke-test payload. Phase 3 expands this. + var payload := { + "version": SAVE_VERSION, + "sim_tick": Sim.tick, + "game_state": GameState.save_dict(), + } + var f := FileAccess.open(SAVE_PATH, FileAccess.WRITE) + if f == null: + push_error("SaveSystem.write_save: cannot open %s" % SAVE_PATH) + return false + f.store_string(JSON.stringify(payload)) + return true + + +func read_save() -> Dictionary: + if not FileAccess.file_exists(SAVE_PATH): + return {} + var f := FileAccess.open(SAVE_PATH, FileAccess.READ) + if f == null: + return {} + var raw := f.get_as_text() + var parsed: Variant = JSON.parse_string(raw) + if typeof(parsed) != TYPE_DICTIONARY: + push_error("SaveSystem.read_save: corrupt save") + return {} + if int(parsed.get("version", 0)) != SAVE_VERSION: + push_warning( + "SaveSystem.read_save: version mismatch (%s vs %s)" % + [parsed.get("version", "?"), SAVE_VERSION] + ) + return parsed diff --git a/autoload/sim.gd b/autoload/sim.gd new file mode 100644 index 0000000..4595aff --- /dev/null +++ b/autoload/sim.gd @@ -0,0 +1,28 @@ +extends Node +## Sim tick loop owner. Sim runs at 20 Hz, render at 60 Hz, decoupled. +## +## Speed factor controls how many sim ticks queue per render frame: +## 1× — 1 tick per 3 render frames (20 ticks/s) +## Fast — 5 ticks per 3 render frames (~100 ticks/s) +## Ultra — 12 ticks per 3 render frames (~240 ticks/s) +## Pause — 0 +## +## Saves are taken between ticks only — never mid-tick — so JobRunner mid-toil +## state round-trips cleanly. See docs/architecture.md "Time / tick model". + +const SIM_HZ: int = 20 +const TICK_INTERVAL_S: float = 1.0 / float(SIM_HZ) + +enum Speed { PAUSE, NORMAL, FAST, ULTRA } + +const SPEED_FACTOR: Dictionary = { + Speed.PAUSE: 0, + Speed.NORMAL: 1, + Speed.FAST: 5, + Speed.ULTRA: 12, +} + +var current_speed: Speed = Speed.PAUSE +var tick: int = 0 + +# Tick-loop body lands in Phase 1. diff --git a/autoload/strings.gd b/autoload/strings.gd new file mode 100644 index 0000000..8d7367b --- /dev/null +++ b/autoload/strings.gd @@ -0,0 +1,22 @@ +extends Node +## i18n string table — player-visible strings ONLY. Code keys, EN values. +## +## Locked from day one (per CLAUDE.md): no hardcoded display copy in scenes or +## scripts. If you have player-facing text, add a key here and call Strings.t(key). +## +## Locale switching is post-MVP; the indirection lands now so we don't have to +## retrofit the whole game later. When the table grows, move it to a .tres or +## external CSV import; the public API (`Strings.t(key)`) stays the same. + +const TABLE: Dictionary = { + # Phase 0 placeholder — populate as features land. + &"app.title": "Rimlike", + &"smoke.hello": "Phase 0 — autoloads online.", +} + + +func t(key: StringName) -> String: + if TABLE.has(key): + return TABLE[key] + push_warning("Strings.t(): missing key %s" % key) + return String(key) diff --git a/autoload/world.gd b/autoload/world.gd new file mode 100644 index 0000000..da527f8 --- /dev/null +++ b/autoload/world.gd @@ -0,0 +1,14 @@ +extends Node +## Runtime entity registry + tile-related sim state. +## +## All gameplay entities (pawns, items, furniture, animals, corpses) live here. +## TileMap data is owned by the world-view scene; World holds the *indirect* +## state (designation queue, dirty-haul set, zone records, etc.) that doesn't +## belong on the TileMap itself. +## +## See docs/architecture.md. + +# Phase 1 will add the entity registries (pawns, items, furniture). +# Phase 4 will add `items_needing_haul` (the dirty set). +# Phase 5 will add the BuildJob queue. +# Phase 13 will add zones / rooms / dirtiness. diff --git a/data/events/.gitkeep b/data/events/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/data/pawns/.gitkeep b/data/pawns/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/data/recipes/.gitkeep b/data/recipes/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/data/thoughts/.gitkeep b/data/thoughts/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/data/weather/.gitkeep b/data/weather/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/docs/art.md b/docs/art.md index 9167fa7..d8c0ba3 100644 --- a/docs/art.md +++ b/docs/art.md @@ -92,12 +92,24 @@ Even with the bundle, these need authoring or sourcing: ## Audit list (action items before any release build) -- [ ] Autotile coverage of `Houses Tileset 2 Seasons/FG_Houses.png` — count corner/T-junction/cap pieces for player-built walls. -- [ ] Autotile coverage of `Fortress Tileset 2 Seasons/FG_Fortress.png` (alt material). -- [ ] Aesthetic harmony test: open one tile from ElvGames Forest 4 Seasons + one from Ventilatore + render in same scene. Is the palette cohesive enough to mix? Decides whether Ventilatore stays as accent or gets shelved. -- [ ] Wolf sprite source — confirm what's available from EvoMonster / Turn-Based Monster packs; if none, plan a custom. -- [ ] Confirm Retro Graveyard 16×16 has grave-marker tile we can use. -- [ ] License attribution credits document — list every pack we use, credit string for game credits screen. +**Audit results, 2026-05-10** — three of five closed via visual inspection (researcher Haiku subagent + verification). + +- [x] **`FG_Houses.png` autotile coverage** — **NOT autotile-solvable.** Pieces are pre-built decorative house compositions across 4 distinct roof palettes (blue-roof timber, red-brick, dark-slate, plus accent details). No corner / T-junction / cap modular family. Estimated cost to author terrain bits on top: ~½–1 day per material. +- [x] **`FG_Fortress.png` autotile coverage** — **autotile-solvable.** ~20–30 modular tan-stone pieces (straight, corners, battlement caps) with dark-grid mortar lines. Wang-style Godot 4 terrain works with minimal extra art. Iconic Homestead fallback not needed. +- [ ] **Aesthetic harmony test** — still open. Needs your eye on a Forest tile + a Ventilatore tile side-by-side. ~15 min. +- [x] **Wolf sprite source** — **none in the bundle.** Scanned EvoMonster Packs 03/04/06/09/10 (cute fantasy creatures, no canines), broader Tier 1–3 sweep (farm animals + humanoid monsters only). Action items in `memory.md` Open questions: commission, find CC0, or check Ventilatore. +- [x] **Grave marker** — **`Retro Graveyard 16x16 Tileset [Kingdom Explorer]` confirmed in Tier 3.** Path: `Ultimate Farming RPG Tier 3/Retro Graveyard 16x16 Tileset [Kingdom Explorer]/Tilesets/KE_Graveyard.png`. Contents: tombstones (varied), wooden crosses, grave mounds, crypt entries, skeletal markers, ground-transition tiles. Direct use in Phase 14. +- [ ] **License attribution credits document** — kick off the list now; add to it as packs come in. Include Kingdom Explorer (likely separate license from ElvGames — check `License.txt`). + +## Wall-material decision (consequence of the audit) + +The `FG_Houses` non-autotile finding is the biggest live blocker. `architecture.md` and `implementation.md` Phase 5 assume the player paints walls and they auto-junction. Options now under consideration (in `memory.md` Open questions): + +1. **Fortress-stone-only** — player walls use `FG_Fortress` exclusively. `FG_Houses` becomes static prebuilt-shelter art (no construction verb for cabins). Aesthetic shifts toward Going Medieval; loses some of the cute-cabin Stardew warmth. +2. **Custom-author wood walls** on top of `FG_Houses` (~½–1 day per material). Preserves aesthetic; delays Phase 5 by ~3 days. +3. **Mix**: `FG_Houses` static = starter shelter (drop-in, can't deconstruct), `FG_Fortress` autotile = player-built upgrade walls. Two materials, no custom art investment, but loses the "build your own first cabin" beat. + +Decision needed before Phase 1 wall-tileset import (Phase 1 needs at least one wall material to render). ## Tileset prep (the gotcha) diff --git a/docs/implementation.md b/docs/implementation.md index 723f9b1..52f2ea8 100644 --- a/docs/implementation.md +++ b/docs/implementation.md @@ -6,7 +6,8 @@ Effort estimates are wall-time at **focused solo pace**. Scale up generously for | Status | Phase | |---|---| -| ⏳ next | **Phase 0 — Project scaffold & foundations** | +| ✅ scaffold landed (headless-verified); awaits editor-side green-dot check | **Phase 0 — Project scaffold & foundations** | +| ⏳ next | **Phase 1 — World, tilemap, camera** | Use this doc as a checklist: tick boxes as items complete, and update the **Status** row above whenever a phase rolls over. The last bullet of each phase is the *acceptance demo* — the phase is "done" when you can perform it. @@ -18,10 +19,14 @@ Refs to `docs/` files are linked so each item lands in the right spec. The five items from `memory.md` *Open questions / Audit*. None of these need code, but several of them gate Phase 1+ (autotile drives the wall pipeline; aesthetic harmony decides whether Ventilatore stays in active use). Knock these out before Phase 0. -- [ ] **Aesthetic harmony test** — ElvGames Forest vs Ventilatore tile, side-by-side. Decide use-both or drop-Ventilatore. (~15 min) -- [ ] **ElvGames autotile audit** — count corner / T / cap pieces in `Houses Tileset 2 Seasons/FG_Houses.png` and `Fortress Tileset 2 Seasons/FG_Fortress.png`. ≥16 wall variants → autotile is solvable; <8 → fall back to Iconic Homestead ($19.99) or custom-author. (~15–30 min) -- [ ] **Wolf sprite source** — bundle's Animal Sprites lacks wolves. Browse EvoMonster Packs 01–15 or Turn-Based RPG Monster packs. Or plan custom (~few hours). (~15 min) -- [ ] **Grave marker source** — Retro Graveyard 16×16 (Tier 3) probably has it. (~10 min) +- [ ] **Aesthetic harmony test** — ElvGames Forest vs Ventilatore tile, side-by-side. Decide use-both or drop-Ventilatore. (~15 min) — **needs your eye** +- [x] **ElvGames autotile audit** — done 2026-05-10. Findings (visual inspection): + - **`FG_Houses.png` is NOT autotile-solvable as-is.** Pieces are pre-built decorative house compositions (4 distinct roof palettes), not modular wall variants. ~½–1 day per material to author terrain bits on top. + - **`FG_Fortress.png` IS autotile-solvable.** 20–30 modular tan-stone-with-dark-mortar pieces — straight, corners, caps. Wang-style Godot 4 terrain works with minimal extra art. + - **Recommendation:** make Fortress stone the primary player wall material. Defer custom-authored Houses walls to v2 OR keep Houses as static prebuilt-shelter art only. + - Iconic Homestead $19.99 fallback **not needed**. +- [x] **Wolf sprite source** — done 2026-05-10. **No wolf in the bundle.** EvoMonster packs are all cute/fantasy creatures (slimes, ghosts, dragons), Turn-Based RPG Monsters are humanoid-style. No 4-legged canine predator anywhere in the bundle. Action: commission a 16×16 wolf (idle + 2–4-frame walk × 4 directions) OR check Ventilatore bundle OR find a CC0 sprite. **Open in `memory.md`.** +- [x] **Grave marker source** — done 2026-05-10. `Retro Graveyard 16x16 Tileset [Kingdom Explorer]` confirmed in Tier 3, full graveyard suite (tombstones, crosses, mounds, crypts). - [ ] **License compilation start** — kick off the credits string list now; add to it as packs come in. (open across phases) --- @@ -30,27 +35,29 @@ The five items from `memory.md` *Open questions / Audit*. None of these need cod **Goal:** a Godot project that opens cleanly, has all the autoloads and folder structure committed, and runs an empty test scene. -- [ ] `godot --headless` (or editor) → New Project at repo root, Forward+ renderer (mobile-friendly path TBD; check what export complains about) -- [ ] Re-copy `addons/godot_mcp/` from `/mnt/d/godot/mcp/addons/godot_mcp` and enable in Project Settings → Plugins. Verify green dot in MCP Pro bottom panel. -- [ ] Folder layout: - - `scenes/` (`world/`, `pawn/`, `ui/`, `entities/`, `effects/`) - - `scripts/` (mirrors scenes/, plus `autoloads/`, `systems/`, `ai/`, `data/`) +- [x] `project.godot` at repo root. **GL Compatibility renderer** (max mobile reach; Forward+ would lock out older devices). Pixel-snap on, texture filter = nearest. Landscape sensor orientation. Viewport 1280×720 (`canvas_items` stretch, `keep` aspect). +- [x] Re-copy `addons/godot_mcp/` from `/mnt/d/godot/mcp/addons/godot_mcp` and enable in `project.godot` `[editor_plugins]`. ⏳ **Editor-side green-dot check pending** — needs you to open the editor once. +- [x] Folder layout (cribbed from tavernkeep's idiomatic Godot pattern — co-located scripts in `scenes/`, `autoload/` at root): + - `autoload/` — singletons (`world.gd`, `sim.gd`, `game_state.gd`, `event_bus.gd`, `strings.gd`, `audit.gd`, `save_system.gd`) + - `scenes/` (`main/`, `world/`, `pawn/`, `ui/`, `entities/`, `effects/`) - `data/` (`recipes/`, `thoughts/`, `events/`, `weather/`, `pawns/`) - `art/` (`tiles/`, `sprites/`, `ui/`, `fx/`) - `audio/` (`sfx/`, `music/`) - - `tests/` (GUT or built-in test runner — pick later) -- [ ] Autoloads (empty stubs, real bodies land in later phases): + - `tests/`, `tools/` +- [x] Autoloads (stubs; real bodies land in later phases): - `World` — entity registry, tile state, signals - - `Sim` — tick loop owner, speed/pause state - - `GameState` — current map, save metadata, game-mode flags - - `EventBus` — global signal bus - - `Strings` — i18n string table (string keys → EN values; load from `data/strings.en.tres`) - - `Audit` — debug-only logging, perf counters -- [ ] Input map: `tap`, `long_press`, `pinch_in`, `pinch_out`, `drag`, `pause`, `speed_cycle`. Touch + gamepad mappings. -- [ ] Save/load skeleton: `SaveSystem.save(slot)` / `SaveSystem.load(slot)` — version field, file IO, save-between-ticks contract enforced (no save calls allowed mid-tick). Round-trip a literal "hello" for now. -- [ ] `.gitignore` already covers Godot output; verify `.godot/`, `*.import`, `addons/godot_mcp/`, exports are all excluded -- [ ] Smoke-test scene: blank `World.tscn` with a `Camera2D`, hit Play, see a coloured background. Done. -- [ ] **Acceptance:** `godot --headless --quit` exits 0; clicking Play in editor opens a blank world without errors; `MCP Pro` panel shows the green dot. + - `Sim` — tick loop owner, speed/pause state, Speed enum + factor table + - `GameState` — current map, session timestamp, `save_dict()` / `apply_dict()` + - `EventBus` — global signal hub (no signals yet — added per-phase) + - `Strings` — i18n string table (`Strings.t(key)` lookup; const dict for now, .tres later) + - `Audit` — debug-only logging gate + - `SaveSystem` — `write_save()` / `read_save()`, version + path, JSON +- [x] Input map: `pause`, `speed_cycle`, `speed_normal`, `speed_fast`, `speed_ultra`, `confirm`, `cancel`. Mobile gestures (pinch / drag / long-press) handled at script level, not as input actions — Godot's `InputEventScreenTouch` / `InputEventScreenDrag` / `InputEventMagnifyGesture` cover them. +- [x] `SaveSystem` skeleton: version field (`SAVE_VERSION = 1`), `user://save_slot.json`, JSON serialize, mismatch warning. **Smoke-test payload only**; Phase 3 expands. +- [x] `.gitignore` covers `.godot/`, `.import/`, `addons/godot_mcp/`, exports — verified. +- [x] Smoke-test scene: `scenes/main/main.tscn` (Node + Camera2D + Label). `main.gd._ready()` asserts every autoload alive and shows the i18n-resolved hello string. +- [x] **Acceptance (headless):** `godot --headless --path . --quit` exits 0, `[main] Phase 0 smoke test online.` prints, no errors. **Confirmed.** +- [ ] **Acceptance (editor):** open `project.godot` in Godot 4.6, hit Play — see "Phase 0 — autoloads online." rendered at 32,32. MCP Pro bottom panel shows green dot. ⏳ **Needs your hand.** --- diff --git a/icon.svg b/icon.svg new file mode 100644 index 0000000..9264810 --- /dev/null +++ b/icon.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/memory.md b/memory.md index 6e6940b..94de5e6 100644 --- a/memory.md +++ b/memory.md @@ -41,6 +41,9 @@ Distilled from the brainstorm. Each lock has a "why" — change with deliberate | **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. | ### Architecture (tech) @@ -86,16 +89,22 @@ Distilled from the brainstorm. Each lock has a "why" — change with deliberate ### Audit / unblock-the-prototype action items -These are concrete checks to run before serious construction begins. Total ~75 min. +Total ~75 min. **3 of 5 closed on 2026-05-10**; see session log + `docs/art.md` for findings. Two open. -- [ ] **Aesthetic harmony test** — open one tile from ElvGames Forest 4 Seasons + one from Ventilatore + view side-by-side. Decide whether they mesh (use both) or clash (drop Ventilatore from active use). ~15 min. -- [ ] **ElvGames autotile audit** — count corner / T-junction / cap pieces in `Houses Tileset 2 Seasons/FG_Houses.png` and `Fortress Tileset 2 Seasons/FG_Fortress.png`. ≥16 wall variants per material → autotile is solvable; <8 → fall back to Mana Seed Iconic Homestead ($19.99) or custom-author. ~15–30 min. -- [ ] **Wolf sprite source** — bundle's Animal Sprites pack lacks wolves. Browse EvoMonster Packs 01–15 or Turn-Based RPG Monster packs for a suitable predator. Or plan custom (~few hours pixel art). ~15 min. -- [ ] **Grave marker source** — Retro Graveyard 16×16 (Tier 3) probably has it; verify. ~10 min. -- [ ] **License compilation** — maintain credits string for every pack used (ElvGames + Ventilatore + any others). Display in game's credits screen. Confirm specific terms per pack before any commercial release. +- [ ] **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 +- [ ] **Player-built wall material strategy** (NEW from 2026-05-10 audit): `FG_Houses.png` doesn't autotile — it's pre-built house compositions, not modular wall pieces. Options: + - (a) **Fortress-stone-only** for player walls (uses `FG_Fortress.png` autotile-solvable). Cabin starts as a pre-placed `FG_Houses` static prop; player upgrades to stone fortress walls. Aesthetic shifts toward Going Medieval, away from cute-cabin Stardew vibe. + - (b) **Custom-author wood + stone walls** on top of `FG_Houses.png` and `FG_Fortress.png` — ~½–1 day per material. Preserves the cute-farming-RPG warmth but delays Phase 5 by ~3 days. + - (c) **Mix**: use `FG_Houses` static prebuilt as starter shelter (no construction verb for it) + `FG_Fortress` autotile for player-built upgrade walls. Two materials, no custom art investment, but loses the "build your own first cabin" beat. + - Decision before Phase 1 wall-tileset import (Phase 1 needs at least one wall material to render). +- [ ] **Wolf sprite acquisition** (NEW from 2026-05-10 audit): bundle has nothing canine-predator. Options: (a) commission a 16×16 wolf (idle + 2–4-frame walk × 4 directions; ~few hours pixel art or paid commission ~$30–60); (b) check Ventilatore bundle for a usable beast; (c) source a CC0 wolf from OpenGameArt; (d) reskin an existing animal placeholder (dark dog?) for MVP and replace later. Phase 10 blocker; can defer until then. - [ ] **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." @@ -157,7 +166,13 @@ Same scope as locked in `~/claude/ideas/rimlike/plan.md`. Realistic timeline 3 - 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; current value is **Phase 0 — Project scaffold & foundations** (gated by the 75-min pre-implementation audit). Wired into the index table above. +- 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. ## External references diff --git a/project.godot b/project.godot new file mode 100644 index 0000000..8cf6cd8 --- /dev/null +++ b/project.godot @@ -0,0 +1,85 @@ +; Engine configuration file. +; It's best edited using the editor UI and not directly, +; since the parameters that go here are not all obvious. +; +; Format: +; [section] ; section goes between [] +; param=value ; assign values to parameters + +config_version=5 + +[application] + +config/name="Rimlike" +config/description="2D tile-based cute-farming-RPG meets colony sim. Mobile-first, Godot 4." +run/main_scene="res://scenes/main/main.tscn" +config/features=PackedStringArray("4.6", "GL Compatibility") +config/icon="res://icon.svg" + +[autoload] + +EventBus="*res://autoload/event_bus.gd" +Strings="*res://autoload/strings.gd" +Audit="*res://autoload/audit.gd" +GameState="*res://autoload/game_state.gd" +World="*res://autoload/world.gd" +Sim="*res://autoload/sim.gd" +SaveSystem="*res://autoload/save_system.gd" + +[display] + +window/size/viewport_width=1280 +window/size/viewport_height=720 +window/stretch/mode="canvas_items" +window/stretch/aspect="keep" +window/handheld/orientation="sensor_landscape" + +[editor_plugins] + +enabled=PackedStringArray("res://addons/godot_mcp/plugin.cfg") + +[input] + +pause={ +"deadzone": 0.5, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":32,"physical_keycode":0,"key_label":0,"unicode":32,"location":0,"echo":false,"script":null) +] +} +speed_cycle={ +"deadzone": 0.5, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":4194306,"physical_keycode":0,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) +] +} +speed_normal={ +"deadzone": 0.5, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":49,"physical_keycode":0,"key_label":0,"unicode":49,"location":0,"echo":false,"script":null) +] +} +speed_fast={ +"deadzone": 0.5, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":50,"physical_keycode":0,"key_label":0,"unicode":50,"location":0,"echo":false,"script":null) +] +} +speed_ultra={ +"deadzone": 0.5, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":51,"physical_keycode":0,"key_label":0,"unicode":51,"location":0,"echo":false,"script":null) +] +} +confirm={ +"deadzone": 0.5, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":4194309,"physical_keycode":0,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) +] +} +cancel={ +"deadzone": 0.5, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":4194305,"physical_keycode":0,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) +] +} + +[rendering] + +textures/canvas_textures/default_texture_filter=0 +renderer/rendering_method="gl_compatibility" +renderer/rendering_method.mobile="gl_compatibility" +2d/snap/snap_2d_transforms_to_pixel=true +2d/snap/snap_2d_vertices_to_pixel=true diff --git a/scenes/effects/.gitkeep b/scenes/effects/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/scenes/entities/.gitkeep b/scenes/entities/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/scenes/main/main.gd b/scenes/main/main.gd new file mode 100644 index 0000000..b421539 --- /dev/null +++ b/scenes/main/main.gd @@ -0,0 +1,20 @@ +extends Node +## Phase 0 smoke-test scene root. +## +## Verifies the autoload graph is alive and the i18n table resolves a key. +## Once Phase 1 lands the world view, this becomes the bootstrap that loads +## the right scene based on game state (new game / continue / settings). + +@onready var hello_label: Label = $HelloLabel + + +func _ready() -> void: + Audit.log("main", "Phase 0 smoke test online.") + # Verify autoloads are alive. + assert(World != null, "World autoload missing") + assert(Sim != null, "Sim autoload missing") + assert(GameState != null, "GameState autoload missing") + assert(EventBus != null, "EventBus autoload missing") + assert(Strings != null, "Strings autoload missing") + assert(SaveSystem != null, "SaveSystem autoload missing") + hello_label.text = Strings.t(&"smoke.hello") diff --git a/scenes/main/main.tscn b/scenes/main/main.tscn new file mode 100644 index 0000000..b6f27a2 --- /dev/null +++ b/scenes/main/main.tscn @@ -0,0 +1,15 @@ +[gd_scene load_steps=2 format=3 uid="uid://rimlike_main"] + +[ext_resource type="Script" path="res://scenes/main/main.gd" id="1_main"] + +[node name="Main" type="Node"] +script = ExtResource("1_main") + +[node name="Camera2D" type="Camera2D" parent="."] + +[node name="HelloLabel" type="Label" parent="."] +offset_left = 32.0 +offset_top = 32.0 +offset_right = 800.0 +offset_bottom = 96.0 +text = "(boot)" diff --git a/scenes/pawn/.gitkeep b/scenes/pawn/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/scenes/ui/.gitkeep b/scenes/ui/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/scenes/world/.gitkeep b/scenes/world/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/tests/.gitkeep b/tests/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/tools/.gitkeep b/tools/.gitkeep new file mode 100644 index 0000000..e69de29