Adding class library
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -3,7 +3,6 @@
|
||||
|
||||
*.xml
|
||||
*.cfg
|
||||
*.lnk
|
||||
Data/
|
||||
|
||||
# User-specific files
|
||||
|
||||
82
Classes/ButtonControl.lua
Normal file
82
Classes/ButtonControl.lua
Normal file
@@ -0,0 +1,82 @@
|
||||
-- Path of Building
|
||||
--
|
||||
-- Class: Button Control
|
||||
-- Basic button control.
|
||||
--
|
||||
|
||||
local ButtonClass = common.NewClass("ButtonControl", function(self, x, y, width, height, label, onClick, enableFunc)
|
||||
self.x = x
|
||||
self.y = y
|
||||
self.width = width
|
||||
self.height = height
|
||||
self.label = label
|
||||
self.onClick = onClick
|
||||
self.enableFunc = enableFunc
|
||||
end)
|
||||
|
||||
function ButtonClass:IsMouseOver()
|
||||
if self.hidden then
|
||||
return false
|
||||
end
|
||||
local x = type(self.x) == "function" and self:x() or self.x
|
||||
local y = type(self.y) == "function" and self:y() or self.y
|
||||
local cx, cy = GetCursorPos()
|
||||
return cx >= x and cy >= y and cx < x + self.width and cy < y + self.height
|
||||
end
|
||||
|
||||
function ButtonClass:Draw()
|
||||
if self.hidden then
|
||||
return
|
||||
end
|
||||
local x = type(self.x) == "function" and self:x() or self.x
|
||||
local y = type(self.y) == "function" and self:y() or self.y
|
||||
local enabled = not self.enableFunc or self.enableFunc()
|
||||
local mOver = self:IsMouseOver()
|
||||
if not enabled then
|
||||
SetDrawColor(0.33, 0.33, 0.33)
|
||||
elseif mOver then
|
||||
SetDrawColor(1, 1, 1)
|
||||
else
|
||||
SetDrawColor(0.5, 0.5, 0.5)
|
||||
end
|
||||
DrawImage(nil, x, y, self.width, self.height)
|
||||
if not enabled then
|
||||
SetDrawColor(0, 0, 0)
|
||||
elseif self.clicked and mOver then
|
||||
SetDrawColor(0.5, 0.5, 0.5)
|
||||
elseif mOver then
|
||||
SetDrawColor(0.33, 0.33, 0.33)
|
||||
else
|
||||
SetDrawColor(0, 0, 0)
|
||||
end
|
||||
DrawImage(nil, x + 1, y + 1, self.width - 2, self.height - 2)
|
||||
if enabled then
|
||||
SetDrawColor(1, 1, 1)
|
||||
else
|
||||
SetDrawColor(0.33, 0.33, 0.33)
|
||||
end
|
||||
DrawString(x + self.width / 2, y + 2, "CENTER_X", self.height - 4, "VAR", self.label)
|
||||
end
|
||||
|
||||
function ButtonClass:OnKeyDown(key)
|
||||
if self.enableFunc and not self.enableFunc() then
|
||||
return true
|
||||
end
|
||||
if key == "LEFTBUTTON" then
|
||||
self.clicked = true
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function ButtonClass:OnKeyUp(key)
|
||||
if self.enableFunc and not self.enableFunc() then
|
||||
return true
|
||||
end
|
||||
if key == "LEFTBUTTON" then
|
||||
if self:IsMouseOver() then
|
||||
self.onClick()
|
||||
end
|
||||
end
|
||||
self.clicked = false
|
||||
return true
|
||||
end
|
||||
151
Classes/DropDownControl.lua
Normal file
151
Classes/DropDownControl.lua
Normal file
@@ -0,0 +1,151 @@
|
||||
-- Path of Building
|
||||
--
|
||||
-- Class: DropDown Control
|
||||
-- Basic drop down control.
|
||||
--
|
||||
|
||||
local DropDownClass = common.NewClass("DropDownControl", function(self, x, y, width, height, list, selFunc, enableFunc)
|
||||
self.x = x
|
||||
self.y = y
|
||||
self.width = width
|
||||
self.height = height
|
||||
self.list = list or { }
|
||||
self.sel = 1
|
||||
self.selFunc = selFunc
|
||||
self.enableFunc = enableFunc
|
||||
end)
|
||||
|
||||
function DropDownClass:SelByValue(val)
|
||||
for index, listVal in ipairs(self.list) do
|
||||
if listVal == val then
|
||||
self.sel = index
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function DropDownClass:IsMouseOver()
|
||||
if self.hidden then
|
||||
return false
|
||||
end
|
||||
local x = type(self.x) == "function" and self:x() or self.x
|
||||
local y = type(self.y) == "function" and self:y() or self.y
|
||||
local cx, cy = GetCursorPos()
|
||||
local dropExtra = self.dropped and (self.height - 4) * #self.list + 2 or 0
|
||||
return cx >= x and cy >= y and cx < x + self.width and cy < y + self.height + dropExtra, cy < y + self.height
|
||||
end
|
||||
|
||||
function DropDownClass:Draw()
|
||||
if self.hidden then
|
||||
return false
|
||||
end
|
||||
local x = type(self.x) == "function" and self:x() or self.x
|
||||
local y = type(self.y) == "function" and self:y() or self.y
|
||||
local enabled = not self.enableFunc or self.enableFunc()
|
||||
local mOver, mOverBody = self:IsMouseOver()
|
||||
local dropExtra = (self.height - 4) * #self.list + 4
|
||||
if not enabled then
|
||||
SetDrawColor(0.33, 0.33, 0.33)
|
||||
elseif mOver or self.dropped then
|
||||
SetDrawColor(1, 1, 1)
|
||||
else
|
||||
SetDrawColor(0.5, 0.5, 0.5)
|
||||
end
|
||||
DrawImage(nil, x, y, self.width, self.height)
|
||||
if self.dropped then
|
||||
DrawImage(nil, x, y + self.height, self.width, dropExtra)
|
||||
end
|
||||
if not enabled then
|
||||
SetDrawColor(0, 0, 0)
|
||||
elseif self.dropped then
|
||||
SetDrawColor(0.5, 0.5, 0.5)
|
||||
elseif mOver then
|
||||
SetDrawColor(0.33, 0.33, 0.33)
|
||||
else
|
||||
SetDrawColor(0, 0, 0)
|
||||
end
|
||||
DrawImage(nil, x + 1, y + 1, self.width - 2, self.height - 2)
|
||||
if not enabled then
|
||||
SetDrawColor(0.33, 0.33, 0.33)
|
||||
elseif mOver or self.dropped then
|
||||
SetDrawColor(1, 1, 1)
|
||||
else
|
||||
SetDrawColor(0.5, 0.5, 0.5)
|
||||
end
|
||||
local x1 = x + self.width - self.height / 2 - self.height / 4
|
||||
local x2 = x1 + self.height / 2
|
||||
local y1 = y + self.height / 4
|
||||
local y2 = y1 + self.height / 2
|
||||
DrawImageQuad(nil, x1, y1, x2, y1, (x1+x2)/2, y2, (x1+x2)/2, y2)
|
||||
if self.dropped then
|
||||
SetDrawColor(0, 0, 0)
|
||||
DrawImage(nil, x + 1, y + self.height + 1, self.width - 2, dropExtra - 2)
|
||||
end
|
||||
if enabled then
|
||||
SetDrawColor(1, 1, 1)
|
||||
else
|
||||
SetDrawColor(0.66, 0.66, 0.66)
|
||||
end
|
||||
DrawString(x + 2, y + 2, "LEFT", self.height - 4, "VAR", self.list[self.sel] or "")
|
||||
if self.dropped then
|
||||
self.hoverSel = mOver and math.floor((select(2,GetCursorPos()) - y - self.height) / (self.height - 4)) + 1
|
||||
if self.hoverSel and self.hoverSel < 1 then
|
||||
self.hoverSel = nil
|
||||
end
|
||||
for index, val in ipairs(self.list) do
|
||||
local y = y + self.height + 2 + (index - 1) * (self.height - 4)
|
||||
if index == self.hoverSel then
|
||||
SetDrawColor(0.5, 0.4, 0.3)
|
||||
DrawImage(nil, x + 2, y, self.width - 4, self.height - 4)
|
||||
end
|
||||
if index == self.hoverSel or index == self.sel then
|
||||
SetDrawColor(1, 1, 1)
|
||||
else
|
||||
SetDrawColor(0.66, 0.66, 0.66)
|
||||
end
|
||||
DrawString(x + 2, y, "LEFT", self.height - 4, "VAR", StripEscapes(val))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function DropDownClass:OnKeyDown(key)
|
||||
if self.enableFunc and not self.enableFunc() then
|
||||
return true
|
||||
end
|
||||
if key == "LEFTBUTTON" then
|
||||
local all, body = self:IsMouseOver()
|
||||
if not all or (self.dropped and body) then
|
||||
self.dropped = false
|
||||
return true
|
||||
end
|
||||
self.dropped = true
|
||||
return false
|
||||
elseif key == "ESCAPE" then
|
||||
self.dropped = false
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function DropDownClass:OnKeyUp(key)
|
||||
if self.enableFunc and not self.enableFunc() then
|
||||
return true
|
||||
end
|
||||
if key == "LEFTBUTTON" then
|
||||
local all, body = self:IsMouseOver()
|
||||
if not all then
|
||||
self.dropped = false
|
||||
return true
|
||||
elseif not body then
|
||||
local y = type(self.y) == "function" and self:y() or self.y
|
||||
local sel = math.max(1, math.floor((select(2,GetCursorPos()) - y - self.height) / (self.height - 4)) + 1)
|
||||
self.sel = sel
|
||||
if self.selFunc then
|
||||
self.selFunc(sel, self.list[sel])
|
||||
end
|
||||
self.dropped = false
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
80
Classes/EditControl.lua
Normal file
80
Classes/EditControl.lua
Normal file
@@ -0,0 +1,80 @@
|
||||
-- Path of Building
|
||||
--
|
||||
-- Class: Edit Control
|
||||
-- Wrapper for the basic edit field.
|
||||
--
|
||||
|
||||
local EditClass = common.NewClass("EditControl", function(self, x, y, width, height, ...)
|
||||
self.x = x
|
||||
self.y = y
|
||||
self.width = width
|
||||
self.height = height
|
||||
self.edit = common.newEditField(...)
|
||||
self.edit.width = width - 4
|
||||
self.edit.height = height - 4
|
||||
self.edit.leader = ""
|
||||
end)
|
||||
|
||||
function EditClass:SetText(text)
|
||||
self.edit:SetText(text)
|
||||
end
|
||||
|
||||
function EditClass:UpdatePosition()
|
||||
local x = type(self.x) == "function" and self:x() or self.x
|
||||
local y = type(self.y) == "function" and self:y() or self.y
|
||||
self.edit.x = x + 2
|
||||
self.edit.y = y + 2
|
||||
return x, y
|
||||
end
|
||||
|
||||
function EditClass:IsMouseOver()
|
||||
if self.hidden then
|
||||
return false
|
||||
end
|
||||
self:UpdatePosition()
|
||||
return self.edit:IsMouseOver()
|
||||
end
|
||||
|
||||
function EditClass:Draw()
|
||||
if self.hidden then
|
||||
return
|
||||
end
|
||||
local x, y = self:UpdatePosition()
|
||||
local enabled = not self.edit.enableFunc or self.edit.enableFunc()
|
||||
local mOver = self:IsMouseOver()
|
||||
if not enabled then
|
||||
SetDrawColor(0.33, 0.33, 0.33)
|
||||
elseif mOver then
|
||||
SetDrawColor(1, 1, 1)
|
||||
else
|
||||
SetDrawColor(0.5, 0.5, 0.5)
|
||||
end
|
||||
DrawImage(nil, x, y, self.width, self.height)
|
||||
if not enabled then
|
||||
SetDrawColor(0, 0, 0)
|
||||
elseif self.active or mOver then
|
||||
SetDrawColor(0.15, 0.15, 0.15)
|
||||
else
|
||||
SetDrawColor(0, 0, 0)
|
||||
end
|
||||
DrawImage(nil, x + 1, y + 1, self.width - 2, self.height - 2)
|
||||
self.edit:Draw(nil, nil, nil, not self.active)
|
||||
end
|
||||
|
||||
function EditClass:OnKeyDown(key, doubleClick)
|
||||
self:UpdatePosition()
|
||||
self.active = not self.edit:OnKeyDown(key, doubleClick)
|
||||
return not self.active
|
||||
end
|
||||
|
||||
function EditClass:OnKeyUp(key)
|
||||
self:UpdatePosition()
|
||||
self.active = not self.edit:OnKeyUp(key)
|
||||
return not self.active
|
||||
end
|
||||
|
||||
function EditClass:OnChar(key)
|
||||
self:UpdatePosition()
|
||||
self.active = not self.edit:OnChar(key)
|
||||
return not self.active
|
||||
end
|
||||
@@ -1,9 +1,9 @@
|
||||
-- Path of Building
|
||||
--
|
||||
-- Module: Spec
|
||||
-- Class: Passive Spec
|
||||
-- Passive tree spec class.
|
||||
--
|
||||
local launch, cfg, main = ...
|
||||
local launch = ...
|
||||
|
||||
local pairs = pairs
|
||||
local ipairs = ipairs
|
||||
@@ -12,87 +12,144 @@ local m_max = math.max
|
||||
local m_floor = math.floor
|
||||
local t_insert = table.insert
|
||||
|
||||
local SpecClass = { }
|
||||
SpecClass.__index = SpecClass
|
||||
local SpecClass = common.NewClass("PassiveSpec", function(self, tree)
|
||||
self.tree = tree
|
||||
|
||||
local function buildPathFromNode(root)
|
||||
root.pathDist = 0
|
||||
root.path = { }
|
||||
local queue = { root }
|
||||
local o, i = 1, 2
|
||||
while o < i do
|
||||
local node = queue[o]
|
||||
o = o + 1
|
||||
local curDist = node.pathDist + 1
|
||||
for _, other in ipairs(node.linked) do
|
||||
if other.type ~= "class" and other.type ~= "ascendClass" and other.pathDist > curDist and (node.ascendancyName == other.ascendancyName or (curDist == 1 and not other.ascendancyName)) then
|
||||
other.pathDist = curDist
|
||||
other.path = wipeTable(other.path)
|
||||
other.path[1] = other
|
||||
for i, n in ipairs(node.path) do
|
||||
other.path[i+1] = n
|
||||
-- Make a local copy of the passive tree that we can modify
|
||||
self.nodes = { }
|
||||
for _, treeNode in ipairs(tree.nodes) do
|
||||
self.nodes[treeNode.id] = setmetatable({
|
||||
rsq = treeNode.overlay and treeNode.overlay.rsq,
|
||||
size = treeNode.overlay and treeNode.overlay.size,
|
||||
linked = { }
|
||||
}, { __index = treeNode })
|
||||
end
|
||||
for id, node in pairs(self.nodes) do
|
||||
for _, otherId in ipairs(node.linkedId) do
|
||||
t_insert(node.linked, self.nodes[otherId])
|
||||
end
|
||||
end
|
||||
|
||||
self.allocNodes = { }
|
||||
|
||||
self.undo = { }
|
||||
self.redo = { }
|
||||
|
||||
self:SelectClass(0)
|
||||
end)
|
||||
|
||||
function SpecClass:Load(xml, dbFileName)
|
||||
for _, node in pairs(xml) do
|
||||
if type(node) == "table" then
|
||||
if node.elem == "URL" then
|
||||
if type(node[1]) ~= "string" then
|
||||
launch:ShowErrMsg("^1Error parsing '%s': 'URL' element missing content", fileName)
|
||||
return true
|
||||
end
|
||||
queue[i] = other
|
||||
i = i + 1
|
||||
self:DecodeURL(node[1])
|
||||
self.undo = { node[1] }
|
||||
self.redo = { }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
function SpecClass:BuildAllPaths()
|
||||
for id, node in pairs(self.nodes) do
|
||||
node.pathDist = node.alloc and 0 or 1000
|
||||
node.path = nil
|
||||
end
|
||||
for id, node in pairs(self.allocNodes) do
|
||||
buildPathFromNode(node)
|
||||
end
|
||||
self.modFlag = false
|
||||
end
|
||||
|
||||
local function findStart(node, visited, noAscend)
|
||||
node.visited = true
|
||||
t_insert(visited, node)
|
||||
for _, other in ipairs(node.linked) do
|
||||
if other.alloc and (other.type == "class" or other.type == "ascendClass" or (not other.visited and findStart(other, visited, noAscend))) then
|
||||
if not noAscend or other.type ~= "ascendClass" then
|
||||
function SpecClass:Save(xml)
|
||||
t_insert(xml, {
|
||||
elem = "URL",
|
||||
[1] = self:EncodeURL("https://www.pathofexile.com/passive-skill-tree/")
|
||||
})
|
||||
self.modFlag = false
|
||||
end
|
||||
|
||||
function SpecClass:DecodeURL(url)
|
||||
self:ResetNodes()
|
||||
local b = common.base64.decode(url:gsub("^.+/",""):gsub("-","+"):gsub("_","/"))
|
||||
local ver = b:byte(4)
|
||||
local classId = b:byte(5)
|
||||
local ascendClassId = (ver >= 4) and b:byte(6) or 0
|
||||
self:SelectClass(classId)
|
||||
for i = (ver >= 4) and 8 or 7, #b-1, 2 do
|
||||
local id = b:byte(i) * 256 + b:byte(i + 1)
|
||||
local node = self.nodes[id]
|
||||
if node then
|
||||
node.alloc = true
|
||||
self.allocNodes[id] = node
|
||||
if ascendClassId == 0 and node.ascendancyName then
|
||||
ascendClassId = self.tree.ascendNameMap[node.ascendancyName].ascendClassId
|
||||
end
|
||||
end
|
||||
end
|
||||
self:SelectAscendClass(ascendClassId)
|
||||
end
|
||||
|
||||
function SpecClass:EncodeURL(prefix)
|
||||
local a = { 0, 0, 0, 4, self.curClassId, self.curAscendClassId, 0 }
|
||||
for id, node in pairs(self.allocNodes) do
|
||||
if node.type ~= "class" and node.type ~= "ascendClass" then
|
||||
t_insert(a, m_floor(id / 256))
|
||||
t_insert(a, id % 256)
|
||||
end
|
||||
end
|
||||
return (prefix or "")..common.base64.encode(string.char(unpack(a))):gsub("+","-"):gsub("/","_")
|
||||
end
|
||||
|
||||
function SpecClass:SelectClass(classId)
|
||||
if self.curClassId then
|
||||
local oldStartNodeId = self.tree.classes[self.curClassId].startNodeId
|
||||
self.nodes[oldStartNodeId].alloc = false
|
||||
self.allocNodes[oldStartNodeId] = nil
|
||||
end
|
||||
self.curClassId = classId
|
||||
local class = self.tree.classes[classId]
|
||||
local startNode = self.nodes[class.startNodeId]
|
||||
startNode.alloc = true
|
||||
self.allocNodes[startNode.id] = startNode
|
||||
self:SelectAscendClass(0)
|
||||
end
|
||||
|
||||
function SpecClass:SelectAscendClass(ascendClassId)
|
||||
self.curAscendClassId = ascendClassId
|
||||
local ascendClass = self.tree.classes[self.curClassId].classes[tostring(ascendClassId)] or { name = "" }
|
||||
for id, node in pairs(self.allocNodes) do
|
||||
if node.ascendancyName and node.ascendancyName ~= ascendClass.name then
|
||||
node.alloc = false
|
||||
self.allocNodes[id] = nil
|
||||
end
|
||||
end
|
||||
if ascendClass.startNodeId then
|
||||
local startNode = self.nodes[ascendClass.startNodeId]
|
||||
startNode.alloc = true
|
||||
self.allocNodes[startNode.id] = startNode
|
||||
end
|
||||
self:BuildAllDepends()
|
||||
self:BuildAllPaths()
|
||||
end
|
||||
|
||||
function SpecClass:IsClassConnected(classId)
|
||||
for _, other in ipairs(self.nodes[self.tree.classes[classId].startNodeId].linked) do
|
||||
if other.alloc then
|
||||
other.visited = true
|
||||
local visited = { }
|
||||
local found = findStart(other, visited, true)
|
||||
for i, n in ipairs(visited) do
|
||||
n.visited = false
|
||||
end
|
||||
other.visited = false
|
||||
if found then
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
function SpecClass:BuildAllDepends()
|
||||
local visited = { }
|
||||
|
||||
function SpecClass:ResetNodes()
|
||||
for id, node in pairs(self.nodes) do
|
||||
node.depends = wipeTable(node.depends)
|
||||
if node.alloc then
|
||||
node.depends[1] = node
|
||||
node.visited = true
|
||||
local anyStartFound = (node.type == "class" or node.type == "ascendClass")
|
||||
for _, other in ipairs(node.linked) do
|
||||
if other.alloc then
|
||||
if other.type == "class" or other.type == "ascendClass" then
|
||||
anyStartFound = true
|
||||
elseif findStart(other, visited) then
|
||||
anyStartFound = true
|
||||
for i, n in ipairs(visited) do
|
||||
n.visited = false
|
||||
visited[i] = nil
|
||||
end
|
||||
else
|
||||
for i, n in ipairs(visited) do
|
||||
t_insert(node.depends, n)
|
||||
n.visited = false
|
||||
visited[i] = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
node.visited = false
|
||||
if not anyStartFound then
|
||||
for _, depNode in ipairs(node.depends) do
|
||||
depNode.alloc = false
|
||||
self.allocNodes[depNode.id] = nil
|
||||
end
|
||||
end
|
||||
if node.type ~= "class" and node.type ~= "ascendClass" then
|
||||
node.alloc = false
|
||||
self.allocNodes[id] = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -104,7 +161,7 @@ function SpecClass:AllocNode(node, altPath)
|
||||
for _, pathNode in ipairs(altPath or node.path) do
|
||||
pathNode.alloc = true
|
||||
self.allocNodes[pathNode.id] = pathNode
|
||||
buildPathFromNode(pathNode)
|
||||
self:BuildPathFromNode(pathNode)
|
||||
end
|
||||
if node.isMultipleChoiceOption then
|
||||
local parent = node.linked[1]
|
||||
@@ -118,6 +175,7 @@ function SpecClass:AllocNode(node, altPath)
|
||||
end
|
||||
self:BuildAllDepends()
|
||||
end
|
||||
|
||||
function SpecClass:DeallocNode(node)
|
||||
for _, depNode in ipairs(node.depends) do
|
||||
depNode.alloc = false
|
||||
@@ -143,93 +201,88 @@ function SpecClass:CountAllocNodes()
|
||||
return used, ascUsed
|
||||
end
|
||||
|
||||
function SpecClass:ResetNodes()
|
||||
for id, node in pairs(self.nodes) do
|
||||
if node.type ~= "class" and node.type ~= "ascendClass" then
|
||||
node.alloc = false
|
||||
self.allocNodes[id] = nil
|
||||
function SpecClass:BuildPathFromNode(root)
|
||||
root.pathDist = 0
|
||||
root.path = { }
|
||||
local queue = { root }
|
||||
local o, i = 1, 2
|
||||
while o < i do
|
||||
local node = queue[o]
|
||||
o = o + 1
|
||||
local curDist = node.pathDist + 1
|
||||
for _, other in ipairs(node.linked) do
|
||||
if other.type ~= "class" and other.type ~= "ascendClass" and other.pathDist > curDist and (node.ascendancyName == other.ascendancyName or (curDist == 1 and not other.ascendancyName)) then
|
||||
other.pathDist = curDist
|
||||
other.path = wipeTable(other.path)
|
||||
other.path[1] = other
|
||||
for i, n in ipairs(node.path) do
|
||||
other.path[i+1] = n
|
||||
end
|
||||
queue[i] = other
|
||||
i = i + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function SpecClass:IsClassConnected(classId)
|
||||
for _, other in ipairs(self.nodes[self.tree.classes[classId].startNodeId].linked) do
|
||||
if other.alloc then
|
||||
other.visited = true
|
||||
local visited = { }
|
||||
local found = findStart(other, visited, true)
|
||||
for i, n in ipairs(visited) do
|
||||
n.visited = false
|
||||
end
|
||||
other.visited = false
|
||||
if found then
|
||||
function SpecClass:BuildAllPaths()
|
||||
for id, node in pairs(self.nodes) do
|
||||
node.pathDist = node.alloc and 0 or 1000
|
||||
node.path = nil
|
||||
end
|
||||
for id, node in pairs(self.allocNodes) do
|
||||
self:BuildPathFromNode(node)
|
||||
end
|
||||
end
|
||||
|
||||
function SpecClass:FindStartFromNode(node, visited, noAscend)
|
||||
node.visited = true
|
||||
t_insert(visited, node)
|
||||
for _, other in ipairs(node.linked) do
|
||||
if other.alloc and (other.type == "class" or other.type == "ascendClass" or (not other.visited and self:FindStartFromNode(other, visited, noAscend))) then
|
||||
if not noAscend or other.type ~= "ascendClass" then
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function SpecClass:SelectClass(classId)
|
||||
if self.curClassId then
|
||||
local oldStartNodeId = self.tree.classes[self.curClassId].startNodeId
|
||||
self.nodes[oldStartNodeId].alloc = false
|
||||
self.allocNodes[oldStartNodeId] = nil
|
||||
end
|
||||
self.curClassId = classId
|
||||
local class = self.tree.classes[classId]
|
||||
local startNode = self.nodes[class.startNodeId]
|
||||
startNode.alloc = true
|
||||
self.allocNodes[startNode.id] = startNode
|
||||
self:SelectAscendClass(0)
|
||||
end
|
||||
function SpecClass:SelectAscendClass(ascendClassId)
|
||||
self.curAscendClassId = ascendClassId
|
||||
local ascendClass = self.tree.classes[self.curClassId].classes[tostring(ascendClassId)] or { name = "" }
|
||||
for id, node in pairs(self.allocNodes) do
|
||||
if node.ascendancyName and node.ascendancyName ~= ascendClass.name then
|
||||
node.alloc = false
|
||||
self.allocNodes[id] = nil
|
||||
end
|
||||
end
|
||||
if ascendClass.startNodeId then
|
||||
local startNode = self.nodes[ascendClass.startNodeId]
|
||||
startNode.alloc = true
|
||||
self.allocNodes[startNode.id] = startNode
|
||||
end
|
||||
self:BuildAllDepends()
|
||||
self:BuildAllPaths()
|
||||
end
|
||||
|
||||
function SpecClass:DecodeURL(url)
|
||||
self:ResetNodes()
|
||||
local b = common.base64.decode(url:gsub("^.+/",""):gsub("-","+"):gsub("_","/"))
|
||||
local ver = b:byte(4)
|
||||
local classId = b:byte(5)
|
||||
local ascendClassId = (ver >= 4) and b:byte(6) or 0
|
||||
self:SelectClass(classId)
|
||||
for i = (ver >= 4) and 8 or 7, #b-1, 2 do
|
||||
local id = b:byte(i) * 256 + b:byte(i + 1)
|
||||
local node = self.nodes[id]
|
||||
if node then
|
||||
node.alloc = true
|
||||
self.allocNodes[id] = node
|
||||
if ascendClassId == 0 and node.ascendancyName then
|
||||
ascendClassId = self.tree.ascendNameMap[node.ascendancyName].ascendClassId
|
||||
function SpecClass:BuildAllDepends()
|
||||
local visited = { }
|
||||
for id, node in pairs(self.nodes) do
|
||||
node.depends = wipeTable(node.depends)
|
||||
if node.alloc then
|
||||
node.depends[1] = node
|
||||
node.visited = true
|
||||
local anyStartFound = (node.type == "class" or node.type == "ascendClass")
|
||||
for _, other in ipairs(node.linked) do
|
||||
if other.alloc then
|
||||
if other.type == "class" or other.type == "ascendClass" then
|
||||
anyStartFound = true
|
||||
elseif self:FindStartFromNode(other, visited) then
|
||||
anyStartFound = true
|
||||
for i, n in ipairs(visited) do
|
||||
n.visited = false
|
||||
visited[i] = nil
|
||||
end
|
||||
else
|
||||
for i, n in ipairs(visited) do
|
||||
t_insert(node.depends, n)
|
||||
n.visited = false
|
||||
visited[i] = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
node.visited = false
|
||||
if not anyStartFound then
|
||||
for _, depNode in ipairs(node.depends) do
|
||||
depNode.alloc = false
|
||||
self.allocNodes[depNode.id] = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
self:SelectAscendClass(ascendClassId)
|
||||
end
|
||||
function SpecClass:EncodeURL(prefix)
|
||||
local a = { 0, 0, 0, 4, self.curClassId, self.curAscendClassId, 0 }
|
||||
for id, node in pairs(self.allocNodes) do
|
||||
if node.type ~= "class" and node.type ~= "ascendClass" then
|
||||
t_insert(a, m_floor(id / 256))
|
||||
t_insert(a, id % 256)
|
||||
end
|
||||
end
|
||||
return (prefix or "")..common.base64.encode(string.char(unpack(a))):gsub("+","-"):gsub("/","_")
|
||||
end
|
||||
|
||||
function SpecClass:AddUndoState(noClearRedo)
|
||||
@@ -241,6 +294,7 @@ function SpecClass:AddUndoState(noClearRedo)
|
||||
self.redo = { }
|
||||
end
|
||||
end
|
||||
|
||||
function SpecClass:Undo()
|
||||
if self.undo[2] then
|
||||
t_insert(self.redo, 1, table.remove(self.undo, 1))
|
||||
@@ -250,6 +304,7 @@ function SpecClass:Undo()
|
||||
self.buildFlag = true
|
||||
end
|
||||
end
|
||||
|
||||
function SpecClass:Redo()
|
||||
if self.redo[1] then
|
||||
self:DecodeURL(table.remove(self. redo, 1))
|
||||
@@ -258,58 +313,3 @@ function SpecClass:Redo()
|
||||
self.buildFlag = true
|
||||
end
|
||||
end
|
||||
|
||||
function SpecClass:Load(xml, dbFileName)
|
||||
for _, node in pairs(xml) do
|
||||
if type(node) == "table" then
|
||||
if node.elem == "URL" then
|
||||
if type(node[1]) ~= "string" then
|
||||
launch:ShowErrMsg("^1Error parsing '%s': 'URL' element missing content", fileName)
|
||||
return true
|
||||
end
|
||||
self:DecodeURL(node[1])
|
||||
self.undo = { node[1] }
|
||||
self.redo = { }
|
||||
end
|
||||
end
|
||||
end
|
||||
self.modFlag = false
|
||||
end
|
||||
function SpecClass:Save(xml)
|
||||
t_insert(xml, {
|
||||
elem = "URL",
|
||||
[1] = self:EncodeURL("https://www.pathofexile.com/passive-skill-tree/")
|
||||
})
|
||||
self.modFlag = false
|
||||
end
|
||||
|
||||
function SpecClass.NewSpec(tree)
|
||||
local self = setmetatable({}, SpecClass)
|
||||
|
||||
self.tree = tree
|
||||
|
||||
self.nodes = { }
|
||||
for _, treeNode in ipairs(tree.nodes) do
|
||||
self.nodes[treeNode.id] = setmetatable({
|
||||
rsq = treeNode.overlay and treeNode.overlay.rsq,
|
||||
size = treeNode.overlay and treeNode.overlay.size,
|
||||
linked = { }
|
||||
}, { __index = treeNode })
|
||||
end
|
||||
for id, node in pairs(self.nodes) do
|
||||
for _, otherId in ipairs(node.linkedId) do
|
||||
t_insert(node.linked, self.nodes[otherId])
|
||||
end
|
||||
end
|
||||
|
||||
self.allocNodes = { }
|
||||
|
||||
self.undo = { }
|
||||
self.redo = { }
|
||||
|
||||
self:SelectClass(0)
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
return SpecClass
|
||||
@@ -1,9 +1,9 @@
|
||||
-- Path of Building
|
||||
--
|
||||
-- Module: Tree
|
||||
-- Class: Passive Tree
|
||||
-- Passive skill tree class.
|
||||
--
|
||||
local launch, cfg, main = ...
|
||||
local launch = ...
|
||||
|
||||
local pairs = pairs
|
||||
local ipairs = ipairs
|
||||
@@ -16,83 +16,7 @@ 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()
|
||||
local TreeClass = common.NewClass("PassiveTree", function(self)
|
||||
os.execute("mkdir Data")
|
||||
|
||||
ConPrintf("Loading JSON...")
|
||||
@@ -129,20 +53,21 @@ function TreeClass.NewTree()
|
||||
classFile:write(classText)
|
||||
classFile:close()
|
||||
end
|
||||
local self = common.json.decode(treeText)
|
||||
for k, v in pairs(common.json.decode(treeText)) do
|
||||
self[k] = v
|
||||
end
|
||||
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"])
|
||||
self:CacheImage(imgName, data["0.3835"] or data["1"])
|
||||
data.handle = NewImageHandle()
|
||||
data.handle:Load(imgName)
|
||||
data.width, data.height = data.handle:ImageSize()
|
||||
@@ -156,7 +81,7 @@ function TreeClass.NewTree()
|
||||
if not sheet then
|
||||
sheet = { }
|
||||
local imgName = "Data/"..maxZoom.filename
|
||||
cacheImage(imgName, self.imageRoot.."build-gen/passive-skill-sprite/"..maxZoom.filename)
|
||||
self: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()
|
||||
@@ -336,8 +261,77 @@ function TreeClass.NewTree()
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
return self
|
||||
function TreeClass: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
|
||||
|
||||
return 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
|
||||
@@ -1,9 +1,9 @@
|
||||
-- Path of Building
|
||||
--
|
||||
-- Module: TreeView
|
||||
-- Passive skill tree view for the active build
|
||||
-- Class: Passive Tree View
|
||||
-- Passive skill tree viewer.
|
||||
--
|
||||
local launch, cfg, main = ...
|
||||
local launch = ...
|
||||
|
||||
local pairs = pairs
|
||||
local ipairs = ipairs
|
||||
@@ -12,143 +12,84 @@ local m_min = math.min
|
||||
local m_max = math.max
|
||||
local m_floor = math.floor
|
||||
|
||||
local function drawAsset(data, x, y, scale, isHalf)
|
||||
local width = data.width * scale * 1.33
|
||||
local height = data.height * scale * 1.33
|
||||
if isHalf then
|
||||
DrawImage(data.handle, x - width, y - height * 2, width * 2, height * 2)
|
||||
DrawImage(data.handle, x - width, y, width * 2, height * 2, 0, 1, 1, 0)
|
||||
else
|
||||
DrawImage(data.handle, x - width, y - height, width * 2, height * 2, unpack(data))
|
||||
end
|
||||
end
|
||||
local TreeViewClass = common.NewClass("PassiveTreeView", function(self, main)
|
||||
self.main = main
|
||||
|
||||
local TreeViewClass = { }
|
||||
TreeViewClass.__index = TreeViewClass
|
||||
self.ring = NewImageHandle()
|
||||
self.ring:Load("Art/ring.png")
|
||||
|
||||
function TreeViewClass:Zoom(level, viewPort)
|
||||
self.zoomLevel = m_max(0, m_min(12, self.zoomLevel + level))
|
||||
local oldZoom = self.zoom
|
||||
self.zoomLevel = 3
|
||||
self.zoom = 1.2 ^ self.zoomLevel
|
||||
local factor = self.zoom / oldZoom
|
||||
local cursorX, cursorY = GetCursorPos()
|
||||
local relX = cursorX - viewPort.x - viewPort.width/2
|
||||
local relY = cursorY - viewPort.y - viewPort.height/2
|
||||
self.zoomX = relX + (self.zoomX - relX) * factor
|
||||
self.zoomY = relY + (self.zoomY - relY) * factor
|
||||
self.zoomX = 0
|
||||
self.zoomY = 0
|
||||
|
||||
self.searchStr = ""
|
||||
|
||||
self.controls = { }
|
||||
t_insert(self.controls, common.New("ButtonControl", function() return self.viewPort.x + 4 end, function() return self.viewPort.y + self.viewPort.height + 8 end, 60, 20, "Reset", function()
|
||||
launch:ShowPrompt(1, 0, 0, "Are you sure to want to reset your tree?\nPress Y to continue.", function(key)
|
||||
if key == "y" then
|
||||
self.build.spec:ResetNodes()
|
||||
self.build.spec:AddUndoState()
|
||||
end
|
||||
return true
|
||||
end)
|
||||
end))
|
||||
t_insert(self.controls, common.New("ButtonControl", function() return self.viewPort.x + 4 + 68*1 end, function() return self.viewPort.y + self.viewPort.height + 8 end, 60, 20, "Import", function()
|
||||
launch:ShowPrompt(0, 0, 0, "Press Ctrl+V to import passive tree link.", function(key)
|
||||
if key == "v" and IsKeyDown("CTRL") then
|
||||
local url = Paste()
|
||||
if url and #url > 0 then
|
||||
self.build.spec:DecodeURL(url)
|
||||
self.build.spec:AddUndoState()
|
||||
end
|
||||
return true
|
||||
elseif key == "ESCAPE" then
|
||||
return true
|
||||
end
|
||||
end)
|
||||
end))
|
||||
t_insert(self.controls, common.New("ButtonControl", function() return self.viewPort.x + 4 + 68*2 end, function() return self.viewPort.y + self.viewPort.height + 8 end, 60, 20, "Export", function()
|
||||
launch:ShowPrompt(0, 0, 0, "Press Ctrl+C to copy passive tree link.", function(key)
|
||||
if key == "c" and IsKeyDown("CTRL") then
|
||||
Copy(self.build.spec:EncodeURL("https://www.pathofexile.com/passive-skill-tree/"))
|
||||
return true
|
||||
elseif key == "ESCAPE" then
|
||||
return true
|
||||
end
|
||||
end)
|
||||
end))
|
||||
self.controls.treeSearch = common.New("EditControl", function() return self.viewPort.x + 4 + 68*3 end, function() return self.viewPort.y + self.viewPort.height + 8 end, 400, 20, "", "Search", "[^%c%(%)]", 100, nil, function(buf)
|
||||
self.searchStr = buf
|
||||
end)
|
||||
end)
|
||||
|
||||
function TreeViewClass:Load(xml, fileName)
|
||||
if xml.attrib.zoomLevel then
|
||||
self.zoomLevel = tonumber(xml.attrib.zoomLevel)
|
||||
self.zoom = 1.2 ^ self.zoomLevel
|
||||
end
|
||||
if xml.attrib.zoomX and xml.attrib.zoomY then
|
||||
self.zoomX = tonumber(xml.attrib.zoomX)
|
||||
self.zoomY = tonumber(xml.attrib.zoomY)
|
||||
end
|
||||
if xml.attrib.searchStr then
|
||||
self.searchStr = xml.attrib.searchStr
|
||||
self.controls.treeSearch:SetText(self.searchStr)
|
||||
end
|
||||
if xml.attrib.showHeatMap then
|
||||
self.showHeatMap = xml.attrib.showHeatMap == "true"
|
||||
end
|
||||
end
|
||||
|
||||
function TreeViewClass:AddNodeTooltip(node, build)
|
||||
-- Special case for sockets
|
||||
if node.type == "socket" and node.alloc then
|
||||
local socket, jewel = build.items:GetSocketJewel(node.id)
|
||||
if jewel then
|
||||
build.items:AddItemTooltip(jewel, build)
|
||||
else
|
||||
main:AddTooltipLine(24, "^7"..node.dn..(IsKeyDown("ALT") and " ["..node.id.."]" or ""))
|
||||
end
|
||||
main:AddTooltipSeperator(14)
|
||||
main:AddTooltipLine(14, "^x80A080Tip: Right click this socket to go to the items page and choose the jewel for this socket.")
|
||||
return
|
||||
end
|
||||
|
||||
-- Node name
|
||||
main:AddTooltipLine(24, "^7"..node.dn..(IsKeyDown("ALT") and " ["..node.id.."]" or ""))
|
||||
if IsKeyDown("ALT") and node.power and node.power.dps then
|
||||
main:AddTooltipLine(16, string.format("DPS power: %g Defence power: %g", node.power.dps, node.power.def))
|
||||
end
|
||||
|
||||
-- Node description
|
||||
if node.sd[1] then
|
||||
main:AddTooltipLine(16, "")
|
||||
for i, line in ipairs(node.sd) do
|
||||
if node.mods[i].list then
|
||||
if IsKeyDown("ALT") then
|
||||
local modStr
|
||||
for k, v in pairs(node.mods[i].list) do
|
||||
modStr = (modStr and modStr..", " or "^2") .. string.format("%s = %s", k, tostring(v))
|
||||
end
|
||||
if node.mods[i].extra then
|
||||
modStr = (modStr and modStr.." " or "") .. "^1" .. node.mods[i].extra
|
||||
end
|
||||
if modStr then
|
||||
line = line .. " " .. modStr
|
||||
end
|
||||
end
|
||||
end
|
||||
main:AddTooltipLine(16, "^7"..line)
|
||||
end
|
||||
end
|
||||
|
||||
-- Reminder text
|
||||
if node.reminderText then
|
||||
main:AddTooltipSeperator(14)
|
||||
for _, line in ipairs(node.reminderText) do
|
||||
main:AddTooltipLine(14, "^xA0A080"..line)
|
||||
end
|
||||
end
|
||||
|
||||
-- Mod differences
|
||||
local calcFunc, calcBase = build.calcs:GetNodeCalculator(build)
|
||||
if calcFunc then
|
||||
main:AddTooltipSeperator(14)
|
||||
local count
|
||||
local nodeOutput, pathOutput
|
||||
if node.alloc then
|
||||
count = #node.depends
|
||||
nodeOutput = calcFunc({node}, true)
|
||||
pathOutput = calcFunc(node.depends, true)
|
||||
else
|
||||
local path = self.tracePath or node.path or { }
|
||||
count = #path
|
||||
nodeOutput = calcFunc({node})
|
||||
pathOutput = calcFunc(path)
|
||||
end
|
||||
local none = true
|
||||
local header = false
|
||||
for _, data in ipairs(build.displayStats) do
|
||||
if data.mod then
|
||||
local diff = (nodeOutput[data.mod] or 0) - (calcBase[data.mod] or 0)
|
||||
if diff > 0.001 or diff < -0.001 then
|
||||
none = false
|
||||
if not header then
|
||||
main:AddTooltipLine(14, string.format("^7%s this node will give you:", node.alloc and "Unallocating" or "Allocating"))
|
||||
header = true
|
||||
end
|
||||
main:AddTooltipLine(14, string.format("%s%+"..data.fmt.." %s", diff > 0 and "^x00FF44" or "^xFF3300", diff * (data.pc and 100 or 1), data.label))
|
||||
end
|
||||
end
|
||||
end
|
||||
if count > 1 then
|
||||
header = false
|
||||
for _, data in ipairs(build.displayStats) do
|
||||
if data.mod then
|
||||
local diff = (pathOutput[data.mod] or 0) - (calcBase[data.mod] or 0)
|
||||
if diff > 0.001 or diff < -0.001 then
|
||||
none = false
|
||||
if not header then
|
||||
main:AddTooltipLine(14, string.format("^7%s this node and all nodes %s will give you:", node.alloc and "Unallocating" or "Allocating", node.alloc and "depending on it" or "leading to it"))
|
||||
header = true
|
||||
end
|
||||
main:AddTooltipLine(14, string.format("%s%+"..data.fmt.." %s", diff > 0 and "^x00FF44" or "^xFF3300", diff * (data.pc and 100 or 1), data.label))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
if none then
|
||||
main:AddTooltipLine(14, string.format("^7No changes from %s this node%s.", node.alloc and "unallocating" or "allocating", count > 1 and " or the nodes leading to it" or ""))
|
||||
end
|
||||
end
|
||||
|
||||
-- Pathing distance
|
||||
if node.path and #node.path > 0 then
|
||||
main:AddTooltipSeperator(14)
|
||||
main:AddTooltipLine(14, "^7"..#node.path .. " points to node")
|
||||
if #node.path > 1 then
|
||||
main:AddTooltipLine(14, "^x80A080")
|
||||
main:AddTooltipLine(14, "Tip: To reach this node by a different path, hold Shift, then trace the path and click this node")
|
||||
end
|
||||
end
|
||||
function TreeViewClass:Save(xml)
|
||||
xml.attrib = {
|
||||
zoomLevel = tostring(self.zoomLevel),
|
||||
zoomX = tostring(self.zoomX),
|
||||
zoomY = tostring(self.zoomY),
|
||||
searchStr = self.searchStr,
|
||||
showHeatMap = tostring(self.showHeatMap),
|
||||
}
|
||||
end
|
||||
|
||||
function TreeViewClass:DrawTree(build, viewPort, inputEvents)
|
||||
@@ -308,15 +249,15 @@ function TreeViewClass:DrawTree(build, viewPort, inputEvents)
|
||||
if group.ascendancyName ~= curAscendName then
|
||||
SetDrawColor(1, 1, 1, 0.25)
|
||||
end
|
||||
drawAsset(tree.assets["Classes"..group.ascendancyName], scrX, scrY, scale)
|
||||
self:DrawAsset(tree.assets["Classes"..group.ascendancyName], scrX, scrY, scale)
|
||||
SetDrawColor(1, 1, 1)
|
||||
end
|
||||
elseif group.oo["3"] then
|
||||
drawAsset(tree.assets.PSGroupBackground3, scrX, scrY, scale, true)
|
||||
self:DrawAsset(tree.assets.PSGroupBackground3, scrX, scrY, scale, true)
|
||||
elseif group.oo["2"] then
|
||||
drawAsset(tree.assets.PSGroupBackground2, scrX, scrY, scale)
|
||||
self:DrawAsset(tree.assets.PSGroupBackground2, scrX, scrY, scale)
|
||||
elseif group.oo["1"] then
|
||||
drawAsset(tree.assets.PSGroupBackground1, scrX, scrY, scale)
|
||||
self:DrawAsset(tree.assets.PSGroupBackground1, scrX, scrY, scale)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -406,7 +347,7 @@ function TreeViewClass:DrawTree(build, viewPort, inputEvents)
|
||||
end
|
||||
end
|
||||
if base then
|
||||
drawAsset(base, scrX, scrY, scale)
|
||||
self:DrawAsset(base, scrX, scrY, scale)
|
||||
end
|
||||
if overlay then
|
||||
if node.type ~= "class" and node.type ~= "ascendClass" then
|
||||
@@ -449,7 +390,7 @@ function TreeViewClass:DrawTree(build, viewPort, inputEvents)
|
||||
end
|
||||
end
|
||||
end
|
||||
drawAsset(tree.assets[overlay], scrX, scrY, scale)
|
||||
self:DrawAsset(tree.assets[overlay], scrX, scrY, scale)
|
||||
SetDrawColor(1, 1, 1)
|
||||
end
|
||||
end
|
||||
@@ -479,7 +420,7 @@ function TreeViewClass:DrawTree(build, viewPort, inputEvents)
|
||||
self:AddNodeTooltip(hoverNode, build)
|
||||
local scrX, scrY = treeToScreen(hoverNode.x, hoverNode.y)
|
||||
local size = m_floor(hoverNode.size * scale)
|
||||
main:DrawTooltip(m_floor(scrX - size), m_floor(scrY - size), size * 2, size * 2, viewPort)
|
||||
self.main:DrawTooltip(m_floor(scrX - size), m_floor(scrY - size), size * 2, size * 2, viewPort)
|
||||
end
|
||||
|
||||
SetDrawColor(0.05, 0.05, 0.05)
|
||||
@@ -489,85 +430,138 @@ function TreeViewClass:DrawTree(build, viewPort, inputEvents)
|
||||
common.controlsDraw(self, viewPort)
|
||||
end
|
||||
|
||||
function TreeViewClass:Load(xml, fileName)
|
||||
if xml.attrib.zoomLevel then
|
||||
self.zoomLevel = tonumber(xml.attrib.zoomLevel)
|
||||
self.zoom = 1.2 ^ self.zoomLevel
|
||||
end
|
||||
if xml.attrib.zoomX and xml.attrib.zoomY then
|
||||
self.zoomX = tonumber(xml.attrib.zoomX)
|
||||
self.zoomY = tonumber(xml.attrib.zoomY)
|
||||
end
|
||||
if xml.attrib.searchStr then
|
||||
self.searchStr = xml.attrib.searchStr
|
||||
self.controls.treeSearch:SetText(self.searchStr)
|
||||
end
|
||||
if xml.attrib.showHeatMap then
|
||||
self.showHeatMap = xml.attrib.showHeatMap == "true"
|
||||
function TreeViewClass:DrawAsset(data, x, y, scale, isHalf)
|
||||
local width = data.width * scale * 1.33
|
||||
local height = data.height * scale * 1.33
|
||||
if isHalf then
|
||||
DrawImage(data.handle, x - width, y - height * 2, width * 2, height * 2)
|
||||
DrawImage(data.handle, x - width, y, width * 2, height * 2, 0, 1, 1, 0)
|
||||
else
|
||||
DrawImage(data.handle, x - width, y - height, width * 2, height * 2, unpack(data))
|
||||
end
|
||||
end
|
||||
function TreeViewClass:Save(xml)
|
||||
xml.attrib = {
|
||||
zoomLevel = tostring(self.zoomLevel),
|
||||
zoomX = tostring(self.zoomX),
|
||||
zoomY = tostring(self.zoomY),
|
||||
searchStr = self.searchStr,
|
||||
showHeatMap = tostring(self.showHeatMap),
|
||||
}
|
||||
end
|
||||
|
||||
function TreeViewClass.NewTreeView()
|
||||
local self = setmetatable({}, TreeViewClass)
|
||||
|
||||
self.ring = NewImageHandle()
|
||||
self.ring:Load("ring.png")
|
||||
|
||||
self.zoomLevel = 3
|
||||
function TreeViewClass:Zoom(level, viewPort)
|
||||
self.zoomLevel = m_max(0, m_min(12, self.zoomLevel + level))
|
||||
local oldZoom = self.zoom
|
||||
self.zoom = 1.2 ^ self.zoomLevel
|
||||
self.zoomX = 0
|
||||
self.zoomY = 0
|
||||
|
||||
self.searchStr = ""
|
||||
|
||||
self.controls = { }
|
||||
t_insert(self.controls, common.newButton(function() return self.viewPort.x + 4 end, function() return self.viewPort.y + self.viewPort.height + 8 end, 60, 20, "Reset", function()
|
||||
launch:ShowPrompt(1, 0, 0, "Are you sure to want to reset your tree?\nPress Y to continue.", function(key)
|
||||
if key == "y" then
|
||||
self.build.spec:ResetNodes()
|
||||
self.build.spec:AddUndoState()
|
||||
end
|
||||
return true
|
||||
end)
|
||||
end))
|
||||
t_insert(self.controls, common.newButton(function() return self.viewPort.x + 4 + 68*1 end, function() return self.viewPort.y + self.viewPort.height + 8 end, 60, 20, "Import", function()
|
||||
launch:ShowPrompt(0, 0, 0, "Press Ctrl+V to import passive tree link.", function(key)
|
||||
if key == "v" and IsKeyDown("CTRL") then
|
||||
local url = Paste()
|
||||
if url and #url > 0 then
|
||||
self.build.spec:DecodeURL(url)
|
||||
self.build.spec:AddUndoState()
|
||||
end
|
||||
return true
|
||||
elseif key == "ESCAPE" then
|
||||
return true
|
||||
end
|
||||
end)
|
||||
end))
|
||||
t_insert(self.controls, common.newButton(function() return self.viewPort.x + 4 + 68*2 end, function() return self.viewPort.y + self.viewPort.height + 8 end, 60, 20, "Export", function()
|
||||
launch:ShowPrompt(0, 0, 0, "Press Ctrl+C to copy passive tree link.", function(key)
|
||||
if key == "c" and IsKeyDown("CTRL") then
|
||||
Copy(self.build.spec:EncodeURL("https://www.pathofexile.com/passive-skill-tree/"))
|
||||
return true
|
||||
elseif key == "ESCAPE" then
|
||||
return true
|
||||
end
|
||||
end)
|
||||
end))
|
||||
self.controls.treeSearch = common.newEditControl(function() return self.viewPort.x + 4 + 68*3 end, function() return self.viewPort.y + self.viewPort.height + 8 end, 400, 20, "", "Search", "[^%c%(%)]", 100, nil, function(buf)
|
||||
self.searchStr = buf
|
||||
end)
|
||||
|
||||
return self
|
||||
local factor = self.zoom / oldZoom
|
||||
local cursorX, cursorY = GetCursorPos()
|
||||
local relX = cursorX - viewPort.x - viewPort.width/2
|
||||
local relY = cursorY - viewPort.y - viewPort.height/2
|
||||
self.zoomX = relX + (self.zoomX - relX) * factor
|
||||
self.zoomY = relY + (self.zoomY - relY) * factor
|
||||
end
|
||||
|
||||
return TreeViewClass
|
||||
function TreeViewClass:AddNodeTooltip(node, build)
|
||||
-- Special case for sockets
|
||||
if node.type == "socket" and node.alloc then
|
||||
local socket, jewel = build.items:GetSocketJewel(node.id)
|
||||
if jewel then
|
||||
build.items:AddItemTooltip(jewel, build)
|
||||
else
|
||||
self.main:AddTooltipLine(24, "^7"..node.dn..(IsKeyDown("ALT") and " ["..node.id.."]" or ""))
|
||||
end
|
||||
self.main:AddTooltipSeperator(14)
|
||||
self.main:AddTooltipLine(14, "^x80A080Tip: Right click this socket to go to the items page and choose the jewel for this socket.")
|
||||
return
|
||||
end
|
||||
|
||||
-- Node name
|
||||
self.main:AddTooltipLine(24, "^7"..node.dn..(IsKeyDown("ALT") and " ["..node.id.."]" or ""))
|
||||
if IsKeyDown("ALT") and node.power and node.power.dps then
|
||||
self.main:AddTooltipLine(16, string.format("DPS power: %g Defence power: %g", node.power.dps, node.power.def))
|
||||
end
|
||||
|
||||
-- Node description
|
||||
if node.sd[1] then
|
||||
self.main:AddTooltipLine(16, "")
|
||||
for i, line in ipairs(node.sd) do
|
||||
if node.mods[i].list then
|
||||
if IsKeyDown("ALT") then
|
||||
local modStr
|
||||
for k, v in pairs(node.mods[i].list) do
|
||||
modStr = (modStr and modStr..", " or "^2") .. string.format("%s = %s", k, tostring(v))
|
||||
end
|
||||
if node.mods[i].extra then
|
||||
modStr = (modStr and modStr.." " or "") .. "^1" .. node.mods[i].extra
|
||||
end
|
||||
if modStr then
|
||||
line = line .. " " .. modStr
|
||||
end
|
||||
end
|
||||
end
|
||||
self.main:AddTooltipLine(16, "^7"..line)
|
||||
end
|
||||
end
|
||||
|
||||
-- Reminder text
|
||||
if node.reminderText then
|
||||
self.main:AddTooltipSeperator(14)
|
||||
for _, line in ipairs(node.reminderText) do
|
||||
self.main:AddTooltipLine(14, "^xA0A080"..line)
|
||||
end
|
||||
end
|
||||
|
||||
-- Mod differences
|
||||
local calcFunc, calcBase = build.calcs:GetNodeCalculator(build)
|
||||
if calcFunc then
|
||||
self.main:AddTooltipSeperator(14)
|
||||
local count
|
||||
local nodeOutput, pathOutput
|
||||
if node.alloc then
|
||||
count = #node.depends
|
||||
nodeOutput = calcFunc({node}, true)
|
||||
pathOutput = calcFunc(node.depends, true)
|
||||
else
|
||||
local path = self.tracePath or node.path or { }
|
||||
count = #path
|
||||
nodeOutput = calcFunc({node})
|
||||
pathOutput = calcFunc(path)
|
||||
end
|
||||
local none = true
|
||||
local header = false
|
||||
for _, data in ipairs(build.displayStats) do
|
||||
if data.mod then
|
||||
local diff = (nodeOutput[data.mod] or 0) - (calcBase[data.mod] or 0)
|
||||
if diff > 0.001 or diff < -0.001 then
|
||||
none = false
|
||||
if not header then
|
||||
self.main:AddTooltipLine(14, string.format("^7%s this node will give you:", node.alloc and "Unallocating" or "Allocating"))
|
||||
header = true
|
||||
end
|
||||
self.main:AddTooltipLine(14, string.format("%s%+"..data.fmt.." %s", diff > 0 and "^x00FF44" or "^xFF3300", diff * (data.pc and 100 or 1), data.label))
|
||||
end
|
||||
end
|
||||
end
|
||||
if count > 1 then
|
||||
header = false
|
||||
for _, data in ipairs(build.displayStats) do
|
||||
if data.mod then
|
||||
local diff = (pathOutput[data.mod] or 0) - (calcBase[data.mod] or 0)
|
||||
if diff > 0.001 or diff < -0.001 then
|
||||
none = false
|
||||
if not header then
|
||||
self.main:AddTooltipLine(14, string.format("^7%s this node and all nodes %s will give you:", node.alloc and "Unallocating" or "Allocating", node.alloc and "depending on it" or "leading to it"))
|
||||
header = true
|
||||
end
|
||||
self.main:AddTooltipLine(14, string.format("%s%+"..data.fmt.." %s", diff > 0 and "^x00FF44" or "^xFF3300", diff * (data.pc and 100 or 1), data.label))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
if none then
|
||||
self.main:AddTooltipLine(14, string.format("^7No changes from %s this node%s.", node.alloc and "unallocating" or "allocating", count > 1 and " or the nodes leading to it" or ""))
|
||||
end
|
||||
end
|
||||
|
||||
-- Pathing distance
|
||||
if node.path and #node.path > 0 then
|
||||
self.main:AddTooltipSeperator(14)
|
||||
self.main:AddTooltipLine(14, "^7"..#node.path .. " points to node")
|
||||
if #node.path > 1 then
|
||||
self.main:AddTooltipLine(14, "^x80A080")
|
||||
self.main:AddTooltipLine(14, "Tip: To reach this node by a different path, hold Shift, then trace the path and click this node")
|
||||
end
|
||||
end
|
||||
end
|
||||
330
Common.lua
330
Common.lua
@@ -11,6 +11,34 @@ common.xml = require("xml")
|
||||
common.json = require("dkjson")
|
||||
common.base64 = require("base64")
|
||||
|
||||
-- Basic edit field
|
||||
common.newEditField = require("simplegraphic/editfield")
|
||||
|
||||
-- Class library
|
||||
common.classes = { }
|
||||
function common.NewClass(className, initFunc)
|
||||
local class = { }
|
||||
class.__index = class
|
||||
class._init = initFunc
|
||||
common.classes[className] = class
|
||||
return class
|
||||
end
|
||||
function common.New(className, ...)
|
||||
local class = common.classes[className]
|
||||
if not class then
|
||||
error("Class '"..className.."' not defined")
|
||||
end
|
||||
local object = setmetatable({ }, class)
|
||||
class._init(object, ...)
|
||||
return object
|
||||
end
|
||||
|
||||
-- UI Controls
|
||||
LoadModule("Classes/EditControl")
|
||||
LoadModule("Classes/ButtonControl")
|
||||
LoadModule("Classes/DropDownControl")
|
||||
|
||||
-- Process input events for a host object with a list of controls
|
||||
function common.controlsInput(host, inputEvents)
|
||||
for id, event in ipairs(inputEvents) do
|
||||
if event.type == "KeyDown" then
|
||||
@@ -49,6 +77,7 @@ function common.controlsInput(host, inputEvents)
|
||||
end
|
||||
end
|
||||
|
||||
-- Draw host object's controls
|
||||
function common.controlsDraw(host, ...)
|
||||
for _, control in pairs(host.controls) do
|
||||
if control ~= host.selControl then
|
||||
@@ -61,302 +90,7 @@ function common.controlsDraw(host, ...)
|
||||
|
||||
end
|
||||
|
||||
common.newEditField = require("simplegraphic/editfield")
|
||||
|
||||
local editMeta = { }
|
||||
editMeta.__index = editMeta
|
||||
function editMeta:IsMouseOver()
|
||||
if self.hidden then
|
||||
return false
|
||||
end
|
||||
self.edit.x = type(self.x) == "function" and self:x() + 2 or self.x + 2
|
||||
self.edit.y = type(self.y) == "function" and self:y() + 2 or self.y + 2
|
||||
return self.edit:IsMouseOver()
|
||||
end
|
||||
function editMeta:OnKeyDown(key, doubleClick)
|
||||
self.edit.x = type(self.x) == "function" and self:x() + 2 or self.x + 2
|
||||
self.edit.y = type(self.y) == "function" and self:y() + 2 or self.y + 2
|
||||
self.active = not self.edit:OnKeyDown(key, doubleClick)
|
||||
return not self.active
|
||||
end
|
||||
function editMeta:OnKeyUp(key)
|
||||
self.edit.x = type(self.x) == "function" and self:x() + 2 or self.x + 2
|
||||
self.edit.y = type(self.y) == "function" and self:y() + 2 or self.y + 2
|
||||
self.active = not self.edit:OnKeyUp(key)
|
||||
return not self.active
|
||||
end
|
||||
function editMeta:OnChar(key)
|
||||
self.edit.x = type(self.x) == "function" and self:x() + 2 or self.x + 2
|
||||
self.edit.y = type(self.y) == "function" and self:y() + 2 or self.y + 2
|
||||
self.active = not self.edit:OnChar(key)
|
||||
return not self.active
|
||||
end
|
||||
function editMeta:Draw()
|
||||
if self.hidden then
|
||||
return
|
||||
end
|
||||
local x = type(self.x) == "function" and self:x() or self.x
|
||||
local y = type(self.y) == "function" and self:y() or self.y
|
||||
local enabled = not self.edit.enableFunc or self.edit.enableFunc()
|
||||
local mOver = self:IsMouseOver()
|
||||
if not enabled then
|
||||
SetDrawColor(0.33, 0.33, 0.33)
|
||||
elseif mOver then
|
||||
SetDrawColor(1, 1, 1)
|
||||
else
|
||||
SetDrawColor(0.5, 0.5, 0.5)
|
||||
end
|
||||
DrawImage(nil, x, y, self.width, self.height)
|
||||
if not enabled then
|
||||
SetDrawColor(0, 0, 0)
|
||||
elseif self.active or mOver then
|
||||
SetDrawColor(0.15, 0.15, 0.15)
|
||||
else
|
||||
SetDrawColor(0, 0, 0)
|
||||
end
|
||||
DrawImage(nil, x + 1, y + 1, self.width - 2, self.height - 2)
|
||||
self.edit.x = x + 2
|
||||
self.edit.y = y + 2
|
||||
self.edit:Draw(nil, nil, nil, not self.active)
|
||||
end
|
||||
function editMeta:SetText(text)
|
||||
self.edit:SetText(text)
|
||||
end
|
||||
function common.newEditControl(x, y, width, height, ...)
|
||||
local control = { }
|
||||
control.x = x
|
||||
control.y = y
|
||||
control.width = width
|
||||
control.height = height
|
||||
control.edit = common.newEditField(...)
|
||||
control.edit.width = width - 4
|
||||
control.edit.height = height - 4
|
||||
control.edit.leader = ""
|
||||
return setmetatable(control, editMeta)
|
||||
end
|
||||
|
||||
local buttonMeta = { }
|
||||
buttonMeta.__index = buttonMeta
|
||||
function buttonMeta:IsMouseOver()
|
||||
if self.hidden then
|
||||
return false
|
||||
end
|
||||
local x = type(self.x) == "function" and self:x() or self.x
|
||||
local y = type(self.y) == "function" and self:y() or self.y
|
||||
local cx, cy = GetCursorPos()
|
||||
return cx >= x and cy >= y and cx < x + self.width and cy < y + self.height
|
||||
end
|
||||
function buttonMeta:OnKeyDown(key)
|
||||
if self.enableFunc and not self.enableFunc() then
|
||||
return true
|
||||
end
|
||||
if key == "LEFTBUTTON" then
|
||||
self.clicked = true
|
||||
end
|
||||
return false
|
||||
end
|
||||
function buttonMeta:OnKeyUp(key)
|
||||
if self.enableFunc and not self.enableFunc() then
|
||||
return true
|
||||
end
|
||||
if key == "LEFTBUTTON" then
|
||||
if self:IsMouseOver() then
|
||||
self.onClick()
|
||||
end
|
||||
end
|
||||
self.clicked = false
|
||||
return true
|
||||
end
|
||||
function buttonMeta:Draw()
|
||||
if self.hidden then
|
||||
return
|
||||
end
|
||||
local x = type(self.x) == "function" and self:x() or self.x
|
||||
local y = type(self.y) == "function" and self:y() or self.y
|
||||
local enabled = not self.enableFunc or self.enableFunc()
|
||||
local mOver = self:IsMouseOver()
|
||||
if not enabled then
|
||||
SetDrawColor(0.33, 0.33, 0.33)
|
||||
elseif mOver then
|
||||
SetDrawColor(1, 1, 1)
|
||||
else
|
||||
SetDrawColor(0.5, 0.5, 0.5)
|
||||
end
|
||||
DrawImage(nil, x, y, self.width, self.height)
|
||||
if not enabled then
|
||||
SetDrawColor(0, 0, 0)
|
||||
elseif self.clicked and mOver then
|
||||
SetDrawColor(0.5, 0.5, 0.5)
|
||||
elseif mOver then
|
||||
SetDrawColor(0.33, 0.33, 0.33)
|
||||
else
|
||||
SetDrawColor(0, 0, 0)
|
||||
end
|
||||
DrawImage(nil, x + 1, y + 1, self.width - 2, self.height - 2)
|
||||
if enabled then
|
||||
SetDrawColor(1, 1, 1)
|
||||
else
|
||||
SetDrawColor(0.33, 0.33, 0.33)
|
||||
end
|
||||
DrawString(x + self.width / 2, y + 2, "CENTER_X", self.height - 4, "VAR", self.label)
|
||||
end
|
||||
function common.newButton(x, y, width, height, label, onClick, enableFunc)
|
||||
local control = { }
|
||||
control.x = x
|
||||
control.y = y
|
||||
control.width = width
|
||||
control.height = height
|
||||
control.label = label
|
||||
control.onClick = onClick
|
||||
control.enableFunc = enableFunc
|
||||
return setmetatable(control, buttonMeta)
|
||||
end
|
||||
|
||||
local dropDownMeta = { }
|
||||
dropDownMeta.__index = dropDownMeta
|
||||
function dropDownMeta:IsMouseOver()
|
||||
if self.hidden then
|
||||
return false
|
||||
end
|
||||
local x = type(self.x) == "function" and self:x() or self.x
|
||||
local y = type(self.y) == "function" and self:y() or self.y
|
||||
local cx, cy = GetCursorPos()
|
||||
local dropExtra = self.dropped and (self.height - 4) * #self.list + 2 or 0
|
||||
return cx >= x and cy >= y and cx < x + self.width and cy < y + self.height + dropExtra, cy < y + self.height
|
||||
end
|
||||
function dropDownMeta:OnKeyDown(key)
|
||||
if self.enableFunc and not self.enableFunc() then
|
||||
return true
|
||||
end
|
||||
if key == "LEFTBUTTON" then
|
||||
local all, body = self:IsMouseOver()
|
||||
if not all or (self.dropped and body) then
|
||||
self.dropped = false
|
||||
return true
|
||||
end
|
||||
self.dropped = true
|
||||
return false
|
||||
elseif key == "ESCAPE" then
|
||||
self.dropped = false
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
function dropDownMeta:OnKeyUp(key)
|
||||
if self.enableFunc and not self.enableFunc() then
|
||||
return true
|
||||
end
|
||||
if key == "LEFTBUTTON" then
|
||||
local all, body = self:IsMouseOver()
|
||||
if not all then
|
||||
self.dropped = false
|
||||
return true
|
||||
elseif not body then
|
||||
local y = type(self.y) == "function" and self:y() or self.y
|
||||
local sel = math.max(1, math.floor((select(2,GetCursorPos()) - y - self.height) / (self.height - 4)) + 1)
|
||||
self.sel = sel
|
||||
if self.selFunc then
|
||||
self.selFunc(sel, self.list[sel])
|
||||
end
|
||||
self.dropped = false
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
function dropDownMeta:Draw()
|
||||
if self.hidden then
|
||||
return false
|
||||
end
|
||||
local x = type(self.x) == "function" and self:x() or self.x
|
||||
local y = type(self.y) == "function" and self:y() or self.y
|
||||
local enabled = not self.enableFunc or self.enableFunc()
|
||||
local mOver, mOverBody = self:IsMouseOver()
|
||||
local dropExtra = (self.height - 4) * #self.list + 4
|
||||
if not enabled then
|
||||
SetDrawColor(0.33, 0.33, 0.33)
|
||||
elseif mOver or self.dropped then
|
||||
SetDrawColor(1, 1, 1)
|
||||
else
|
||||
SetDrawColor(0.5, 0.5, 0.5)
|
||||
end
|
||||
DrawImage(nil, x, y, self.width, self.height)
|
||||
if self.dropped then
|
||||
DrawImage(nil, x, y + self.height, self.width, dropExtra)
|
||||
end
|
||||
if not enabled then
|
||||
SetDrawColor(0, 0, 0)
|
||||
elseif self.dropped then
|
||||
SetDrawColor(0.5, 0.5, 0.5)
|
||||
elseif mOver then
|
||||
SetDrawColor(0.33, 0.33, 0.33)
|
||||
else
|
||||
SetDrawColor(0, 0, 0)
|
||||
end
|
||||
DrawImage(nil, x + 1, y + 1, self.width - 2, self.height - 2)
|
||||
if not enabled then
|
||||
SetDrawColor(0.33, 0.33, 0.33)
|
||||
elseif mOver or self.dropped then
|
||||
SetDrawColor(1, 1, 1)
|
||||
else
|
||||
SetDrawColor(0.5, 0.5, 0.5)
|
||||
end
|
||||
local x1 = x + self.width - self.height / 2 - self.height / 4
|
||||
local x2 = x1 + self.height / 2
|
||||
local y1 = y + self.height / 4
|
||||
local y2 = y1 + self.height / 2
|
||||
DrawImageQuad(nil, x1, y1, x2, y1, (x1+x2)/2, y2, (x1+x2)/2, y2)
|
||||
if self.dropped then
|
||||
SetDrawColor(0, 0, 0)
|
||||
DrawImage(nil, x + 1, y + self.height + 1, self.width - 2, dropExtra - 2)
|
||||
end
|
||||
if enabled then
|
||||
SetDrawColor(1, 1, 1)
|
||||
else
|
||||
SetDrawColor(0.66, 0.66, 0.66)
|
||||
end
|
||||
DrawString(x + 2, y + 2, "LEFT", self.height - 4, "VAR", self.list[self.sel] or "")
|
||||
if self.dropped then
|
||||
self.hoverSel = mOver and math.floor((select(2,GetCursorPos()) - y - self.height) / (self.height - 4)) + 1
|
||||
if self.hoverSel and self.hoverSel < 1 then
|
||||
self.hoverSel = nil
|
||||
end
|
||||
for index, val in ipairs(self.list) do
|
||||
local y = y + self.height + 2 + (index - 1) * (self.height - 4)
|
||||
if index == self.hoverSel then
|
||||
SetDrawColor(0.5, 0.4, 0.3)
|
||||
DrawImage(nil, x + 2, y, self.width - 4, self.height - 4)
|
||||
end
|
||||
if index == self.hoverSel or index == self.sel then
|
||||
SetDrawColor(1, 1, 1)
|
||||
else
|
||||
SetDrawColor(0.66, 0.66, 0.66)
|
||||
end
|
||||
DrawString(x + 2, y, "LEFT", self.height - 4, "VAR", StripEscapes(val))
|
||||
end
|
||||
end
|
||||
end
|
||||
function dropDownMeta:SelByValue(val)
|
||||
for index, listVal in ipairs(self.list) do
|
||||
if listVal == val then
|
||||
self.sel = index
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
function common.newDropDown(x, y, width, height, list, selFunc, enableFunc)
|
||||
local control = { }
|
||||
control.x = x
|
||||
control.y = y
|
||||
control.width = width
|
||||
control.height = height
|
||||
control.list = list or { }
|
||||
control.sel = 1
|
||||
control.selFunc = selFunc
|
||||
control.enableFunc = enableFunc
|
||||
return setmetatable(control, dropDownMeta)
|
||||
end
|
||||
|
||||
-- Draw simple popup message box
|
||||
function common.drawPopup(r, g, b, fmt, ...)
|
||||
local screenW, screenH = GetScreenSize()
|
||||
SetDrawColor(0, 0, 0, 0.5)
|
||||
@@ -375,6 +109,7 @@ function common.drawPopup(r, g, b, fmt, ...)
|
||||
DrawString(0, oy + 10, "CENTER", 20, "VAR", txt)
|
||||
end
|
||||
|
||||
-- Make a copy of a table and all subtables
|
||||
function copyTable(tbl)
|
||||
local out = {}
|
||||
for k, v in pairs(tbl) do
|
||||
@@ -387,6 +122,7 @@ function copyTable(tbl)
|
||||
return out
|
||||
end
|
||||
|
||||
-- Wipe all keys from the table and return it, or return a new table if no table provided
|
||||
function wipeTable(tbl)
|
||||
if not tbl then
|
||||
return { }
|
||||
@@ -397,6 +133,7 @@ function wipeTable(tbl)
|
||||
return tbl
|
||||
end
|
||||
|
||||
-- Search a table for a value, and return the corresponding key
|
||||
function isValueInTable(tbl, val)
|
||||
for k, v in pairs(tbl) do
|
||||
if val == v then
|
||||
@@ -405,6 +142,7 @@ function isValueInTable(tbl, val)
|
||||
end
|
||||
end
|
||||
|
||||
-- Search an array for a value, and return the corresponding array index
|
||||
function isValueInArray(tbl, val)
|
||||
for i, v in ipairs(tbl) do
|
||||
if val == v then
|
||||
|
||||
@@ -12,23 +12,23 @@ local buildMode = { }
|
||||
|
||||
buildMode.controls = { }
|
||||
|
||||
t_insert(buildMode.controls, common.newButton(4, 4, 60, 20, "<< Back", function()
|
||||
t_insert(buildMode.controls, common.New("ButtonControl", 4, 4, 60, 20, "<< Back", function()
|
||||
main:SetMode("LIST", buildMode.dbFileName)
|
||||
end))
|
||||
|
||||
t_insert(buildMode.controls, common.newButton(4 + 68, 4, 60, 20, "Tree", function()
|
||||
t_insert(buildMode.controls, common.New("ButtonControl", 4 + 68, 4, 60, 20, "Tree", function()
|
||||
buildMode.viewMode = "TREE"
|
||||
end, function()
|
||||
return buildMode.viewMode ~= "TREE"
|
||||
end))
|
||||
|
||||
t_insert(buildMode.controls, common.newButton(4 + 68*2, 4, 60, 20, "Items", function()
|
||||
t_insert(buildMode.controls, common.New("ButtonControl", 4 + 68*2, 4, 60, 20, "Items", function()
|
||||
buildMode.viewMode = "ITEMS"
|
||||
end, function()
|
||||
return buildMode.viewMode ~= "ITEMS"
|
||||
end))
|
||||
|
||||
t_insert(buildMode.controls, common.newButton(4 + 68*3, 4, 60, 20, "Calcs", function()
|
||||
t_insert(buildMode.controls, common.New("ButtonControl", 4 + 68*3, 4, 60, 20, "Calcs", function()
|
||||
buildMode.viewMode = "CALCS"
|
||||
end, function()
|
||||
return buildMode.viewMode ~= "CALCS"
|
||||
@@ -67,7 +67,7 @@ buildMode.controls.pointDisplay = {
|
||||
end,
|
||||
}
|
||||
|
||||
buildMode.controls.classDrop = common.newDropDown(0, 4, 100, 20, nil, function(index, val)
|
||||
buildMode.controls.classDrop = common.New("DropDownControl", 0, 4, 100, 20, nil, function(index, val)
|
||||
local classId = main.tree.classNameMap[val]
|
||||
if classId ~= buildMode.spec.curClassId then
|
||||
if buildMode.spec:IsClassConnected(classId) or buildMode.spec:CountAllocNodes() == 0 then
|
||||
@@ -87,7 +87,7 @@ end, function()
|
||||
return buildMode.viewMode == "TREE"
|
||||
end)
|
||||
|
||||
buildMode.controls.ascendDrop = common.newDropDown(0, 4, 100, 20, nil, function(index, val)
|
||||
buildMode.controls.ascendDrop = common.New("DropDownControl", 0, 4, 100, 20, nil, function(index, val)
|
||||
local ascendClassId = main.tree.ascendNameMap[val].ascendClassId
|
||||
buildMode.spec:SelectAscendClass(ascendClassId)
|
||||
buildMode.spec:AddUndoState()
|
||||
@@ -164,8 +164,8 @@ function buildMode:Init(dbFileName)
|
||||
self.calcs = LoadModule("Modules/Calcs", launch, cfg, main)
|
||||
self.calcs:Init(self)
|
||||
self.tree = main.tree
|
||||
self.spec = main.SpecClass.NewSpec(main.tree)
|
||||
self.treeView = main.TreeViewClass.NewTreeView()
|
||||
self.spec = common.New("PassiveSpec", main.tree)
|
||||
self.treeView = common.New("PassiveTreeView")
|
||||
|
||||
wipeTable(self.controls.classDrop.list)
|
||||
for classId, class in pairs(self.tree.classes) do
|
||||
|
||||
@@ -10,20 +10,20 @@ local vfs = require("vfs")
|
||||
local listMode = { }
|
||||
|
||||
listMode.controls = {
|
||||
common.newButton(2, 2, 60, 20, "New", function()
|
||||
common.New("ButtonControl", 2, 2, 60, 20, "New", function()
|
||||
listMode:New()
|
||||
end),
|
||||
common.newButton(66, 2, 60, 20, "Copy", function()
|
||||
common.New("ButtonControl", 66, 2, 60, 20, "Copy", function()
|
||||
listMode:CopySel()
|
||||
end, function()
|
||||
return listMode.sel ~= nil
|
||||
end),
|
||||
common.newButton(130, 2, 60, 20, "Rename", function()
|
||||
common.New("ButtonControl", 130, 2, 60, 20, "Rename", function()
|
||||
listMode:RenameSel()
|
||||
end, function()
|
||||
return listMode.sel ~= nil
|
||||
end),
|
||||
common.newButton(194, 2, 60, 20, "Delete", function()
|
||||
common.New("ButtonControl", 194, 2, 60, 20, "Delete", function()
|
||||
listMode:DeleteSel()
|
||||
end, function()
|
||||
return listMode.sel ~= nil
|
||||
|
||||
@@ -48,7 +48,7 @@ elem.input.cellCol = { 0.1, 0.1, 0.4 }
|
||||
function elem.input:Init()
|
||||
local grid = self.grid
|
||||
if self.format == "choice" then
|
||||
self.dropDown = common.newDropDown(0, 0, 0, 0, self.list, function(index, val)
|
||||
self.dropDown = common.New("DropDownControl", 0, 0, 0, 0, self.list, function(index, val)
|
||||
if val ~= grid.input[self.name] then
|
||||
grid.input[self.name] = val
|
||||
grid.changeFlag = true
|
||||
|
||||
@@ -10,6 +10,8 @@ data.gems = { }
|
||||
data.gems["_default"] = {
|
||||
attack = true,
|
||||
melee = true,
|
||||
bow = true,
|
||||
projectile = true,
|
||||
base = {
|
||||
},
|
||||
quality = {
|
||||
@@ -95,5 +97,5 @@ local itemTypes = {
|
||||
"misc"
|
||||
}
|
||||
for _, type in pairs(itemTypes) do
|
||||
LoadModule("items/"..type, data.itemBases)
|
||||
LoadModule("Items/"..type, data.itemBases)
|
||||
end
|
||||
@@ -19,7 +19,7 @@ end
|
||||
items.slots = { }
|
||||
items.controls = { }
|
||||
|
||||
items.controls.addDisplayItem = common.newButton(0, 0, 60, 20, "Add", function()
|
||||
items.controls.addDisplayItem = common.New("ButtonControl", 0, 0, 60, 20, "Add", function()
|
||||
items:AddDisplayItem()
|
||||
end)
|
||||
|
||||
@@ -33,7 +33,7 @@ local function mkItemSlot(x, y, slotName, slotLabel)
|
||||
slot.height = 20
|
||||
slot.slotName = slotName
|
||||
slot.label = slotLabel or slotName
|
||||
slot.dropDown = common.newDropDown(x, y, slot.width, slot.height, slot.list, function(sel)
|
||||
slot.dropDown = common.New("DropDownControl", x, y, slot.width, slot.height, slot.list, function(sel)
|
||||
if slot.items[sel] ~= slot.selItem then
|
||||
slot.selItem = slot.items[sel]
|
||||
items:PopulateSlots()
|
||||
|
||||
@@ -10,6 +10,13 @@ local m_floor = math.floor
|
||||
local m_max = math.max
|
||||
local t_insert = table.insert
|
||||
|
||||
LoadModule("Modules/Data")
|
||||
LoadModule("Modules/ModTools")
|
||||
|
||||
LoadModule("Classes/PassiveTree", launch)
|
||||
LoadModule("Classes/PassiveSpec", launch)
|
||||
LoadModule("Classes/PassiveTreeView", launch)
|
||||
|
||||
local cfg = { }
|
||||
|
||||
local main = { }
|
||||
@@ -80,13 +87,6 @@ function main:DrawTooltip(x, y, w, h, viewPort, col, center)
|
||||
end
|
||||
end
|
||||
|
||||
LoadModule("Modules/Data")
|
||||
LoadModule("Modules/ModTools")
|
||||
|
||||
main.TreeClass = LoadModule("Modules/Tree", launch, cfg, main)
|
||||
main.SpecClass = LoadModule("Modules/Spec", launch, cfg, main)
|
||||
main.TreeViewClass = LoadModule("Modules/TreeView", launch, cfg, main)
|
||||
|
||||
main.modes = { }
|
||||
main.modes["LIST"] = LoadModule("Modules/BuildList", launch, cfg, main)
|
||||
main.modes["BUILD"] = LoadModule("Modules/Build", launch, cfg, main)
|
||||
@@ -192,7 +192,7 @@ end
|
||||
function main:Init()
|
||||
self.inputEvents = { }
|
||||
|
||||
self.tree = self.TreeClass.NewTree()
|
||||
self.tree = common.New("PassiveTree")
|
||||
|
||||
self:SetMode("LIST")
|
||||
|
||||
|
||||
@@ -49,9 +49,16 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Modules", "Modules", "{35D8
|
||||
Modules\Main.lua = Modules\Main.lua
|
||||
Modules\ModParser.lua = Modules\ModParser.lua
|
||||
Modules\ModTools.lua = Modules\ModTools.lua
|
||||
Modules\Spec.lua = Modules\Spec.lua
|
||||
Modules\Tree.lua = Modules\Tree.lua
|
||||
Modules\TreeView.lua = Modules\TreeView.lua
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Classes", "Classes", "{7EE4DE67-DABD-4A3C-8772-37D4A9DB6D59}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
Classes\ButtonControl.lua = Classes\ButtonControl.lua
|
||||
Classes\DropDownControl.lua = Classes\DropDownControl.lua
|
||||
Classes\EditControl.lua = Classes\EditControl.lua
|
||||
Classes\PassiveSpec.lua = Classes\PassiveSpec.lua
|
||||
Classes\PassiveTree.lua = Classes\PassiveTree.lua
|
||||
Classes\PassiveTreeView.lua = Classes\PassiveTreeView.lua
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Global
|
||||
@@ -62,5 +69,6 @@ Global
|
||||
{EE9D06F5-7310-4567-AB18-6923BCF0A2A1} = {AE7B8DC6-833A-4817-915D-AAB9B8F17C1B}
|
||||
{5BCB69A7-EFE8-4C72-B5E3-DF2C1E227290} = {AE7B8DC6-833A-4817-915D-AAB9B8F17C1B}
|
||||
{35D8BE82-CD83-4C2D-AD31-47026E724F46} = {AE7B8DC6-833A-4817-915D-AAB9B8F17C1B}
|
||||
{7EE4DE67-DABD-4A3C-8772-37D4A9DB6D59} = {AE7B8DC6-833A-4817-915D-AAB9B8F17C1B}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
|
||||
Reference in New Issue
Block a user