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
+211
View File
@@ -0,0 +1,211 @@
import type { GeneralTriggerState } from '@sammo-ts/logic/domain/entities.js';
import { type GeneralActionContext, GeneralTriggerCaller } from '@sammo-ts/logic/triggers/general.js';
import type {
GeneralStatName,
TriggerDomesticActionType,
TriggerDomesticVarType,
TriggerNationalIncomeType,
TriggerStrategicActionType,
TriggerStrategicVarType,
} from './types.js';
import {
dispatchGeneralActionEventHandlers,
type GeneralActionEvent,
type GeneralActionEventContext,
type GeneralActionEventHandlers,
type GeneralActionEventType,
} from './events.js';
interface GeneralActionModuleBase<TriggerState extends GeneralTriggerState = GeneralTriggerState> {
getName?: (() => string) | undefined;
getInfo?: (() => string) | undefined;
getPreTurnExecuteTriggerList?:
((context: GeneralActionContext<TriggerState>) => GeneralTriggerCaller<TriggerState> | null) | undefined;
onCalcDomestic?:
| ((
context: GeneralActionContext<TriggerState>,
turnType: TriggerDomesticActionType,
varType: TriggerDomesticVarType,
value: number,
aux?: unknown
) => number)
| undefined;
onCalcStat?:
| ((
context: GeneralActionContext<TriggerState>,
statName: GeneralStatName,
value: number,
aux?: unknown
) => number)
| undefined;
onCalcOpposeStat?:
| ((
context: GeneralActionContext<TriggerState>,
statName: GeneralStatName,
value: number,
aux?: unknown
) => number)
| undefined;
onCalcStrategic?:
| ((
context: GeneralActionContext<TriggerState>,
turnType: TriggerStrategicActionType,
varType: TriggerStrategicVarType,
value: number
) => number)
| undefined;
onCalcNationalIncome?:
| ((context: GeneralActionContext<TriggerState>, type: TriggerNationalIncomeType, amount: number) => number)
| undefined;
}
interface GeneralActionLeafModule<TriggerState extends GeneralTriggerState> {
eventHandlers?: GeneralActionEventHandlers<TriggerState> | undefined;
handleEvent?: never;
}
interface GeneralActionCompositeModule<TriggerState extends GeneralTriggerState> {
eventHandlers?: never;
handleEvent<K extends GeneralActionEventType>(
context: GeneralActionEventContext<K, TriggerState>,
event: GeneralActionEvent<K, TriggerState>
): GeneralActionEvent<K, TriggerState>;
}
/**
* leaf handler와 합성 router는 상호 배타적입니다. 한 module이 같은 이벤트를
* 두 경로로 중복 처리할 수 없습니다.
*/
export type GeneralActionModule<TriggerState extends GeneralTriggerState = GeneralTriggerState> =
GeneralActionModuleBase<TriggerState> &
(GeneralActionLeafModule<TriggerState> | GeneralActionCompositeModule<TriggerState>);
export const dispatchGeneralActionModuleEvent = <
TriggerState extends GeneralTriggerState,
K extends GeneralActionEventType,
>(
module: GeneralActionModule<TriggerState>,
context: GeneralActionEventContext<K, TriggerState>,
event: GeneralActionEvent<K, TriggerState>
): GeneralActionEvent<K, TriggerState> =>
module.handleEvent
? module.handleEvent(context, event)
: dispatchGeneralActionEventHandlers(module.eventHandlers, context, event);
export class GeneralActionPipeline<TriggerState extends GeneralTriggerState = GeneralTriggerState> {
private readonly modules: ReadonlyArray<GeneralActionModule<TriggerState>>;
constructor(modules: ReadonlyArray<GeneralActionModule<TriggerState> | null | undefined>) {
this.modules = modules.filter(Boolean) as ReadonlyArray<GeneralActionModule<TriggerState>>;
}
getPreTurnExecuteTriggerList(context: GeneralActionContext<TriggerState>): GeneralTriggerCaller<TriggerState> {
const triggerCaller = new GeneralTriggerCaller<TriggerState>();
for (const module of this.modules) {
const triggers = module.getPreTurnExecuteTriggerList?.(context);
if (triggers) {
triggerCaller.merge(triggers);
}
}
return triggerCaller;
}
onCalcDomestic(
context: GeneralActionContext<TriggerState>,
turnType: TriggerDomesticActionType,
varType: TriggerDomesticVarType,
value: number,
aux?: unknown
): number {
let current = value;
for (const module of this.modules) {
if (!module.onCalcDomestic) {
continue;
}
current = module.onCalcDomestic(context, turnType, varType, current, aux);
}
return current;
}
onCalcStat(
context: GeneralActionContext<TriggerState>,
statName: GeneralStatName,
value: number,
aux?: unknown
): number {
let current = value;
for (const module of this.modules) {
if (!module.onCalcStat) {
continue;
}
current = module.onCalcStat(context, statName, current, aux);
}
return current;
}
onCalcOpposeStat(
context: GeneralActionContext<TriggerState>,
statName: GeneralStatName,
value: number,
aux?: unknown
): number {
let current = value;
for (const module of this.modules) {
if (!module.onCalcOpposeStat) {
continue;
}
current = module.onCalcOpposeStat(context, statName, current, aux);
}
return current;
}
onCalcStrategic(
context: GeneralActionContext<TriggerState>,
turnType: TriggerStrategicActionType,
varType: TriggerStrategicVarType,
value: number
): number {
let current = value;
for (const module of this.modules) {
if (!module.onCalcStrategic) {
continue;
}
current = module.onCalcStrategic(context, turnType, varType, current);
}
return current;
}
onCalcNationalIncome(
context: GeneralActionContext<TriggerState>,
type: TriggerNationalIncomeType,
amount: number
): number {
let current = amount;
for (const module of this.modules) {
if (!module.onCalcNationalIncome) {
continue;
}
current = module.onCalcNationalIncome(context, type, current);
}
return current;
}
dispatch<K extends GeneralActionEventType>(
context: GeneralActionEventContext<K, TriggerState>,
event: GeneralActionEvent<K, TriggerState>
): GeneralActionEvent<K, TriggerState> {
let current = event;
for (const module of this.modules) {
current = dispatchGeneralActionModuleEvent(module, context, current);
}
return current;
}
}