- Changed SpecialActionKind to TraitKind and updated its usage across the codebase. - Refactored SpecialActionSpec to TraitSpec and updated all references. - Updated the SpecialActionModule and related interfaces to TraitModule. - Added personality traits with corresponding modules and loaders. - Updated tests to reflect the new Trait structure and naming conventions. - Adjusted imports and exports to align with the new Trait system.
264 lines
8.5 KiB
TypeScript
264 lines
8.5 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;
|
|
};
|
|
};
|
|
},
|
|
kind: TraitKind
|
|
): string | null => {
|
|
if (kind === 'domestic') {
|
|
return context.general.role.specialDomestic;
|
|
}
|
|
if (kind === 'war') {
|
|
return context.general.role.specialWar;
|
|
}
|
|
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 {
|
|
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>[];
|
|
}): TraitModuleRegistry<TriggerState> => {
|
|
const domestic = new Map<string, TraitModule<TriggerState>>();
|
|
const war = new Map<string, TraitModule<TriggerState>>();
|
|
const personality = 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);
|
|
}
|
|
|
|
return { domestic, war, personality };
|
|
};
|
|
|
|
// 특성 레지스트리를 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),
|
|
],
|
|
war: [
|
|
new TraitWarActionRouter<TriggerState>('domestic', registry),
|
|
new TraitWarActionRouter<TriggerState>('war', registry),
|
|
new TraitWarActionRouter<TriggerState>('personality', registry),
|
|
],
|
|
});
|