Addeed the following new gems: Artillery Ballista Ensnaring Arrow Shrapnel Ballista Arrow Nova Barrage (does not give accurate damage numbers) Greater Volley The 35 new Awakened support gems Applied the skill reworks and balance changes for 3.9.0 Updated item bases, mods, and enchantments for 3.9.0 Added new influence bases to crafting window Fix all Oils on tree to have correct values Add more detailed breakdown for shock and chill
436 lines
14 KiB
Lua
436 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.implyCondList then
|
|
for _, implyCond in ipairs(varData.implyCondList) do
|
|
if (implyCond and mainEnv.conditionsUsed[implyCond]) then
|
|
return true
|
|
end
|
|
end
|
|
end
|
|
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.implyCondList then
|
|
for _, implyCond in ipairs(varData.implyCondList) do
|
|
if (implyCond and mainEnv.conditionsUsed[implyCond]) then
|
|
return true
|
|
end
|
|
end
|
|
end
|
|
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
|