422 lines
14 KiB
Lua
422 lines
14 KiB
Lua
-- Path of Building
|
|
--
|
|
-- Module: Config Tab
|
|
-- Configuration tab for the current build.
|
|
--
|
|
local t_insert = table.insert
|
|
local m_min = math.min
|
|
local m_max = math.max
|
|
local m_floor = math.floor
|
|
|
|
local gameVersionDropList = { }
|
|
for _, version in ipairs(targetVersionList) do
|
|
local data = targetVersions[version]
|
|
t_insert( gameVersionDropList, {
|
|
label = data.long,
|
|
version = version,
|
|
versionPretty = data.short,
|
|
})
|
|
end
|
|
|
|
local varList = LoadModule("Modules/ConfigOptions")
|
|
|
|
local ConfigTabClass = newClass("ConfigTab", "UndoHandler", "ControlHost", "Control", function(self, build)
|
|
self.UndoHandler()
|
|
self.ControlHost()
|
|
self.Control()
|
|
|
|
self.build = build
|
|
|
|
self.input = { }
|
|
|
|
self.sectionList = { }
|
|
self.varControls = { }
|
|
|
|
self:BuildModList()
|
|
|
|
local lastSection
|
|
for _, varData in ipairs(varList) do
|
|
if varData.section then
|
|
lastSection = new("SectionControl", {"TOPLEFT",self,"TOPLEFT"}, 0, 0, 360, 0, varData.section)
|
|
lastSection.varControlList = { }
|
|
lastSection.col = varData.col
|
|
lastSection.height = function(self)
|
|
local height = 20
|
|
for _, varControl in pairs(self.varControlList) do
|
|
if varControl:IsShown() then
|
|
height = height + 20
|
|
end
|
|
end
|
|
return m_max(height, 32)
|
|
end
|
|
t_insert(self.sectionList, lastSection)
|
|
t_insert(self.controls, lastSection)
|
|
elseif not varData.ifVer or varData.ifVer == build.targetVersion then
|
|
local control
|
|
if varData.type == "check" then
|
|
control = new("CheckBoxControl", {"TOPLEFT",lastSection,"TOPLEFT"}, 234, 0, 18, nil, function(state)
|
|
self.input[varData.var] = state
|
|
self:AddUndoState()
|
|
self:BuildModList()
|
|
self.build.buildFlag = true
|
|
end)
|
|
elseif varData.type == "count" or varData.type == "integer" then
|
|
control = new("EditControl", {"TOPLEFT",lastSection,"TOPLEFT"}, 234, 0, 90, 18, "", nil, varData.type == "integer" and "^%-%d" or "%D", 6, function(buf)
|
|
self.input[varData.var] = tonumber(buf)
|
|
self:AddUndoState()
|
|
self:BuildModList()
|
|
self.build.buildFlag = true
|
|
end)
|
|
elseif varData.type == "list" then
|
|
control = new("DropDownControl", {"TOPLEFT",lastSection,"TOPLEFT"}, 234, 0, 118, 16, varData.list, function(index, value)
|
|
self.input[varData.var] = value.val
|
|
self:AddUndoState()
|
|
self:BuildModList()
|
|
self.build.buildFlag = true
|
|
end)
|
|
else
|
|
control = new("Control", {"TOPLEFT",lastSection,"TOPLEFT"}, 234, 0, 16, 16)
|
|
end
|
|
if varData.ifNode then
|
|
control.shown = function()
|
|
if self.build.spec.allocNodes[varData.ifNode] then
|
|
return true
|
|
end
|
|
local node = self.build.spec.nodes[varData.ifNode]
|
|
if node.type == "Keystone" then
|
|
return self.build.calcsTab.mainEnv.keystonesAdded[node.dn]
|
|
end
|
|
end
|
|
control.tooltipText = function()
|
|
return "This option is specific to '"..self.build.spec.nodes[varData.ifNode].dn.."'."..(varData.tooltip and "\n"..varData.tooltip or "")
|
|
end
|
|
elseif varData.ifOption then
|
|
control.shown = function()
|
|
return self.input[varData.ifOption]
|
|
end
|
|
elseif varData.ifCond or varData.ifMinionCond or varData.ifEnemyCond then
|
|
control.shown = function()
|
|
local mainEnv = self.build.calcsTab.mainEnv
|
|
if self.input[varData.var] then
|
|
if (varData.implyCond and mainEnv.conditionsUsed[varData.implyCond]) or
|
|
(varData.implyMinionCond and mainEnv.minionConditionsUsed[varData.implyMinionCond]) or
|
|
(varData.implyEnemyCond and mainEnv.enemyConditionsUsed[varData.implyEnemyCond]) then
|
|
return true
|
|
end
|
|
end
|
|
if varData.ifCond then
|
|
return mainEnv.conditionsUsed[varData.ifCond]
|
|
elseif varData.ifMinionCond then
|
|
return mainEnv.minionConditionsUsed[varData.ifMinionCond]
|
|
else
|
|
return mainEnv.enemyConditionsUsed[varData.ifEnemyCond]
|
|
end
|
|
end
|
|
control.tooltipText = function()
|
|
if launch.devModeAlt then
|
|
local out = varData.tooltip or ""
|
|
local list
|
|
if varData.ifCond then
|
|
list = self.build.calcsTab.mainEnv.conditionsUsed[varData.ifCond]
|
|
elseif varData.ifMinionCond then
|
|
list = self.build.calcsTab.mainEnv.minionConditionsUsed[varData.ifMinionCond]
|
|
else
|
|
list = self.build.calcsTab.mainEnv.enemyConditionsUsed[varData.ifEnemyCond]
|
|
end
|
|
for _, mod in ipairs(list) do
|
|
out = (#out > 0 and out.."\n" or out) .. modLib.formatMod(mod) .. "|" .. mod.source
|
|
end
|
|
return out
|
|
else
|
|
return varData.tooltip
|
|
end
|
|
end
|
|
elseif varData.ifMult or varData.ifEnemyMult then
|
|
control.shown = function()
|
|
local mainEnv = self.build.calcsTab.mainEnv
|
|
if self.input[varData.var] then
|
|
if (varData.implyCond and mainEnv.conditionsUsed[varData.implyCond]) or
|
|
(varData.implyMinionCond and mainEnv.minionConditionsUsed[varData.implyMinionCond]) or
|
|
(varData.implyEnemyCond and mainEnv.enemyConditionsUsed[varData.implyEnemyCond]) then
|
|
return true
|
|
end
|
|
end
|
|
if varData.ifMult then
|
|
return mainEnv.multipliersUsed[varData.ifMult]
|
|
else
|
|
return mainEnv.enemyMultipliersUsed[varData.ifEnemyMult]
|
|
end
|
|
end
|
|
control.tooltipText = function()
|
|
if launch.devModeAlt then
|
|
local out = varData.tooltip or ""
|
|
for _, mod in ipairs(self.build.calcsTab.mainEnv.multipliersUsed[varData.ifMult]) do
|
|
out = (#out > 0 and out.."\n" or out) .. modLib.formatMod(mod) .. "|" .. mod.source
|
|
end
|
|
return out
|
|
else
|
|
return varData.tooltip
|
|
end
|
|
end
|
|
elseif varData.ifFlag then
|
|
control.shown = function()
|
|
return self.build.calcsTab.mainEnv.player.mainSkill.skillFlags[varData.ifFlag] -- O_O
|
|
end
|
|
control.tooltipText = varData.tooltip
|
|
elseif varData.ifSkill or varData.ifSkillList then
|
|
control.shown = function()
|
|
if varData.ifSkillList then
|
|
for _, skillName in ipairs(varData.ifSkillList) do
|
|
if self.build.calcsTab.mainEnv.skillsUsed[skillName] then
|
|
return true
|
|
end
|
|
end
|
|
else
|
|
return self.build.calcsTab.mainEnv.skillsUsed[varData.ifSkill]
|
|
end
|
|
end
|
|
control.tooltipText = varData.tooltip
|
|
else
|
|
control.tooltipText = varData.tooltip
|
|
end
|
|
t_insert(self.controls, new("LabelControl", {"RIGHT",control,"LEFT"}, -4, 0, 0, DrawStringWidth(14, "VAR", varData.label) > 228 and 12 or 14, "^7"..varData.label))
|
|
if varData.var then
|
|
self.varControls[varData.var] = control
|
|
end
|
|
t_insert(self.controls, control)
|
|
t_insert(lastSection.varControlList, control)
|
|
end
|
|
end
|
|
|
|
-- Special control for game version selector
|
|
self.controls.gameVersion = new("DropDownControl", {"TOPLEFT",self.sectionList[1],"TOPLEFT"}, 234, 0, 118, 16, gameVersionDropList, function(index, value)
|
|
if value.version ~= build.targetVersion then
|
|
main:OpenConfirmPopup("Convert Build", colorCodes.WARNING.."Warning:^7 Converting a build to a different game version may have side effects.\nFor example, if the passive tree has changed, then some passives may be deallocated.\nYou should create a backup copy of the build before proceeding.", "Convert to "..value.versionPretty, function()
|
|
if build.unsaved then
|
|
build:OpenSavePopup("VERSION", value.version)
|
|
else
|
|
if build.dbFileName then
|
|
build.targetVersion = value.version
|
|
build:SaveDBFile()
|
|
end
|
|
build:Shutdown()
|
|
build:Init(build.dbFileName, build.buildName, nil, value.version)
|
|
end
|
|
end)
|
|
end
|
|
end)
|
|
t_insert(self.controls, new("LabelControl", {"RIGHT",self.controls.gameVersion,"LEFT"}, -4, 0, 0, 14, "^7Game Version:"))
|
|
t_insert(self.sectionList[1].varControlList, 1, self.controls.gameVersion)
|
|
|
|
self.controls.scrollBar = new("ScrollBarControl", {"TOPRIGHT",self,"TOPRIGHT"}, 0, 0, 18, 0, 50, "VERTICAL", true)
|
|
end)
|
|
|
|
function ConfigTabClass:Load(xml, fileName)
|
|
for _, node in ipairs(xml) do
|
|
if node.elem == "Input" then
|
|
if not node.attrib.name then
|
|
launch:ShowErrMsg("^1Error parsing '%s': 'Input' element missing name attribute", fileName)
|
|
return true
|
|
end
|
|
if node.attrib.number then
|
|
self.input[node.attrib.name] = tonumber(node.attrib.number)
|
|
elseif node.attrib.string then
|
|
self.input[node.attrib.name] = node.attrib.string
|
|
elseif node.attrib.boolean then
|
|
self.input[node.attrib.name] = node.attrib.boolean == "true"
|
|
else
|
|
launch:ShowErrMsg("^1Error parsing '%s': 'Input' element missing number, string or boolean attribute", fileName)
|
|
return true
|
|
end
|
|
end
|
|
end
|
|
self:BuildModList()
|
|
self:UpdateControls()
|
|
self:ResetUndo()
|
|
end
|
|
|
|
function ConfigTabClass:Save(xml)
|
|
for k, v in pairs(self.input) do
|
|
if v then
|
|
local child = { elem = "Input", attrib = {name = k} }
|
|
if type(v) == "number" then
|
|
child.attrib.number = tostring(v)
|
|
elseif type(v) == "boolean" then
|
|
child.attrib.boolean = tostring(v)
|
|
else
|
|
child.attrib.string = tostring(v)
|
|
end
|
|
t_insert(xml, child)
|
|
end
|
|
end
|
|
self.modFlag = false
|
|
end
|
|
|
|
function ConfigTabClass:UpdateControls()
|
|
for var, control in pairs(self.varControls) do
|
|
if control._className == "EditControl" then
|
|
control:SetText(tostring(self.input[var] or ""))
|
|
elseif control._className == "CheckBoxControl" then
|
|
control.state = self.input[var]
|
|
elseif control._className == "DropDownControl" then
|
|
control:SelByValue(self.input[var], "val")
|
|
end
|
|
end
|
|
end
|
|
|
|
function ConfigTabClass:Draw(viewPort, inputEvents)
|
|
self.x = viewPort.x
|
|
self.y = viewPort.y
|
|
self.width = viewPort.width
|
|
self.height = viewPort.height
|
|
|
|
if not main.popups[1] then
|
|
-- >_>
|
|
self.controls.gameVersion:SelByValue(self.build.targetVersion, "version")
|
|
end
|
|
|
|
for id, event in ipairs(inputEvents) do
|
|
if event.type == "KeyDown" then
|
|
if event.key == "z" and IsKeyDown("CTRL") then
|
|
self:Undo()
|
|
self.build.buildFlag = true
|
|
elseif event.key == "y" and IsKeyDown("CTRL") then
|
|
self:Redo()
|
|
self.build.buildFlag = true
|
|
end
|
|
end
|
|
end
|
|
|
|
self:ProcessControlsInput(inputEvents, viewPort)
|
|
for id, event in ipairs(inputEvents) do
|
|
if event.type == "KeyUp" then
|
|
if event.key == "WHEELDOWN" then
|
|
self.controls.scrollBar:Scroll(1)
|
|
elseif event.key == "WHEELUP" then
|
|
self.controls.scrollBar:Scroll(-1)
|
|
end
|
|
end
|
|
end
|
|
|
|
local maxCol = m_floor((viewPort.width - 10) / 370)
|
|
local maxColY = 0
|
|
local colY = { }
|
|
for _, section in ipairs(self.sectionList) do
|
|
local y = 14
|
|
section.shown = true
|
|
local doShow = false
|
|
for _, varControl in ipairs(section.varControlList) do
|
|
if varControl:IsShown() then
|
|
doShow = true
|
|
local width, height = varControl:GetSize()
|
|
varControl.y = y + (18 - height) / 2
|
|
y = y + 20
|
|
end
|
|
end
|
|
section.shown = doShow
|
|
if doShow then
|
|
local width, height = section:GetSize()
|
|
local col
|
|
if section.col and (colY[section.col] or 0) + height + 28 <= viewPort.height then
|
|
col = section.col
|
|
else
|
|
col = 1
|
|
for c = 2, maxCol do
|
|
colY[c] = colY[c] or 0
|
|
if colY[c] < colY[col] then
|
|
col = c
|
|
end
|
|
end
|
|
end
|
|
colY[col] = colY[col] or 0
|
|
section.x = 10 + (col - 1) * 370
|
|
section.y = colY[col] + 18
|
|
colY[col] = colY[col] + height + 18
|
|
maxColY = m_max(maxColY, colY[col])
|
|
end
|
|
end
|
|
|
|
self.controls.scrollBar.height = viewPort.height
|
|
self.controls.scrollBar:SetContentDimension(maxColY + 10, viewPort.height)
|
|
for _, section in ipairs(self.sectionList) do
|
|
section.y = section.y - self.controls.scrollBar.offset
|
|
end
|
|
|
|
main:DrawBackground(viewPort)
|
|
|
|
self:DrawControls(viewPort)
|
|
end
|
|
|
|
function ConfigTabClass:BuildModList()
|
|
local modList = new("ModList")
|
|
self.modList = modList
|
|
local enemyModList = new("ModList")
|
|
self.enemyModList = enemyModList
|
|
local input = self.input
|
|
for _, varData in ipairs(varList) do
|
|
if varData.apply and (not varData.ifVer or varData.ifVer == self.build.targetVersion) then
|
|
if varData.type == "check" then
|
|
if input[varData.var] then
|
|
varData.apply(true, modList, enemyModList, self.build)
|
|
end
|
|
elseif varData.type == "count" or varData.type == "integer" then
|
|
if input[varData.var] and input[varData.var] ~= 0 then
|
|
varData.apply(input[varData.var], modList, enemyModList, self.build)
|
|
end
|
|
elseif varData.type == "list" then
|
|
if input[varData.var] then
|
|
varData.apply(input[varData.var], modList, enemyModList, self.build)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
function ConfigTabClass:ImportCalcSettings()
|
|
local input = self.input
|
|
local calcsInput = self.build.calcsTab.input
|
|
local function import(old, new)
|
|
input[new] = calcsInput[old]
|
|
calcsInput[old] = nil
|
|
end
|
|
import("Cond_LowLife", "conditionLowLife")
|
|
import("Cond_FullLife", "conditionFullLife")
|
|
import("buff_power", "usePowerCharges")
|
|
import("buff_frenzy", "useFrenzyCharges")
|
|
import("buff_endurance", "useEnduranceCharges")
|
|
import("CondBuff_Onslaught", "buffOnslaught")
|
|
import("CondBuff_Phasing", "buffPhasing")
|
|
import("CondBuff_Fortify", "buffFortify")
|
|
import("CondBuff_UsingFlask", "conditionUsingFlask")
|
|
import("buff_pendulum", "usePendulum")
|
|
import("CondEff_EnemyCursed", "conditionEnemyCursed")
|
|
import("CondEff_EnemyBleeding", "conditionEnemyBleeding")
|
|
import("CondEff_EnemyPoisoned", "conditionEnemyPoisoned")
|
|
import("CondEff_EnemyBurning", "conditionEnemyBurning")
|
|
import("CondEff_EnemyIgnited", "conditionEnemyIgnited")
|
|
import("CondEff_EnemyChilled", "conditionEnemyChilled")
|
|
import("CondEff_EnemyFrozen", "conditionEnemyFrozen")
|
|
import("CondEff_EnemyShocked", "conditionEnemyShocked")
|
|
import("effective_physicalRed", "enemyPhysicalReduction")
|
|
import("effective_fireResist", "enemyFireResist")
|
|
import("effective_coldResist", "enemyColdResist")
|
|
import("effective_lightningResist", "enemyLightningResist")
|
|
import("effective_chaosResist", "enemyChaosResist")
|
|
import("effective_enemyIsBoss", "enemyIsBoss")
|
|
self:BuildModList()
|
|
self:UpdateControls()
|
|
end
|
|
|
|
function ConfigTabClass:CreateUndoState()
|
|
return copyTable(self.input)
|
|
end
|
|
|
|
function ConfigTabClass:RestoreUndoState(state)
|
|
wipeTable(self.input)
|
|
for k, v in pairs(state) do
|
|
self.input[k] = v
|
|
end
|
|
self:UpdateControls()
|
|
self:BuildModList()
|
|
end
|