Skip to content

Commit f532296

Browse files
Blitz54LocalIdentity
andauthored
Fix level requirements on staves, wands, and jewellery and unique items (#2323)
* Remove item requirements from granted skills * Initial commit, level reqs fixed * Fix unique exports The unique export now has a number of fixes and additions It now inherits the implicit mods from a item base and include skill mods in the implicit count without having to specifically state it It correctly orders implicit mods now which was not done before Bases that grant skills now use the correct level range e.g Raise Shield now doesn't show the level range of 1-20 Belt bases now have the correct implicit mods and exclude the charm mod if the base already grants it Level requirements for gear now only print the value if it exceeds the base type Many uniques have had their unique mod IDs updated to use the correct ones so the required level is correct * Import + some base fixes Uniques not grab their base level from the unique in the db so that if they are modified by runes they can fall back to the correct level if the rune is removed Fixed the level requirement for Runic Fork as it wasn't being exported as it didn't have a skill Fix the export of uniques with legacy implicits not having the correct Implicit count Fix Wondertrap export typo Clean up uModsToText script Adds tests for new scenarios * ModCache * Spelling * Fix test to use correct base type --------- Co-authored-by: LocalIdentity <localidentity2@gmail.com>
1 parent e5a5091 commit f532296

49 files changed

Lines changed: 875 additions & 992 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

spec/System/TestImportReimport_spec.lua

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,67 @@ Fireball 20/0 1
220220
assert.are.equal(0, build.itemsTab.slots["Gloves Jewel Socket 2"].selItemId)
221221
end)
222222

223+
it("uses unique database and rune levels when importing unique items from account data", function()
224+
while main.uniqueDB.loading do
225+
runCallback("OnFrame")
226+
end
227+
228+
build.importTab.controls.charImportItemsClearItems.state = true
229+
build.importTab.controls.charImportItemsClearSkills.state = true
230+
231+
local body = makeImportItem("Rusted Cuirass", "BodyArmour", "test-import-bramblejack")
232+
body.frameType = 3
233+
body.name = "Bramblejack"
234+
body.requirements = {
235+
{ name = "Level", values = { { "22", 0 } } },
236+
}
237+
build.importTab:ImportItemsAndSkills(buildImportPayload({ body }, {}))
238+
runCallback("OnFrame")
239+
240+
local importedItem = build.itemsTab.items[build.itemsTab.slots["Body Armour"].selItemId]
241+
assert.are.equal("Bramblejack, Rusted Cuirass", importedItem.name)
242+
assert.are.equal(0, importedItem.requirements.level)
243+
244+
body.sockets = {
245+
{ type = "rune" },
246+
}
247+
body.socketedItems = {
248+
{ baseType = "Legacy of Blackbraid" },
249+
}
250+
251+
build.importTab:ImportItemsAndSkills(buildImportPayload({ body }, {}))
252+
runCallback("OnFrame")
253+
254+
importedItem = build.itemsTab.items[build.itemsTab.slots["Body Armour"].selItemId]
255+
assert.are.equal(65, importedItem.requirements.level)
256+
257+
importedItem.runes[1] = "None"
258+
importedItem:UpdateRunes()
259+
importedItem:BuildAndParseRaw()
260+
assert.are.equal(0, importedItem.requirements.level)
261+
262+
local weapon = makeImportItem("Runemastered Ironhead Spear", "Weapon", "test-import-tyranny's-grip")
263+
weapon.frameType = 3
264+
weapon.name = "Tyranny's Grip"
265+
weapon.sockets = {
266+
{ type = "rune" },
267+
}
268+
weapon.socketedItems = {
269+
{ baseType = "Legacy of Tyranny's Grip" },
270+
}
271+
272+
build.importTab:ImportItemsAndSkills(buildImportPayload({ weapon }, {}))
273+
runCallback("OnFrame")
274+
275+
importedItem = build.itemsTab.items[build.itemsTab.slots["Weapon 1"].selItemId]
276+
assert.are.equal(65, importedItem.requirements.level)
277+
278+
importedItem.runes[1] = "None"
279+
importedItem:UpdateRunes()
280+
importedItem:BuildAndParseRaw()
281+
assert.are.equal(55, importedItem.requirements.level)
282+
end)
283+
223284
it("preserves skill part selection when reimporting items and skills", function()
224285
assertReimportPreservesSkillSubstate("Twig Focus", "Offhand", "Dark Effigy", "skillPart", 2)
225286
end)

