-- Path of Building -- -- Module: Config Tab -- Configuration tab for the current build. -- local launch, main = ... local t_insert = table.insert local m_max = math.max local varList = { { section = "General" }, { var = "enemyLevel", type = "number", label = "Enemy Level:", tooltip = "This overrides the default enemy level used to estimate your hit and evade chances.\nThe default level is your character level, capped at 84, which is the same value\nused in-game to calculate the stats on the character sheet." }, { var = "conditionLowLife", type = "check", label = "Are you always on Low Life?", tooltip = "You will automatically be considered to be on Low Life if you have at least 65% life reserved,\nbut you can use this option to force it if necessary.", apply = function(val, modList, enemyModList) modList:NewMod("Misc", "LIST", { type = "Condition", var = "LowLife" }, "Config") end }, { var = "conditionFullLife", type = "check", label = "Are you always on Full Life?", tooltip = "You will automatically be considered to be on Full Life if you have Chaos Innoculation,\nbut you can use this option to force it if necessary.", apply = function(val, modList, enemyModList) modList:NewMod("Misc", "LIST", { type = "Condition", var = "FullLife" }, "Config") end }, { section = "When In Combat" }, { var = "usePowerCharges", type = "check", label = "Do you use Power Charges?" }, { var = "useFrenzyCharges", type = "check", label = "Do you use Frenzy Charges?" }, { var = "useEnduranceCharges", type = "check", label = "Do you use Endurance Charges?" }, { var = "buffOnslaught", type = "check", label = "Do you have Onslaught?", tooltip = "In addition to allowing any 'while you have Onslaught' modifiers to apply,\nthis will enable the Onslaught buff itself. (20% increased Attack/Cast/Movement Speed)", apply = function(val, modList, enemyModList) modList:NewMod("Misc", "LIST", { type = "Condition", var = "Onslaught" }, "Config", { type = "Condition", var = "Combat" }) end }, { var = "buffUnholyMight", type = "check", label = "Do you have Unholy Might?", tooltip = "This will enable the Unholy Might buff. (Gain 30% of Physical Damage as Extra Chaos Damage)", apply = function(val, modList, enemyModList) modList:NewMod("Misc", "LIST", { type = "Condition", var = "UnholyMight" }, "Config", { type = "Condition", var = "Combat" }) end }, { var = "buffPhasing", type = "check", label = "Do you have Phasing?", apply = function(val, modList, enemyModList) modList:NewMod("Misc", "LIST", { type = "Condition", var = "Phasing" }, "Config", { type = "Condition", var = "Combat" }) end }, { var = "buffFortify", type = "check", label = "Do you have Fortify?", apply = function(val, modList, enemyModList) modList:NewMod("Misc", "LIST", { type = "Condition", var = "Fortify" }, "Config", { type = "Condition", var = "Combat" }) end }, { var = "conditionUsingFlask", type = "check", label = "Do you have a Flask active?", apply = function(val, modList, enemyModList) modList:NewMod("Misc", "LIST", { type = "Condition", var = "UsingFlask" }, "Config", { type = "Condition", var = "Combat" }) end }, { var = "conditionOnConsecratedGround", type = "check", label = "Are you on Consecrated Ground?", apply = function(val, modList, enemyModList) modList:NewMod("Misc", "LIST", { type = "Condition", var = "OnConsecratedGround" }, "Config", { type = "Condition", var = "Combat" }) end }, { var = "conditionHitRecently", type = "check", label = "Have you Hit Recently?", tooltip = "You will automatically be considered to have Hit Recently if your main skill is self-cast,\nbut you can use this option to force it if necessary.", apply = function(val, modList, enemyModList) modList:NewMod("Misc", "LIST", { type = "Condition", var = "HitRecently" }, "Config", { type = "Condition", var = "Combat" }) end }, { var = "conditionCritRecently", type = "check", label = "Have you Crit Recently?", apply = function(val, modList, enemyModList) modList:NewMod("Misc", "LIST", { type = "Condition", var = "CritRecently" }, "Config", { type = "Condition", var = "Combat" }) end }, { var = "conditionKilledRecently", type = "check", label = "Have you Killed Recently?", apply = function(val, modList, enemyModList) modList:NewMod("Misc", "LIST", { type = "Condition", var = "KilledRecently" }, "Config", { type = "Condition", var = "Combat" }) end }, { var = "conditionTotemsKilledRecently", type = "check", label = "Have your Totems Killed Recently?", apply = function(val, modList, enemyModList) modList:NewMod("Misc", "LIST", { type = "Condition", var = "TotemsKilledRecently" }, "Config", { type = "Condition", var = "Combat" }) end }, { var = "conditionBeenHitRecently", type = "check", label = "Have you been Hit Recently?", apply = function(val, modList, enemyModList) modList:NewMod("Misc", "LIST", { type = "Condition", var = "BeenHitRecently" }, "Config", { type = "Condition", var = "Combat" }) end }, { var = "buffPendulum", type = "check", label = "Is Pendulum of Destruction active?", ifNode = 57197, apply = function(val, modList, enemyModList) modList:NewMod("Misc", "LIST", { type = "Condition", var = "PendulumOfDestruction" }, "Config", { type = "Condition", var = "Combat" }) end }, { var = "conditionAttackedRecently", type = "check", label = "Have you Attacked Recently?", ifNode = 3154, tooltip = "You will automatically be considered to have Attacked Recently if your main skill is an attack,\nbut you can use this option to force it if necessary.", apply = function(val, modList, enemyModList) modList:NewMod("Misc", "LIST", { type = "Condition", var = "AttackedRecently" }, "Config", { type = "Condition", var = "Combat" }) end }, { var = "conditionCastSpellRecently", type = "check", label = "Have you Cast a Spell Recently?", ifNode = 3154, tooltip = "You will automatically be considered to have Cast a Spell Recently if your main skill is a spell,\nbut you can use this option to force it if necessary.", apply = function(val, modList, enemyModList) modList:NewMod("Misc", "LIST", { type = "Condition", var = "CastSpellRecently" }, "Config", { type = "Condition", var = "Combat" }) end }, { var = "conditionUsedFireSkillInPast10Sec", type = "check", label = "Used a Fire Skill in the past 10s?", ifNode = 61259, apply = function(val, modList, enemyModList) modList:NewMod("Misc", "LIST", { type = "Condition", var = "UsedFireSkillInPast10Sec" }, "Config", { type = "Condition", var = "Combat" }) end }, { var = "conditionUsedColdSkillInPast10Sec", type = "check", label = "Used a Cold Skill in the past 10s?", ifNode = 61259, apply = function(val, modList, enemyModList) modList:NewMod("Misc", "LIST", { type = "Condition", var = "UsedColdSkillInPast10Sec" }, "Config", { type = "Condition", var = "Combat" }) end }, { var = "conditionUsedLightningSkillInPast10Sec", type = "check", label = "Used a Light. Skill in the past 10s?", ifNode = 61259, apply = function(val, modList, enemyModList) modList:NewMod("Misc", "LIST", { type = "Condition", var = "UsedLightningSkillInPast10Sec" }, "Config", { type = "Condition", var = "Combat" }) end }, { section = "For Effective DPS" }, { var = "conditionEnemyFullLife", type = "check", label = "Is the enemy on Full Life?", apply = function(val, modList, enemyModList) modList:NewMod("Misc", "LIST", { type = "Condition", var = "EnemyFullLife" }, "Config", { type = "Condition", var = "Effective" }) end }, { var = "conditionEnemyLowLife", type = "check", label = "Is the enemy on Low Life?", apply = function(val, modList, enemyModList) modList:NewMod("Misc", "LIST", { type = "Condition", var = "EnemyLowLife" }, "Config", { type = "Condition", var = "Effective" }) end }, { var = "conditionEnemyCursed", type = "check", label = "Is the enemy Cursed?", tooltip = "Your enemy will automatically be considered to be Cursed if you have at least one curse enabled,\nbut you can use this option to force it if necessary.", apply = function(val, modList, enemyModList) modList:NewMod("Misc", "LIST", { type = "Condition", var = "EnemyCursed" }, "Config", { type = "Condition", var = "Effective" }) end }, { var = "conditionEnemyBleeding", type = "check", label = "Is the enemy Bleeding?", apply = function(val, modList, enemyModList) modList:NewMod("Misc", "LIST", { type = "Condition", var = "EnemyBleeding" }, "Config", { type = "Condition", var = "Effective" }) end }, { var = "conditionEnemyPoisoned", type = "check", label = "Is the enemy Poisoned?", apply = function(val, modList, enemyModList) modList:NewMod("Misc", "LIST", { type = "Condition", var = "EnemyPoisoned" }, "Config", { type = "Condition", var = "Effective" }) end }, { var = "conditionEnemyMaimed", type = "check", label = "Is the enemy Maimed?", apply = function(val, modList, enemyModList) modList:NewMod("Misc", "LIST", { type = "Condition", var = "EnemyMaimed" }, "Config", { type = "Condition", var = "Effective" }) end }, { var = "conditionEnemyBurning", type = "check", label = "Is the enemy Burning?", apply = function(val, modList, enemyModList) modList:NewMod("Misc", "LIST", { type = "Condition", var = "EnemyBurning" }, "Config", { type = "Condition", var = "Effective" }) end }, { var = "conditionEnemyIgnited", type = "check", label = "Is the enemy Ignited?", tooltip = "This also implies that the enemy is Burning.", apply = function(val, modList, enemyModList) modList:NewMod("Misc", "LIST", { type = "Condition", var = "EnemyIgnited" }, "Config", { type = "Condition", var = "Effective" }) end }, { var = "conditionEnemyChilled", type = "check", label = "Is the enemy Chilled?", apply = function(val, modList, enemyModList) modList:NewMod("Misc", "LIST", { type = "Condition", var = "EnemyChilled" }, "Config", { type = "Condition", var = "Effective" }) end }, { var = "conditionEnemyFrozen", type = "check", label = "Is the enemy Frozen?", apply = function(val, modList, enemyModList) modList:NewMod("Misc", "LIST", { type = "Condition", var = "EnemyFrozen" }, "Config", { type = "Condition", var = "Effective" }) end }, { var = "conditionEnemyShocked", type = "check", label = "Is the enemy Shocked?", tooltip = "In addition to allowing any 'against Shocked Enemies' modifiers to apply,\nthis will apply Shock's Damage Taken modifier to the enemy.", apply = function(val, modList, enemyModList) modList:NewMod("Misc", "LIST", { type = "Condition", var = "EnemyShocked" }, "Config", { type = "Condition", var = "Effective" }) end }, { var = "enemyIsBoss", type = "check", label = "Is the enemy a Boss?", tooltip = "This adds the following modifiers:\n60% less Effect of your Curses\n+30% to enemy Elemental Resistances\n+15% to enemy Chaos Resistance", apply = function(val, modList, enemyModList) enemyModList:NewMod("CurseEffect", "MORE", -60, "Boss") enemyModList:NewMod("ElementalResist", "BASE", 30, "Boss") enemyModList:NewMod("ChaosResist", "BASE", 15, "Boss") end }, { var = "enemyPhysicalReduction", type = "number", label = "Enemy Phys. Damage Reduction:", apply = function(val, modList, enemyModList) enemyModList:NewMod("PhysicalDamageReduction", "INC", val, "Config") end }, { var = "enemyFireResist", type = "number", label = "Enemy Fire Resistance:", apply = function(val, modList, enemyModList) enemyModList:NewMod("FireResist", "BASE", val, "Config") end }, { var = "enemyColdResist", type = "number", label = "Enemy Cold Resistance:", apply = function(val, modList, enemyModList) enemyModList:NewMod("ColdResist", "BASE", val, "Config") end }, { var = "enemyLightningResist", type = "number", label = "Enemy Lightning Resistance:", apply = function(val, modList, enemyModList) enemyModList:NewMod("LightningResist", "BASE", val, "Config") end }, { var = "enemyChaosResist", type = "number", label = "Enemy Chaos Resistance:", apply = function(val, modList, enemyModList) enemyModList:NewMod("ChaosResist", "BASE", val, "Config") end }, { var = "enemyConditionHitByFireDamage", type = "check", label = "Enemy was Hit by Fire Damage?", ifNode = 39085, apply = function(val, modList, enemyModList) modList:NewMod("Misc", "LIST", { type = "EnemyCondition", var = "HitByFireDamage" }, "Config") end }, { var = "enemyConditionHitByColdDamage", type = "check", label = "Enemy was Hit by Cold Damage?", ifNode = 39085, apply = function(val, modList, enemyModList) modList:NewMod("Misc", "LIST", { type = "EnemyCondition", var = "HitByColdDamage" }, "Config") end }, { var = "enemyConditionHitByLightningDamage", type = "check", label = "Enemy was Hit by Light. Damage?", ifNode = 39085, apply = function(val, modList, enemyModList) modList:NewMod("Misc", "LIST", { type = "EnemyCondition", var = "HitByLightningDamage" }, "Config") end }, } local ConfigTabClass = common.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 = common.New("SectionControl", {"TOPLEFT",self,"TOPLEFT"}, 0, 0, 300, 0, varData.section) lastSection.varControlList = { } 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) else local control if varData.type == "check" then control = common.New("CheckBoxControl", {"TOPLEFT",lastSection,"TOPLEFT"}, 216, 0, 18, nil, function(state) self.input[varData.var] = state self:AddUndoState() self:BuildModList() self.build.buildFlag = true end) elseif varData.type == "number" then control = common.New("EditControl", {"TOPLEFT",lastSection,"TOPLEFT"}, 216, 0, 50, 18, "", nil, "^%-%d", 4, function(buf) self.input[varData.var] = tonumber(buf) self:AddUndoState() self:BuildModList() self.build.buildFlag = true end) end if varData.ifNode then control.shown = function() return self.build.spec.allocNodes[varData.ifNode] end control.tooltip = function() return "This option is specific to '"..self.build.spec.nodes[varData.ifNode].dn.."'."..(varData.tooltip and "\n"..varData.tooltip or "") end else control.tooltip = varData.tooltip end t_insert(self.controls, common.New("LabelControl", {"RIGHT",control,"LEFT"}, -4, 2, 0, 14, "^7"..varData.label)) self.varControls[varData.var] = control t_insert(self.controls, control) t_insert(lastSection.varControlList, control) end end 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 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 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] end end end function ConfigTabClass:Draw(viewPort, inputEvents) self.x = viewPort.x self.y = viewPort.y self.width = viewPort.width self.height = viewPort.height 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) local colY = { } for _, section in ipairs(self.sectionList) do local y = 14 for _, varControl in ipairs(section.varControlList) do if varControl:IsShown() then varControl.y = y y = y + 20 end end local width, height = section:GetSize() local col = 1 while true do colY[col] = colY[col] or 18 if colY[col] + height + 10 <= viewPort.height then break end col = col + 1 end section.x = 10 + (col - 1) * 310 section.y = colY[col] colY[col] = colY[col] + height + 18 end main:DrawBackground(viewPort) self:DrawControls(viewPort) end function ConfigTabClass:BuildModList() local modList = common.New("ModList") self.modList = modList local enemyModList = common.New("ModList") self.enemyModList = enemyModList local input = self.input for _, varData in ipairs(varList) do if varData.apply then if varData.type == "check" then if input[varData.var] then varData.apply(true, modList, enemyModList) end elseif varData.type == "number" then if input[varData.var] and input[varData.var] ~= 0 then varData.apply(input[varData.var], modList, enemyModList) 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