Skip to content

Commit 1781ab2

Browse files
author
LocalIdentity
committed
Refactor
1 parent 01b0ca0 commit 1781ab2

1 file changed

Lines changed: 171 additions & 128 deletions

File tree

src/Classes/PassiveSpec.lua

Lines changed: 171 additions & 128 deletions
Original file line numberDiff line numberDiff line change
@@ -1590,10 +1590,82 @@ function PassiveSpecClass:ReconnectNodeToClassStart(node)
15901590
end
15911591
end
15921592

1593-
function PassiveSpecClass:BuildClusterJewelGraphs()
1593+
-- Initializes temporary lookup tables used when loading legacy (v1) cluster hashes.
1594+
-- Returns true when legacy conversion is active for this graph rebuild.
1595+
function PassiveSpecClass:BeginLegacyClusterHashConversion()
15941596
local needsLegacyClusterHashConversion = (self.clusterHashFormatVersion or 2) < 2
15951597
self.legacyClusterNodeMap = needsLegacyClusterHashConversion and { } or nil
15961598
self.legacyClusterNodeMapReverse = needsLegacyClusterHashConversion and { } or nil
1599+
return needsLegacyClusterHashConversion
1600+
end
1601+
1602+
-- Legacy conversion updates node IDs while rebuilding cluster subgraphs.
1603+
-- This helper keeps forward and reverse mappings in sync.
1604+
function PassiveSpecClass:RegisterLegacyClusterNodeMap(legacyNodeId, currentNodeId)
1605+
if not self.legacyClusterNodeMap or not legacyNodeId or not currentNodeId then
1606+
return
1607+
end
1608+
self.legacyClusterNodeMap[legacyNodeId] = currentNodeId
1609+
if self.legacyClusterNodeMapReverse then
1610+
self.legacyClusterNodeMapReverse[currentNodeId] = legacyNodeId
1611+
end
1612+
end
1613+
1614+
-- Returns the remapped node ID when a valid legacy -> current cluster mapping exists.
1615+
function PassiveSpecClass:GetMappedClusterNodeId(nodeId)
1616+
local mappedNodeId = self.legacyClusterNodeMap and self.legacyClusterNodeMap[nodeId]
1617+
if mappedNodeId and self.nodes[mappedNodeId] then
1618+
return mappedNodeId
1619+
end
1620+
return nodeId
1621+
end
1622+
1623+
-- Applies legacy -> current remapping to both cluster allocations and socketed jewel ownership.
1624+
function PassiveSpecClass:ApplyLegacyClusterNodeRemap()
1625+
if not self.legacyClusterNodeMap then
1626+
return
1627+
end
1628+
1629+
local convertedNodeIds = { }
1630+
local seenNodeIds = { }
1631+
for _, nodeId in ipairs(self.allocSubgraphNodes) do
1632+
nodeId = self:GetMappedClusterNodeId(nodeId)
1633+
if not seenNodeIds[nodeId] then
1634+
seenNodeIds[nodeId] = true
1635+
t_insert(convertedNodeIds, nodeId)
1636+
end
1637+
end
1638+
self.allocSubgraphNodes = convertedNodeIds
1639+
1640+
-- Legacy cluster socket IDs can be normal tree node IDs (< 65536), so they bypass allocSubgraphNodes.
1641+
-- Move any such allocations onto their mapped current cluster node IDs.
1642+
for legacyNodeId, currentNodeId in pairs(self.legacyClusterNodeMap) do
1643+
if legacyNodeId ~= currentNodeId and self.allocNodes[legacyNodeId] and self.nodes[currentNodeId] then
1644+
self.allocNodes[legacyNodeId].alloc = false
1645+
self.allocNodes[legacyNodeId] = nil
1646+
if not seenNodeIds[currentNodeId] then
1647+
seenNodeIds[currentNodeId] = true
1648+
t_insert(self.allocSubgraphNodes, currentNodeId)
1649+
end
1650+
end
1651+
end
1652+
1653+
local convertedJewels = { }
1654+
for nodeId, itemId in pairs(self.jewels) do
1655+
convertedJewels[self:GetMappedClusterNodeId(nodeId)] = itemId
1656+
end
1657+
self.jewels = convertedJewels
1658+
end
1659+
1660+
-- Finalizes cluster hash conversion state after each graph rebuild.
1661+
function PassiveSpecClass:EndLegacyClusterHashConversion()
1662+
self.clusterHashFormatVersion = 2
1663+
self.legacyClusterNodeMap = nil
1664+
self.legacyClusterNodeMapReverse = nil
1665+
end
1666+
1667+
function PassiveSpecClass:BuildClusterJewelGraphs()
1668+
local needsLegacyClusterHashConversion = self:BeginLegacyClusterHashConversion()
15971669

