Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
72 commits
Select commit Hold shift + click to select a range
538e2fb
Port unique updater scripts from PoB2
EtherealCarnivore Feb 21, 2026
70a87af
Add generated mod data and intermediate export files
EtherealCarnivore Feb 21, 2026
b01d5f3
Update unique item data from GGPK export
EtherealCarnivore Feb 21, 2026
be97f4b
Rename minz/maxz to minZ/maxZ for spellchecking
EtherealCarnivore Feb 22, 2026
d6aee35
Make scripts more robust and fix tags for PoE1
Wires77 Mar 3, 2026
109497d
Revert "Update unique item data from GGPK export"
Wires77 Mar 3, 2026
945c600
Update uTextToMods to be much more robust
Wires77 Mar 3, 2026
31ef9fe
Small script improvements + axe uniques
Wires77 Mar 4, 2026
82eeae0
Exclude all itemTypes from uTextToMods to prevent accidents
Wires77 Mar 4, 2026
713aaef
bows
Wires77 Mar 4, 2026
45de6ce
Missed some axe mods
Wires77 Mar 4, 2026
d49b5f4
Fix numerical errors after export
Wires77 Mar 4, 2026
34b8c6e
Claws
Wires77 Mar 4, 2026
b756039
Daggers
Wires77 Mar 4, 2026
adde8f2
Fishing rods, maces
Wires77 Mar 4, 2026
732d915
Hopefully final bugfix and rewrite
Wires77 Mar 4, 2026
40fb2b7
Staves
Wires77 Mar 4, 2026
e8c8982
Swords
Wires77 Mar 4, 2026
268d4e6
Wands
Wires77 Mar 4, 2026
ec5e1f0
Body armour, boots, gloves
EtherealCarnivore Mar 4, 2026
1fd34ac
Jewels
EtherealCarnivore Mar 5, 2026
b48067d
Re-export body, boots, gloves, jewel via uTextToMods + uModsToText
EtherealCarnivore Mar 5, 2026
49ac1b8
Re-export belt via uTextToMods + uModsToText
EtherealCarnivore Mar 5, 2026
1e26e18
Fix spelling for linesBackward
Wires77 Mar 5, 2026
01480b6
Revert "Re-export belt via uTextToMods + uModsToText"
EtherealCarnivore Mar 5, 2026
5e9b64e
Fix decimal range parsing in uModsToText
EtherealCarnivore Mar 5, 2026
241fb2e
Re-export belt via uTextToMods + uModsToText COPIUM
EtherealCarnivore Mar 5, 2026
1c9bbb2
Forgot to commit the staff export
Wires77 Mar 5, 2026
660189c
Skip continuation lines after resolving multi-line mods
EtherealCarnivore Mar 5, 2026
7c5c0c4
Merge branch 'port-unique-updater-scripts' of https://github.com/Ethe…
Wires77 Mar 5, 2026
311399e
Helmets
Wires77 Mar 5, 2026
1c61e40
Manually fix enemy aura mods for now
Wires77 Mar 5, 2026
4be938f
Fix multi-line mod handling in export round-trip
EtherealCarnivore Mar 5, 2026
92c085f
Revert ModItemExclusive.lua changes - generated file, fix in unique f…
EtherealCarnivore Mar 5, 2026
c1228ca
Add ConvertMod to ModStore/ModList/ModDB
EtherealCarnivore Mar 5, 2026
a3445ca
Revert "Add ConvertMod to ModStore/ModList/ModDB"
EtherealCarnivore Mar 5, 2026
d5176f6
Rings
Wires77 Mar 5, 2026
d83a2d6
Update export script to support multi-line implicit mods
PJacek Mar 4, 2026
5ae94e3
Tinctures
PJacek Mar 4, 2026
9e4545a
Flasks
PJacek Mar 5, 2026
b3385a0
Fix rings
PJacek Mar 6, 2026
bbf339c
Add missing implicit line on Grattus Signet
Wires77 Mar 6, 2026
61972b2
Update exporter
PJacek Mar 6, 2026
b750d17
Regenerate mods
PJacek Mar 6, 2026
0fd0704
Jewels
PJacek Mar 6, 2026
f060736
Merge branch 'port-unique-updater-scripts' of https://github.com/Ethe…
Wires77 Mar 7, 2026
9e0c915
Amulets, haven't checked over the text changes yet
Wires77 Mar 7, 2026
3636c5b
Amulet mod value updates
Wires77 Mar 8, 2026
088e281
Export amulets after fix
Wires77 Mar 8, 2026
2c1a451
Belts
PJacek Mar 7, 2026
d009120
Quivers
PJacek Mar 9, 2026
5d726e5
Update export to handle spaces in mod names
PJacek Mar 9, 2026
e422fa6
Shields
PJacek Mar 9, 2026
1dee959
Body
Wires77 Mar 14, 2026
57a7189
Boots
PJacek Mar 19, 2026
aa74b91
Gloves, text to mod
Wires77 Mar 21, 2026
7cb7b66
Export gloves text
Wires77 Mar 22, 2026
3eb9d25
Merge remote-tracking branch 'pjacek/port-unique-updater' into port-u…
Wires77 Mar 22, 2026
0f24294
Merge remote-tracking branch 'fork/dev' into port-unique-updater-scripts
Wires77 Mar 22, 2026
1548524
New Rings
Wires77 Mar 22, 2026
938f038
New items, mod ordering and small fixes for weapons
Wires77 Mar 24, 2026
ab17850
New armor items
Wires77 Mar 24, 2026
0b385fa
Final fixes and updates for jewels, jewellery, and consumables
Wires77 Mar 24, 2026
32a831a
Fix typo
Wires77 Mar 24, 2026
b05af61
Re-export exclusive mods
Wires77 Mar 24, 2026
b4a97a3
Export ModCache
Wires77 Mar 24, 2026
1da5ae3
Merge branch 'dev' into port-unique-updater-scripts
Apr 12, 2026
1ee9973
Merge branch 'dev' into port-unique-updater-scripts
Apr 12, 2026
1c2d790
Fix Seething Fury export
Apr 12, 2026
73ce884
Fix export of aura mods
Apr 12, 2026
b602b8f
Remove old code for removing BuffTemplate stats
Apr 12, 2026
7b09e3f
Update ModCache
Apr 12, 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
28 changes: 14 additions & 14 deletions src/Classes/Item.lua
Original file line number Diff line number Diff line change
Expand Up @@ -104,12 +104,12 @@ local function getTagBasedModifiers(tagName, itemSlotName)
if data.itemTagSpecial[tagName] and data.itemTagSpecial[tagName][itemSlotName] then
for _, specialMod in ipairs(data.itemTagSpecial[tagName][itemSlotName]) do
if dv:lower():find(specialMod:lower()) then
exclude = true
excluded = true
break
end
end
end
if exclude then
if excluded then
found = true
break
end
Expand All @@ -131,12 +131,12 @@ local function getTagBasedModifiers(tagName, itemSlotName)
if data.itemTagSpecial[tagName] and data.itemTagSpecial[tagName][itemSlotName] then
for _, specialMod in ipairs(data.itemTagSpecial[tagName][itemSlotName]) do
if dv:lower():find(specialMod:lower()) then
exclude = true
excluded = true
break
end
end
end
if exclude then
if excluded then
found = true
break
end
Expand Down Expand Up @@ -444,16 +444,16 @@ function ItemClass:ParseRaw(raw, rarity, highQuality)
end
end
elseif specName == "Radius" and self.type == "Jewel" then
self.jewelRadiusLabel = specVal:match("^%a+")
if specVal:match("^%a+") == "Variable" then
-- Jewel radius is variable and must be read from it's mods instead after they are parsed
deferJewelRadiusIndexAssignment = true
else
for index, data in pairs(data.jewelRadius) do
if specVal:match("^%a+") == data.label then
self.jewelRadiusIndex = index
break
end
self.jewelRadiusLabel = specVal:match("^[%a ]+")
if specVal:match("^[%a ]+") == "Variable" then
-- Jewel radius is variable and must be read from it's mods instead after they are parsed
deferJewelRadiusIndexAssignment = true
else
for index, data in pairs(data.jewelRadius) do
if specVal:match("^[%a ]+") == data.label then
self.jewelRadiusIndex = index
break
end
end
end
elseif specName == "Limited to" and self.type == "Jewel" then
Expand Down
8,550 changes: 8,550 additions & 0 deletions src/Data/ModItemExclusive.lua
Comment thread
Wires77 marked this conversation as resolved.

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion src/Data/Uniques/amulet.lua
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ Implicits: 1
{variant:1}{tags:life}(30-40) Life gained when you Block
{variant:2,3,4}{tags:life}(34-48) Life gained when you Block
{variant:1}{tags:mana}(10-20) Mana gained when you Block
{variant:2,3,4}{tags:mana}(10-24) Mana gained when you Block
{variant:2,3,4}{tags:mana}(18-24) Mana gained when you Block
{variant:1}{tags:speed}20% reduced Movement Speed
{variant:2}{tags:speed}10% reduced Movement Speed
+3% to maximum Chance to Block Attack Damage
Expand Down
48 changes: 43 additions & 5 deletions src/Export/Scripts/mods.lua
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,6 @@ local function writeMods(outName, condFunc)
print("[Jewel]: Skipping '" .. mod.Id .. "'")
goto continue
end
elseif mod.Family[1] and mod.Family[1].Id ~= "AuraBonus" and mod.Family[1].Id ~= "ArbalestBonus" and mod.GenerationType == 3 and not (mod.Domain == 16 or (mod.Domain == 1 and mod.Id:match("^Synthesis") or mod.Id:match("^MutatedUnique") or (mod.Family[2] and mod.Family[2].Id:match("MatchedInfluencesTier")))) then
goto continue
end
local stats, orders = describeMod(mod)
if #orders > 0 then
Expand Down Expand Up @@ -137,8 +135,10 @@ end

