fix(combat): gate Brewery attack bonus on an active Mead-Festival [#294] (#321)

The Brewery (building 35, Teuton-only) grants a +1% attack bonus per level
to the whole empire, but only while a Mead-Festival is running (72h), and the
building is capital-only. The attack-bonus block in calculateBattle() ignored
both rules: it read the Brewery level from $AttackerWref (the *launching*
village, which usually has no Brewery) and applied the bonus purely on the
building level, with no festival check.

As a result the bonus never reacted to the festival being started or expired:
attacks with and without an active festival produced identical losses, so the
army appeared unaffected by the festival (issue #294). Depending on whether the
attack was launched from the capital, the bonus was either permanently on or
permanently off, but never gated by the festival.

Read the Brewery level from the attacker's capital and gate the bonus on the
festival being active, mirroring the catapult-randomization gate in Units.php
and the chief-penalty gate in Automation.php. The simulator passes
AttackerID = 0, so it keeps a 0 bonus exactly as before.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Ferywir
2026-06-30 14:06:44 +02:00
committed by GitHub
parent 3af041df8c
commit 06d1523d9d
+23 -12
View File
@@ -844,25 +844,36 @@ class Battle {
/******************************************************************
* ATTACK / DEFENSE TOTAL
******************************************************************/
if ($AttackerWref != 0) {
$bonus = 0;
$typeLevel = $this->getTypeLevel(35, $AttackerWref);
// Brewery (35) Mead-Festival attack bonus: Teuton-only, capital-only but
// empire-wide, and active ONLY while a festival is running (72h). It must be
// read from the attacker's CAPITAL — $AttackerWref is the launching village,
// which usually has no Brewery — and gated on the festival being active,
// otherwise the bonus is permanent and never reacts to the festival being
// started/expired (issue #294). This mirrors the catapult-randomization gate
// in Units.php and the chief-penalty gate in Automation.php. The simulator
// passes AttackerID = 0, so it keeps a 0 bonus exactly as before.
if ($AttackerID != 0 && $att_tribe == 2) {
$bonus = isset($bid35[$typeLevel])
? $bid35[$typeLevel]['attri']
: 0;
$attackerCapital = $database->getVillage($AttackerID, 3);
$rap = round(
($ap + $cap) + (
(($ap + $cap) / 100) * $bonus
)
);
if ($attackerCapital && (int)$attackerCapital['festival'] > time()) {
} else {
$typeLevel = $this->getTypeLevel(35, $attackerCapital['wref']);
$rap = round($ap + $cap);
$bonus = isset($bid35[$typeLevel])
? $bid35[$typeLevel]['attri']
: 0;
}
}
$rap = round(
($ap + $cap) + (
(($ap + $cap) / 100) * $bonus
)
);
if ($rap == 0) {
$rdp = round($dp + $cdp);