15981670
-- Remove old subgraphs
15991671
for id, subGraph in pairs(self.subGraphs) do
@@ -1639,43 +1711,8 @@ function PassiveSpecClass:BuildClusterJewelGraphs()
16391711
end
16401712
end
16411713

1642-
if needsLegacyClusterHashConversion and self.legacyClusterNodeMap then
1643-
local convertedNodeIds = { }
1644-
local seenNodeIds = { }
1645-
for _, nodeId in ipairs(self.allocSubgraphNodes) do
1646-
local convertedNodeId = self.legacyClusterNodeMap[nodeId]
1647-
if convertedNodeId and self.nodes[convertedNodeId] then
1648-
nodeId = convertedNodeId
1649-
end
1650-
if not seenNodeIds[nodeId] then
1651-
seenNodeIds[nodeId] = true
1652-
t_insert(convertedNodeIds, nodeId)
1653-
end
1654-
end
1655-
self.allocSubgraphNodes = convertedNodeIds
1656-
1657-
-- Legacy cluster socket IDs can be normal tree node IDs (< 65536), so they bypass allocSubgraphNodes.
1658-
-- Move any such allocations onto their mapped current cluster node IDs.
1659-
for legacyNodeId, currentNodeId in pairs(self.legacyClusterNodeMap) do
1660-
if legacyNodeId ~= currentNodeId and self.allocNodes[legacyNodeId] and self.nodes[currentNodeId] then
1661-
self.allocNodes[legacyNodeId].alloc = false
1662-
self.allocNodes[legacyNodeId] = nil
1663-
if not seenNodeIds[currentNodeId] then
1664-
seenNodeIds[currentNodeId] = true
1665-
t_insert(self.allocSubgraphNodes, currentNodeId)
1666-
end
1667-
end
1668-
end
1669-
1670-
local convertedJewels = { }
1671-
for nodeId, itemId in pairs(self.jewels) do
1672-
local convertedNodeId = self.legacyClusterNodeMap[nodeId]
1673-
if convertedNodeId and self.nodes[convertedNodeId] then
1674-
nodeId = convertedNodeId
1675-
end
1676-
convertedJewels[nodeId] = itemId
1677-
end
1678-
self.jewels = convertedJewels
1714+
if needsLegacyClusterHashConversion then
1715+
self:ApplyLegacyClusterNodeRemap()
16791716
end
16801717

16811718
-- (Re-)allocate subgraph nodes
@@ -1698,9 +1735,95 @@ function PassiveSpecClass:BuildClusterJewelGraphs()
16981735

