Files
core2026/docs/architecture/legacy-engine-war.md
T

110 lines
3.8 KiB
Markdown

# Legacy Battle and War Resolution
This document summarizes the legacy battle pipeline centered on
`legacy/hwe/process_war.php`, `legacy/hwe/sammo/WarUnit.php`,
`legacy/hwe/sammo/WarUnitGeneral.php`, and `legacy/hwe/sammo/WarUnitCity.php`.
## Entry Point: `processWar`
Inputs:
- `warSeed` (string): battle RNG seed
- `attackerGeneral`: `General`
- `rawAttackerNation`: nation row (snapshot)
- `rawDefenderCity`: city row (snapshot)
Setup steps:
1. Initialize RNG with `warSeed` (`LiteHashDRBG`).
2. Load defender nation row (or default neutral nation if `nation = 0`).
3. Build `WarUnitGeneral` (attacker) and `WarUnitCity` (defender city).
4. Collect defender generals in the city, filter with `extractBattleOrder()`.
5. Optionally append city as defender if city order > 0.
6. Sort defenders by battle order, iterate via `getNextDefender()`.
7. Run `processWar_NG()`; update DB and nation/city stats.
## Battle Order (`extractBattleOrder`)
For defender generals:
- Must have crew > 0
- Must have rice > crew/100
- `train` and `atmos` must meet `defence_train`
- Battle order uses:
- `totalStat = (realStat + fullStat) / 2`
- `totalCrew = crew / 1_000_000 * (train * atmos) ^ 1.5`
- `totalStat + totalCrew / 100`
For cities:
- Uses attacker `onCalcOpposeStat('cityBattleOrder', -1)`.
## Battle Loop (`processWar_NG`)
1. Log start, include seed in battle logs.
2. If no defenders remain, set defender = city and switch to siege.
3. **Initial engagement**
- `setOppose()` for attacker/defender
- `addTrain(1)` for both
- Fire battle-init triggers
4. **Per phase**
- `beginPhase()` computes war power
- Fire battle-phase triggers
- `calcDamage()` on both sides
- Clamp damage if it exceeds HP ratios
- Apply damage, increase killed/dead counters
- Log phase results
5. **Continuation checks**
- `continueWar()` fails on no rice or HP <= 0
- On retreat/defeat: log, apply win/lose, try wound
- If defender removed, move to next defender (or city siege)
6. **Finish**
- `logBattleResult()` for last phase if needed
- `finishBattle()` for attacker/defender
- City conflict tracking and history logs
## Post-Battle Updates (`processWar`)
After `processWar_NG()`:
- Apply attacker DB updates
- Update nation rice (supply and siege rules)
- Distribute city `dead` counts (40% attacker city, 60% defender city)
- Increase nation tech based on killed/dead and nation size adjustments
- Update `diplomacy.dead` for both sides
- If city conquered: call `ConquerCity()`
## `WarUnitGeneral` Highlights
- Train/atmos bonuses depend on city level and attacker/defender role.
- War power:
- Base attack/defence from crew type + tech
- Adjusted by train/atmos, dex (`getDex()`), crew type coefficients
- Experience level scales war power and counter-scales opponent
- `General::getWarPowerMultiplier()` applies special multipliers
- Rice consumption on kills: proportional to damage, tech cost, unit rice
- Wound chance: 5% unless `부상무효` / `퇴각부상무효` triggered
- `finishBattle()` updates rank stats, rounds values, and checks stat changes
## `WarUnitCity` Highlights
- Uses `DummyGeneral` with `CREWTYPE_CASTLE`
- HP = `def * 10`
- Computed attack/defence = `(def + wall * 9) / 500 + 200`
- City train/atmos scales with elapsed years since `startYear`
- Siege state:
- Non-siege battle ends after one exchange
- Siege continues until HP <= 0
- `heavyDecreaseWealth()` halves `agri/comm/secu` on supply-based rout
- `addConflict()` records contribution in `city.conflict` JSON
## Deterministic RNG
- Main battle uses `warSeed` directly.
- City conquest uses `hiddenSeed + 'ConquerCity' + year + month + nationID + generalID + cityID`.
## Open Questions / Follow-ups
- Detailed conquest outcomes (nation collapse, officer handling) extend beyond
the summary here; see `ConquerCity()` in `legacy/hwe/process_war.php`.