# 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_필살시도.php` → `che_필살발동.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).

## Related Files

- `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`
