Commit Graph

1587 Commits

Author SHA1 Message Date
Ferywir 06d1523d9d 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>
2026-06-30 15:06:44 +03:00
novgorodschi catalin 3af041df8c Fix issue #320
Fix issue #320 - now you can delete village
2026-06-30 14:29:13 +03:00
novgorodschi catalin 97d6b7650c Change capital add index
Change capital add index
2026-06-30 10:32:01 +03:00
Ferywir 12badfa84b fix(troops): apply Tournament Square bonus only beyond the threshold [#304] (#317)
procDistanceTime() multiplied the whole travel distance by the Tournament
Square speed factor as soon as the distance reached TS_THRESHOLD. That
made the trip time jump down at the threshold, so a target just past it
arrived dramatically sooner than a nearer one (e.g. a village 41 tiles
away raided faster than one 18 tiles away).

In T3.6 the Tournament Square only speeds up the part of the journey
beyond the threshold: the first TS_THRESHOLD tiles are walked at base
speed and the remainder at the boosted speed. Split the computation
accordingly so travel time stays monotonic with distance while still
rewarding a high-level square.

This is a long-standing bug, unrelated to the Generator refactor (which
only reformatted the same whole-distance multiplication). The same fix is
applied to the duplicate procDistanceTime() in Admin/database.php used by
the admin troop-return helper.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-30 09:01:16 +03:00
novgorodschi catalin f7594e3f81 Fix login #312
Fix login #312
2026-06-30 08:24:37 +03:00
novgorodschi catalin 2fc35729b4 Fix admin log addVillage
Fix admin log addVillage
2026-06-30 08:21:55 +03:00
Ferywir 58475a0358 fix(combat): bounce attacks home when a multi-wave razes their target [#298] (#311) 2026-06-29 17:41:19 +03:00
Ferywir ffa04cad3a fix(combat): bounce attacks home when their target village no longer exists [#298] (#310)
Follow-up to #309. That fix only skipped follow-up waves whose target
was razed within the same resolution batch (an in-memory set). But waves
timed a second apart land in separate ticks: the first razes the village,
the next tick re-resolves the follow-up wave against a village that no
longer exists. getMInfo() (wdata LEFT JOIN vdata) then returns NULL vdata
columns, so the return trip is computed from a NULL wref (NULL coords) ->
a bogus arrival time that strands the troops forever (report against
"[?]"); DelVillage() does not reliably bounce every in-flight wave
either, leaving the attack at proc=0 and re-fetched every tick.

Detect the razed target from DB truth: after resolveVillageTarget(), if
the village is gone ($to['wref'] is NULL) or it was razed earlier in this
same batch ($razedTargets, still needed because getMInfo() is cached and
a same-batch wave would otherwise see the stale "alive" row), bounce the
whole army straight home and mark the movement processed instead of
fighting a phantom. setMovementProc() returns true only when it flips
proc 0->1, so we never duplicate a return DelVillage() already created.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 13:52:31 +03:00
Ferywir f849260bfa fix(combat): stop attacks looping on a village razed earlier in the same tick [#298] (#309)
When several catapult waves land in the same resolution tick,
sendunitsComplete() fetches them all into one in-memory batch. The first
wave can raze the target village: handleVillageDestruction() -> DelVillage()
already bounces every still-in-flight attack straight home (symmetric return
time) and deletes the vdata row, leaving the wdata tile as an empty valley.

The loop then kept resolving the remaining waves from the in-memory batch
against the now-deleted village. getMInfo() LEFT-JOINs wdata onto the missing
vdata, so every vdata column (including wref) comes back NULL. The return
trip in finalizeReturnOrDeath() is then computed from a NULL wref (NULL
coordinates), producing a bogus arrival time that strands the troops, and a
duplicate of the bounce DelVillage() had already issued. The attacker sees
the waves "coming to nowhere and never coming back" (issue #298).

Track the tiles razed during the current batch and skip any follow-up wave
that targets one of them: those troops were already bounced home by
DelVillage(). No change to the battle maths; for a normal (non-razed) target
the set stays empty, so behaviour is preserved.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 12:18:38 +03:00
Ferywir 299e3c7338 fix(spawn): stop WW/artifact villages colliding on axis tiles [#301] (#308)
generateBase()'s four sector predicates overlapped on the axes: a tile
with x = 0 or y = 0 satisfied two of them (e.g. both "x <= 0" and
"x >= 0" matched x = 0). createWWVillages() and addArtifactVillages()
generate a whole batch in one go and only flip occupied = 0 -> 1 at the
very end (setFieldTaken()), so two sectors could each select the SAME
axis tile and assign its wref to two different villages. The duplicate
addVillage() insert then violated the vdata PRIMARY KEY(wref); under the
PHP 8.1+ mysqli exception mode this threw and aborted generateVillages()
before setFieldTaken()/addResourceFields()/addUnits() ran. The result:
the villages existed in vdata (and showed up under the Natars profile)
but their map tile was never marked occupied and had no resource fields
or troops, so the map still rendered an "abandoned valley" (issue #301).

Make the four sectors mutually exclusive by using a strict bound on one
side of each axis, so every tile belongs to exactly one sector and no
wref can be handed out twice within a batch.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 11:54:23 +03:00
novgorodschi catalin e1847bc50b Fix #302
Fix add troops in addUser.php
2026-06-29 11:42:22 +03:00
Ferywir 8c1a6ad05b fix(admin): render a proper error page instead of a blank page on denial [#299] (#307)
Issue #299: posting to an admin Mod (eg editBuildings.php) could show an
essentially blank page. The admin panel and the game share the same PHP
session, so a game logout (session_destroy) — or a mobile browser dropping the
session cookie / serving a cached form with a stale token — wipes the admin
session. The Mod then stopped on a bare die('<h1>Access Denied</h1>') (or the
403 die() in csrf_verify()), which renders as a blank/broken page outside the
panel.

Add a shared admin_deny() helper in GameEngine/Admin/csrf.php that renders a
clean, self-contained, styled error page (with a "Return to Admin Panel" link)
and a no-store header, then exits. Wire it into csrf_verify() and replace every
bare "Access Denied" die() across the 42 admin Mods. Each Mod now loads
csrf.php at the top so admin_deny() is available before its first access check.

This is the presentation fix Shadow asked for ("we must receive an error not
blank page"). The deeper root cause (admin and game sharing one PHP session) is
left for a follow-up: giving the admin panel its own session cookie name.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 10:25:18 +03:00
novgorodschi catalin a998edc205 Fix #294 code for brewery
Fix #294 now brewery is fully coded!
2026-06-29 08:02:09 +03:00
novgorodschi catalin b4e54c6ac3 Fix #291
// Bug fix: RemoveXSS() calls htmlspecialchars() (&,<,>,",' -> entities).
        // Every display site for these values ALREADY escapes correctly on output
        // (links.tpl's safeHTML(), and preference.tpl's edit-row value=""), so
        // encoding here too meant a saved "&" was stored as literal "&amp;" text
        // in the DB, then got escaped AGAIN on redisplay — surviving one level of
        // browser entity-decoding as visible "&amp;". Worse, it silently broke
        // any saved link with a real query parameter after the first one (e.g.
        // build.php?gid=16&t=99): the stored value no longer had a real "&"
        // separator there, so "t" was never received as its own GET param.
        // strip_tags() (for name) + mysqli_real_escape_string() (below, for SQL)
        // are sufficient at save time; HTML-escaping belongs only at display time.
2026-06-29 07:49:49 +03:00
Ferywir fa1057d277 Refactor(Technology): de-duplicate unit-summing and tidy getUpkeep() [#219] (#289) 2026-06-26 06:30:38 +03:00
Ferywir 0e13ae23be Refactor(Technology): split trainUnit() into focused helpers [#219] (#288) 2026-06-26 06:29:37 +03:00
Ferywir 082ad85697 Refactor(Technology): make getTrainingList() table-driven [#219] (#287) 2026-06-25 15:45:05 +03:00
novgorodschi catalin 8f204ec4ae Compact checkvacation function
Compact checkvacation function to reduce query
2026-06-25 14:35:04 +03:00
Ferywir 596f007139 Refactor(Units): extract resolveCatapultTargets() from sendTroops() [#219] (#284)
sendTroops() inlined ~65 lines deciding the catapult targets ctar1/ctar2: the
"Rivals great confusion" artefact lookup, the rally-point-level-driven list of
invalid target buildings, the troop/level eligibility rules and the Teuton
Brewery / artefact adjustments. Move that whole block into
resolveCatapultTargets(&$post, $data), which mutates $post['ctar1']/['ctar2'] by
reference exactly as before; sendTroops() now calls it before building the
attack. None of the block's locals were used afterwards. Behaviour-preserving.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-25 13:44:42 +03:00
Ferywir 58469ea024 Refactor(Units): split checkErrors() into focused validators [#219] (#283) 2026-06-25 13:28:47 +03:00
Ferywir db3953d825 Refactor(Units): extract buildHeroStats() to de-duplicate Hero() [#219] (#282)
Both branches of Hero() (single hero when !$all, full list when $all) computed
the same five derived stats (atk/di/dc/ob/db) and assembled a byte-identical
hero stat array from a getHero() entry plus its unit base data. Extract that
into buildHeroStats($hero, $herodata) and call it from both branches.
Behaviour-preserving.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-25 12:43:26 +03:00
Ferywir 1e661b7a03 Refactor(Units): collapse the duplicated procUnits() cases 1-4 [#219] (#281)
Cases 1 to 4 of the procUnits() switch had a byte-identical body (send troops
when the rally-point form is submitted, otherwise load the unit form). Stack the
four case labels and keep a single shared body via switch fall-through.
Behaviour-preserving.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-25 12:37:54 +03:00
Ferywir 06089df64c Refactor(Technology): merge upgradeSword/upgradeArmour into upgradeWeaponOrArmour() [#219] (#280)
upgradeSword() and upgradeArmour() were near-identical: the only differences
were the AB-tech key prefix ('b' vs 'a'), the building type whose level gates
the research (Smithy 12 vs Armoury 13) and the matching bid building data
($bid12 vs $bid13). Merge them into a single upgradeWeaponOrArmour($get, $type)
parameterised by the prefix, deriving the building type from it, and route both
procTechno() cases through it. Resolves the pre-existing //TODO. Behaviour-
preserving.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-25 12:20:26 +03:00
novgorodschi catalin fe6ff01396 Fix Some PHP 8+ view error in Build & Credits
Fix Some PHP 8+ view error in Build & Credits

TO DO :  Full audit in build directory
2026-06-25 10:54:58 +03:00
Catalin Novgorodschi 23708a2e54 Fix critic bug typo critic: $refValule
Fix critic bug typo critic: $refValule
2026-06-24 20:24:35 +03:00
Ferywir 49ce36fd99 Refactor(Automation): TO-DO list items 7-9 [#266] (#278) 2026-06-24 18:21:30 +03:00
Ferywir 045f72a6e8 Refactor(Automation): clean up the Automation.php TO-DO list [#266] (#276) 2026-06-24 17:25:22 +03:00
Ferywir d0479a55ce fix(profile): store profile descriptions raw to stop double-escaping (#273) 2026-06-24 06:51:24 +03:00
Ferywir e17bb3dec6 fix(admin): verify CSRF token in maintenance admin Mods [#139] (#269) 2026-06-23 17:01:20 +03:00
Ferywir 886f421f50 fix(admin): verify CSRF token in server-settings admin Mods [#139] (#268) 2026-06-23 16:12:54 +03:00
novgorodschi catalin 8d492bebd3 Update some files & remove unused code
Update some files & remove unused code
2026-06-23 13:43:17 +03:00
Ferywir 6472b30bd2 fix(admin): verify CSRF token in message admin Mods [#139] (#264)
sendMessage, massmessage and sysmessage are POSTed to directly, bypassing
admin.php's central csrf_verify(). Add csrf_verify() (after the admin access
check, via the shared GameEngine/Admin/csrf.php) and csrf_field() in their
forms (Newmessage.tpl, massmessage.tpl, sysmessage.tpl; the mass/sys templates
have both a prepare and an execute form).

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-23 11:49:32 +03:00
Ferywir fb225b562f fix(admin): repair broken admin-log variables in medals Mod (#262)
The post-delete admin-log block referenced variables that were never defined
($admid/$adminID/$medalid/$uid), so on PHP 8.1+ (mysqli throws on error) the
malformed INSERT raised an uncaught mysqli_sql_exception → HTTP 500 after the
medal was already deleted. Use the correct ids ($admid from session, $uid from
POST), look up the target player's username (escaped), and redirect to the
sanitized $uid.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-23 11:12:04 +03:00
Ferywir 8a3a67d175 fix(admin): verify CSRF token in alliance/medal admin Mods [#139] (#261)
editAli, delAli, medals, delallymedal, delallymedalbyaid, delallymedalbyweek
and deletemedalbyweek are POSTed to directly, bypassing admin.php's central
csrf_verify(). Add csrf_verify() (after the admin access check, via the shared
GameEngine/Admin/csrf.php) and csrf_field() in their forms (playermedals.tpl,
editAli.tpl, delAli.tpl, delmedal.tpl, allymedals.tpl, delallymedal.tpl).

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-23 11:11:32 +03:00
Ferywir ca991408c1 fix(admin): verify CSRF token in village admin Mods [#139] (#259)
editVillageOwner, renameVillage, editBuildings and editResources are POSTed
to directly, bypassing admin.php's central csrf_verify(). Add csrf_verify()
(after the admin access check, via the shared GameEngine/Admin/csrf.php) and
csrf_field() in their forms (editVillage.tpl, village.tpl, editResources.tpl).

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-23 10:31:08 +03:00
Ferywir 8d1a1cab38 fix(admin): verify CSRF token in troop admin Mods [#139] (#258)
addTroops and addABTroops are POSTed to directly, bypassing admin.php's
central csrf_verify(). Add csrf_verify() (after the admin access check, via
the shared GameEngine/Admin/csrf.php) and csrf_field() in their forms.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-23 08:43:18 +03:00
Ferywir b880622fd1 fix(admin): route password/protection pages and align username validation (#257) 2026-06-23 06:55:13 +03:00
Ferywir e49069a9c6 fix(admin): verify CSRF token in player-management admin Mods [#139] (#256) 2026-06-23 06:54:03 +03:00
Ferywir 6888a09b5f fix(admin): let editPlus subtract Plus/bonus days as the form advertises (#254) 2026-06-22 18:41:28 +03:00
Ferywir 6e79c47951 fix(admin): verify CSRF token in Plus/Gold admin Mods [#139] (#253) 2026-06-22 18:40:43 +03:00
Ferywir 9d2d7699a9 fix(profile): neutralize stored XSS in profile descriptions [#250] (#252) 2026-06-22 16:31:20 +03:00
novgorodschi catalin b9d36ba311 Fix also in admin panel removexss
Fix also in admin panel removexss
2026-06-22 12:40:07 +03:00
novgorodschi catalin c250a19f5a Fix #250
Fix #250
2026-06-22 12:34:31 +03:00
novgorodschi catalin 561933b23b Fix fLevel
Fix fLevel
2026-06-22 11:42:44 +03:00
novgorodschi catalin 41e0fd6313 Some fix
Some fix
2026-06-22 10:41:25 +03:00
novgorodschi catalin 337c94b981 Fix Admin Panel view & some PHP 8.1+
Fix Admin Panel view & some PHP 8.1+
2026-06-22 09:20:16 +03:00
Ferywir f581add125 feature(rally-point): mark incoming attacks + show per-troop travel time [#245] (#248) 2026-06-22 06:54:41 +03:00
Ferywir b7e943ea90 fix(admin): wire CSRF token into admin.php-routed forms [#139] (#244) 2026-06-20 06:44:12 +03:00
Ferywir 1ce1003153 fix(chat): stop echoing the raw INSERT query in add_data() [#139] (#243) 2026-06-19 13:31:21 +03:00
Ferywir b0e6680705 fix(chat): replace eval() of server output with JSON.parse() [#139] (#242) 2026-06-19 13:30:54 +03:00