Skip to content

Commit 8f86373

Browse files
Blitz54LocalIdentity
andauthored
Add new 0.5 Uniques (#2138)
* Berek's Grip * Berek's Pass * Berek's Respite * Brutus' Lead Sprinkler * Cat O' Nine Tails * Decree of Acuity * Decree of Flight * Decree of Loyalty * Duality * Eventide Petals * Eyes of the Runefather * Forgotten Warden * Geofri's Sanctuary * Horror's Flight * Immaculate Adherence * Ironbound * Liminal Coil * Nightfall * Opportunity * Periphery * Redemption * Runeseeker's Call * Sadist's Mercy * Serle's Grit * Spiteful Floret * league name fixes * Surge of the Tide * Sylvan's Effigy * The Auspex * The Ordained * The Raven's Flock * The Sunken Vessel * The Taming * The Unleashed * Twisted Empyrean * Uhtred's Chalice * Veilpiercer * Vestige of Darkness * Voices * Mageblood (not supported, just added to pob) * Loreweave * Fix mod ordering on loreweave * Modcache * League for facebreaker * Auspex - deflect chance is lucky * Fix Spiteful Floret * Runeseeker's Call - Rune only mod * Serle's Grit - quality display mod * Geofri's Sanctuary - life regen per 10 int * Decree of Acuity - The Effect of Blind on you is reversed * Spire of Ire - gem name changed in 0.3 * Fury of the King - bear skills convert * Mageblood - Legacy mods and scaling support * improve mod parser and floor mod value * Fix topaz mageblood mod * Fix parsing for runeseekers call Add scaling for the runes mod too * Fix item paste from game for scaled rune mods * Scale rune mod lines visually --------- Co-authored-by: LocalIdentity <localidentity2@gmail.com>
1 parent a9c43cb commit 8f86373

45 files changed

Lines changed: 1755 additions & 113 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/TestItemParse_spec.lua

Lines changed: 131 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -476,6 +476,136 @@ describe("TestItemParse", function()
476476
assert.are.equals("Bonded: +20 to maximum Mana", item.runeModLines[3].line)
477477
end)
478478

479+
it("applies increased effect of socketed runes", function()
480+
local item = new("Item", [[
481+
Test Wand
482+
Runic Fork
483+
Sockets: S
484+
Rune: Lesser Desert Rune
485+
Implicits: 1
486+
{enchant}{rune}Gain 6% of Damage as Extra Fire Damage
487+
200% increased effect of Socketed Runes
488+
]])
489+
item:BuildAndParseRaw()
490+
491+
local damageGainAsFire = 0
492+
for _, mod in ipairs(item.slotModList[1]) do
493+
if mod.name == "DamageGainAsFire" and mod.type == "BASE" then
494+
damageGainAsFire = damageGainAsFire + mod.value
495+
end
496+
end
497+
assert.are.equals(18, damageGainAsFire)
498+
assert.is_not_nil(item:BuildRaw():match("{enchant}{rune}Gain 18%% of Damage as Extra Fire Damage"))
499+
end)
500+
501+
it("does not double-scale imported socketed rune text", function()
502+
local item = new("Item", [[
503+
Runeseeker's Call
504+
Runic Fork
505+
Unique ID: bbcd083b0a9da5650f3ac0a001364b1c99d6b866c1f52f0568fafab863b44ccb
506+
Item Level: 86
507+
Quality: 20
508+
Sockets: S S S S S S
509+
Rune: Hedgewitch Assandra's Rune of Wisdom
510+
Rune: Saqawal's Rune of the Sky
511+
Rune: Perfect Iron Rune
512+
Rune: Perfect Iron Rune
513+
Rune: Perfect Vision Rune
514+
Rune: Legacy of Lifesprig
515+
LevelReq: 90
516+
Implicits: 11
517+
{enchant}{rune}210% increased Spell Damage
518+
{enchant}{rune}+9 to Level of all Spell Skills
519+
{enchant}{rune}84% increased Critical Hit Chance for Spells
520+
{enchant}{rune}Gain 15% of Damage as Extra Damage of all Elements
521+
{enchant}{rune}Bonded: 75% increased Critical Damage Bonus
522+
{enchant}{rune}Bonded: 36% chance when collecting an Elemental Infusion to gain an
523+
{enchant}{rune}additional Elemental Infusion of the same type
524+
{enchant}{rune}Bonded: Archon recovery period expires 90% faster
525+
{enchant}{rune}Bonded: Break Armour on Critical Hit with Spells equal to 72% of Physical Damage dealt
526+
{enchant}{rune}Bonded: Leeches 3% of maximum Life when you Cast a Spell
527+
Grants Skill: Level 20 The Stars Answer
528+
Only Runes can be Socketed in this item
529+
200% increased effect of Socketed Runes
530+
Corrupted
531+
]])
532+
item:BuildAndParseRaw()
533+
534+
local spellDamage = 0
535+
for _, mod in ipairs(item.slotModList[1]) do
536+
if mod.name == "Damage" and mod.type == "INC" and mod.flags == ModFlag.Spell then
537+
spellDamage = spellDamage + mod.value
538+
end
539+
end
540+
assert.are.equals(210, spellDamage)
541+
local rawItem = item:BuildRaw()
542+
assert.is_not_nil(rawItem:match("{enchant}{rune}210%% increased Spell Damage"))
543+
assert.is_not_nil(rawItem:match("{enchant}{rune}%+9 to Level of all Spell Skills"))
544+
end)
545+
546+
it("infers pasted game rune lines with socketed rune effect", function()
547+
local item = new("Item", [[
548+
Item Class: Wands
549+
Rarity: Unique
550+
Runeseeker's Call
551+
Runic Fork
552+
--------
553+
Quality: +20% (augmented)
554+
--------
555+
Requires: Level 90 (unmet)
556+
--------
557+
Sockets: S S S S S
558+
--------
559+
Item Level: 86
560+
--------
561+
Gain 120% of Damage as Extra Lightning Damage (rune)
562+
Remnants you create have 75% reduced effect (rune)
563+
Remnants can be collected from 150% further away (rune)
564+
--------
565+
Grants Skill: Level 20 The Stars Answer
566+
--------
567+
{ Unique Modifier }
568+
Only Runes can be Socketed in this item — Unscalable Value
569+
{ Unique Modifier }
570+
200% increased effect of Socketed Runes — Unscalable Value
571+
--------
572+
Smithed from ancient metal
573+
wrought from the very stars.
574+
It is a means to call upon them,
575+
for one capable of wielding it.
576+
--------
577+
Corrupted
578+
]])
579+
580+
local damageGainAsLightning = 0
581+
for _, mod in ipairs(item.slotModList[1]) do
582+
if mod.name == "DamageGainAsLightning" and mod.type == "BASE" then
583+
damageGainAsLightning = damageGainAsLightning + mod.value
584+
end
585+
end
586+
assert.are.equals(120, damageGainAsLightning)
587+
588+
item:BuildAndParseRaw()
589+
590+
assert.are.equals(5, item.itemSocketCount)
591+
assert.are.equals(5, #item.runes)
592+
for _, rune in ipairs(item.runes) do
593+
assert.are_not.equals("None", rune)
594+
end
595+
596+
damageGainAsLightning = 0
597+
for _, mod in ipairs(item.slotModList[1]) do
598+
if mod.name == "DamageGainAsLightning" and mod.type == "BASE" then
599+
damageGainAsLightning = damageGainAsLightning + mod.value
600+
end
601+
end
602+
assert.are.equals(120, damageGainAsLightning)
603+
local rawItem = item:BuildRaw()
604+
assert.is_not_nil(rawItem:match("{enchant}{rune}Gain 120%% of Damage as Extra Lightning Damage"))
605+
assert.is_not_nil(rawItem:match("{enchant}{rune}Remnants you create have 75%% reduced effect"))
606+
assert.is_not_nil(rawItem:match("{enchant}{rune}Remnants can be collected from 150%% further away"))
607+
end)
608+
479609
it("multi-line rune mod", function()
480610
-- Thruldana is Bow-only as well
481611
local item = new("Item", [[
@@ -697,4 +827,4 @@ describe("TestAdvancedItemParse #item", function()
697827
Note: ~b/o 2 chaos
698828
]])
699829
end)
700-
end)
830+
end)

src/Classes/Item.lua

Lines changed: 85 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -850,8 +850,8 @@ function ItemClass:ParseRaw(raw, rarity, highQuality)
850850
gameModeStage = "IMPLICIT"
851851
end
852852
local catalystScalar = 1
853-
if line:match(" %- Unscalable Value$") then
854-
line = line:gsub(" %- Unscalable Value$", "")
853+
if line:match(" %- Unscalable Value$") or line:match(" — Unscalable Value$") then
854+
line = line:gsub(" %- Unscalable Value$", ""):gsub(" — Unscalable Value$", "")
855855
modLine.unscalable = true
856856
else
857857
catalystScalar = getCatalystScalar(self.catalyst, modLine, self.catalystQuality)
@@ -1014,6 +1014,18 @@ function ItemClass:ParseRaw(raw, rarity, highQuality)
10141014
if self.base then
10151015
if self.base.weapon or self.base.armour or self.base.tags.wand or self.base.tags.staff or self.base.tags.sceptre then
10161016
local shouldFixRunesOnItem = #self.runes == 0
1017+
if not shouldFixRunesOnItem and #self.runeModLines > 0 then
1018+
local canRebuildRunes = true
1019+
for _, rune in ipairs(self.runes) do
1020+
if rune ~= "None" and not data.itemMods.Runes[rune] then
1021+
canRebuildRunes = false
1022+
break
1023+
end
1024+
end
1025+
if canRebuildRunes then
1026+
self:UpdateRunes()
1027+
end
1028+
end
10171029

10181030
local function getRuneLineParts(modLine)
10191031
local values = { }
@@ -1105,17 +1117,17 @@ function ItemClass:ParseRaw(raw, rarity, highQuality)
11051117
local broadItemType = self.base.weapon and "weapon" or (self.base.tags.wand or self.base.tags.staff) and "caster" or "armour" -- minor optimisation
11061118
local specificItemType = self.base.type:lower()
11071119
for runeName, runeMods in pairs(data.itemMods.Runes) do
1108-
local addModToGroupedRunes = function (modLine)
1120+
local addModToGroupedRunes = function (modLine, augmentType)
11091121
local runeStrippedModLine, runeValues = getRuneLineParts(modLine)
11101122
if statGroupedRunes[runeStrippedModLine] == nil then
11111123
statGroupedRunes[runeStrippedModLine] = { }
11121124
end
1113-
t_insert(statGroupedRunes[runeStrippedModLine], { name = runeName, values = runeValues })
1125+
t_insert(statGroupedRunes[runeStrippedModLine], { name = runeName, type = augmentType, values = runeValues })
11141126
end
11151127
for slotType, slotMod in pairs(runeMods) do
11161128
if slotType == broadItemType or slotType == specificItemType then
11171129
for _, mod in ipairs(slotMod) do
1118-
addModToGroupedRunes(mod)
1130+
addModToGroupedRunes(mod, slotMod.type)
11191131
end
11201132
end
11211133
end
@@ -1126,19 +1138,45 @@ function ItemClass:ParseRaw(raw, rarity, highQuality)
11261138
table.sort(runes, function(a, b) return compareRuneValueSets(a.values, b.values) end)
11271139
end
11281140

1141+
local gameSocketedRuneEffectModifier = 0
1142+
if mode == "GAME" and shouldFixRunesOnItem then
1143+
for _, modLines in ipairs({ self.enchantModLines, self.implicitModLines, self.explicitModLines }) do
1144+
for _, effectModLine in ipairs(modLines) do
1145+
for _, mod in ipairs(effectModLine.modList or { }) do
1146+
if mod.name == "SocketedRuneEffect" and mod.type == "INC" then
1147+
gameSocketedRuneEffectModifier = gameSocketedRuneEffectModifier + mod.value / 100
1148+
end
1149+
end
1150+
end
1151+
end
1152+
end
1153+
11291154
local remainingRunes = self.itemSocketCount
11301155
for i, modLine in ipairs(self.runeModLines) do
11311156
local strippedModLine, targetValues = getRuneLineParts(modLine.line)
11321157
local groupedRunes = statGroupedRunes[strippedModLine]
11331158
if groupedRunes and not modLine.bonded then -- found the rune category with the relevant stat.
1134-
local result, numRunes = findRuneCombination(groupedRunes, targetValues, remainingRunes)
1159+
local result, numRunes
1160+
local socketedRuneEffectAlreadyApplied
1161+
if gameSocketedRuneEffectModifier ~= 0 then
1162+
local unscaledTargetValues = { }
1163+
for valueIndex, value in ipairs(targetValues) do
1164+
unscaledTargetValues[valueIndex] = value / (1 + gameSocketedRuneEffectModifier)
1165+
end
1166+
result, numRunes = findRuneCombination(groupedRunes, unscaledTargetValues, remainingRunes)
1167+
socketedRuneEffectAlreadyApplied = result ~= nil
1168+
end
1169+
if not result then
1170+
result, numRunes = findRuneCombination(groupedRunes, targetValues, remainingRunes)
1171+
end
11351172

11361173
if result then -- we have found a valid combo for that rune category
11371174
remainingRunes = remainingRunes - numRunes
11381175
-- this code should probably be refactored to based off stored self.runes rather than the recomputed amounts off the runeModLines this
11391176
-- is too avoid having to run the relatively expensive recomputation every time the item is parsed even if we know the runes on the item already.
1140-
modLine.soulCore = groupedRunes[1].name:match("Soul Core") ~= nil
1177+
modLine.augmentType = groupedRunes[1].type
11411178
modLine.runeCount = numRunes
1179+
modLine.socketedRuneEffectAlreadyApplied = socketedRuneEffectAlreadyApplied
11421180

11431181
if shouldFixRunesOnItem then
11441182
for index, rune in ipairs(groupedRunes) do
@@ -1286,6 +1324,9 @@ end
12861324

12871325
function ItemClass:BuildRaw()
12881326
local rawLines = { }
1327+
if self.runeModLines and self.runeModLines[1] then
1328+
self:ApplySocketedRuneDisplayScalars()
1329+
end
12891330
t_insert(rawLines, "Rarity: " .. self.rarity)
12901331
if self.title then
12911332
t_insert(rawLines, self.title)
@@ -1345,7 +1386,8 @@ function ItemClass:BuildRaw()
13451386
t_insert(rawLines, "Item Level: " .. self.itemLevel)
13461387
end
13471388
local function writeModLine(modLine)
1348-
local line = modLine.line
1389+
local displayValueScalar = modLine.displayValueScalar and (modLine.valueScalar or 1) * modLine.displayValueScalar
1390+
local line = displayValueScalar and itemLib.applyRange(modLine.line, modLine.range or main.defaultItemAffixQuality, displayValueScalar, modLine.corruptedRange) or modLine.line
13491391
if modLine.range and line:match("%(%-?[%d%.]+%-%-?[%d%.]+%)") then
13501392
line = "{range:" .. round(modLine.range, 3) .. "}" .. line
13511393
end
@@ -1526,7 +1568,7 @@ function ItemClass:UpdateRunes()
15261568
for _, mod in ipairs(gatheredMods) do
15271569
for i, modLine in ipairs(mod) do
15281570
local order = mod.statOrder[i]
1529-
local orderKey = modLine:match("^Bonded:") and "Bonded:"..order or order
1571+
local orderKey = mod.type .. ":" .. (modLine:match("^Bonded:") and "Bonded:"..order or order)
15301572
if statOrder[orderKey] then
15311573
-- Combine stats
15321574
local start = 1
@@ -1535,8 +1577,12 @@ function ItemClass:UpdateRunes()
15351577
start = e + 1
15361578
return tonumber(num) + tonumber(other)
15371579
end)
1580+
local modList, extra = modLib.parseMod(statOrder[orderKey].line)
1581+
statOrder[orderKey].modList = modList or { }
1582+
statOrder[orderKey].extra = extra
15381583
else
1539-
local modLine = { line = modLine, order = order, rune = true, enchant = true }
1584+
local modList, extra = modLib.parseMod(modLine)
1585+
local modLine = { line = modLine, order = order, modList = modList or { }, extra = extra, rune = true, enchant = true, augmentType = mod.type }
15401586
for l = 1, #self.runeModLines + 1 do
15411587
if not self.runeModLines[l] or self.runeModLines[l].order > order then
15421588
t_insert(self.runeModLines, l, modLine)
@@ -1551,6 +1597,18 @@ function ItemClass:UpdateRunes()
15511597
end
15521598
end
15531599

