From 91a980fa3dc6e2aea2f20e5638b4a9294e7b17ad Mon Sep 17 00:00:00 2001 From: Ferywir <65760459+Ferywir@users.noreply.github.com> Date: Sun, 9 Aug 2026 19:20:37 +0200 Subject: [PATCH] fix(combat): keep the hero casualty when rams force a battle recalculation [#372] (#373) When rams change the wall level, applyRamDamage() replays the whole battle through calculateBattle(). That helper WRITES to the hero table, so the replay applied the hero damage a second time: applyHeroBattleDamage() only looks up heroes with dead = 0, so the hero who had just died on the first pass was no longer found, the call returned null and casualties_attacker[11] was never set. Consequences, all visible in the screenshots of the issue: the report showed the hero alive and "gained XP" instead of "died", modifyAttack3() never decremented t11, and returnunitsComplete() added the hero back to units.hero - a hero dead in the hero table but still displayed in dorf1 and in the rally point. Defender heroes were also charged their health damage twice. calculateBattle() now takes an optional $previousHeroOutcome: when the caller hands over the first pass result, the hero damage is not applied again and the first verdict (attacker / defender / reinforcement heroes) is carried over. Also: - drop the temporary [TravianZ][EROU] error_log() debug traces; - 37_revive.tpl: only render a revive row for a hero that is actually dead (the revive action itself was already gated on dead = 1). Co-authored-by: Claude Opus 5 (1M context) Co-authored-by: Catalin Novgorodschi --- .../Automation/AutomationBattleResolution.php | 10 ++++++++-- GameEngine/Battle.php | 16 ++++++++++++---- Templates/Build/37_revive.tpl | 7 ++++++- 3 files changed, 26 insertions(+), 7 deletions(-) diff --git a/GameEngine/Automation/AutomationBattleResolution.php b/GameEngine/Automation/AutomationBattleResolution.php index 009d8828..41b12065 100644 --- a/GameEngine/Automation/AutomationBattleResolution.php +++ b/GameEngine/Automation/AutomationBattleResolution.php @@ -1477,7 +1477,8 @@ trait AutomationBattleResolution { * @param int $walllevel Current wall level. * @param int $wallid Wall building field id (40). * @param int $ram_pic Ram unit id (for the report fragment). - * @param array $battlepart Battle result (provides the ram damage factors). + * @param array $battlepart Battle result (provides the ram damage factors, + * and the hero verdict carried over to the recalculation). * @param array $ctx Battle context for the post-damage recalculation * (Attacker, Defender, tribes, populations, AB tech, ...). * @return array { battlepart: array (possibly recalculated), info_ram: string } @@ -1510,7 +1511,12 @@ trait AutomationBattleResolution { //If the wall got damaged/destroyed during the attack //we need to recalculate the whole battle if($newLevel != $walllevel){ - $battlepart = $battle->calculateBattle($ctx['Attacker'], $ctx['Defender'], $newLevel, $ctx['att_tribe'], $ctx['def_tribe'], $ctx['residence'], $ctx['attpop'], $ctx['defpop'], $ctx['type'], $ctx['def_ab'], $ctx['att_ab1'], $ctx['att_ab2'], $ctx['att_ab3'], $ctx['att_ab4'], $ctx['att_ab5'], $ctx['att_ab6'], $ctx['att_ab7'], $ctx['att_ab8'], $ctx['tblevel'], $ctx['stonemason'], $newLevel, 0, 0, 0, $ctx['AttackerID'], $ctx['DefenderID'], $ctx['AttackerWref'], $ctx['DefenderWref'], $ctx['conqureby'], $ctx['enforcementarray']); + // The heroes already took their damage during the first pass and + // it was written to the hero table, so the recalculation must not + // apply it again: it would look up living heroes only, miss the + // one who just died and drop him from the report (issue #372). + // The first verdict is handed over instead. + $battlepart = $battle->calculateBattle($ctx['Attacker'], $ctx['Defender'], $newLevel, $ctx['att_tribe'], $ctx['def_tribe'], $ctx['residence'], $ctx['attpop'], $ctx['defpop'], $ctx['type'], $ctx['def_ab'], $ctx['att_ab1'], $ctx['att_ab2'], $ctx['att_ab3'], $ctx['att_ab4'], $ctx['att_ab5'], $ctx['att_ab6'], $ctx['att_ab7'], $ctx['att_ab8'], $ctx['tblevel'], $ctx['stonemason'], $newLevel, 0, 0, 0, $ctx['AttackerID'], $ctx['DefenderID'], $ctx['AttackerWref'], $ctx['DefenderWref'], $ctx['conqureby'], $ctx['enforcementarray'], $battlepart); } } diff --git a/GameEngine/Battle.php b/GameEngine/Battle.php index e72b87b0..69a29723 100644 --- a/GameEngine/Battle.php +++ b/GameEngine/Battle.php @@ -484,6 +484,13 @@ class Battle { Function to process Calculate Battle (Phase 2: orchestrator — fiecare sectiune a devenit un helper privat, mai jos) + + $previousHeroOutcome: result of an earlier + pass over the SAME battle (rams changed the + wall level, so it is replayed). When set, + the hero damage is not applied a second + time — the first verdict is reused instead + (issue #372). *****************************************/ function calculateBattle( @@ -498,8 +505,9 @@ class Battle { $AttackerID, $DefenderID, $AttackerWref, $DefenderWref, $conqureby, - $defReinforcements = null) { - + $defReinforcements = null, + $previousHeroOutcome = null) { + global $database, $unitsbytype; /****************************************************************** @@ -742,7 +750,7 @@ class Battle { /****************************************************************** * HERO DAMAGE (DEFENDER) ******************************************************************/ - if (!empty($units['Def_unit']['hero']) && !empty($defenderhero['heroid'])) { + if (!$heroDamageAlreadyApplied && !empty($units['Def_unit']['hero']) && !empty($defenderhero['heroid'])) { $dead = $this->applyHeroBattleDamage($defenderhero['heroid'], $result[2]); @@ -754,7 +762,7 @@ class Battle { /****************************************************************** * HERO DAMAGE (DEFENDER + REINFORCEMENTS) ******************************************************************/ - if (!empty($DefendersAll)) { + if (!$heroDamageAlreadyApplied && !empty($DefendersAll)) { $battleHeroesCache = []; $villageOwnerCache = []; diff --git a/Templates/Build/37_revive.tpl b/Templates/Build/37_revive.tpl index efdbf71b..291e127a 100644 --- a/Templates/Build/37_revive.tpl +++ b/Templates/Build/37_revive.tpl @@ -184,8 +184,13 @@ foreach ($heroes as $hero_datarow) {