feat(systems): add gatheringSystem with timer, stat modifier, and inventory

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
root
2026-03-08 22:49:47 +00:00
parent 61c463c1c7
commit 5fba656c62
2 changed files with 234 additions and 0 deletions
+68
View File
@@ -0,0 +1,68 @@
import {
PRODUCTIVITY_RECOVERY_RATE, PRODUCTIVITY_THRESHOLD,
type Needs, type Movement, type NPCBrain, type Position,
} from '@dflike/shared';
import type { World } from '../ecs/World.js';
import type { GameMap } from '../map/GameMap.js';
import { getEffectiveStat } from './statHelpers.js';
import { addItem } from '../industry/inventoryHelpers.js';
import { industryConfig } from '../config/industryConfig.js';
export interface GatheringState {
resourceType: string;
ticksRemaining: number;
}
export function gatheringSystem(world: World, map: GameMap): void {
for (const entity of world.query('npcBrain', 'position', 'needs', 'movement')) {
const brain = world.getComponent<NPCBrain>(entity, 'npcBrain')!;
if (brain.currentGoal !== 'gather') continue;
const pos = world.getComponent<Position>(entity, 'position')!;
const needs = world.getComponent<Needs>(entity, 'needs')!;
const movement = world.getComponent<Movement>(entity, 'movement')!;
const gs = world.getComponent<GatheringState>(entity, 'gatheringState');
// Already gathering — tick down
if (gs) {
gs.ticksRemaining--;
needs.productivity = Math.min(100, needs.productivity + PRODUCTIVITY_RECOVERY_RATE);
if (gs.ticksRemaining <= 0) {
// Complete: add resource to inventory
const inv = world.getComponent<Map<string, number>>(entity, 'inventory');
if (inv) {
addItem(inv, gs.resourceType, industryConfig.gatherYield);
}
world.removeComponent(entity, 'gatheringState');
// Decide next action
if (needs.productivity >= PRODUCTIVITY_THRESHOLD) {
brain.currentGoal = 'wander';
}
// else brain stays on 'gather', npcBrain will pick new target next tick
}
continue;
}
// Not yet gathering — check if at resource tile and idle
if (movement.state !== 'idle') continue;
const resourceTile = map.resourceTiles.find(r => r.x === pos.x && r.y === pos.y);
if (!resourceTile) continue;
// Start gathering
const str = getEffectiveStat(world, entity, 'strength');
const baseTicks = industryConfig.gatherBaseTicks;
const ticks = Math.max(1, Math.round(baseTicks * (1 - (str - 10) * industryConfig.gatherStrengthModifier)));
world.addComponent<GatheringState>(entity, 'gatheringState', {
resourceType: resourceTile.resourceType,
ticksRemaining: ticks,
});
// Recover productivity on first tick too
needs.productivity = Math.min(100, needs.productivity + PRODUCTIVITY_RECOVERY_RATE);
}
}