diff --git a/app/game-engine/src/turn/monthlyCreateAdminNpcAction.ts b/app/game-engine/src/turn/monthlyCreateAdminNpcAction.ts new file mode 100644 index 0000000..a781b42 --- /dev/null +++ b/app/game-engine/src/turn/monthlyCreateAdminNpcAction.ts @@ -0,0 +1,5 @@ +import type { MonthlyEventActionHandler } from './monthlyEventHandler.js'; + +// 레거시 CreateAdminNPC::run()은 [class, "NYI"]만 반환하고 어떤 상태도 +// 변경하지 않는다. 새 관리자 NPC를 임의로 만들지 않고 no-op 계약을 보존한다. +export const createCreateAdminNpcHandler = (): MonthlyEventActionHandler => () => undefined; diff --git a/app/game-engine/src/turn/turnDaemon.ts b/app/game-engine/src/turn/turnDaemon.ts index ae19cf7..660cc88 100644 --- a/app/game-engine/src/turn/turnDaemon.ts +++ b/app/game-engine/src/turn/turnDaemon.ts @@ -50,6 +50,7 @@ import { createUpdateCitySupplyHandler } from './monthlyCitySupplyAction.js'; import { createUpdateNationLevelHandler } from './monthlyNationLevelAction.js'; import { createProcessSemiAnnualHandler } from './monthlySemiAnnualAction.js'; import { createProcessWarIncomeHandler } from './monthlyWarIncomeAction.js'; +import { createCreateAdminNpcHandler } from './monthlyCreateAdminNpcAction.js'; import { DatabaseTurnDaemonLease, TurnDaemonLeaseUnavailableError } from '../lifecycle/databaseTurnDaemonLease.js'; export interface TurnDaemonRuntimeOptions { @@ -217,6 +218,7 @@ const createTurnDaemonRuntimeWithLease = async ( nationTraits: nationTraitMap, }) ); + eventActions.set('CreateAdminNPC', createCreateAdminNpcHandler()); if (reservedTurnStoreHandle) { eventActions.set( 'UpdateNationLevel', diff --git a/app/game-engine/test/monthlyCreateAdminNpcAction.test.ts b/app/game-engine/test/monthlyCreateAdminNpcAction.test.ts new file mode 100644 index 0000000..5e7071c --- /dev/null +++ b/app/game-engine/test/monthlyCreateAdminNpcAction.test.ts @@ -0,0 +1,71 @@ +import { describe, expect, it } from 'vitest'; + +import { InMemoryTurnWorld } from '../src/turn/inMemoryWorld.js'; +import { createCreateAdminNpcHandler } from '../src/turn/monthlyCreateAdminNpcAction.js'; +import { createMonthlyEventHandler } from '../src/turn/monthlyEventHandler.js'; +import type { TurnWorldSnapshot, TurnWorldState } from '../src/turn/types.js'; + +describe('CreateAdminNPC monthly action', () => { + it('preserves the legacy NYI no-op while allowing the event to continue', async () => { + const state: TurnWorldState = { + id: 1, + currentYear: 193, + currentMonth: 11, + tickSeconds: 600, + lastTurnTime: new Date('0193-11-01T00:00:00.000Z'), + meta: {}, + }; + const snapshot: TurnWorldSnapshot = { + scenarioConfig: { + stat: { total: 300, min: 10, max: 100, npcTotal: 150, npcMax: 50, npcMin: 10, chiefMin: 70 }, + iconPath: '', + map: {}, + const: {}, + environment: { mapName: 'test', unitSet: 'default' }, + }, + map: { + id: 'test', + name: 'test', + cities: [], + defaults: { trust: 50, trade: 100, supplyState: 1, frontState: 0 }, + }, + generals: [], + cities: [], + nations: [], + troops: [], + diplomacy: [], + events: [ + { + id: 77, + targetCode: 'month', + priority: 1_000, + condition: true, + action: [['CreateAdminNPC'], ['DeleteEvent']], + meta: { marker: true }, + }, + ], + initialEvents: [], + }; + let world: InMemoryTurnWorld | null = null; + const calendarHandler = createMonthlyEventHandler({ + getWorld: () => world, + startYear: 190, + actions: new Map([['CreateAdminNPC', createCreateAdminNpcHandler()]]), + }); + world = new InMemoryTurnWorld(state, snapshot, { + schedule: { entries: [{ startMinute: 0, tickMinutes: 10 }] }, + calendarHandler, + }); + + await world.advanceMonth(new Date('0193-12-01T00:00:00.000Z')); + + expect(world.listGenerals()).toEqual([]); + expect(world.peekDirtyState()).toMatchObject({ + generals: [], + cities: [], + nations: [], + logs: [], + deletedEvents: [77], + }); + }); +});