feat(memory): record need crisis, recovery, and goal change events
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -5,6 +5,7 @@ import {
|
||||
import type { World } from '../ecs/World.js';
|
||||
import type { GameMap } from '../map/GameMap.js';
|
||||
import { findPath } from '../map/pathfinding.js';
|
||||
import type { EventMemoryService } from '../llm/eventMemoryService.js';
|
||||
|
||||
function closestPOI(map: GameMap, from: Position, type: 'food' | 'rest'): Position | null {
|
||||
const pois = map.getPointsOfInterest(type);
|
||||
@@ -25,7 +26,7 @@ function directionTo(from: Position, to: Position): number {
|
||||
return dy > 0 ? Direction.DOWN : Direction.UP;
|
||||
}
|
||||
|
||||
export function npcBrainSystem(world: World, map: GameMap): void {
|
||||
export function npcBrainSystem(world: World, map: GameMap, eventMemoryService?: EventMemoryService): void {
|
||||
for (const entity of world.query('npcBrain', 'needs', 'movement', 'position')) {
|
||||
const brain = world.getComponent<NPCBrain>(entity, 'npcBrain')!;
|
||||
const needs = world.getComponent<Needs>(entity, 'needs')!;
|
||||
@@ -40,21 +41,54 @@ export function npcBrainSystem(world: World, map: GameMap): void {
|
||||
|
||||
// Check if at goal destination — recover needs
|
||||
if (brain.currentGoal === 'eat' && movement.state === 'idle') {
|
||||
const prevHunger = needs.hunger;
|
||||
needs.hunger = Math.min(100, needs.hunger + 20);
|
||||
if (eventMemoryService && prevHunger < HUNGER_THRESHOLD && needs.hunger >= HUNGER_THRESHOLD) {
|
||||
const name = world.getComponent<string>(entity, 'name') ?? 'Unknown';
|
||||
eventMemoryService.record(entity, {
|
||||
type: 'need_recovery',
|
||||
tick: 0,
|
||||
need: 'hunger',
|
||||
detail: `${name} satisfied their hunger`,
|
||||
});
|
||||
}
|
||||
brain.currentGoal = null;
|
||||
}
|
||||
if (brain.currentGoal === 'rest' && movement.state === 'idle') {
|
||||
const prevEnergy = needs.energy;
|
||||
needs.energy = Math.min(100, needs.energy + 20);
|
||||
if (eventMemoryService && prevEnergy < ENERGY_THRESHOLD && needs.energy >= ENERGY_THRESHOLD) {
|
||||
const name = world.getComponent<string>(entity, 'name') ?? 'Unknown';
|
||||
eventMemoryService.record(entity, {
|
||||
type: 'need_recovery',
|
||||
tick: 0,
|
||||
need: 'energy',
|
||||
detail: `${name} recovered their energy`,
|
||||
});
|
||||
}
|
||||
brain.currentGoal = null;
|
||||
}
|
||||
|
||||
// Determine new goal based on needs priority
|
||||
const prevGoal = brain.currentGoal;
|
||||
let goal: 'rest' | 'eat' | 'wander' = 'wander';
|
||||
if (needs.energy < ENERGY_THRESHOLD) goal = 'rest';
|
||||
else if (needs.hunger < HUNGER_THRESHOLD) goal = 'eat';
|
||||
|
||||
brain.currentGoal = goal;
|
||||
|
||||
if (eventMemoryService && brain.currentGoal !== prevGoal && prevGoal !== null) {
|
||||
const name = world.getComponent<string>(entity, 'name') ?? 'Unknown';
|
||||
const goalLabels: Record<string, string> = { wander: 'wandering', eat: 'looking for food', rest: 'looking for rest' };
|
||||
eventMemoryService.record(entity, {
|
||||
type: 'goal_change',
|
||||
tick: 0,
|
||||
oldGoal: prevGoal,
|
||||
newGoal: brain.currentGoal ?? 'idle',
|
||||
detail: `${name} started ${goalLabels[brain.currentGoal ?? ''] ?? brain.currentGoal}`,
|
||||
});
|
||||
}
|
||||
|
||||
// Pick target based on goal
|
||||
let target: Position | null = null;
|
||||
if (goal === 'eat') {
|
||||
|
||||
Reference in New Issue
Block a user