Skip to content

Commit dc8bff7

Browse files
LocalIdentityNostrademousRegisleLocalIdentityLothrik
authored
Timeless Jewel implementation (#4527)
* WIP: start of Timeless Jewel implementation * QoL minor edit * Feat: add auto-addition of timeless stats to Notables for 'add' case * Feat: added Militant Faith; made additions to Notables on separate line * Feat: Add Elegant Hubris, add support for 'replace' * Fix: remove a print; add a print * Fix: added change recommendations from Lothrik * Fix: fix things that broke in last commit * fix correct file (#11) * Fix Militant Faith * Remove all remaining editedNode logic and add timeless jewel search UI (#12) * Remove all remaining editedNode logic * Initial pass on timeless jewel search UI * Update TreeTab.lua * move handling of result out of read function * swap to using numerical IDs and move seed range check * fix issues * Glorious Vanity * WIP: GV stuff * WIP: more GV processing fixes * WIP: more fixes * Fix: reading the GV file fully * Fix: Glorious Vanity timeless implemenation - still need to set roll values * Second pass on Timeless Jewel search UI (#15) * add handling of small and might_legacy of the vaal * add stats to might and legacy of the vaal * add bias to better determine node type * add stat ranges for might and legacy of the vaal * Implement timeless jewel search function (#16) * Implement timeless jewel search function * remove no-op as its unused now * Update to use 1 NodeIndex file * fix additions conflict * jewel data conflict * Move Glorious Vanity to zip file * Fix Elegant Hubris seeds in jewel search tool (#4528) * Fix integer Glorious Vanity notables, load LUTs from compressed files (#4536) * Inflate/Deflate LUTs * Fixed integer Glorious Vanity notables * move stuff to helper function and add support for "g" format mods * Fixed loading of compressed GV timeless jewel * Add generated .zip files * defer table creation of specific nodes until that node is read * cleanup * Code cleanup * Implement logic to load from binary file by default, and to decompress and create it from .zip if it doesn't exist Co-authored-by: Regisle <Regisle.godform@gmail.com> * Further timeless jewel search improvements (#4529) * Inflate/Deflate LUTs * Further timeless jewel search improvements .. including a passive tree view for socket locations and visual feedback when timeless jewels are added to the build. * Hide socketViewer if hovering over jewelType dropdown * Update TreeTab.lua * Add conqueror selection dropdown menu * Update TreeTab.lua * Add node weights to timeless jewel search * Fixed integer Glorious Vanity notables * Add "required" node weight option, improve formatting * Add small mod searching (Glorious Vanity) * move stuff to helper function and add support for "g" format mods * Swap weight slider and search dropdown positions * Fixed loading of compressed GV timeless jewel * Add generated .zip files * Fix Glorious Vanity small node processing * Update TreeTab.lua * Improve search result output formatting * Automatically determine nearby socket keystones * Exit loop earlier to avoid wasting time * Update TreeTab.lua * Update TreeTab.lua * defer table creation of specific nodes until that node is read * cleanup * Add detailed node breakdown to tooltips * Code cleanup * Update node breakdown to display actual stats * Make most timeless search UI settings persistent * Fix bug where node tooltip results were culled * Erase desired node input on jewel type change * Remove unused Might/Legacy of the Vaal code * Make "required" nodes actually required * Update TreeTab.lua * optimise search function and show total weight in first column * Code clean up * Remove unused variables * Update TreeTab.lua * Fix Glorious Vanity node filtering * Standardize search result number formatting .. and hide search result tooltips when hovering over node selection dropdown. * Add secondary node weight slider for GV * Update TreeTab.lua * Default to 0 for missing nodeWeights instead of 0.1 * Revert last commit + enable second nodeSlider for non-GV jewels * Update TimelessJewelListControl.lua * Fix broken node weights with a fairly big rewrite * Swap nodeWeight/nodeWeight2 back... * Fix timeless jewel node filtering typo * Implement logic to load from binary file by default, and to decompress and create it from .zip if it doesn't exist Co-authored-by: Wires77 <Wires77@users.noreply.github.com> Co-authored-by: Regisle <Regisle.godform@gmail.com> * Further Timeless Jewel search improvements (#4546) * Fix broken might/legacy of the vaal + misc cleanup * Update to new noTooltip draw behavior * Update edit box when slider values change .. and prevent duplicate nodes from being added to edit box. * Remove search result total weight column * Fix dynamic slider update bug * Sanitize user input and fix a few errors * Wipe all search input on reset * Fix another missed variable sync * Rebuild searchListTbl from timelessData.searchList * Save timeless jewel search config to build XML * Update TreeTab.lua * Fixed swapped weight values, minor refactors * Fix search list result updating * Fix statMod2 nil error on small nodes * Properly handle replace and add types, finally * Minor consistency edit * Remove dynamic slider width code * Set modFlag to prompt build saving on several .. .. different events, including: - timelessData.searchList change (node weight sliders) - updateSearchList called - conqueror change - jewel socket change - desired node change (direct and via dropdown list) * Remove lingering duplicate variable from old implementation * Save socketFilter state to build XML * Change tooltip colour for tree node names Co-authored-by: Wires77 <Wires77@users.noreply.github.com> Co-authored-by: LocalIdentity <localidentity2@gmail.com> Co-authored-by: Nostrademous <nostrademous@hotmail.com> Co-authored-by: Regisle <49933620+Regisle@users.noreply.github.com> Co-authored-by: LocalIdentity <localidentity2@gmail.com> Co-authored-by: Lothrik (MaXiMiUS) <maximius@gmail.com> Co-authored-by: Regisle <Regisle.godform@gmail.com> Co-authored-by: Wires77 <Wires77@users.noreply.github.com>
1 parent db890d9 commit dc8bff7

16 files changed

+7440
-3659
lines changed

src/Classes/PassiveSpec.lua

Lines changed: 164 additions & 121 deletions
Original file line numberDiff line numberDiff line change
@@ -119,46 +119,6 @@ function PassiveSpecClass:Load(xml, dbFileName)
119119
elseif url then
120120
self:DecodeURL(url)
121121
end
122-
for _, node in pairs(xml) do
123-
if type(node) == "table" then
124-
if node.elem == "EditedNodes" then
125-
for _, child in ipairs(node) do
126-
if not child.attrib.nodeId then
127-
launch:ShowErrMsg("^1Error parsing '%s': 'EditedNode' element missing 'nodeId' attribute", dbFileName)
128-
return true
129-
end
130-
if not child.attrib.editorSeed then
131-
launch:ShowErrMsg("^1Error parsing '%s': 'EditedNode' element missing 'editorSeed' attribute", dbFileName)
132-
return true
133-
end
134-
135-
local editorSeed = tonumber(child.attrib.editorSeed)
136-
local nodeId = tonumber(child.attrib.nodeId)
137-
if not self.tree.legion.editedNodes then
138-
self.tree.legion.editedNodes = { }
139-
end
140-
if not self.tree.legion.editedNodes[editorSeed] then
141-
self.tree.legion.editedNodes[editorSeed] = { }
142-
end
143-
self.tree.legion.editedNodes[editorSeed][nodeId] = copyTable(self.nodes[nodeId], true)
144-
self.tree.legion.editedNodes[editorSeed][nodeId].id = nodeId
145-
self.tree.legion.editedNodes[editorSeed][nodeId].dn = child.attrib.nodeName
146-
self.tree.legion.editedNodes[editorSeed][nodeId].icon = child.attrib.icon
147-
if self.tree.legion.nodes[child.attrib.spriteId] then
148-
self.tree.legion.editedNodes[editorSeed][nodeId].spriteId = child.attrib.spriteId
149-
self.tree.legion.editedNodes[editorSeed][nodeId].sprites = self.tree.legion.nodes[child.attrib.spriteId].sprites
150-
end
151-
local modCount = 0
152-
for _, modLine in ipairs(child) do
153-
for line in string.gmatch(modLine .. "\r\n", "([^\r\n\t]*)\r?\n") do
154-
self:NodeAdditionOrReplacementFromString(self.tree.legion.editedNodes[editorSeed][nodeId], line, modCount == 0)
155-
modCount = modCount + 1
156-
end
157-
end
158-
end
159-
end
160-
end
161-
end
162122
self:ResetUndo()
163123
end
164124

@@ -171,33 +131,6 @@ function PassiveSpecClass:Save(xml)
171131
for mastery, effect in pairs(self.masterySelections) do
172132
t_insert(masterySelections, "{"..mastery..","..effect.."}")
173133
end
174-
local editedNodes = {
175-
elem = "EditedNodes"
176-
}
177-
if self.tree.legion.editedNodes then
178-
for seed, nodes in pairs(self.tree.legion.editedNodes) do
179-
for nodeId, node in pairs(nodes) do
180-
local editedNode = { elem = "EditedNode", attrib = { nodeId = tostring(nodeId), editorSeed = tostring(seed), nodeName = node.dn, icon = node.icon, spriteId = node.spriteId } }
181-
for _, modLine in ipairs(node.sd) do
182-
t_insert(editedNode, modLine)
183-
end
184-
-- Do not save current editedNode data unless the current node is conquered
185-
if self.nodes[nodeId] and self.nodes[nodeId].conqueredBy then
186-
-- Do not save current editedNode data unless the current node is anointed or allocated
187-
if self.build.calcsTab.mainEnv.grantedPassives[nodeId] then
188-
t_insert(editedNodes, editedNode)
189-
else
190-
for allocNodeId in pairs(self.allocNodes) do
191-
if nodeId == allocNodeId then
192-
t_insert(editedNodes, editedNode)
193-
end
194-
end
195-
end
196-
end
197-
end
198-
end
199-
end
200-
t_insert(xml, editedNodes)
201134
xml.attrib = {
202135
title = self.title,
203136
treeVersion = self.treeVersion,
@@ -739,67 +672,177 @@ function PassiveSpecClass:BuildAllDependsAndPaths()
739672
if node.conqueredBy and node.type ~= "Socket" then
740673
local conqueredBy = node.conqueredBy
741674
local legionNodes = self.tree.legion.nodes
675+
local legionAdditions = self.tree.legion.additions
676+
677+
-- FIXME - continue implementing
678+
local jewelType = 5
679+
if conqueredBy.conqueror.type == "vaal" then
680+
jewelType = 1
681+
elseif conqueredBy.conqueror.type == "karui" then
682+
jewelType = 2
683+
elseif conqueredBy.conqueror.type == "maraketh" then
684+
jewelType = 3
685+
elseif conqueredBy.conqueror.type == "templar" then
686+
jewelType = 4
687+
end
688+
local seed = conqueredBy.id
689+
if jewelType == 5 then
690+
seed = seed / 20
691+
end
692+
693+
local replaceHelperFunc = function(statToFix, statKey, statMod, value)
694+
if statMod.fmt == "g" then -- note the only one we actualy care about is "Ritual of Flesh" life regen
695+
if statKey:find("per_minute") then
696+
value = round(value / 60, 1)
697+
elseif statKey:find("permyriad") then
698+
value = value / 100
699+
elseif statKey:find("_ms") then
700+
value = value / 1000
701+
end
702+
end
703+
--if statMod.fmt == "d" then -- only ever d or g, and we want both past here
704+
if statMod.min ~= statMod.max then
705+
return statToFix:gsub("%("..statMod.min.."%-"..statMod.max.."%)", value)
706+
elseif statMod.min ~= value then -- only true for might/legacy of the vaal which can combine stats
707+
return statToFix:gsub(statMod.min, value)
708+
end
709+
return statToFix -- if it doesn't need to be changed
710+
end
742711

743-
-- Replace with edited node if applicable
744-
if self.tree.legion.editedNodes and self.tree.legion.editedNodes[conqueredBy.id] and self.tree.legion.editedNodes[conqueredBy.id][node.id] then
745-
local editedNode = self.tree.legion.editedNodes[conqueredBy.id][node.id]
746-
node.dn = editedNode.dn
747-
node.sd = editedNode.sd
748-
node.sprites = editedNode.sprites
749-
node.mods = editedNode.mods
750-
node.modList = editedNode.modList
751-
node.modKey = editedNode.modKey
752-
node.icon = editedNode.icon
753-
node.spriteId = editedNode.spriteId
754-
else
755-
if node.type == "Keystone" then
756-
local legionNode = legionNodes[conqueredBy.conqueror.type.."_keystone_"..conqueredBy.conqueror.id]
757-
self:ReplaceNode(node, legionNode)
758-
elseif conqueredBy.conqueror.type == "eternal" and node.type == "Normal" then
759-
local legionNode = legionNodes["eternal_small_blank"]
760-
self:ReplaceNode(node,legionNode)
761-
elseif conqueredBy.conqueror.type == "eternal" and node.type == "Notable" then
762-
local legionNode = legionNodes["eternal_notable_fire_resistance_1"]
763-
node.dn = "Eternal Empire notable node"
764-
node.sd = {"Right click to set mod"}
765-
node.sprites = legionNode.sprites
766-
node.mods = {""}
767-
node.modList = new("ModList")
768-
node.modKey = ""
769-
node.reminderText = { }
712+
if node.type == "Notable" then
713+
local jewelDataTbl = { }
714+
if seed ~= m_max(m_min(seed, data.timelessJewelSeedMax[jewelType]), data.timelessJewelSeedMin[jewelType]) then
715+
ConPrintf("ERROR: Seed " .. seed .. " is outside of valid range [" .. data.timelessJewelSeedMin[jewelType] .. " - " .. data.timelessJewelSeedMax[jewelType] .. "] for jewel type: " .. data.timelessJewelTypes[jewelType])
716+
else
717+
jewelDataTbl = data.readLUT(conqueredBy.id, node.id, jewelType)
718+
end
719+
--print("Need to Update: " .. node.id .. " [" .. node.dn .. "]")
720+
if not next(jewelDataTbl) then
721+
ConPrintf("Missing LUT: " .. data.timelessJewelTypes[jewelType])
722+
else
723+
if jewelType == 1 then
724+
local headerSize = #jewelDataTbl
725+
-- FIXME: complete implementation of this. Need to set roll values for stats
726+
-- based on their `fmt` specification
727+
if headerSize == 2 or headerSize == 3 then
728+
self:ReplaceNode(node, legionNodes[jewelDataTbl[1] - 94])
729+
730+
for i, repStat in ipairs(legionNodes[jewelDataTbl[1] - 94].sd) do
731+
local statKey = legionNodes[jewelDataTbl[1] - 94].sortedStats[i]
732+
local statMod = legionNodes[jewelDataTbl[1] - 94].stats[statKey]
733+
repStat = replaceHelperFunc(repStat, statKey, statMod, jewelDataTbl[statMod.index + 1])
734+
self:NodeAdditionOrReplacementFromString(node, repStat, i == 1) -- wipe mods on first run
735+
end
736+
-- should fix the stat values here (note headerSize == 3 has 2 values)
737+
elseif headerSize == 6 or headerSize == 8 then
738+
local bias = 0
739+
for i,val in ipairs(jewelDataTbl) do
740+
if i > (headerSize / 2) then
741+
break
742+
elseif val <= 21 then
743+
bias = bias + 1
744+
else
745+
bias = bias - 1
746+
end
747+
end
748+
if bias >= 0 then
749+
self:ReplaceNode(node, legionNodes[76]) -- might of the vaal
750+
else
751+
self:ReplaceNode(node, legionNodes[77]) -- legacy of the vaal
752+
end
753+
local additions = {}
754+
for i,val in ipairs(jewelDataTbl) do
755+
if i <= (headerSize / 2) then
756+
local roll = jewelDataTbl[i + headerSize / 2]
757+
if not additions[val] then
758+
additions[val] = roll
759+
else
760+
additions[val] = additions[val] + roll
761+
end
762+
else
763+
break
764+
end
765+
end
766+
for add,val in pairs(additions) do
767+
local addition = legionAdditions[add]
768+
for _, addStat in ipairs(addition.sd) do
769+
for k,statMod in pairs(addition.stats) do -- should only be 1 big, these didnt get changed so cant just grab index
770+
addStat = replaceHelperFunc(addStat, k, statMod, val)
771+
end
772+
self:NodeAdditionOrReplacementFromString(node, addStat)
773+
end
774+
end
775+
else
776+
ConPrintf("Unhandled Glorious Vanity headerSize: " .. headerSize)
777+
end
778+
else
779+
for _, jewelData in ipairs(jewelDataTbl) do
780+
if jewelData >= 94 then -- replace
781+
jewelData = jewelData - 94
782+
local legionNode = legionNodes[jewelData]
783+
if legionNode then
784+
self:ReplaceNode(node, legionNode)
785+
else
786+
ConPrintf("Unhandled 'replace' ID: " .. jewelData)
787+
end
788+
elseif jewelData then -- add
789+
local addition = legionAdditions[jewelData]
790+
for _, addStat in ipairs(addition.sd) do
791+
self:NodeAdditionOrReplacementFromString(node, " \n" .. addStat)
792+
end
793+
elseif next(jewelData) then
794+
ConPrintf("Unhandled OP: " .. jewelData)
795+
end
796+
end
797+
end
798+
end
799+
elseif node.type == "Keystone" then
800+
local matchStr = conqueredBy.conqueror.type .. "_keystone_" .. conqueredBy.conqueror.id
801+
for _, legionNode in ipairs(legionNodes) do
802+
if legionNode.id == matchStr then
803+
self:ReplaceNode(node, legionNode)
804+
break
805+
end
806+
end
807+
elseif node.type == "Normal" then
808+
if conqueredBy.conqueror.type == "vaal" then
809+
local jewelDataTbl = { }
810+
if seed ~= m_max(m_min(seed, data.timelessJewelSeedMax[jewelType]), data.timelessJewelSeedMin[jewelType]) then
811+
ConPrintf("ERROR: Seed " .. seed .. " is outside of valid range [" .. data.timelessJewelSeedMin[jewelType] .. " - " .. data.timelessJewelSeedMax[jewelType] .. "] for jewel type: " .. data.timelessJewelTypes[jewelType])
812+
else
813+
jewelDataTbl = data.readLUT(conqueredBy.id, node.id, jewelType)
814+
end
815+
print("Need to Update: " .. node.id .. " [" .. node.dn .. "]")
816+
if not next(jewelDataTbl) then
817+
ConPrintf("Missing LUT: " .. data.timelessJewelTypes[jewelType])
818+
else
819+
self:ReplaceNode(node, legionNodes[jewelDataTbl[1] - 94])
820+
for i, repStat in ipairs(node.sd) do
821+
local statKey = legionNodes[jewelDataTbl[1] - 94].sortedStats[i]
822+
local statMod = legionNodes[jewelDataTbl[1] - 94].stats[statKey]
823+
repStat = replaceHelperFunc(repStat, statKey, statMod, jewelDataTbl[2])
824+
self:NodeAdditionOrReplacementFromString(node, repStat, true)
825+
end
826+
end
827+
elseif conqueredBy.conqueror.type == "karui" then
828+
local str = isValueInArray(attributes, node.dn) and "2" or "4"
829+
self:NodeAdditionOrReplacementFromString(node, " \n+" .. str .. " to Strength")
830+
elseif conqueredBy.conqueror.type == "maraketh" then
831+
local dex = isValueInArray(attributes, node.dn) and "2" or "4"
832+
self:NodeAdditionOrReplacementFromString(node, " \n+" .. dex .. " to Dexterity")
770833
elseif conqueredBy.conqueror.type == "templar" then
771834
if isValueInArray(attributes, node.dn) then
772-
local legionNode =legionNodes["templar_devotion_node"]
773-
self:ReplaceNode(node,legionNode)
835+
local legionNode = legionNodes[90] -- templar_devotion_node
836+
self:ReplaceNode(node, legionNode)
774837
else
775-
self:NodeAdditionOrReplacementFromString(node,"+5 to Devotion")
838+
self:NodeAdditionOrReplacementFromString(node, " \n+5 to Devotion")
776839
end
777-
elseif conqueredBy.conqueror.type == "maraketh" and node.type == "Normal" then
778-
local dex = isValueInArray(attributes, node.dn) and "2" or "4"
779-
self:NodeAdditionOrReplacementFromString(node,"+"..dex.." to Dexterity")
780-
elseif conqueredBy.conqueror.type == "karui" and node.type == "Normal" then
781-
local str = isValueInArray(attributes, node.dn) and "2" or "4"
782-
self:NodeAdditionOrReplacementFromString(node,"+"..str.." to Strength")
783-
elseif conqueredBy.conqueror.type == "vaal" and node.type == "Normal" then
784-
local legionNode =legionNodes["vaal_small_fire_resistance"]
785-
node.dn = "Vaal small node"
786-
node.sd = {"Right click to set mod"}
787-
node.sprites = legionNode.sprites
788-
node.mods = {""}
789-
node.modList = new("ModList")
790-
node.modKey = ""
791-
elseif conqueredBy.conqueror.type == "vaal" and node.type == "Notable" then
792-
local legionNode =legionNodes["vaal_notable_curse_1"]
793-
node.dn = "Vaal notable node"
794-
node.sd = {"Right click to set mod"}
795-
node.sprites = legionNode.sprites
796-
node.mods = {""}
797-
node.modList = new("ModList")
798-
node.modKey = ""
799-
node.reminderText = { }
840+
elseif conqueredBy.conqueror.type == "eternal" then
841+
local legionNode = legionNodes[109] -- eternal_small_blank
842+
self:ReplaceNode(node, legionNode)
800843
end
801-
self:ReconnectNodeToClassStart(node)
802844
end
845+
self:ReconnectNodeToClassStart(node)
803846
end
804847
end
805848

src/Classes/PassiveTreeView.lua

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -292,19 +292,6 @@ function PassiveTreeViewClass:Draw(build, viewPort, inputEvents)
292292
build.itemsTab:SelectControl(slot)
293293
build.viewMode = "ITEMS"
294294
end
295-
296-
--[[ Only allow node editing in these situations:
297-
Vaal (Glorious Vanity): any non-keystone
298-
Maraketh (Brutal Restraint): only notables, +dex already set
299-
Eternal (Elegant Hubris): only notables, other passives are blank
300-
Karui (Lethal Pride): only notables, +str already set
301-
Templar (Militant Faith): any non-keystone, non-notables add devotion or replace with devotion
302-
]]--
303-
elseif hoverNode and hoverNode.conqueredBy and hoverNode.type ~= "Keystone" and
304-
(hoverNode.conqueredBy.conqueror.type == "vaal"
305-
or hoverNode.isNotable) then
306-
build.treeTab:ModifyNodePopup(hoverNode, viewPort)
307-
build.buildFlag = true
308295
elseif hoverNode and hoverNode.alloc and hoverNode.type == "Mastery" and hoverNode.masteryEffects then
309296
build.treeTab:OpenMasteryPopup(hoverNode, viewPort)
310297
build.buildFlag = true
@@ -1009,14 +996,6 @@ function PassiveTreeViewClass:AddNodeTooltip(tooltip, node, build)
1009996
end
1010997
end
1011998

1012-
-- Conqueror node editing
1013-
if node and node.conqueredBy and node.type ~= "Keystone" and
1014-
(node.conqueredBy.conqueror.type == "vaal"
1015-
or node.isNotable) then
1016-
tooltip:AddSeparator(14)
1017-
tooltip:AddLine(14, colorCodes.TIP.."Tip: Right click to edit the modifiers for this node")
1018-
end
1019-
1020999
-- Mod differences
10211000
if self.showStatDifferences then
10221001
local calcFunc, calcBase = build.calcsTab:GetMiscCalculator(build)

0 commit comments

Comments
 (0)