import type { RandUtil } from '@sammo-ts/common'; import type { General } from '@sammo-ts/logic/domain/entities.js'; import { TriggerCaller, type Trigger } from '@sammo-ts/logic/triggers/core.js'; import type { WarUnit } from './units.js'; import { removeEquippedItem } from '@sammo-ts/logic/items/inventory.js'; export interface WarTriggerContext { rng: RandUtil; attacker: WarUnit; defender: WarUnit; } export interface WarTriggerEnv { e_attacker: Record; e_defender: Record; stopNextAction?: boolean; } export type WarTrigger = Trigger; export const createWarTriggerEnv = (): WarTriggerEnv => ({ e_attacker: {}, e_defender: {}, }); export class WarTriggerCaller extends TriggerCaller {} export type WarTriggerFactory = (unit: WarUnit) => WarTrigger | WarTriggerCaller | null; export type WarTriggerRegistry = Record; // 전투 트리거 공통 베이스. 전투 유닛 기준으로 동작하며, env는 공격/수비 분리 상태를 유지한다. export abstract class BaseWarUnitTrigger implements WarTrigger { public static readonly TYPE_NONE = 0; public static readonly TYPE_ITEM = 1; public static readonly TYPE_CONSUMABLE_ITEM = 1 | 2; public static readonly TYPE_DEDUP_TYPE_BASE = 1024; protected readonly raiseType: number; protected constructor( protected readonly unit: WarUnit, public readonly priority: number, raiseType = BaseWarUnitTrigger.TYPE_NONE ) { this.raiseType = raiseType; } get uniqueId(): string { return `${this.priority}_${this.constructor.name}_${this.unit.getUnitId()}_${this.raiseType}`; } action(context: WarTriggerContext, env: WarTriggerEnv): WarTriggerEnv { const nextEnv: WarTriggerEnv = { e_attacker: env.e_attacker ?? {}, e_defender: env.e_defender ?? {}, ...(env.stopNextAction ? { stopNextAction: true } : {}), }; if (nextEnv.stopNextAction) { return nextEnv; } const self = this.unit; const isAttacker = self.isAttacker(); const oppose = isAttacker ? context.defender : context.attacker; const selfEnv = isAttacker ? nextEnv.e_attacker : nextEnv.e_defender; const opposeEnv = isAttacker ? nextEnv.e_defender : nextEnv.e_attacker; const shouldContinue = this.actionWar(self, oppose, selfEnv, opposeEnv); nextEnv.e_attacker = isAttacker ? selfEnv : opposeEnv; nextEnv.e_defender = isAttacker ? opposeEnv : selfEnv; if (!shouldContinue) { nextEnv.stopNextAction = true; } return nextEnv; } // 실제 트리거 구현부는 여기서 처리한다. protected abstract actionWar( self: WarUnit, oppose: WarUnit, selfEnv: Record, opposeEnv: Record ): boolean; public processConsumableItem(): boolean { if (!(this.raiseType & BaseWarUnitTrigger.TYPE_ITEM)) { return false; } if (this.unit.hasActivatedSkill('아이템사용')) { return false; } this.unit.activateSkill('아이템사용'); if (this.raiseType !== BaseWarUnitTrigger.TYPE_CONSUMABLE_ITEM) { return false; } if (this.unit.hasActivatedSkill('아이템소모')) { return false; } const unit = this.unit as WarUnit & { getGeneral?: () => General; }; const general = unit.getGeneral?.(); if (!general) { return false; } this.unit.activateSkill('아이템소모'); return removeEquippedItem(general, 'item') !== null; } }