feat: industrySystem scores recipes by desire match

Adds scoreCraftableRecipes helper that scores recipes based on NPC
desires (priority * 10 for own_item match). Falls back to first
available when no desires match.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
root
2026-03-10 00:07:09 +00:00
parent c548d0ac05
commit f3591e68d7
2 changed files with 62 additions and 3 deletions
+29 -3
View File
@@ -1,6 +1,6 @@
import {
PRODUCTIVITY_THRESHOLD,
type Needs, type NPCBrain, type Position,
type Desire, type Needs, type NPCBrain, type Position,
} from '@dflike/shared';
import type { World } from '../ecs/World.js';
import type { GameMap } from '../map/GameMap.js';
@@ -8,10 +8,35 @@ import type { GatheringState } from './gatheringSystem.js';
import type { CraftingState } from './craftingSystem.js';
import type { BuildingState, StructureData } from './buildingSystem.js';
import type { PickupState } from './pickupSystem.js';
import { RecipeRegistry } from '../industry/recipeRegistry.js';
import { RecipeRegistry, type Recipe } from '../industry/recipeRegistry.js';
import { getEffectiveStat } from './statHelpers.js';
import { industryConfig } from '../config/industryConfig.js';
function scoreCraftableRecipes(recipes: Recipe[], desires: Desire[]): Recipe {
if (recipes.length === 1 || desires.length === 0) return recipes[0];
let bestRecipe = recipes[0];
let bestScore = 0;
for (const recipe of recipes) {
let score = 1; // base score
for (const desire of desires) {
const f = desire.fulfillment;
if (f.type === 'own_item' && f.itemId === recipe.outputItemId) {
score += desire.priority * 10;
} else if (f.type === 'own_item_category') {
score += desire.priority * 5;
}
}
if (score > bestScore) {
bestScore = score;
bestRecipe = recipe;
}
}
return bestRecipe;
}
const BUILD_RECIPE_MAP: Record<string, { type: 'stockpile' | 'workshop'; subtype: string }> = {
stockpile: { type: 'stockpile', subtype: 'general' },
workbench: { type: 'workshop', subtype: 'workbench' },
@@ -65,7 +90,8 @@ export function industrySystem(world: World, map: GameMap): void {
// Try craft first
const craftRecipes = registry.findCraftable(inv).filter(r => !r.id.startsWith('build_'));
if (craftRecipes.length > 0) {
const recipe = craftRecipes[0];
const desires = world.getComponent<Desire[]>(entity, 'desires') ?? [];
const recipe = scoreCraftableRecipes(craftRecipes, desires);
const dex = getEffectiveStat(world, entity, 'dexterity');
const baseTicks = industryConfig.craftBaseTicks;
const ticks = Math.max(1, Math.round(baseTicks * (1 - (dex - 10) * industryConfig.craftDexterityModifier)));