Files
core2026/docs/architecture/legacy-engine-triggers.md

7.7 KiB

Legacy Trigger System (iAction + Trigger Callers)

This document explains how the legacy Trigger system is built on iAction and TriggerCaller, and how it affects both command outcomes and battle flow. Primary references include legacy/hwe/sammo/iAction.php, legacy/hwe/sammo/TriggerCaller.php, legacy/hwe/sammo/General.php, legacy/hwe/process_war.php, and legacy/hwe/sammo/WarUnit.php.

Core Concepts

iAction: modifier hooks + trigger provider

iAction is the unified interface for traits, specials, crew types, items, scenario effects, and hidden buffs. It provides:

  • Modifier hooks (onCalcDomestic, onCalcStat, onCalcOpposeStat, onCalcStrategic, onCalcNationalIncome, getWarPowerMultiplier).
  • Trigger hooks (getPreTurnExecuteTriggerList, getBattleInitSkillTriggerList, getBattlePhaseSkillTriggerList).
  • Ad-hoc hooks (onArbitraryAction) for non-turn side effects.

Action sources are merged into a single list in General::getActionList():

  1. Nation type
  2. Officer level
  3. Domestic special
  4. War special
  5. Personality
  6. Crew type (GameUnitDetail)
  7. Inheritance buff (TriggerInheritBuff)
  8. Scenario effect
  9. Items

The order above is the execution order for modifier hooks, and also the merge order for trigger lists (later actions override duplicates with the same unique trigger ID).

Crew types (GameUnitDetail) are special:

  • They implement iAction.
  • They expose initSkillTrigger and phaseSkillTrigger arrays defined in scenario data (buildWarUnitTriggerClass).
  • They can include an iActionList (crew-type-specific actions) which themselves implement iAction.

ObjectTrigger and TriggerCaller

Triggers are small, prioritized actions:

  • ObjectTrigger defines priorities and a single action() method.
  • TriggerCaller groups triggers by priority, merges lists, and fire()s them in priority order.

Priority constants (lower runs earlier):

  • PRIORITY_BEGIN = 10000
  • PRIORITY_PRE = 20000
  • PRIORITY_BODY = 30000
  • PRIORITY_POST = 40000
  • PRIORITY_FINAL = 50000

TriggerCaller uses a unique ID (priority + class + object id) for dedup. BaseWarUnitTrigger extends this with raiseType to separate item-based triggers from trait-based ones.

Specialized callers enforce type safety:

  • GeneralTriggerCaller accepts BaseGeneralTrigger.
  • WarUnitTriggerCaller accepts BaseWarUnitTrigger.

Modifier Hooks (Command + Stat + War)

onCalcDomestic

Used by general commands to adjust cost, success, failure, and score. Inputs:

  • turnType: command key (징병, 조달, 주민선정, 정착장려, 등).
  • varType: cost, rice, train, atmos, success, fail, score.
  • aux: extra context (ex: armType in che_징병).

Examples:

  • Command/General/che_징병.php uses cost, rice, train, atmos.
  • Command/General/che_주민선정.php uses score, success, fail.
  • GeneralTrigger/che_병력군량소모.php uses 징집인구:score.

onCalcStrategic

Used by nation commands to adjust delays/limits:

  • varType often delay, globalDelay, strategic_cmd_limit.
  • Examples: Command/Nation/che_급습.php, che_백성동원.php, che_수몰.php.

onCalcNationalIncome

Used by Event/Action/ProcessSemiAnnual.php to adjust population growth and income ratios at the nation level. Nation types (e.g., 유가/법가/병가 계열)이 여기에서 보정을 걸어준다.

onCalcStat / onCalcOpposeStat

Used for base stats and battle-derived parameters. Common statName keys:

  • Base stats: leadership, strength, intel (General stat calc).
  • Progression: addDex, experience, dedication.
  • Battle timing: initWarPhase (phase count from WarUnitGeneral).
  • Battle accuracy: dex{armType} (e.g., dex2).
  • Train/atmos: bonusTrain, bonusAtmos.
  • Combat odds: warCriticalRatio, warAvoidRatio.
  • War magic: warMagicTrialProb, warMagicSuccessProb, warMagicSuccessDamage, warMagicFailDamage.
  • Damage range: criticalDamageRange (in WarUnit::criticalDamage()).
  • Supply cost: killRice (war rice consumption).
  • Battle order: cityBattleOrder (opponent modifies city order).

