From 18b04fdf0f6a5366ab79ff02398e60803eaae563 Mon Sep 17 00:00:00 2001 From: Wires77 Date: Tue, 24 Mar 2026 10:58:55 -0500 Subject: [PATCH 1/3] Add initial test items to feel out the format --- spec/System/TestItemParse_spec.lua | 148 +++++++++++++++++++++++++++++ 1 file changed, 148 insertions(+) diff --git a/spec/System/TestItemParse_spec.lua b/spec/System/TestItemParse_spec.lua index 4890207cea..ba8b50939f 100644 --- a/spec/System/TestItemParse_spec.lua +++ b/spec/System/TestItemParse_spec.lua @@ -465,3 +465,151 @@ describe("TestItemParse", function() assert.are.equal("+1500 to Armour", item.buffModLines[1].line) end) end) + +describe("TestAdvancedItemParse", function() + it("parses item", function() + local advancedItem = new("Item", [[ + Item Class: Belts + Rarity: Rare + Beast Snare + Cord Belt + -------- + Requirements: + Level: 51 + -------- + Item Level: 83 + -------- + Allocates Surveillance (enchant) + -------- + { Implicit Modifier } + Can be Anointed + -------- + { Fractured Prefix Modifier "Thorny" (Tier: 2) — Damage, Physical } + Reflects 3(1-4) Physical Damage to Melee Attackers + { Prefix Modifier "Fecund" (Tier: 1) — Life } + +142(130-144) to maximum Life + { Prefix Modifier "Glowing" (Tier: 9) — Defences, Energy Shield } + +15(13-15) to maximum Energy Shield + { Suffix Modifier "of the Tempest" (Tier: 4) — Elemental, Lightning, Resistance } + +34(30-35)% to Lightning Resistance + { Master Crafted Suffix Modifier "of Craft" (Rank: 3) — Elemental, Cold, Resistance } + +35(29-35)% to Cold Resistance + -------- + Fractured Item + ]]) + + local equivalentCraftItem = new("Item", [[ + Beast Snare + Cord Belt + Crafted: true + Prefix: {range:0.599}AttackerTakesDamage1 + Prefix: {range:0.859}IncreasedLife9 + Prefix: {range:0.845}IncreasedEnergyShield4 + Suffix: {range:0.732}LightningResist5 + Suffix: None + Suffix: None + LevelReq: 51 + Implicits: 1 + {crafted}Allocates Surveillance + Can be Anointed + +15 to maximum Energy Shield + +142 to maximum Life + +34% to Lightning Resistance + Reflects 3 Physical Damage to Melee Attackers + {tags:elemental,cold,resistance}{crafted}{range:1}+(29-35)% to Cold Resistance + ]]) + + local catalyst = new("Item", [[ + Item Class: Amulets + Rarity: Unique + Astramentis + Onyx Amulet + -------- + Quality (Attribute Modifiers): +20% (augmented) + -------- + Requirements: + Level: 20 + -------- + Item Level: 80 + -------- + Allocates Weathered Hunter (enchant) + -------- + { Implicit Modifier — Attribute — 20% Increased } + +16(10-16) to all Attributes + (Attributes are Strength, Dexterity, and Intelligence) + -------- + { Unique Modifier — Attribute — 20% Increased } + +86(80-100) to all Attributes + (Attributes are Strength, Dexterity, and Intelligence) + { Unique Modifier — Physical, Attack } + -4 Physical Damage taken from Attack Hits + -------- + Mindless rage will shake the world, + Cunning lies will bend it. + Reckless haste will break the world, + And into darkness send it. + -------- + Note: ~b/o 50 chaos + ]]) + local godTestItem = new("Item", [[ + Item Class: Sceptres + Rarity: Unique + Nebulis + Synthesised Void Sceptre + -------- + Sceptre + Physical Damage: 50-76 + Critical Strike Chance: 7.30% + Attacks per Second: 1.25 + Weapon Range: 1.1 metres + Memory Strands: 58 + -------- + Requirements: + Level: 68 + Str: 104 + Int: 122 + -------- + Sockets: B R + -------- + Item Level: 87 + -------- + +30% to Fire Resistance (scourge) + 22% reduced Global Defences (scourge) + (Armour, Evasion Rating and Energy Shield are the standard Defences) (scourge) + -------- + 8% increased Explicit Cold Modifier magnitudes (enchant) + Has 1 White Socket (enchant) + -------- + { Searing Exarch Implicit Modifier (Lesser) } + Tempest Shield has 15(15-17)% increased Buff Effect + { Implicit Modifier — Damage, Critical — 106% Increased } + +15(15-17)% to Global Critical Strike Multiplier + -------- + { Prefix Modifier "Freezing" (Tier: 5) — Damage, Elemental, Cold, Caster — 8% Increased } + Adds 17(16-20) to 35(30-36) Cold Damage to Spells + { Unique Modifier } + 106(60-120)% increased Implicit Modifier magnitudes — Unscalable Value + (Implicit Modifiers are those that come from an item's type, rather than its random properties) + { Master Crafted Suffix Modifier "of Craft" (Rank: 3) — Elemental, Cold, Resistance } + +35(29-35)% to Cold Resistance + { Fractured Prefix Modifier "Thorny" (Tier: 2) — Damage, Physical } + Reflects 3(1-4) Physical Damage to Melee Attackers + { Prefix Modifier "Veiled" } + Veiled Prefix + Searing Exarch Item + -------- + { Allocated Crucible Passive Skill (Tier: 2) } + Adds 2 to 6 Physical Damage to Spells + -------- + Synthesised Item + -------- + Corrupted + -------- + Scourged + -------- + Hinekora's Lock + -------- + Note: ~b/o 2 chaos + ]]) + end) +end) \ No newline at end of file From d04aee28c57467c9ea687860c537cb78b5beae9e Mon Sep 17 00:00:00 2001 From: Wires77 Date: Sun, 19 Apr 2026 07:23:06 -0500 Subject: [PATCH 2/3] Add actual test --- spec/System/TestItemParse_spec.lua | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/spec/System/TestItemParse_spec.lua b/spec/System/TestItemParse_spec.lua index ba8b50939f..f5cf416881 100644 --- a/spec/System/TestItemParse_spec.lua +++ b/spec/System/TestItemParse_spec.lua @@ -519,6 +519,8 @@ describe("TestAdvancedItemParse", function() {tags:elemental,cold,resistance}{crafted}{range:1}+(29-35)% to Cold Resistance ]]) + assert.are.equals(advancedItem:BuildRaw(), equivalentCraftItem:BuildRaw()) + local catalyst = new("Item", [[ Item Class: Amulets Rarity: Unique @@ -587,6 +589,12 @@ describe("TestAdvancedItemParse", function() -------- { Prefix Modifier "Freezing" (Tier: 5) — Damage, Elemental, Cold, Caster — 8% Increased } Adds 17(16-20) to 35(30-36) Cold Damage to Spells + { Prefix Modifier "Beetle's" (Tier: 6) — Defences, Armour } + 9(6-13)% increased Armour + 7(6-7)% increased Stun and Block Recovery + { Master Crafted Prefix Modifier "Upgraded" — Life, Defences, Armour } + 21(18-21)% increased Armour + +18(17-19) to maximum Life { Unique Modifier } 106(60-120)% increased Implicit Modifier magnitudes — Unscalable Value (Implicit Modifiers are those that come from an item's type, rather than its random properties) From 4e3e1d75957f73f008974babc68dd671963a0cbd Mon Sep 17 00:00:00 2001 From: Wires77 Date: Mon, 4 May 2026 00:10:08 -0500 Subject: [PATCH 3/3] Initial pass at parsing advanced copy/paste. Still need to handle unscaled values --- src/Classes/Item.lua | 54 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/src/Classes/Item.lua b/src/Classes/Item.lua index 4597dde52b..86ecb3f7da 100644 --- a/src/Classes/Item.lua +++ b/src/Classes/Item.lua @@ -369,6 +369,7 @@ function ItemClass:ParseRaw(raw, rarity, highQuality) local deferJewelRadiusIndexAssignment local gameModeStage = "FINDIMPLICIT" local foundExplicit, foundImplicit + local linePrefix = "" while self.rawLines[l] do local line = self.rawLines[l] @@ -398,7 +399,42 @@ function ItemClass:ParseRaw(raw, rarity, highQuality) self[influenceItemMap[line]] = true elseif line == "Requirements:" then -- nothing to do + elseif line:match("^{ ") then + -- We're parsing advanced copy/paste format + linePrefix = "" + self.crafted = true + local fullModName, modTags, increasedAmt = line:match("^{ (.-) %- (.-) %- (%d*).*}$") + if not fullModName then + fullModName, modTags = line:match("^{ (.-) %- (.-) }$") + end + if not fullModName then + fullModName = line:match("^{ (.-) }$") + end + local modName = fullModName:match("^.*Modifier \"(.*)\"") + if modName and modName ~= "" then + for modId, modData in pairs(self.affixes) do + if modData.affix == modName then + if modData.type == "Prefix" then + pendingAffix = { modId = modId, table = self.prefixes } + elseif modData.type == "Suffix" then + pendingAffix = { modId = modId, table = self.suffixes } + end + end + end + end + local possibleLineFlags = fullModName:match("(.*)Modifier.*") + if possibleLineFlags then + for flag in possibleLineFlags:gmatch("%a+") do + if lineFlags[flag:lower()] then + linePrefix = linePrefix .. "{" .. flag:lower() .. "}" + end + end + end + if modTags and modTags ~= "" then + linePrefix = linePrefix .. "{tags:" .. modTags:lower():gsub("%s+", "") .. "}" + end else + line = linePrefix .. line if self.checkSection then if gameModeStage == "IMPLICIT" then if foundImplicit then @@ -733,6 +769,24 @@ function ItemClass:ParseRaw(raw, rarity, highQuality) gameModeStage = "IMPLICIT" end local catalystScalar = getCatalystScalar(self.catalyst, modLine.modTags, self.catalystQuality) + for value, range in line:gmatch("(%d+)%((%d+%-%d+)%)") do + -- Find advanced copy paste format: 45(40-50) + if pendingAffix then + local min, max = range:match("(%d+)%-(%d+)") + local numRange = round((value - min) / (tonumber(max) - min), 3) + line = line:gsub(value .. "%(" .. range:gsub("%-", "%%-") .. "%)", value) + t_insert(pendingAffix.table, { + modId = pendingAffix.modId, + range = tonumber(numRange), + }) + pendingAffix = nil + else + local min, max = range:match("(%d+)%-(%d+)") + local numRange = round((value - min) / (tonumber(max) - min), 3) + modLine.range = tonumber(numRange) + line = line:gsub(value .. "%(" .. range:gsub("%-", "%%-") .. "%)", "(" .. range .. ")") + end + end local rangedLine = itemLib.applyRange(line, 1, catalystScalar) local modList, extra = modLib.parseMod(rangedLine) if (not modList or extra) and self.rawLines[l+1] then