Files
PathOfBuilding/Modules/Tree.lua
2016-05-05 01:15:55 +10:00

339 lines
9.5 KiB
Lua

local launch, cfg, main = ...
local pairs = pairs
local ipairs = ipairs
local m_min = math.min
local m_max = math.max
local m_pi = math.pi
local m_sin = math.sin
local m_cos = math.cos
local m_tan = math.tan
local m_sqrt = math.sqrt
local t_insert = table.insert
local function cacheImage(imgName, url)
local imgFile = io.open(imgName, "r")
if imgFile then
imgFile:close()
else
ConPrintf("Downloading '%s'...", imgName)
imgFile = io.open(imgName, "wb")
local easy = common.curl.easy()
easy:setopt_url(url)
easy:setopt_writefunction(imgFile)
easy:perform()
easy:close()
imgFile:close()
end
end
local TreeClass = { }
TreeClass.__index = TreeClass
function TreeClass:BuildConnector(node1, node2)
local conn = {
ascendancyName = node1.ascendancyName,
nodeId1 = node1.id,
nodeId2 = node2.id,
c = { }
}
if node1.g == node2.g and node1.o == node2.o then
conn.type = "Orbit" .. node1.o
if node1.angle > node2.angle then
node1, node2 = node2, node1
end
local span = node2.angle - node1.angle
if span > m_pi then
node1, node2 = node2, node1
span = m_pi * 2 - span
end
local clipAngle = m_pi / 4 - span / 2
local p = 1 - m_max(m_tan(clipAngle), 0)
local angle = node1.angle - clipAngle
local norm, act = { }, { }
for tbl, state in pairs({[norm] = "Normal", [act] = "Active"}) do
local art = self.assets[conn.type..state]
local size = art.width * 2 * 1.33
local oX, oY = size * m_sqrt(2) * m_sin(angle + m_pi/4), size * m_sqrt(2) * -m_cos(angle + m_pi/4)
local cX, cY = node1.group.x + oX, node1.group.y + oY
tbl[1], tbl[2] = node1.group.x, node1.group.y
tbl[3], tbl[4] = cX + (size * m_sin(angle) - oX) * p, cY + (size * -m_cos(angle) - oY) * p
tbl[5], tbl[6] = cX, cY
tbl[7], tbl[8] = cX + (size * m_cos(angle) - oX) * p, cY + (size * m_sin(angle) - oY) * p
end
conn.vert = { Normal = norm, Intermediate = norm, Active = act }
conn.c[9], conn.c[10] = 1, 1
conn.c[11], conn.c[12] = 0, p
conn.c[13], conn.c[14] = 0, 0
conn.c[15], conn.c[16] = p, 0
else
conn.type = "LineConnector"
local art = self.assets.LineConnectorNormal
local vX, vY = node2.x - node1.x, node2.y - node1.y
local dist = m_sqrt(vX * vX + vY * vY)
local scale = art.height * 1.33 / dist
local nX, nY = vX * scale, vY * scale
local endS = dist / (art.width * 1.33)
conn[1], conn[2] = node1.x - nY, node1.y + nX
conn[3], conn[4] = node1.x + nY, node1.y - nX
conn[5], conn[6] = node2.x + nY, node2.y - nX
conn[7], conn[8] = node2.x - nY, node2.y + nX
conn.vert = { Normal = conn, Intermediate = conn, Active = conn }
conn.c[9], conn.c[10] = 0, 1
conn.c[11], conn.c[12] = 0, 0
conn.c[13], conn.c[14] = endS, 0
conn.c[15], conn.c[16] = endS, 1
end
return conn
end
function TreeClass.NewTree()
os.execute("mkdir Data")
ConPrintf("Loading JSON...")
local treeText, classText
local treeFile = io.open("Data/tree.json", "r")
local classFile = io.open("Data/classes.json", "r")
if treeFile and classFile then
treeText = treeFile:read("*a")
treeFile:close()
classText = classFile:read("*a")
classFile:close()
else
if treeFile then
treeFile:close()
elseif classFile then
classFile:close()
end
ConPrintf("Downloading JSON...")
local page = ""
local easy = common.curl.easy()
easy:setopt_url("https://www.pathofexile.com/passive-skill-tree/")
easy:setopt_writefunction(function(data)
page = page..data
return true
end)
easy:perform()
easy:close()
treeText = page:match("var passiveSkillTreeData = (%b{})")
treeFile = io.open("Data/tree.json", "w")
treeFile:write(treeText)
treeFile:close()
classText = page:match("ascClasses: (%b{})")
classFile = io.open("Data/classes.json", "w")
classFile:write(classText)
classFile:close()
end
local self = common.json.decode(treeText)
self.classes = { }
for id, data in pairs(common.json.decode(classText)) do
self.classes[tonumber(id)] = data
data.classes["0"] = { name = "None "}
end
setmetatable(self, TreeClass)
self.size = m_min(self.max_x - self.min_x, self.max_y - self.min_y) * 1.1
ConPrintf("Loading assets...")
for name, data in pairs(self.assets) do
local imgName = "Data/"..name..".png"
cacheImage(imgName, data["0.3835"] or data["1"])
data.handle = NewImageHandle()
data.handle:Load(imgName)
data.width, data.height = data.handle:ImageSize()
end
local spriteMap = { }
local spriteSheets = { }
for type, data in pairs(self.skillSprites) do
local maxZoom = data[#data]
local sheet = spriteSheets[maxZoom.filename]
if not sheet then
sheet = { }
local imgName = "Data/"..maxZoom.filename
cacheImage(imgName, self.imageRoot.."build-gen/passive-skill-sprite/"..maxZoom.filename)
sheet.handle = NewImageHandle()
sheet.handle:Load(imgName, "CLAMP")
sheet.width, sheet.height = sheet.handle:ImageSize()
spriteSheets[maxZoom.filename] = sheet
end
for name, coords in pairs(maxZoom.coords) do
if not spriteMap[name] then
spriteMap[name] = { }
end
spriteMap[name][type] = {
handle = sheet.handle,
width = coords.w,
height = coords.h,
[1] = coords.x / sheet.width,
[2] = coords.y / sheet.height,
[3] = (coords.x + coords.w) / sheet.width,
[4] = (coords.y + coords.h) / sheet.height
}
end
end
local nodeOverlay = {
normal = {
alloc = "PSSkillFrameActive",
path = "PSSkillFrameHighlighted",
unalloc = "PSSkillFrame",
allocAscend = "PassiveSkillScreenAscendancyFrameSmallAllocated",
pathAscend = "PassiveSkillScreenAscendancyFrameSmallCanAllocate",
unallocAscend = "PassiveSkillScreenAscendancyFrameSmallNormal"
},
notable = {
alloc = "NotableFrameAllocated",
path = "NotableFrameCanAllocate",
unalloc = "NotableFrameUnallocated",
allocAscend = "PassiveSkillScreenAscendancyFrameLargeAllocated",
pathAscend = "PassiveSkillScreenAscendancyFrameLargeCanAllocate",
unallocAscend = "PassiveSkillScreenAscendancyFrameLargeNormal"
},
keystone = {
alloc = "KeystoneFrameAllocated",
path = "KeystoneFrameCanAllocate",
unalloc = "KeystoneFrameUnallocated"
},
socket = {
alloc = "JewelFrameAllocated",
path = "JewelFrameCanAllocate",
unalloc = "JewelFrameUnallocated"
}
}
for type, data in pairs(nodeOverlay) do
local size = self.assets[data.unalloc].width * 1.33
data.size = size
data.rsq = size * size
end
ConPrintf("Building nodes...")
local nodeMap = { }
local orbitMult = { [0] = 0, m_pi / 3, m_pi / 6, m_pi / 6, m_pi / 20 }
local orbitDist = { [0] = 0, 82, 162, 335, 493 }
for _, node in ipairs(self.nodes) do
nodeMap[node.id] = node
if node.spc[1] then
node.type = "class"
elseif node.isAscendancyStart then
node.type = "ascendClass"
elseif node.m then
node.type = "mastery"
elseif node.isJewelSocket then
node.type = "socket"
elseif node.ks then
node.type = "keystone"
elseif node["not"] then
node.type = "notable"
else
node.type = "normal"
end
node.sprites = spriteMap[node.icon]
node.overlay = nodeOverlay[node.type]
node.linkedId = { }
local group = self.groups[tostring(node.g)]
group.ascendancyName = node.ascendancyName
if node.isAscendancyStart then
group.isAscendancyStart = true
end
node.group = group
node.angle = node.oidx * orbitMult[node.o]
local dist = orbitDist[node.o]
node.x = group.x + m_sin(node.angle) * dist
node.y = group.y - m_cos(node.angle) * dist
node.mods = { }
node.modKey = ""
local i = 1
while node.sd[i] do
local line = node.sd[i]
local list, extra
if line:match("\n") then
list, extra = mod.parseMod(line:gsub("\n", " "))
if list and not extra then
node.sd[i] = line:gsub("\n", " ")
else
table.remove(node.sd, i)
local si = i
for subLine in line:gmatch("[^\n]+") do
table.insert(node.sd, si, subLine)
si = si + 1
end
list, extra = mod.parseMod(node.sd[i])
end
else
list, extra = mod.parseMod(line)
end
if not list then
node.unknown = true
elseif extra then
node.extra = true
else
for k, v in pairs(list) do
node.modKey = node.modKey..k.."="..tostring(v)..","
end
end
node.mods[i] = { list = list, extra = extra }
i = i + 1
end
end
ConPrintf("Building connections...")
self.conn = { }
for _, node in ipairs(self.nodes) do
for _, otherId in ipairs(node.out) do
local other = nodeMap[otherId]
t_insert(node.linkedId, otherId)
t_insert(other.linkedId, node.id)
if node.type ~= "class" and other.type ~= "class" and node.ascendancyName == other.ascendancyName then
t_insert(self.conn, self:BuildConnector(node, other))
end
end
end
self.classNameMap = { }
self.ascendNameMap = { ["None"] = { classId = 0, ascendClassId = 0 } }
for classId, class in pairs(self.classes) do
self.classNameMap[class.name] = classId
for ascendClassId, ascendClass in pairs(class.classes) do
self.ascendNameMap[ascendClass.name] = {
classId = classId,
ascendClassId = ascendClassId
}
end
end
local classArt = {
[0] = "centerscion",
[1] = "centermarauder",
[2] = "centerranger",
[3] = "centerwitch",
[4] = "centerduelist",
[5] = "centertemplar",
[6] = "centershadow"
}
for _, node in ipairs(self.nodes) do
if node.type == "class" then
local class = self.classes[node.spc[1]]
class.startNodeId = node.id
node.startArt = classArt[node.spc[1]]
for _, otherId in ipairs(node.linkedId) do
local other = nodeMap[otherId]
if other.type == "ascendClass" then
for _, ascendClass in pairs(class.classes) do
if other.ascendancyName == ascendClass.name then
ascendClass.startNodeId = otherId
break
end
end
end
end
end
end
return self
end
return TreeClass