aux carries context such as isAttacker, opposeType, or magic name (반목, 화계, etc.).

Pre-Turn General Triggers

TurnExecutionHelper::preprocessCommand() runs pre-turn triggers:

  1. General::getPreTurnExecuteTriggerList() merges triggers from all actions.
  2. Base triggers are appended:
    • GeneralTrigger/che_부상경감 (priority 10000 / BEGIN)
    • GeneralTrigger/che_병력군량소모 (priority 50000 / FINAL)
  3. TriggerCaller::fire() executes them before the command runs.

General triggers use General::activateSkill() for logging and gating. TurnExecutionHelper::processCommand() clears activated skills after the command completes.

Battle Triggers

Battle triggers are fired in two stages inside process_war.php:

Battle-init triggers

  • Fired once per engagement when a defender is first set (phase == 0).
  • Constructed via General::getBattleInitSkillTriggerList().
  • Used for start-of-battle setup (ex: che_부상무효 from 견고).

Phase triggers

Every phase:

  1. WarUnit::beginPhase() clears activated skills and recomputes war power.
  2. General::getBattlePhaseSkillTriggerList() builds the trigger list.
    • Base triggers (always included):
      • che_필살시도, che_필살발동
      • che_회피시도, che_회피발동
      • che_계략시도, che_계략발동, che_계략실패
  3. Attacker/defender trigger lists are merged and fired.
  4. Damage is calculated using getWarPower() (after trigger multipliers).

Attempt → Execute pattern (PRE/POST)

Most battle skills split into two triggers:

  • Attempt (PRE): check conditions, set flags or env payload.
  • Execute (POST): read env/flags and apply damage or status.

Examples:

  • WarUnitTrigger/che_저격시도.php (PRE) sets 저격발동자, wound ranges, then che_저격발동.php (POST) applies wounds and logs.
  • WarUnitTrigger/che_계략시도.php (PRE) sets magic and success/failure, then che_계략발동.php / che_계략실패.php (POST) applies multipliers.
  • WarUnitTrigger/che_필살시도.phpche_필살발동.php adjusts war power.

Battle env and stop flags

BaseWarUnitTrigger::action() supplies a mutable env:

  • e_attacker / e_defender: per-side state map.
  • stopNextAction: if true, later triggers are skipped.

Triggers can return false from actionWar() to set stopNextAction.

Item-based triggers and consumption

BaseWarUnitTrigger uses a raiseType bitmask to tag item-based triggers:

  • TYPE_ITEM: item-triggered, with 아이템사용 skill gating.
  • TYPE_CONSUMABLE_ITEM: consumes and deletes the item.
  • TYPE_DEDUP_TYPE_BASE: offset for dedup grouping.

processConsumableItem() handles the consumption flow and logging. Item triggers typically pass raiseType so the same skill can coexist with non-item versions (see ActionItem/che_저격_매화수전.php).

Interaction Notes for Porting

  • Preserve General::getActionList() order; modifier hooks stack in sequence.
  • Apply onCalcOpposeStat using the opponent's action list after the general's own onCalcStat adjustments.
  • Keep priority-based execution (PRIORITY_*) and the PRE/POST split.
  • Replicate trigger dedup semantics (getUniqueID + raiseType).
  • Maintain beginPhase() clearing of activated skills; battle triggers assume per-phase activation.
  • Ensure RNG usage stays deterministic (RandUtil everywhere in triggers).
  • legacy/hwe/sammo/iAction.php
  • legacy/hwe/sammo/TriggerCaller.php
  • legacy/hwe/sammo/ObjectTrigger.php
  • legacy/hwe/sammo/General.php
  • legacy/hwe/sammo/WarUnit.php
  • legacy/hwe/process_war.php