1600+
function ItemClass:ApplySocketedRuneDisplayScalars()
1601+
for _, modLine in ipairs(self.runeModLines or { }) do
1602+
local effectModifier = modLine.augmentType == "SoulCore" and (self.socketedSoulCoreEffectModifier or 0)
1603+
or modLine.augmentType == "Rune" and (self.socketedRuneEffectModifier or 0)
1604+
if effectModifier and effectModifier ~= 0 and not modLine.socketedRuneEffectAlreadyApplied then
1605+
modLine.displayValueScalar = 1 + effectModifier
1606+
else
1607+
modLine.displayValueScalar = nil
1608+
end
1609+
end
1610+
end
1611+
15541612
-- Rebuild explicit modifiers using the item's affixes
15551613
function ItemClass:Craft()
15561614
-- Save off any custom mods so they can be re-added at the end
@@ -2020,6 +2078,20 @@ function ItemClass:BuildModList()
20202078
for _, modLine in ipairs(self.explicitModLines) do
20212079
processModLine(modLine)
20222080
end
2081+
self.socketedSoulCoreEffectModifier = calcLocal(baseList, "SocketedSoulCoreEffect", "INC", 0) / 100
2082+
self.socketedRuneEffectModifier = calcLocal(baseList, "SocketedRuneEffect", "INC", 0) / 100
2083+
if self.runeModLines[1] then
2084+
self:ApplySocketedRuneDisplayScalars()
2085+
end
2086+
for _, modLine in ipairs(self.runeModLines) do
2087+
local effectModifier = modLine.augmentType == "SoulCore" and self.socketedSoulCoreEffectModifier
2088+
or modLine.augmentType == "Rune" and self.socketedRuneEffectModifier
2089+
if effectModifier and effectModifier ~= 0 and self:CheckModLineVariant(modLine) and not modLine.extra and not modLine.socketedRuneEffectAlreadyApplied then
2090+
for _, mod in ipairs(modLine.modList) do
2091+
baseList:ScaleAddMod(mod, effectModifier)
2092+
end
2093+
end
2094+
end
20232095
self.grantedSkills = { }
20242096
for _, skill in ipairs(baseList:List(nil, "ExtraSkill")) do
20252097
if skill.name ~= "Unknown" then
@@ -2085,6 +2157,9 @@ function ItemClass:BuildModList()
20852157
baseList:NewMod("ArmourData", "LIST", { key = "EnergyShield", value = 0 })
20862158
self.requirements.int = 0
20872159
end
2160+
if self.name == "Geofri's Sanctuary, Revered Vestments" then
2161+
baseList:NewMod("ArmourData", "LIST", { key = "EnergyShield", value = 0 })
2162+
end
20882163
if calcLocal(baseList, "NoAttributeRequirements", "FLAG", 0) then
20892164
self.requirements.strMod = 0
20902165
self.requirements.dexMod = 0
@@ -2126,5 +2201,4 @@ function ItemClass:BuildModList()
21262201
else
21272202
self.modList = self:BuildModListForSlotNum(baseList)
21282203
end
2129-
self.socketedSoulCoreEffectModifier = calcLocal(baseList, "SocketedSoulCoreEffect", "INC", 0) / 100
21302204
end

