Skip to content

fix(combat): armour durability wear targeted the attacker instead of the defender#574

Merged
CoreyRDean merged 1 commit into
developfrom
fix/armour-wear-defender
Jun 10, 2026
Merged

fix(combat): armour durability wear targeted the attacker instead of the defender#574
CoreyRDean merged 1 commit into
developfrom
fix/armour-wear-defender

Conversation

@CoreyRDean

Copy link
Copy Markdown
Collaborator

The bug (gameplay, runtime-confirmed, found by PR #572's pins)

In ActorAttack's melee path, the "Damage armour" block (GameServer.bb:551-566) iterated A1 (the attacker's) inventory over the armour slots and echoed P_ItemHealth to A1\RNID — but the armour that absorbed the blow is the defender's (all three damage formulas read GetArmourLevel(A2\Inventory)). Net effect: attacking wore down your own armour; the defender's never degraded. Almost certainly a copy-paste of the weapon-wear block above it (:537-549), where A1 is correct. Pinned empirically in #572: defender chest stayed exactly 100 over 300 swings.

The fix

A1A2 inside that one block only — the inventory iterated/decremented and the echo recipient. Structure, guards, Rand(1,5) wear rate, slot range, and packet shape unchanged. NPC defenders wear silently under the existing If RNID > 0 convention (same as the weapon block); their armour state stays authoritative server-side.

Wire-routing correctness (verifier-checked): the P_ItemHealth handler (ClientNet.bb:249-252) carries no actor ID — it updates the recipient's own inventory slot. So sending to A2\RNID makes the defending player's client update the same slot the server just decremented in A2's authoritative inventory. Server and client share the SlotI_* constants; the mapping is exact.

Coherence bonus: GetArmourLevel only counts pieces with ItemHealth > 0, so the fix closes the intended loop — the defender's armour degrades and eventually stops protecting the defender (previously, attacking nonsensically degraded the attacker's own defense).

Plus the deliberate flip of the #572 pin: testArmourWearHitsAttackerNotDefendertestArmourWearHitsDefender (defender wears, attacker stays 100; 300 swings × 1-in-5 wear ≈ 60 expected decrements, so the assertions discriminate with enormous margin). The weapon-wear pin is untouched.

Deliberately untouched: the formula-3 vulnerability paradox at :521 (separate, maintainer-gated) and the block's position/conditions (wear still rolls on the melee path exactly where it did).

Out-of-scope observation for a follow-up: the projectile hit path (GameServer.bb:268-304) reads GetArmourLevel(A2\Inventory) but has no armour-wear block at all — ranged hits never wear the defender's armour, before or after this fix.

Verification (independent verifier ≠ implementer)

  • Diff = the one block in GameServer.bb (+ comment) and the one test flip; weapon-wear block and :521 byte-identical; no generator-gated files.
  • No other consumer of the attacker-wear side effect exists (repair UI, serialisation, BVM accessors all wear-source-agnostic).
  • Full suite Ran 58 files: 58 passed, 0 failed.; FULL compile.bat (engine + all 7 tools) exit 0.

🤖 Generated with Claude Code

…the defender

The 'Damage armour' block in ActorAttack (GameServer.bb) was a copy-paste
of the weapon-wear block directly above it, which correctly targets A1
(the attacker's weapon wears). The copy kept A1 for armour wear too, so
each swing wore down the ATTACKER's equipped armour and echoed the
P_ItemHealth durability update to the attacker -- even though the armour
that absorbed the blow belongs to A2 (the defender; the damage formulas
read GetArmourLevel(A2\Inventory)). Re-target the armour-wear block only:
A2's Shield..Feet slots now wear, and the P_ItemHealth echo goes to
A2\RNID under the same RNID > 0 NPC guard the block already used.

Deliberate test flip: CombatDamageFormulaTest.bb's
testArmourWearHitsAttackerNotDefender pinned the shipped bug
(FLAG-FOR-HUMAN, PR #572). Renamed to testArmourWearHitsDefender and
flipped its assertions to the corrected behavior -- the defender's chest
piece loses durability over a 300-swing volley while the attacker's stays
at exactly 100. The attacker's weapon-wear pin and every other pin are
untouched.

Verified: test.bat CombatDamageFormula PASS; full suite 58/58 green;
full compile.bat (engine + tools) exit 0.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@CoreyRDean CoreyRDean requested a review from a team as a code owner June 10, 2026 23:44
@CoreyRDean CoreyRDean merged commit 0e8e59f into develop Jun 10, 2026
1 check passed
@CoreyRDean CoreyRDean deleted the fix/armour-wear-defender branch June 10, 2026 23:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant