Skip to content
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
96 changes: 76 additions & 20 deletions src/Classes/PassiveSpec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,12 @@ function PassiveSpecClass:Load(xml, dbFileName)
end
local hashList = { }
for hash in xml.attrib.nodes:gmatch("%d+") do
t_insert(hashList, tonumber(hash))
hash = tonumber(hash)
-- if cluster node and old node flag is true
if not self.oldClusterGeneration and (hash >= 65536) and (band(hash, 0x8000) == 0) then
self.oldClusterGeneration = true
end
t_insert(hashList, hash)
end
local masteryEffects = { }
if xml.attrib.masteryEffects then
Expand Down Expand Up @@ -476,39 +481,81 @@ function PassiveSpecClass:EncodeURL(prefix)
local clusterCount = 0
local masteryCount = 0

local nodeIds = {}
local clusterNodeIds = {}
local masteryNodeIds = {}
local masteryNodes = {}

local computeClusterId = function(id, oidx)
local socketType = band(b_rshift(id, 11), 3) -- 0 if socketed into large socket, 1 for medium, 2 for small
local groupSize = band(b_rshift(id, 4), 3)
local largeIndex = band(b_rshift(id, 6), 7)
local mediumIndex = band(b_rshift(id, 9), 3)
local nodeIndex
if socketType == 0 and groupSize > 0 then
nodeIndex = ({[0] = 0, 1, 1, 2, 3, 4, 4, 5, 6, 7, 7, 8, 9, 10, 10, 11})[oidx]
elseif socketType == 1 and groupSize == 1 then
nodeIndex = ({[0] = 0, 0, 0, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 5, 5, 5})[oidx] -- index mapping for 2 and 10 are estimated, could not figure out mapping based on testing
elseif socketType ~= 2 and groupSize == 0 then
nodeIndex = math.floor(oidx/2)
else
nodeIndex = oidx
end
-- Extended hash numbering logic
-- TLDR: Depth first numbering using cluster jewel socket indexes
-- Starting from the large cluster jewel socket index 0, there are 12 possible nodes, 3 of which can be medium cluster jewel sockets (Voices)
-- Then for each possible medium cluster socket there are 6 possible nodes, one of which can be a small cluster jewel socket
-- A small cluster jewel socket can have 3 nodes
-- 12 + 3 * (6 + 3) = 39 so large cluster jewel sockets start indexing from large index * 39
-- Medium cluster jewel sockets start indexing from parent start index + 12 (to account for the 12 possible nodes)
-- Small cluster jewel sockets start indexing from parent start index + 6 (to account for the 6 possible nodes)
nodeIndex = nodeIndex + largeIndex * 39
if socketType > 0 then
nodeIndex = nodeIndex + 12 + mediumIndex * 9
end
if socketType > 1 then
nodeIndex = nodeIndex + 6
end
return nodeIndex
end

for id, node in pairs(self.allocNodes) do
if node.type ~= "ClassStart" and node.type ~= "AscendClassStart" and id < 65536 and nodeCount < 255 then
t_insert(a, m_floor(id / 256))
t_insert(a, id % 256)
if node.type ~= "ClassStart" and node.type ~= "AscendClassStart" and id < 65536 and nodeCount < 255 and not node.hashId then
t_insert(nodeIds, id)
nodeCount = nodeCount + 1
if self.masterySelections[node.id] then
local effect_id = self.masterySelections[node.id]
t_insert(masteryNodeIds, m_floor(effect_id / 256))
t_insert(masteryNodeIds, effect_id % 256)
t_insert(masteryNodeIds, m_floor(node.id / 256))
t_insert(masteryNodeIds, node.id % 256)
t_insert(masteryNodes, {nodeId = node.id, effectId = self.masterySelections[node.id]})
masteryCount = masteryCount + 1
end
elseif id >= 65536 then
local clusterId = id - 65536
t_insert(clusterNodeIds, m_floor(clusterId / 256))
t_insert(clusterNodeIds, clusterId % 256)
elseif id >= 65536 or node.hashId then
local clusterId = computeClusterId(node.hashId or id, node.oidx)
t_insert(clusterNodeIds, clusterId)
clusterCount = clusterCount + 1
end
end
t_insert(a, 7, nodeCount)

-- Sort ids to ensure generated URL is the same as PoE
table.sort(nodeIds)
table.sort(masteryNodes, function(a, b) return a.nodeId < b.nodeId end)
table.sort(clusterNodeIds)

t_insert(a, nodeCount)
for _, id in pairs(nodeIds) do
t_insert(a, m_floor(id / 256))
t_insert(a, id % 256)
end

t_insert(a, clusterCount)
for _, id in pairs(clusterNodeIds) do
t_insert(a, id)
t_insert(a, m_floor(id / 256))
t_insert(a, id % 256)
end

t_insert(a, masteryCount)
for _, id in pairs(masteryNodeIds) do
t_insert(a, id)
for _, node in pairs(masteryNodes) do
t_insert(a, m_floor(node.effectId / 256))
t_insert(a, node.effectId % 256)
t_insert(a, m_floor(node.nodeId / 256))
t_insert(a, node.nodeId % 256)
end

return (prefix or "")..common.base64.encode(string.char(unpack(a))):gsub("+","-"):gsub("/","_")
Expand Down Expand Up @@ -1369,9 +1416,17 @@ function PassiveSpecClass:BuildSubgraph(jewel, parentSocket, id, upSize, importe
-- 4-5: Group size (0-2)
-- 6-8: Large index (0-5)
-- 9-10: Medium index (0-2)
-- 11-15: Unused
-- 11-12: Socket type - 0 for large, 1 for medium, or 2 for small
-- This is to differentiate between a small/medium cluster jewel socketed into a large/medium/small cluster socket
-- 13-14: Unused
-- 15: 1 since version 2.38.5? to be used for backwards compatibility, 0 otherwise
-- This bit is used to check when builds are later imported to see if the old method of generating nodeIds need to be applied
-- 16: 1 (signal bit, to prevent conflict with node hashes)
id = id or 0x10000
if self.oldClusterGeneration then
id = id or 0x10000
else
id = (id and id + 0x00800) or 0x18000 -- Increment the socket type when called with an existing id
end
if expansionJewel.size == 2 then
id = id + b_lshift(expansionJewel.index, 6)
elseif expansionJewel.size == 1 then
Expand Down Expand Up @@ -1556,6 +1611,7 @@ function PassiveSpecClass:BuildSubgraph(jewel, parentSocket, id, upSize, importe
group = subGraph.group,
o = nodeOrbit,
oidx = nodeIndex,
hashId = nodeId + nodeIndex -- Property used when generating the extended hash for export URL
}
t_insert(subGraph.nodes, node)
indicies[nodeIndex] = node
Expand Down