# 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) - `docs/plans/` -- Design and implementation docs ## Commands - `npm install` -- install all workspace dependencies - `npm -w server run dev` -- start server (port 3001) - `npm -w server run test` -- run server tests (115 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) - Client: vite (bundler), Phaser 3 - Shared: compiled to `shared/dist/` (auto-built) ## Key Entry Points - `server/src/main.ts` -- server startup - `server/src/game/GameLoop.ts` -- tick loop orchestration - `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) - `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 -> relationship -> movement -> broadcast - 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 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)