diff --git a/server/src/industry/__tests__/inventionRegistrar.test.ts b/server/src/industry/__tests__/inventionRegistrar.test.ts new file mode 100644 index 0000000..af52355 --- /dev/null +++ b/server/src/industry/__tests__/inventionRegistrar.test.ts @@ -0,0 +1,97 @@ +import { describe, it, expect } from 'vitest'; +import { registerInvention } from '../inventionRegistrar.js'; +import { ItemRegistry } from '../itemRegistry.js'; +import { RecipeRegistry } from '../recipeRegistry.js'; +import { createInventionTimeline } from '../inventionTimeline.js'; +import type { RawInvention } from '../inventionValidator.js'; + +function createRegistries() { + return { + itemRegistry: ItemRegistry.createDefault(), + recipeRegistry: RecipeRegistry.createDefault(), + timeline: createInventionTimeline(), + }; +} + +describe('registerInvention', () => { + it('registers new item and recipe', () => { + const { itemRegistry, recipeRegistry, timeline } = createRegistries(); + const raw: RawInvention = { + name: 'rope', + description: 'Twisted plant fibers', + category: 'material', + inputs: [{ itemId: 'log', quantity: 2 }], + }; + + const result = registerInvention({ + raw, + itemId: 'rope', + itemRegistry, + recipeRegistry, + timeline, + inventorEntityId: 1, + inventorName: 'Gwen', + tick: 500, + day: 2, + }); + + expect(result).toBe(true); + expect(itemRegistry.get('rope')).toBeDefined(); + expect(itemRegistry.get('rope')!.source).toBe('invented'); + expect(itemRegistry.get('rope')!.inventedBy!.name).toBe('Gwen'); + expect(recipeRegistry.get('craft_rope')).toBeDefined(); + expect(recipeRegistry.get('craft_rope')!.source).toBe('invented'); + expect(timeline.getAll()).toHaveLength(1); + }); + + it('registers structure recipe with build_ prefix', () => { + const { itemRegistry, recipeRegistry, timeline } = createRegistries(); + const raw: RawInvention = { + name: 'kiln', + description: 'A stone furnace', + category: 'structure', + inputs: [{ itemId: 'stone', quantity: 5 }], + workshopType: 'kiln', + }; + + registerInvention({ + raw, + itemId: 'kiln', + itemRegistry, + recipeRegistry, + timeline, + inventorEntityId: 2, + inventorName: 'Bram', + tick: 700, + day: 3, + }); + + expect(recipeRegistry.get('build_kiln')).toBeDefined(); + expect(recipeRegistry.get('build_kiln')!.outputItemId).toBe('kiln'); + }); + + it('includes toolRequired in recipe', () => { + const { itemRegistry, recipeRegistry, timeline } = createRegistries(); + const raw: RawInvention = { + name: 'plank', + description: 'A wooden plank', + category: 'material', + inputs: [{ itemId: 'log', quantity: 1 }], + toolRequired: 'wooden_axe', + }; + + registerInvention({ + raw, + itemId: 'plank', + itemRegistry, + recipeRegistry, + timeline, + inventorEntityId: 1, + inventorName: 'Gwen', + tick: 500, + day: 2, + }); + + expect(recipeRegistry.get('craft_plank')!.toolRequired).toBe('wooden_axe'); + }); +}); diff --git a/server/src/industry/inventionRegistrar.ts b/server/src/industry/inventionRegistrar.ts new file mode 100644 index 0000000..8043745 --- /dev/null +++ b/server/src/industry/inventionRegistrar.ts @@ -0,0 +1,48 @@ +import type { EntityId } from '@dflike/shared'; +import type { ItemRegistry } from './itemRegistry.js'; +import type { RecipeRegistry } from './recipeRegistry.js'; +import type { InventionTimeline } from './inventionTimeline.js'; +import type { RawInvention } from './inventionValidator.js'; + +export interface RegisterInventionArgs { + raw: RawInvention; + itemId: string; + itemRegistry: ItemRegistry; + recipeRegistry: RecipeRegistry; + timeline: InventionTimeline; + inventorEntityId: EntityId; + inventorName: string; + tick: number; + day: number; +} + +export function registerInvention(args: RegisterInventionArgs): boolean { + const { raw, itemId, itemRegistry, recipeRegistry, timeline, inventorEntityId, inventorName, tick, day } = args; + + // Register item + itemRegistry.register({ + id: itemId, + name: raw.name, + description: raw.description, + category: raw.category as 'resource' | 'tool' | 'material' | 'structure', + source: 'invented', + inventedBy: { entityId: inventorEntityId, name: inventorName, tick, day }, + }); + + // Register recipe + const recipePrefix = raw.category === 'structure' ? 'build_' : 'craft_'; + recipeRegistry.register({ + id: `${recipePrefix}${itemId}`, + outputItemId: itemId, + outputQuantity: 1, + inputs: raw.inputs, + workshopType: raw.workshopType ?? undefined, + toolRequired: raw.toolRequired ?? undefined, + source: 'invented', + }); + + // Record in timeline + timeline.record({ itemId, inventorEntityId, inventorName, tick, day }); + + return true; +}