diff --git a/packages/logic/src/actions/turn/general/che_NPC능동.ts b/packages/logic/src/actions/turn/general/che_NPC능동.ts index 536f80e..d65079f 100644 --- a/packages/logic/src/actions/turn/general/che_NPC능동.ts +++ b/packages/logic/src/actions/turn/general/che_NPC능동.ts @@ -1,11 +1,6 @@ import type { GeneralTriggerState } from '@sammo-ts/logic/domain/entities.js'; import type { Constraint, ConstraintContext } from '@sammo-ts/logic/constraints/types.js'; -import { - mustBeNPC, - reqGeneralGold, - unknownOrDeny, - existsDestCity, -} from '@sammo-ts/logic/constraints/presets.js'; +import { mustBeNPC, reqGeneralGold, unknownOrDeny, existsDestCity } from '@sammo-ts/logic/constraints/presets.js'; import type { GeneralActionDefinition } from '@sammo-ts/logic/actions/definition.js'; import type { GeneralActionOutcome, @@ -25,17 +20,16 @@ export interface NPCSelfArgs { destCityId?: number; } -export type NPCSelfResolveContext< - TriggerState extends GeneralTriggerState = GeneralTriggerState -> = GeneralActionResolveContext & { - map?: MapDefinition; -}; +export type NPCSelfResolveContext = + GeneralActionResolveContext & { + map?: MapDefinition; + }; const ACTION_NAME = 'NPC능동'; const ACTION_KEY = 'che_NPC능동'; export class ActionResolver< - TriggerState extends GeneralTriggerState = GeneralTriggerState + TriggerState extends GeneralTriggerState = GeneralTriggerState, > implements GeneralActionResolver { readonly key = ACTION_KEY; @@ -85,7 +79,7 @@ export class ActionResolver< } export class ActionDefinition< - TriggerState extends GeneralTriggerState = GeneralTriggerState + TriggerState extends GeneralTriggerState = GeneralTriggerState, > implements GeneralActionDefinition> { public readonly key = ACTION_KEY; public readonly name = ACTION_NAME; @@ -111,9 +105,7 @@ export class ActionDefinition< } buildConstraints(ctx: ConstraintContext, args: NPCSelfArgs): Constraint[] { - const constraints = [ - mustBeNPC() - ]; + const constraints = [mustBeNPC()]; if (args.optionText === '순간이동') { constraints.push(existsDestCity()); diff --git a/packages/logic/src/actions/turn/general/che_강행.ts b/packages/logic/src/actions/turn/general/che_강행.ts index a73ef0d..1d5e99b 100644 --- a/packages/logic/src/actions/turn/general/che_강행.ts +++ b/packages/logic/src/actions/turn/general/che_강행.ts @@ -28,7 +28,7 @@ export interface ForcedMoveArgs { } export interface ForcedMoveResolveContext< - TriggerState extends GeneralTriggerState = GeneralTriggerState + TriggerState extends GeneralTriggerState = GeneralTriggerState, > extends GeneralActionResolveContext { moveGenerals?: General[]; // For roaming move map?: MapDefinition; @@ -39,7 +39,7 @@ const ACTION_NAME = '강행'; const ACTION_KEY = 'che_강행'; export class ActionResolver< - TriggerState extends GeneralTriggerState = GeneralTriggerState + TriggerState extends GeneralTriggerState = GeneralTriggerState, > implements GeneralActionResolver { readonly key = ACTION_KEY; @@ -97,7 +97,8 @@ export class ActionResolver< const nextTrain = Math.max(20, general.train - 5); const nextAtmos = Math.max(20, general.atmos - 5); const nextExp = general.experience + 100; - const nextLeadershipExp = (typeof general.meta.leadership_exp === 'number' ? general.meta.leadership_exp : 0) + 1; + const nextLeadershipExp = + (typeof general.meta.leadership_exp === 'number' ? general.meta.leadership_exp : 0) + 1; effects.push( createGeneralPatchEffect( @@ -128,14 +129,14 @@ export class ActionResolver< // Currently addLog attaches provided logs to turnLog (which is for the actor). // To log for OTHERS, we might need specific effect or handle it differently. // For now, I will omit logs for others or use a special effect if available. - // The legacy TS porting pattern for "others" logs isn't fully standardized yet in shared snippets. + // The legacy TS porting pattern for "others" logs isn't fully standardized yet in shared snippets. // Assuming createGeneralPatchEffect handles state. Logs for others might be missing in this iteration unless I find `createLogEffect`. effects.push( createGeneralPatchEffect( { ...target, - cityId: destCityId + cityId: destCityId, }, target.id ) @@ -148,7 +149,7 @@ export class ActionResolver< } export class ActionDefinition< - TriggerState extends GeneralTriggerState = GeneralTriggerState + TriggerState extends GeneralTriggerState = GeneralTriggerState, > implements GeneralActionDefinition> { public readonly key = ACTION_KEY; public readonly name = ACTION_NAME; @@ -173,7 +174,7 @@ export class ActionDefinition< const cost = ctx.env.develCost as number; return (cost ?? 0) * 5; }), - reqGeneralRice(() => 0) // Legacy checks cost[1] which is 0, but included constraint. + reqGeneralRice(() => 0), // Legacy checks cost[1] which is 0, but included constraint. ]; } diff --git a/packages/logic/src/actions/turn/general/che_귀환.ts b/packages/logic/src/actions/turn/general/che_귀환.ts index 4c8f16a..6827b36 100644 --- a/packages/logic/src/actions/turn/general/che_귀환.ts +++ b/packages/logic/src/actions/turn/general/che_귀환.ts @@ -1,4 +1,3 @@ - import type { City, General, GeneralTriggerState, Nation } from '@sammo-ts/logic/domain/entities.js'; import type { Constraint, ConstraintContext } from '@sammo-ts/logic/constraints/types.js'; import { @@ -23,7 +22,7 @@ import type { TurnCommandEnv } from '@sammo-ts/logic/actions/turn/commandEnv.js' import type { ActionContextBuilder } from '@sammo-ts/logic/actions/turn/actionContext.js'; import type { GeneralTurnCommandSpec } from './index.js'; -export interface ReturnArgs { } +export interface ReturnArgs {} const ACTION_NAME = '귀환'; const ACTION_KEY = 'che_귀환'; @@ -71,7 +70,7 @@ export class ActionResolver< throw new Error('Destination city not found (No capital?).'); } - // We need city name for log. + // We need city name for log. // If we don't have city object in context, we can't log name easily unless we fetch it. // We should add `getWorldCity(id)` to context or something. // Or assume resolving involves looking up city. @@ -83,8 +82,8 @@ export class ActionResolver< // `GeneralActionResolveContext` usually doesn't have `worldRef`. // We must rely on `contextBuilder` to populate necessary data. - // Let's iterate `nationCities` if provided. - // Or use `ActionContextBuilder` to fetch city by ID if we knew it? + // Let's iterate `nationCities` if provided. + // Or use `ActionContextBuilder` to fetch city by ID if we knew it? // We don't know ID until we check logic. // In `ActionContextBuilder`, we can just load all cities of the nation? @@ -94,7 +93,7 @@ export class ActionResolver< let foundDestCity: City | undefined; if (context.nationCities) { - foundDestCity = context.nationCities.find(c => c.id === destCityId); + foundDestCity = context.nationCities.find((c) => c.id === destCityId); if (foundDestCity) { destCityName = foundDestCity.name; } @@ -108,7 +107,7 @@ export class ActionResolver< context.addLog(`${destCityName}${josaRo} 귀환했습니다.`, { category: LogCategory.ACTION, - format: LogFormat.MONTH, // Or HM if we want HH:MM suffix? + format: LogFormat.MONTH, // Or HM if we want HH:MM suffix? // Legacy uses explicit date at end. // We can just use standard MONTH format for now. }); @@ -116,27 +115,32 @@ export class ActionResolver< const exp = 70; const ded = 100; - effects.push(createGeneralPatchEffect({ - ...general, - cityId: destCityId, - experience: general.experience + exp, - dedication: general.dedication + ded, - stats: { - ...general.stats, - leadership: general.stats.leadership, // Update not needed unless verified - }, - // leadership_exp + 1 in legacy? - // "increaseVar('leadership_exp', 1)" - // General entity doesn't show leadership_exp in Interface? - // Checking `entities.ts`... `stats` is Leadership/Strength/Intel. - // `experience` is total exp? - // If `leadership_exp` is missing in Entity, we must use meta or omit. - // Default to meta if needed. - meta: { - ...general.meta, - leadership_exp: (readMetaNumberFromUnknown(general.meta, 'leadership_exp') ?? 0) + 1 - } - }, general.id)); + effects.push( + createGeneralPatchEffect( + { + ...general, + cityId: destCityId, + experience: general.experience + exp, + dedication: general.dedication + ded, + stats: { + ...general.stats, + leadership: general.stats.leadership, // Update not needed unless verified + }, + // leadership_exp + 1 in legacy? + // "increaseVar('leadership_exp', 1)" + // General entity doesn't show leadership_exp in Interface? + // Checking `entities.ts`... `stats` is Leadership/Strength/Intel. + // `experience` is total exp? + // If `leadership_exp` is missing in Entity, we must use meta or omit. + // Default to meta if needed. + meta: { + ...general.meta, + leadership_exp: (readMetaNumberFromUnknown(general.meta, 'leadership_exp') ?? 0) + 1, + }, + }, + general.id + ) + ); return { effects }; } @@ -173,9 +177,9 @@ export class ActionDefinition< export const actionContextBuilder: ActionContextBuilder = (base, options) => { return { ...base, - // We effectively need all cities to find the destination name if it's not the capital. + // We effectively need all cities to find the destination name if it's not the capital. // Or at least nation cities. - nationCities: options.worldRef?.listCities().filter(c => c.nationId === base.nation?.id) ?? [], + nationCities: options.worldRef?.listCities().filter((c) => c.nationId === base.nation?.id) ?? [], }; }; diff --git a/packages/logic/src/actions/turn/general/che_등용수락.ts b/packages/logic/src/actions/turn/general/che_등용수락.ts index 002e74a..86141a7 100644 --- a/packages/logic/src/actions/turn/general/che_등용수락.ts +++ b/packages/logic/src/actions/turn/general/che_등용수락.ts @@ -1,4 +1,3 @@ - import type { General, GeneralTriggerState, Nation } from '@sammo-ts/logic/domain/entities.js'; import type { Constraint, ConstraintContext } from '@sammo-ts/logic/constraints/types.js'; import { @@ -45,7 +44,10 @@ export class ActionResolver< > implements GeneralActionResolver { readonly key = ACTION_KEY; - resolve(context: AcceptScoutResolveContext, _args: AcceptScoutArgs): GeneralActionOutcome { + resolve( + context: AcceptScoutResolveContext, + _args: AcceptScoutArgs + ): GeneralActionOutcome { const general = context.general; const currentNation = context.nation; const destNation = context.destNation; @@ -64,7 +66,8 @@ export class ActionResolver< const josaYi = JosaUtil.pick(generalName, '이'); // Self Log - context.addLog(`${destNationName}${josaRo} 망명하여 수도로 이동합니다.`, { // Text says "Move to Capital", but logic might move to recruiter city. + context.addLog(`${destNationName}${josaRo} 망명하여 수도로 이동합니다.`, { + // Text says "Move to Capital", but logic might move to recruiter city. // Legacy log says "수도로 이동합니다", but implementation moves to destGeneral city if present! // We should match implementation or text? Text is just flavor. category: LogCategory.ACTION, @@ -89,10 +92,15 @@ export class ActionResolver< }); // 2. Recruiter Rewards - effects.push(createGeneralPatchEffect({ - experience: destGeneral.experience + 100, - dedication: destGeneral.dedication + 100, - }, destGeneral.id)); + effects.push( + createGeneralPatchEffect( + { + experience: destGeneral.experience + 100, + dedication: destGeneral.dedication + 100, + }, + destGeneral.id + ) + ); // 3. Betrayal Logic // If currentNation exists (and > 0), handle betrayal return logic. @@ -107,7 +115,7 @@ export class ActionResolver< let newExp = general.experience; let newDed = general.dedication; - const betrayCount = (readMetaNumberFromUnknown(general.meta, 'betray') ?? 0); + const betrayCount = readMetaNumberFromUnknown(general.meta, 'betray') ?? 0; let newBetray = betrayCount; if (currentNation && currentNation.id !== 0) { @@ -125,16 +133,22 @@ export class ActionResolver< } if (returnGold > 0 || returnRice > 0) { - effects.push(createNationPatchEffect({ - gold: currentNation.gold + returnGold, - rice: currentNation.rice + returnRice - }, currentNation.id)); + effects.push( + createNationPatchEffect( + { + gold: currentNation.gold + returnGold, + rice: currentNation.rice + returnRice, + }, + currentNation.id + ) + ); } // Penalty // 10% * betray count deduction - const penaltyFactor = 1 - (0.1 * betrayCount); - if (penaltyFactor < 0) { // Should not be less than 0? capped at ? + const penaltyFactor = 1 - 0.1 * betrayCount; + if (penaltyFactor < 0) { + // Should not be less than 0? capped at ? // Legacy: (1 - 0.1 * betray). } // Apply penalty @@ -152,26 +166,31 @@ export class ActionResolver< // If recruiter is not valid city? if (!targetCityId) targetCityId = destNation.capitalCityId!; - effects.push(createGeneralPatchEffect({ - nationId: destNation.id, - cityId: targetCityId, - experience: newExp, - dedication: newDed, - gold: newGold, - rice: newRice, - officerLevel: 1, // Reset rank - // officer_city: 0 via meta - crew: general.crew, // Keep crew? Legacy implies checking troop leader. - // If troop leader, disband troop. - // TS entity `troopId`. - troopId: 0, // Quit troop - meta: { - ...general.meta, - officer_city: 0, - betray: newBetray, - // killturn logic? - } - }, general.id)); + effects.push( + createGeneralPatchEffect( + { + nationId: destNation.id, + cityId: targetCityId, + experience: newExp, + dedication: newDed, + gold: newGold, + rice: newRice, + officerLevel: 1, // Reset rank + // officer_city: 0 via meta + crew: general.crew, // Keep crew? Legacy implies checking troop leader. + // If troop leader, disband troop. + // TS entity `troopId`. + troopId: 0, // Quit troop + meta: { + ...general.meta, + officer_city: 0, + betray: newBetray, + // killturn logic? + }, + }, + general.id + ) + ); // 5. Update Nations Gen Count (Visual only? or real count) // Legacy updates `gennum`. @@ -214,7 +233,10 @@ export class ActionDefinition< ]; } - resolve(context: AcceptScoutResolveContext, args: AcceptScoutArgs): GeneralActionOutcome { + resolve( + context: AcceptScoutResolveContext, + args: AcceptScoutArgs + ): GeneralActionOutcome { return this.resolver.resolve(context, args); } } diff --git a/packages/logic/test/scenarios/general/che_NPC능동.test.ts b/packages/logic/test/scenarios/general/che_NPC능동.test.ts index ba02b64..05062ca 100644 --- a/packages/logic/test/scenarios/general/che_NPC능동.test.ts +++ b/packages/logic/test/scenarios/general/che_NPC능동.test.ts @@ -101,7 +101,6 @@ function createViewState(world: InMemoryWorld, year: number = 200, env: TurnComm } describe('che_NPC능동', () => { - it('should allow NPC to teleport with "순간이동"', async () => { const bootstrapResult = buildScenarioBootstrap({ scenario: MOCK_SCENARIO_BASE, @@ -125,7 +124,12 @@ describe('che_NPC능동', () => { experience: 0, dedication: 0, officerLevel: 0, - role: { personality: null, specialDomestic: null, specialWar: null, items: { horse: null, weapon: null, book: null, item: null } }, + role: { + personality: null, + specialDomestic: null, + specialWar: null, + items: { horse: null, weapon: null, book: null, item: null }, + }, injury: 0, gold: 1000, rice: 1000, @@ -144,9 +148,11 @@ describe('che_NPC능동', () => { { generalId: general.id, commandKey: 'che_NPC능동', - resolver: (await import('../../../src/actions/turn/general/che_NPC능동.js')).commandSpec.createDefinition({} as any), + resolver: ( + await import('../../../src/actions/turn/general/che_NPC능동.js') + ).commandSpec.createDefinition({} as any), args: { optionText: '순간이동', destCityId }, - } + }, ]); const updated = world.getGeneral(general.id); @@ -173,7 +179,12 @@ describe('che_NPC능동', () => { experience: 0, dedication: 0, officerLevel: 0, - role: { personality: null, specialDomestic: null, specialWar: null, items: { horse: null, weapon: null, book: null, item: null } }, + role: { + personality: null, + specialDomestic: null, + specialWar: null, + items: { horse: null, weapon: null, book: null, item: null }, + }, injury: 0, gold: 1000, rice: 1000, @@ -188,7 +199,9 @@ describe('che_NPC능동', () => { }; world.snapshot.generals.push(general); - const def = (await import('../../../src/actions/turn/general/che_NPC능동.js')).commandSpec.createDefinition({} as any); + const def = (await import('../../../src/actions/turn/general/che_NPC능동.js')).commandSpec.createDefinition( + {} as any + ); const args = { optionText: '순간이동', destCityId }; const ctx = createConstraintContext(general, 200, args); diff --git a/packages/logic/test/scenarios/general/che_강행.test.ts b/packages/logic/test/scenarios/general/che_강행.test.ts index 17574e5..4eaf969 100644 --- a/packages/logic/test/scenarios/general/che_강행.test.ts +++ b/packages/logic/test/scenarios/general/che_강행.test.ts @@ -35,11 +35,56 @@ const LINEAR_MAP: MapDefinition = { id: 'linear_map', name: 'Linear Map', cities: [ - { id: 101, name: 'City1', level: 1, region: 1, position: { x: 0, y: 0 }, connections: [102], max: {} as any, initial: {} as any }, - { id: 102, name: 'City2', level: 1, region: 1, position: { x: 0, y: 0 }, connections: [101, 103], max: {} as any, initial: {} as any }, - { id: 103, name: 'City3', level: 1, region: 1, position: { x: 0, y: 0 }, connections: [102, 104], max: {} as any, initial: {} as any }, - { id: 104, name: 'City4', level: 1, region: 1, position: { x: 0, y: 0 }, connections: [103, 105], max: {} as any, initial: {} as any }, - { id: 105, name: 'City5', level: 1, region: 1, position: { x: 0, y: 0 }, connections: [104], max: {} as any, initial: {} as any }, + { + id: 101, + name: 'City1', + level: 1, + region: 1, + position: { x: 0, y: 0 }, + connections: [102], + max: {} as any, + initial: {} as any, + }, + { + id: 102, + name: 'City2', + level: 1, + region: 1, + position: { x: 0, y: 0 }, + connections: [101, 103], + max: {} as any, + initial: {} as any, + }, + { + id: 103, + name: 'City3', + level: 1, + region: 1, + position: { x: 0, y: 0 }, + connections: [102, 104], + max: {} as any, + initial: {} as any, + }, + { + id: 104, + name: 'City4', + level: 1, + region: 1, + position: { x: 0, y: 0 }, + connections: [103, 105], + max: {} as any, + initial: {} as any, + }, + { + id: 105, + name: 'City5', + level: 1, + region: 1, + position: { x: 0, y: 0 }, + connections: [104], + max: {} as any, + initial: {} as any, + }, ], defaults: { trust: 50, trade: 100, supplyState: 1, frontState: 0 }, }; @@ -140,7 +185,12 @@ describe('che_강행', () => { experience: 1000, dedication: 0, officerLevel: 1, - role: { personality: null, specialDomestic: null, specialWar: null, items: { horse: null, weapon: null, book: null, item: null } }, + role: { + personality: null, + specialDomestic: null, + specialWar: null, + items: { horse: null, weapon: null, book: null, item: null }, + }, injury: 0, gold: 1000, rice: 1000, @@ -154,18 +204,29 @@ describe('che_강행', () => { meta: {}, }; const nation: Nation = { - id: 1, name: 'MyNation', color: '#000', capitalCityId: 101, chiefGeneralId: 1, - gold: 0, rice: 0, power: 0, level: 1, typeCode: 'che_def', meta: {}, + id: 1, + name: 'MyNation', + color: '#000', + capitalCityId: 101, + chiefGeneralId: 1, + gold: 0, + rice: 0, + power: 0, + level: 1, + typeCode: 'che_def', + meta: {}, }; world.snapshot.generals.push(general); world.snapshot.nations.push(nation); // Move 101 -> 104 (dist 3) OK - const definition = (await import('../../../src/actions/turn/general/che_강행.js')).commandSpec.createDefinition({ - scenarioConfig: { const: { develCost: 10 } } as any, - worldRef: { listGenerals: () => world.getAllGenerals() } as any, // Mock world ref context - map: LINEAR_MAP - } as any); + const definition = (await import('../../../src/actions/turn/general/che_강행.js')).commandSpec.createDefinition( + { + scenarioConfig: { const: { develCost: 10 } } as any, + worldRef: { listGenerals: () => world.getAllGenerals() } as any, // Mock world ref context + map: LINEAR_MAP, + } as any + ); await runner.runTurn([ { @@ -175,9 +236,9 @@ describe('che_강행', () => { args: { destCityId: 104 }, context: { map: LINEAR_MAP, - startDevelCost: 10 - } - } + startDevelCost: 10, + }, + }, ]); const updated = world.getGeneral(general.id); @@ -206,7 +267,12 @@ describe('che_강행', () => { experience: 1000, dedication: 0, officerLevel: 1, - role: { personality: null, specialDomestic: null, specialWar: null, items: { horse: null, weapon: null, book: null, item: null } }, + role: { + personality: null, + specialDomestic: null, + specialWar: null, + items: { horse: null, weapon: null, book: null, item: null }, + }, injury: 0, gold: 1000, rice: 1000, @@ -220,18 +286,29 @@ describe('che_강행', () => { meta: {}, }; const nation: Nation = { - id: 1, name: 'MyNation', color: '#000', capitalCityId: 101, chiefGeneralId: 1, - gold: 0, rice: 0, power: 0, level: 1, typeCode: 'che_def', meta: {}, + id: 1, + name: 'MyNation', + color: '#000', + capitalCityId: 101, + chiefGeneralId: 1, + gold: 0, + rice: 0, + power: 0, + level: 1, + typeCode: 'che_def', + meta: {}, }; world.snapshot.generals.push(general); world.snapshot.nations.push(nation); // Move 101 -> 105 (dist 4) Fail - const definition = (await import('../../../src/actions/turn/general/che_강행.js')).commandSpec.createDefinition({ - scenarioConfig: { const: { develCost: 10 } } as any, - worldRef: { listGenerals: () => world.getAllGenerals() } as any, - map: LINEAR_MAP - } as any); + const definition = (await import('../../../src/actions/turn/general/che_강행.js')).commandSpec.createDefinition( + { + scenarioConfig: { const: { develCost: 10 } } as any, + worldRef: { listGenerals: () => world.getAllGenerals() } as any, + map: LINEAR_MAP, + } as any + ); // Manual constraint check const args = { destCityId: 105 }; @@ -258,35 +335,88 @@ describe('che_강행', () => { const runner = new TestGameRunner(world, 200, 1); const leader: General = { - id: 1, name: 'Leader', nationId: 1, cityId: 101, troopId: 0, - stats: { leadership: 50, strength: 50, intelligence: 50 }, experience: 0, dedication: 0, - officerLevel: 12, role: { personality: null, specialDomestic: null, specialWar: null, items: { horse: null, weapon: null, book: null, item: null } }, - injury: 0, gold: 1000, rice: 1000, crew: 0, crewTypeId: 0, train: 50, atmos: 50, age: 20, npcState: 0, triggerState: { flags: {}, counters: {}, modifiers: {}, meta: {} }, meta: {}, + id: 1, + name: 'Leader', + nationId: 1, + cityId: 101, + troopId: 0, + stats: { leadership: 50, strength: 50, intelligence: 50 }, + experience: 0, + dedication: 0, + officerLevel: 12, + role: { + personality: null, + specialDomestic: null, + specialWar: null, + items: { horse: null, weapon: null, book: null, item: null }, + }, + injury: 0, + gold: 1000, + rice: 1000, + crew: 0, + crewTypeId: 0, + train: 50, + atmos: 50, + age: 20, + npcState: 0, + triggerState: { flags: {}, counters: {}, modifiers: {}, meta: {} }, + meta: {}, }; const sub: General = { - id: 2, name: 'Sub', nationId: 1, cityId: 101, troopId: 0, - stats: { leadership: 50, strength: 50, intelligence: 50 }, experience: 0, dedication: 0, - officerLevel: 1, role: { personality: null, specialDomestic: null, specialWar: null, items: { horse: null, weapon: null, book: null, item: null } }, - injury: 0, gold: 1000, rice: 1000, crew: 0, crewTypeId: 0, train: 50, atmos: 50, age: 20, npcState: 0, triggerState: { flags: {}, counters: {}, modifiers: {}, meta: {} }, meta: {}, + id: 2, + name: 'Sub', + nationId: 1, + cityId: 101, + troopId: 0, + stats: { leadership: 50, strength: 50, intelligence: 50 }, + experience: 0, + dedication: 0, + officerLevel: 1, + role: { + personality: null, + specialDomestic: null, + specialWar: null, + items: { horse: null, weapon: null, book: null, item: null }, + }, + injury: 0, + gold: 1000, + rice: 1000, + crew: 0, + crewTypeId: 0, + train: 50, + atmos: 50, + age: 20, + npcState: 0, + triggerState: { flags: {}, counters: {}, modifiers: {}, meta: {} }, + meta: {}, }; const nation: Nation = { - id: 1, name: 'RoamingNation', color: '#000', capitalCityId: 101, chiefGeneralId: 1, - gold: 0, rice: 0, power: 0, + id: 1, + name: 'RoamingNation', + color: '#000', + capitalCityId: 101, + chiefGeneralId: 1, + gold: 0, + rice: 0, + power: 0, level: 0, // Roaming - typeCode: 'che_def', meta: {}, + typeCode: 'che_def', + meta: {}, }; world.snapshot.generals.push(leader); world.snapshot.generals.push(sub); world.snapshot.nations.push(nation); - const definition = (await import('../../../src/actions/turn/general/che_강행.js')).commandSpec.createDefinition({ - scenarioConfig: { const: { develCost: 10 } } as any, - worldRef: { listGenerals: () => world.getAllGenerals() } as any, // Must return valid list - map: LINEAR_MAP - } as any); + const definition = (await import('../../../src/actions/turn/general/che_강행.js')).commandSpec.createDefinition( + { + scenarioConfig: { const: { develCost: 10 } } as any, + worldRef: { listGenerals: () => world.getAllGenerals() } as any, // Must return valid list + map: LINEAR_MAP, + } as any + ); await runner.runTurn([ { @@ -297,9 +427,9 @@ describe('che_강행', () => { context: { map: LINEAR_MAP, startDevelCost: 10, - moveGenerals: world.getAllGenerals() - } - } + moveGenerals: world.getAllGenerals(), + }, + }, ]); const updatedLeader = world.getGeneral(leader.id); diff --git a/packages/logic/test/scenarios/general/che_귀환.test.ts b/packages/logic/test/scenarios/general/che_귀환.test.ts index c1f0d07..91d128c 100644 --- a/packages/logic/test/scenarios/general/che_귀환.test.ts +++ b/packages/logic/test/scenarios/general/che_귀환.test.ts @@ -35,9 +35,36 @@ const LINEAR_MAP: MapDefinition = { id: 'linear_map', name: 'Linear Map', cities: [ - { id: 101, name: 'CapitalCity', level: 1, region: 1, position: { x: 0, y: 0 }, connections: [102], max: {} as any, initial: {} as any }, - { id: 102, name: 'OtherCity', level: 1, region: 1, position: { x: 0, y: 0 }, connections: [101, 103], max: {} as any, initial: {} as any }, - { id: 103, name: 'OfficerCity', level: 1, region: 1, position: { x: 0, y: 0 }, connections: [102], max: {} as any, initial: {} as any }, + { + id: 101, + name: 'CapitalCity', + level: 1, + region: 1, + position: { x: 0, y: 0 }, + connections: [102], + max: {} as any, + initial: {} as any, + }, + { + id: 102, + name: 'OtherCity', + level: 1, + region: 1, + position: { x: 0, y: 0 }, + connections: [101, 103], + max: {} as any, + initial: {} as any, + }, + { + id: 103, + name: 'OfficerCity', + level: 1, + region: 1, + position: { x: 0, y: 0 }, + connections: [102], + max: {} as any, + initial: {} as any, + }, ], defaults: { trust: 50, trade: 100, supplyState: 1, frontState: 0 }, }; @@ -78,7 +105,7 @@ function createConstraintContext(actor: General, year: number = 200, args: any = world: { currentYear: year }, openingPartYear: systemEnv.openingPartYear, map: LINEAR_MAP, - cities: LINEAR_MAP.cities + cities: LINEAR_MAP.cities, }, mode: 'full', }; @@ -119,7 +146,6 @@ function createViewState(world: InMemoryWorld, year: number = 200, env: TurnComm } describe('che_귀환', () => { - it('should return to capital if normal officer', async () => { const bootstrapResult = buildScenarioBootstrap({ scenario: MOCK_SCENARIO_BASE, @@ -129,14 +155,45 @@ describe('che_귀환', () => { const runner = new TestGameRunner(world, 200, 1); const general: General = { - id: 1, name: 'NormalOfficer', nationId: 1, cityId: 102, troopId: 0, - stats: { leadership: 50, strength: 50, intelligence: 50 }, experience: 0, dedication: 0, - officerLevel: 1, role: { personality: null, specialDomestic: null, specialWar: null, items: { horse: null, weapon: null, book: null, item: null } }, - injury: 0, gold: 1000, rice: 1000, crew: 0, crewTypeId: 0, train: 0, atmos: 0, age: 20, npcState: 0, triggerState: { flags: {}, counters: {}, modifiers: {}, meta: {} }, meta: {}, + id: 1, + name: 'NormalOfficer', + nationId: 1, + cityId: 102, + troopId: 0, + stats: { leadership: 50, strength: 50, intelligence: 50 }, + experience: 0, + dedication: 0, + officerLevel: 1, + role: { + personality: null, + specialDomestic: null, + specialWar: null, + items: { horse: null, weapon: null, book: null, item: null }, + }, + injury: 0, + gold: 1000, + rice: 1000, + crew: 0, + crewTypeId: 0, + train: 0, + atmos: 0, + age: 20, + npcState: 0, + triggerState: { flags: {}, counters: {}, modifiers: {}, meta: {} }, + meta: {}, }; const nation: Nation = { - id: 1, name: 'MyNation', color: '#000', capitalCityId: 101, chiefGeneralId: 1, - gold: 0, rice: 0, power: 0, level: 1, typeCode: 'che_def', meta: {}, + id: 1, + name: 'MyNation', + color: '#000', + capitalCityId: 101, + chiefGeneralId: 1, + gold: 0, + rice: 0, + power: 0, + level: 1, + typeCode: 'che_def', + meta: {}, }; world.snapshot.generals.push(general); world.snapshot.nations.push(nation); @@ -145,9 +202,11 @@ describe('che_귀환', () => { { generalId: general.id, commandKey: 'che_귀환', - resolver: (await import('../../../src/actions/turn/general/che_귀환.js')).commandSpec.createDefinition({} as any), + resolver: (await import('../../../src/actions/turn/general/che_귀환.js')).commandSpec.createDefinition( + {} as any + ), args: {}, - } + }, ]); const updated = world.getGeneral(general.id); @@ -168,15 +227,45 @@ describe('che_귀환', () => { const runner = new TestGameRunner(world, 200, 1); const general: General = { - id: 1, name: 'Governor', nationId: 1, cityId: 102, troopId: 0, - stats: { leadership: 50, strength: 50, intelligence: 50 }, experience: 0, dedication: 0, - officerLevel: 4, role: { personality: null, specialDomestic: null, specialWar: null, items: { horse: null, weapon: null, book: null, item: null } }, - injury: 0, gold: 1000, rice: 1000, crew: 0, crewTypeId: 0, train: 0, atmos: 0, age: 20, npcState: 0, triggerState: { flags: {}, counters: {}, modifiers: {}, meta: {} }, + id: 1, + name: 'Governor', + nationId: 1, + cityId: 102, + troopId: 0, + stats: { leadership: 50, strength: 50, intelligence: 50 }, + experience: 0, + dedication: 0, + officerLevel: 4, + role: { + personality: null, + specialDomestic: null, + specialWar: null, + items: { horse: null, weapon: null, book: null, item: null }, + }, + injury: 0, + gold: 1000, + rice: 1000, + crew: 0, + crewTypeId: 0, + train: 0, + atmos: 0, + age: 20, + npcState: 0, + triggerState: { flags: {}, counters: {}, modifiers: {}, meta: {} }, meta: { officer_city: 103 }, }; const nation: Nation = { - id: 1, name: 'MyNation', color: '#000', capitalCityId: 101, chiefGeneralId: 1, - gold: 0, rice: 0, power: 0, level: 1, typeCode: 'che_def', meta: {}, + id: 1, + name: 'MyNation', + color: '#000', + capitalCityId: 101, + chiefGeneralId: 1, + gold: 0, + rice: 0, + power: 0, + level: 1, + typeCode: 'che_def', + meta: {}, }; world.snapshot.generals.push(general); world.snapshot.nations.push(nation); @@ -185,9 +274,11 @@ describe('che_귀환', () => { { generalId: general.id, commandKey: 'che_귀환', - resolver: (await import('../../../src/actions/turn/general/che_귀환.js')).commandSpec.createDefinition({} as any), + resolver: (await import('../../../src/actions/turn/general/che_귀환.js')).commandSpec.createDefinition( + {} as any + ), args: {}, - } + }, ]); const updated = world.getGeneral(general.id); @@ -202,19 +293,52 @@ describe('che_귀환', () => { const world = new InMemoryWorld(bootstrapResult.snapshot); const general: General = { - id: 1, name: 'AlreadyHome', nationId: 1, cityId: 101, troopId: 0, - stats: { leadership: 50, strength: 50, intelligence: 50 }, experience: 0, dedication: 0, - officerLevel: 1, role: { personality: null, specialDomestic: null, specialWar: null, items: { horse: null, weapon: null, book: null, item: null } }, - injury: 0, gold: 1000, rice: 1000, crew: 0, crewTypeId: 0, train: 0, atmos: 0, age: 20, npcState: 0, triggerState: { flags: {}, counters: {}, modifiers: {}, meta: {} }, meta: {}, + id: 1, + name: 'AlreadyHome', + nationId: 1, + cityId: 101, + troopId: 0, + stats: { leadership: 50, strength: 50, intelligence: 50 }, + experience: 0, + dedication: 0, + officerLevel: 1, + role: { + personality: null, + specialDomestic: null, + specialWar: null, + items: { horse: null, weapon: null, book: null, item: null }, + }, + injury: 0, + gold: 1000, + rice: 1000, + crew: 0, + crewTypeId: 0, + train: 0, + atmos: 0, + age: 20, + npcState: 0, + triggerState: { flags: {}, counters: {}, modifiers: {}, meta: {} }, + meta: {}, }; const nation: Nation = { - id: 1, name: 'MyNation', color: '#000', capitalCityId: 101, chiefGeneralId: 1, - gold: 0, rice: 0, power: 0, level: 1, typeCode: 'che_def', meta: {}, + id: 1, + name: 'MyNation', + color: '#000', + capitalCityId: 101, + chiefGeneralId: 1, + gold: 0, + rice: 0, + power: 0, + level: 1, + typeCode: 'che_def', + meta: {}, }; world.snapshot.generals.push(general); world.snapshot.nations.push(nation); - const def = (await import('../../../src/actions/turn/general/che_귀환.js')).commandSpec.createDefinition({} as any); + const def = (await import('../../../src/actions/turn/general/che_귀환.js')).commandSpec.createDefinition( + {} as any + ); const args = {}; const ctx = createConstraintContext(general, 200, args); @@ -251,15 +375,45 @@ describe('che_귀환', () => { const runner = new TestGameRunner(world, 200, 1); const general: General = { - id: 1, name: 'GovernorAtHome', nationId: 1, cityId: 103, troopId: 0, - stats: { leadership: 50, strength: 50, intelligence: 50 }, experience: 0, dedication: 0, - officerLevel: 4, role: { personality: null, specialDomestic: null, specialWar: null, items: { horse: null, weapon: null, book: null, item: null } }, - injury: 0, gold: 1000, rice: 1000, crew: 0, crewTypeId: 0, train: 0, atmos: 0, age: 20, npcState: 0, triggerState: { flags: {}, counters: {}, modifiers: {}, meta: {} }, + id: 1, + name: 'GovernorAtHome', + nationId: 1, + cityId: 103, + troopId: 0, + stats: { leadership: 50, strength: 50, intelligence: 50 }, + experience: 0, + dedication: 0, + officerLevel: 4, + role: { + personality: null, + specialDomestic: null, + specialWar: null, + items: { horse: null, weapon: null, book: null, item: null }, + }, + injury: 0, + gold: 1000, + rice: 1000, + crew: 0, + crewTypeId: 0, + train: 0, + atmos: 0, + age: 20, + npcState: 0, + triggerState: { flags: {}, counters: {}, modifiers: {}, meta: {} }, meta: { officer_city: 103 }, }; const nation: Nation = { - id: 1, name: 'MyNation', color: '#000', capitalCityId: 101, chiefGeneralId: 1, - gold: 0, rice: 0, power: 0, level: 1, typeCode: 'che_def', meta: {}, + id: 1, + name: 'MyNation', + color: '#000', + capitalCityId: 101, + chiefGeneralId: 1, + gold: 0, + rice: 0, + power: 0, + level: 1, + typeCode: 'che_def', + meta: {}, }; world.snapshot.generals.push(general); world.snapshot.nations.push(nation); @@ -271,9 +425,11 @@ describe('che_귀환', () => { { generalId: general.id, commandKey: 'che_귀환', - resolver: (await import('../../../src/actions/turn/general/che_귀환.js')).commandSpec.createDefinition({} as any), + resolver: (await import('../../../src/actions/turn/general/che_귀환.js')).commandSpec.createDefinition( + {} as any + ), args: {}, - } + }, ]); const updated = world.getGeneral(general.id); diff --git a/packages/logic/test/scenarios/general/che_등용수락.test.ts b/packages/logic/test/scenarios/general/che_등용수락.test.ts index d742c08..393c3c2 100644 --- a/packages/logic/test/scenarios/general/che_등용수락.test.ts +++ b/packages/logic/test/scenarios/general/che_등용수락.test.ts @@ -1,4 +1,3 @@ - import { describe, expect, it } from 'vitest'; import type { General, Nation } from '../../../src/domain/entities.js'; import { buildScenarioBootstrap } from '../../../src/world/bootstrap.js'; @@ -36,8 +35,26 @@ const MINIMAL_MAP = { id: 'minimal_map', name: 'Minimal Map', cities: [ - { id: 101, name: 'Capital1', level: 1, region: 1, position: { x: 0, y: 0 }, connections: [], max: {} as any, initial: {} as any }, - { id: 102, name: 'Capital2', level: 1, region: 1, position: { x: 0, y: 0 }, connections: [], max: {} as any, initial: {} as any }, + { + id: 101, + name: 'Capital1', + level: 1, + region: 1, + position: { x: 0, y: 0 }, + connections: [], + max: {} as any, + initial: {} as any, + }, + { + id: 102, + name: 'Capital2', + level: 1, + region: 1, + position: { x: 0, y: 0 }, + connections: [], + max: {} as any, + initial: {} as any, + }, ], defaults: { trust: 50, trade: 100, supplyState: 1, frontState: 0 }, }; @@ -78,7 +95,7 @@ function createConstraintContext(actor: General, year: number = 200, args: any = world: { currentYear: year }, openingPartYear: systemEnv.openingPartYear, map: MINIMAL_MAP, - cities: MINIMAL_MAP.cities + cities: MINIMAL_MAP.cities, }, mode: 'full', }; @@ -131,21 +148,70 @@ describe('che_등용수락', () => { const runner = new TestGameRunner(world, 200, 1); const neutralGen: General = { - id: 1, name: 'Neutral', nationId: 0, cityId: 101, troopId: 0, - stats: { leadership: 50, strength: 50, intelligence: 50 }, experience: 0, dedication: 0, - officerLevel: 0, role: { personality: null, specialDomestic: null, specialWar: null, items: { horse: null, weapon: null, book: null, item: null } }, - injury: 0, gold: 1000, rice: 1000, crew: 0, crewTypeId: 0, train: 0, atmos: 0, age: 20, npcState: 0, triggerState: { flags: {}, counters: {}, modifiers: {}, meta: {} }, meta: {}, + id: 1, + name: 'Neutral', + nationId: 0, + cityId: 101, + troopId: 0, + stats: { leadership: 50, strength: 50, intelligence: 50 }, + experience: 0, + dedication: 0, + officerLevel: 0, + role: { + personality: null, + specialDomestic: null, + specialWar: null, + items: { horse: null, weapon: null, book: null, item: null }, + }, + injury: 0, + gold: 1000, + rice: 1000, + crew: 0, + crewTypeId: 0, + train: 0, + atmos: 0, + age: 20, + npcState: 0, + triggerState: { flags: {}, counters: {}, modifiers: {}, meta: {} }, + meta: {}, }; const recruiterGen: General = { - id: 2, name: 'Recruiter', nationId: 2, cityId: 102, troopId: 0, - stats: { leadership: 50, strength: 50, intelligence: 50 }, experience: 0, dedication: 0, - officerLevel: 1, role: null as any, injury: 0, gold: 1000, rice: 1000, crew: 0, crewTypeId: 0, train: 0, atmos: 0, age: 20, npcState: 0, triggerState: { flags: {}, counters: {}, modifiers: {}, meta: {} }, meta: {}, + id: 2, + name: 'Recruiter', + nationId: 2, + cityId: 102, + troopId: 0, + stats: { leadership: 50, strength: 50, intelligence: 50 }, + experience: 0, + dedication: 0, + officerLevel: 1, + role: null as any, + injury: 0, + gold: 1000, + rice: 1000, + crew: 0, + crewTypeId: 0, + train: 0, + atmos: 0, + age: 20, + npcState: 0, + triggerState: { flags: {}, counters: {}, modifiers: {}, meta: {} }, + meta: {}, }; const nation2: Nation = { - id: 2, name: 'Nation2', color: '#000', capitalCityId: 102, chiefGeneralId: 2, - gold: 0, rice: 0, power: 0, level: 1, typeCode: 'che_def', meta: {}, + id: 2, + name: 'Nation2', + color: '#000', + capitalCityId: 102, + chiefGeneralId: 2, + gold: 0, + rice: 0, + power: 0, + level: 1, + typeCode: 'che_def', + meta: {}, }; world.snapshot.generals.push(neutralGen); @@ -156,14 +222,16 @@ describe('che_등용수락', () => { { generalId: neutralGen.id, commandKey: 'che_등용수락', - resolver: (await import('../../../src/actions/turn/general/che_등용수락.js')).commandSpec.createDefinition({} as any), + resolver: ( + await import('../../../src/actions/turn/general/che_등용수락.js') + ).commandSpec.createDefinition({} as any), args: { destNationId: 2, destGeneralId: 2 }, context: { destNation: nation2, destGeneral: recruiterGen, - env: systemEnv - } - } + env: systemEnv, + }, + }, ]); const updatedSelf = world.getGeneral(neutralGen.id); @@ -188,24 +256,77 @@ describe('che_등용수락', () => { const runner = new TestGameRunner(world, 200, 1); const betrayer: General = { - id: 1, name: 'Betrayer', nationId: 1, cityId: 101, troopId: 0, - stats: { leadership: 50, strength: 50, intelligence: 50 }, experience: 1000, dedication: 1000, - officerLevel: 1, role: null as any, injury: 0, gold: 2000, rice: 2000, crew: 0, crewTypeId: 0, train: 0, atmos: 0, age: 20, npcState: 0, triggerState: { flags: {}, counters: {}, modifiers: {}, meta: {} }, + id: 1, + name: 'Betrayer', + nationId: 1, + cityId: 101, + troopId: 0, + stats: { leadership: 50, strength: 50, intelligence: 50 }, + experience: 1000, + dedication: 1000, + officerLevel: 1, + role: null as any, + injury: 0, + gold: 2000, + rice: 2000, + crew: 0, + crewTypeId: 0, + train: 0, + atmos: 0, + age: 20, + npcState: 0, + triggerState: { flags: {}, counters: {}, modifiers: {}, meta: {} }, meta: { betray: 1 }, }; const nation1: Nation = { - id: 1, name: 'Nation1', color: '#000', capitalCityId: 101, chiefGeneralId: 0, - gold: 0, rice: 0, power: 0, level: 1, typeCode: 'che_def', meta: {}, + id: 1, + name: 'Nation1', + color: '#000', + capitalCityId: 101, + chiefGeneralId: 0, + gold: 0, + rice: 0, + power: 0, + level: 1, + typeCode: 'che_def', + meta: {}, }; const recruiterGen: General = { - id: 2, name: 'Recruiter', nationId: 2, cityId: 102, troopId: 0, - stats: { leadership: 50, strength: 50, intelligence: 50 }, experience: 0, dedication: 0, - officerLevel: 1, role: null as any, injury: 0, gold: 1000, rice: 1000, crew: 0, crewTypeId: 0, train: 0, atmos: 0, age: 20, npcState: 0, triggerState: { flags: {}, counters: {}, modifiers: {}, meta: {} }, meta: {}, + id: 2, + name: 'Recruiter', + nationId: 2, + cityId: 102, + troopId: 0, + stats: { leadership: 50, strength: 50, intelligence: 50 }, + experience: 0, + dedication: 0, + officerLevel: 1, + role: null as any, + injury: 0, + gold: 1000, + rice: 1000, + crew: 0, + crewTypeId: 0, + train: 0, + atmos: 0, + age: 20, + npcState: 0, + triggerState: { flags: {}, counters: {}, modifiers: {}, meta: {} }, + meta: {}, }; const nation2: Nation = { - id: 2, name: 'Nation2', color: '#000', capitalCityId: 102, chiefGeneralId: 2, - gold: 0, rice: 0, power: 0, level: 1, typeCode: 'che_def', meta: {}, + id: 2, + name: 'Nation2', + color: '#000', + capitalCityId: 102, + chiefGeneralId: 2, + gold: 0, + rice: 0, + power: 0, + level: 1, + typeCode: 'che_def', + meta: {}, }; world.snapshot.generals.push(betrayer); @@ -217,14 +338,16 @@ describe('che_등용수락', () => { { generalId: betrayer.id, commandKey: 'che_등용수락', - resolver: (await import('../../../src/actions/turn/general/che_등용수락.js')).commandSpec.createDefinition({} as any), + resolver: ( + await import('../../../src/actions/turn/general/che_등용수락.js') + ).commandSpec.createDefinition({} as any), args: { destNationId: 2, destGeneralId: 2 }, context: { destNation: nation2, destGeneral: recruiterGen, - env: systemEnv - } - } + env: systemEnv, + }, + }, ]); const updatedSelf = world.getGeneral(betrayer.id); @@ -248,23 +371,81 @@ describe('che_등용수락', () => { const world = new InMemoryWorld(bootstrapResult.snapshot); const monarch: General = { - id: 1, name: 'Monarch', nationId: 1, cityId: 101, troopId: 0, - stats: { leadership: 50, strength: 50, intelligence: 50 }, experience: 0, dedication: 0, - officerLevel: 12, role: { personality: null, specialDomestic: null, specialWar: null, items: { horse: null, weapon: null, book: null, item: null } }, - injury: 0, gold: 1000, rice: 1000, crew: 0, crewTypeId: 0, train: 0, atmos: 0, age: 20, npcState: 0, triggerState: { flags: {}, counters: {}, modifiers: {}, meta: {} }, meta: {}, + id: 1, + name: 'Monarch', + nationId: 1, + cityId: 101, + troopId: 0, + stats: { leadership: 50, strength: 50, intelligence: 50 }, + experience: 0, + dedication: 0, + officerLevel: 12, + role: { + personality: null, + specialDomestic: null, + specialWar: null, + items: { horse: null, weapon: null, book: null, item: null }, + }, + injury: 0, + gold: 1000, + rice: 1000, + crew: 0, + crewTypeId: 0, + train: 0, + atmos: 0, + age: 20, + npcState: 0, + triggerState: { flags: {}, counters: {}, modifiers: {}, meta: {} }, + meta: {}, }; const nation1: Nation = { - id: 1, name: 'Nation1', color: '#000', capitalCityId: 101, chiefGeneralId: 1, - gold: 0, rice: 0, power: 0, level: 1, typeCode: 'che_def', meta: {}, + id: 1, + name: 'Nation1', + color: '#000', + capitalCityId: 101, + chiefGeneralId: 1, + gold: 0, + rice: 0, + power: 0, + level: 1, + typeCode: 'che_def', + meta: {}, }; const recruiterGen: General = { - id: 2, name: 'Recruiter', nationId: 2, cityId: 102, troopId: 0, - stats: { leadership: 50, strength: 50, intelligence: 50 }, experience: 0, dedication: 0, - officerLevel: 1, role: null as any, injury: 0, gold: 1000, rice: 1000, crew: 0, crewTypeId: 0, train: 0, atmos: 0, age: 20, npcState: 0, triggerState: { flags: {}, counters: {}, modifiers: {}, meta: {} }, meta: {}, + id: 2, + name: 'Recruiter', + nationId: 2, + cityId: 102, + troopId: 0, + stats: { leadership: 50, strength: 50, intelligence: 50 }, + experience: 0, + dedication: 0, + officerLevel: 1, + role: null as any, + injury: 0, + gold: 1000, + rice: 1000, + crew: 0, + crewTypeId: 0, + train: 0, + atmos: 0, + age: 20, + npcState: 0, + triggerState: { flags: {}, counters: {}, modifiers: {}, meta: {} }, + meta: {}, }; const nation2: Nation = { - id: 2, name: 'Nation2', color: '#000', capitalCityId: 102, chiefGeneralId: 2, - gold: 0, rice: 0, power: 0, level: 1, typeCode: 'che_def', meta: {}, + id: 2, + name: 'Nation2', + color: '#000', + capitalCityId: 102, + chiefGeneralId: 2, + gold: 0, + rice: 0, + power: 0, + level: 1, + typeCode: 'che_def', + meta: {}, }; world.snapshot.generals.push(monarch); @@ -273,7 +454,9 @@ describe('che_등용수락', () => { world.snapshot.nations.push(nation2); // Manually check constraints for denial - const def = (await import('../../../src/actions/turn/general/che_등용수락.js')).commandSpec.createDefinition({} as any); + const def = (await import('../../../src/actions/turn/general/che_등용수락.js')).commandSpec.createDefinition( + {} as any + ); const args = { destNationId: 2, destGeneralId: 2 }; const ctx = createConstraintContext(monarch, 200, args); @@ -283,7 +466,7 @@ describe('che_등용수락', () => { expect(result.kind).toBe('deny'); if (result.kind === 'deny') { const reason = result.constraintName || result.reason; - // notLord constraint failure. + // notLord constraint failure. // In TS presets, notLord(monarch) returns deny. // ConstraintName should be 'notLord' or 'NotLord'. expect(result.constraintName).toMatch(/NotLord/i); @@ -298,27 +481,74 @@ describe('che_등용수락', () => { const world = new InMemoryWorld(bootstrapResult.snapshot); const general: General = { - id: 1, name: 'Gen1', nationId: 1, cityId: 101, troopId: 0, - stats: { leadership: 50, strength: 50, intelligence: 50 }, experience: 0, dedication: 0, - officerLevel: 1, role: null as any, injury: 0, gold: 1000, rice: 1000, crew: 0, crewTypeId: 0, train: 0, atmos: 0, age: 20, npcState: 0, triggerState: { flags: {}, counters: {}, modifiers: {}, meta: {} }, meta: {}, + id: 1, + name: 'Gen1', + nationId: 1, + cityId: 101, + troopId: 0, + stats: { leadership: 50, strength: 50, intelligence: 50 }, + experience: 0, + dedication: 0, + officerLevel: 1, + role: null as any, + injury: 0, + gold: 1000, + rice: 1000, + crew: 0, + crewTypeId: 0, + train: 0, + atmos: 0, + age: 20, + npcState: 0, + triggerState: { flags: {}, counters: {}, modifiers: {}, meta: {} }, + meta: {}, }; const nation1: Nation = { - id: 1, name: 'Nation1', color: '#000', capitalCityId: 101, chiefGeneralId: 1, - gold: 0, rice: 0, power: 0, level: 1, typeCode: 'che_def', meta: {}, + id: 1, + name: 'Nation1', + color: '#000', + capitalCityId: 101, + chiefGeneralId: 1, + gold: 0, + rice: 0, + power: 0, + level: 1, + typeCode: 'che_def', + meta: {}, }; // Recruiter also in same nation? Or different? // Arg destNationId is key. const recruiterGen: General = { - id: 2, name: 'Recruiter', nationId: 1, cityId: 101, troopId: 0, - stats: { leadership: 50, strength: 50, intelligence: 50 }, experience: 0, dedication: 0, - officerLevel: 1, role: null as any, injury: 0, gold: 1000, rice: 1000, crew: 0, crewTypeId: 0, train: 0, atmos: 0, age: 20, npcState: 0, triggerState: { flags: {}, counters: {}, modifiers: {}, meta: {} }, meta: {}, + id: 2, + name: 'Recruiter', + nationId: 1, + cityId: 101, + troopId: 0, + stats: { leadership: 50, strength: 50, intelligence: 50 }, + experience: 0, + dedication: 0, + officerLevel: 1, + role: null as any, + injury: 0, + gold: 1000, + rice: 1000, + crew: 0, + crewTypeId: 0, + train: 0, + atmos: 0, + age: 20, + npcState: 0, + triggerState: { flags: {}, counters: {}, modifiers: {}, meta: {} }, + meta: {}, }; world.snapshot.generals.push(general); world.snapshot.generals.push(recruiterGen); world.snapshot.nations.push(nation1); - const def = (await import('../../../src/actions/turn/general/che_등용수락.js')).commandSpec.createDefinition({} as any); + const def = (await import('../../../src/actions/turn/general/che_등용수락.js')).commandSpec.createDefinition( + {} as any + ); const args = { destNationId: 1, destGeneralId: 2 }; // Target same nation 1 const ctx = createConstraintContext(general, 200, args);