feat: add thirst decay to needsDecaySystem with crisis events
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { describe, it, expect, vi } from 'vitest';
|
||||
import { World } from '../../ecs/World.js';
|
||||
import { GameMap } from '../../map/GameMap.js';
|
||||
import { needsDecaySystem } from '../needsDecaySystem.js';
|
||||
@@ -13,7 +13,7 @@ const rc = createRuntimeConstants();
|
||||
function createNPC(world: World, x: number, y: number, hunger = 80, energy = 80) {
|
||||
const e = world.createEntity();
|
||||
world.addComponent<Position>(e, 'position', { x, y });
|
||||
world.addComponent<Needs>(e, 'needs', { hunger, energy, productivity: 80 });
|
||||
world.addComponent<Needs>(e, 'needs', { hunger, energy, thirst: 80, productivity: 80 });
|
||||
world.addComponent<Movement>(e, 'movement', { state: 'idle', target: null, path: [], direction: 0, moveProgress: 0 });
|
||||
world.addComponent<NPCBrain>(e, 'npcBrain', { currentGoal: null, goalQueue: [] });
|
||||
return e;
|
||||
@@ -139,6 +139,36 @@ describe('needsDecaySystem', () => {
|
||||
const needs = world.getComponent<Needs>(e, 'needs')!;
|
||||
expect(needs.energy).toBe(100);
|
||||
});
|
||||
|
||||
it('decays thirst each tick', () => {
|
||||
const world = new World();
|
||||
const e = createNPC(world, 0, 0, 50, 50);
|
||||
const needs = world.getComponent<Needs>(e, 'needs')!;
|
||||
needs.thirst = 50;
|
||||
needsDecaySystem(world, undefined, rc);
|
||||
expect(needs.thirst).toBeCloseTo(50 - rc.get('THIRST_DECAY_PER_TICK'));
|
||||
});
|
||||
|
||||
it('decays thirst at half rate during sleep', () => {
|
||||
const world = new World();
|
||||
const e = createNPC(world, 0, 0, 50, 50);
|
||||
const needs = world.getComponent<Needs>(e, 'needs')!;
|
||||
needs.thirst = 50;
|
||||
const brain = world.getComponent<NPCBrain>(e, 'npcBrain')!;
|
||||
brain.currentGoal = 'sleep';
|
||||
needsDecaySystem(world, undefined, rc);
|
||||
expect(needs.thirst).toBeCloseTo(50 - rc.get('THIRST_DECAY_PER_TICK') * rc.get('SLEEP_HUNGER_DECAY_MULTIPLIER'));
|
||||
});
|
||||
|
||||
it('fires thirst crisis event when crossing threshold', () => {
|
||||
const world = new World();
|
||||
const e = createNPC(world, 0, 0, 50, 50);
|
||||
const needs = world.getComponent<Needs>(e, 'needs')!;
|
||||
needs.thirst = rc.get('THIRST_THRESHOLD') + 0.01;
|
||||
const mockEMS = { record: vi.fn() };
|
||||
needsDecaySystem(world, mockEMS as any, rc);
|
||||
expect(mockEMS.record).toHaveBeenCalledWith(e, expect.objectContaining({ type: 'need_crisis', need: 'thirst' }));
|
||||
});
|
||||
});
|
||||
|
||||
describe('npcBrainSystem', () => {
|
||||
|
||||
@@ -10,6 +10,7 @@ export function needsDecaySystem(world: World, eventMemoryService: EventMemorySe
|
||||
const con = getEffectiveStat(world, entity, 'constitution');
|
||||
const conMultiplier = 1 - (con - 10) * 0.03;
|
||||
const prevHunger = needs.hunger;
|
||||
const prevThirst = needs.thirst;
|
||||
const prevEnergy = needs.energy;
|
||||
const prevProductivity = needs.productivity;
|
||||
|
||||
@@ -19,9 +20,11 @@ export function needsDecaySystem(world: World, eventMemoryService: EventMemorySe
|
||||
if (isSleeping) {
|
||||
needs.energy = Math.min(100, needs.energy + rc.get('SLEEP_ENERGY_RECOVERY_PER_TICK'));
|
||||
needs.hunger = Math.max(0, needs.hunger - rc.get('HUNGER_DECAY_PER_TICK') * conMultiplier * rc.get('SLEEP_HUNGER_DECAY_MULTIPLIER'));
|
||||
needs.thirst = Math.max(0, needs.thirst - rc.get('THIRST_DECAY_PER_TICK') * conMultiplier * rc.get('SLEEP_HUNGER_DECAY_MULTIPLIER'));
|
||||
} else {
|
||||
needs.energy = Math.max(0, needs.energy - rc.get('ENERGY_DECAY_PER_TICK') * conMultiplier);
|
||||
needs.hunger = Math.max(0, needs.hunger - rc.get('HUNGER_DECAY_PER_TICK') * conMultiplier);
|
||||
needs.thirst = Math.max(0, needs.thirst - rc.get('THIRST_DECAY_PER_TICK') * conMultiplier);
|
||||
}
|
||||
needs.productivity = Math.max(0, needs.productivity - rc.get('PRODUCTIVITY_DECAY_PER_TICK'));
|
||||
|
||||
@@ -44,6 +47,15 @@ export function needsDecaySystem(world: World, eventMemoryService: EventMemorySe
|
||||
detail: `${name} became exhausted`,
|
||||
});
|
||||
}
|
||||
if (prevThirst >= rc.get('THIRST_THRESHOLD') && needs.thirst < rc.get('THIRST_THRESHOLD')) {
|
||||
const name = world.getComponent<string>(entity, 'name') ?? 'Unknown';
|
||||
eventMemoryService.record(entity, {
|
||||
type: 'need_crisis',
|
||||
tick: 0,
|
||||
need: 'thirst',
|
||||
detail: `${name} is getting dehydrated`,
|
||||
});
|
||||
}
|
||||
if (prevProductivity >= rc.get('PRODUCTIVITY_THRESHOLD') && needs.productivity < rc.get('PRODUCTIVITY_THRESHOLD')) {
|
||||
const name = world.getComponent<string>(entity, 'name') ?? 'Unknown';
|
||||
eventMemoryService.record(entity, {
|
||||
|
||||
Reference in New Issue
Block a user