# Legacy General Model and Action Stack

This document captures how `General` aggregates modifiers, computes stats, and
exposes trigger hooks. The main reference is `legacy/hwe/sammo/General.php`.

## Construction and Core State

- `General` builds from raw DB rows (general, city, nation, rank, access log).
- `last_turn` is parsed into `LastTurn`, and a separate `resultTurn` is
  maintained for updates.
- `penalty` JSON is decoded into `PenaltyKey -> value` map.
- `aux` JSON is lazily decoded and only written when aux values change.
- Rank data is tracked with:
    - `rankVarRead` (current read view)
    - `rankVarIncrease` (increment queue)
    - `rankVarSet` (explicit set queue)

## Action Objects (`iAction`)

`General` composes action sources into a single list:

1. Nation type (`buildNationTypeClass`)
2. Officer level (`TriggerOfficerLevel`)
3. Domestic special (`buildGeneralSpecialDomesticClass`)
4. War special (`buildGeneralSpecialWarClass`)
5. Personality (`buildPersonalityClass`)
6. Crew type (`GameUnitConst::byID`)
7. Inheritance buff (`TriggerInheritBuff`)
8. Scenario effect (`buildScenarioEffectClass`)
9. Items: horse / weapon / book / item (`buildItemClass`)

`getActionList()` returns this merged list and is the core entry point for
modifiers and triggers.

## Stat Computation

`getStatValue()` applies layered modifiers:

- Base stat from raw value
- Injury multiplier
- Cross-stat adjust (strength/intel + 1/4 of the other stat)
- Clamp to `GameConst::$maxLevel`
- `onCalcStat()` from every action object
- Cached by `(statName, injury, action, adjust)` tuple

Convenience wrappers:

- `getLeadership()`, `getStrength()`, `getIntel()`

## Turn Reservation and LastTurn

- `getReservedTurn(turnIdx)` reads from `general_turn`.
- When no entry exists, `buildGeneralCommandClass(null)` creates `휴식`.
- `LastTurn` holds `{ command, arg, term, seq }` and is used by
  `BaseCommand::addTermStack()` to advance multi-turn commands.
- `applyDB()` persists updated fields and writes `last_turn` if it changed.

## Aux and Penalty Behavior

- `aux` is loaded on demand (`unpackAux()`), and `setAuxVar()` writes through to
  `general.aux` only when values changed.
- Setting an aux value to `null` deletes the key from JSON.
- Command constraints that require aux values trigger `General::unpackAux()`
  before evaluation.
- `penalty` is used in both `General` logic (AI and permission checks) and
  API flows (messages, nation tools); runtime expiration is not automatic.

## Trigger and Modifier Hooks

`General` delegates calculations and trigger lists through action objects:

- Economy/strategy: `onCalcDomestic`, `onCalcStrategic`,
  `onCalcNationalIncome`
- Combat modifiers: `onCalcStat`, `onCalcOpposeStat`, `getWarPowerMultiplier`
- Arbitrary actions: `onArbitraryAction` (used in city conquest)

Turn-prep triggers:

- `getPreTurnExecuteTriggerList()` merges trigger lists from every action
  object. `TurnExecutionHelper::preprocessCommand()` adds
  `che_부상경감` + `che_병력군량소모` on top.

Battle triggers:

- `getBattleInitSkillTriggerList()` merges per-action battle-init triggers.
- `getBattlePhaseSkillTriggerList()` includes base phase triggers:
    - `che_필살시도`, `che_필살발동`
    - `che_회피시도`, `che_회피발동`
    - `che_계략시도`, `che_계략발동`, `che_계략실패`
    - plus any action-specific additions.

## Rank and Access Log Updates

- `increaseRankVar()` / `setRankVar()` queue updates until `applyDB()`.
- `applyDB()` updates `general` and `rank_data`, then flushes logs.
- `checkStatChange()` uses `*_exp` thresholds to increase/decrease stats and
  logs level changes.

## Open Questions / Follow-ups

- `PenaltyKey` effects and how penalties are applied are outside this file.
- `GeneralBase` and `LazyVarAndAuxUpdater` may contain additional state
  conventions for new code to mirror.
