Phase 0 scaffold + asset audit findings
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>
This commit is contained in:
parent
daf26ed27a
commit
128294c14f
33 changed files with 389 additions and 35 deletions
29
README.md
Normal file
29
README.md
Normal file
|
|
@ -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).
|
||||
0
art/fx/.gitkeep
Normal file
0
art/fx/.gitkeep
Normal file
0
art/sprites/.gitkeep
Normal file
0
art/sprites/.gitkeep
Normal file
0
art/tiles/.gitkeep
Normal file
0
art/tiles/.gitkeep
Normal file
0
art/ui/.gitkeep
Normal file
0
art/ui/.gitkeep
Normal file
0
audio/music/.gitkeep
Normal file
0
audio/music/.gitkeep
Normal file
0
audio/sfx/.gitkeep
Normal file
0
audio/sfx/.gitkeep
Normal file
12
autoload/audit.gd
Normal file
12
autoload/audit.gd
Normal file
|
|
@ -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])
|
||||
9
autoload/event_bus.gd
Normal file
9
autoload/event_bus.gd
Normal file
|
|
@ -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, …).
|
||||
28
autoload/game_state.gd
Normal file
28
autoload/game_state.gd
Normal file
|
|
@ -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"])
|
||||
45
autoload/save_system.gd
Normal file
45
autoload/save_system.gd
Normal file
|
|
@ -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
|
||||
28
autoload/sim.gd
Normal file
28
autoload/sim.gd
Normal file
|
|
@ -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.
|
||||
22
autoload/strings.gd
Normal file
22
autoload/strings.gd
Normal file
|
|
@ -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)
|
||||
14
autoload/world.gd
Normal file
14
autoload/world.gd
Normal file
|
|
@ -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.
|
||||
0
data/events/.gitkeep
Normal file
0
data/events/.gitkeep
Normal file
0
data/pawns/.gitkeep
Normal file
0
data/pawns/.gitkeep
Normal file
0
data/recipes/.gitkeep
Normal file
0
data/recipes/.gitkeep
Normal file
0
data/thoughts/.gitkeep
Normal file
0
data/thoughts/.gitkeep
Normal file
0
data/weather/.gitkeep
Normal file
0
data/weather/.gitkeep
Normal file
24
docs/art.md
24
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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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.**
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
13
icon.svg
Normal file
13
icon.svg
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128" viewBox="0 0 128 128">
|
||||
<rect width="128" height="128" fill="#3a4a2a"/>
|
||||
<rect x="0" y="80" width="128" height="48" fill="#5a3a22"/>
|
||||
<rect x="40" y="48" width="48" height="40" fill="#8a6a3a"/>
|
||||
<polygon points="32,48 64,16 96,48" fill="#3a2418"/>
|
||||
<rect x="58" y="64" width="12" height="24" fill="#1a0f08"/>
|
||||
<rect x="44" y="56" width="8" height="8" fill="#f0c060"/>
|
||||
<rect x="76" y="56" width="8" height="8" fill="#f0c060"/>
|
||||
<rect x="8" y="96" width="8" height="8" fill="#7a8a5a"/>
|
||||
<rect x="20" y="100" width="8" height="8" fill="#7a8a5a"/>
|
||||
<rect x="100" y="98" width="8" height="8" fill="#7a8a5a"/>
|
||||
<rect x="112" y="104" width="8" height="8" fill="#7a8a5a"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 749 B |
29
memory.md
29
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
|
||||
|
||||
|
|
|
|||
85
project.godot
Normal file
85
project.godot
Normal file
|
|
@ -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
|
||||
0
scenes/effects/.gitkeep
Normal file
0
scenes/effects/.gitkeep
Normal file
0
scenes/entities/.gitkeep
Normal file
0
scenes/entities/.gitkeep
Normal file
20
scenes/main/main.gd
Normal file
20
scenes/main/main.gd
Normal file
|
|
@ -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")
|
||||
15
scenes/main/main.tscn
Normal file
15
scenes/main/main.tscn
Normal file
|
|
@ -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)"
|
||||
0
scenes/pawn/.gitkeep
Normal file
0
scenes/pawn/.gitkeep
Normal file
0
scenes/ui/.gitkeep
Normal file
0
scenes/ui/.gitkeep
Normal file
0
scenes/world/.gitkeep
Normal file
0
scenes/world/.gitkeep
Normal file
0
tests/.gitkeep
Normal file
0
tests/.gitkeep
Normal file
0
tools/.gitkeep
Normal file
0
tools/.gitkeep
Normal file
Loading…
Add table
Add a link
Reference in a new issue