Files
core2026/packages/logic/src/triggers/special/registry.ts
T

250 lines
8.9 KiB
TypeScript

import type { GeneralTriggerState } from '@sammo-ts/logic/domain/entities.js';
import type { GeneralActionContext } from '@sammo-ts/logic/triggers/general.js';
import type { GeneralActionModule } from '@sammo-ts/logic/triggers/general-action.js';
import type {
GeneralStatName,
TriggerActionPhase,
TriggerActionType,
TriggerDomesticActionType,
TriggerDomesticVarType,
TriggerNationalIncomeType,
TriggerStrategicActionType,
TriggerStrategicVarType,
WarStatName,
} from '@sammo-ts/logic/triggers/types.js';
import type { WarActionContext, WarActionModule } from '@sammo-ts/logic/war/actions.js';
import type { WarUnit } from '@sammo-ts/logic/war/units.js';
import type { WarTriggerCaller } from '@sammo-ts/logic/war/triggers.js';
import type { TraitKind, TraitModule, TraitModuleRegistry } from './types.js';
const resolveTraitKey = (
context: {
general: {
role: {
personality: string | null;
specialDomestic: string | null;
specialWar: string | null;
};
};
nation?: { typeCode: string } | null;
},
kind: TraitKind
): string | null => {
if (kind === 'domestic') {
return context.general.role.specialDomestic;
}
if (kind === 'war') {
return context.general.role.specialWar;
}
if (kind === 'nation') {
return context.nation?.typeCode ?? null;
}
return context.general.role.personality;
};
const resolveModule = <TriggerState extends GeneralTriggerState>(
registry: TraitModuleRegistry<TriggerState>,
kind: TraitKind,
key: string | null
): TraitModule<TriggerState> | null => {
if (!key) {
return null;
}
let bucket: Map<string, TraitModule<TriggerState>>;
if (kind === 'domestic') {
bucket = registry.domestic;
} else if (kind === 'war') {
bucket = registry.war;
} else if (kind === 'nation') {
bucket = registry.nation;
} else {
bucket = registry.personality;
}
return bucket.get(key) ?? null;
};
// General 파이프라인에서 특성(특기/성격) 모듈을 선택해 위임하는 라우터.
export class TraitGeneralActionRouter<
TriggerState extends GeneralTriggerState = GeneralTriggerState,
> implements GeneralActionModule<TriggerState> {
constructor(
private readonly kind: TraitKind,
private readonly registry: TraitModuleRegistry<TriggerState>
) {}
private getModule(context: GeneralActionContext<TriggerState>): TraitModule<TriggerState> | null {
const key = resolveTraitKey(context, this.kind);
return resolveModule(this.registry, this.kind, key);
}
getPreTurnExecuteTriggerList(context: GeneralActionContext<TriggerState>) {
const module = this.getModule(context);
return module?.getPreTurnExecuteTriggerList?.(context) ?? null;
}
onCalcDomestic(
context: GeneralActionContext<TriggerState>,
turnType: TriggerDomesticActionType,
varType: TriggerDomesticVarType,
value: number,
aux?: unknown
): number {
const module = this.getModule(context);
return module?.onCalcDomestic?.(context, turnType, varType, value, aux) ?? value;
}
onCalcStat(
context: GeneralActionContext<TriggerState>,
statName: GeneralStatName,
value: number,
aux?: unknown
): number {
const module = this.getModule(context);
return module?.onCalcStat?.(context, statName, value, aux) ?? value;
}
onCalcOpposeStat(
context: GeneralActionContext<TriggerState>,
statName: GeneralStatName,
value: number,
aux?: unknown
): number {
const module = this.getModule(context);
return module?.onCalcOpposeStat?.(context, statName, value, aux) ?? value;
}
onCalcStrategic(
context: GeneralActionContext<TriggerState>,
turnType: TriggerStrategicActionType,
varType: TriggerStrategicVarType,
value: number
): number {
const module = this.getModule(context);
return module?.onCalcStrategic?.(context, turnType, varType, value) ?? value;
}
onCalcNationalIncome(
context: GeneralActionContext<TriggerState>,
type: TriggerNationalIncomeType,
amount: number
): number {
const module = this.getModule(context);
return module?.onCalcNationalIncome?.(context, type, amount) ?? amount;
}
onArbitraryAction(
context: GeneralActionContext<TriggerState>,
actionType: TriggerActionType,
phase?: TriggerActionPhase | null,
aux?: Record<string, unknown> | null
): Record<string, unknown> | null {
const module = this.getModule(context);
const result = module?.onArbitraryAction?.(context, actionType, phase, aux);
return result === undefined ? (aux ?? null) : result;
}
}
// 전투 파이프라인에서 특성(특기/성격) 모듈을 선택해 위임하는 라우터.
export class TraitWarActionRouter<
TriggerState extends GeneralTriggerState = GeneralTriggerState,
> implements WarActionModule<TriggerState> {
constructor(
private readonly kind: TraitKind,
private readonly registry: TraitModuleRegistry<TriggerState>
) {}
private getModule(context: WarActionContext<TriggerState>): TraitModule<TriggerState> | null {
const key = resolveTraitKey(context, this.kind);
return resolveModule(this.registry, this.kind, key);
}
getBattleInitTriggerList(context: WarActionContext<TriggerState>): WarTriggerCaller | null {
const module = this.getModule(context);
return module?.getBattleInitTriggerList?.(context) ?? null;
}
getBattlePhaseTriggerList(context: WarActionContext<TriggerState>): WarTriggerCaller | null {
const module = this.getModule(context);
return module?.getBattlePhaseTriggerList?.(context) ?? null;
}
onCalcStat(
context: WarActionContext<TriggerState>,
statName: WarStatName,
value: number | [number, number],
aux?: unknown
): number | [number, number] {
const module = this.getModule(context);
return module?.onCalcStat?.(context, statName, value, aux) ?? value;
}
onCalcOpposeStat(
context: WarActionContext<TriggerState>,
statName: WarStatName,
value: number | [number, number],
aux?: unknown
): number | [number, number] {
const module = this.getModule(context);
return module?.onCalcOpposeStat?.(context, statName, value, aux) ?? value;
}
getWarPowerMultiplier(
context: WarActionContext<TriggerState>,
unit: WarUnit<TriggerState>,
oppose: WarUnit<TriggerState>
): [number, number] {
const module = this.getModule(context);
return module?.getWarPowerMultiplier?.(context, unit, oppose) ?? [1, 1];
}
}
export interface TraitModuleSet<TriggerState extends GeneralTriggerState = GeneralTriggerState> {
general: GeneralActionModule<TriggerState>[];
war: WarActionModule<TriggerState>[];
}
export const createTraitModuleRegistry = <TriggerState extends GeneralTriggerState = GeneralTriggerState>(options: {
domestic?: TraitModule<TriggerState>[];
war?: TraitModule<TriggerState>[];
personality?: TraitModule<TriggerState>[];
nation?: TraitModule<TriggerState>[];
}): TraitModuleRegistry<TriggerState> => {
const domestic = new Map<string, TraitModule<TriggerState>>();
const war = new Map<string, TraitModule<TriggerState>>();
const personality = new Map<string, TraitModule<TriggerState>>();
const nation = new Map<string, TraitModule<TriggerState>>();
for (const module of options.domestic ?? []) {
domestic.set(module.key, module);
}
for (const module of options.war ?? []) {
war.set(module.key, module);
}
for (const module of options.personality ?? []) {
personality.set(module.key, module);
}
for (const module of options.nation ?? []) {
nation.set(module.key, module);
}
return { domestic, war, personality, nation };
};
// 특성 레지스트리를 General/전투 파이프라인용 모듈 목록으로 변환한다.
export const createTraitModules = <TriggerState extends GeneralTriggerState = GeneralTriggerState>(
registry: TraitModuleRegistry<TriggerState>
): TraitModuleSet<TriggerState> => ({
general: [
new TraitGeneralActionRouter<TriggerState>('domestic', registry),
new TraitGeneralActionRouter<TriggerState>('war', registry),
new TraitGeneralActionRouter<TriggerState>('personality', registry),
new TraitGeneralActionRouter<TriggerState>('nation', registry),
],
war: [
new TraitWarActionRouter<TriggerState>('domestic', registry),
new TraitWarActionRouter<TriggerState>('war', registry),
new TraitWarActionRouter<TriggerState>('personality', registry),
new TraitWarActionRouter<TriggerState>('nation', registry),
],
});