16991736
-- Rebuild node search cache because the tree might have changed
17001737
self.build.treeTab.viewer.searchStrCached = ""
1701-
self.clusterHashFormatVersion = 2
1702-
self.legacyClusterNodeMap = nil
1703-
self.legacyClusterNodeMapReverse = nil
1738+
self:EndLegacyClusterHashConversion()
1739+
end
1740+
1741+
-- Finds a specific expansion socket entry within a passive-tree group.
1742+
function PassiveSpecClass:FindClusterSocket(group, index)
1743+
for _, nodeId in ipairs(group.n) do
1744+
local node = self.tree.nodes[tonumber(nodeId)]
1745+
if node.expansionJewel and node.expansionJewel.index == index then
1746+
return node
1747+
end
1748+
end
1749+
end
1750+
1751+
-- Legacy parser behavior downsized the proxy group while descending into nested clusters.
1752+
-- Reproducing that traversal lets us recover legacy socket IDs for migration.
1753+
function PassiveSpecClass:BuildLegacyProxyGroup(proxyGroup, expansionJewelSize, clusterSizeIndex)
1754+
local legacyGroup = proxyGroup
1755+
local groupSize = expansionJewelSize
1756+
local guard = 0
1757+
while clusterSizeIndex < groupSize and guard < 4 do
1758+
local socket = self:FindClusterSocket(legacyGroup, 1) or self:FindClusterSocket(legacyGroup, 0)
1759+
if not socket then
1760+
break
1761+
end
1762+
local legacyProxyNode = self.tree.nodes[tonumber(socket.expansionJewel.proxy)]
1763+
if not legacyProxyNode or not legacyProxyNode.group then
1764+
break
1765+
end
1766+
legacyGroup = legacyProxyNode.group
1767+
groupSize = socket.expansionJewel.size
1768+
guard = guard + 1
1769+
end
1770+
return legacyGroup
1771+
end
1772+
1773+
-- Converts cluster orbit indices between different node-count spaces.
1774+
-- 12<->16 mappings reflect the 3.17 cluster export change; 6<->16 supports legacy nested mapping.
1775+
function PassiveSpecClass:TranslateClusterOrbitIndex(srcOidx, srcNodesPerOrbit, destNodesPerOrbit)
1776+
if srcNodesPerOrbit == destNodesPerOrbit then
1777+
return srcOidx
1778+
elseif srcNodesPerOrbit == 12 and destNodesPerOrbit == 16 then
1779+
return ({[0] = 0, 1, 3, 4, 5, 7, 8, 9, 11, 12, 13, 15})[srcOidx]
1780+
elseif srcNodesPerOrbit == 16 and destNodesPerOrbit == 12 then
1781+
return ({[0] = 0, 1, 1, 2, 3, 4, 4, 5, 6, 7, 7, 8, 9, 10, 10, 11})[srcOidx]
1782+
elseif srcNodesPerOrbit == 6 and destNodesPerOrbit == 16 then
1783+
return ({[0] = 0, 3, 5, 8, 11, 13})[srcOidx]
1784+
elseif srcNodesPerOrbit == 16 and destNodesPerOrbit == 6 then
1785+
return ({[0] = 0, 0, 0, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 5, 5, 5})[srcOidx]
1786+
else
1787+
-- there is no known case where this should happen...
1788+
launch:ShowErrMsg("^1Error: unexpected cluster jewel node counts %d -> %d", srcNodesPerOrbit, destNodesPerOrbit)
1789+
-- ...but if a future patch adds one, this should end up only a little krangled, close enough for initial skill data imports:
1790+
return m_floor(srcOidx * destNodesPerOrbit / srcNodesPerOrbit)
1791+
end
1792+
end
1793+
1794+
-- Applies proxy orbit offsets and converts from cluster template indices into tree orbit-space indices.
1795+
function PassiveSpecClass:ApplyClusterOrbitIndexAdjustment(indicies, startOidx, clusterTotalIndicies, skillsPerOrbit)
1796+
for _, node in pairs(indicies) do
1797+
local correctedNodeOidxRelativeToClusterIndicies = (node.oidx + startOidx) % clusterTotalIndicies
1798+
node.oidx = self:TranslateClusterOrbitIndex(correctedNodeOidxRelativeToClusterIndicies, clusterTotalIndicies, skillsPerOrbit)
1799+
end
1800+
end
1801+
1802+
-- Builds additional legacy node mappings by matching equivalent nodes in legacy and current orbit spaces.
1803+
function PassiveSpecClass:BuildLegacyClusterOrbitMappings(indicies, proxyNode, clusterTotalIndicies, skillsPerOrbit)
1804+
if not self.legacyClusterNodeMap then
1805+
return
1806+
end
1807+
1808+
local legacySkillsPerOrbit = self.tree.skillsPerOrbit[proxyNode.o + 1]
1809+
local legacyProxyNodeOidxRelativeToClusterIndicies = self:TranslateClusterOrbitIndex(proxyNode.oidx, legacySkillsPerOrbit, clusterTotalIndicies)
1810+
local legacyNodeIdsByOidx = { }
1811+
local currentNodeIdsByOidx = { }
1812+
for nodeIndex, node in pairs(indicies) do
1813+
local legacyNodeOidxRelativeToClusterIndicies = (nodeIndex + legacyProxyNodeOidxRelativeToClusterIndicies) % clusterTotalIndicies
1814+
local legacyNodeOidx = self:TranslateClusterOrbitIndex(legacyNodeOidxRelativeToClusterIndicies, clusterTotalIndicies, legacySkillsPerOrbit)
1815+
legacyNodeIdsByOidx[legacyNodeOidx] = node.id
1816+
1817+
local currentNodeOidxRelativeToClusterIndicies = self:TranslateClusterOrbitIndex(node.oidx, skillsPerOrbit, clusterTotalIndicies)
1818+
local currentNodeOidxInLegacySkillsPerOrbit = self:TranslateClusterOrbitIndex(currentNodeOidxRelativeToClusterIndicies, clusterTotalIndicies, legacySkillsPerOrbit)
1819+
currentNodeIdsByOidx[currentNodeOidxInLegacySkillsPerOrbit] = node.id
1820+
end
1821+
for oidx, legacyNodeId in pairs(legacyNodeIdsByOidx) do
1822+
local currentNodeId = currentNodeIdsByOidx[oidx]
1823+
if currentNodeId and legacyNodeId ~= currentNodeId then
1824+
self:RegisterLegacyClusterNodeMap(legacyNodeId, currentNodeId)
1825+
end
1826+
end
17041827
end
17051828