spec/System/TestItemParse_spec.lua

Lines changed: 111 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -375,13 +375,108 @@ describe("TestItemParse", function()
375375
assert.are.equals("~price 1 chaos", item.note)
376376
end)
377377

378-
it("Attribute Requirements", function()
379-
local item = new("Item", raw("Dex: 100"))
380-
assert.are.equals(100, item.requirements.dex)
381-
item = new("Item", raw("Int: 101"))
382-
assert.are.equals(101, item.requirements.int)
383-
item = new("Item", raw("Str: 102"))
384-
assert.are.equals(102, item.requirements.str)
378+
it("Rune level requirements", function()
379+
local item = new("Item", [[
380+
Test Wand
381+
Runic Fork
382+
Sockets: S
383+
Rune: Perfect Storm Rune
384+
LevelReq: 1
385+
Implicits: 1
386+
{enchant}{rune}Gain 12% of Damage as Extra Lightning Damage
387+
]])
388+
assert.are.equals(50, item.requirements.level)
389+
end)
390+
391+
it("Unique mod level requirements", function()
392+
local foundAnvil
393+
for _, rawUnique in ipairs(data.uniques.amulet) do
394+
if rawUnique:match("The Anvil") then
395+
local item = new("Item", rawUnique)
396+
assert.are.equals(18, item.requirements.level)
397+
assert.is_nil(rawUnique:match("Requires Level 18"))
398+
foundAnvil = true
399+
break
400+
end
401+
end
402+
assert(foundAnvil, "The Anvil not found")
403+
404+
local foundChoirOfTheStorm
405+
for _, rawUnique in ipairs(data.uniques.amulet) do
406+
if rawUnique:match("Choir of the Storm") then
407+
assert(rawUnique:find("Grants Skill: Level (1-20) Lightning Bolt", 1, true))
408+
assert(rawUnique:find("Trigger Lightning Bolt Skill on Critical Hit", 1, true))
409+
foundChoirOfTheStorm = true
410+
break
411+
end
412+
end
413+
assert(foundChoirOfTheStorm, "Choir of the Storm not found")
414+
415+
local foundSylvansEffigy
416+
for _, rawUnique in ipairs(data.uniques.sceptre) do
417+
if rawUnique:match("Sylvan's Effigy") then
418+
local item = new("Item", rawUnique)
419+
assert.are.equals(62, item.requirements.level)
420+
foundSylvansEffigy = true
421+
break
422+
end
423+
end
424+
assert(foundSylvansEffigy, "Sylvan's Effigy not found")
425+
426+
for _, rawUnique in ipairs(data.uniques.amulet) do
427+
if rawUnique:match("Hinekora's Sight") then
428+
local item = new("Item", rawUnique)
429+
assert.are.equals(44, item.requirements.level)
430+
assert(rawUnique:find("Grants Skill: Level (1-20) Future-Past", 1, true))
431+
return
432+
end
433+
end
434+
assert(false, "Hinekora's Sight not found")
435+
end)
436+
437+
it("keeps legacy base implicit variants as implicits", function()
438+
for _, rawUnique in ipairs(data.uniques.belt) do
439+
if rawUnique:match("Goregirdle") then
440+
assert(rawUnique:find("Implicits: 3\n{variant:2}+(140-180) to Armour\n{variant:1}+(100-140) to Armour\nHas (1-3) Charm Slot", 1, true))
441+
return
442+
end
443+
end
444+
assert(false, "Goregirdle not found")
445+
end)
446+
447+
it("uses upgraded base requirements for uniques", function()
448+
local item = new("Item", [[
449+
Item Class: Spears
450+
Rarity: Unique
451+
Tyranny's Grip
452+
Runemastered Ironhead Spear
453+
Requires: Level 55, 31 Str, 76 Dex
454+
Item Level: 30
455+
]])
456+
assert.are.equals(55, item.requirements.level)
457+
458+
item.itemSocketCount = 1
459+
item.runes = { "Legacy of Tyranny's Grip" }
460+
item:UpdateRunes()
461+
item:BuildAndParseRaw()
462+
assert.are.equals(65, item.requirements.level)
463+
464+
item.runes[1] = "None"
465+
item:UpdateRunes()
466+
item:BuildAndParseRaw()
467+
assert.are.equals(55, item.requirements.level)
468+
end)
469+
470+
it("inherits implicits from variant base types", function()
471+
for _, rawUnique in ipairs(data.uniques.shield) do
472+
if rawUnique:match("The Surrender") then
473+
assert(rawUnique:find("Implicits: 1\nGrants Skill: Raise Shield", 1, true))
474+
local item = new("Item", rawUnique)
475+
assert.are.equals(75, item.requirements.level)
476+
return
477+
end
478+
end
479+
assert(false, "The Surrender not found")
385480
end)
386481

387482
it("Requires Class", function()
@@ -711,6 +806,7 @@ describe("TestItemParse", function()
711806
--------
712807
Corrupted
713808
]])
809+
assert.are.equals(90, item.requirements.level)
714810

