5fba656c62
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
69 lines
2.5 KiB
TypeScript
69 lines
2.5 KiB
TypeScript
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);
|
|
}
|
|
}
|