Add support for multiple configurations (#7212)
* add support for multiple configurations * fix scrolling * defaultIndex for boss preset and damageType dropdowns * fix for custom mods built into modList * clear configs before copying over changes for values that do not overlap between sets when changing activeSet * add configSets to Loadouts fix bug with Item/Skill/Config SetListControls refactor functions in Loadout logic for identical code for item/skill/config add SyncLoadouts to Delete in SetListControls and Convert functions in TreeTab update help section * add Sync to PassiveSpecListControl Delete * add Sync to ConfigSet Rename * Add unique formatting to options in dropdowns to avoid conflicting with user-created loadouts * revert bugfix as it caused another bug * typos * remove duplicateCheck and recolor logic, fix duplicate scenario where sets have same name and same group * typo * refactor doubling up of setting values, use self.configSets[self.activeConfigSetId] everywhere possible * fix for the "Default" loadout update Help Section with colour formatting info fix bug when reordering Tree Sets that are actively in loadouts * Alter scrollbar height for new UI elements * Sort loadouts by set name if it's there * Sort loadouts by tree order + properly reset all dropdowns --------- Co-authored-by: Wires77 <Wires77@users.noreply.github.com>
This commit is contained in:
17
help.txt
17
help.txt
@@ -115,12 +115,17 @@ Advanced tricks:
|
||||
|
||||
---[Loadouts]
|
||||
|
||||
Loadouts can be selected from the dropdown in the top middle of the Build Tab. Selecting a Loadout will load all three sets at once. These are automatically registered based on one of two conditions:
|
||||
1) All three sets share the same name, e.g. "Leveling"
|
||||
2) All three sets have the same alphanumeric identifier inside of braces { } at the end of the name, e.g. "Leveling Tree {1}", "Leveling Items {1}", "Leveling Skills {1}"
|
||||
- If you would like a single set to be used in multiple Loadouts, you can put the identifiers in the braces separated by commas. For example, an Item Set could be named "Early Game {1,2}" and this would be recognized as a loadout given there's a Tree and Skill Set with {1}, {2}, or {1,2} in their names as well
|
||||
- Lastly, the name of the Loadout in the dropdown is based on the name of the Tree Set, identifiers are shown for clarity when using sets multiple times
|
||||
The "New Loadout" option allows the user to create all three sets from a single popup for convenience.
|
||||
Loadouts can be selected from the dropdown in the top middle of the screen. Selecting a Loadout will load all four sets at once. These are automatically registered based on one of two conditions:
|
||||
|
||||
1) All four sets share the same name and colour formatting, e.g. "Leveling"
|
||||
- If you have a set named ^4Leveling^7, it will not match to other sets named Leveling
|
||||
|
||||
2) All four sets have the same alphanumeric identifier inside of braces { } at the end of the name, e.g. "Leveling Tree {1}", "Leveling Items {1}", "Leveling Skills {1}", "Leveling Config {1}"
|
||||
- If you would like a single set to be used in multiple Loadouts, you can put the identifiers in the braces separated by commas. For example, an Item Set could be named "Early Game {1,2}" and there could be Tree, Skill, and Config Sets with {1} and {2}, resulting in two loadouts linked to the same Item Set
|
||||
- The name of the Loadout in the dropdown is based on the name of the Tree Set, identifiers are shown for clarity when using sets multiple times
|
||||
- These sets can have differing colour formatting so long as the identifier texts match
|
||||
|
||||
The "New Loadout" option allows the user to create all four sets from a single popup for convenience.
|
||||
The "Sync" option is a backup option to force the UI to update in case the user has changed this data behind the scenes.
|
||||
|
||||
|
||||
|
||||
110
src/Classes/ConfigSetListControl.lua
Normal file
110
src/Classes/ConfigSetListControl.lua
Normal file
@@ -0,0 +1,110 @@
|
||||
-- Path of Building
|
||||
--
|
||||
-- Class: Config Set List
|
||||
-- Config Set list control
|
||||
--
|
||||
local t_insert = table.insert
|
||||
local t_remove = table.remove
|
||||
local m_max = math.max
|
||||
|
||||
local ConfigSetListClass = newClass("ConfigSetListControl", "ListControl", function(self, anchor, x, y, width, height, configTab)
|
||||
self.ListControl(anchor, x, y, width, height, 16, "VERTICAL", true, configTab.configSetOrderList)
|
||||
self.configTab = configTab
|
||||
self.controls.copy = new("ButtonControl", {"BOTTOMLEFT",self,"TOP"}, 2, -4, 60, 18, "Copy", function()
|
||||
local configSet = configTab.configSets[self.selValue]
|
||||
local newConfigSet = copyTable(configSet, true)
|
||||
newConfigSet.id = 1
|
||||
while configTab.configSets[newConfigSet.id] do
|
||||
newConfigSet.id = newConfigSet.id + 1
|
||||
end
|
||||
configTab.configSets[newConfigSet.id] = newConfigSet
|
||||
self:RenameSet(newConfigSet, true)
|
||||
end)
|
||||
self.controls.copy.enabled = function()
|
||||
return self.selValue ~= nil
|
||||
end
|
||||
self.controls.delete = new("ButtonControl", {"LEFT",self.controls.copy,"RIGHT"}, 4, 0, 60, 18, "Delete", function()
|
||||
self:OnSelDelete(self.selIndex, self.selValue)
|
||||
end)
|
||||
self.controls.delete.enabled = function()
|
||||
return self.selValue ~= nil and #self.list > 1
|
||||
end
|
||||
self.controls.rename = new("ButtonControl", {"BOTTOMRIGHT",self,"TOP"}, -2, -4, 60, 18, "Rename", function()
|
||||
self:RenameSet(configTab.configSets[self.selValue])
|
||||
end)
|
||||
self.controls.rename.enabled = function()
|
||||
return self.selValue ~= nil
|
||||
end
|
||||
self.controls.new = new("ButtonControl", {"RIGHT",self.controls.rename,"LEFT"}, -4, 0, 60, 18, "New", function()
|
||||
self:RenameSet(configTab:NewConfigSet(), true)
|
||||
end)
|
||||
end)
|
||||
|
||||
function ConfigSetListClass:RenameSet(configSet, addOnName)
|
||||
local controls = { }
|
||||
controls.label = new("LabelControl", nil, 0, 20, 0, 16, "^7Enter name for this config set:")
|
||||
controls.edit = new("EditControl", nil, 0, 40, 350, 20, configSet.title, nil, nil, 100, function(buf)
|
||||
controls.save.enabled = buf:match("%S")
|
||||
end)
|
||||
controls.save = new("ButtonControl", nil, -45, 70, 80, 20, "Save", function()
|
||||
configSet.title = controls.edit.buf
|
||||
self.configTab.modFlag = true
|
||||
if addOnName then
|
||||
t_insert(self.list, configSet.id)
|
||||
self.selIndex = #self.list
|
||||
self.selValue = configSet
|
||||
end
|
||||
self.configTab:AddUndoState()
|
||||
self.configTab.build:SyncLoadouts()
|
||||
main:ClosePopup()
|
||||
end)
|
||||
controls.save.enabled = false
|
||||
controls.cancel = new("ButtonControl", nil, 45, 70, 80, 20, "Cancel", function()
|
||||
if addOnName then
|
||||
self.configTab.configSets[configSet.id] = nil
|
||||
end
|
||||
main:ClosePopup()
|
||||
end)
|
||||
main:OpenPopup(370, 100, configSet.title and "Rename" or "Set Name", controls, "save", "edit", "cancel")
|
||||
end
|
||||
|
||||
function ConfigSetListClass:GetRowValue(column, index, configSetId)
|
||||
local configSet = self.configTab.configSets[configSetId]
|
||||
if column == 1 then
|
||||
return (configSet.title or "Default") .. (configSetId == self.configTab.activeConfigSetId and " ^9(Current)" or "")
|
||||
end
|
||||
end
|
||||
|
||||
function ConfigSetListClass:OnOrderChange()
|
||||
self.configTab.modFlag = true
|
||||
end
|
||||
|
||||
function ConfigSetListClass:OnSelClick(index, configSetId, doubleClick)
|
||||
if doubleClick and configSetId ~= self.configTab.activeConfigSetId then
|
||||
self.configTab:SetActiveConfigSet(configSetId)
|
||||
self.configTab:AddUndoState()
|
||||
end
|
||||
end
|
||||
|
||||
function ConfigSetListClass:OnSelDelete(index, configSetId)
|
||||
local configSet = self.configTab.configSets[configSetId]
|
||||
if #self.list > 1 then
|
||||
main:OpenConfirmPopup("Delete Config Set", "Are you sure you want to delete '"..(configSet.title or "Default").."'?", "Delete", function()
|
||||
t_remove(self.list, index)
|
||||
self.configTab.configSets[configSetId] = nil
|
||||
self.selIndex = nil
|
||||
self.selValue = nil
|
||||
if configSetId == self.configTab.activeConfigSetId then
|
||||
self.configTab:SetActiveConfigSet(self.list[m_max(1, index - 1)])
|
||||
end
|
||||
self.configTab:AddUndoState()
|
||||
self.configTab.build:SyncLoadouts()
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
function ConfigSetListClass:OnSelKeyDown(index, configSetId, key)
|
||||
if key == "F2" then
|
||||
self:RenameSet(self.configTab.configSets[configSetId])
|
||||
end
|
||||
end
|
||||
@@ -21,18 +21,37 @@ local ConfigTabClass = newClass("ConfigTab", "UndoHandler", "ControlHost", "Cont
|
||||
self.input = { }
|
||||
self.placeholder = { }
|
||||
self.defaultState = { }
|
||||
|
||||
-- Initialise config sets
|
||||
self.configSets = { }
|
||||
self.configSetOrderList = { 1 }
|
||||
self:NewConfigSet(1)
|
||||
self:SetActiveConfigSet(1, true)
|
||||
|
||||
self.enemyLevel = 1
|
||||
|
||||
self.sectionList = { }
|
||||
self.varControls = { }
|
||||
|
||||
self:BuildModList()
|
||||
|
||||
self.toggleConfigs = false
|
||||
|
||||
self.controls.sectionAnchor = new("LabelControl", { "TOPLEFT", self, "TOPLEFT" }, 0, 20, 0, 0, "")
|
||||
self.controls.search = new("EditControl", { "TOPLEFT", self.controls.sectionAnchor, "TOPLEFT" }, 8, -15, 360, 20, "", "Search", "%c", 100, function()
|
||||
|
||||
-- Set selector
|
||||
self.controls.setSelect = new("DropDownControl", { "TOPLEFT", self.controls.sectionAnchor, "TOPLEFT" }, 76, -12, 210, 20, nil, function(index, value)
|
||||
self:SetActiveConfigSet(self.configSetOrderList[index])
|
||||
self:AddUndoState()
|
||||
end)
|
||||
self.controls.setSelect.enableDroppedWidth = true
|
||||
self.controls.setSelect.enabled = function()
|
||||
return #self.configSetOrderList > 1
|
||||
end
|
||||
self.controls.setLabel = new("LabelControl", { "RIGHT", self.controls.setSelect, "LEFT" }, -2, 0, 0, 16, "^7Config set:")
|
||||
self.controls.setManage = new("ButtonControl", { "LEFT", self.controls.setSelect, "RIGHT" }, 4, 0, 90, 20, "Manage...", function()
|
||||
self:OpenConfigSetManagePopup()
|
||||
end)
|
||||
|
||||
self.controls.search = new("EditControl", { "TOPLEFT", self.controls.sectionAnchor, "TOPLEFT" }, 8, 15, 360, 20, "", "Search", "%c", 100, function()
|
||||
self:UpdateControls()
|
||||
end, nil, nil, true)
|
||||
self.controls.toggleConfigs = new("ButtonControl", { "LEFT", self.controls.search, "RIGHT" }, 10, 0, 200, 20, function()
|
||||
@@ -75,7 +94,7 @@ local ConfigTabClass = newClass("ConfigTab", "UndoHandler", "ControlHost", "Cont
|
||||
|
||||
local function implyCond(varData)
|
||||
local mainEnv = self.build.calcsTab.mainEnv
|
||||
if self.input[varData.var] then
|
||||
if self.configSets[self.activeConfigSetId].input[varData.var] then
|
||||
if varData.implyCondList then
|
||||
for _, implyCond in ipairs(varData.implyCondList) do
|
||||
if (implyCond and mainEnv.conditionsUsed[implyCond]) then
|
||||
@@ -125,7 +144,7 @@ local ConfigTabClass = newClass("ConfigTab", "UndoHandler", "ControlHost", "Cont
|
||||
local lastSection
|
||||
for _, varData in ipairs(varList) do
|
||||
if varData.section then
|
||||
lastSection = new("SectionControl", {"TOPLEFT",self.controls.sectionAnchor,"TOPLEFT"}, 0, 0, 360, 0, varData.section)
|
||||
lastSection = new("SectionControl", {"TOPLEFT",self.controls.search,"BOTTOMLEFT"}, 0, 0, 360, 0, varData.section)
|
||||
lastSection.varControlList = { }
|
||||
lastSection.col = varData.col
|
||||
lastSection.height = function(self)
|
||||
@@ -143,7 +162,7 @@ local ConfigTabClass = newClass("ConfigTab", "UndoHandler", "ControlHost", "Cont
|
||||
local control
|
||||
if varData.type == "check" then
|
||||
control = new("CheckBoxControl", {"TOPLEFT",lastSection,"TOPLEFT"}, 234, 0, 18, varData.label, function(state)
|
||||
self.input[varData.var] = state
|
||||
self.configSets[self.activeConfigSetId].input[varData.var] = state
|
||||
self:AddUndoState()
|
||||
self:BuildModList()
|
||||
self.build.buildFlag = true
|
||||
@@ -151,9 +170,9 @@ local ConfigTabClass = newClass("ConfigTab", "UndoHandler", "ControlHost", "Cont
|
||||
elseif varData.type == "count" or varData.type == "integer" or varData.type == "countAllowZero" or varData.type == "float" then
|
||||
control = new("EditControl", {"TOPLEFT",lastSection,"TOPLEFT"}, 234, 0, 90, 18, "", nil, (varData.type == "integer" and "^%-%d") or (varData.type == "float" and "^%d.") or "%D", 7, function(buf, placeholder)
|
||||
if placeholder then
|
||||
self.placeholder[varData.var] = tonumber(buf)
|
||||
self.configSets[self.activeConfigSetId].placeholder[varData.var] = tonumber(buf)
|
||||
else
|
||||
self.input[varData.var] = tonumber(buf)
|
||||
self.configSets[self.activeConfigSetId].input[varData.var] = tonumber(buf)
|
||||
self:AddUndoState()
|
||||
self:BuildModList()
|
||||
end
|
||||
@@ -161,7 +180,7 @@ local ConfigTabClass = newClass("ConfigTab", "UndoHandler", "ControlHost", "Cont
|
||||
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.configSets[self.activeConfigSetId].input[varData.var] = value.val
|
||||
self:AddUndoState()
|
||||
self:BuildModList()
|
||||
self.build.buildFlag = true
|
||||
@@ -169,9 +188,9 @@ local ConfigTabClass = newClass("ConfigTab", "UndoHandler", "ControlHost", "Cont
|
||||
elseif varData.type == "text" and not varData.resizable then
|
||||
control = new("EditControl", {"TOPLEFT",lastSection,"TOPLEFT"}, 8, 0, 344, 118, "", nil, "^%C\t\n", nil, function(buf, placeholder)
|
||||
if placeholder then
|
||||
self.placeholder[varData.var] = tostring(buf)
|
||||
self.configSets[self.activeConfigSetId].placeholder[varData.var] = tostring(buf)
|
||||
else
|
||||
self.input[varData.var] = tostring(buf)
|
||||
self.configSets[self.activeConfigSetId].input[varData.var] = tostring(buf)
|
||||
self:AddUndoState()
|
||||
self:BuildModList()
|
||||
end
|
||||
@@ -180,9 +199,9 @@ local ConfigTabClass = newClass("ConfigTab", "UndoHandler", "ControlHost", "Cont
|
||||
elseif varData.type == "text" and varData.resizable then
|
||||
control = new("ResizableEditControl", {"TOPLEFT",lastSection,"TOPLEFT"}, 8, 0, nil, 344, nil, nil, 118, 118 + 16 * 40, "", nil, "^%C\t\n", nil, function(buf, placeholder)
|
||||
if placeholder then
|
||||
self.placeholder[varData.var] = tostring(buf)
|
||||
self.configSets[self.activeConfigSetId].placeholder[varData.var] = tostring(buf)
|
||||
else
|
||||
self.input[varData.var] = tostring(buf)
|
||||
self.configSets[self.activeConfigSetId].input[varData.var] = tostring(buf)
|
||||
self:AddUndoState()
|
||||
self:BuildModList()
|
||||
end
|
||||
@@ -242,7 +261,7 @@ local ConfigTabClass = newClass("ConfigTab", "UndoHandler", "ControlHost", "Cont
|
||||
end
|
||||
if varData.ifOption then
|
||||
t_insert(shownFuncs, listOrSingleIfOption(varData.ifOption, function(ifOption)
|
||||
return self.input[ifOption]
|
||||
return self.configSets[self.activeConfigSetId].input[ifOption]
|
||||
end))
|
||||
end
|
||||
if varData.ifCond then
|
||||
@@ -505,13 +524,13 @@ local ConfigTabClass = newClass("ConfigTab", "UndoHandler", "ControlHost", "Cont
|
||||
t_insert(self.controls, labelControl)
|
||||
end
|
||||
if varData.var then
|
||||
self.input[varData.var] = varData.defaultState
|
||||
self.configSets[self.activeConfigSetId].input[varData.var] = varData.defaultState
|
||||
control.state = varData.defaultState
|
||||
self.varControls[varData.var] = control
|
||||
self.placeholder[varData.var] = varData.defaultPlaceholderState
|
||||
self.configSets[self.activeConfigSetId].placeholder[varData.var] = varData.defaultPlaceholderState
|
||||
control.placeholder = varData.defaultPlaceholderState
|
||||
if varData.defaultIndex then
|
||||
self.input[varData.var] = varData.list[varData.defaultIndex].val
|
||||
self.configSets[self.activeConfigSetId].input[varData.var] = varData.list[varData.defaultIndex].val
|
||||
control.selIndex = varData.defaultIndex
|
||||
end
|
||||
if varData.type == "check" then
|
||||
@@ -531,7 +550,7 @@ local ConfigTabClass = newClass("ConfigTab", "UndoHandler", "ControlHost", "Cont
|
||||
if not varData.doNotHighlight then
|
||||
control.borderFunc = function()
|
||||
local shown = type(innerShown) == "boolean" and innerShown or innerShown()
|
||||
local cur = self.input[varData.var]
|
||||
local cur = self.configSets[self.activeConfigSetId].input[varData.var]
|
||||
local def = self:GetDefaultState(varData.var, type(cur))
|
||||
if cur ~= nil and cur ~= def then
|
||||
if not shown then
|
||||
@@ -549,14 +568,14 @@ local ConfigTabClass = newClass("ConfigTab", "UndoHandler", "ControlHost", "Cont
|
||||
return false
|
||||
end
|
||||
local shown = type(innerShown) == "boolean" and innerShown or innerShown()
|
||||
local cur = self.input[varData.var]
|
||||
local cur = self.configSets[self.activeConfigSetId].input[varData.var]
|
||||
local def = self:GetDefaultState(varData.var, type(cur))
|
||||
return not shown and cur ~= nil and cur ~= def or shown
|
||||
end
|
||||
local innerLabel = labelControl.label
|
||||
labelControl.label = function()
|
||||
local shown = type(innerShown) == "boolean" and innerShown or innerShown()
|
||||
local cur = self.input[varData.var]
|
||||
local cur = self.configSets[self.activeConfigSetId].input[varData.var]
|
||||
local def = self:GetDefaultState(varData.var, type(cur))
|
||||
if not shown and cur ~= nil and cur ~= def then
|
||||
return colorCodes.NEGATIVE..StripEscapes(innerLabel)
|
||||
@@ -577,7 +596,7 @@ local ConfigTabClass = newClass("ConfigTab", "UndoHandler", "ControlHost", "Cont
|
||||
end
|
||||
|
||||
local shown = type(innerShown) == "boolean" and innerShown or innerShown()
|
||||
local cur = self.input[varData.var]
|
||||
local cur = self.configSets[self.activeConfigSetId].input[varData.var]
|
||||
local def = self:GetDefaultState(varData.var, type(cur))
|
||||
if not shown and cur ~= nil and cur ~= def then
|
||||
tooltip:AddLine(14, colorCodes.NEGATIVE.."This config option is conditional with missing source and is invalid.")
|
||||
@@ -593,26 +612,30 @@ local ConfigTabClass = newClass("ConfigTab", "UndoHandler", "ControlHost", "Cont
|
||||
end)
|
||||
|
||||
function ConfigTabClass:Load(xml, fileName)
|
||||
for _, node in ipairs(xml) do
|
||||
self.activeConfigSetId = 1
|
||||
self.configSets = { }
|
||||
self.configSetOrderList = { 1 }
|
||||
|
||||
local function setInputAndPlaceholder(node, configSetId)
|
||||
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)
|
||||
self.configSets[configSetId].input[node.attrib.name] = tonumber(node.attrib.number)
|
||||
elseif node.attrib.string then
|
||||
if node.attrib.name == "enemyIsBoss" then
|
||||
self.input[node.attrib.name] = node.attrib.string:lower():gsub("(%l)(%w*)", function(a,b) return s_upper(a)..b end)
|
||||
self.configSets[configSetId].input[node.attrib.name] = node.attrib.string:lower():gsub("(%l)(%w*)", function(a,b) return s_upper(a)..b end)
|
||||
:gsub("Uber Atziri", "Boss"):gsub("Shaper", "Pinnacle"):gsub("Sirus", "Pinnacle")
|
||||
-- backwards compat <=3.20, Uber Atziri Flameblast -> Atziri Flameblast
|
||||
elseif node.attrib.name == "presetBossSkills" then
|
||||
self.input[node.attrib.name] = node.attrib.string:gsub("^Uber ", "")
|
||||
self.configSets[configSetId].input[node.attrib.name] = node.attrib.string:gsub("^Uber ", "")
|
||||
else
|
||||
self.input[node.attrib.name] = node.attrib.string
|
||||
self.configSets[configSetId].input[node.attrib.name] = node.attrib.string
|
||||
end
|
||||
elseif node.attrib.boolean then
|
||||
self.input[node.attrib.name] = node.attrib.boolean == "true"
|
||||
self.configSets[configSetId].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
|
||||
@@ -623,23 +646,39 @@ function ConfigTabClass:Load(xml, fileName)
|
||||
return true
|
||||
end
|
||||
if node.attrib.number then
|
||||
self.placeholder[node.attrib.name] = tonumber(node.attrib.number)
|
||||
self.configSets[configSetId].placeholder[node.attrib.name] = tonumber(node.attrib.number)
|
||||
elseif node.attrib.string then
|
||||
self.input[node.attrib.name] = node.attrib.string
|
||||
self.configSets[configSetId].input[node.attrib.name] = node.attrib.string
|
||||
else
|
||||
launch:ShowErrMsg("^1Error parsing '%s': 'Placeholder' element missing number", fileName)
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
self:BuildModList()
|
||||
self:UpdateControls()
|
||||
for index, node in ipairs(xml) do
|
||||
if node.elem ~= "ConfigSet" then
|
||||
if not self.configSets[1] then
|
||||
self:NewConfigSet(1, "Default")
|
||||
end
|
||||
setInputAndPlaceholder(node, 1)
|
||||
else
|
||||
local configSetId = tonumber(node.attrib.id)
|
||||
self:NewConfigSet(configSetId, node.attrib.title or "Default")
|
||||
self.configSetOrderList[index] = configSetId
|
||||
for _, child in ipairs(node) do
|
||||
setInputAndPlaceholder(child, configSetId)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
self:SetActiveConfigSet(tonumber(xml.attrib.activeConfigSet) or 1)
|
||||
self:ResetUndo()
|
||||
self.build:SyncLoadouts()
|
||||
end
|
||||
|
||||
function ConfigTabClass:GetDefaultState(var, varType)
|
||||
if self.placeholder[var] ~= nil then
|
||||
return self.placeholder[var]
|
||||
if self.configSets[self.activeConfigSetId].placeholder[var] ~= nil then
|
||||
return self.configSets[self.activeConfigSetId].placeholder[var]
|
||||
end
|
||||
|
||||
if self.defaultState[var] ~= nil then
|
||||
@@ -658,41 +697,50 @@ function ConfigTabClass:GetDefaultState(var, varType)
|
||||
end
|
||||
|
||||
function ConfigTabClass:Save(xml)
|
||||
for k, v in pairs(self.input) do
|
||||
if v ~= self:GetDefaultState(k, type(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
|
||||
for k, v in pairs(self.placeholder) do
|
||||
local child = { elem = "Placeholder", attrib = { name = k } }
|
||||
if type(v) == "number" then
|
||||
child.attrib.number = tostring(v)
|
||||
else
|
||||
child.attrib.string = tostring(v)
|
||||
end
|
||||
xml.attrib = {
|
||||
activeConfigSet = tostring(self.activeConfigSetId)
|
||||
}
|
||||
for _, configSetId in ipairs(self.configSetOrderList) do
|
||||
local configSet = self.configSets[configSetId]
|
||||
local child = { elem = "ConfigSet", attrib = { id = tostring(configSetId), title = configSet.title } }
|
||||
t_insert(xml, child)
|
||||
|
||||
for k, v in pairs(configSet.input) do
|
||||
if v ~= self:GetDefaultState(k, type(v)) then
|
||||
local node = { elem = "Input", attrib = { name = k } }
|
||||
if type(v) == "number" then
|
||||
node.attrib.number = tostring(v)
|
||||
elseif type(v) == "boolean" then
|
||||
node.attrib.boolean = tostring(v)
|
||||
else
|
||||
node.attrib.string = tostring(v)
|
||||
end
|
||||
t_insert(child, node)
|
||||
end
|
||||
end
|
||||
for k, v in pairs(configSet.placeholder) do
|
||||
local node = { elem = "Placeholder", attrib = { name = k } }
|
||||
if type(v) == "number" then
|
||||
node.attrib.number = tostring(v)
|
||||
else
|
||||
node.attrib.string = tostring(v)
|
||||
end
|
||||
t_insert(child, node)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function ConfigTabClass:UpdateControls()
|
||||
for var, control in pairs(self.varControls) do
|
||||
if control._className == "EditControl" or control._className == "ResizableEditControl" then
|
||||
control:SetText(tostring(self.input[var] or ""))
|
||||
if self.placeholder[var] then
|
||||
control:SetPlaceholder(tostring(self.placeholder[var]))
|
||||
control:SetText(tostring(self.configSets[self.activeConfigSetId].input[var] or ""))
|
||||
if self.configSets[self.activeConfigSetId].placeholder[var] then
|
||||
control:SetPlaceholder(tostring(self.configSets[self.activeConfigSetId].placeholder[var]))
|
||||
end
|
||||
elseif control._className == "CheckBoxControl" then
|
||||
control.state = self.input[var]
|
||||
control.state = self.configSets[self.activeConfigSetId].input[var]
|
||||
elseif control._className == "DropDownControl" then
|
||||
control:SelByValue(self.input[var], "val")
|
||||
control:SelByValue(self.configSets[self.activeConfigSetId].input[var] or self:GetDefaultState(var), "val")
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -735,7 +783,7 @@ function ConfigTabClass:Draw(viewPort, inputEvents)
|
||||
local y = 14
|
||||
section.shown = true
|
||||
local doShow = false
|
||||
for _, varControl in ipairs(section.varControlList) do
|
||||
for _, varControl in pairs(section.varControlList) do
|
||||
if varControl:IsShown() then
|
||||
doShow = true
|
||||
local width, height = varControl:GetSize()
|
||||
@@ -766,9 +814,19 @@ function ConfigTabClass:Draw(viewPort, inputEvents)
|
||||
maxColY = m_max(maxColY, colY[col])
|
||||
end
|
||||
end
|
||||
|
||||
local newSetList = { }
|
||||
for index, configSetId in ipairs(self.configSetOrderList) do
|
||||
local configSet = self.configSets[configSetId]
|
||||
t_insert(newSetList, configSet.title or "Default")
|
||||
if configSetId == self.activeConfigSetId then
|
||||
self.controls.setSelect.selIndex = index
|
||||
end
|
||||
end
|
||||
self.controls.setSelect:SetList(newSetList)
|
||||
|
||||
self.controls.scrollBar.height = viewPort.height
|
||||
self.controls.scrollBar:SetContentDimension(maxColY + 30, viewPort.height)
|
||||
self.controls.scrollBar:SetContentDimension(maxColY + 58, viewPort.height)
|
||||
self.controls.sectionAnchor.y = 20 - self.controls.scrollBar.offset
|
||||
|
||||
main:DrawBackground(viewPort)
|
||||
@@ -777,8 +835,8 @@ function ConfigTabClass:Draw(viewPort, inputEvents)
|
||||
end
|
||||
|
||||
function ConfigTabClass:UpdateLevel()
|
||||
local input = self.input
|
||||
local placeholder = self.placeholder
|
||||
local input = self.configSets[self.activeConfigSetId].input
|
||||
local placeholder = self.configSets[self.activeConfigSetId].placeholder
|
||||
if input.enemyLevel and input.enemyLevel > 0 then
|
||||
self.enemyLevel = m_min(data.misc.MaxEnemyLevel, input.enemyLevel)
|
||||
elseif placeholder.enemyLevel and placeholder.enemyLevel > 0 then
|
||||
@@ -793,8 +851,8 @@ function ConfigTabClass:BuildModList()
|
||||
self.modList = modList
|
||||
local enemyModList = new("ModList")
|
||||
self.enemyModList = enemyModList
|
||||
local input = self.input
|
||||
local placeholder = self.placeholder
|
||||
local input = self.configSets[self.activeConfigSetId].input
|
||||
local placeholder = self.configSets[self.activeConfigSetId].placeholder
|
||||
self:UpdateLevel() -- enemy level handled here because it's needed to correctly set boss stats
|
||||
for _, varData in ipairs(varList) do
|
||||
if varData.apply then
|
||||
@@ -822,7 +880,7 @@ function ConfigTabClass:BuildModList()
|
||||
end
|
||||
|
||||
function ConfigTabClass:ImportCalcSettings()
|
||||
local input = self.input
|
||||
local input = self.configSets[self.activeConfigSetId].input
|
||||
local calcsInput = self.build.calcsTab.input
|
||||
local function import(old, new)
|
||||
input[new] = calcsInput[old]
|
||||
@@ -859,14 +917,73 @@ function ConfigTabClass:ImportCalcSettings()
|
||||
end
|
||||
|
||||
function ConfigTabClass:CreateUndoState()
|
||||
return copyTable(self.input)
|
||||
return copyTable(self.configSets[self.activeConfigSetId].input)
|
||||
end
|
||||
|
||||
function ConfigTabClass:RestoreUndoState(state)
|
||||
wipeTable(self.input)
|
||||
wipeTable(self.configSets[self.activeConfigSetId].input)
|
||||
for k, v in pairs(state) do
|
||||
self.input[k] = v
|
||||
self.configSets[self.activeConfigSetId].input[k] = v
|
||||
end
|
||||
self:UpdateControls()
|
||||
self:BuildModList()
|
||||
end
|
||||
|
||||
function ConfigTabClass:OpenConfigSetManagePopup()
|
||||
main:OpenPopup(370, 290, "Manage Config Sets", {
|
||||
new("ConfigSetListControl", nil, 0, 50, 350, 200, self),
|
||||
new("ButtonControl", nil, 0, 260, 90, 20, "Done", function()
|
||||
main:ClosePopup()
|
||||
end),
|
||||
})
|
||||
end
|
||||
|
||||
-- Creates a new config set
|
||||
function ConfigTabClass:NewConfigSet(configSetId, title)
|
||||
local configSet = { id = configSetId, title = title, input = { }, placeholder = { } }
|
||||
if not configSetId then
|
||||
configSet.id = 1
|
||||
while self.configSets[configSet.id] do
|
||||
configSet.id = configSet.id + 1
|
||||
end
|
||||
end
|
||||
-- there are default values for input and placeholder that every new config set needs to have
|
||||
for _, varData in ipairs(varList) do
|
||||
if varData.var then
|
||||
configSet.input[varData.var] = varData.defaultState
|
||||
configSet.placeholder[varData.var] = varData.defaultPlaceholderState
|
||||
if varData.defaultIndex then
|
||||
configSet.input[varData.var] = varData.list[varData.defaultIndex].val
|
||||
end
|
||||
end
|
||||
end
|
||||
self.configSets[configSet.id] = configSet
|
||||
return configSet
|
||||
end
|
||||
|
||||
-- Changes the active config set
|
||||
function ConfigTabClass:SetActiveConfigSet(configSetId, init)
|
||||
-- Initialize config sets if needed
|
||||
if not self.configSetOrderList[1] then
|
||||
self.configSetOrderList[1] = 1
|
||||
self:NewConfigSet(1)
|
||||
end
|
||||
|
||||
if not configSetId then
|
||||
configSetId = self.activeConfigSetId
|
||||
end
|
||||
|
||||
if not self.configSets[configSetId] then
|
||||
configSetId = self.configSetOrderList[1]
|
||||
end
|
||||
|
||||
self.input = self.configSets[configSetId].input
|
||||
self.placeholder = self.configSets[configSetId].placeholder
|
||||
self.activeConfigSetId = configSetId
|
||||
|
||||
if not init then
|
||||
self:UpdateControls()
|
||||
self:BuildModList()
|
||||
end
|
||||
self.build.buildFlag = true
|
||||
end
|
||||
|
||||
@@ -128,6 +128,7 @@ function ItemSetListClass:OnSelDelete(index, itemSetId)
|
||||
self.itemsTab:SetActiveItemSet(self.list[m_max(1, index - 1)])
|
||||
end
|
||||
self.itemsTab:AddUndoState()
|
||||
self.itemsTab.build:SyncLoadouts()
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -83,6 +83,7 @@ function PassiveSpecListClass:OnOrderChange()
|
||||
self.treeTab.activeSpec = isValueInArray(self.list, self.treeTab.build.spec)
|
||||
self.treeTab.modFlag = true
|
||||
self:UpdateItemsTabPassiveTreeDropdown()
|
||||
self.treeTab.build:SyncLoadouts()
|
||||
end
|
||||
|
||||
function PassiveSpecListClass:OnSelClick(index, spec, doubleClick)
|
||||
@@ -104,6 +105,7 @@ function PassiveSpecListClass:OnSelDelete(index, spec)
|
||||
end
|
||||
self.treeTab.modFlag = true
|
||||
self:UpdateItemsTabPassiveTreeDropdown()
|
||||
self.treeTab.build:SyncLoadouts()
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -108,6 +108,7 @@ function SkillSetListClass:OnSelDelete(index, skillSetId)
|
||||
self.skillsTab:SetActiveSkillSet(self.list[m_max(1, index - 1)])
|
||||
end
|
||||
self.skillsTab:AddUndoState()
|
||||
self.skillsTab.build:SyncLoadouts()
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -504,6 +504,8 @@ function TreeTabClass:ConvertToVersion(version, remove, success, ignoreRuthlessC
|
||||
if success then
|
||||
main:OpenMessagePopup("Tree Converted", "The tree has been converted to "..treeVersions[version].display..".\nNote that some or all of the passives may have been de-allocated due to changes in the tree.\n\nYou can switch back to the old tree using the tree selector at the bottom left.")
|
||||
end
|
||||
-- on convert, check the names of the sets in case there's a match now
|
||||
self.build:SyncLoadouts()
|
||||
end
|
||||
|
||||
function TreeTabClass:ConvertAllToVersion(version)
|
||||
|
||||
@@ -234,21 +234,21 @@ function buildMode:Init(dbFileName, buildName, buildXML, convertBuild)
|
||||
-- self.buildFlag = true
|
||||
--end)
|
||||
self.controls.buildLoadouts = new("DropDownControl", {"LEFT",self.controls.ascendDrop,"RIGHT"}, 8, 0, 190, 20, {}, function(index, value)
|
||||
if value == "Loadouts:" or value == "-----" then
|
||||
if value == "^7^7Loadouts:" or value == "^7^7-----" then
|
||||
self.controls.buildLoadouts:SetSel(1)
|
||||
return
|
||||
end
|
||||
if value == "Sync" then
|
||||
if value == "^7^7Sync" then
|
||||
self:SyncLoadouts()
|
||||
self.controls.buildLoadouts:SetSel(1)
|
||||
return
|
||||
end
|
||||
if value == "^7Help >>" then
|
||||
if value == "^7^7Help >>" then
|
||||
main:OpenAboutPopup(7)
|
||||
self.controls.buildLoadouts:SetSel(1)
|
||||
return
|
||||
end
|
||||
if value == "New Loadout" then
|
||||
if value == "^7^7New Loadout" then
|
||||
local controls = { }
|
||||
controls.label = new("LabelControl", nil, 0, 20, 0, 16, "^7Enter name for this loadout:")
|
||||
controls.edit = new("EditControl", nil, 0, 40, 350, 20, "New Loadout", nil, nil, 100, function(buf)
|
||||
@@ -269,6 +269,10 @@ function buildMode:Init(dbFileName, buildName, buildXML, convertBuild)
|
||||
t_insert(self.skillsTab.skillSetOrderList, skillSet.id)
|
||||
skillSet.title = loadout
|
||||
|
||||
local configSet = self.configTab:NewConfigSet(#self.configTab.configSets + 1)
|
||||
t_insert(self.configTab.configSetOrderList, configSet.id)
|
||||
configSet.title = loadout
|
||||
|
||||
self:SyncLoadouts()
|
||||
self.modFlag = true
|
||||
main:ClosePopup()
|
||||
@@ -297,37 +301,35 @@ function buildMode:Init(dbFileName, buildName, buildXML, convertBuild)
|
||||
end
|
||||
end
|
||||
end
|
||||
local newItemId = nil
|
||||
for _, itemOrder in ipairs(self.itemsTab.itemSetOrderList) do
|
||||
if value == self.itemsTab.itemSets[itemOrder].title then
|
||||
newItemId = itemOrder
|
||||
else
|
||||
local linkMatch = string.match(value, "%{(%w+)%}")
|
||||
if linkMatch then
|
||||
newItemId = self.itemListSpecialLinks[linkMatch]["setId"]
|
||||
end
|
||||
end
|
||||
end
|
||||
local newSkillId = nil
|
||||
for _, skillOrder in ipairs(self.skillsTab.skillSetOrderList) do
|
||||
if value == self.skillsTab.skillSets[skillOrder].title then
|
||||
newSkillId = skillOrder
|
||||
else
|
||||
local linkMatch = string.match(value, "%{(%w+)%}")
|
||||
if linkMatch then
|
||||
newSkillId = self.skillListSpecialLinks[linkMatch]["setId"]
|
||||
|
||||
-- item, skill, and config sets have identical structure
|
||||
-- return id as soon as it's found
|
||||
local function findSetId(setOrderList, value, sets, setSpecialLinks)
|
||||
for _, setOrder in ipairs(setOrderList) do
|
||||
if value == (sets[setOrder].title or "Default") then
|
||||
return setOrder
|
||||
else
|
||||
local linkMatch = string.match(value, "%{(%w+)%}")
|
||||
if linkMatch then
|
||||
return setSpecialLinks[linkMatch]["setId"]
|
||||
end
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
local newItemId = findSetId(self.itemsTab.itemSetOrderList, value, self.itemsTab.itemSets, self.itemListSpecialLinks)
|
||||
local newSkillId = findSetId(self.skillsTab.skillSetOrderList, value, self.skillsTab.skillSets, self.skillListSpecialLinks)
|
||||
local newConfigId = findSetId(self.configTab.configSetOrderList, value, self.configTab.configSets, self.configListSpecialLinks)
|
||||
|
||||
-- if exact match nor special grouping cannot find setIds, bail
|
||||
if newSpecId == nil or newItemId == nil or newSkillId == nil then
|
||||
if newSpecId == nil or newItemId == nil or newSkillId == nil or newConfigId == nil then
|
||||
return
|
||||
end
|
||||
|
||||
self.treeTab:SetActiveSpec(newSpecId)
|
||||
self.itemsTab:SetActiveItemSet(newItemId)
|
||||
self.skillsTab:SetActiveSkillSet(newSkillId)
|
||||
self.configTab:SetActiveConfigSet(newConfigId)
|
||||
|
||||
self.controls.buildLoadouts:SelByValue(value)
|
||||
end)
|
||||
@@ -889,94 +891,87 @@ end
|
||||
function buildMode:SyncLoadouts(reset)
|
||||
self.controls.buildLoadouts.list = {"No Loadouts"}
|
||||
|
||||
local filteredList = {"Loadouts:"}
|
||||
local filteredList = {"^7^7Loadouts:"}
|
||||
local treeList = {}
|
||||
local itemList = {}
|
||||
local skillList = {}
|
||||
local configList = {}
|
||||
-- used when clicking on the dropdown to set the correct setId for each SetActiveSet()
|
||||
self.treeListSpecialLinks, self.itemListSpecialLinks, self.skillListSpecialLinks = {}, {}, {}
|
||||
self.treeListSpecialLinks, self.itemListSpecialLinks, self.skillListSpecialLinks, self.configListSpecialLinks = {}, {}, {}, {}
|
||||
|
||||
if self.treeTab ~= nil and self.itemsTab ~= nil and self.skillsTab ~= nil then
|
||||
if self.treeTab ~= nil and self.itemsTab ~= nil and self.skillsTab ~= nil and self.configTab ~= nil then
|
||||
local transferTable = {}
|
||||
local sortedTreeListSpecialLinks = {}
|
||||
for id, spec in ipairs(self.treeTab.specList) do
|
||||
local specTitle = spec.title or "Default"
|
||||
t_insert(treeList, (spec.treeVersion ~= latestTreeVersion and ("["..treeVersions[spec.treeVersion].display.."] ") or "")..(specTitle))
|
||||
-- only alphanumeric and comma are allowed in the braces { }
|
||||
local linkIdentifier = string.match(specTitle, "%{([%w,]+)%}")
|
||||
if linkIdentifier then
|
||||
-- iterate over each identifier, delimited by comma, and set the index so we can grab it later
|
||||
-- setId index is the id of the set in the global list needed for SetActive
|
||||
-- setId index is the id of the set in the global list needed for SetActiveSet
|
||||
-- setName is only used for Tree currently and we strip the braces to get the plain name of the set, this is used as the name of the loadout
|
||||
for linkId in string.gmatch(linkIdentifier, "[^%,]+") do
|
||||
transferTable["setId"] = id
|
||||
transferTable["setName"] = string.match(specTitle, "(.+)% {")
|
||||
transferTable["linkId"] = linkId
|
||||
self.treeListSpecialLinks[linkId] = transferTable
|
||||
t_insert(sortedTreeListSpecialLinks, transferTable)
|
||||
transferTable = {}
|
||||
end
|
||||
else
|
||||
t_insert(treeList, (spec.treeVersion ~= latestTreeVersion and ("["..treeVersions[spec.treeVersion].display.."] ") or "")..(specTitle))
|
||||
end
|
||||
end
|
||||
|
||||
-- item, skill, and config sets have identical structure
|
||||
local function identifyLinks(setOrderList, tabSets, setList, specialLinks)
|
||||
for id, set in ipairs(setOrderList) do
|
||||
local setTitle = tabSets[set].title or "Default"
|
||||
local linkIdentifier = string.match(setTitle, "%{([%w,]+)%}")
|
||||
-- this if/else prioritizes group identifier in case the user creates sets with same name AND same identifiers
|
||||
-- result is only the group is recognized and one loadout is created rather than a duplicate from each condition met
|
||||
if linkIdentifier then
|
||||
for linkId in string.gmatch(linkIdentifier, "[^%,]+") do
|
||||
transferTable["setId"] = set
|
||||
transferTable["setName"] = string.match(setTitle, "(.+)% {")
|
||||
specialLinks[linkId] = transferTable
|
||||
transferTable = {}
|
||||
end
|
||||
else
|
||||
t_insert(setList, setTitle)
|
||||
end
|
||||
end
|
||||
end
|
||||
for id, item in ipairs(self.itemsTab.itemSetOrderList) do
|
||||
local itemTitle = self.itemsTab.itemSets[item].title or "Default"
|
||||
t_insert(itemList, itemTitle)
|
||||
local linkIdentifier = string.match(itemTitle, "%{([%w,]+)%}")
|
||||
if linkIdentifier then
|
||||
for linkId in string.gmatch(linkIdentifier, "[^%,]+") do
|
||||
transferTable["setId"] = item
|
||||
transferTable["setName"] = string.match(itemTitle, "(.+)% {")
|
||||
self.itemListSpecialLinks[linkId] = transferTable
|
||||
transferTable = {}
|
||||
end
|
||||
end
|
||||
end
|
||||
for id, skill in ipairs(self.skillsTab.skillSetOrderList) do
|
||||
local skillTitle = self.skillsTab.skillSets[skill].title or "Default"
|
||||
t_insert(skillList, skillTitle)
|
||||
local linkIdentifier = string.match(skillTitle, "%{([%w,]+)%}")
|
||||
if linkIdentifier then
|
||||
for linkId in string.gmatch(linkIdentifier, "[^%,]+") do
|
||||
transferTable["setId"] = skill
|
||||
transferTable["setName"] = string.match(skillTitle, "(.+)% {")
|
||||
self.skillListSpecialLinks[linkId] = transferTable
|
||||
transferTable = {}
|
||||
end
|
||||
end
|
||||
end
|
||||
local duplicateCheck = { }
|
||||
identifyLinks(self.itemsTab.itemSetOrderList, self.itemsTab.itemSets, itemList, self.itemListSpecialLinks)
|
||||
identifyLinks(self.skillsTab.skillSetOrderList, self.skillsTab.skillSets, skillList, self.skillListSpecialLinks)
|
||||
identifyLinks(self.configTab.configSetOrderList, self.configTab.configSets, configList, self.configListSpecialLinks)
|
||||
|
||||
-- loop over all for exact match loadouts
|
||||
for id, tree in ipairs(treeList) do
|
||||
for id, skill in ipairs(skillList) do
|
||||
for id, item in ipairs(itemList) do
|
||||
if (tree == skill and tree == item) then
|
||||
if duplicateCheck[tree] then -- if already seen, re-colour NEGATIVE to alert user of duplicate
|
||||
tree = colorCodes.NEGATIVE..tree
|
||||
for id, config in ipairs(configList) do
|
||||
if (tree == skill and tree == item and tree == config) then
|
||||
t_insert(filteredList, tree)
|
||||
end
|
||||
t_insert(filteredList, tree)
|
||||
duplicateCheck[tree] = true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
-- loop over the identifiers found within braces and set the loadout name to the TreeSet
|
||||
for treeLinkId, tree in pairs(self.treeListSpecialLinks) do
|
||||
for itemLinkId, item in pairs(self.itemListSpecialLinks) do
|
||||
for skillLinkId, skill in pairs(self.skillListSpecialLinks) do
|
||||
if (treeLinkId == skillLinkId and treeLinkId == itemLinkId) then
|
||||
local loadoutName = tree["setName"].." {"..treeLinkId.."}"
|
||||
if duplicateCheck[loadoutName] then
|
||||
loadoutName = colorCodes.NEGATIVE..loadoutName
|
||||
end
|
||||
t_insert(filteredList, loadoutName)
|
||||
duplicateCheck[loadoutName] = true
|
||||
end
|
||||
end
|
||||
for _, tree in ipairs(sortedTreeListSpecialLinks) do
|
||||
local treeLinkId = tree.linkId
|
||||
if (self.itemListSpecialLinks[treeLinkId] and self.skillListSpecialLinks[treeLinkId] and self.configListSpecialLinks[treeLinkId]) then
|
||||
t_insert(filteredList, tree.setName .." {"..treeLinkId.."}")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
t_insert(filteredList, "-----")
|
||||
t_insert(filteredList, "New Loadout")
|
||||
t_insert(filteredList, "Sync")
|
||||
t_insert(filteredList, "^7Help >>")
|
||||
-- giving the options unique formatting so it can not match with user-created sets
|
||||
t_insert(filteredList, "^7^7-----")
|
||||
t_insert(filteredList, "^7^7New Loadout")
|
||||
t_insert(filteredList, "^7^7Sync")
|
||||
t_insert(filteredList, "^7^7Help >>")
|
||||
|
||||
if #filteredList > 0 then
|
||||
self.controls.buildLoadouts.list = filteredList
|
||||
@@ -986,7 +981,7 @@ function buildMode:SyncLoadouts(reset)
|
||||
self.controls.buildLoadouts:SetSel(1)
|
||||
end
|
||||
|
||||
return treeList, itemList, skillList
|
||||
return treeList, itemList, skillList, configList
|
||||
end
|
||||
|
||||
function buildMode:EstimatePlayerProgress()
|
||||
|
||||
@@ -133,15 +133,15 @@ return {
|
||||
-- Section: General options
|
||||
{ section = "General", col = 1 },
|
||||
{ var = "resistancePenalty", type = "list", label = "Resistance penalty:", list = {{val=0,label="None"},{val=-30,label="Act 5 (-30%)"},{val=-60,label="Act 10 (-60%)"}}, defaultIndex = 3 },
|
||||
{ var = "bandit", type = "list", label = "Bandit quest:", tooltipFunc = banditTooltip, list = {{val="None",label="Kill all"},{val="Oak",label="Help Oak"},{val="Kraityn",label="Help Kraityn"},{val="Alira",label="Help Alira"}} },
|
||||
{ var = "pantheonMajorGod", type = "list", label = "Major God:", tooltipFunc = applyPantheonDescription, list = {
|
||||
{ var = "bandit", type = "list", defaultIndex = 1, label = "Bandit quest:", tooltipFunc = banditTooltip, list = {{val="None",label="Kill all"},{val="Oak",label="Help Oak"},{val="Kraityn",label="Help Kraityn"},{val="Alira",label="Help Alira"}} },
|
||||
{ var = "pantheonMajorGod", type = "list", defaultIndex = 1, label = "Major God:", tooltipFunc = applyPantheonDescription, list = {
|
||||
{ label = "Nothing", val = "None" },
|
||||
{ label = "Soul of the Brine King", val = "TheBrineKing" },
|
||||
{ label = "Soul of Lunaris", val = "Lunaris" },
|
||||
{ label = "Soul of Solaris", val = "Solaris" },
|
||||
{ label = "Soul of Arakaali", val = "Arakaali" },
|
||||
} },
|
||||
{ var = "pantheonMinorGod", type = "list", label = "Minor God:", tooltipFunc = applyPantheonDescription, list = {
|
||||
{ var = "pantheonMinorGod", type = "list", defaultIndex = 1, label = "Minor God:", tooltipFunc = applyPantheonDescription, list = {
|
||||
{ label = "Nothing", val = "None" },
|
||||
{ label = "Soul of Gruthkul", val = "Gruthkul" },
|
||||
{ label = "Soul of Yugul", val = "Yugul" },
|
||||
@@ -1946,7 +1946,7 @@ Huge sets the radius to 11.
|
||||
{ var = "enemyArmour", type = "count", label = "Enemy Base Armour:", apply = function(val, modList, enemyModList)
|
||||
enemyModList:NewMod("Armour", "BASE", val, "Config")
|
||||
end },
|
||||
{ var = "presetBossSkills", type = "list", label = "Boss Skill Preset", tooltipFunc = bossSkillsTooltip, list = data.bossSkillsList, apply = function(val, modList, enemyModList, build)
|
||||
{ var = "presetBossSkills", type = "list", defaultIndex = 1, label = "Boss Skill Preset", tooltipFunc = bossSkillsTooltip, list = data.bossSkillsList, apply = function(val, modList, enemyModList, build)
|
||||
if not (val == "None") then
|
||||
local bossData = data.bossSkills[val]
|
||||
local isUber = build.configTab.varControls['enemyIsBoss'].list[build.configTab.varControls['enemyIsBoss'].selIndex].val == "Uber"
|
||||
@@ -2023,14 +2023,14 @@ Huge sets the radius to 11.
|
||||
end
|
||||
end },
|
||||
{ var = "enemyDamageRollRange", type = "integer", label = "Enemy Skill Roll Range %:", ifFlag = "BossSkillActive", tooltip = "The percentage of the roll range the enemy hits for \n eg at 100% the enemy deals its maximum damage", defaultPlaceholderState = 70, hideIfInvalid = true },
|
||||
{ var = "enemyDamageType", type = "list", label = "Enemy Damage Type:", tooltip = "Controls which types of damage the EHP calculation uses:\n\tAverage: uses the Average of all typed damage types (not Untyped)\n\nIf a specific damage type is selected, that will be the only type used.", list = {{val="Average",label="Average"},{val="Untyped",label="Untyped"},{val="Melee",label="Melee"},{val="Projectile",label="Projectile"},{val="Spell",label="Spell"},{val="SpellProjectile",label="Projectile Spell"}} },
|
||||
{ var = "enemyDamageType", type = "list", defaultIndex = 1, label = "Enemy Damage Type:", tooltip = "Controls which types of damage the EHP calculation uses:\n\tAverage: uses the Average of all typed damage types (not Untyped)\n\nIf a specific damage type is selected, that will be the only type used.", list = {{val="Average",label="Average"},{val="Untyped",label="Untyped"},{val="Melee",label="Melee"},{val="Projectile",label="Projectile"},{val="Spell",label="Spell"},{val="SpellProjectile",label="Projectile Spell"}} },
|
||||
{ var = "enemySpeed", type = "integer", label = "Enemy attack / cast time in ms:", defaultPlaceholderState = 700 },
|
||||
{ var = "enemyMultiplierPvpDamage", type = "count", label = "Custom PvP Damage multiplier percent:", ifFlag = "isPvP", tooltip = "This multiplies the damage of a given skill in pvp, for instance any with damage multiplier specific to pvp (from skill or support or item like sire of shards)", apply = function(val, modList, enemyModList)
|
||||
enemyModList:NewMod("MultiplierPvpDamage", "BASE", val, "Config")
|
||||
end },
|
||||
{ var = "enemyCritChance", type = "integer", label = "Enemy critical strike chance:", defaultPlaceholderState = 5 },
|
||||
{ var = "enemyCritDamage", type = "integer", label = "Enemy critical strike multiplier:", defaultPlaceholderState = 30 },
|
||||
{ var = "enemyPhysicalDamage", type = "integer", label = "Enemy Skill Physical Damage:", tooltip = "This overrides the default damage amount used to estimate your damage reduction from armour.\nThe default is 1.5 times the enemy's base damage, which is the same value\nused in-game to calculate the estimate shown on the character sheet."},
|
||||
{ var = "enemyPhysicalDamage", type = "integer", label = "Enemy Skill Physical Damage:", tooltip = "This overrides the default damage amount used to estimate your damage reduction from armour.\nThe default is 1.5 times the enemy's base damage, which is the same value\nused in-game to calculate the estimate shown on the character sheet.", defaultPlaceholderState = 7 },
|
||||
{ var = "enemyPhysicalOverwhelm", type = "integer", label = "Enemy Skill Physical Overwhelm:"},
|
||||
{ var = "enemyLightningDamage", type = "integer", label = "Enemy Skill ^xADAA47Lightning Damage:"},
|
||||
{ var = "enemyLightningPen", type = "integer", label = "Enemy Skill ^xADAA47Lightning Pen:"},
|
||||
|
||||
Reference in New Issue
Block a user