writeMods("../Data/ModItem.lua", function(mod)
return (mod.Domain == 1 or mod.Domain == 16)
and (mod.GenerationType == 1 or mod.GenerationType == 2 or (mod.GenerationType == 3 and (not mod.Id:match("^MutatedUnique")) and (mod.Id:match("^Synthesis") or (mod.Family[1].Id ~= "AuraBonus" and mod.Family[1].Id ~= "ArbalestBonus") and not (mod.Family[2] and mod.Family[2].Id:match("MatchedInfluencesTier")))) or mod.GenerationType == 5
or mod.GenerationType == 25 or mod.GenerationType == 24 or mod.GenerationType == 28 or mod.GenerationType == 29)
and (mod.GenerationType == 1 or mod.GenerationType == 2
or (mod.GenerationType == 3 and mod.Domain == 1 and mod.Id:match("^Synthesis"))
or (mod.GenerationType == 3 and mod.Domain == 16)
or mod.GenerationType == 5 or mod.GenerationType == 25 or mod.GenerationType == 24 or mod.GenerationType == 28 or mod.GenerationType == 29)
and not mod.Id:match("^Hellscape[UpDown]+sideMap") -- Exclude Scourge map mods
and not mod.Id:match("Royale")
and #mod.AuraFlags == 0
Expand All @@ -147,7 +147,7 @@ writeMods("../Data/ModFlask.lua", function(mod)
return mod.Domain == 2 and (mod.GenerationType == 1 or mod.GenerationType == 2)
end)
writeMods("../Data/ModTincture.lua", function(mod)
return (mod.Domain == 34) and (mod.GenerationType == 1 or mod.GenerationType == 2 or mod.GenerationType == 3)
return (mod.Domain == 34) and (mod.GenerationType == 1 or mod.GenerationType == 2)
end)
writeMods("../Data/ModJewel.lua", function(mod)
return (mod.Domain == 10 or mod.Domain == 16) and (mod.GenerationType == 1 or mod.GenerationType == 2 or mod.GenerationType == 5)
Expand All @@ -173,6 +173,11 @@ end)
writeMods("../Data/ModNecropolis.lua", function(mod)
return mod.Domain == 1 and mod.Id:match("^NecropolisCrafting")
end)
writeMods("../Data/ModItemExclusive.lua", function(mod) -- contains primarily uniques and items implicits but also other mods only available on a single base or unique.
return (mod.Domain == 1 or mod.Domain == 2 or mod.Domain == 10 or mod.Domain == 21 or mod.Domain == 34) and mod.GenerationType == 3
and (mod.Family[1] and mod.Family[1].Id ~= "AuraBonus")
and not mod.Id:match("^Synthesis") and not mod.Id:match("Royale") and not mod.Id:match("Cowards") and not mod.Id:match("Map") and not mod.Id:match("Ultimatum")
end)
writeMods("../Data/ModGraft.lua", function(mod)
return mod.Domain == 38 and (mod.GenerationType == 1 or mod.GenerationType == 2 or mod.GenerationType == 5)
end)
Expand All @@ -183,4 +188,37 @@ writeMods("../Data/ModFoulborn.lua", function(mod)
return mod.Domain == 1 and mod.GenerationType == 3 and mod.Id:match("^MutatedUnique")
end)

-- Generate unique mod mappings from text to mod
local out = io.open("Uniques/ModTextMap.lua", "w")
local modTextMap = {}
out:write('-- This file is automatically generated, do not edit!\n')
out:write('-- Item data (c) Grinding Gear Games\n\nreturn {\n')
for modName, mod in pairs(LoadModule("../Data/ModItemExclusive.lua")) do
if modTextMap[mod[1]:lower()] then
table.insert(modTextMap[mod[1]:lower()], modName)
else
modTextMap[mod[1]:lower()] = { modName }
end
-- Add generic mod for matching legacy values
if mod[1]:match('%d+') then
local genericText = mod[1]:gsub('(%-*%d*%.*%d+%-*%-*%d*%.*%d+)', '#')
if genericText ~= mod[1] then
if modTextMap[genericText:lower()] then
table.insert(modTextMap[genericText:lower()], modName)
else
modTextMap[genericText:lower()] = { modName }
end
end
end
end
for key, value in pairs(modTextMap) do
out:write('\t["' .. key .. '"] = { ')
for _, modName in ipairs(value) do
out:write('"' .. modName .. '", ')
end
out:write('},\n')
end
out:write('\n}')
out:close()

print("Mods exported.")
178 changes: 178 additions & 0 deletions src/Export/Scripts/uModsToText.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
if not table.containsId then
dofile("Scripts/mods.lua")
end
local catalystTags = {
["elemental_damage"] = true,
["caster"] = true,
["attack"] = true,
["defences"] = true,
["resource"] = true,
["resistance"] = true,
["attribute"] = true,
["physical_damage"] = true,
["chaos_damage"] = true,
["speed"] = true,
["critical"] = true,
}
local itemTypes = {
-- "axe",
-- "bow",
-- "claw",
-- "dagger",
-- "fishing",
-- "mace",
-- "staff",
-- "sword",
-- "wand",
-- "helmet",
-- "body",
-- "gloves",
-- "boots",
-- "shield",
-- "quiver",
"amulet",
-- "ring",
-- "belt",
-- "jewel",
-- "flask",
-- "tincture",
}
local function writeMods(out, statOrder)
local orders = { }
for order, _ in pairs(statOrder) do
table.insert(orders, order)
end
table.sort(orders)
for _, order in pairs(orders) do
for _, line in ipairs(statOrder[order]) do
out:write(line, "\n")
end
end
end

local uniqueMods = LoadModule("../Data/ModItemExclusive.lua")
for _, name in ipairs(itemTypes) do
local out = io.open("../Data/Uniques/"..name..".lua", "w")
local statOrder = {}
local postModLines = {}
local modLines = 0
local implicits
local nextOrder = 100000
for line in io.lines("Uniques/"..name..".lua") do
if implicits then -- remove 1 downs to 0
implicits = implicits - 1
end
local specName, specVal = line:match("^([%a ]+): (.+)$")
if line:match("]],") then -- start new unique
writeMods(out, statOrder)
for _, line in ipairs(postModLines) do
out:write(line, "\n")
end
out:write(line, "\n")
statOrder = { }
postModLines = { }
modLines = 0
nextOrder = 100000
elseif not specName then
local prefix = ""
local variantString = line:match("({variant:[%d,]+})")
local fractured = line:match("({fractured})") or ""
local cleanLine = line:gsub("{.-}", "")
-- Check if this is a mod ID: purely alphanumeric+underscore, optionally followed by [num,num] ranges
local modName = cleanLine:match("^([%a%d_]+)%[") or cleanLine:match("^([%a%d_]+)$")
local legacy = modName and cleanLine:sub(#modName + 1) or ""
-- Legacy ranges must contain actual brackets, not just stray characters
if legacy ~= "" and not legacy:match("%[") then
legacy = ""
modName = nil
end
local mod = modName and uniqueMods[modName]
if mod or (modName and legacy ~= "") then
modLines = modLines + 1
if variantString then
prefix = prefix ..variantString
end

local tags = {}
if mod then
if isValueInArray({"amulet", "ring", "belt"}, name) then
for _, tag in ipairs(mod.modTags) do
if catalystTags[tag] then
table.insert(tags, tag)
end
end
end
end
if tags[1] then
prefix = prefix.."{tags:"..table.concat(tags, ",").."}"
end
prefix = prefix..fractured
local legacyMod
if legacy ~= "" then
local values = { }
for range in legacy:gmatch("%b[]") do
local min, max = range:match("%[([%d%-]+),([%d%-]+)%]")
table.insert(values, { min = tonumber(min), max = tonumber(max) })
end
local mod = dat("Mods"):GetRow("Id", modName)
if mod then
local stats = { }
for i = 1, 6 do
if mod["Stat"..i] then
stats[mod["Stat"..i].Id] = values[i]
end
end
if mod.Type then
stats.Type = mod.Type
end
legacyMod = describeStats(stats)
else
ConPrintf("Warning: Could not find mod data for legacy mod '%s' in %s", modName, name)
end
end
local modText = legacyMod or mod
if modText then
for i, line in ipairs(modText) do
local order = mod and mod.statOrder and mod.statOrder[i] or (nextOrder)
nextOrder = nextOrder + 1
if statOrder[order] then
table.insert(statOrder[order], prefix..line)
else
statOrder[order] = { prefix..line }
end
end
end
else
if modLines > 0 then -- treat as post line e.g. mirrored, or unresolved text mod
-- Unresolved text lines get a sequential order to preserve position among mods
if statOrder[nextOrder] then
table.insert(statOrder[nextOrder], line)
else
statOrder[nextOrder] = { line }
end
nextOrder = nextOrder + 1
else
out:write(line, "\n")
end
end
else
if specName == "Implicits" then
implicits = tonumber(specVal)
end
out:write(line, "\n")
end
if implicits and implicits == 0 then
writeMods(out, statOrder)
implicits = nil
statOrder = { }
modLines = 0
end
end
writeMods(out, statOrder)
for _, line in ipairs(postModLines) do
out:write(line, "\n")
end
out:close()
end

print("Unique text updated.")
Loading