From b5c12aad982a1d6922cb766d93e871c47ca97cd6 Mon Sep 17 00:00:00 2001 From: Openarl Date: Thu, 5 May 2016 22:18:03 +1000 Subject: [PATCH] Big cleanup pass --- Classes/Grid.lua | 491 +++++++++++++++++++++++++++++ Classes/ItemSlot.lua | 83 +++++ Classes/PassiveSpec.lua | 6 +- Classes/PassiveTreeView.lua | 2 +- Common.lua | 3 +- Launch.lua | 170 +++++------ Modules/Build.lua | 320 ++++++++++--------- Modules/BuildList.lua | 291 +++++++++--------- Modules/Calcs.lua | 595 +++++------------------------------- Modules/CalcsControl.lua | 128 ++++---- Modules/CalcsView.lua | 30 +- Modules/Items.lua | 391 ++++++++++-------------- Modules/Main.lua | 243 +++++++-------- Modules/ModParser.lua | 3 + PathOfBuilding.sln | 2 + 15 files changed, 1411 insertions(+), 1347 deletions(-) create mode 100644 Classes/Grid.lua create mode 100644 Classes/ItemSlot.lua diff --git a/Classes/Grid.lua b/Classes/Grid.lua new file mode 100644 index 00000000..5c2ac9d8 --- /dev/null +++ b/Classes/Grid.lua @@ -0,0 +1,491 @@ +-- Path of Building +-- +-- Class: Grid +-- Display grid for the calculations breakdown view +-- +local launch = ... + +local pairs = pairs +local m_max = math.max +local m_floor = math.floor +local t_insert = table.insert + +local cfg = { } +cfg.gridHeight = 18 +cfg.defGridWidth = 50 +cfg.defBorderCol = { 0.1, 0.1, 0.1 } +cfg.defCellCol = { 0, 0, 0 } + +local function alignCellText(elem) + if elem.align == "RIGHT" then + return elem.grid.offX + elem.x + elem.grid[elem.gx].width - 2, "RIGHT_X" + elseif elem.align == "CENTER" then + return elem.grid.offX + elem.x + elem.grid[elem.gx].width / 2, "CENTER_X" + else + return elem.grid.offX + elem.x + 2, "LEFT" + end +end + +local function formatCellText(fn, val) + if fn then + local errMsg, text = PCall(fn, val) + if errMsg then + launch:ShowErrMsg("Error formatting cell: %s", errMsg) + return "" + else + return text + end + else + return tostring(val) + end +end + +local elemTypes = {} + +elemTypes.input = {} +elemTypes.input.__index = elemTypes.input +elemTypes.input.borderCol = { 0.9, 0.9, 0.9 } +elemTypes.input.cellCol = { 0.1, 0.1, 0.4 } +function elemTypes.input:Init() + local grid = self.grid + if self.format == "choice" then + 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 + end + end) + self.dropDown.sel = 1 + end +end +function elemTypes.input:Draw() + local grid = self.grid + if self.format == "check" then + local x, align = alignCellText(self) + DrawString(x, grid.offY + self.y + 2, align, cfg.gridHeight - 4, "FIXED", grid.input[self.name] and "^x33FF33Yes" or "^xFF3333No") + elseif grid.focus == self then + if self.edit then + self.edit:Draw() + else + self.dropDown:Draw() + end + elseif grid.input[self.name] then + local x, align = alignCellText(self) + DrawString(x, grid.offY + self.y + 2, align, cfg.gridHeight - 4, "VAR", "^7"..formatCellText(self.formatFunc, grid.input[self.name])) + end +end +function elemTypes.input:OnKeyDown(key, doubleClick) + local grid = self.grid + if grid.focus == self then + if key == "RETURN" or key == "TAB" then + if self.edit then + local newVal = #self.edit.buf and self.edit.buf or nil + if self.format == "number" then + newVal = tonumber((newVal:gsub(",",""):gsub("%*","e"))) + end + if newVal ~= grid.input[self.name] then + grid.input[self.name] = newVal + grid.changeFlag = true + end + end + grid:SetFocus() + grid:MoveSel(key == "TAB" and "RIGHT" or "DOWN", true) + elseif self.edit then + self.edit:OnKeyDown(key) + elseif self.dropDown then + if self.dropDown:OnKeyDown(key) then + grid:SetFocus() + end + end + elseif key == "RIGHTBUTTON" or (key == "LEFTBUTTON" and doubleClick) then + if self.format == "check" then + grid.input[self.name] = not grid.input[self.name] + grid.changeFlag = true + elseif self.format == "choice" then + grid:SetFocus(self) + else + grid:SetFocus(self) + self.edit:SetText(grid.input[self.name] or "") + end + elseif key == "WHEELUP" then + if self.format == "number" then + grid.input[self.name] = (grid.input[self.name] or 0) + 1 + grid.changeFlag = true + end + elseif key == "WHEELDOWN" then + if self.format == "number" then + grid.input[self.name] = (grid.input[self.name] or 0) - 1 + grid.changeFlag = true + end + elseif self.edit then + if key == "c" and IsKeyDown("CTRL") then + if grid.input[self.name] then + Copy(tostring(grid.input[self.name])) + end + elseif key == "v" and IsKeyDown("CTRL") then + local newVal = Paste() + if newVal then + if self.format == "number" then + newVal = tonumber(newVal) + end + if newVal ~= grid.input[self.name] then + grid.input[self.name] = newVal + grid.changeFlag = true + end + end + end + end +end +function elemTypes.input:OnKeyUp(key) + local grid = self.grid + if grid.focus == self then + if self.dropDown then + if self.dropDown:OnKeyUp(key) then + grid:SetFocus() + end + else + self.edit:OnKeyUp(key) + end + end +end +function elemTypes.input:OnChar(key) + local grid = self.grid + if self.format == "check" then + if key == " " then + grid.input[self.name] = not grid.input[self.name] + grid.changeFlag = true + end + return + elseif self.format == "choice" then + return + end + if key == "\r" then + return + elseif not grid.focus and key == "\b" then + grid.input[self.name] = nil + grid.changeFlag = true + return + elseif key:match("%c") then + return + end + if not grid.focus then + if self.format == "number" and key == "+" then + grid.input[self.name] = (grid.input[self.name] or 0) + 1 + grid.changeFlag = true + return + end + grid:SetFocus(self) + end + self.edit:OnChar(key) +end +function elemTypes.input:OnFocusGained() + local grid = self.grid + if self.format == "choice" then + self.dropDown.x = grid.offX + self.x + self.dropDown.y = grid.offY + self.y + self.dropDown.width = grid:GetElemWidth(self) + self.dropDown.height = cfg.gridHeight + self.dropDown:SelByValue(grid.input[self.name]) + self.dropDown:OnKeyDown("LEFTBUTTON") + else + local fmtFilter = { number = "[-%d%.e,*]", string = "." } + self.edit = common.newEditField(nil, nil, fmtFilter[self.format]) + self.edit.x = grid.offX + self.x + 2 + self.edit.y = grid.offY + self.y + 2 + self.edit.width = grid:GetElemWidth(self) + self.edit.height = cfg.gridHeight - 4 + end +end +function elemTypes.input:OnFocusLost() + self.edit = nil + if self.dropDown then + self.dropDown.dropped = false + end +end + +elemTypes.output = {} +elemTypes.output.__index = elemTypes.output +elemTypes.output.borderCol = { 0.7, 0.7, 0.7 } +function elemTypes.output:Draw() + local grid = self.grid + if grid.output[self.name] then + local x, align = alignCellText(self) + DrawString(x, grid.offY + self.y + 2, align, cfg.gridHeight - 4, self.font or "VAR", "^7"..formatCellText(self.formatFunc, grid.output[self.name])) + end +end +function elemTypes.output:OnKeyDown(key) + local grid = self.grid + if key == "c" and IsKeyDown("CTRL") and grid.output[self.name] then + Copy(tostring(grid.output[self.name])) + end +end + +elemTypes.label = {} +elemTypes.label.__index = elemTypes.label +function elemTypes.label:Draw() + local grid = self.grid + local x, align = alignCellText(self) + DrawString(x, grid.offY + self.y + 2, align, cfg.gridHeight - 4, "VAR BOLD", "^xD0D5D0"..self.text) +end + +local GridClass = common.NewClass("Grid", function(self, input, output) + self.input = input + self.output = output + self:Clear() +end) + +function GridClass:Draw() + local x = self.offX + local h = cfg.gridHeight + for gx = 1, self.width do + local y = self.offY + local w = self[gx].width + for gy = 1, self.height do + local elem = self[gx][gy] + if not elem or elem.gx == gx then + local ew = w + if elem and elem.width and elem.width > 1 then + for i = 1, elem.width - 1 do + ew = ew + self[gx + i].width + end + end + SetDrawColor(unpack(elem and elem.borderCol or cfg.defBorderCol)) + DrawImage(nil, x, y, ew, h) + SetDrawColor(unpack(elem and elem.cellCol or cfg.defCellCol)) + DrawImage(nil, x + 1, y + 1, ew - 2, h - 2) + end + y = y + cfg.gridHeight + end + x = x + self[gx].width + end + for gx = 1, self.width do + for gy = 1, self.height do + local elem = self[gx][gy] + if elem and elem.gx == gx and elem.Draw and elem ~= self.focus then + elem:Draw() + end + end + end + if self.focus then + self.focus:Draw() + end + if self.sel and self.sel.gx then + local selX, selY = self.sel.gx, self.sel.gy + SetDrawColor(1, 1, 1) + local x, y = self.sel.x + self.offX, self.sel.y + self.offY + local w, h = self:GetElemWidth(self.sel), cfg.gridHeight + DrawImage(nil, x - 2, y - 2, w + 4, 4) + DrawImage(nil, x - 2, y + h - 2, w + 4, 4) + DrawImage(nil, x - 2, y - 2, 4, h + 4) + DrawImage(nil, x + w - 2, y - 2, 4, h + 4) + end +end + +function GridClass:OnKeyDown(key, doubleClick) + if self.focus then + if self.focus.OnKeyDown then + self.focus:OnKeyDown(key, doubleClick) + end + elseif key == "LEFTBUTTON" or key == "RIGHTBUTTON" then + self.sel = nil + local cx, cy = GetCursorPos() + local gcx, gcy = cx - self.offX, cy - self.offY + local gy = math.floor(gcy / cfg.gridHeight) + 1 + if gcx >= 0 and gcy >= 0 and gcx < self.realWidth and gcy < self.realHeight then + local x = 0 + for gx = 1, self.width do + if gcx >= x and gcx < x + self[gx].width then + if self[gx][gy] then + local elem = self[gx][gy] + self.sel = elem + if elem.OnKeyDown then + elem:OnKeyDown(key, doubleClick) + end + end + return + end + x = x + self[gx].width + end + end + elseif key == "LEFT" or key == "RIGHT" or key == "UP" or key == "DOWN" then + self:MoveSel(key) + elseif self.sel then + if self.sel.OnKeyDown then + self.sel:OnKeyDown(key) + end + end +end + +function GridClass:OnKeyUp(key) + if self.focus then + if key == "ESCAPE" then + self:SetFocus() + elseif self.focus.OnKeyUp then + self.focus:OnKeyUp(key) + end + elseif key == "ESCAPE" then + self.sel = nil + elseif self.sel then + if self.sel.OnKeyUp then + self.sel:OnKeyUp(key) + end + end +end + +function GridClass:OnChar(key) + if self.focus then + if self.focus.OnChar then + self.focus:OnChar(key) + end + elseif self.sel then + if self.sel.OnChar then + self.sel:OnChar(key) + end + end +end + +function GridClass:Clear() + for gx in ipairs(self) do + self[gx] = nil + end + self.width = 1 + self.height = 1 + self[1] = { width = cfg.defGridWidth, x = 0 } + self:CalcCoords() +end + +function GridClass:SetSize(w, h) + if w < self.width then + for gx = w + 1, self.width do + self[gx] = nil + end + end + self.width = w + self.height = h + for gx = 1, w do + self[gx] = self[gx] or { width = cfg.defGridWidth } + end + self:CalcCoords() +end + +function GridClass:CheckSize() + local mx, my = 1, 1 + for gx = 1, self.width do + for gy = 1, self.height do + if self[gx][gy] then + mx = math.max(mx, gx + self[gx][gy].width - 1) + my = math.max(my, gy) + end + end + end + self:SetSize(mx, my) +end + +function GridClass:CalcCoords() + local x = 0 + for gx = 1, self.width do + self[gx].x = x + for gy = 1, self.height do + if self[gx][gy] and self[gx][gy].gx == gx then + self[gx][gy].x = x + end + end + x = x + self[gx].width + end + self.realWidth = x + self.realHeight = self.height * cfg.gridHeight +end + +function GridClass:SetColWidth(gx, sz) + if self[gx] then + self[gx].width = sz + end + self:CalcCoords() +end + +function GridClass:GetElem(gx, gy) + return self[gx] and self[gx][gy] +end + +function GridClass:SetElem(gx, gy, elem) + if elem then + elem.grid = self + elem.gx = gx + elem.gy = gy + elem.width = elem.width or 1 + if gx + elem.width - 1 > self.width then + self:SetSize(gx + elem.width - 1, self.height) + end + if gy > self.height then + self:SetSize(self.width, gy) + end + for i = 1, elem.width do + self[gx + i - 1][gy] = elem + end + elem.x = self[gx].x + elem.y = (gy - 1) * cfg.gridHeight + if elemTypes[elem.type] then + setmetatable(elem, elemTypes[elem.type]) + end + if elem.Init then + elem:Init() + end + elseif self:GetElem(gx, gy) then + self[gx][gy] = nil + self:CheckSize() + end +end + +function GridClass:GetElemWidth(elem) + local width = 0 + for gx = elem.gx, elem.gx + elem.width - 1 do + width = width + self[gx].width + end + return width +end + +function GridClass:SetFocus(elem) + if self.focus and self.focus.OnFocusLost then + self.focus:OnFocusLost() + end + self.focus = elem + if self.focus and self.focus.OnFocusGained then + self.focus:OnFocusGained() + end +end + +function GridClass:MoveSel(dir, force) + if not self.sel or not self.sel.gx then + return + end + local selX, selY = self.sel.gx, self.sel.gy + local s, elem, i + if dir == "LEFT" or dir == "RIGHT" then + if dir == "LEFT" then + s, elem, i = selX - 1, 1, -1 + else + s, elem, i = selX + 1, self.width, 1 + end + for gx = s, elem, i do + if self[gx][selY] then + self.sel = self[gx][selY] + return + end + end + else + if dir == "UP" then + s, elem, i = selY - 1, 1, -1 + else + s, elem, i = selY + 1, self.height, 1 + end + for gy = s, elem, i do + if self[selX][gy] then + self.sel = self[selX][gy] + return + end + end + end + if force then + self.sel = nil + end +end \ No newline at end of file diff --git a/Classes/ItemSlot.lua b/Classes/ItemSlot.lua new file mode 100644 index 00000000..0a36de00 --- /dev/null +++ b/Classes/ItemSlot.lua @@ -0,0 +1,83 @@ +-- Path of Building +-- +-- Class: Item Slot +-- Item Slot control, wrapper for the basic dropdown control +-- +local launch, main = ... + +local ipairs = ipairs +local t_insert = table.insert + +local ItemSlotClass = common.NewClass("ItemSlot", function(self, itemsMain, x, y, slotName, slotLabel) + self.itemsMain = itemsMain + self.items = { } + self.list = { } + self.x = x + self.y = y + self.width = 320 + self.height = 20 + self.slotName = slotName + self.label = slotLabel or slotName + self.dropDown = common.New("DropDownControl", x, y, self.width, self.height, self.list, function(sel) + if self.itemsMain[sel] ~= self.selItem then + self.selItem = self.itemsMain[sel] + itemsMain:PopulateSlots() + itemsMain.buildFlag = true + itemsMain.modFlag = true + end + end) + itemsMain.slots[slotName] = self +end) + +function ItemSlotClass:Populate() + wipeTable(self.items) + wipeTable(self.list) + self.items[1] = 0 + self.list[1] = "None" + self.dropDown.sel = 1 + for _, item in ipairs(self.itemsMain.list) do + if self.itemsMain:IsItemValidForSlot(item, self.slotName) then + t_insert(self.items, item.id) + t_insert(self.list, data.colorCodes[item.rarity]..item.name) + if item.id == self.selItem then + self.dropDown.sel = #self.list + end + end + end + if not self.selItem or not self.itemsMain.list[self.selItem] or not self.itemsMain:IsItemValidForSlot(self.itemsMain.list[self.selItem], self.slotName) then + self.selItem = 0 + end +end + +function ItemSlotClass:IsMouseOver() + return self.dropDown:IsMouseOver() +end + +function ItemSlotClass:Draw(viewPort) + self.dropDown.x = viewPort.x + self.x + self.dropDown.y = viewPort.y + self.y + DrawString(self.dropDown.x - 2, self.dropDown.y + 2, "RIGHT_X", self.height - 4, "VAR", "^7"..self.label..":") + self.dropDown:Draw() + if self.dropDown:IsMouseOver() then + local ttItem + if self.dropDown.dropped then + if self.dropDown.hoverSel then + ttItem = itemsMain.list[self.items[self.dropDown.hoverSel]] + end + elseif self.selItem and not self.itemsMain.selControl then + ttItem = self.itemsMain.list[self.selItem] + end + if ttItem then + self.itemsMain:AddItemTooltip(ttItem) + main:DrawTooltip(self.dropDown.x, self.dropDown.y, self.width, self.height, viewPort, data.colorCodes[ttItem.rarity], true) + end + end +end + +function ItemSlotClass:OnKeyDown(key) + return self.dropDown:OnKeyDown(key) +end + +function ItemSlotClass:OnKeyUp(key) + return self.dropDown:OnKeyUp(key) +end diff --git a/Classes/PassiveSpec.lua b/Classes/PassiveSpec.lua index cef6194b..8b8acd4d 100644 --- a/Classes/PassiveSpec.lua +++ b/Classes/PassiveSpec.lua @@ -300,16 +300,12 @@ function SpecClass:Undo() t_insert(self.redo, 1, table.remove(self.undo, 1)) self:DecodeURL(table.remove(self.undo, 1)) self:AddUndoState(true) - self.modFlag = true - self.buildFlag = true end end function SpecClass:Redo() if self.redo[1] then - self:DecodeURL(table.remove(self. redo, 1)) + self:DecodeURL(table.remove(self.redo, 1)) self:AddUndoState(true) - self.modFlag = true - self.buildFlag = true end end diff --git a/Classes/PassiveTreeView.lua b/Classes/PassiveTreeView.lua index dfd1d3c6..1dc864c9 100644 --- a/Classes/PassiveTreeView.lua +++ b/Classes/PassiveTreeView.lua @@ -7,10 +7,10 @@ local launch = ... local pairs = pairs local ipairs = ipairs -local t_insert = table.insert local m_min = math.min local m_max = math.max local m_floor = math.floor +local t_insert = table.insert local TreeViewClass = common.NewClass("PassiveTreeView", function(self, main) self.main = main diff --git a/Common.lua b/Common.lua index 71ae77a4..5e4f6849 100644 --- a/Common.lua +++ b/Common.lua @@ -6,12 +6,11 @@ common = { } +-- External libraries common.curl = require("lcurl") common.xml = require("xml") common.json = require("dkjson") common.base64 = require("base64") - --- Basic edit field common.newEditField = require("simplegraphic/editfield") -- Class library diff --git a/Launch.lua b/Launch.lua index 43d8680b..0e3be1f5 100644 --- a/Launch.lua +++ b/Launch.lua @@ -2,7 +2,7 @@ -- Path of Building -- -- Module: Launch --- Program entry point; runs the Main module within a protected environment +-- Program entry point; loads and runs the Main module within a protected environment -- SetWindowTitle("PathOfBuilding") @@ -12,16 +12,12 @@ ConExecute("vid_resizable 3") LoadModule("Common") local launch = { } +SetMainObject(launch) -function launch:LoadMain() +function launch:OnInit() ConPrintf("Loading main script...") - if self.main and self.main.Shutdown then - PCall(self.main.Shutdown, self.main) - end - self.main = nil - collectgarbage("collect") local errMsg - errMsg, self.main = PLoadModule("Modules/main", launch) + errMsg, self.main = PLoadModule("Modules/main", self) if errMsg then self:ShowErrMsg("Error loading main script: %s", errMsg) elseif not self.main then @@ -34,6 +30,84 @@ function launch:LoadMain() end end +function launch:OnExit() + if self.main and self.main.Shutdown then + PCall(self.main.Shutdown, self.main) + end +end + +function launch:OnFrame() + if self.main then + if self.main.OnFrame then + local errMsg = PCall(self.main.OnFrame, self.main) + if errMsg then + self:ShowErrMsg("In 'OnFrame': %s", errMsg) + end + end + end + if self.promptMsg then + local r, g, b = unpack(self.promptCol) + common.drawPopup(r, g, b, "^0%s", self.promptMsg) + end + if self.doReload then + local screenW, screenH = GetScreenSize() + SetDrawColor(0, 0, 0, 0.75) + DrawImage(nil, 0, 0, screenW, screenH) + SetDrawColor(1, 1, 1) + DrawString(0, screenH/2, "CENTER", 24, "FIXED", "Reloading...") + Restart() + end +end + +function launch:OnKeyDown(key, doubleClick) + if key == "F5" then + self.doReload = true + elseif self.promptMsg then + local errMsg, ret = PCall(self.promptFunc, key) + if errMsg then + self:ShowErrMsg("In prompt func: %s", errMsg) + elseif ret then + self.promptMsg = nil + end + else + if self.main and self.main.OnKeyDown then + local errMsg = PCall(self.main.OnKeyDown, self.main, key, doubleClick) + if errMsg then + self:ShowErrMsg("In 'OnKeyDown': %s", errMsg) + end + end + end +end + +function launch:OnKeyUp(key) + if not self.promptMsg then + if self.main and self.main.OnKeyUp then + local errMsg = PCall(self.main.OnKeyUp, self.main, key) + if errMsg then + self:ShowErrMsg("In 'OnKeyUp': %s", errMsg) + end + end + end +end + +function launch:OnChar(key) + if self.promptMsg then + local errMsg, ret = PCall(self.promptFunc, key) + if errMsg then + self:ShowErrMsg("In prompt func: %s", errMsg) + elseif ret then + self.promptMsg = nil + end + else + if self.main and self.main.OnChar then + local errMsg = PCall(self.main.OnChar, self.main, key) + if errMsg then + self:ShowErrMsg("In 'OnChar': %s", errMsg) + end + end + end +end + function launch:ShowPrompt(r, g, b, str, func) if self.promptMsg then return @@ -54,83 +128,3 @@ function launch:ShowErrMsg(fmt, ...) end end) end - -launch:LoadMain() - -SetCallback("OnFrame", function() - if launch.main then - if launch.main.OnFrame then - local errMsg = PCall(launch.main.OnFrame, launch.main) - if errMsg then - launch:ShowErrMsg("In 'OnFrame': %s", errMsg) - end - end - end - if launch.promptMsg then - local r, g, b = unpack(launch.promptCol) - common.drawPopup(r, g, b, "^0%s", launch.promptMsg) - end - if launch.doReload then - local screenW, screenH = GetScreenSize() - SetDrawColor(0, 0, 0, 0.75) - DrawImage(nil, 0, 0, screenW, screenH) - SetDrawColor(1, 1, 1) - DrawString(0, screenH/2, "CENTER", 24, "FIXED", "Reloading...") - Restart() - end -end) - -SetCallback("OnKeyDown", function(key, doubleClick) - if key == "F5" then - launch.doReload = true - elseif launch.promptMsg then - local errMsg, ret = PCall(launch.promptFunc, key) - if errMsg then - launch:ShowErrMsg("In prompt func: %s", errMsg) - elseif ret then - launch.promptMsg = nil - end - else - if launch.main and launch.main.OnKeyDown then - local errMsg = PCall(launch.main.OnKeyDown, launch.main, key, doubleClick) - if errMsg then - launch:ShowErrMsg("In 'OnKeyDown': %s", errMsg) - end - end - end -end) - -SetCallback("OnKeyUp", function(key) - if not launch.promptMsg then - if launch.main and launch.main.OnKeyUp then - local errMsg = PCall(launch.main.OnKeyUp, launch.main, key) - if errMsg then - launch:ShowErrMsg("In 'OnKeyUp': %s", errMsg) - end - end - end -end) - -SetCallback("OnChar", function(key) - if launch.promptMsg then - local errMsg, ret = PCall(launch.promptFunc, key) - if errMsg then - launch:ShowErrMsg("In prompt func: %s", errMsg) - elseif ret then - launch.promptMsg = nil - end - else - if launch.main and launch.main.OnChar then - local errMsg = PCall(launch.main.OnChar, launch.main, key) - if errMsg then - launch:ShowErrMsg("In 'OnChar': %s", errMsg) - end - end - end -end) - -SetCallback("OnExit", function() - if launch.main and launch.main.Shutdown then - PCall(launch.main.Shutdown, launch.main) - end -end) \ No newline at end of file diff --git a/Modules/Build.lua b/Modules/Build.lua index f1d9a808..c0bbcb0d 100644 --- a/Modules/Build.lua +++ b/Modules/Build.lua @@ -3,171 +3,101 @@ -- Module: Build -- Loads and manages the active build. -- -local launch, cfg, main = ... +local launch, main = ... local ipairs = ipairs local t_insert = table.insert local buildMode = { } -buildMode.controls = { } - -t_insert(buildMode.controls, common.New("ButtonControl", 4, 4, 60, 20, "<< Back", function() - main:SetMode("LIST", buildMode.dbFileName) -end)) - -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.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.New("ButtonControl", 4 + 68*3, 4, 60, 20, "Calcs", function() - buildMode.viewMode = "CALCS" -end, function() - return buildMode.viewMode ~= "CALCS" -end)) - -t_insert(buildMode.controls, { - x = 4 + 68*4, - y = 4, - Draw = function(self) - local buildName = buildMode.dbFileName:gsub(".xml","") - local bnw = DrawStringWidth(16, "VAR", buildName) - SetDrawColor(0.5, 0.5, 0.5) - DrawImage(nil, self.x + 91, self.y, bnw + 6, 20) - SetDrawColor(0, 0, 0) - DrawImage(nil, self.x + 92, self.y + 1, bnw + 4, 18) - SetDrawColor(1, 1, 1) - DrawString(self.x, self.y + 2, "LEFT", 16, "VAR", "Current build: "..buildName.." "..((buildMode.calcs.modFlag or buildMode.spec.modFlag or buildMode.items.modFlag) and "(Unsaved)" or "")) - end, -}) - -buildMode.controls.pointDisplay = { - x = 0, - y = 4, - Draw = function(self) - local used, ascUsed = buildMode.spec:CountAllocNodes() - local usedMax = 120 + (buildMode.calcs.output.total_extraPoints or 0) - local ascMax = 6 - local str = string.format("%s%3d / %3d %s%d / %d", used > usedMax and "^1" or "^7", used, usedMax, ascUsed > ascMax and "^1" or "^7", ascUsed, ascMax) - local strW = DrawStringWidth(16, "FIXED", str) + 6 - SetDrawColor(1, 1, 1) - DrawImage(nil, self.x, self.y, strW + 2, 20) - SetDrawColor(0, 0, 0) - DrawImage(nil, self.x + 1, self.y + 1, strW, 18) - SetDrawColor(1, 1, 1) - DrawString(self.x + 4, self.y + 2, "LEFT", 16, "FIXED", str) - end, -} - -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 - buildMode.spec:SelectClass(classId) - buildMode.spec:AddUndoState() - else - launch:ShowPrompt(0, 0, 0, "Changing class to "..val.." will reset your tree.\nThis can be avoided by connecting one of the "..val.." starting nodes to your tree.\n\nPress Y to continue.", function(key) - if key == "y" then - buildMode.spec:SelectClass(classId) - buildMode.spec:AddUndoState() - end - return true - end) - end - end -end, function() - return buildMode.viewMode == "TREE" -end) - -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() -end, function() - return buildMode.viewMode == "TREE" -end) - -buildMode.savers = { - ["Build"] = "", - ["Calcs"] = "calcs", - ["Items"] = "items", - ["Spec"] = "spec", - ["TreeView"] = "treeView", -} - -function buildMode:LoadDB() - local dbXML, errMsg = common.xml.LoadXMLFile(self.dbFileName) - if not dbXML then - launch:ShowErrMsg("^1Error loading '%s': %s", self.dbFileName, errMsg) - return true - elseif dbXML[1].elem ~= "PathOfBuilding" then - launch:ShowErrMsg("^1Error parsing '%s': 'PathOfBuilding' root element missing", self.dbFileName) - return true - end - for _, node in ipairs(dbXML[1]) do - if type(node) == "table" then - local key = self.savers[node.elem] - if key then - local saver = self[key] or self - if saver:Load(node, self.dbFileName) then - return true - end - end - end - end -end -function buildMode:SaveDB() - local dbXML = { elem = "PathOfBuilding" } - for elem, key in pairs(self.savers) do - local saver = self[key] or self - local node = { elem = elem } - saver:Save(node) - t_insert(dbXML, node) - end - local res, errMsg = common.xml.SaveXMLFile(dbXML, self.dbFileName) - if not res then - launch:ShowErrMsg("Error saving '%s': %s", self.dbFileName, errMsg) - return true - end -end - -function buildMode:Load(xml, fileName) - if xml.attrib.viewMode then - self.viewMode = xml.attrib.viewMode - end -end -function buildMode:Save(xml) - xml.attrib = { - viewMode = self.viewMode, - className = self.tree.classes[self.spec.curClassId].name, - ascendClassName = self.spec.curAscendClassId > 0 and self.tree.classes[self.spec.curClassId].classes[tostring(self.spec.curAscendClassId)].name, - level = tostring(self.calcs.input.player_level or 1) - } -end - function buildMode:Init(dbFileName) - self.dbFileName = dbFileName - ConPrintf("Loading '%s'...", dbFileName) - self.abortSave = true - self.items = LoadModule("Modules/Items", launch, cfg, main) + self.items = LoadModule("Modules/Items", launch, main) self.items:Init(self) - self.calcs = LoadModule("Modules/Calcs", launch, cfg, main) + self.calcs = LoadModule("Modules/Calcs", launch, main) self.calcs:Init(self) self.tree = main.tree - self.spec = common.New("PassiveSpec", main.tree) - self.treeView = common.New("PassiveTreeView") + self.spec = common.New("PassiveSpec", self.tree) + self.treeView = common.New("PassiveTreeView", main) + + self.controls = { } + t_insert(self.controls, common.New("ButtonControl", 4, 4, 60, 20, "<< Back", function() + main:SetMode("LIST", self.dbFileName) + end)) + t_insert(self.controls, common.New("ButtonControl", 4 + 68, 4, 60, 20, "Tree", function() + self.viewMode = "TREE" + end, function() + return self.viewMode ~= "TREE" + end)) + t_insert(self.controls, common.New("ButtonControl", 4 + 68*2, 4, 60, 20, "Items", function() + self.viewMode = "ITEMS" + end, function() + return self.viewMode ~= "ITEMS" + end)) + t_insert(self.controls, common.New("ButtonControl", 4 + 68*3, 4, 60, 20, "Calcs", function() + self.viewMode = "CALCS" + end, function() + return self.viewMode ~= "CALCS" + end)) + t_insert(self.controls, { + x = 4 + 68*4, + y = 4, + Draw = function(control) + local buildName = self.dbFileName:gsub(".xml","") + local bnw = DrawStringWidth(16, "VAR", buildName) + SetDrawColor(0.5, 0.5, 0.5) + DrawImage(nil, control.x + 91, control.y, bnw + 6, 20) + SetDrawColor(0, 0, 0) + DrawImage(nil, control.x + 92, control.y + 1, bnw + 4, 18) + SetDrawColor(1, 1, 1) + DrawString(control.x, control.y + 2, "LEFT", 16, "VAR", "Current build: "..buildName.." "..((self.calcs.modFlag or self.spec.modFlag or self.items.modFlag) and "(Unsaved)" or "")) + end, + }) + self.controls.pointDisplay = { + x = 0, + y = 4, + Draw = function(control) + local used, ascUsed = self.spec:CountAllocNodes() + local usedMax = 120 + (self.calcs.output.total_extraPoints or 0) + local ascMax = 6 + local str = string.format("%s%3d / %3d %s%d / %d", used > usedMax and "^1" or "^7", used, usedMax, ascUsed > ascMax and "^1" or "^7", ascUsed, ascMax) + local strW = DrawStringWidth(16, "FIXED", str) + 6 + SetDrawColor(1, 1, 1) + DrawImage(nil, control.x, control.y, strW + 2, 20) + SetDrawColor(0, 0, 0) + DrawImage(nil, control.x + 1, control.y + 1, strW, 18) + SetDrawColor(1, 1, 1) + DrawString(control.x + 4, control.y + 2, "LEFT", 16, "FIXED", str) + end, + } + self.controls.classDrop = common.New("DropDownControl", 0, 4, 100, 20, nil, function(index, val) + local classId = self.tree.classNameMap[val] + if classId ~= self.spec.curClassId then + if self.spec:IsClassConnected(classId) or self.spec:CountAllocNodes() == 0 then + self:SelectClass(classId) + self.spec:AddUndoState() + else + launch:ShowPrompt(0, 0, 0, "Changing class to "..val.." will reset your tree.\nThis can be avoided by connecting one of the "..val.." starting nodes to your tree.\n\nPress Y to continue.", function(key) + if key == "y" then + self.spec:SelectClass(classId) + self.spec:AddUndoState() + end + return true + end) + end + end + end, function() + return self.viewMode == "TREE" + end) + self.controls.ascendDrop = common.New("DropDownControl", 0, 4, 100, 20, nil, function(index, val) + local ascendClassId = self.tree.ascendNameMap[val].ascendClassId + self.spec:SelectAscendClass(ascendClassId) + self.spec:AddUndoState() + end, function() + return self.viewMode == "TREE" + end) - wipeTable(self.controls.classDrop.list) for classId, class in pairs(self.tree.classes) do t_insert(self.controls.classDrop.list, class.name) end @@ -202,6 +132,17 @@ function buildMode:Init(dbFileName) self.viewMode = "TREE" + self.dbFileName = dbFileName + ConPrintf("Loading '%s'...", dbFileName) + + self.savers = { + ["Build"] = self, + ["Calcs"] = self.calcs, + ["Items"] = self.items, + ["Spec"] = self.spec, + ["TreeView"] = self.treeView, + } + if self:LoadDB() then main:SetMode("LIST", dbFileName) return @@ -224,6 +165,9 @@ function buildMode:Shutdown() end self.abortSave = nil + self.savers = nil + self.controls = nil + self.calcs:Shutdown() self.calcs = nil self.items:Shutdown() @@ -254,7 +198,7 @@ function buildMode:OnFrame(inputEvents) self.controls.classDrop:SelByValue(class.name) self.controls.ascendDrop:SelByValue(ascendClass and ascendClass.name or "None") - self.controls.pointDisplay.x = cfg.screenW / 2 + 6 + self.controls.pointDisplay.x = main.screenW / 2 + 6 self.controls.classDrop.x = self.controls.pointDisplay.x + 154 self.controls.ascendDrop.x = self.controls.classDrop.x + self.controls.classDrop.width + 8 @@ -264,39 +208,39 @@ function buildMode:OnFrame(inputEvents) local viewPort = { x = 258, y = 32, - width = cfg.screenW - 258, - height = cfg.screenH - 32 + width = main.screenW - 258, + height = main.screenH - 32 } self.treeView:DrawTree(self, viewPort, inputEvents) elseif self.viewMode == "CALCS" then local viewPort = { x = 0, y = 32, - width = cfg.screenW, - height = cfg.screenH - 32 + width = main.screenW, + height = main.screenH - 32 } self.calcs:DrawGrid(viewPort, inputEvents) elseif self.viewMode == "ITEMS" then local viewPort = { x = 258, y = 32, - width = cfg.screenW - 258, - height = cfg.screenH - 32 + width = main.screenW - 258, + height = main.screenH - 32 } self.items:DrawItems(viewPort, inputEvents) end SetDrawColor(0.2, 0.2, 0.2) - DrawImage(nil, 0, 0, cfg.screenW, 28) + DrawImage(nil, 0, 0, main.screenW, 28) SetDrawColor(0.85, 0.85, 0.85) - DrawImage(nil, 0, 28, cfg.screenW, 4) - DrawImage(nil, cfg.screenW/2 - 2, 0, 4, 28) + DrawImage(nil, 0, 28, main.screenW, 4) + DrawImage(nil, main.screenW/2 - 2, 0, 4, 28) common.controlsDraw(self, viewPort) if self.viewMode ~= "CALCS" then SetDrawColor(0.1, 0.1, 0.1) - DrawImage(nil, 0, 32, 254, cfg.screenH - 32) + DrawImage(nil, 0, 32, 254, main.screenH - 32) SetDrawColor(0.85, 0.85, 0.85) - DrawImage(nil, 254, 32, 4, cfg.screenH - 32) + DrawImage(nil, 254, 32, 4, main.screenH - 32) local y = 36 for index, data in ipairs(self.displayStats) do if data.mod then @@ -312,4 +256,52 @@ function buildMode:OnFrame(inputEvents) end end +function buildMode:LoadDB() + local dbXML, errMsg = common.xml.LoadXMLFile(self.dbFileName) + if not dbXML then + launch:ShowErrMsg("^1Error loading '%s': %s", self.dbFileName, errMsg) + return true + elseif dbXML[1].elem ~= "PathOfBuilding" then + launch:ShowErrMsg("^1Error parsing '%s': 'PathOfBuilding' root element missing", self.dbFileName) + return true + end + for _, node in ipairs(dbXML[1]) do + if type(node) == "table" then + local saver = self.savers[node.elem] + if saver then + if saver:Load(node, self.dbFileName) then + return true + end + end + end + end +end +function buildMode:SaveDB() + local dbXML = { elem = "PathOfBuilding" } + for elem, saver in pairs(self.savers) do + local node = { elem = elem } + saver:Save(node) + t_insert(dbXML, node) + end + local res, errMsg = common.xml.SaveXMLFile(dbXML, self.dbFileName) + if not res then + launch:ShowErrMsg("Error saving '%s': %s", self.dbFileName, errMsg) + return true + end +end + +function buildMode:Load(xml, fileName) + if xml.attrib.viewMode then + self.viewMode = xml.attrib.viewMode + end +end +function buildMode:Save(xml) + xml.attrib = { + viewMode = self.viewMode, + className = self.tree.classes[self.spec.curClassId].name, + ascendClassName = self.spec.curAscendClassId > 0 and self.tree.classes[self.spec.curClassId].classes[tostring(self.spec.curAscendClassId)].name, + level = tostring(self.calcs.input.player_level or 1) + } +end + return buildMode \ No newline at end of file diff --git a/Modules/BuildList.lua b/Modules/BuildList.lua index 51c2d660..c05a38cf 100644 --- a/Modules/BuildList.lua +++ b/Modules/BuildList.lua @@ -3,32 +3,156 @@ -- Module: BuildList -- Displays the list of builds. -- -local launch, cfg, main = ... +local launch, main = ... + +local t_insert = table.insert local vfs = require("vfs") local listMode = { } -listMode.controls = { - common.New("ButtonControl", 2, 2, 60, 20, "New", function() +function listMode:Init(selFileName) + self:BuildList() + self:SelFileByName(selFileName) + + self.controls = { } + t_insert(self.controls, common.New("ButtonControl", 2, 2, 60, 20, "New", function() listMode:New() - end), - common.New("ButtonControl", 66, 2, 60, 20, "Copy", function() + end)) + t_insert(self.controls, common.New("ButtonControl", 66, 2, 60, 20, "Copy", function() listMode:CopySel() end, function() return listMode.sel ~= nil - end), - common.New("ButtonControl", 130, 2, 60, 20, "Rename", function() + end)) + t_insert(self.controls, common.New("ButtonControl", 130, 2, 60, 20, "Rename", function() listMode:RenameSel() end, function() return listMode.sel ~= nil - end), - common.New("ButtonControl", 194, 2, 60, 20, "Delete", function() + end)) + t_insert(self.controls, common.New("ButtonControl", 194, 2, 60, 20, "Delete", function() listMode:DeleteSel() end, function() return listMode.sel ~= nil - end), -} + end)) +end + +function listMode:Shutdown() + if self.edit then + self:EditCancel() + end +end + +function listMode:OnFrame(inputEvents) + for id, event in ipairs(inputEvents) do + if event.type == "KeyDown" then + self:OnKeyDown(event.key, event.doubleClick) + elseif event.type == "KeyUp" then + self:OnKeyUp(event.key) + elseif event.type == "Char" then + self:OnChar(event.key) + end + end + common.controlsDraw(self) + for index, build in ipairs(self.list) do + local y = 4 + index * 20 + if self.sel == index then + SetDrawColor(1, 1, 1) + else + SetDrawColor(0.5, 0.5, 0.5) + end + DrawImage(nil, 0, y, main.screenW, 20) + if self.sel == index then + SetDrawColor(0.33, 0.33, 0.33) + else + SetDrawColor(0, 0, 0) + end + DrawImage(nil, 0, y + 1, main.screenW, 18) + if self.edit == index then + self.editField:Draw(2, y + 2, 16) + else + if self.sel == index then + SetDrawColor(1, 1, 1) + else + SetDrawColor(0.8, 0.8, 0.8) + end + DrawString(4, y + 2, "LEFT", 16, "VAR", build.fileName:gsub(".xml","")) + DrawString(304, y + 2, "LEFT", 16, "VAR", string.format("Level %d %s", build.level, build.ascendClassName or build.className or "?")) + end + end +end + +function listMode:OnKeyDown(key, doubleClick) + if self.edit then + if key == "RETURN" then + self:EditFinish() + elseif key == "ESCAPE" then + self:EditCancel() + else + self.editField:OnKeyDown(key) + end + elseif key == "LEFTBUTTON" then + self.selControl = nil + local cx, cy = GetCursorPos() + for _, control in pairs(self.controls) do + if control.IsMouseOver and control:IsMouseOver() then + control:OnKeyDown(key) + self.selControl = control + return + end + end + self.sel = nil + for index, fileName in ipairs(self.list) do + local y = 4 + index * 20 + if cy >= y and cy < y + 20 then + if doubleClick then + main:SetMode("BUILD", self.list[index].fileName) + else + self.sel = index + end + return + end + end + elseif key == "RETURN" then + if self.sel then + main:SetMode("BUILD", self.list[self.sel].fileName) + end + elseif key == "UP" then + if not self.sel then + self.sel = #self.list + else + self.sel = (self.sel - 2) % #self.list + 1 + end + elseif key == "DOWN" then + if not self.sel then + self.sel = 1 + else + self.sel = self.sel % #self.list + 1 + end + elseif key == "F2" then + if self.sel then + self:RenameSel() + end + elseif key == "DELETE" then + if self.sel then + self:DeleteSel() + end + end +end + +function listMode:OnKeyUp(key) + if self.edit then + self.editField:OnKeyUp(key) + elseif self.selControl then + self.selControl:OnKeyUp(key) + self.selControl = nil + end +end + +function listMode:OnChar(key) + if self.edit then + self.editField:OnChar(key) + end +end function listMode:BuildList() self.list = { } @@ -61,13 +185,23 @@ function listMode:SortList() end end +function listMode:SelFileByName(selFileName) + self.sel = nil + for index, build in ipairs(self.list) do + if build.fileName == selFileName then + self.sel = index + break + end + end +end + function listMode:EditInit(finFunc) self.edit = self.sel self.editFinFunc = finFunc self.editField = common.newEditField(self.list[self.sel].fileName:gsub(".xml$",""), nil, "[%w _+.()]") self.editField.x = 2 self.editField.y = 6 + self.sel * 20 - self.editField.width = cfg.screenW + self.editField.width = main.screenW self.editField.height = 16 end @@ -180,137 +314,4 @@ function listMode:DeleteSel() end) end -function listMode:SelFileByName(selFileName) - self.sel = nil - for index, build in ipairs(self.list) do - if build.fileName == selFileName then - self.sel = index - break - end - end -end - -function listMode:Init(selFileName) - self:BuildList() - self:SelFileByName(selFileName) -end - -function listMode:Shutdown() - if self.edit then - self:EditCancel() - end -end - -function listMode:OnFrame(inputEvents) - for id, event in ipairs(inputEvents) do - if event.type == "KeyDown" then - self:OnKeyDown(event.key, event.doubleClick) - elseif event.type == "KeyUp" then - self:OnKeyUp(event.key) - elseif event.type == "Char" then - self:OnChar(event.key) - end - end - common.controlsDraw(self) - for index, build in ipairs(self.list) do - local y = 4 + index * 20 - if self.sel == index then - SetDrawColor(1, 1, 1) - else - SetDrawColor(0.5, 0.5, 0.5) - end - DrawImage(nil, 0, y, cfg.screenW, 20) - if self.sel == index then - SetDrawColor(0.33, 0.33, 0.33) - else - SetDrawColor(0, 0, 0) - end - DrawImage(nil, 0, y + 1, cfg.screenW, 18) - if self.edit == index then - self.editField:Draw(2, y + 2, 16) - else - if self.sel == index then - SetDrawColor(1, 1, 1) - else - SetDrawColor(0.8, 0.8, 0.8) - end - DrawString(4, y + 2, "LEFT", 16, "VAR", build.fileName:gsub(".xml","")) - DrawString(304, y + 2, "LEFT", 16, "VAR", string.format("Level %d %s", build.level, build.ascendClassName or build.className or "?")) - end - end -end - -function listMode:OnKeyDown(key, doubleClick) - if self.edit then - if key == "RETURN" then - self:EditFinish() - elseif key == "ESCAPE" then - self:EditCancel() - else - self.editField:OnKeyDown(key) - end - elseif key == "LEFTBUTTON" then - self.selControl = nil - local cx, cy = GetCursorPos() - for _, control in pairs(self.controls) do - if control.IsMouseOver and control:IsMouseOver() then - control:OnKeyDown(key) - self.selControl = control - return - end - end - self.sel = nil - for index, fileName in ipairs(self.list) do - local y = 4 + index * 20 - if cy >= y and cy < y + 20 then - if doubleClick then - main:SetMode("BUILD", self.list[index].fileName) - else - self.sel = index - end - return - end - end - elseif key == "RETURN" then - if self.sel then - main:SetMode("BUILD", self.list[self.sel].fileName) - end - elseif key == "UP" then - if not self.sel then - self.sel = #self.list - else - self.sel = (self.sel - 2) % #self.list + 1 - end - elseif key == "DOWN" then - if not self.sel then - self.sel = 1 - else - self.sel = self.sel % #self.list + 1 - end - elseif key == "F2" then - if self.sel then - self:RenameSel() - end - elseif key == "DELETE" then - if self.sel then - self:DeleteSel() - end - end -end - -function listMode:OnKeyUp(key) - if self.edit then - self.editField:OnKeyUp(key) - elseif self.selControl then - self.selControl:OnKeyUp(key) - self.selControl = nil - end -end - -function listMode:OnChar(key) - if self.edit then - self.editField:OnChar(key) - end -end - return listMode \ No newline at end of file diff --git a/Modules/Calcs.lua b/Modules/Calcs.lua index 7b5c8727..e27be1fd 100644 --- a/Modules/Calcs.lua +++ b/Modules/Calcs.lua @@ -1,492 +1,64 @@ -- Path of Building -- -- Module: Calcs --- Calculations view for the active build +-- Calculations breakdown view for the active build -- -local launch, cfg, main = ... +local launch, main = ... -cfg.gridHeight = 18 -cfg.defGridWidth = 50 -cfg.defBorderCol = { 0.1, 0.1, 0.1 } -cfg.defCellCol = { 0, 0, 0 } - -local pairs = pairs -local t_insert = table.insert +local ipairs = ipairs local m_max = math.max local m_floor = math.floor +local t_insert = table.insert +local t_remove = table.remove -local function alignCellText(e) - if e.align == "RIGHT" then - return cfg.screenW - (e.grid.offX + e.x + e.grid[e.gx].width - 2) - elseif e.align == "CENTER" then - return - cfg.screenW / 2 + e.grid.offX + e.x + e.grid[e.gx].width / 2 - else - return e.grid.offX + e.x + 2 - end -end - -local function formatCellText(fn, val) - if fn then - local errMsg, text = PCall(fn, val) - if errMsg then - launch:ShowErrMsg("Error formatting cell: %s", errMsg) - return "" - else - return text - end - else - return tostring(val) - end -end - -local elem = {} - -elem.input = {} -elem.input.__index = elem.input -elem.input.borderCol = { 0.9, 0.9, 0.9 } -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.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 - end - end) - self.dropDown.sel = 1 - end -end -function elem.input:Draw() - local grid = self.grid - if self.format == "check" then - DrawString(alignCellText(self), grid.offY + self.y + 2, self.align, cfg.gridHeight - 4, "FIXED", grid.input[self.name] and "^x33FF33Yes" or "^xFF3333No") - elseif grid.focus == self then - if self.edit then - self.edit:Draw() - else - self.dropDown:Draw() - end - elseif grid.input[self.name] then - DrawString(alignCellText(self), grid.offY + self.y + 2, self.align, cfg.gridHeight - 4, "VAR", "^7"..formatCellText(self.formatFunc, grid.input[self.name])) - end -end -function elem.input:OnKeyDown(key, doubleClick) - local grid = self.grid - if grid.focus == self then - if key == "RETURN" or key == "TAB" then - if self.edit then - local newVal = #self.edit.buf and self.edit.buf or nil - if self.format == "number" then - newVal = tonumber((newVal:gsub(",",""):gsub("%*","e"))) - end - if newVal ~= grid.input[self.name] then - grid.input[self.name] = newVal - grid.changeFlag = true - end - end - grid:SetFocus() - grid:MoveSel(key == "TAB" and "RIGHT" or "DOWN", true) - elseif self.edit then - self.edit:OnKeyDown(key) - elseif self.dropDown then - if self.dropDown:OnKeyDown(key) then - grid:SetFocus() - end - end - elseif key == "RIGHTBUTTON" or (key == "LEFTBUTTON" and doubleClick) then - if self.format == "check" then - grid.input[self.name] = not grid.input[self.name] - grid.changeFlag = true - elseif self.format == "choice" then - grid:SetFocus(self) - else - grid:SetFocus(self) - self.edit:SetText(grid.input[self.name] or "") - end - elseif key == "WHEELUP" then - if self.format == "number" then - grid.input[self.name] = (grid.input[self.name] or 0) + 1 - grid.changeFlag = true - end - elseif key == "WHEELDOWN" then - if self.format == "number" then - grid.input[self.name] = (grid.input[self.name] or 0) - 1 - grid.changeFlag = true - end - elseif self.edit then - if key == "c" and IsKeyDown("CTRL") then - if grid.input[self.name] then - Copy(tostring(grid.input[self.name])) - end - elseif key == "v" and IsKeyDown("CTRL") then - local newVal = Paste() - if newVal then - if self.format == "number" then - newVal = tonumber(newVal) - end - if newVal ~= grid.input[self.name] then - grid.input[self.name] = newVal - grid.changeFlag = true - end - end - end - end -end -function elem.input:OnKeyUp(key) - local grid = self.grid - if grid.focus == self then - if self.dropDown then - if self.dropDown:OnKeyUp(key) then - grid:SetFocus() - end - else - self.edit:OnKeyUp(key) - end - end -end -function elem.input:OnChar(key) - local grid = self.grid - if self.format == "check" then - if key == " " then - grid.input[self.name] = not grid.input[self.name] - grid.changeFlag = true - end - return - elseif self.format == "choice" then - return - end - if key == "\r" then - return - elseif not grid.focus and key == "\b" then - grid.input[self.name] = nil - grid.changeFlag = true - return - elseif key:match("%c") then - return - end - if not grid.focus then - if self.format == "number" and key == "+" then - grid.input[self.name] = (grid.input[self.name] or 0) + 1 - grid.changeFlag = true - return - end - grid:SetFocus(self) - end - self.edit:OnChar(key) -end -function elem.input:OnFocusGained() - local grid = self.grid - if self.format == "choice" then - self.dropDown.x = grid.offX + self.x - self.dropDown.y = grid.offY + self.y - self.dropDown.width = grid:GetElemWidth(self) - self.dropDown.height = cfg.gridHeight - self.dropDown:SelByValue(grid.input[self.name]) - self.dropDown:OnKeyDown("LEFTBUTTON") - else - local fmtFilter = { number = "[-%d%.e,*]", string = "." } - self.edit = common.newEditField(nil, nil, fmtFilter[self.format]) - self.edit.x = grid.offX + self.x + 2 - self.edit.y = grid.offY + self.y + 2 - self.edit.width = grid:GetElemWidth(self) - self.edit.height = cfg.gridHeight - 4 - end -end -function elem.input:OnFocusLost() - self.edit = nil - if self.dropDown then - self.dropDown.dropped = false - end -end - -elem.output = {} -elem.output.__index = elem.output -elem.output.borderCol = { 0.7, 0.7, 0.7 } -function elem.output:Draw() - local grid = self.grid - if grid.output[self.name] then - DrawString(alignCellText(self), grid.offY + self.y + 2, self.align, cfg.gridHeight - 4, self.font or "VAR", "^7"..formatCellText(self.formatFunc, grid.output[self.name])) - end -end -function elem.output:OnKeyDown(key) - local grid = self.grid - if key == "c" and IsKeyDown("CTRL") and grid.output[self.name] then - Copy(tostring(grid.output[self.name])) - end -end - -elem.label = {} -elem.label.__index = elem.label -function elem.label:Draw() - local grid = self.grid - DrawString(alignCellText(self), grid.offY + self.y + 2, self.align, cfg.gridHeight - 4, "VAR BOLD", "^xD0D5D0"..self.text) -end - -local grid = { } -function grid:Clear() - for gx in ipairs(self) do - self[gx] = nil - end - self.width = 1 - self.height = 1 - self[1] = { width = cfg.defGridWidth, x = 0 } - self:CalcCoords() -end -function grid:SetSize(w, h) - if w < self.width then - for gx = w + 1, self.width do - self[gx] = nil - end - end - self.width = w - self.height = h - for gx = 1, w do - self[gx] = self[gx] or { width = cfg.defGridWidth } - end - self:CalcCoords() -end -function grid:CalcCoords() - local x = 0 - for gx = 1, self.width do - self[gx].x = x - for gy = 1, self.height do - if self[gx][gy] and self[gx][gy].gx == gx then - self[gx][gy].x = x - end - end - x = x + self[gx].width - end - self.realWidth = x - self.realHeight = self.height * cfg.gridHeight -end -function grid:CheckSize() - local mx, my = 1, 1 - for gx = 1, self.width do - for gy = 1, self.height do - if self[gx][gy] then - mx = math.max(mx, gx + self[gx][gy].width - 1) - my = math.max(my, gy) - end - end - end - grid:SetSize(mx, my) -end -function grid:SetColWidth(gx, sz) - if self[gx] then - self[gx].width = sz - end - self:CalcCoords() -end -function grid:GetElem(gx, gy) - return self[gx] and self[gx][gy] -end -function grid:SetElem(gx, gy, e) - if e then - e.grid = self - e.gx = gx - e.gy = gy - e.width = e.width or 1 - if gx + e.width - 1 > self.width then - grid:SetSize(gx + e.width - 1, self.height) - end - if gy > self.height then - grid:SetSize(self.width, gy) - end - for i = 1, e.width do - grid[gx + i - 1][gy] = e - end - e.x = grid[gx].x - e.y = (gy - 1) * cfg.gridHeight - if elem[e.type] then - setmetatable(e, elem[e.type]) - end - if e.Init then - e:Init() - end - elseif self:GetElem(gx, gy) then - self[gx][gy] = nil - self:CheckSize() - end -end -function grid:GetElemWidth(e) - local width = 0 - for gx = e.gx, e.gx + e.width - 1 do - width = width + self[gx].width - end - return width -end -function grid:SetFocus(e) - if self.focus and self.focus.OnFocusLost then - self.focus:OnFocusLost() - end - self.focus = e - if self.focus and self.focus.OnFocusGained then - self.focus:OnFocusGained() - end -end -function grid:MoveSel(dir, force) - if not self.sel or not self.sel.gx then - return - end - local selX, selY = self.sel.gx, self.sel.gy - local s, e, i - if dir == "LEFT" or dir == "RIGHT" then - if dir == "LEFT" then - s, e, i = selX - 1, 1, -1 - else - s, e, i = selX + 1, self.width, 1 - end - for gx = s, e, i do - if self[gx][selY] then - self.sel = self[gx][selY] - return - end - end - else - if dir == "UP" then - s, e, i = selY - 1, 1, -1 - else - s, e, i = selY + 1, self.height, 1 - end - for gy = s, e, i do - if self[selX][gy] then - self.sel = self[selX][gy] - return - end - end - end - if force then - self.sel = nil - end -end -function grid:Draw() - local x = self.offX - local h = cfg.gridHeight - for gx = 1, self.width do - local y = self.offY - local w = self[gx].width - for gy = 1, self.height do - local e = self[gx][gy] - if not e or e.gx == gx then - local ew = w - if e and e.width and e.width > 1 then - for i = 1, e.width - 1 do - ew = ew + self[gx + i].width - end - end - SetDrawColor(unpack(e and e.borderCol or cfg.defBorderCol)) - DrawImage(nil, x, y, ew, h) - SetDrawColor(unpack(e and e.cellCol or cfg.defCellCol)) - DrawImage(nil, x + 1, y + 1, ew - 2, h - 2) - end - y = y + cfg.gridHeight - end - x = x + self[gx].width - end - for gx = 1, self.width do - for gy = 1, self.height do - local e = self[gx][gy] - if e and e.gx == gx and e.Draw and e ~= self.focus then - e:Draw() - end - end - end - if self.focus then - self.focus:Draw() - end - if self.sel and self.sel.gx then - local selX, selY = self.sel.gx, self.sel.gy - SetDrawColor(1, 1, 1) - local x, y = self.sel.x + self.offX, self.sel.y + self.offY - local w, h = self:GetElemWidth(self.sel), cfg.gridHeight - DrawImage(nil, x - 2, y - 2, w + 4, 4) - DrawImage(nil, x - 2, y + h - 2, w + 4, 4) - DrawImage(nil, x - 2, y - 2, 4, h + 4) - DrawImage(nil, x + w - 2, y - 2, 4, h + 4) - end -end -function grid:OnKeyDown(key, doubleClick) - if self.focus then - if self.focus.OnKeyDown then - self.focus:OnKeyDown(key, doubleClick) - end - elseif key == "LEFTBUTTON" or key == "RIGHTBUTTON" then - self.sel = nil - local cx, cy = GetCursorPos() - local gcx, gcy = cx - self.offX, cy - self.offY - local gy = math.floor(gcy / cfg.gridHeight) + 1 - if gcx >= 0 and gcy >= 0 and gcx < self.realWidth and gcy < self.realHeight then - local x = 0 - for gx = 1, self.width do - if gcx >= x and gcx < x + self[gx].width then - if self[gx][gy] then - local e = self[gx][gy] - self.sel = e - if e.OnKeyDown then - e:OnKeyDown(key, doubleClick) - end - end - return - end - x = x + self[gx].width - end - end - elseif key == "LEFT" or key == "RIGHT" or key == "UP" or key == "DOWN" then - self:MoveSel(key) - elseif self.sel then - if self.sel.OnKeyDown then - self.sel:OnKeyDown(key) - end - end -end -function grid:OnKeyUp(key) - if self.focus then - if key == "ESCAPE" then - self:SetFocus() - elseif self.focus.OnKeyUp then - self.focus:OnKeyUp(key) - end - elseif key == "ESCAPE" then - self.sel = nil - elseif self.sel then - if self.sel.OnKeyUp then - self.sel:OnKeyUp(key) - end - end -end -function grid:OnChar(key) - if self.focus then - if self.focus.OnChar then - self.focus:OnChar(key) - end - elseif self.sel then - if self.sel.OnChar then - self.sel:OnChar(key) - end - end -end +LoadModule("Classes/Grid", launch) local calcs = { } function calcs:Init(build) self.build = build + self.input = { } self.output = { } - grid.input = self.input - grid.output = self.output - grid:Clear() + self.grid = common.New("Grid", self.input, self.output) + self.undo = { } self.redo = { } + self:LoadControl() end + function calcs:Shutdown() - grid:SetFocus() - grid:Clear() + self.grid:Clear() + self.grid = nil self.redo = nil self.undo = nil end +function calcs:DrawGrid(viewPort, inputEvents) + self.grid.offX = viewPort.x + m_floor((viewPort.width - self.grid.realWidth) / 2) + self.grid.offY = viewPort.y + 2 + for id, event in ipairs(inputEvents) do + if event.type == "KeyDown" then + if event.key == "r" and IsKeyDown("CTRL") then + self:LoadControl() + self.buildFlag = true + elseif event.key == "z" and IsKeyDown("CTRL") then + self:Undo() + elseif event.key == "y" and IsKeyDown("CTRL") then + self:Redo() + else + self.grid:OnKeyDown(event.key, event.doubleClick) + end + elseif event.type == "KeyUp" then + self.grid:OnKeyUp(event.key) + elseif event.type == "Char" then + self.grid:OnChar(event.key) + end + end + self.grid:Draw() +end + function calcs:Load(xml, dbFileName) for _, node in ipairs(xml) do if type(node) == "table" then @@ -508,9 +80,11 @@ function calcs:Load(xml, dbFileName) end end end - self:AddUndoState() + self.undo = { copyTable(self.input) } + self.redo = { } self.buildFlag = true end + function calcs:Save(xml) self.modFlag = false for k, v in pairs(self.input) do @@ -526,15 +100,10 @@ function calcs:Save(xml) end end -function calcs:AddUndoState() - t_insert(self.undo, 1, copyTable(self.input)) - self.undo[102] = nil -end - function calcs:LoadControl() - grid:Clear() + self.grid:Clear() local errMsg - errMsg, self.control = PLoadModule("Modules/CalcsControl", grid) + errMsg, self.control = PLoadModule("Modules/CalcsControl", self.grid) if errMsg then launch:ShowErrMsg("Error loading control script: %s", errMsg) elseif not self.control then @@ -543,15 +112,9 @@ function calcs:LoadControl() end function calcs:RunControl() - if grid.changeFlag then - grid.changeFlag = false - self.modFlag = true - self.buildFlag = true + if self.grid.changeFlag then + self.grid.changeFlag = false self:AddUndoState() - if not self.noClearRedo then - self.redo = {} - end - self.noClearRedo = false end if self.buildFlag or self.build.spec.buildFlag or self.build.items.buildFlag then self.buildFlag = false @@ -559,7 +122,7 @@ function calcs:RunControl() self.build.items.buildFlag = false wipeTable(self.output) if self.control and self.control.buildOutput then - local errMsg, otherMsg = PCall(self.control.buildOutput, grid.input, grid.output, self.build) + local errMsg, otherMsg = PCall(self.control.buildOutput, self.input, self.output, self.build) if errMsg then launch:ShowErrMsg("Error building output: %s", errMsg) elseif otherMsg then @@ -606,7 +169,7 @@ end function calcs:GetNodeCalculator() if self.control and self.control.getNodeCalculator then - local errMsg, calcFunc, calcBase = PCall(self.control.getNodeCalculator, grid.input, self.build) + local errMsg, calcFunc, calcBase = PCall(self.control.getNodeCalculator, self.input, self.build) if errMsg then launch:ShowErrMsg("Error creating calculator: %s", errMsg) elseif otherMsg then @@ -618,7 +181,7 @@ end function calcs:GetItemCalculator() if self.control and self.control.getItemCalculator then - local errMsg, calcFunc, calcBase = PCall(self.control.getItemCalculator, grid.input, self.build) + local errMsg, calcFunc, calcBase = PCall(self.control.getItemCalculator, self.input, self.build) if errMsg then launch:ShowErrMsg("Error creating calculator: %s", errMsg) elseif otherMsg then @@ -628,43 +191,35 @@ function calcs:GetItemCalculator() end end -function calcs:DrawGrid(viewPort, inputEvents) - grid.offX = viewPort.x + m_floor((viewPort.width - grid.realWidth) / 2) - grid.offY = viewPort.y + 2 - for id, event in ipairs(inputEvents) do - if event.type == "KeyDown" then - if event.key == "r" and IsKeyDown("CTRL") then - self:LoadControl() - self.buildFlag = true - elseif event.key == "z" and IsKeyDown("CTRL") then - if self.undo[2] then - t_insert(self.redo, 1, table.remove(self.undo, 1)) - wipeTable(self.input) - for k, v in pairs(table.remove(self.undo, 1)) do - self.input[k] = v - end - grid.changeFlag = true - self.noClearRedo = true - end - elseif event.key == "y" and IsKeyDown("CTRL") then - if self.redo[1] then - wipeTable(self.input) - for k, v in pairs(table.remove(self.redo, 1)) do - self.input[k] = v - end - grid.changeFlag = true - self.noClearRedo = true - end - else - grid:OnKeyDown(event.key, event.doubleClick) - end - elseif event.type == "KeyUp" then - grid:OnKeyUp(event.key) - elseif event.type == "Char" then - grid:OnChar(event.key) - end +function calcs:AddUndoState(noClearRedo) + t_insert(self.undo, 1, copyTable(self.input)) + self.undo[102] = nil + self.modFlag = true + self.buildFlag = true + if not noClearRedo then + self.redo = {} + end +end + +function calcs:Undo() + if self.undo[2] then + t_insert(self.redo, 1, t_remove(self.undo, 1)) + wipeTable(self.input) + for k, v in pairs(t_remove(self.undo, 1)) do + self.input[k] = v + end + self:AddUndoState(true) + end +end + +function calcs:Redo() + if self.redo[1] then + wipeTable(self.input) + for k, v in pairs(t_remove(self.redo, 1)) do + self.input[k] = v + end + self:AddUndoState(true) end - grid:Draw() end return calcs \ No newline at end of file diff --git a/Modules/CalcsControl.lua b/Modules/CalcsControl.lua index 40f978a9..f91897a8 100644 --- a/Modules/CalcsControl.lua +++ b/Modules/CalcsControl.lua @@ -1043,8 +1043,9 @@ local function calcPrimary(env, output) -- Calculate skill duration if startWatch(env, "duration") then local durationBase = getMiscVal(modDB, "skill", "durationBase", 0) + output.total_durationMod = (1 + sumMods(modDB, false, "durationInc") / 100) * sumMods(modDB, true, "durationMore") if durationBase > 0 then - output.total_duration = durationBase * (1 + sumMods(modDB, false, "durationInc") / 100) * sumMods(modDB, true, "durationMore") + output.total_duration = durationBase * output.total_durationMod end endWatch(env, "duration") end @@ -1094,26 +1095,23 @@ local function calcPrimary(env, output) -- Calculate bleeding chance and damage if startWatch(env, "bleed", "physical", "dps_crit") then output.bleed_chance = m_min(100, sumMods(modDB, false, "bleedChance")) / 100 - if output.total_physicalAvg > 0 then - env.skillFlags.canBleed = true - if output.bleed_chance > 0 then - env.skillFlags.dot = true - env.skillFlags.bleed = true - env.skillFlags.duration = true - buildSpaceTable(modDB, { - dot = true, - degen = true, - bleed = true, - projectile = env.skillSpaceFlags.projectile, - aoe = env.skillSpaceFlags.aoe, - totem = env.skillSpaceFlags.totem, - trap = env.skillSpaceFlags.trap, - mine = env.skillSpaceFlags.mine, - }) - local baseVal = output.total_physicalAvg * output.total_critEffect * 0.1 - output.bleed_dps = baseVal * (1 + sumMods(modDB, false, "damageInc", "physicalInc") / 100) * sumMods(modDB, true, "damageMore", "physicalMore") - output.bleed_duration = 5 * (1 + sumMods(modDB, false, "durationInc") / 100) * sumMods(modDB, true, "durationMore") - end + if output.bleed_chance > 0 and output.total_physicalAvg > 0 then + env.skillFlags.dot = true + env.skillFlags.bleed = true + env.skillFlags.duration = true + buildSpaceTable(modDB, { + dot = true, + degen = true, + bleed = true, + projectile = env.skillSpaceFlags.projectile, + aoe = env.skillSpaceFlags.aoe, + totem = env.skillSpaceFlags.totem, + trap = env.skillSpaceFlags.trap, + mine = env.skillSpaceFlags.mine, + }) + local baseVal = output.total_physicalAvg * output.total_critEffect * 0.1 + output.bleed_dps = baseVal * (1 + sumMods(modDB, false, "damageInc", "physicalInc") / 100) * sumMods(modDB, true, "damageMore", "physicalMore") + output.bleed_duration = 5 * (1 + sumMods(modDB, false, "durationInc") / 100) * sumMods(modDB, true, "durationMore") end endWatch(env, "bleed") end @@ -1121,26 +1119,23 @@ local function calcPrimary(env, output) -- Calculate poison chance and damage if startWatch(env, "poison", "physical", "chaos", "dps_crit") then output.poison_chance = m_min(100, sumMods(modDB, false, "poisonChance")) / 100 - if output.total_physicalAvg > 0 or output.total_chaosAvg > 0 then - env.skillFlags.canPoison = true - if output.poison_chance > 0 then - env.skillFlags.dot = true - env.skillFlags.poison = true - env.skillFlags.duration = true - buildSpaceTable(modDB, { - dot = true, - degen = true, - poison = true, - projectile = env.skillSpaceFlags.projectile, - aoe = env.skillSpaceFlags.aoe, - totem = env.skillSpaceFlags.totem, - trap = env.skillSpaceFlags.trap, - mine = env.skillSpaceFlags.mine, - }) - local baseVal = (output.total_physicalAvg + output.total_chaosAvg) * output.total_critEffect * 0.1 - output.poison_dps = baseVal * (1 + sumMods(modDB, false, "damageInc", "chaosInc") / 100) * sumMods(modDB, true, "damageMore", "chaosMore") - output.poison_duration = 2 * (1 + sumMods(modDB, false, "durationInc") / 100) * sumMods(modDB, true, "durationMore") - end + if output.poison_chance > 0 and (output.total_physicalAvg > 0 or output.total_chaosAvg > 0) then + env.skillFlags.dot = true + env.skillFlags.poison = true + env.skillFlags.duration = true + buildSpaceTable(modDB, { + dot = true, + degen = true, + poison = true, + projectile = env.skillSpaceFlags.projectile, + aoe = env.skillSpaceFlags.aoe, + totem = env.skillSpaceFlags.totem, + trap = env.skillSpaceFlags.trap, + mine = env.skillSpaceFlags.mine, + }) + local baseVal = (output.total_physicalAvg + output.total_chaosAvg) * output.total_critEffect * 0.1 + output.poison_dps = baseVal * (1 + sumMods(modDB, false, "damageInc", "chaosInc") / 100) * sumMods(modDB, true, "damageMore", "chaosMore") + output.poison_duration = 2 * (1 + sumMods(modDB, false, "durationInc") / 100) * sumMods(modDB, true, "durationMore") end endWatch(env, "poison") end @@ -1148,28 +1143,41 @@ local function calcPrimary(env, output) -- Calculate ignite chance and damage if startWatch(env, "ignite", "fire", "dps_crit") then output.ignite_chance = m_min(100, sumMods(modDB, false, "igniteChance")) / 100 - if output.total_fireAvg > 0 then - env.skillFlags.canIgnite = true - if output.ignite_chance > 0 then - env.skillFlags.dot = true - env.skillFlags.ignite = true - buildSpaceTable(modDB, { - dot = true, - degen = true, - ignite = true, - projectile = env.skillSpaceFlags.projectile, - aoe = env.skillSpaceFlags.aoe, - totem = env.skillSpaceFlags.totem, - trap = env.skillSpaceFlags.trap, - mine = env.skillSpaceFlags.mine, - }) - local baseVal = output.total_fireAvg * output.total_critEffect * 0.2 - output.ignite_dps = baseVal * (1 + sumMods(modDB, false, "damageInc", "fireInc", "elemInc") / 100) * sumMods(modDB, true, "damageMore", "fireMore", "elemMore") - output.ignite_duration = 4 * (1 + getMiscVal(modDB, "ignite", "durationInc", 0) / 100) - end + if output.ignite_chance > 0 and output.total_fireAvg > 0 then + env.skillFlags.dot = true + env.skillFlags.ignite = true + buildSpaceTable(modDB, { + dot = true, + degen = true, + ignite = true, + projectile = env.skillSpaceFlags.projectile, + aoe = env.skillSpaceFlags.aoe, + totem = env.skillSpaceFlags.totem, + trap = env.skillSpaceFlags.trap, + mine = env.skillSpaceFlags.mine, + }) + local baseVal = output.total_fireAvg * output.total_critEffect * 0.2 + output.ignite_dps = baseVal * (1 + sumMods(modDB, false, "damageInc", "fireInc", "elemInc") / 100) * sumMods(modDB, true, "damageMore", "fireMore", "elemMore") + output.ignite_duration = 4 * (1 + getMiscVal(modDB, "ignite", "durationInc", 0) / 100) end endWatch(env, "ignite") end + + -- Calculate shock and freeze chance + duration modifier + if startWatch(env, "shock", "lightning") then + output.shock_chance = m_min(100, sumMods(modDB, false, "shockChance")) / 100 + if output.shock_chance > 0 and output.total_lightningAvg > 0 then + env.skillFlags.shock = true + output.shock_durationMod = 1 + getMiscVal(modDB, "shock", "durationInc", 0) / 100 + end + end + if startWatch(env, "freeze", "cold") then + output.freeze_chance = m_min(100, sumMods(modDB, false, "freezeChance")) / 100 + if output.freeze_chance > 0 and output.total_coldAvg > 0 then + env.skillFlags.freeze = true + output.freeze_durationMod = 1 + getMiscVal(modDB, "freeze", "durationInc", 0) / 100 + end + end end local control = { } diff --git a/Modules/CalcsView.lua b/Modules/CalcsView.lua index 6162646b..60b391cc 100644 --- a/Modules/CalcsView.lua +++ b/Modules/CalcsView.lua @@ -1,7 +1,7 @@ -- Path of Building -- -- Module: CalcsView --- Configures the grid display in the calculations view +-- Configures the display grid in the calculations breakdown view -- local grid = ... @@ -429,6 +429,7 @@ columns[7] = { }, { flag = "duration", { "output", "Spec Duration %:", "spec_durationInc" }, + { "output", "Skill Duration Mod:", "total_durationMod", formatPercent }, { "output", "Skill Duration:", "total_duration", formatSec }, }, { flag = "trap", @@ -439,36 +440,39 @@ columns[7] = { { "output", "Gear DoT Dmg %:", fieldNames("gear_dot", "Inc", "pfa") }, { "output", "DoT:", fieldNames("total", "Dot", "plcfh"), getFormatRound(1) }, }, { - flag = "canBleed", + flag = "bleed", { "output", "Spec Bleed Chance %:", "spec_bleedChance" }, { "output", "Gear Bleed Chance %:", "gear_bleedChance" }, - { "input", "Other Bleed Chance %:", "other_bleedChance" }, - }, { - flag = "bleed", { "output", "Bleed Chance:", "bleed_chance", formatPercent }, { "output", "Bleed DPS:", "bleed_dps", getFormatRound(1) }, { "output", "Bleed Duration:", "bleed_duration", formatSec }, }, { - flag = "canPoison", + flag = "poison", { "output", "Spec Poison Chance %:", "spec_poisonChance" }, { "output", "Gear Poison Chance %:", "gear_poisonChance" }, - { "input", "Other Poison Chance %:", "other_poisonChance" }, - }, { - flag = "poison", { "output", "Spec Poison Dmg %:", "spec_poison_damageInc" }, { "output", "Poison Chance:", "poison_chance", formatPercent }, { "output", "Poison DPS:", "poison_dps", getFormatRound(1) }, { "output", "Poison Duration:", "poison_duration", formatSec }, }, { - flag = "canIgnite", + flag = "ignite", { "output", "Spec Ignite Chance %:", "spec_igniteChance" }, { "output", "Gear Ignite Chance %:", "gear_igniteChance" }, - { "input", "Other Ignite Chance %:", "other_igniteChance" }, - }, { - flag = "ignite", { "output", "Ignite Chance:", "ignite_chance", formatPercent }, { "output", "Ignite DPS:", "ignite_dps", getFormatRound(1) }, { "output", "Ignite Duration:", "ignite_duration", formatSec }, + }, { + flag = "shock", + { "output", "Spec Shock Chance %:", "spec_shockChance" }, + { "output", "Gear Shock Chance %:", "gear_shockChance" }, + { "output", "Shock Chance:", "shock_chance", formatPercent }, + { "output", "Shock Duration Mod:", "shock_durationMod", formatPercent }, + }, { + flag = "freeze", + { "output", "Spec Freeze Chance %:", "spec_freezeChance" }, + { "output", "Gear Freeze Chance %:", "gear_freezeChance" }, + { "output", "Freeze Chance:", "freeze_chance", formatPercent }, + { "output", "Freeze Duration Mod:", "freeze_durationMod", formatPercent }, } } diff --git a/Modules/Items.lua b/Modules/Items.lua index e0e58b55..cfea448e 100644 --- a/Modules/Items.lua +++ b/Modules/Items.lua @@ -3,113 +3,139 @@ -- Module: Items -- Items view for the active build -- -local launch, cfg, main = ... +local launch, main = ... local t_insert = table.insert local m_floor = math.floor local s_format = string.format -local items = { } - -local function applyRange(line, range) - return line:gsub("%((%d+)%-(%d+) to (%d+)%-(%d+)%)", function(minMin, maxMin, minMax, maxMax) return string.format("%d-%d", tonumber(minMin) + range * (tonumber(minMax) - tonumber(minMin)), tonumber(maxMin) + range * (tonumber(maxMax) - tonumber(maxMin))) end) - :gsub("%((%d+) to (%d+)%)", function(min, max) return tostring(tonumber(min) + range * (tonumber(max) - tonumber(min))) end) -end - -items.slots = { } -items.controls = { } - -items.controls.addDisplayItem = common.New("ButtonControl", 0, 0, 60, 20, "Add", function() - items:AddDisplayItem() -end) - -local function mkItemSlot(x, y, slotName, slotLabel) - local slot = { } - slot.items = { } - slot.list = { } - slot.x = x - slot.y = y - slot.width = 320 - slot.height = 20 - slot.slotName = slotName - slot.label = slotLabel or slotName - 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() - items.buildFlag = true - items.modFlag = true - end - end) - function slot:Populate() - wipeTable(self.items) - wipeTable(self.list) - self.items[1] = 0 - self.list[1] = "None" - self.dropDown.sel = 1 - for _, item in ipairs(items.list) do - if items:IsItemValidForSlot(item, slotName) then - t_insert(self.items, item.id) - t_insert(self.list, data.colorCodes[item.rarity]..item.name) - if item.id == self.selItem then - self.dropDown.sel = #self.list - end - end - end - if not self.selItem or not items.list[self.selItem] or not items:IsItemValidForSlot(items.list[self.selItem], slotName) then - self.selItem = 0 - end - end - function slot:Draw(viewPort) - self.dropDown.x = viewPort.x + self.x - self.dropDown.y = viewPort.y + self.y - DrawString(self.dropDown.x - 2, self.dropDown.y + 2, "RIGHT_X", self.height - 4, "VAR", "^7"..slot.label..":") - self.dropDown:Draw() - if self.dropDown:IsMouseOver() then - local ttItem - if self.dropDown.dropped then - if self.dropDown.hoverSel then - ttItem = items.list[self.items[self.dropDown.hoverSel]] - end - elseif self.selItem and not items.selControl then - ttItem = items.list[self.selItem] - end - if ttItem then - items:AddItemTooltip(ttItem) - main:DrawTooltip(self.dropDown.x, self.dropDown.y, self.width, self.height, viewPort, data.colorCodes[ttItem.rarity], true) - end - end - end - function slot:IsMouseOver() - return self.dropDown:IsMouseOver() - end - function slot:OnKeyDown(key) - return self.dropDown:OnKeyDown(key) - end - function slot:OnKeyUp(key) - return self.dropDown:OnKeyUp(key) - end - items.slots[slotName] = slot - return slot -end +LoadModule("Classes/ItemSlot", launch, main) local baseSlots = { "Helmet", "Body Armour", "Gloves", "Boots", "Amulet", "Ring 1", "Ring 2", "Belt", "Weapon 1", "Weapon 2" } -for index, slotName in pairs(baseSlots) do - t_insert(items.controls, mkItemSlot(400, (index - 1) * 20, slotName)) + +local items = { } + +function items:Init(build) + self.build = build + + self.list = { } + self.orderList = { } + + self.slots = { } + + self.controls = { } + self.controls.addDisplayItem = common.New("ButtonControl", 0, 0, 60, 20, "Add", function() + self:AddDisplayItem() + end) + for index, slotName in pairs(baseSlots) do + t_insert(self.controls, common.New("ItemSlot", self, 400, (index - 1) * 20, slotName)) + end + + self.sockets = { } + for _, node in pairs(main.tree.nodes) do + if node.type == "socket" then + self.sockets[node.id] = common.New("ItemSlot", self, 400, 0, "Jewel "..node.id, "Socket") + end + end end -function items:IsItemValidForSlot(item, slotName) - if item.type == slotName:gsub(" %d+","") then - return true - elseif slotName == "Weapon 1" or slotName == "Weapon" then - return data.itemBases[item.baseName].weapon ~= nil - elseif slotName == "Weapon 2" then - local weapon1Sel = self.slots["Weapon 1"].selItem - local weapon1Type = weapon1Sel > 0 and data.itemBases[self.list[weapon1Sel].baseName].type or "None" - if weapon1Type == "Bow" then - return item.type == "Quiver" - elseif data.weaponTypeInfo[weapon1Type].oneHand then - return item.type == "Shield" or (data.weaponTypeInfo[item.type] and data.weaponTypeInfo[item.type].oneHand and (weapon1Type == "None" or (weapon1Type == "Wand" and item.type == "Wand") or (weapon1Type ~= "Wand" and item.type ~= "Wand"))) +function items:Shutdown() +end + +function items:Load(xml, dbFileName) + for _, node in ipairs(xml) do + if node.elem == "Item" then + local item = { } + item.raw = "" + item.id = tonumber(node.attrib.id) + self:ParseItemRaw(item) + for _, child in ipairs(node) do + if type(child) == "string" then + item.raw = child + self:ParseItemRaw(item) + elseif child.elem == "ModRange" then + local id = tonumber(child.attrib.id) or 0 + local range = tonumber(child.attrib.range) or 1 + if item.modLines[id] then + item.modLines[id].range = range + end + end + end + self:BuildItemModList(item) + self.list[item.id] = item + t_insert(self.orderList, item.id) + elseif node.elem == "Slot" then + if self.slots[node.attrib.name or ""] then + self.slots[node.attrib.name].selItem = tonumber(node.attrib.itemId) + end + end + end + self:PopulateSlots() +end + +function items:Save(xml) + self.modFlag = false + for _, id in ipairs(self.orderList) do + local item = self.list[id] + local child = { elem = "Item", attrib = { id = tostring(id) } } + t_insert(child, item.raw) + for id, modLine in ipairs(item.modLines) do + if modLine.range then + t_insert(child, { elem = "ModRange", attrib = { id = tostring(id), range = tostring(modLine.range) } }) + end + end + t_insert(xml, child) + end + for name, slot in pairs(self.slots) do + t_insert(xml, { elem = "Slot", attrib = { name = name, itemId = tostring(slot.selItem) }}) + end +end + +function items:DrawItems(viewPort, inputEvents) + common.controlsInput(self, inputEvents) + for id, event in ipairs(inputEvents) do + if event.type == "KeyDown" then + if event.key == "v" and IsKeyDown("CTRL") then + local newItem = Paste() + if newItem then + self.displayItem = { + raw = newItem:gsub("^%s+",""):gsub("%s+$",""):gsub("–","-"):gsub("%b<>",""):gsub("ö","o") + } + self:ParseItemRaw(self.displayItem) + if not self.displayItem.baseName then + self.displayItem = nil + end + end + end + end + end + + if self.displayItem then + self.controls.addDisplayItem.x = viewPort.x + viewPort.width - 530 + self.controls.addDisplayItem.y = viewPort.y + 4 + self.controls.addDisplayItem.hidden = false + self.controls.addDisplayItem.label = self.list[self.displayItem.id] and "Save" or "Add" + self:AddItemTooltip(self.displayItem) + main:DrawTooltip(viewPort.x + viewPort.width - 500, viewPort.y + 28, nil, nil, viewPort, data.colorCodes[self.displayItem.rarity], true) + else + self.controls.addDisplayItem.hidden = true + end + + self:UpdateJewels() + + common.controlsDraw(self, viewPort) + + for index, id in pairs(self.orderList) do + local item = self.list[id] + local rarityCode = data.colorCodes[item.rarity] + SetDrawColor(rarityCode) + local x = viewPort.x + 2 + local y = viewPort.y + 2 + 16 * (index - 1) + DrawString(x, y, "LEFT", 16, "VAR", item.name) + local cx, cy = GetCursorPos() + if cx >= x and cx < x + 250 and cy >= y and cy < y + 16 then + self:AddItemTooltip(item) + main:DrawTooltip(x, y, 250, 16, viewPort, rarityCode, true) end end end @@ -147,6 +173,42 @@ function items:GetSocketJewel(nodeId) return self.sockets[nodeId], self.list[self.sockets[nodeId].selItem] end +function items:AddDisplayItem() + for _, item in pairs(self.list) do + if item.raw == self.displayItem.raw then + self.displayItem = nil + return + end + end + if not self.list[self.displayItem.id] then + t_insert(self.orderList, self.displayItem.id) + end + self.list[self.displayItem.id] = self.displayItem + self.displayItem = nil + self:PopulateSlots() +end + +function items:ApplyRange(line, range) + return line:gsub("%((%d+)%-(%d+) to (%d+)%-(%d+)%)", function(minMin, maxMin, minMax, maxMax) return string.format("%d-%d", tonumber(minMin) + range * (tonumber(minMax) - tonumber(minMin)), tonumber(maxMin) + range * (tonumber(maxMax) - tonumber(maxMin))) end) + :gsub("%((%d+) to (%d+)%)", function(min, max) return tostring(tonumber(min) + range * (tonumber(max) - tonumber(min))) end) +end + +function items:IsItemValidForSlot(item, slotName) + if item.type == slotName:gsub(" %d+","") then + return true + elseif slotName == "Weapon 1" or slotName == "Weapon" then + return data.itemBases[item.baseName].weapon ~= nil + elseif slotName == "Weapon 2" then + local weapon1Sel = self.slots["Weapon 1"].selItem + local weapon1Type = weapon1Sel > 0 and data.itemBases[self.list[weapon1Sel].baseName].type or "None" + if weapon1Type == "Bow" then + return item.type == "Quiver" + elseif data.weaponTypeInfo[weapon1Type].oneHand then + return item.type == "Shield" or (data.weaponTypeInfo[item.type] and data.weaponTypeInfo[item.type].oneHand and (weapon1Type == "None" or (weapon1Type == "Wand" and item.type == "Wand") or (weapon1Type ~= "Wand" and item.type ~= "Wand"))) + end + end +end + function items:ParseItemRaw(item) if not item.id then item.id = 1 @@ -213,7 +275,7 @@ function items:ParseItemRaw(item) else local rangedLine if line:match("%(%d+%-%d+ to %d+%-%d+%)") or line:match("%(%d+ to %d+%)") then - rangedLine = applyRange(line, 1) + rangedLine = self:ApplyRange(line, 1) end local modList, extra = mod.parseMod(rangedLine or line) if modList then @@ -232,7 +294,7 @@ function items:BuildItemModList(item) for _, modLine in ipairs(item.modLines) do if not modLine.extra then if modLine.range then - local line = applyRange(modLine.line, modLine.range) + local line = self:ApplyRange(modLine.line, modLine.range) local list, extra = mod.parseMod(line) if list and not extra then mod.mods = list @@ -360,7 +422,7 @@ function items:AddItemTooltip(item) if item.modLines[1] then main:AddTooltipSeperator(10) for index, modLine in pairs(item.modLines) do - local line = modLine.range and applyRange(modLine.line, modLine.range) or modLine.line + local line = modLine.range and self:ApplyRange(modLine.line, modLine.range) or modLine.line main:AddTooltipLine(16, (modLine.extra and data.colorCodes.NORMAL or data.colorCodes.MAGIC)..line) if index == 1 and base.implicit and item.modLines[2] then main:AddTooltipSeperator(10) @@ -408,131 +470,4 @@ function items:AddItemTooltip(item) end end -function items:AddDisplayItem() - for _, item in pairs(self.list) do - if item.raw == self.displayItem.raw then - self.displayItem = nil - return - end - end - if not self.list[self.displayItem.id] then - t_insert(self.orderList, self.displayItem.id) - end - self.list[self.displayItem.id] = self.displayItem - self.displayItem = nil - self:PopulateSlots() -end - -function items:Init(build) - self.build = build - self.list = { } - self.sockets = { } - for _, node in pairs(main.tree.nodes) do - if node.type == "socket" then - self.sockets[node.id] = mkItemSlot(400, 0, "Jewel "..node.id, "Socket") - end - end - self.orderList = { } -end -function items:Shutdown() - -end - -function items:Load(xml, dbFileName) - for _, node in ipairs(xml) do - if node.elem == "Item" then - local item = { } - item.raw = "" - item.id = tonumber(node.attrib.id) - self:ParseItemRaw(item) - for _, child in ipairs(node) do - if type(child) == "string" then - item.raw = child - self:ParseItemRaw(item) - elseif child.elem == "ModRange" then - local id = tonumber(child.attrib.id) or 0 - local range = tonumber(child.attrib.range) or 1 - if item.modLines[id] then - item.modLines[id].range = range - end - end - end - self:BuildItemModList(item) - self.list[item.id] = item - t_insert(self.orderList, item.id) - elseif node.elem == "Slot" then - if self.slots[node.attrib.name or ""] then - self.slots[node.attrib.name].selItem = tonumber(node.attrib.itemId) - end - end - end - self:PopulateSlots() -end -function items:Save(xml) - self.modFlag = false - for _, id in ipairs(self.orderList) do - local item = self.list[id] - local child = { elem = "Item", attrib = { id = tostring(id) } } - t_insert(child, item.raw) - for id, modLine in ipairs(item.modLines) do - if modLine.range then - t_insert(child, { elem = "ModRange", attrib = { id = tostring(id), range = tostring(modLine.range) } }) - end - end - t_insert(xml, child) - end - for name, slot in pairs(self.slots) do - t_insert(xml, { elem = "Slot", attrib = { name = name, itemId = tostring(slot.selItem) }}) - end -end - -function items:DrawItems(viewPort, inputEvents) - common.controlsInput(self, inputEvents) - for id, event in ipairs(inputEvents) do - if event.type == "KeyDown" then - if event.key == "v" and IsKeyDown("CTRL") then - local newItem = Paste() - if newItem then - self.displayItem = { - raw = newItem:gsub("^%s+",""):gsub("%s+$",""):gsub("–","-"):gsub("%b<>",""):gsub("ö","o") - } - self:ParseItemRaw(self.displayItem) - if not self.displayItem.baseName then - self.displayItem = nil - end - end - end - end - end - - if self.displayItem then - self.controls.addDisplayItem.x = viewPort.x + viewPort.width - 530 - self.controls.addDisplayItem.y = viewPort.y + 4 - self.controls.addDisplayItem.hidden = false - self.controls.addDisplayItem.label = self.list[self.displayItem.id] and "Save" or "Add" - self:AddItemTooltip(self.displayItem) - main:DrawTooltip(viewPort.x + viewPort.width - 500, viewPort.y + 28, nil, nil, viewPort, data.colorCodes[self.displayItem.rarity], true) - else - self.controls.addDisplayItem.hidden = true - end - - self:UpdateJewels() - - common.controlsDraw(self, viewPort) - - for index, id in pairs(self.orderList) do - local item = self.list[id] - local rarityCode = data.colorCodes[item.rarity] - SetDrawColor(rarityCode) - local x = viewPort.x + 2 - local y = viewPort.y + 2 + 16 * (index - 1) - DrawString(x, y, "LEFT", 16, "VAR", item.name) - local cx, cy = GetCursorPos() - if cx >= x and cx < x + 250 and cy >= y and cy < y + 16 then - self:AddItemTooltip(item) - main:DrawTooltip(x, y, 250, 16, viewPort, rarityCode, true) - end - end -end - return items \ No newline at end of file diff --git a/Modules/Main.lua b/Modules/Main.lua index bff4585e..60d97be8 100644 --- a/Modules/Main.lua +++ b/Modules/Main.lua @@ -17,19 +17,138 @@ LoadModule("Classes/PassiveTree", launch) LoadModule("Classes/PassiveSpec", launch) LoadModule("Classes/PassiveTreeView", launch) -local cfg = { } - local main = { } -main.tooltipLines = { } +function main:Init() + self.modes = { } + self.modes["LIST"] = LoadModule("Modules/BuildList", launch, self) + self.modes["BUILD"] = LoadModule("Modules/Build", launch, self) + + self.tree = common.New("PassiveTree") + + self.inputEvents = { } + self.tooltipLines = { } + + self:SetMode("LIST") + + self:LoadSettings() +end + +function main:Shutdown() + self:CallMode("Shutdown") + + self:SaveSettings() +end + +function main:OnFrame() + self.screenW, self.screenH = GetScreenSize() + + if self.newMode then + if self.mode then + self:CallMode("Shutdown") + end + self.mode = self.newMode + self.modeArgs = self.newModeArgs + self.newMode = nil + self:CallMode("Init", unpack(self.modeArgs)) + end + + self:CallMode("OnFrame", self.inputEvents) + + wipeTable(self.inputEvents) +end + +function main:OnKeyDown(key, doubleClick) + t_insert(self.inputEvents, { type = "KeyDown", key = key, doubleClick = doubleClick }) +end + +function main:OnKeyUp(key) + t_insert(self.inputEvents, { type = "KeyUp", key = key }) +end + +function main:OnChar(key) + t_insert(self.inputEvents, { type = "Char", key = key }) +end + +function main:SetMode(newMode, ...) + self.newMode = newMode + self.newModeArgs = {...} +end + +function main:CallMode(func, ...) + local modeTbl = self.modes[self.mode] + if modeTbl[func] then + modeTbl[func](modeTbl, ...) + end +end + +function main:LoadSettings() + local setXML, errMsg = common.xml.LoadXMLFile("Settings.xml") + if not setXML then + return true + elseif setXML[1].elem ~= "PathOfBuilding" then + launch:ShowErrMsg("^1Error parsing 'Settings.xml': 'PathOfBuilding' root element missing") + return true + end + for _, node in ipairs(setXML[1]) do + if type(node) == "table" then + if node.elem == "Mode" then + if not node.attrib.mode or not self.modes[node.attrib.mode] then + launch:ShowErrMsg("^1Error parsing 'Settings.xml': Invalid mode attribute in 'Mode' element") + return true + end + local args = { } + for _, child in ipairs(node) do + if type(child) == "table" then + if child.elem == "Arg" then + if child.attrib.number then + t_insert(args, tonumber(child.attrib.number)) + elseif child.attrib.string then + t_insert(args, child.attrib.string) + elseif child.attrib.boolean then + t_insert(args, child.attrib.boolean == "true") + end + end + end + end + self:SetMode(node.attrib.mode, unpack(args)) + end + end + end +end + +function main:SaveSettings() + local setXML = { elem = "PathOfBuilding" } + local mode = { elem = "Mode", attrib = { mode = self.mode } } + for _, val in ipairs(self.modeArgs) do + local child = { elem = "Arg", attrib = {} } + if type(val) == "number" then + child.attrib.number = tostring(val) + elseif type(val) == "boolean" then + child.attrib.boolean = tostring(val) + else + child.attrib.string = tostring(val) + end + t_insert(mode, child) + end + t_insert(setXML, mode) + local res, errMsg = common.xml.SaveXMLFile(setXML, "Settings.xml") + if not res then + launch:ShowErrMsg("Error saving 'Settings.xml': %s", errMsg) + return true + end +end + function main:AddTooltipLine(size, text) for line in string.gmatch(text .. "\n", "([^\n]*)\n") do t_insert(self.tooltipLines, { size = size, text = line }) end end + function main:AddTooltipSeperator(size) t_insert(self.tooltipLines, { size = size }) end + function main:DrawTooltip(x, y, w, h, viewPort, col, center) local ttW, ttH = 0, 0 for _, data in ipairs(self.tooltipLines) do @@ -87,122 +206,4 @@ function main:DrawTooltip(x, y, w, h, viewPort, col, center) end end -main.modes = { } -main.modes["LIST"] = LoadModule("Modules/BuildList", launch, cfg, main) -main.modes["BUILD"] = LoadModule("Modules/Build", launch, cfg, main) - -function main:SetMode(newMode, ...) - self.newMode = newMode - self.newModeArgs = {...} -end - -function main:CallMode(func, ...) - local modeTbl = self.modes[self.mode] - if modeTbl[func] then - modeTbl[func](modeTbl, ...) - end -end - -function main:LoadSettings() - local setXML, errMsg = common.xml.LoadXMLFile("Settings.xml") - if not setXML then - return true - elseif setXML[1].elem ~= "PathOfBuilding" then - launch:ShowErrMsg("^1Error parsing 'Settings.xml': 'PathOfBuilding' root element missing") - return true - end - for _, node in ipairs(setXML[1]) do - if type(node) == "table" then - if node.elem == "Mode" then - if not node.attrib.mode or not self.modes[node.attrib.mode] then - launch:ShowErrMsg("^1Error parsing 'Settings.xml': Invalid mode attribute in 'Mode' element") - return true - end - local args = { } - for _, child in ipairs(node) do - if type(child) == "table" then - if child.elem == "Arg" then - if child.attrib.number then - t_insert(args, tonumber(child.attrib.number)) - elseif child.attrib.string then - t_insert(args, child.attrib.string) - elseif child.attrib.boolean then - t_insert(args, child.attrib.boolean == "true") - end - end - end - end - self:SetMode(node.attrib.mode, unpack(args)) - end - end - end -end -function main:SaveSettings() - local setXML = { elem = "PathOfBuilding" } - local mode = { elem = "Mode", attrib = { mode = self.mode } } - for _, val in ipairs(self.modeArgs) do - local child = { elem = "Arg", attrib = {} } - if type(val) == "number" then - child.attrib.number = tostring(val) - elseif type(val) == "boolean" then - child.attrib.boolean = tostring(val) - else - child.attrib.string = tostring(val) - end - t_insert(mode, child) - end - t_insert(setXML, mode) - local res, errMsg = common.xml.SaveXMLFile(setXML, "Settings.xml") - if not res then - launch:ShowErrMsg("Error saving 'Settings.xml': %s", errMsg) - return true - end -end - -function main:OnFrame() - cfg.screenW, cfg.screenH = GetScreenSize() - - if self.newMode then - if self.mode then - self:CallMode("Shutdown") - end - self.mode = self.newMode - self.modeArgs = self.newModeArgs - self.newMode = nil - self:CallMode("Init", unpack(self.modeArgs)) - end - - self:CallMode("OnFrame", self.inputEvents) - - wipeTable(self.inputEvents) -end - -function main:OnKeyDown(key, doubleClick) - t_insert(self.inputEvents, { type = "KeyDown", key = key, doubleClick = doubleClick }) -end - -function main:OnKeyUp(key) - t_insert(self.inputEvents, { type = "KeyUp", key = key }) -end - -function main:OnChar(key) - t_insert(self.inputEvents, { type = "Char", key = key }) -end - -function main:Init() - self.inputEvents = { } - - self.tree = common.New("PassiveTree") - - self:SetMode("LIST") - - self:LoadSettings() -end - -function main:Shutdown() - self:CallMode("Shutdown") - - self:SaveSettings() -end - return main \ No newline at end of file diff --git a/Modules/ModParser.lua b/Modules/ModParser.lua index f30a6f2a..028d043e 100644 --- a/Modules/ModParser.lua +++ b/Modules/ModParser.lua @@ -323,6 +323,9 @@ local specialModList = { ["all attacks with this weapon are critical strikes"] = { weaponAlwaysCrit = true }, ["hits can't be evaded"] = { weaponX_noEvade = true }, ["no block chance"] = { shieldNoBlock = true }, + ["causes bleeding on hit"] = { bleedChance = 100 }, + ["poisonous hit"] = { poisonChance = 100 }, + ["your chaos damage poisons enemies"] = { poisonChance = 100 }, ["has 1 socket"] = { }, ["socketed gems have (.+)"] = { }, ["socketed gems are Supported by (.+)"] = { }, diff --git a/PathOfBuilding.sln b/PathOfBuilding.sln index 91444ca0..89ceca1c 100644 --- a/PathOfBuilding.sln +++ b/PathOfBuilding.sln @@ -56,6 +56,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Classes", "Classes", "{7EE4 Classes\ButtonControl.lua = Classes\ButtonControl.lua Classes\DropDownControl.lua = Classes\DropDownControl.lua Classes\EditControl.lua = Classes\EditControl.lua + Classes\Grid.lua = Classes\Grid.lua + Classes\ItemSlot.lua = Classes\ItemSlot.lua Classes\PassiveSpec.lua = Classes\PassiveSpec.lua Classes\PassiveTree.lua = Classes\PassiveTree.lua Classes\PassiveTreeView.lua = Classes\PassiveTreeView.lua