17061829
function PassiveSpecClass:BuildSubgraph(jewel, parentSocket, id, upSize, importedNodes, importedGroups)
@@ -1834,36 +1957,9 @@ function PassiveSpecClass:BuildSubgraph(jewel, parentSocket, id, upSize, importe
18341957
return
18351958
end
18361959

1837-
local function findSocket(group, index)
1838-
-- Find the given socket index in the group
1839-
for _, nodeId in ipairs(group.n) do
1840-
local node = self.tree.nodes[tonumber(nodeId)]
1841-
if node.expansionJewel and node.expansionJewel.index == index then
1842-
return node
1843-
end
1844-
end
1845-
end
1846-
18471960
local legacyProxyGroup
18481961
if self.legacyClusterNodeMap then
1849-
-- Legacy builds used proxy-group downsizing; reproduce it so old socket IDs can be mapped.
1850-
local legacyGroup = proxyGroup
1851-
local groupSize = expansionJewel.size
1852-
local guard = 0
1853-
while clusterJewel.sizeIndex < groupSize and guard < 4 do
1854-
local socket = findSocket(legacyGroup, 1) or findSocket(legacyGroup, 0)
1855-
if not socket then
1856-
break
1857-
end
1858-
local legacyProxyNode = self.tree.nodes[tonumber(socket.expansionJewel.proxy)]
1859-
if not legacyProxyNode or not legacyProxyNode.group then
1860-
break
1861-
end
1862-
legacyGroup = legacyProxyNode.group
1863-
groupSize = socket.expansionJewel.size
1864-
guard = guard + 1
1865-
end
1866-
legacyProxyGroup = legacyGroup
1962+
legacyProxyGroup = self:BuildLegacyProxyGroup(proxyGroup, expansionJewel.size, clusterJewel.sizeIndex)
18671963
end
18681964

18691965
-- Initialise orbit flags
@@ -1915,7 +2011,7 @@ function PassiveSpecClass:BuildSubgraph(jewel, parentSocket, id, upSize, importe
19152011

19162012
local function makeJewel(nodeIndex, jewelIndex)
19172013
-- Look for the socket
1918-
local socket = findSocket(proxyGroup, jewelIndex)
2014+
local socket = self:FindClusterSocket(proxyGroup, jewelIndex)
19192015
assert(socket, "Socket not found (ran out of sockets nani?)")
19202016

19212017
-- Construct the new node
@@ -1934,12 +2030,9 @@ function PassiveSpecClass:BuildSubgraph(jewel, parentSocket, id, upSize, importe
19342030
indicies[nodeIndex] = node
19352031

19362032
if legacyProxyGroup and self.legacyClusterNodeMap then
1937-
local legacySocket = findSocket(legacyProxyGroup, jewelIndex)
2033+
local legacySocket = self:FindClusterSocket(legacyProxyGroup, jewelIndex)
19382034
if legacySocket then
1939-
self.legacyClusterNodeMap[legacySocket.id] = node.id
1940-
if self.legacyClusterNodeMapReverse then
1941-
self.legacyClusterNodeMapReverse[node.id] = legacySocket.id
1942-
end
2035+
self:RegisterLegacyClusterNodeMap(legacySocket.id, node.id)
19432036
end
19442037
end
19452038
end
@@ -2067,61 +2160,11 @@ function PassiveSpecClass:BuildSubgraph(jewel, parentSocket, id, upSize, importe
20672160
assert(indicies[0], "No entrance to subgraph")
20682161
subGraph.entranceNode = indicies[0]
20692162

2070-
-- The nodes' oidx values we just calculated are all relative to the totalIndicies properties of Data/ClusterJewels,
2071-
-- but the PassiveTree rendering logic treats node.oidx as relative to the tree.skillsPerOrbit constants. Those used
2072-
-- to be the same, but as of 3.17 they can differ, so we need to translate the ClusterJewels-relative indices into
2073-
-- tree.skillsPerOrbit-relative indices before we invoke tree:ProcessNode or do math against proxyNode.oidx.
2074-
--
2075-
-- The specific 12<->16 mappings are derived from https://github.com/grindinggear/skilltree-export/blob/3.17.0/README.md
2076-
local function translateOidx(srcOidx, srcNodesPerOrbit, destNodesPerOrbit)
2077-
if srcNodesPerOrbit == destNodesPerOrbit then
2078-
return srcOidx
2079-
elseif srcNodesPerOrbit == 12 and destNodesPerOrbit == 16 then
2080-
return ({[0] = 0, 1, 3, 4, 5, 7, 8, 9, 11, 12, 13, 15})[srcOidx]
2081-
elseif srcNodesPerOrbit == 16 and destNodesPerOrbit == 12 then
2082-
return ({[0] = 0, 1, 1, 2, 3, 4, 4, 5, 6, 7, 7, 8, 9, 10, 10, 11})[srcOidx]
2083-
elseif srcNodesPerOrbit == 6 and destNodesPerOrbit == 16 then
2084-
return ({[0] = 0, 3, 5, 8, 11, 13})[srcOidx]
2085-
elseif srcNodesPerOrbit == 16 and destNodesPerOrbit == 6 then
2086-
return ({[0] = 0, 0, 0, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 5, 5, 5})[srcOidx]
2087-
else
2088-
-- there is no known case where this should happen...
2089-
launch:ShowErrMsg("^1Error: unexpected cluster jewel node counts %d -> %d", srcNodesPerOrbit, destNodesPerOrbit)
2090-
-- ...but if a future patch adds one, this should end up only a little krangled, close enough for initial skill data imports:
2091-
return m_floor(srcOidx * destNodesPerOrbit / srcNodesPerOrbit)
2092-
end
2093-
end
2094-
2163+
-- Convert from cluster-template index space into the tree's orbit index space.
20952164
local skillsPerOrbit = self.tree.skillsPerOrbit[clusterJewel.sizeIndex+2]
20962165
local startOidx = data.clusterJewels.orbitOffsets[proxyNode.id][clusterJewel.sizeIndex]
2097-
-- Translate oidx positioning to TreeData-relative values
2098-
for _, node in pairs(indicies) do
2099-
local correctedNodeOidxRelativeToClusterIndicies = (node.oidx + startOidx) % clusterJewel.totalIndicies
2100-
local correctedNodeOidxRelativeToTreeSkillsPerOrbit = translateOidx(correctedNodeOidxRelativeToClusterIndicies, clusterJewel.totalIndicies, skillsPerOrbit)
2101-
node.oidx = correctedNodeOidxRelativeToTreeSkillsPerOrbit
2102-
end
2103-
2104-
if self.legacyClusterNodeMap then
2105-
local legacySkillsPerOrbit = self.tree.skillsPerOrbit[proxyNode.o+1]
2106-
local legacyProxyNodeOidxRelativeToClusterIndicies = translateOidx(proxyNode.oidx, legacySkillsPerOrbit, clusterJewel.totalIndicies)
2107-
local legacyNodeIdsByOidx = { }
2108-
local currentNodeIdsByOidx = { }
2109-
for nodeIndex, node in pairs(indicies) do
2110-
local legacyNodeOidxRelativeToClusterIndicies = (nodeIndex + legacyProxyNodeOidxRelativeToClusterIndicies) % clusterJewel.totalIndicies
2111-
local legacyNodeOidx = translateOidx(legacyNodeOidxRelativeToClusterIndicies, clusterJewel.totalIndicies, legacySkillsPerOrbit)
2112-
legacyNodeIdsByOidx[legacyNodeOidx] = node.id
2113-
local currentNodeOidxRelativeToClusterIndicies = translateOidx(node.oidx, skillsPerOrbit, clusterJewel.totalIndicies)
2114-
local currentNodeOidxInLegacySkillsPerOrbit = translateOidx(currentNodeOidxRelativeToClusterIndicies, clusterJewel.totalIndicies, legacySkillsPerOrbit)
2115-
currentNodeIdsByOidx[currentNodeOidxInLegacySkillsPerOrbit] = node.id
2116-
end
2117-
for oidx, legacyNodeId in pairs(legacyNodeIdsByOidx) do
2118-
local currentNodeId = currentNodeIdsByOidx[oidx]
2119-
if currentNodeId and legacyNodeId ~= currentNodeId then
2120-
self.legacyClusterNodeMap[legacyNodeId] = currentNodeId
2121-
self.legacyClusterNodeMapReverse[currentNodeId] = legacyNodeId
2122-
end
2123-
end
2124-
end
2166+
self:ApplyClusterOrbitIndexAdjustment(indicies, startOidx, clusterJewel.totalIndicies, skillsPerOrbit)
2167+
self:BuildLegacyClusterOrbitMappings(indicies, proxyNode, clusterJewel.totalIndicies, skillsPerOrbit)
21252168

21262169
-- Perform processing on nodes to calculate positions, parse mods, and other goodies
21272170
for _, node in ipairs(subGraph.nodes) do

0 commit comments

Comments
 (0)