import type { AccessorySlot, PortraitSlot, BerryPhase } from './constants.js'; import type { NarrationEvent } from './narration.js'; export type EntityId = number; export type MemoryEventType = 'social_positive' | 'social_negative' | 'proposal_accepted' | 'proposal_rejected' | 'tier_change' | 'need_crisis' | 'need_recovery' | 'goal_change' | 'bond_formed' | 'bond_dissolved' | 'spawned' | 'invention' | 'desire_fulfilled' | 'desire_added'; export interface MemoryEvent { id: number; type: MemoryEventType; tick: number; otherEntityId?: EntityId; otherName?: string; detail: string; oldTier?: string; newTier?: string; need?: 'hunger' | 'thirst' | 'energy' | 'productivity'; oldGoal?: string; newGoal?: string; } export interface Position { x: number; y: number; } export interface Velocity { dx: number; dy: number; } export interface Appearance { skinId: string; accessories: Partial>; portraitFeatures?: Partial>; } export interface Needs { hunger: number; thirst: number; energy: number; productivity: number; } export interface Stats { strength: number; dexterity: number; constitution: number; intelligence: number; perception: number; sociability: number; courage: number; curiosity: number; empathy: number; temperament: number; } export type StatName = keyof Stats; export interface StatModifier { stat: StatName; value: number; remaining: number; } export interface StatModifiers { modifiers: StatModifier[]; } export type MovementState = 'idle' | 'walking'; export type GoalType = 'wander' | 'eat' | 'drink' | 'forage' | 'sleep' | 'gather' | 'craft' | 'build' | 'dropoff' | 'pickup'; export interface Movement { state: MovementState; target: Position | null; path: Position[]; direction: number; moveProgress: number; } export interface PlayerControlled { playerId: string; mode: 'avatar' | 'camera'; } export interface NPCBrain { currentGoal: GoalType | null; goalQueue: GoalType[]; gatherTarget?: { x: number; y: number; resourceType: string; } | null; } export type InteractionPhase = 'none' | 'facing' | 'pausing' | 'emoting' | 'proposing'; export type InteractionOutcome = 'positive' | 'negative'; export interface SocialState { phase: InteractionPhase; partnerId: EntityId | null; phaseTimer: number; outcome: InteractionOutcome | null; globalCooldown: number; pairCooldowns: Map; lastOutcome: LastOutcome | null; proposalCooldown: number; pendingProposal: { targetId: EntityId; type: string; } | null; isProposalInteraction: boolean; } export interface RelationshipData { value: number; interactions: number; lastInteractionTick: number; status: 'active' | 'memory'; } export type Relationships = Map; export interface LastOutcome { partnerId: EntityId; outcome: InteractionOutcome; tick: number; } export interface EntityState { id: EntityId; position: Position; movement: Movement; appearance: Appearance; needs?: Needs; stats?: Stats; npcBrain?: NPCBrain; playerControlled?: PlayerControlled; name?: string; backstory?: string; inventory?: Record; desires?: { description: string; category: DesireCategory; }[]; socialState?: { phase: InteractionPhase; partnerId: EntityId | null; outcome: InteractionOutcome | null; }; relationships?: Array<{ entityId: EntityId; name: string; value: number; classification: string; status: 'active' | 'memory'; bond: string | null; }>; } export interface WorldState { entities: EntityState[]; worldWidth: number; worldHeight: number; tileSize: number; obstacles: Position[]; terrain: number[]; decorations: number[]; trunkDecorations: number[]; resourceTiles: Array<{ x: number; y: number; resourceType: string; }>; berryBushStates: Array<{ x: number; y: number; phase: BerryPhase; }>; } export interface StateUpdate { entities: EntityState[]; tick: number; gameTime: number; dayNumber: number; berryBushStates: Array<{ x: number; y: number; phase: BerryPhase; }>; } export interface PlayerJoined { playerId: string; entityId: EntityId; } export interface PlayerLeft { playerId: string; } export interface PlayerInput { type: 'move' | 'toggle-mode' | 'follow' | 'interact'; direction?: { dx: number; dy: number; }; targetPlayerId?: string; targetEntityId?: EntityId; } export interface SuperlativeEntry { entityId: EntityId; name: string; value: number; } export interface SuperlativesData { mostLoved: SuperlativeEntry | null; mostPopular: SuperlativeEntry | null; mostReviled: SuperlativeEntry | null; mostAnnoying: SuperlativeEntry | null; shyest: SuperlativeEntry | null; mostOutgoing: SuperlativeEntry | null; biggestHeartbreaker: SuperlativeEntry | null; mostDevoted: SuperlativeEntry | null; loneliest: SuperlativeEntry | null; mostPolarizing: SuperlativeEntry | null; socialButterfly: SuperlativeEntry | null; mostInventive: SuperlativeEntry | null; } export interface InventionSummary { itemId: string; name: string; category: 'resource' | 'tool' | 'material' | 'structure'; inputs: { itemId: string; quantity: number; }[]; workshopType: string | null; toolRequired: string | null; inventorName: string; day: number; } export interface StockpileLogEntry { npcName: string; action: 'dropoff' | 'pickup'; itemId: string; quantity: number; tick: number; } export type DesireCategory = 'material' | 'social' | 'shelter' | 'comfort' | 'community' | 'creative'; export type FulfillmentCriteria = { type: 'own_item'; itemId: string; quantity: number; } | { type: 'own_item_category'; category: string; quantity: number; } | { type: 'structure_exists'; structureType: string; } | { type: 'building_exists'; buildingType: string; } | { type: 'relationship_tier'; tier: string; count: number; } | { type: 'recipe_exists'; tag: string; } | { type: 'custom'; check: string; }; export interface Desire { id: string; description: string; category: DesireCategory; fulfillment: FulfillmentCriteria; priority: number; source: 'spawn' | 'event' | 'periodic'; sourceDetail?: string; createdAtTick: number; cooldownTicks?: number; } export interface TunableConstants { TICK_RATE: number; BROADCAST_EVERY_N_TICKS: number; HUNGER_DECAY_PER_TICK: number; ENERGY_DECAY_PER_TICK: number; HUNGER_THRESHOLD: number; THIRST_DECAY_PER_TICK: number; THIRST_THRESHOLD: number; ENERGY_THRESHOLD: number; NEED_RECOVERY_RATE: number; SLEEP_ENERGY_RECOVERY_PER_TICK: number; SLEEP_HUNGER_DECAY_MULTIPLIER: number; SLEEP_WAKE_THRESHOLD: number; SLEEP_VOLUNTARY_ENERGY_THRESHOLD: number; NAP_BUFFER: number; PRODUCTIVITY_DECAY_PER_TICK: number; PRODUCTIVITY_THRESHOLD: number; PRODUCTIVITY_RECOVERY_RATE: number; DAY_NIGHT_RATIO: number; DAY_HOURS: number; SUNSET_DURATION_HOURS: number; SUNRISE_DURATION_HOURS: number; NIGHT_DARKNESS: number; AWARENESS_RADIUS: number; FACING_DURATION: number; PAUSING_DURATION: number; EMOTING_DURATION: number; SOCIAL_GLOBAL_COOLDOWN: number; SOCIAL_PAIR_COOLDOWN: number; PROPOSAL_EMOTING_DURATION: number; MAX_NPC_COUNT: number; MOVE_SPEED: number; } export type TunableKey = keyof TunableConstants; export type LogSeverity = 'error' | 'warning' | 'info'; export type LogCategory = 'LLM' | 'Save' | 'Network' | 'Performance' | 'Game'; export interface LogEntry { timestamp: number; severity: LogSeverity; category: LogCategory; message: string; } export interface LlmCallTypeStats { templateName: string; label: string; count: number; totalCost: number; minCost: number; maxCost: number; avgCost: number; retryCount: number; failCount: number; retryPct: number; failPct: number; } export interface LlmStatsData { types: LlmCallTypeStats[]; totalCount: number; totalCost: number; totalRetries: number; totalFailures: number; trackingEnabled: boolean; } export interface ServerEvents { 'world-state': (data: WorldState) => void; 'state-update': (data: StateUpdate) => void; 'player-joined': (data: PlayerJoined) => void; 'player-left': (data: PlayerLeft) => void; 'npc-recomposed': (data: { entityId: EntityId; appearance: Appearance; }) => void; 'superlatives-update': (data: SuperlativesData) => void; 'narration-event': (data: NarrationEvent) => void; 'narration-update': (data: { id: number; narration: string; }) => void; 'narration-history': (data: NarrationEvent[]) => void; 'npc-thought': (data: { entityId: EntityId; text: string; emoji: string; }) => void; 'memory-event': (data: { entityId: EntityId; event: MemoryEvent; }) => void; 'memory-history': (data: { entityId: EntityId; events: MemoryEvent[]; }) => void; 'invention-event': (data: InventionSummary) => void; 'invention-history': (data: InventionSummary[]) => void; 'stockpile-event': (data: StockpileLogEntry) => void; 'stockpile-history': (data: StockpileLogEntry[]) => void; 'stockpile-summary': (data: Record) => void; 'admin-auth-result': (data: { success: boolean; constants?: TunableConstants; }) => void; 'admin-constants-updated': (data: TunableConstants) => void; 'log-history': (data: LogEntry[]) => void; 'log-entry': (data: LogEntry) => void; 'admin-llm-stats-result': (data: LlmStatsData) => void; 'admin-llm-tracking-toggled': (data: { enabled: boolean; }) => void; } export interface ClientEvents { 'player-input': (data: PlayerInput) => void; 'spawn-npc': (data: { x: number; y: number; }) => void; 'superlatives-subscribe': () => void; 'superlatives-unsubscribe': () => void; 'follow-npc': (data: { entityId: EntityId | null; }) => void; 'admin-auth': (data: { password: string; }) => void; 'admin-update-constant': (data: { key: TunableKey; value: number; }) => void; 'admin-reset-defaults': () => void; 'log-subscribe': () => void; 'log-unsubscribe': () => void; 'admin-llm-stats': () => void; 'admin-toggle-llm-tracking': (data: { enabled: boolean; }) => void; 'admin-reset-llm-stats': () => void; }