refactor: improve war module type safety and reserved turns test data handling

This commit is contained in:
2026-01-05 17:34:36 +00:00
parent 2e45a2d194
commit 44df19b079
10 changed files with 137 additions and 111 deletions
+49 -41
View File
@@ -12,54 +12,62 @@ import type {
} from './types.js';
export interface GeneralActionModule<TriggerState extends GeneralTriggerState = GeneralTriggerState> {
getName?(): string;
getInfo?(): string;
getName?: (() => string) | undefined;
getInfo?: (() => string) | undefined;
getPreTurnExecuteTriggerList?(
context: GeneralActionContext<TriggerState>
): GeneralTriggerCaller<TriggerState> | null;
getPreTurnExecuteTriggerList?:
| ((context: GeneralActionContext<TriggerState>) => GeneralTriggerCaller<TriggerState> | null)
| undefined;
onCalcDomestic?(
context: GeneralActionContext<TriggerState>,
turnType: TriggerDomesticActionType,
varType: TriggerDomesticVarType,
value: number,
aux?: unknown
): number;
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;
onCalcStat?:
| ((
context: GeneralActionContext<TriggerState>,
statName: GeneralStatName,
value: number,
aux?: unknown
) => number)
| undefined;
onCalcOpposeStat?(
context: GeneralActionContext<TriggerState>,
statName: GeneralStatName,
value: number,
aux?: unknown
): number;
onCalcOpposeStat?:
| ((
context: GeneralActionContext<TriggerState>,
statName: GeneralStatName,
value: number,
aux?: unknown
) => number)
| undefined;
onCalcStrategic?(
context: GeneralActionContext<TriggerState>,
turnType: TriggerStrategicActionType,
varType: TriggerStrategicVarType,
value: number
): number;
onCalcStrategic?:
| ((
context: GeneralActionContext<TriggerState>,
turnType: TriggerStrategicActionType,
varType: TriggerStrategicVarType,
value: number
) => number)
| undefined;
onCalcNationalIncome?(
context: GeneralActionContext<TriggerState>,
type: TriggerNationalIncomeType,
amount: number
): number;
onCalcNationalIncome?:
| ((context: GeneralActionContext<TriggerState>, type: TriggerNationalIncomeType, amount: number) => number)
| undefined;
onArbitraryAction?(
context: GeneralActionContext<TriggerState>,
actionType: TriggerActionType,
phase?: TriggerActionPhase | null,
aux?: Record<string, unknown> | null
): Record<string, unknown> | null;
onArbitraryAction?:
| ((
context: GeneralActionContext<TriggerState>,
actionType: TriggerActionType,
phase?: TriggerActionPhase | null,
aux?: Record<string, unknown> | null
) => Record<string, unknown> | null)
| undefined;
}
export class GeneralActionPipeline<TriggerState extends GeneralTriggerState = GeneralTriggerState> {
@@ -14,15 +14,16 @@ export const parseWarDexAux = (aux: unknown): WarDexAux => {
const opposeRaw = aux.opposeType;
if (!isRecord(opposeRaw)) {
return { isAttacker };
return isAttacker === undefined ? {} : { isAttacker };
}
const armType = opposeRaw.armType;
if (typeof armType !== 'number') {
return { isAttacker };
return isAttacker === undefined ? {} : { isAttacker };
}
return { isAttacker, opposeType: { armType } };
const opposeType = { armType };
return isAttacker === undefined ? { opposeType } : { isAttacker, opposeType };
};
export const getAuxArmType = (aux: unknown): number | undefined => {
@@ -44,7 +44,7 @@ export const traitModule: TraitModule = {
kind: 'war',
getName: () => '견고',
getInfo: () => '[전투] 상대 필살 확률 -20%p, 상대 계략 시도시 성공 확률 -10%p, 부상 없음, 아군 피해 -10%',
onCalcOpposeStat: (_context, statName, value, _aux) => {
onCalcOpposeStat: ((_context, statName, value, _aux) => {
if (statName === 'warMagicSuccessProb' && typeof value === 'number') {
return value - 0.1;
}
@@ -52,7 +52,7 @@ export const traitModule: TraitModule = {
return value - 0.2;
}
return value;
},
}) as TraitModule['onCalcOpposeStat'],
getBattleInitTriggerList: (_context) => {
if (!_context.unit) return null;
return new WarTriggerCaller(new che_부상무효(_context.unit));
@@ -4,6 +4,8 @@ import type { WarActionContext } from '@sammo-ts/logic/war/actions.js';
import type { TraitModule } from '@sammo-ts/logic/triggers/special/types.js';
import { getMetaNumber } from '@sammo-ts/logic/war/utils.js';
import { WarUnit } from '@sammo-ts/logic/war/units.js';
type WarUnitWithGeneral = WarUnit & { getGeneral: () => { meta: Record<string, unknown> } };
const hasGeneral = (unit: WarUnit): unit is WarUnitWithGeneral =>
@@ -44,7 +46,11 @@ export const traitModule: TraitModule = {
// Note: unit.getGeneral() is only available for WarUnitGeneral.
// In a real scenario, we should check if unit is WarUnitGeneral.
if (hasGeneral(unit)) {
const killnum = getMetaNumber(unit.getGeneral().meta, 'rank_killnum', 0);
const killnum = getMetaNumber(
unit.getGeneral().meta as Record<string, import('@sammo-ts/logic/domain/entities.js').TriggerValue>,
'rank_killnum',
0
);
const logVal = Math.log2(Math.max(1, killnum / 5));
attackMultiplier += logVal / 20;
defenceMultiplier -= logVal / 50;
@@ -99,12 +99,12 @@ export const traitModule: TraitModule = {
getName: () => '반계',
getInfo: () =>
'[전투] 상대의 계략 성공 확률 -10%p, 상대의 계략을 40% 확률로 되돌림, 반목 성공시 대미지 추가(+60% → +150%)',
onCalcOpposeStat: (_context, statName, value, _aux) => {
onCalcOpposeStat: ((_context, statName, value, _aux) => {
if (statName === 'warMagicSuccessProb' && typeof value === 'number') {
return value - 0.1;
}
return value;
},
}) as TraitModule['onCalcOpposeStat'],
getBattlePhaseTriggerList: (_context) => {
if (!_context.unit) return null;
return new WarTriggerCaller(new che_반계시도(_context.unit), new che_반계발동(_context.unit));