Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
dd9af08
feat(weighted-score): add shared WeightedScore module and integrate i…
mcagnion Mar 21, 2026
1c22786
test(weighted-score): add WeightedScore test coverage
mcagnion Mar 21, 2026
76da1ab
feat(weighted-score): add WeightedScore sort support to anoint panel
mcagnion Mar 22, 2026
d131886
feat(weighted-score): add getValue to WeightedScore powerStatList entry
mcagnion Mar 22, 2026
7269245
fix(weighted-score): cache WeightedScore module load in Data.lua
mcagnion Mar 23, 2026
693a2a2
fix(weighted-score): support getValue in generateFallbackWeights
mcagnion Mar 24, 2026
22ba8ed
fix(weighted-score): propagate getValue into fallbackWeightsList entries
mcagnion Mar 24, 2026
f658951
fix(trade): initialize stat weights before opening editor
mcagnion Mar 28, 2026
d5dd43c
feat(weighted-score): show weighted score in Power Report
mcagnion May 9, 2026
78a581f
refactor(weighted-score): unify Edit Weights affordance via dropdown …
mcagnion May 10, 2026
6abb446
fix(weighted-score): preserve output stat semantics
mcagnion Jul 26, 2026
4d6aa30
fix(weighted-score): limit sorting to supported surfaces
mcagnion Jul 26, 2026
4d49bf9
fix(weighted-score): preserve Item DB ranking
mcagnion Jul 26, 2026
bbe974e
fix(weighted-score): preserve comparison context
mcagnion Jul 26, 2026
699de1f
test(weighted-score): cover combined stat weights
mcagnion Jul 26, 2026
31fe642
refactor(weighted-score): share default weights
mcagnion Jul 26, 2026
6ab3c64
fix(weighted-score): use Full DPS for anoint ranking
mcagnion Jul 26, 2026
86a3bc4
fix(weighted-score): persist weight edits
mcagnion Jul 26, 2026
0e9c4e1
fix(weighted-score): place score last in menus
mcagnion Jul 28, 2026
3b093e2
fix(weighted-score): place weight editor after score
mcagnion Jul 28, 2026
3f90e45
feat(weighted-score): sort item modifiers by score
mcagnion Jul 28, 2026
5f40766
fix(weighted-score): preserve refresh callback after reset
mcagnion Aug 6, 2026
fc8f530
Fix weighted score sorting for crafted affixes
mcagnion Aug 6, 2026
37efced
Add weighted score editing to remaining selectors
mcagnion Aug 7, 2026
c7fe989
Adapt weighted score tests to current class syntax
mcagnion Aug 15, 2026
051f4cb
Simplify weighted score consumer plumbing
mcagnion Aug 20, 2026
b016206
Clarify weighted score calculation semantics
mcagnion Aug 20, 2026
c7949fc
Merge origin/dev into weighted score
mcagnion Aug 20, 2026
ad7bf52
Centralize weighted score editor routing
mcagnion Aug 20, 2026
198fd2f
Compact weighted score test contracts
mcagnion Aug 20, 2026
8ece022
Extract Power Report fix from weighted score
mcagnion Aug 21, 2026
22b2edd
Clarify weighted score semantic contracts
mcagnion Aug 21, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions spec/System/TestAbyssTimelessJewel_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,42 @@ describe("Abyss timeless jewels", function()
assert.matches("abyss_special_small_attribute25, 1, 0, 0", build.timelessData.searchListFallback, nil, true)
end)

it("exposes weight editing beside the Weighted Score fallback mode", function()
build.timelessData.jewelType = { id = 11 }
build.timelessData.conquerorType = { }
build.timelessData.jewelSocket = { id = 26196 }
build.itemsTab.tradeQuery.statSortSelectionList = {
{ stat = "FullDPS", label = "Full DPS", weightMult = 1 },
}
build.treeTab:FindTimelessJewel()
local control = main.popups[1].controls.fallbackWeightsList
local weightedIndex
local weightedCount = 0
for index, entry in ipairs(control.list) do
if entry.stat == "WeightedScore" then
weightedIndex = index
weightedCount = weightedCount + 1
end
end
assert.are.equal(1, weightedCount)
assert.is_truthy(weightedIndex)
assert.is_function(control.list[weightedIndex + 1].action)
assert.is_true(data.powerStatList.RequiresFullDPS(control.list[weightedIndex], build))

local opened = false
build.itemsTab.tradeQuery.SetStatWeights = function()
opened = true
end
control:SetSel(weightedIndex)
for char in ("Edit"):gmatch(".") do
control:OnSearchChar(char)
end
control:SetSel(1)

assert.is_true(opened)
assert.are.equal("WeightedScore", control:GetSelValue().stat)
end)

