refactor(logic): replace arbitrary action hooks with typed events

This commit is contained in:
2026-07-30 03:31:31 +00:00
parent 96b41ef4b4
commit 7d3cb50a5b
168 changed files with 1554 additions and 631 deletions
+2 -2
View File
@@ -2,7 +2,7 @@ import type { RandUtil } from '@sammo-ts/common';
import type { City, General, GeneralTriggerState, Nation } from '@sammo-ts/logic/domain/entities.js';
import type { ActionLogger } from '@sammo-ts/logic/logging/actionLogger.js';
import type { WarStatName } from '@sammo-ts/logic/triggers/types.js';
import type { WarStatName } from '@sammo-ts/logic/actionModules/types.js';
import type { WarUnit } from './units.js';
import { WarTriggerCaller } from './triggers.js';
@@ -54,7 +54,7 @@ export class WarActionPipeline<TriggerState extends GeneralTriggerState = Genera
// 전투용 iAction 파이프라인: 스탯/트리거/전투력 보정 흐름을 순서대로 적용한다.
private readonly modules: WarActionModule<TriggerState>[];
constructor(modules: Array<WarActionModule<TriggerState> | null | undefined>) {
constructor(modules: ReadonlyArray<WarActionModule<TriggerState> | null | undefined>) {
this.modules = modules.filter(Boolean) as WarActionModule<TriggerState>[];
}
+46 -1
View File
@@ -1,8 +1,10 @@
import { JosaUtil, LiteHashDRBG, RandUtil } from '@sammo-ts/common';
import type { City, General, GeneralTriggerState, Nation } from '@sammo-ts/logic/domain/entities.js';
import { createGeneralActionEvent } from '@sammo-ts/logic/actionModules/events.js';
import { GeneralActionPipeline } from '@sammo-ts/logic/actionModules/general.js';
import { ActionLogger } from '@sammo-ts/logic/logging/actionLogger.js';
import { LogFormat, type LogEntryDraft } from '@sammo-ts/logic/logging/types.js';
import { LogCategory, LogFormat, LogScope, type LogEntryDraft } from '@sammo-ts/logic/logging/types.js';
import { buildCrewTypeIndex, getTechCost, getTechLevel } from '@sammo-ts/logic/world/unitSet.js';
import type { WarUnitReport } from './types.js';
import type {
@@ -530,6 +532,49 @@ export const resolveWarAftermath = <TriggerState extends GeneralTriggerState = G
)
)
);
// Legacy ConquerCity calls every general action module for every
// defender-nation general stationed in the conquered city, before any
// nation-collapse RNG is consumed. Keep that order and the same RNG
// object instead of opening a second random stream.
const pipeline = new GeneralActionPipeline(input.generalActionModules ?? []);
const cityDefenders = input.generals.filter(
(general) =>
general.nationId !== 0 &&
general.nationId === input.defenderCity.nationId &&
general.cityId === input.defenderCity.id
);
for (const general of cityDefenders) {
pipeline.dispatch(
{
general,
nation: input.defenderNation,
rng,
worldView: {
listGenerals: () => input.generals,
listGeneralsByCity: (cityId) =>
input.generals.filter((candidate) => candidate.cityId === cityId),
listNations: () => input.nations,
},
log: {
push: (text) =>
logs.push({
scope: LogScope.GENERAL,
category: LogCategory.ACTION,
format: LogFormat.MONTH,
generalId: general.id,
nationId: general.nationId,
text,
}),
},
},
createGeneralActionEvent('city.conquered', {
attacker: input.battle.attacker,
})
);
affectedGenerals.add(general);
}
conquest = resolveConquerCity(input, rng);
logs.push(...conquest.logs);
+3 -1
View File
@@ -1,6 +1,7 @@
import type { RandUtil } from '@sammo-ts/common';
import type { City, General, GeneralTriggerState, Nation } from '@sammo-ts/logic/domain/entities.js';
import type { GeneralActionModule } from '@sammo-ts/logic/actionModules/general.js';
import type { ActionLogger } from '@sammo-ts/logic/logging/actionLogger.js';
import type { LogEntryDraft } from '@sammo-ts/logic/logging/types.js';
import type { UnitSetDefinition } from '@sammo-ts/logic/world/types.js';
@@ -39,7 +40,7 @@ export interface WarGeneralInput<TriggerState extends GeneralTriggerState = Gene
city: City;
nation: Nation | null;
logger?: ActionLogger;
modules?: Array<WarActionModule<TriggerState> | null | undefined>;
modules?: ReadonlyArray<WarActionModule<TriggerState> | null | undefined>;
}
export interface WarBattleInput<TriggerState extends GeneralTriggerState = GeneralTriggerState> {
@@ -177,6 +178,7 @@ export interface WarAftermathInput<TriggerState extends GeneralTriggerState = Ge
time: WarTimeContext;
hiddenSeed?: string;
rng?: RandUtil;
generalActionModules?: ReadonlyArray<GeneralActionModule<TriggerState> | null | undefined>;
calcNationTechGain?: (context: WarAftermathTechContext) => number;
}
+1 -1
View File
@@ -10,7 +10,7 @@ import type {
} from '@sammo-ts/logic/domain/entities.js';
import type { ActionLogger } from '@sammo-ts/logic/logging/actionLogger.js';
import { LogFormat } from '@sammo-ts/logic/logging/types.js';
import type { WarStatName } from '@sammo-ts/logic/triggers/types.js';
import type { WarStatName } from '@sammo-ts/logic/actionModules/types.js';
import { getTechAbility, getTechCost } from '@sammo-ts/logic/world/unitSet.js';
import type { WarActionPipeline, WarActionContext } from '../actions.js';
import type { WarEngineConfig } from '../types.js';