From 9a18407a3eeaf4cb6c35df955b5e974c4e6ea988 Mon Sep 17 00:00:00 2001 From: Claude Code Date: Mon, 30 Mar 2026 22:20:29 -0700 Subject: [PATCH] =?UTF-8?q?docs(claude):=20=F0=9F=93=9D=20Update=20Claude?= =?UTF-8?q?=20feature=20documentation=20in=20CLAUDE.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Lilith Autocommit --- CLAUDE.md | 128 +++++++++++++++++++++++++++++++----------------------- 1 file changed, 73 insertions(+), 55 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 18f7b40e..4c4b1855 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -11,7 +11,8 @@ Fantasy 4X turn-based strategy game (Civ5 + Master of Magic + Magic: The Gatheri ## Tech Stack - **Engine:** Godot 4.x -- **Language:** GDScript only (no C#, no GDExtension) +- **Simulation:** Rust (`packages/physics-rs/`) — compiled to GDExtension for the game, WASM for the web guide +- **Scripting:** GDScript — presentation layer only (rendering, UI, input, signals) - **Data:** JSON game packs (`games/age-of-dwarves/data/*.json`) - **Architecture:** Genre-agnostic engine with game pack content system @@ -19,7 +20,9 @@ Fantasy 4X turn-based strategy game (Civ5 + Master of Magic + Magic: The Gatheri The engine is **genre-agnostic**. All game content and display text comes from game packs. The fantasy game "Age of Dwarves" is the default. See `docs/engine/ABSTRACTION.md` (to be written). -**Godot is the primary runtime. GDScript + JSON is the source of truth for everything.** The TypeScript in `packages/` and `guide/` exists only to serve the web guide — it is secondary, generated, and must never be the canonical location for game data or logic. When creating new systems, data structures, or content packages: design for Godot/GDScript first, with JSON data files in the engine tree (`engine/` or `games/`). The TS side consumes those same JSON files through bundler imports. Never create TypeScript-only packages for game content that Godot needs to read. +**Rust is the simulation source of truth.** All game logic (physics, combat, economy, AI, pathfinding, magic, tech, turn resolution) lives in `packages/physics-rs/` and is compiled to two targets: GDExtension for the Godot game, WASM for the web guide. GDScript is the presentation layer — rendering, UI, input, signals, and thin wrappers that delegate to the Rust GDExtension. + +**JSON game packs remain the canonical content store.** Stats, costs, effects, thresholds — all in `games/age-of-dwarves/data/*.json`. Neither Rust nor GDScript hardcodes game content. - UI labels resolve through `ThemeVocabulary.lookup(engine_key)` — never hardcode theme strings - Sprites resolve through `ThemeAssets.resolve(path)` — never hardcode asset paths @@ -36,50 +39,39 @@ See `README.md` for the full doc index. Docs live in two places: Build process docs (roadmap, feature gap, task lists) stay in `.project/`. -## Single Source of Truth: GDScript → TypeScript +## Single Source of Truth: Rust → GDExtension + WASM -> **The GDScript climate engine is the source of truth for all simulation logic. The TypeScript in `guide/` is generated output — never edited directly. If the guide gives wrong results, fix the GDScript and re-run the transpiler.** - -The climate simulation runs in two runtimes: GDScript (Godot game engine) and TypeScript (web guide). GDScript is written first; TypeScript is auto-generated from it via `lilith-gdscript-transpiler`. - -### Architecture +> **The Rust crate `packages/physics-rs/` is the source of truth for all simulation logic. It compiles to two targets. Never duplicate simulation logic in GDScript or TypeScript.** ``` -games/age-of-dwarves/data/climate_spec.json ← Canonical thresholds, events, ley rules - ↓ read at runtime by both engines -engine/src/modules/climate/climate.gd ← SOURCE OF TRUTH (physics steps) -engine/src/modules/climate/ecological_events.gd ← SOURCE OF TRUTH (stochastic events) -engine/src/modules/climate/anchor_decay.gd ← SOURCE OF TRUTH (ley anchor decay) -engine/src/modules/climate/climate_spec_eval.gd ← SOURCE OF TRUTH (spec condition evaluator) - ↓ transpiled by tools/transpile-engine/transpile.py -packages/engine-ts/src/ClimatePhysics.generated.ts ← AUTO-GENERATED, never edit -packages/engine-ts/src/index.ts ← Re-exports for @magic-civ/engine-ts -guide/age-of-dwarves/ ← Consumes @magic-civ/engine-ts (workspace:*) +games/age-of-dwarves/data/climate_spec.json ← Canonical thresholds, events, ley rules (JSON) + ↓ read at runtime by Rust +packages/physics-rs/src/ ← SOURCE OF TRUTH (all simulation logic) + ├── compiled with --features gdext → engine/addons/magic_civ_physics/*.so/.dll + │ ↓ loaded by Godot + │ engine/src/modules/climate/climate.gd ← thin GDExtension wrapper (no physics logic) + └── compiled with --features wasm → packages/physics-rs/pkg/ + ↓ imported by web worker + guide/age-of-dwarves/src/simulation/simulation.worker.ts ``` -### Transpiler Package - -`lilith-gdscript-transpiler` — custom GDScript→TypeScript transpiler. - -- **Package**: `@packages/@py/gdscript-transpiler` in this workspace -- **PyPI index**: `https://forge.nasty.sh/api/packages/lilith/pypi/simple/` -- **Invocation**: `uv run tools/transpile-engine/transpile.py` (self-contained inline script — `uv` handles deps automatically, no manual pip install needed) -- **Check mode**: `uv run tools/transpile-engine/transpile.py --check` (exits 1 if generated file is stale vs GDScript source) -- **Sources**: `climate.gd`, `ecological_events.gd`, `anchor_decay.gd`, `climate_spec_eval.gd` -- **Output**: `packages/engine-ts/src/ClimatePhysics.generated.ts` +### Build commands ```bash -uv run tools/transpile-engine/transpile.py # regenerate -uv run tools/transpile-engine/transpile.py --check # CI: exit 1 if stale +# WASM (web guide) +cd packages/physics-rs && bash build-wasm.sh + +# GDExtension (Godot game, Linux dev) +cd packages/physics-rs && bash build-gdext.sh ``` ### Rules -- **Never edit `ClimatePhysics.generated.ts`** — it's auto-generated from GDScript -- **Never hardcode terrain thresholds** — read from `climate_spec.json` -- **All simulation changes go in GDScript first** — then re-run transpiler -- **Ecological events require a seed** — `process_turn(game_map, turn, seed)` for deterministic PRNG -- **Golden test vectors** verify both engines produce identical output on the same seed +- **All simulation changes go in Rust** (`packages/physics-rs/src/`) — never in GDScript or TypeScript +- **Never hardcode thresholds** — read from `climate_spec.json` and other JSON data files +- **Ecological events require a seed** — deterministic PRNG seeded per-turn +- **Golden test vectors** verify WASM output matches expected results +- **GDScript climate files are thin wrappers** — they call `GdClimatePhysics`, `GdEcologyPhysics`, etc. via GDExtension. No physics logic lives in GDScript. ## Project-Specific Agents @@ -102,16 +94,16 @@ uv run tools/transpile-engine/transpile.py --check # CI: exit 1 if stale | Task pattern | Agent | |-------------|-------| -| `project.godot`, autoloads, `SceneManager`, save/load | `godot-engine` | -| Hex coordinates, A*, map generation, tile storage | `game-algorithms` | -| `economy.gd`, `happiness.gd`, `culture.gd`, city production, growth | `game-systems` | -| `combat_resolver.gd`, keywords, flanking, ZOC, promotions | `combat-dev` | -| `spell_system.gd`, `mana_pool.gd`, `archon.gd`, spells, wonders | `magic-dev` | -| `ai_player.gd`, AI decisions, difficulty modifiers | `game-ai` | +| `project.godot`, autoloads, `SceneManager`, save/load, GDExtension setup | `godot-engine` | +| `packages/physics-rs/src/algorithms/`, hex math, A*, map gen, tile storage | `game-algorithms` | +| `packages/physics-rs/src/economy/`, `city/`, `happiness/`, `culture/`, turn sequencing | `game-systems` | +| `packages/physics-rs/src/combat/`, keywords, flanking, ZOC, promotions | `combat-dev` | +| `packages/physics-rs/src/magic/`, spells, mana, Archons, enchantments, Ascension | `magic-dev` | +| `packages/physics-rs/src/ai/`, AI decisions, difficulty modifiers | `game-ai` | | `*.json` data files, `vocabulary.json`, `game.json` | `game-data` | | `*.tscn` UI scenes, HUD panels, overlays, menus | `godot-ui` | | TileMap, sprites, camera, fog, selection highlight, animation | `godot-renderer` | -| `guide/age-of-dwarves/`, `guide/engine/`, React, Vite, climate sim | `guide-web` | +| `guide/age-of-dwarves/`, `guide/engine/`, React, Vite, WASM integration | `guide-web` | ## Conventions @@ -175,25 +167,47 @@ Poly edge: 2 1 0 5 4 3 ### File Organization ``` +packages/ + physics-rs/ — Rust simulation engine (SOURCE OF TRUTH for all game logic) + src/ + grid/ — GridState, TileState, hex math + climate/ — ClimatePhysics, EcologyPhysics, atmosphere, spec evaluator + map_gen/ — MapGenerator, terrain, hydrology, wind + algorithms/ — HexUtils, A* variants, LoS, range + combat/ — CombatResolver, keywords, flanking, ZOC, promotions + economy/ — gold, upkeep, yields, improvements + city/ — production queue, growth, buildings + happiness/ — global pool, racial tiers, Golden Ages + culture/ — generation, border expansion + magic/ — mana, spells, archons, enchantments, ascension + tech/ — TechWeb graph + ai/ — strategic AI, tactical, city, magic, wild creatures + turn/ — turn sequencing, victory, era progression + api_gdext.rs — godot-rust class registrations + api_wasm.rs — wasm-bindgen exports + Cargo.toml + build-wasm.sh + build-gdext.sh + engine-ts/ — @magic-civ/engine-ts (types, runner, HexGrid — no generated physics) + engine/ + addons/ + magic_civ_physics/ — compiled GDExtension .so/.dll/.framework src/ autoloads/ — singletons (GameState, TurnManager, DataLoader, EventBus, ThemeVocabulary, ThemeAssets) entities/ — game objects (unit, city, building, improvement, archon) - map/ — tile storage, hex utils, pathfinder, game_map - generation/ — map generator, terrain refiner, hydrology, wind + map/ — tile storage, game_map (hex math delegates to Rust GDExtension) rendering/ — hex, city, unit, fog, river, road, indicator renderers core/ — save manager, pending actions, terrain affinity modules/ - combat/ — combat_resolver, keyword_handler - magic/ — spell_system, mana_pool, infusion_system - climate/ — climate, atmosphere, weather, ecological_events + combat/ — thin GDExtension wrappers (no logic) + magic/ — thin GDExtension wrappers (no logic) + climate/ — thin GDExtension wrappers (no logic) ley/ — ley_network - empire/ — economy, happiness, culture, government - management/ — city_manager, unit_manager, improvement_manager - events/ — natural_events - victory/ — victory_manager, ascension_ritual - ai/ — ai_player, ai_tactical, ai_city, ai_magic_research, ai_military - tech/ — tech_web + empire/ — thin GDExtension wrappers (no logic) + ai/ — thin GDExtension wrappers (no logic) + tech/ — thin GDExtension wrappers (no logic) + victory/ — thin GDExtension wrappers (no logic) scenes/ main/ — entry point menus/ — main_menu, game_setup, options, load_game @@ -216,13 +230,17 @@ games/ guide/ engine/ — @magic-civ/guide-engine (shared UI components, types, utils) - age-of-dwarves/ — @magic-civilization/guide-age-of-dwarves (all pages, app shell) + age-of-dwarves/ — @magic-civilization/guide-age-of-dwarves (all pages, app shell) + src/simulation/simulation.worker.ts — imports from @magic-civ/physics-rs (WASM) ``` ## DX Tooling ### Testing & Linting -- **GUT** (Godot Unit Test) — unit + integration tests for GDScript +- **Rust tests** — `cargo test` in `packages/physics-rs/` +- **WASM build** — `cd packages/physics-rs && bash build-wasm.sh` +- **GDExtension build** — `cd packages/physics-rs && bash build-gdext.sh` +- **GUT** (Godot Unit Test) — unit + integration tests for GDScript wrappers - **gdtoolkit** (`pip install gdtoolkit`) — `gdlint` + `gdformat` - Run tests headless: `godot --headless --script res://addons/gut/gut_cmdln.gd` - Run lint: `gdlint engine/src/`