Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
274 changes: 274 additions & 0 deletions spec/System/TestItemParse_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -694,6 +694,235 @@ describe("TestAdvancedItemParse #item", function()
Note: ~b/o 2 chaos
]])
end)

it("parses allocated Crucible passive skills from advanced copy", function()
local item = new("Item", raw([[
{ Allocated Crucible Passive Skill (Tier: 1) }
-3% to Critical Strike Chance
+100% to Global Critical Strike Multiplier
{ Allocated Crucible Passive Skill (Tier: 1) }
Rampage
(You gain Rampage bonuses for Killing multiple Enemies in quick succession)
]], "Citadel Bow"))

assert.are.equals(3, #item.crucibleModLines)
assert.are.equals(0, #item.explicitModLines)
assert.are.same({
"-3% to Critical Strike Chance",
"+100% to Global Critical Strike Multiplier",
"Rampage",
}, {
item.crucibleModLines[1].line,
item.crucibleModLines[2].line,
item.crucibleModLines[3].line,
})
end)

it("ignores attribute requirements from socketed gems", function()
local item = new("Item", raw([[
Requirements:
Str: 126 (unmet)
Dex: 185 (unmet)
Int: 129 (unmet)
--------
Sockets: W-W-W-W-W-W
--------
Item Level: 83
]], "Citadel Bow"))

assert.are.same({ str = 0, dex = 185, int = 0 }, {
str = item.requirements.strMod,
dex = item.requirements.dexMod,
int = item.requirements.intMod,
})
end)

it("orders fractured mods first and crafted mods last", function()
local item = new("Item", raw([[
Item Level: 83
{ Fractured Prefix Modifier "Cheetah's" (Tier: 2) — Speed }
30% increased Movement Speed
{ Prefix Modifier "Athlete's" (Tier: 1) — Life }
+128(115-129) to maximum Life
{ Master Crafted Prefix Modifier "Upgraded" (Rank: 2) — Mana }
+43(35-44) to maximum Mana
{ Suffix Modifier "of the Jaguar" (Tier: 3) — Attribute }
+41(38-42) to Dexterity
]], "Dragonscale Boots"))
local expectedLines = {
"30% increased Movement Speed",
"+41 to Dexterity",
"+128 to maximum Life",
"+(35-44) to maximum Mana",
}
assert.are.same(expectedLines, {
item.explicitModLines[1].line,
item.explicitModLines[2].line,
item.explicitModLines[3].line,
item.explicitModLines[4].line,
})

item:Craft()
item:Craft()
assert.are.same(expectedLines, {
item.explicitModLines[1].line,
item.explicitModLines[2].line,
item.explicitModLines[3].line,
item.explicitModLines[4].line,
})
end)

it("filters flask base properties and parses fixed-value advanced rolls", function()
local item = new("Item", [[
Rarity: Unique
Soul Catcher
Quartz Flask
--------
Lasts 7.20 (augmented) Seconds
Consumes 30 of 60 Charges on use
Currently has 59 Charges
+10% chance to Suppress Spell Damage
(40% of Damage from Suppressed Hits and Ailments they inflict is prevented)
Phasing
--------
{ Unique Modifier }
Consumes Maximum Charges to use
{ Unique Modifier }
Vaal Skills used during effect have 40(10)% reduced Soul Gain Prevention Duration
]])

assert.are.equals(2, #item.buffModLines)
assert.are.equals(0, #item.implicitModLines)
assert.are.equals(2, #item.explicitModLines)
assert.are.equals("Consumes Maximum Charges to use", item.explicitModLines[1].line)
assert.are.equals("Vaal Skills used during effect have 40% reduced Soul Gain Prevention Duration", item.explicitModLines[2].line)
end)

it("preserves rolls from large advanced-copy ranges", function()
local item = new("Item", [[
Rarity: Unique
Elegant Hubris
Timeless Jewel
{ Unique Modifier }
Commissioned 150720(2000-160000) coins to commemorate Chitus(Cadiro-Victario)
]])

local seedLine = itemLib.applyRange(item.explicitModLines[1].line, item.explicitModLines[1].range)
assert.are.equals("Commissioned 150720 coins to commemorate Chitus", seedLine)

item:BuildAndParseRaw()
seedLine = itemLib.applyRange(item.explicitModLines[1].line, item.explicitModLines[1].range)
assert.are.equals("Commissioned 150720 coins to commemorate Chitus", seedLine)
end)

it("preserves independently rolled values on the same modifier line", function()
local item = new("Item", [[
Rarity: Unique
Prismweave
Rustic Sash
{ Unique Modifier — Damage, Elemental, Fire, Attack }
Adds 16(14-16) to 32(30-32) Fire Damage to Attacks
{ Unique Modifier — Damage, Elemental, Cold, Attack }
Adds 10(10-12) to 27(24-28) Cold Damage to Attacks
]])

assert.are.equals("Adds (14-16) to (30-32) Fire Damage to Attacks", item.explicitModLines[1].line)
assert.are.equals("Adds 10 to 27 Cold Damage to Attacks", item.explicitModLines[2].line)
end)

it("orders advanced-copy unique modifiers by their database stat order", function()
local item = new("Item", [[
Rarity: Unique
Geofri's Sanctuary
Elegant Ringmail
{ Unique Modifier — Life }
+66(60-70) to maximum Life
{ Unique Modifier — Defences, Energy Shield }
+31(30-40) to maximum Energy Shield
{ Unique Modifier — Defences, Armour, Energy Shield }
63(50-75)% increased Armour and Energy Shield
{ Unique Modifier — Life, Defences, Energy Shield }
Zealot's Oath
{ Unique Modifier — Defences, Energy Shield }
+2 maximum Energy Shield per 5 Strength
{ Unique Modifier — Elemental, Resistance }
+18(14-18)% to all Elemental Resistances
]])

assert.are.same({
"(50-75)% increased Armour and Energy Shield",
"+(30-40) to maximum Energy Shield",
"+(60-70) to maximum Life",
"+(14-18)% to all Elemental Resistances",
"+2 maximum Energy Shield per 5 Strength",
"Zealot's Oath",
}, {
item.explicitModLines[1].line,
item.explicitModLines[2].line,
item.explicitModLines[3].line,
item.explicitModLines[4].line,
item.explicitModLines[5].line,
item.explicitModLines[6].line,
})
end)

it("keeps the selected value from advanced-copy enum ranges", function()
local item = new("Item", [[
Rarity: Unique
The Dark Monarch
Lich's Circlet
{ Unique Modifier }
Maximum number of Raised Zombies (Animated Weapons-Holy Armaments) is Doubled
Cannot have Minions other than Raised Zombies (Animated Weapons-Holy Armaments)
]])

assert.are.equals("Maximum number of Raised Zombies is Doubled", item.explicitModLines[1].line)
assert.are.equals("Cannot have Minions other than Raised Zombies", item.explicitModLines[2].line)
assert.is_true(#item.explicitModLines[1].modList > 0)
end)

it("parses punctuated enum and descending numeric ranges", function()
local gemItem = new("Item", [[
Rarity: Unique
Replica Dragonfang's Flight
Onyx Amulet
{ Unique Modifier }
+3 to Level of all Lightning Tendrils(Fireball-Mana-Infused Staff) Gems
]])
assert.are.equals("+3 to Level of all Lightning Tendrils Gems", gemItem.explicitModLines[1].line)
assert.is_true(#gemItem.explicitModLines[1].modList > 0)

local requirementItem = new("Item", [[
Rarity: Unique
Replica Dragonfang's Flight
Onyx Amulet
{ Unique Modifier }
Items and Gems have 8(10-5)% reduced Attribute Requirements
]])
assert.are.equals("Items and Gems have (5-10)% reduced Attribute Requirements", requirementItem.explicitModLines[1].line)
assert.are.equals("Items and Gems have 8% reduced Attribute Requirements",
itemLib.applyRange(requirementItem.explicitModLines[1].line, requirementItem.explicitModLines[1].range))
end)

it("parses Memory Strands as an item property", function()
local item = new("Item", [[
Rarity: Magic
Imperial Maul of Revitalization
Weapon Range: 1.3 metres
Memory Strands: 70
Item Level: 85
{ Suffix Modifier "of Revitalization" (Tier: 1) — Life, Attack }
Grants 28(27-30) Life per Enemy Hit
]])

assert.are.equals(70, item.memoryStrands)
assert.are.equals(1, #item.explicitModLines)

item:BuildAndParseRaw()
assert.are.equals(70, item.memoryStrands)
assert.are.equals(1, #item.explicitModLines)
end)

describe("mod magnitude scaling", function()
before_each(function()
newBuild()
Expand All @@ -715,6 +944,51 @@ describe("TestAdvancedItemParse #item", function()
return build.calcsTab.mainEnv.modDB:Sum("INC", { flags = ModFlag.Spell }, "Damage")
end

it("scales advanced-copy Simplex Amulet explicit mods on the first parse", function()
local rawItem = [[
Rarity: Rare
Grim Collar
Simplex Amulet
Quality (Critical Modifiers): +20% (augmented)
{ Implicit Modifier }
-2 Prefix Modifiers allowed
-1 Suffix Modifier allowed
100% increased Explicit Modifier magnitudes
{ Prefix Modifier "The Elder's" (Tier: 1) — Damage, Chaos — 100% Increased }
Gain 13(3-5)% of Non-Chaos Damage as extra Chaos Damage
{ Suffix Modifier "of Destruction" (Tier: 1) — Damage, Critical — 120% Increased }
+70(35-38)% to Global Critical Strike Multiplier
{ Suffix Modifier "of Amassment" — Drop — 100% Increased }
20(17-20)% increased Quantity of Items found
Shaper Item
Elder Item
]]

build.itemsTab:CreateDisplayItemFromRaw(rawItem, true)
local firstItem = build.itemsTab.displayItem
local function findModLine(line)
for _, modLine in ipairs(firstItem.explicitModLines) do
if modLine.line == line then
return modLine
end
end
end
assert.are.equals(2, findModLine("Gain 13% of Non-Chaos Damage as extra Chaos Damage").valueScalar)
assert.are.equals(2.2, findModLine("+70% to Global Critical Strike Multiplier").valueScalar)

-- Changing a different affix must not normalise either legacy roll.
firstItem.suffixes[2].range = 0
firstItem:Craft()
assert.are.equals(2, findModLine("Gain 13% of Non-Chaos Damage as extra Chaos Damage").valueScalar)
assert.are.equals(2.2, findModLine("+70% to Global Critical Strike Multiplier").valueScalar)

-- Editing the legacy affix itself deliberately returns it to the current range.
firstItem.suffixes[1].range = 1
firstItem:Craft()
assert.are.equals(2, findModLine("Gain 13% of Non-Chaos Damage as extra Chaos Damage").valueScalar)
assert.are.equals(2.2, findModLine("+38% to Global Critical Strike Multiplier").valueScalar)
end)

it("scales matching implicit mods by modifier magnitude", function()
-- 130% * 1.7 = 221
build.itemsTab:CreateDisplayItemFromRaw([[
Expand Down
Loading
Loading