715811
local damageGainAsLightning = 0
716812
for _, mod in ipairs(item.slotModList[1]) do
@@ -721,6 +817,7 @@ describe("TestItemParse", function()
721817
assert.are.equals(120, damageGainAsLightning)
722818

723819
item:BuildAndParseRaw()
820+
assert.are.equals(90, item.requirements.level)
724821

725822
assert.are.equals(5, item.itemSocketCount)
726823
assert.are.equals(5, #item.runes)
@@ -739,6 +836,13 @@ describe("TestItemParse", function()
739836
assert.is_not_nil(rawItem:match("{enchant}{rune}Gain 120%% of Damage as Extra Lightning Damage"))
740837
assert.is_not_nil(rawItem:match("{enchant}{rune}Remnants you create have 75%% reduced effect"))
741838
assert.is_not_nil(rawItem:match("{enchant}{rune}Remnants can be collected from 150%% further away"))
839+
840+
for i = 1, item.itemSocketCount do
841+
item.runes[i] = "None"
842+
end
843+
item:UpdateRunes()
844+
item:BuildAndParseRaw()
845+
assert.are.equals(65, item.requirements.level)
742846
end)
743847

744848
it("multi-line rune mod", function()

src/Classes/ImportTab.lua

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ local ImportTabClass = newClass("ImportTab", "ControlHost", "Control", function(
4848
self.controls.logoutApiButton.shown = function()
4949
return (self.charImportMode == "SELECTCHAR" or self.charImportMode == "GETACCOUNTNAME") and main.api.authToken ~= nil
5050
end
51-
51+
5252
self.controls.characterImportAnchor = new("Control", {"TOPLEFT",self.controls.sectionCharImport,"TOPLEFT"}, {6, 40, 200, 16})
5353
self.controls.sectionCharImport.height = function() return self.charImportMode == "AUTHENTICATION" and 60 or 200 end
5454

@@ -442,7 +442,7 @@ function ImportTabClass:DownloadCharacterList()
442442
return "Standard"
443443
end
444444
end
445-
445+
446446
self.charImportMode = "DOWNLOADCHARLIST"
447447
self.charImportStatus = "Retrieving character list..."
448448
local realm = realmList[self.controls.accountRealm.selIndex]
@@ -954,10 +954,10 @@ function ImportTabClass:ImportItemsAndSkills(charData)
954954
local funcGetGemInstance = function(skillData)
955955
local typeLine = sanitiseText(skillData.typeLine) .. (skillData.support and " Support" or "")
956956
local gemId = self.build.data.gemForBaseName[typeLine:lower()]
957-
957+
958958
if typeLine:match("^Spectre:") then
959959
gemId = "Metadata/Items/Gems/SkillGemSummonSpectre"
960-
end
960+
end
961961
if typeLine:match("^Companion:") then
962962
gemId = "Metadata/Items/Gems/SkillGemSummonBeast"
963963
end
@@ -1061,7 +1061,7 @@ function ImportTabClass:ImportItemsAndSkills(charData)
10611061
end
10621062
for _, skillData in pairs(charData.skills) do
10631063
local gemInstance = funcGetGemInstance(skillData)
1064-
1064+
10651065
if gemInstance then
10661066
local group = { label = "", enabled = true, gemList = { } }
10671067
t_insert(group.gemList, gemInstance )
@@ -1302,6 +1302,11 @@ function ImportTabClass:ImportItem(itemData, slotName)
13021302
end
13031303
end
13041304
end
1305+
local dbItem = item:GetUniqueDBItem()
1306+
if dbItem then
1307+
item.requirements = item.requirements or { }
1308+
item.requirements.level = dbItem.requirements.naturalLevel or dbItem.requirements.level
1309+
end
13051310
item.enchantModLines = { }
13061311
item.runeModLines = { }
13071312
item.classRequirementModLines = { }

0 commit comments

Comments
 (0)