55--
66local m_floor = math.floor
77local dkjson = require " dkjson"
8- local queryModsData
9- do
10- local queryModFile = io.open (" Data/QueryMods.lua" , " r" )
11- if queryModFile then
12- queryModFile :close ()
13- queryModsData = LoadModule (" Data/QueryMods" )
14- end
15- end
168
179local M = {}
1810
@@ -36,48 +28,11 @@ function M.modLineValue(line)
3628 return tonumber (line :match (" [%d]+%.?[%d]*" )) or 0
3729end
3830
39- -- Helper: lazily build a reverse lookup from QueryMods tradeMod.text → tradeMod.id
40- local _tradeModLookup = nil
41- local function getTradeModLookup ()
42- if _tradeModLookup then return _tradeModLookup end
43- _tradeModLookup = {}
44- if not queryModsData then return _tradeModLookup end
45- for _groupName , mods in pairs (queryModsData ) do
46- for _modKey , modData in pairs (mods ) do
47- if type (modData ) == " table" and modData .tradeMod then
48- local text = modData .tradeMod .text
49- local modType = modData .tradeMod .type or " explicit"
50- local id = modData .tradeMod .id
51- local key = text .. " |" .. modType
52- _tradeModLookup [key ] = id
53- if not _tradeModLookup [text ] then
54- _tradeModLookup [text ] = id
55- end
56- -- Also store with template-converted text for mods with literal numbers
57- -- (e.g. "1 Added Passive Skill is X" → "# Added Passive Skill is X")
58- local template = M .modLineTemplate (text )
59- if template ~= text then
60- local templateKey = template .. " |" .. modType
61- if not _tradeModLookup [templateKey ] then
62- _tradeModLookup [templateKey ] = id
63- end
64- if not _tradeModLookup [template ] then
65- _tradeModLookup [template ] = id
66- end
67- end
68- end
69- end
70- end
71- return _tradeModLookup
72- end
73-
74- -- Helper: lazily fetch and cache the trade API stats for comprehensive mod matching
75- -- Covers mods not in QueryMods.lua (cluster enchants, unique-specific mods, etc.)
76- local _tradeStatsLookup = nil
31+ -- Helper: fetch and cache the trade API stats
32+ local _tradeStats = nil
7733local _tradeStatsFetched = false
7834local function getTradeStatsLookup ()
79- if _tradeStatsFetched then return _tradeStatsLookup end
80- _tradeStatsFetched = true
35+ if _tradeStats then return _tradeStats end
8136 local tradeStats = " "
8237 local easy = common .curl .easy ()
8338 if not easy then return nil end
@@ -89,24 +44,10 @@ local function getTradeStatsLookup()
8944 end )
9045 local ok = easy :perform ()
9146 easy :close ()
92- if not ok or tradeStats == " " then return nil end
47+ if not ok or tradeStats == " " then return {} end
9348 local parsed = dkjson .decode (tradeStats )
94- if not parsed or not parsed .result then return nil end
95- _tradeStatsLookup = {}
96- for _ , category in ipairs (parsed .result ) do
97- local catLabel = category .label
98- for _ , entry in ipairs (category .entries ) do
99- local stripped = entry .text :gsub (" [#()0-9%-%+%.]" , " " )
100- local key = stripped .. " |" .. catLabel
101- if not _tradeStatsLookup [key ] then
102- _tradeStatsLookup [key ] = entry
103- end
104- if not _tradeStatsLookup [stripped ] then
105- _tradeStatsLookup [stripped ] = entry
106- end
107- end
108- end
109- return _tradeStatsLookup
49+ _tradeStats = parsed .result
50+ return _tradeStats
11051end
11152
11253-- Map source types used in OpenBuySimilarPopup to trade API category labels
@@ -116,50 +57,133 @@ M.sourceTypeToCategory = {
11657 [" enchant" ] = " Enchant" ,
11758}
11859
60+ function M .shouldBeInverted (tradeId , modLine , modType )
61+ local formattedLine = M .formatDatabaseText (M .formatDatabaseText (modLine ))
62+ for _ , category in ipairs (getTradeStatsLookup ()) do
63+ if category .id == modType then
64+ for _ , stat in ipairs (category .entries ) do
65+ if tradeId == stat .id then
66+ -- remove radius jewel extra text
67+ local formattedTradeSiteText = M .formatDatabaseText (stat .text )
68+ -- local modifiers don't seem to be inverted. same goes for
69+ -- the single stat that has (charm) in it
70+ if formattedTradeSiteText :match (" (Local)" ) or formattedTradeSiteText :match (" %(Charm%)$" ) then
71+ return false
72+ end
73+ -- trade site sometimes has a + sign, sometimes not
74+ return not (formattedLine == formattedTradeSiteText or formattedLine :gsub (" ^%+" , " " ) == formattedTradeSiteText )
75+ end
76+ end
77+ end
78+ end
79+ end
80+
81+ -- Helper: normalise data texts to # format
82+ function M .formatDatabaseText (text )
83+ -- decimal -> integer
84+ text = text :gsub (" %d+%.%d+" , " 1" )
85+ -- (123-124) -> #
86+ text = text :gsub (" %(%d+%-%d+%)" , " #" )
87+ text = text :gsub (" %d+" , " #" )
88+ -- remove radius jewel text. the same description is used for regular and
89+ -- radius jewels in the exports
90+ text = text :gsub (" ^Notable Passive Skills in Radius also grant " , " " )
91+ text = text :gsub (" ^Small Passive Skills in Radius also grant " , " " )
92+ return text
93+ end
94+
11995-- Helper: find the trade stat ID for a mod line
120- function M .findTradeModId (modLine , modType )
121- -- Try QueryMods-based lookup
122- local lookup = getTradeModLookup ()
123- local template = M .modLineTemplate (modLine )
124- -- Try exact match with type first
125- local key = template .. " |" .. modType
126- if lookup [key ] then
127- return lookup [key ]
96+ function M .findTradeHash (item , modLine , modType , isVeiled )
97+ local formattedLine = M .formatDatabaseText (modLine )
98+ -- the data export splits some mods into different parts, even though they
99+ -- are technically just one stat. we handle that here
100+ function findStat (dbMod , allowDefault )
101+ local excludeTags = (not allowDefault ) and { default = true } or nil
102+ if # dbMod .weightKey > 0 and not (item :GetModSpawnWeight (dbMod , nil , excludeTags ) > 0 ) then
103+ return nil
104+ end
105+ for tradeHash , description in pairs (dbMod .tradeHashes ) do
106+ for _ , line in ipairs (description ) do
107+ local dbFormatted = M .formatDatabaseText (line )
108+ if formattedLine == dbFormatted then
109+ return tradeHash
110+ end
111+ end
112+ end
128113 end
129- -- Try without leading +/- sign
130- local stripped = template :gsub (" ^[%+%-]" , " " )
131- key = stripped .. " |" .. modType
132- if lookup [key ] then
133- return lookup [key ]
114+
115+ -- implicit mods
116+ if modType == " implicit" then
117+ for _ , dbName in ipairs ({" Implicit" , " Synthesis" , " Eldritch" }) do
118+ for _ , dbMod in pairs (data .itemMods [dbName ]) do
119+ local tradeHashMaybe = findStat (dbMod )
120+ if tradeHashMaybe then
121+ return tradeHashMaybe
122+ end
123+ end
124+ end
134125 end
135- -- Fallback: match by template text only (any type)
136- if lookup [template ] then
137- return lookup [template ]
126+
127+ -- enchantments TODO
128+
129+ -- scourge mods
130+ if modType == " scourge" then
131+ for _ , dbMod in pairs (data .itemMods .Scourge ) do
132+ local tradeHashMaybe = findStat (dbMod )
133+ if tradeHashMaybe then
134+ return tradeHashMaybe
135+ end
136+ end
138137 end
139- if lookup [stripped ] then
140- return lookup [stripped ]
138+
139+ -- crucible mods
140+ -- TODO: add trade hash to these
141+ if modType == " crucible" then
142+ for _ , dbMod in pairs (data .crucible ) do
143+ local tradeHashMaybe = findStat (dbMod )
144+ if tradeHashMaybe then
145+ return tradeHashMaybe
146+ end
147+ end
141148 end
142149
143- -- Try trade API stats (covers mods not in QueryMods)
144- local tradeStats = getTradeStatsLookup ()
145- if tradeStats then
146- local strippedLine = modLine :gsub (" [#()0-9%-%+%.]" , " " )
147- local category = M .sourceTypeToCategory [modType ]
148- if category then
149- local catKey = strippedLine .. " |" .. category
150- if tradeStats [catKey ] then
151- return tradeStats [catKey ].id
150+ -- veiled mods
151+
152+ for _ , dbMod in pairs (data .veiledMods ) do
153+ local tradeHashMaybe = findStat (dbMod )
154+ if tradeHashMaybe then
155+ return tradeHashMaybe
156+ end
157+ end
158+ -- rest of the explicit mods
159+ for _ , dbName in ipairs ({ " Delve" , " Explicit" }) do
160+ for _ , dbMod in pairs (data .itemMods [dbName ]) do
161+ local tradeHashMaybe = findStat (dbMod )
162+ if tradeHashMaybe then
163+ return tradeHashMaybe
152164 end
153165 end
154- -- Fallback: any category
155- if tradeStats [strippedLine ] then
156- return tradeStats [strippedLine ].id
166+ end
167+
168+ for _ , dbMod in pairs (data .itemMods .Scourge ) do
169+ local tradeHashMaybe = findStat (dbMod )
170+ if tradeHashMaybe then
171+ return tradeHashMaybe
172+ end
173+ end
174+
175+ -- implicit mods
176+ if modType == " explicit" then
177+ for _ , dbMod in pairs (data .itemMods .Implicit ) do
178+ local tradeHashMaybe = findStat (dbMod )
179+ if tradeHashMaybe then
180+ return tradeHashMaybe
181+ end
157182 end
158183 end
159184
160- return nil
185+ -- TODO flask, graft, jewels
161186end
162-
163187-- Map slot name + item type to (trade API category string, itemCategoryTags key).
164188-- queryStr: e.g. "armour.shield", "weapon.onemace"
165189-- categoryLabel: e.g. "Shield", "1HMace", "1HWeapon" (nil for flask / generic jewel / unsupported)
0 commit comments