From 55ab994421934849f32cd1b499e017aae297b864 Mon Sep 17 00:00:00 2001 From: Lucas Karsten Date: Sat, 15 Aug 2026 15:00:32 -0300 Subject: [PATCH] Count exposure granted to the enemy as an exposure source The Whispering Ice lowers the enemy's elemental resistances through an EnemyModifier, and so does "Enemies in your Presence have Exposure". Neither grants an ExposureChance modifier or the InflictExposure flag, which are the only things hasExposureSource looks for, so the build ends up in a state where the enemy has Fire Exposure while the player is said to be unable to apply it. Condition:CanApplyExposure gates the "Is the enemy Exposed" config options, so with those items the options are never drawn and the exposure cannot be inspected in the Calcs tab, even though the resistances already reflect it. The enemy's own exposure modifiers are now treated as a source. Exposure still resolves to the largest single source, so enabling the config on top of an item that already grants more does not change the result. --- spec/System/TestExposureSource_spec.lua | 56 +++++++++++++++++++++++++ src/Modules/CalcPerform.lua | 6 +++ 2 files changed, 62 insertions(+) create mode 100644 spec/System/TestExposureSource_spec.lua diff --git a/spec/System/TestExposureSource_spec.lua b/spec/System/TestExposureSource_spec.lua new file mode 100644 index 0000000000..3b5b092b6d --- /dev/null +++ b/spec/System/TestExposureSource_spec.lua @@ -0,0 +1,56 @@ +describe("ExposureSource", function() + before_each(function() + newBuild() + end) + + local function equipWhisperingIce() + build.itemsTab:CreateDisplayItemFromRaw([[ + New Item + Permafrost Staff + Implicits: 0 + Inflict Elemental Exposure on Hit, lowering Total Elemental Resistances by 60% + ]]) + build.itemsTab:AddDisplayItem() + runCallback("OnFrame") + end + + local function canApply(element) + return build.calcsTab.calcsEnv.modDB:Flag(nil, "Condition:CanApply" .. element .. "Exposure") == true + end + + local function enemyResist(element) + return build.calcsTab.calcsEnv.enemyDB:Sum("BASE", nil, element .. "Resist") + end + + it("counts exposure granted directly to the enemy as a source", function() + equipWhisperingIce() + + -- Without this the enemy is exposed while the build is told it cannot + -- expose, which hides the "Is the enemy Exposed" config entirely. + assert.is_true(build.calcsTab.calcsEnv.enemyDB:Flag(nil, "Condition:HasFireExposure") == true) + for _, element in ipairs({ "Fire", "Cold", "Lightning" }) do + assert.is_true(canApply(element)) + end + end) + + it("does not report a source when nothing grants exposure", function() + runCallback("OnFrame") + + for _, element in ipairs({ "Fire", "Cold", "Lightning" }) do + assert.is_false(canApply(element)) + end + end) + + it("keeps the largest exposure when the config is also enabled", function() + equipWhisperingIce() + local itemOnly = enemyResist("Fire") + + build.configTab.input.conditionEnemyFireExposure = true + build.configTab:BuildModList() + runCallback("OnFrame") + + -- Exposure does not stack: the -20% from the config must not add to the + -- -60% from the item. + assert.are.equals(itemOnly, enemyResist("Fire")) + end) +end) diff --git a/src/Modules/CalcPerform.lua b/src/Modules/CalcPerform.lua index dbc4bb9767..bd0058197a 100644 --- a/src/Modules/CalcPerform.lua +++ b/src/Modules/CalcPerform.lua @@ -431,6 +431,12 @@ local function doActorAttribsConditions(env, actor) if modDB:Sum("BASE", nil, modName) > 0 or modDB:HasMod("FLAG", nil, "InflictExposure") then return true end + -- Exposure granted straight to the enemy, as The Whispering Ice and + -- "Enemies in your Presence have Exposure" do, carries no chance mod + -- and no InflictExposure flag, so it has to be read off the enemy. + if env.enemy and env.enemy.modDB and env.enemy.modDB:Sum("BASE", nil, element .. "Exposure") > 0 then + return true + end for _, activeSkill in ipairs(env.player.activeSkillList) do if hasActiveSkillExposureSource(activeSkill, modName) then return true