it("reads Zorath seed 6564 node and Inquisitor ascendancy changes", function()
data.timelessJewelLUTs[11] = parseAbyssJewel(11, zorathExampleData())
local expected = {
Expand Down
104 changes: 104 additions & 0 deletions spec/System/TestItemDBControl_spec.lua
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
describe("ItemDBControl", function()
local originalGetCursorPos
local function findPowerStat(statName)
for _, stat in ipairs(data.powerStatList) do
if stat.stat == statName then
return stat
end
end
end

before_each(function()
originalGetCursorPos = GetCursorPos
Expand Down Expand Up @@ -62,6 +69,103 @@ describe("ItemDBControl", function()
assert.are.equal(-math.huge, invalidItem.measuredPower)
end)

it("preserves negative WeightedScore results and skips unneeded FullDPS", function()
local function makeItem(name)
return {
name = name,
base = {},
enchantModLines = {},
implicitModLines = {},
explicitModLines = {},
baseModList = {},
}
end
local betterItem = makeItem("Better Item")
local worseItem = makeItem("Worse Item")
local invalidItem = makeItem("Invalid Item")
local takenDamage = {
[betterItem] = 80,
[worseItem] = 120,
}
local requestedFullDPS = { }
local itemsTab = {
activeItemSet = { useSecondWeaponSet = false },
slots = { ["Body Armour"] = {} },
tradeQuery = {
statSortSelectionList = {
{ stat = "PhysicalTakenHit", weightMult = 1, transform = function(value) return -value end },
},
},
IsItemValidForSlot = function(_, item)
return item ~= invalidItem
end,
}
itemsTab.build = {
itemsTab = itemsTab,
calcsTab = {
GetMiscCalculator = function()
return function(args, useFullDPS)
table.insert(requestedFullDPS, useFullDPS)
return { PhysicalTakenHit = takenDamage[args.repItem] }
end, { PhysicalTakenHit = 100 }
end,
},
}
local control = new("ItemDBControl"):ItemDBControl(nil, { 0, 0, 100, 100 }, itemsTab, {
list = { invalidItem, betterItem, worseItem },
}, "RARE")
control.sortDetail = copyTable(findPowerStat("WeightedScore"))
control.sortOrder = { control.sortControl.STAT, control.sortControl.NAME }

control:ListBuilder()

assert.are.equal(betterItem, control.list[1])
assert.are.equal(worseItem, control.list[2])
assert.are.equal(invalidItem, control.list[3])
assert.is_true(betterItem.measuredPower < 0)
assert.is_true(worseItem.measuredPower < betterItem.measuredPower)
assert.are.equal(-math.huge, invalidItem.measuredPower)
assert.are.same({ false, false }, requestedFullDPS)
end)

it("requests FullDPS for WeightedScore when active weights need it", function()
local item = {
name = "Full DPS Item",
base = {},
enchantModLines = {},
implicitModLines = {},
explicitModLines = {},
baseModList = {},
}
local requestedFullDPS = { }
local itemsTab = {
activeItemSet = { useSecondWeaponSet = false },
slots = { ["Body Armour"] = {} },
tradeQuery = { statSortSelectionList = { { stat = "FullDPS", weightMult = 1 } } },
IsItemValidForSlot = function()
return true
end,
}
itemsTab.build = {
itemsTab = itemsTab,
calcsTab = {
GetMiscCalculator = function()
return function(_, useFullDPS)
table.insert(requestedFullDPS, useFullDPS)
return { FullDPS = 120 }
end, { FullDPS = 100 }
end,
},
}
local control = new("ItemDBControl"):ItemDBControl(nil, { 0, 0, 100, 100 }, itemsTab, { list = { item } }, "RARE")
control.sortDetail = copyTable(findPowerStat("WeightedScore"))
control.sortOrder = { control.sortControl.STAT, control.sortControl.NAME }

control:ListBuilder()

assert.are.same({ true }, requestedFullDPS)
end)

it("searches Foulborn modifier text without case sensitivity", function()
local item = new("Item"):Item([[
Rarity: Unique
Expand Down
45 changes: 45 additions & 0 deletions spec/System/TestNotableDBControl_spec.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
describe("NotableDBControl", function()
it("requests FullDPS when sorting by WeightedScore with FullDPS weights", function()
local notable = {
dn = "Full DPS Notable",
sd = {},
recipe = { "Amber Oil" },
modKey = "NotableFullDPS",
}
local requestedFullDPS = {}
local itemsTab = {
displayItem = { base = { type = "Amulet" } },
tradeQuery = { statSortSelectionList = { { stat = "FullDPS", weightMult = 1 } } },
anointItem = function(_, node)
return node
end,
}
itemsTab.build = {
itemsTab = itemsTab,
calcsTab = {
GetMiscCalculator = function()
return function(args, useFullDPS)
table.insert(requestedFullDPS, useFullDPS)
return { FullDPS = args.repItem and 120 or 100 }
end
end,
},
}
local control = new("NotableDBControl"):NotableDBControl(nil, { 0, 0, 100, 100 }, itemsTab, { [1] = notable }, "ANOINT")
for _, stat in ipairs(data.powerStatList) do
if stat.stat == "WeightedScore" then
control.sortDetail = copyTable(stat)
break
end
end
control.sortOrder = { control.sortControl.STAT, control.sortControl.NAME }

control:ListBuilder()

assert.are.same({ true, true }, requestedFullDPS)
assert.are.equal(notable, control.list[1])
assert.is_true(notable.measuredPower > 0)
assert.are.equal(notable.measuredPower, control.sortMaxPower)
assert.are.equal("^xFF8080Full DPS Notable", control:GetRowValue(1, 1, notable))
end)
end)
56 changes: 56 additions & 0 deletions spec/System/TestTradeQuery_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -114,4 +114,60 @@ describe("TradeQuery", function()
assert.are.equals(1.2, result)
end)
end)

describe("SetStatWeights", function()
it("marks the build modified after saving changed weights", function()
local capturedControls
local originalOpenPopup = main.OpenPopup
local originalClosePopup = main.ClosePopup
main.OpenPopup = function(_, _, _, _, controls)
capturedControls = controls
end
main.ClosePopup = function() end

local itemsTab = {}
local tradeQuery = new("TradeQuery"):TradeQuery(itemsTab)
local ok, errMsg = pcall(function()
tradeQuery:SetStatWeights()
for _, entry in ipairs(capturedControls.ListControl.list) do
if entry.stat.stat == "FullDPS" then
entry.stat.weightMult = 0.75
break
end
end
capturedControls.finalise.onClick()
end)
main.OpenPopup = originalOpenPopup
main.ClosePopup = originalClosePopup

assert.is_true(ok, errMsg)
assert.is_true(itemsTab.modFlag)
assert.are.equal(0.75, tradeQuery.statSortSelectionList[1].weightMult)
end)

it("preserves the save callback after resetting weights", function()
local capturedControls
local originalOpenPopup = main.OpenPopup
local originalClosePopup = main.ClosePopup
main.OpenPopup = function(_, _, _, _, controls)
capturedControls = controls
end
main.ClosePopup = function() end

local callbackCount = 0
local ok, errMsg = pcall(function()
local tradeQuery = new("TradeQuery"):TradeQuery({})
tradeQuery:SetStatWeights(nil, function()
callbackCount = callbackCount + 1
end)
capturedControls.reset.onClick()
capturedControls.finalise.onClick()
end)
main.OpenPopup = originalOpenPopup
main.ClosePopup = originalClosePopup

assert.is_true(ok, errMsg)
assert.are.equal(1, callbackCount)
end)
end)
end)
Loading
Loading