Skip to content

Commit 01b0ca0

Browse files
author
LocalIdentity
committed
Working solution
1 parent 40d7761 commit 01b0ca0

4 files changed

Lines changed: 130 additions & 3 deletions

File tree

src/Classes/ImportTab.lua

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -700,6 +700,8 @@ function ImportTabClass:ImportPassiveTreeAndJewels(json, charData)
700700
end
701701
end
702702

703+
-- Character import uses current GGG cluster hashes.
704+
self.build.spec.clusterHashFormatVersion = 2
703705
self.build.spec:ImportFromNodeList(charPassiveData.character,
704706
charPassiveData.ascendancy,
705707
charPassiveData.alternate_ascendancy or 0,

src/Classes/ItemSlotControl.lua

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,10 @@ function ItemSlotClass:SetSelItemId(selItemId)
7272
end
7373

7474
function ItemSlotClass:Populate()
75+
if self.nodeId and self.itemsTab.build.spec then
76+
self.selItemId = self.itemsTab.build.spec.jewels[self.nodeId] or 0
77+
end
78+
7579
wipeTable(self.items)
7680
wipeTable(self.list)
7781
self.items[1] = 0

src/Classes/PassiveSpec.lua

Lines changed: 123 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -84,10 +84,15 @@ function PassiveSpecClass:Init(treeVersion, convert)
8484

8585
-- Cached highlight path for Split Personality jewels
8686
self.splitPersonalityPath = { }
87+
88+
-- Cluster hash format version used by saved builds; 2 is current.
89+
self.clusterHashFormatVersion = 2
8790
end
8891

8992
function PassiveSpecClass:Load(xml, dbFileName)
9093
self.title = xml.attrib.title
94+
-- Specs without this attribute predate the hash-fix migration and are treated as legacy.
95+
self.clusterHashFormatVersion = tonumber(xml.attrib.clusterHashFormatVersion) or (xml.attrib.nodes and 1 or 2)
9196
local url
9297
for _, node in pairs(xml) do
9398
if type(node) == "table" then
@@ -190,6 +195,7 @@ function PassiveSpecClass:Save(xml)
190195
xml.attrib = {
191196
title = self.title,
192197
treeVersion = self.treeVersion,
198+
clusterHashFormatVersion = tostring(self.clusterHashFormatVersion or 2),
193199
-- New format
194200
classId = tostring(self.curClassId),
195201
ascendClassId = tostring(self.curAscendClassId),
@@ -868,6 +874,17 @@ function PassiveSpecClass:GetJewel(itemId)
868874
return item
869875
end
870876

877+
function PassiveSpecClass:GetSocketedJewel(nodeId)
878+
local itemId = self.jewels[nodeId]
879+
if (not itemId or itemId == 0) and self.legacyClusterNodeMapReverse then
880+
local legacyNodeId = self.legacyClusterNodeMapReverse[nodeId]
881+
if legacyNodeId then
882+
itemId = self.jewels[legacyNodeId]
883+
end
884+
end
885+
return self:GetJewel(itemId)
886+
end
887+
871888
-- Perform a breadth-first search of the tree, starting from this node, and determine if it is the closest node to any other nodes
872889
function PassiveSpecClass:BuildPathFromNode(root)
873890
root.pathDist = 0
@@ -1574,6 +1591,10 @@ function PassiveSpecClass:ReconnectNodeToClassStart(node)
15741591
end
15751592

15761593
function PassiveSpecClass:BuildClusterJewelGraphs()
1594+
local needsLegacyClusterHashConversion = (self.clusterHashFormatVersion or 2) < 2
1595+
self.legacyClusterNodeMap = needsLegacyClusterHashConversion and { } or nil
1596+
self.legacyClusterNodeMapReverse = needsLegacyClusterHashConversion and { } or nil
1597+
15771598
-- Remove old subgraphs
15781599
for id, subGraph in pairs(self.subGraphs) do
15791600
for _, node in ipairs(subGraph.nodes) do
@@ -1611,13 +1632,52 @@ function PassiveSpecClass:BuildClusterJewelGraphs()
16111632
end
16121633
for nodeId in pairs(self.tree.sockets) do
16131634
local node = self.tree.nodes[nodeId]
1614-
local jewel = self:GetJewel(self.jewels[nodeId])
1635+
local jewel = self:GetSocketedJewel(nodeId)
16151636
if node and node.expansionJewel and node.expansionJewel.size == 2 and jewel and jewel.jewelData.clusterJewelValid then
16161637
-- This is a Large Jewel Socket, and it has a cluster jewel in it
16171638
self:BuildSubgraph(jewel, self.nodes[nodeId], nil, nil, importedNodes, importedGroups)
16181639
end
16191640
end
16201641

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
1679+
end
1680+
16211681
-- (Re-)allocate subgraph nodes
16221682
for _, nodeId in ipairs(self.allocSubgraphNodes) do
16231683
local node = self.nodes[nodeId]
@@ -1638,6 +1698,9 @@ function PassiveSpecClass:BuildClusterJewelGraphs()
16381698

16391699
-- Rebuild node search cache because the tree might have changed
16401700
self.build.treeTab.viewer.searchStrCached = ""
1701+
self.clusterHashFormatVersion = 2
1702+
self.legacyClusterNodeMap = nil
1703+
self.legacyClusterNodeMapReverse = nil
16411704
end
16421705

16431706
function PassiveSpecClass:BuildSubgraph(jewel, parentSocket, id, upSize, importedNodes, importedGroups)
@@ -1781,6 +1844,28 @@ function PassiveSpecClass:BuildSubgraph(jewel, parentSocket, id, upSize, importe
17811844
end
17821845
end
17831846

1847+
local legacyProxyGroup
1848+
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
1867+
end
1868+
17841869
-- Initialise orbit flags
17851870
local nodeOrbit = clusterJewel.sizeIndex + 1
17861871
subGraph.group.oo[nodeOrbit] = true
@@ -1847,6 +1932,16 @@ function PassiveSpecClass:BuildSubgraph(jewel, parentSocket, id, upSize, importe
18471932
}
18481933
t_insert(subGraph.nodes, node)
18491934
indicies[nodeIndex] = node
1935+
1936+
if legacyProxyGroup and self.legacyClusterNodeMap then
1937+
local legacySocket = findSocket(legacyProxyGroup, jewelIndex)
1938+
if legacySocket then
1939+
self.legacyClusterNodeMap[legacySocket.id] = node.id
1940+
if self.legacyClusterNodeMapReverse then
1941+
self.legacyClusterNodeMapReverse[node.id] = legacySocket.id
1942+
end
1943+
end
1944+
end
18501945
end
18511946

18521947
-- First pass: sockets
@@ -1985,6 +2080,10 @@ function PassiveSpecClass:BuildSubgraph(jewel, parentSocket, id, upSize, importe
19852080
return ({[0] = 0, 1, 3, 4, 5, 7, 8, 9, 11, 12, 13, 15})[srcOidx]
19862081
elseif srcNodesPerOrbit == 16 and destNodesPerOrbit == 12 then
19872082
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]
19882087
else
19892088
-- there is no known case where this should happen...
19902089
launch:ShowErrMsg("^1Error: unexpected cluster jewel node counts %d -> %d", srcNodesPerOrbit, destNodesPerOrbit)
@@ -1997,12 +2096,33 @@ function PassiveSpecClass:BuildSubgraph(jewel, parentSocket, id, upSize, importe
19972096
local startOidx = data.clusterJewels.orbitOffsets[proxyNode.id][clusterJewel.sizeIndex]
19982097
-- Translate oidx positioning to TreeData-relative values
19992098
for _, node in pairs(indicies) do
2000-
local startOidxRelativeToClusterIndicies = translateOidx(startOidx, skillsPerOrbit, clusterJewel.totalIndicies)
20012099
local correctedNodeOidxRelativeToClusterIndicies = (node.oidx + startOidx) % clusterJewel.totalIndicies
20022100
local correctedNodeOidxRelativeToTreeSkillsPerOrbit = translateOidx(correctedNodeOidxRelativeToClusterIndicies, clusterJewel.totalIndicies, skillsPerOrbit)
20032101
node.oidx = correctedNodeOidxRelativeToTreeSkillsPerOrbit
20042102
end
20052103

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
2125+
20062126
-- Perform processing on nodes to calculate positions, parse mods, and other goodies
20072127
for _, node in ipairs(subGraph.nodes) do
20082128
node.linked = { }
@@ -2041,7 +2161,7 @@ function PassiveSpecClass:BuildSubgraph(jewel, parentSocket, id, upSize, importe
20412161
end
20422162
if node.type == "Socket" then
20432163
-- Recurse to smaller jewels
2044-
local jewel = self:GetJewel(self.jewels[node.id])
2164+
local jewel = self:GetSocketedJewel(node.id)
20452165
if jewel and jewel.jewelData.clusterJewelValid then
20462166
self:BuildSubgraph(jewel, node, id, upSize, importedNodes, importedGroups)
20472167
end

src/Classes/TreeTab.lua

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -520,6 +520,7 @@ function TreeTabClass:PostLoad()
520520
for _, spec in ipairs(self.specList) do
521521
spec:PostLoad()
522522
end
523+
self.build.itemsTab:PopulateSlots()
523524
end
524525

525526
function TreeTabClass:Save(xml)

0 commit comments

Comments
 (0)