src/Classes/ItemsTab.lua

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1918,6 +1918,14 @@ end)
19181918

19191919
function ItemsTabClass:GetValidRunesForItem(item)
19201920
local runes = { }
1921+
local socketedItemType
1922+
if item.baseModList then
1923+
if item.baseModList:Flag(nil, "SocketedSoulCoresOnly") then
1924+
socketedItemType = "SoulCore"
1925+
elseif item.baseModList:Flag(nil, "SocketedRunesOnly") then
1926+
socketedItemType = "Rune"
1927+
end
1928+
end
19211929
for _, rune in pairs(runeModLines) do
19221930
local subType = item.base.subType and item.base.subType:lower()
19231931
local itemType = item.base.type:lower()
@@ -1939,13 +1947,9 @@ function ItemsTabClass:GetValidRunesForItem(item)
19391947
end
19401948
end
19411949
if isRuneValidForSlot(rune.slot) then
1942-
if item.title == "Atziri's Splendour" then
1943-
if rune.slot == "None" or rune.type == "SoulCore" then
1944-
table.insert(runes, rune)
1945-
end
1946-
else
1947-
table.insert(runes, rune)
1948-
end
1950+
if rune.slot == "None" or not socketedItemType or rune.type == socketedItemType then
1951+
table.insert(runes, rune)
1952+
end
19491953
end
19501954
end
19511955
return runes

0 commit comments

Comments
 (0)