From 3a159b4a43154ef255bb435d699fa052c6372ae8 Mon Sep 17 00:00:00 2001 From: vaisest <4550061+vaisest@users.noreply.github.com> Date: Sun, 12 Jul 2026 18:28:15 +0300 Subject: [PATCH] Fix 2H weapons not removing off-hand items in comparisons --- spec/System/TestMiscCalculator_spec.lua | 126 ++++++++++++++++++++++++ src/Modules/CalcSetup.lua | 26 ++--- 2 files changed, 141 insertions(+), 11 deletions(-) create mode 100644 spec/System/TestMiscCalculator_spec.lua diff --git a/spec/System/TestMiscCalculator_spec.lua b/spec/System/TestMiscCalculator_spec.lua new file mode 100644 index 0000000000..3d4c1319ce --- /dev/null +++ b/spec/System/TestMiscCalculator_spec.lua @@ -0,0 +1,126 @@ +describe("MiscCalculator", function() + before_each(function() + newBuild() + end) + describe("repItem behaviour", function() + local calcFunc + before_each(function () + calcFunc = build.calcsTab:GetMiscCalculator() + end) + + local function pasteAndEquipItem(itemText) + build.itemsTab:CreateDisplayItemFromRaw(itemText) + build.itemsTab:AddDisplayItem() + runCallback("OnFrame") + end + + local function allocateKeystone(keystoneText) + build.configTab.input.customMods = keystoneText + build.configTab:BuildModList() + runCallback("OnFrame") + calcFunc = build.calcsTab:GetMiscCalculator() + end + + local function equipInSlot(item, slotName) + build.itemsTab:AddItem(item, true) + build.itemsTab.slots[slotName]:SetSelItemId(item.id) + runCallback("OnFrame") + end + local focus = [[Rarity: RARE ++2 Energy Shield +Hallowed Focus +Energy Shield: 57 +Crafted: true +Prefix: {range:0.8}LocalIncreasedEnergyShield8 +Prefix: {range:0.8}LocalIncreasedEnergyShieldPercent6 +Prefix: None +Suffix: {range:0.8}GlobalSpellGemsLevel2 +Suffix: None +Suffix: None +Quality: 0 +LevelReq: 61 +Implicits: 0 ++71 to maximum Energy Shield +89% increased Energy Shield ++2 to Level of all Spell Skills]] + + local quiver = [[Test Subject +Rarity: Rare +Toxic Quiver +Crafted: true +Implicits: 1 ++100 to maximum energy shield]] + + local bow = new("Item", [[Rarity: Rare +Test Subject +Gemini Bow]]) + + local talisman = new("Item", [[Rarity: Rare +Test Subject +Spiny Talisman]]) + + local staff = new("Item", [[Rarity: Rare +Test Subject +Chiming Staff]]) + + local mace = new("Item", [[Rarity: Rare +Test Subject +Ironwood Greathammer]]) + + local sceptre = new("Item", [[Rarity: Rare +Test Subject +Rattling Sceptre ++100 to maximum Energy Shield]]) + local wand = new("Item", [[Rarity: Rare +Test Subject +Dueling Wand]]) + it("calculates off-hand without a weapon", function () + pasteAndEquipItem(focus) + local output = calcFunc() + assert.True(output.EnergyShield > 0) + end) + it("unequips the off-hand focus when replacing weapon 1 with two-handed weapons", function() + pasteAndEquipItem(focus) + for _, item in ipairs({ bow, talisman, staff, mace }) do + local output = calcFunc({ repSlotName = "Weapon 1", repItem = item }) + assert.True(output.EnergyShield == 0) + end + end) + it("keeps the off-hand quiver when using bow", function() + -- quivers need a bow to be equipped first + pasteAndEquipItem(bow:BuildRaw()) + pasteAndEquipItem(quiver) + local output = calcFunc({ repSlotName = "Weapon 1", repItem = bow }) + assert.True(output.EnergyShield > 0) + end) + it("keeps the off-hand focus when using a two-handed weapon with Giant's Blood", function() + pasteAndEquipItem(focus) + allocateKeystone("You can wield Two-Handed Axes, Maces and Swords in one hand") + local output = calcFunc({ repSlotName = "Weapon 1", repItem = mace }) + assert.True(output.EnergyShield > 0) + end) + it("keeps the off-hand focus when using a staff with Instruments of Power", function() + pasteAndEquipItem(focus) + allocateKeystone("You can equip a Focus while wielding a Staff") + local output = calcFunc({ repSlotName = "Weapon 1", repItem = staff }) + assert.True(output.EnergyShield > 0) + end) + it("keeps rare off-hand sceptre when using a talisman with Lord of the Wilds", function() + equipInSlot(sceptre, "Weapon 2") + allocateKeystone("You can equip a non-Unique Sceptre while wielding a Talisman") + local output = calcFunc({ repSlotName = "Weapon 1", repItem = talisman }) + assert.True(output.EnergyShield > 0) + end) + it("unequips unique off-hand sceptre when using a talisman with Lord of the Wilds", function() + equipInSlot(new("Item", "Rarity: Unique\n" .. sceptre:BuildRaw()), "Weapon 2") + allocateKeystone("You can equip a non-Unique Sceptre while wielding a Talisman") + local output = calcFunc({ repSlotName = "Weapon 1", repItem = talisman }) + assert.True(output.EnergyShield == 0) + end) + it("keeps off-hand when using one-handed weapon", function() + pasteAndEquipItem(focus) + local output = calcFunc({ repSlotName = "Weapon 1", repItem = wand }) + assert.True(output.EnergyShield > 0) + end) + end) +end) diff --git a/src/Modules/CalcSetup.lua b/src/Modules/CalcSetup.lua index 5ba727778c..489e6dc72c 100644 --- a/src/Modules/CalcSetup.lua +++ b/src/Modules/CalcSetup.lua @@ -854,9 +854,9 @@ function calcs.initEnv(build, mode, override, specEnv) modDB:NewMod("Condition:WeaponSet" .. (build.itemsTab.activeItemSet.useSecondWeaponSet and 2 or 1) , "FLAG", true, "Weapon Set") local weaponFlagState = { - giantsBlood = nodesModsList:Flag(nil, "GiantsBlood") or false, - instrumentsOfPower = nodesModsList:Flag(nil, "InstrumentsOfPower") or false, - lordOfTheWilds = nodesModsList:Flag(nil, "LordOfTheWilds") or false, + giantsBlood = nodesModsList:Flag(nil, "GiantsBlood") or modDB:Flag(nil, "GiantsBlood") or false, + instrumentsOfPower = nodesModsList:Flag(nil, "InstrumentsOfPower") or modDB:Flag(nil, "InstrumentsOfPower") or false, + lordOfTheWilds = nodesModsList:Flag(nil, "LordOfTheWilds") or modDB:Flag(nil, "LordOfTheWilds") or false, } -- Only mutate equipped items in the real build pass; calculator overrides (e.g. node power) are transient. if mode == "MAIN" then @@ -896,19 +896,23 @@ function calcs.initEnv(build, mode, override, specEnv) local item if slotName == override.repSlotName then item = override.repItem - elseif override.repItem and override.repSlotName:match("^Weapon 1") and slotName:match("^Weapon 2") and - ( - (not lordOfTheWilds and override.repItem.base.type == "Talisman" and item and item.base.type ~= "Sceptre" and item.rarity ~= "UNIQUE" and item.rarity ~= "RELIC") - or (not instrumentsOfPower and override.repItem.base.type == "Staff" and item and item.base.type ~= "Focus") - or (not giantsBlood and (override.repItem.base.type == "Two Hand Sword" or override.repItem.base.type == "Two Hand Axe" or override.repItem.base.type == "Two Hand Mace")) - or (override.repItem.base.type == "Bow" and item and item.base.type ~= "Quiver") - ) then - goto continue elseif slot.nodeId and override.spec then item = build.itemsTab.items[env.spec.jewels[slot.nodeId]] else item = build.itemsTab.items[slot.selItemId] end + -- if we are replacing the main hand weapon, we should unequip the off hand weapon if it's not allowed + local overrideTwoHand = override.repItem and override.repItem.base.type and not (env.data.weaponTypeInfo[override.repItem.base.type] or {}).oneHand + if overrideTwoHand and override.repSlotName and override.repSlotName:match("^Weapon 1") and slotName:match("^Weapon 2") then + local allowLordOfTheWilds = lordOfTheWilds and override.repItem.base.type == "Talisman" and item and item.base.type == "Sceptre" and item.rarity ~= "UNIQUE" and item.rarity ~= "RELIC" + local allowInstrumentsOfPower = instrumentsOfPower and override.repItem.base.type == "Staff" and item and item.base.type == "Focus" + local allowGiantsBlood = giantsBlood and (override.repItem.base.type == "Two Hand Sword" or override.repItem.base.type == "Two Hand Axe" or override.repItem.base.type == "Two Hand Mace") + local allowQuiver = (override.repItem.base.type == "Bow" and item and item.base.type == "Quiver") + local allowOffHand = allowLordOfTheWilds or allowInstrumentsOfPower or allowGiantsBlood or allowQuiver + if not allowOffHand then + goto continue + end + end if item and item.grantedSkills then -- Find skills granted by this item for _, skill in ipairs(item.grantedSkills) do