fix(combat): stop the dead hero being removed twice from the troop count [#379] (#380)

This commit is contained in:
Ferywir
2026-08-25 09:13:24 +02:00
committed by GitHub
parent dda489b26f
commit 22477a5571
2 changed files with 60 additions and 9 deletions
@@ -1231,9 +1231,24 @@ trait AutomationBattleResolution {
if ($unitlist) {
$owndead['hero'] = (isset($battlepart['deadherodef']) ? $battlepart['deadherodef'] : '');
$unitModifications_units[] = 'hero';
$unitModifications_amounts[] = $owndead['hero'];
$unitModifications_modes[] = 0;
/**
* BUG FIXED (issue #379): units.hero ended up at -1.
*
* The value is still reported (it feeds the battle report and the
* points), but it must NOT be subtracted from units.hero here any
* more. Battle::applyHeroBattleDamage() already clears the column
* itself ("UPDATE units u JOIN vdata v SET u.hero = 0 WHERE
* v.owner = <hero owner>") the moment the hero dies, and that runs
* inside calculateBattle(), i.e. BEFORE this method.
*
* So the column went 1 -> 0 there and 0 - 1 -> -1 here: the
* village kept showing "-1 Hero" in the troop list while the
* Hero's Mansion correctly offered a revive.
*
* One writer only: the death is owned by applyHeroBattleDamage(),
* which is also the only place that knows about the hero's other
* possible locations (reinforcements, another village).
*/
}
// modify units in DB
@@ -1330,10 +1345,21 @@ trait AutomationBattleResolution {
}
if ($enforce['hero'] > 0) {
$enforceModificationsById[$enforce['id']]['units'][] = 'hero';
$enforceModificationsById[$enforce['id']]['amounts'][] = $battlepart['deadheroref'][$enforce['id']];
$enforceModificationsById[$enforce['id']]['modes'][] = 0;
/**
* BUG FIXED (issue #379), same double subtraction as in
* applyOwnDefenceCasualties(): a reinforcement hero who died
* was already removed by Battle::applyHeroBattleDamage()
* ("UPDATE enforcement e JOIN vdata v SET e.hero = 0"), so
* subtracting him again here drove enforcement.hero to -1.
*
* $enforce still reads 1 because getEnforceVillage() answers
* from the request cache filled before the battle, so the
* stale row hid the problem instead of preventing it.
*
* The dead count below is kept: it is what the reinforcement
* report and $wrong (which decides whether the wiped-out
* reinforcement row is deleted) are built from.
*/
$dead['hero'] = $battlepart['deadheroref'][$enforce['id']];
$alldead['hero'] += $dead['hero'];
$wrong = $dead['hero'] != $enforce['hero'];
+27 -2
View File
@@ -614,7 +614,25 @@ trait DatabaseTroopQueries {
//Fixed part of negative troops (double troops) - by InCube
$array_amt[$i] = (int) $array_amt[$i] < 0 ? 0 : $array_amt[$i];
//Fixed part of negative troops (double troops) - by InCube
$units .= $unit.' = '.$unit.' '.(($array_mode[$i] == 1)? '+':'-').' '.($array_amt[$i] ? $array_amt[$i] : 0).(($number > $i+1) ? ', ' : '');
$amount = (int) ($array_amt[$i] ? $array_amt[$i] : 0);
/**
* The guard above only clamps the AMOUNT, never the RESULT, so any
* caller subtracting more than the column holds wrote a NEGATIVE
* troop count (issue #379: "-1 Hero" in the village troop list,
* after the hero was removed once by Battle::applyHeroBattleDamage()
* and a second time by the casualty pipeline).
*
* A negative troop count is never a valid game state, so floor the
* subtraction at 0 in SQL. Additions are untouched.
*/
if ($array_mode[$i] == 1) {
$units .= $unit.' = '.$unit.' + '.$amount;
} else {
$units .= $unit.' = GREATEST('.$unit.' - '.$amount.', 0)';
}
$units .= (($number > $i+1) ? ', ' : '');
}
$q = "UPDATE ".TB_PREFIX."units set $units WHERE vref = $vref";
return mysqli_query($this->dblink, $q);
@@ -881,7 +899,14 @@ trait DatabaseTroopQueries {
foreach ($unit as $index => $unitType) {
$unitType = ($unitType != 'hero' ? 'u' . $this->escape($unitType) : $unitType);
$pairs[] = $unitType . ' = ' . $unitType . (!(int) $mode[$index] ? ' - ' : ' + ') . (int) $amt[$index];
// Same floor as modifyUnit(): a reinforcement column must never go
// negative either (issue #379).
if ((int) $mode[$index]) {
$pairs[] = $unitType . ' = ' . $unitType . ' + ' . (int) $amt[$index];
} else {
$pairs[] = $unitType . ' = GREATEST(' . $unitType . ' - ' . (int) $amt[$index] . ', 0)';
}
}
$q = "UPDATE " . TB_PREFIX . "enforcement SET ".implode(', ', $pairs)." WHERE id = $id";