4c24a016f1
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
109 lines
7.6 KiB
Markdown
109 lines
7.6 KiB
Markdown
# CLAUDE.md
|
|
|
|
## Project
|
|
|
|
Multiplayer NPC simulation game (Dwarf Fortress-inspired). Server-authoritative ECS architecture.
|
|
|
|
## Structure
|
|
|
|
- `shared/` -- Types and constants (no runtime code)
|
|
- `server/` -- Node.js + Socket.io game server with ECS
|
|
- `client/` -- Phaser 3 + TypeScript renderer
|
|
- `chars/` -- Character sprite assets (do not modify)
|
|
- `saves/` -- SQLite save files (gitignored, one .db per region)
|
|
- `docs/plans/` -- Design and implementation docs
|
|
|
|
## Commands
|
|
|
|
- `npm install` -- install all workspace dependencies
|
|
- `npm -w server run dev` -- start server (port 3001), loads existing save or creates new world
|
|
- `npm -w server run dev -- --new-world` -- start server with fresh world (overwrites save)
|
|
- `npm -w server run test` -- run server tests (408 tests)
|
|
- `npm -w server run test:watch` -- run server tests in watch mode
|
|
- `npm -w client run dev` -- start client dev server (port 3000)
|
|
- `npm -w client run build` -- build client for production
|
|
- `npx -w shared tsc` -- rebuild shared types (required after modifying shared/src/)
|
|
|
|
## Tooling
|
|
|
|
- ESM modules throughout (`"type": "module"`)
|
|
- Server: tsx (runtime), vitest (tests), better-sqlite3 (persistence)
|
|
- Client: vite (bundler), Phaser 3
|
|
- Shared: compiled to `shared/dist/` (auto-built)
|
|
|
|
## Key Entry Points
|
|
|
|
- `server/src/main.ts` -- server startup, --new-world flag, graceful shutdown (SIGINT/SIGTERM)
|
|
- `server/src/game/GameLoop.ts` -- tick loop orchestration, save/load integration
|
|
- `server/src/persistence/database.ts` -- SQLite connection, schema creation, migration runner
|
|
- `server/src/persistence/saveManager.ts` -- orchestrates periodic saves, shutdown saves, load-on-startup
|
|
- `server/src/persistence/worldSerializer.ts` -- save/load map tiles and metadata
|
|
- `server/src/persistence/entitySerializer.ts` -- save/load entities, components, relationships, bonds
|
|
- `server/src/persistence/eventSerializer.ts` -- save/load narration, memory, stockpile, invention events
|
|
- `server/src/spawner/appearanceGenerator.ts` -- NPC appearance + portrait feature generation
|
|
- `server/src/spawner/nameGenerator.ts` -- NPC name generation
|
|
- `server/src/spawner/statGenerator.ts` -- 3d6 stat generation for NPCs
|
|
- `server/src/systems/statHelpers.ts` -- getEffectiveStat (base + modifiers, clamped 3-18)
|
|
- `server/src/systems/statModifierSystem.ts` -- modifier decay and needs-based modifiers
|
|
- `server/src/systems/socialSystem.ts` -- NPC social interaction phases and stat integration
|
|
- `server/src/systems/relationshipSystem.ts` -- relationship delta calculation and despawn handling
|
|
- `server/src/systems/relationshipHelpers.ts` -- classify, getPartnerCap, getFriendCap, getEffectiveClassification
|
|
- `server/src/config/relationshipConfig.ts` -- tuning values for relationship system (config-driven)
|
|
- `server/src/systems/industrySystem.ts` -- craft/build/gather decision tree
|
|
- `server/src/systems/craftingSystem.ts` -- input consumption, timer, output production
|
|
- `server/src/systems/buildingSystem.ts` -- structure creation with build progress
|
|
- `server/src/systems/dropoffSystem.ts` -- stockpile item deposit
|
|
- `server/src/industry/recipeRegistry.ts` -- recipe registry with seed recipes
|
|
- `server/src/config/industryConfig.ts` -- tuning values for gathering, crafting, building
|
|
- `client/src/main.ts` -- Phaser game bootstrap
|
|
- `client/src/scenes/GameScene.ts` -- main game scene (camera/avatar/follow modes)
|
|
- `client/src/sprites/CharacterCompositor.ts` -- sprite layer compositing
|
|
- `client/src/sprites/PortraitCompositor.ts` -- portrait layer compositing (data URL output)
|
|
- `client/src/ui/NpcInfoPanel.ts` -- follow-mode NPC info panel (HTML/CSS overlay)
|
|
- `shared/src/types.ts` -- protocol types
|
|
- `shared/src/constants.ts` -- game constants (incl. portrait slots, folder mapping)
|
|
|
|
## Key Conventions
|
|
|
|
- ECS components are plain data objects, systems are pure functions
|
|
- Server owns all game state; clients are renderers
|
|
- Character sprites are 48x48 per frame, 6 cols x 4 rows spritesheet layout
|
|
- Compositing: skins + accessories layered in z-order, cached as single textures
|
|
- Portraits: skin-specific layers composited client-side, exported as data URLs for HTML panel
|
|
- Portrait assets are in `chars/baseman/accessories/portraits/` organized by skin folder (skin01-07)
|
|
- Sprite skin `shape00_skinXX` maps to portrait folder `skin{XX+1}`, base file `skin{XX}.png`
|
|
- Shared types ensure client/server protocol agreement
|
|
- Systems run in order: statModifier -> needsDecay -> npcBrain -> social -> narration -> relationship -> industry -> gathering -> crafting -> building -> dropoff -> movement -> thought
|
|
- Follow mode shows EarthBound-styled NPC info panel with live-updating needs bars and stats
|
|
- NPC stats use 3d6 generation (range 3-18, bell curve): 5 physical (STR/DEX/CON/INT/PER) + 5 personality (SOC/COU/CUR/EMP/TMP)
|
|
- Stats have transient modifiers (needs-based permanent + event-based decaying); effective value = base + modifiers, clamped 3-18
|
|
- Base stats are floats (drift slowly from experience); `getEffectiveStat` floors to int for gameplay use
|
|
- Wire protocol sends effective (computed) stats only; modifiers stay server-side
|
|
- Relationships: per-NPC Map<EntityId, RelationshipData> component, lazy-initialized on first encounter
|
|
- Relationship values range -100 to 100, classified into tiers (Partner/Friend/Acquaintance/Stranger/Wary/Rival/Enemy/Nemesis)
|
|
- Relationship deltas use diminishing returns and blended asymmetry (30% shared + 70% individual outcome)
|
|
- Stats influence relationships: empathy (positive gain), temperament (negative loss), sociability (friend cap + bonus)
|
|
- Partner cap: default 1, poly (2) requires sociability 15+ AND empathy 14+; friend cap scales with sociability
|
|
- Despawned strong relationships (|value| >= 20) persist as memories; weak ones fade and get cleaned up
|
|
- SocialSystem emits lastOutcome on SocialState; relationshipSystem consumes and clears it
|
|
- NPC info panel has Status/Relations tabs; relationships tab shows tier-grouped sliders with colored markers
|
|
- All relationship tuning values in server/src/config/relationshipConfig.ts (designed for future admin UI)
|
|
- Industry system decides craft/build/gather based on inventory and existing structures
|
|
- RecipeRegistry singleton holds seed recipes (craft_wooden_axe, craft_hammer, build_stockpile, build_workbench)
|
|
- Crafting: 40 tick base, dexterity modifier; consumes inputs, produces output item
|
|
- Building: creates structure entity with buildProgress 0→1; structures have type (stockpile/workshop) + subtype + inventory
|
|
- After gathering, NPCs drop off items at nearest completed stockpile via 'dropoff' goal
|
|
- Movement pauses during gathering, crafting, and building (same skip pattern)
|
|
- World persistence uses SQLite (better-sqlite3) with single file per region at `saves/default.db`
|
|
- Event data (narration, memory, stockpile, inventions) is written to DB immediately on occurrence
|
|
- Entity state (NPCs, structures, relationships, bonds) is batch-saved every 30 seconds
|
|
- Graceful shutdown (Ctrl-C) triggers final save before exit
|
|
- On startup, loads from existing save or generates fresh world; `--new-world` flag forces fresh
|
|
- Schema versioning via `schema_version` in metadata table; migration runner for future schema changes
|
|
- In-memory event buffers (50 entries) are caches over full DB history; populated from DB on startup
|
|
- Map tiles stored fully in DB (not regenerated from seed); decoupled from map generator
|
|
- Entity IDs preserved across save/load via `World.createEntityWithId()`; nextId counter restored
|
|
- Components with Map types (inventory, pairCooldowns, structure.inventory) serialize via Object.fromEntries/new Map
|
|
- Transient state (movement paths, active social interactions, NPC goals) reset on load; NPCs resume from idle
|
|
- Player entities are not persisted; players reconnect fresh
|