From 46183b79d52105e10e844d546970676590d9ff42 Mon Sep 17 00:00:00 2001 From: Ethan Nguyen Date: Sun, 12 Jul 2026 18:59:39 -0400 Subject: [PATCH 1/2] test: reproduce Vulconus + Xoph's Blood Avatar of Fire bug (#3062) Adds a failing system test demonstrating that a permanent source of the Avatar of Fire keystone (as Xoph's Blood grants) does not force Vulconus's 'while you (do not) have Avatar of Fire' state. The character always has Avatar of Fire, yet Condition:HaveAvatarOfFire stays unset (it is only ever set by the Vulconus config checkbox), so the '+2000 Armour while you do not have Avatar of Fire' line remains active. The test asserts the correct behaviour and therefore fails on current code, capturing the bug. Co-Authored-By: Claude Opus 4.8 (1M context) --- spec/System/TestDefence_spec.lua | 48 ++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/spec/System/TestDefence_spec.lua b/spec/System/TestDefence_spec.lua index f7df77a0567..d8de8a73d5a 100644 --- a/spec/System/TestDefence_spec.lua +++ b/spec/System/TestDefence_spec.lua @@ -1283,4 +1283,52 @@ describe("TestDefence", function() assert.are.equals(0, floor(poolsRemaining.Life)) assert.are.equals(0, floor(poolsRemaining.OverkillDamage)) end) + + -- Reproduction for issue #3062 (Vulconus + Xoph's Blood). + -- Vulconus grants Avatar of Fire only temporarily, so PoB gates its + -- "while you (do not) have Avatar of Fire" lines on a config checkbox. + -- When a PERMANENT source of the Avatar of Fire keystone is equipped + -- (Xoph's Blood grants the keystone), the character always has Avatar of + -- Fire and the "+2000 Armour while you do not have Avatar of Fire" line + -- must drop off regardless of the checkbox. It currently does not, because + -- Condition:HaveAvatarOfFire is only ever set by the checkbox and never by + -- keystone presence. These assertions therefore FAIL on the current code, + -- demonstrating the bug. + it("forces Avatar of Fire on with a permanent keystone source (issue #3062)", function() + build.itemsTab:CreateDisplayItemFromRaw([[ + Vulconus + Demon Dagger + Variant: Pre 3.5.0 + Variant: Current + Selected Variant: 2 + Implicits: 1 + 40% increased Global Critical Strike Chance + 50% chance to cause Bleeding on Hit + Every 8 seconds, gain Avatar of Fire for 4 seconds + 160% increased Critical Strike Chance while you have Avatar of Fire + 50% of Physical Damage Converted to Fire while you have Avatar of Fire + +2000 Armour while you do not have Avatar of Fire]]) + build.itemsTab:AddDisplayItem() + runCallback("OnFrame") + + -- Baseline: no permanent Avatar of Fire, checkbox unchecked, so the + -- "+2000 Armour while you do not have Avatar of Fire" line is active. + local armourWithoutAvatarOfFire = build.calcsTab.mainOutput.Armour + assert.is_true(armourWithoutAvatarOfFire >= 2000) + assert.is_falsy(build.calcsTab.mainEnv.player.modDB:Flag(nil, "Condition:HaveAvatarOfFire")) + + -- Equip a permanent source of the Avatar of Fire keystone, exactly as + -- Xoph's Blood does via its "Avatar of Fire" line. + build.itemsTab:CreateDisplayItemFromRaw([[ + New Item + Amber Amulet + Avatar of Fire]]) + build.itemsTab:AddDisplayItem() + runCallback("OnFrame") + + -- The character now permanently has Avatar of Fire, so the condition + -- must be set and the +2000 conditional armour must be gone. + assert.is_truthy(build.calcsTab.mainEnv.player.modDB:Flag(nil, "Condition:HaveAvatarOfFire")) + assert.are.equals(armourWithoutAvatarOfFire - 2000, build.calcsTab.mainOutput.Armour) + end) end) From 6ad1d1f18b0630cd2db49b96ef604199ca83d8c4 Mon Sep 17 00:00:00 2001 From: Ethan Nguyen Date: Sun, 19 Jul 2026 20:26:21 -0400 Subject: [PATCH 2/2] fix: force Avatar of Fire condition from permanent keystone sources (#3062) PoB gated Vulconus's "while you (do not) have Avatar of Fire" modifiers on Condition:HaveAvatarOfFire, which was only ever set by the Vulconus config checkbox. That checkbox models Vulconus's temporary buff, but a PERMANENT source of the Avatar of Fire keystone (Xoph's Blood, or a tree allocation) means the character always has Avatar of Fire. In that case the condition stayed unset, so the "+2000 Armour while you do not have Avatar of Fire" line remained active even though the character permanently had the keystone. Set Condition:HaveAvatarOfFire whenever the Avatar of Fire keystone is active (env.keystonesAdded), right after keystones are merged in calcs.perform. This mirrors the existing env.keystonesAdded idiom used for other keystone-driven behaviour (CalcDefence, CalcOffence) and leaves the config-checkbox path unchanged, since Vulconus never grants the keystone permanently on its own. Also fixes the repro test: assert.is_falsy(Flag(...)) errors when Flag returns nil (Lua drops the trailing nil, so luassert sees zero args); use assert.is_nil to match the codebase idiom. Co-Authored-By: Claude Opus 4.8 (1M context) --- spec/System/TestDefence_spec.lua | 2 +- src/Modules/CalcPerform.lua | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/spec/System/TestDefence_spec.lua b/spec/System/TestDefence_spec.lua index d8de8a73d5a..c4a6aba6418 100644 --- a/spec/System/TestDefence_spec.lua +++ b/spec/System/TestDefence_spec.lua @@ -1315,7 +1315,7 @@ describe("TestDefence", function() -- "+2000 Armour while you do not have Avatar of Fire" line is active. local armourWithoutAvatarOfFire = build.calcsTab.mainOutput.Armour assert.is_true(armourWithoutAvatarOfFire >= 2000) - assert.is_falsy(build.calcsTab.mainEnv.player.modDB:Flag(nil, "Condition:HaveAvatarOfFire")) + assert.is_nil(build.calcsTab.mainEnv.player.modDB:Flag(nil, "Condition:HaveAvatarOfFire")) -- Equip a permanent source of the Avatar of Fire keystone, exactly as -- Xoph's Blood does via its "Avatar of Fire" line. diff --git a/src/Modules/CalcPerform.lua b/src/Modules/CalcPerform.lua index 03ab616ab83..14c6a85ee35 100644 --- a/src/Modules/CalcPerform.lua +++ b/src/Modules/CalcPerform.lua @@ -1103,6 +1103,15 @@ function calcs.perform(env, skipEHP) env.keystonesAdded = { } modLib.mergeKeystones(env, env.modDB) + -- A permanent source of the Avatar of Fire keystone (the passive tree or an + -- item such as Xoph's Blood) means the character always has Avatar of Fire. + -- Force the condition on so "while you (do not) have Avatar of Fire" mods + -- (e.g. on Vulconus) resolve correctly. The Vulconus config option only + -- covers its temporary buff, which is why the condition isn't set otherwise. + if env.keystonesAdded["Avatar of Fire"] then + modDB:NewMod("Condition:HaveAvatarOfFire", "FLAG", true, "Keystone:Avatar of Fire") + end + -- Build minion skills for _, activeSkill in ipairs(env.player.activeSkillList) do activeSkill.skillModList = new("ModList", activeSkill.baseSkillModList)