# 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()` ## Conflict Tracking (`city.conflict`) City conflict tracks which nations contributed to siege damage, used to resolve post-war ownership when multiple attackers participate. Source: `WarUnitCity::addConflict()` in `legacy/hwe/sammo/WarUnitCity.php`. - `city.conflict` is a JSON map `{ nationID: deadContribution }`. - Contribution amount is based on city `dead` (minimum 1). - First/last hit bonus: if no conflict exists yet or city HP is 0, `dead` is multiplied by 1.05 ("선타, 막타 보너스"). - Contributions are sorted descending via `arsort()` after updates. - `addConflict()` returns `true` when a new nation enters the conflict, which triggers the global "분쟁" log in `processWar()`. - `getConquerNation()` returns the first key of the sorted map to select the final owner. - `DeleteConflict($nation)` removes a nation from all city conflicts, used on nation deletion and on the `che_방랑` flow. ## City Conquest Resolution (`ConquerCity`) `ConquerCity()` finalizes city ownership, handles nation collapse, and applies post-siege side effects. It is deterministic with the conquest RNG seed noted below. ### Common Flow - Logs conquest to attacker (general/nation/global) and defender nation history. - Runs `EventTarget::OCCUPY_CITY` handlers via `TurnExecutionHelper::runEventHandler()`. - Calls `onArbitraryAction(..., 'ConquerCity')` for each defender general in the city, then persists them. ### Nation Collapse Path Triggered when the defender nation owns exactly one city (the captured city): - Calls `deleteNation()` using the defender lord (officer level 12). - All defender generals lose 20–50% of gold and rice, -10% experience, and -50% dedication, with action logs. Loss amounts are aggregated. - Optionally issues scout messages to fleeing generals (when `join_mode` allows). - NPC defenders (NPC type 2–8 except 5) can auto-queue `che_임관` to the attacker nation with a random delay (0–12 turns), gated by `GameConst::$joinRuinedNPCProp`. - Attacker reward: - Half of defender nation gold/rice above base (`GameConst::$basegold`, `GameConst::$baserice`) plus half of the aggregated general losses. - Credited to attacker nation and logged to all chiefs (officer level >= 5). - Runs `EventTarget::DESTROY_NATION` handlers. ### Nation Survives Path If the defender nation still has other cities: - Demotes city officers (태수/군사/종사) to general: - `officer_level = 1`, `officer_city = 0`. - If the city was the capital: - Picks a new capital via `findNextCapital()` (closest distance, highest pop). - Logs an emergency relocation message to global and all nation generals. - Sets `nation.capital` to new city and halves nation gold/rice. - Marks new capital as supply city; moves chiefs to it. - Applies 20% morale loss to all generals (`atmos *= 0.8`). - Refreshes cached nation static info. ### Final Ownership + City Reset `getConquerNation()` inspects `city.conflict` to decide final owner. If the attacker loses arbitration, the city is transferred to the conflict winner and logs are emitted for both nations. City stats are reset after ownership is settled: - `supply = 1`, `term = 0`, `conflict = {}`, `nation = conquerNation`, `officer_set = 0`. - `agri/comm/secu` multiplied by 0.7. - `def/wall` reset: - If `level > 3`: both set to `GameConst::$defaultCityWall`. - Else: set to `def_max/2`, `wall_max/2`. - Frontline status recalculated for all nearby nations (`SetNationFront()`). ### Deterministic RNG Conquest RNG seed: `hiddenSeed + 'ConquerCity' + year + month + attackerNationID + attackerID + cityID`. Used for: - Defender general loss ratios (20–50%). - Scout message chance and NPC auto-join chance. - Randomized join turn delay (0–12). ## `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 - No automatic decay for `city.conflict` is visible; confirm if any scheduled cleanup exists outside explicit reset paths.