Add Alchemists Genius Buff Add support for Essence Glutton regen on corpse consume Add support for new Impresence wording Add support for Brewed for Potency cluster notable Add Wither chance for Eternal Suffering cluster notable Add support for Chilling Presence cluster notable Add support for Forbidden Words cluster notable Add support for Lead by Example cluster notable Add support for Disciples cluster notable Add support for many Ailment immune nodes Add support for Overwhelming Odds jewel Add partial support for Rolling Flames jewel Fix The Torrent's Reclamation Implicit
416 lines
13 KiB
Lua
416 lines
13 KiB
Lua
-- Path of Building
|
|
--
|
|
-- Module: Calcs
|
|
-- Manages the calculation system.
|
|
--
|
|
local targetVersion = ...
|
|
|
|
local pairs = pairs
|
|
local ipairs = ipairs
|
|
local t_insert = table.insert
|
|
local t_remove = table.remove
|
|
local s_format = string.format
|
|
|
|
local calcs = { }
|
|
calcs.targetVersion = targetVersion
|
|
calcs.breakdownModule = "Modules/CalcBreakdown"
|
|
LoadModule("Modules/CalcSetup", calcs)
|
|
LoadModule("Modules/CalcPerform", calcs)
|
|
LoadModule("Modules/CalcActiveSkill", calcs)
|
|
LoadModule("Modules/CalcDefence-"..targetVersion, calcs)
|
|
LoadModule("Modules/CalcOffence-"..targetVersion, calcs)
|
|
|
|
-- Print various tables to the console
|
|
local function infoDump(env)
|
|
if env.modDB.parent then
|
|
env.modDB.parent:Print()
|
|
end
|
|
env.modDB:Print()
|
|
if env.minion then
|
|
ConPrintf("=== Minion Mod DB ===")
|
|
env.minion.modDB:Print()
|
|
end
|
|
ConPrintf("=== Enemy Mod DB ===")
|
|
env.enemyDB:Print()
|
|
local mainSkill = env.minion and env.minion.mainSkill or env.player.mainSkill
|
|
ConPrintf("=== Main Skill ===")
|
|
for _, skillEffect in ipairs(mainSkill.effectList) do
|
|
ConPrintf("%s %d/%d", skillEffect.grantedEffect.name, skillEffect.level, skillEffect.quality)
|
|
end
|
|
ConPrintf("=== Main Skill Flags ===")
|
|
ConPrintf("Mod: %s", modLib.formatFlags(mainSkill.skillCfg.flags, ModFlag))
|
|
ConPrintf("Keyword: %s", modLib.formatFlags(mainSkill.skillCfg.keywordFlags, KeywordFlag))
|
|
ConPrintf("=== Main Skill Mods ===")
|
|
mainSkill.skillModList.parent:Print()
|
|
mainSkill.skillModList:Print()
|
|
ConPrintf("=== Main Skill Data ===")
|
|
prettyPrintTable(mainSkill.skillData)
|
|
ConPrintf("== Aux Skills ==")
|
|
for i, aux in ipairs(env.auxSkillList) do
|
|
ConPrintf("Skill #%d:", i)
|
|
for _, skillEffect in ipairs(aux.effectList) do
|
|
ConPrintf(" %s %d/%d", skillEffect.grantedEffect.name, skillEffect.level, skillEffect.quality)
|
|
end
|
|
end
|
|
ConPrintf("== Output Table ==")
|
|
prettyPrintTable(env.player.output)
|
|
end
|
|
|
|
-- Generate a function for calculating the effect of some modification to the environment
|
|
local function getCalculator(build, fullInit, modFunc)
|
|
-- Initialise environment
|
|
local env = calcs.initEnv(build, "CALCULATOR")
|
|
|
|
-- Save a copy of the initial mod database
|
|
local initModDB = new("ModDB")
|
|
initModDB:AddDB(env.modDB)
|
|
initModDB.conditions = copyTable(env.modDB.conditions)
|
|
initModDB.multipliers = copyTable(env.modDB.multipliers)
|
|
local initEnemyDB = new("ModDB")
|
|
initEnemyDB:AddDB(env.enemyDB)
|
|
initEnemyDB.conditions = copyTable(env.enemyDB.conditions)
|
|
initEnemyDB.multipliers = copyTable(env.enemyDB.multipliers)
|
|
|
|
-- Run base calculation pass
|
|
calcs.perform(env)
|
|
local baseOutput = env.player.output
|
|
|
|
env.modDB.parent = initModDB
|
|
env.enemyDB.parent = initEnemyDB
|
|
|
|
return function(...)
|
|
-- Remove mods added during the last pass
|
|
wipeTable(env.modDB.mods)
|
|
wipeTable(env.modDB.conditions)
|
|
wipeTable(env.modDB.multipliers)
|
|
wipeTable(env.enemyDB.mods)
|
|
wipeTable(env.enemyDB.conditions)
|
|
wipeTable(env.enemyDB.multipliers)
|
|
|
|
-- Call function to make modifications to the environment
|
|
modFunc(env, ...)
|
|
|
|
-- Run calculation pass
|
|
calcs.perform(env)
|
|
|
|
return env.player.output
|
|
end, baseOutput
|
|
end
|
|
|
|
-- Get fast calculator for adding tree node modifiers
|
|
function calcs.getNodeCalculator(build)
|
|
return getCalculator(build, true, function(env, nodeList)
|
|
-- Build and merge modifiers for these nodes
|
|
env.modDB:AddList(calcs.buildModListForNodeList(env, nodeList))
|
|
end)
|
|
end
|
|
|
|
-- Get calculator for other changes (adding/removing nodes, items, gems, etc)
|
|
function calcs.getMiscCalculator(build)
|
|
-- Run base calculation pass
|
|
local env = calcs.initEnv(build, "CALCULATOR")
|
|
calcs.perform(env)
|
|
local baseOutput = env.player.output
|
|
|
|
return function(override)
|
|
env = calcs.initEnv(build, "CALCULATOR", override)
|
|
calcs.perform(env)
|
|
return env.player.output
|
|
end, baseOutput
|
|
end
|
|
|
|
-- Build output for display in the side bar or calcs tab
|
|
function calcs.buildOutput(build, mode)
|
|
-- Build output
|
|
local env = calcs.initEnv(build, mode)
|
|
calcs.perform(env)
|
|
|
|
local output = env.player.output
|
|
|
|
if mode == "MAIN" then
|
|
output.ExtraPoints = env.modDB:Sum("BASE", nil, "ExtraPoints")
|
|
|
|
local specCfg = {
|
|
source = "Tree"
|
|
}
|
|
output["Spec:LifeInc"] = env.modDB:Sum("INC", specCfg, "Life")
|
|
output["Spec:ManaInc"] = env.modDB:Sum("INC", specCfg, "Mana")
|
|
output["Spec:ArmourInc"] = env.modDB:Sum("INC", specCfg, "Armour", "ArmourAndEvasion")
|
|
output["Spec:EvasionInc"] = env.modDB:Sum("INC", specCfg, "Evasion", "ArmourAndEvasion")
|
|
output["Spec:EnergyShieldInc"] = env.modDB:Sum("INC", specCfg, "EnergyShield")
|
|
|
|
env.skillsUsed = { }
|
|
for _, activeSkill in ipairs(env.player.activeSkillList) do
|
|
for _, skillEffect in ipairs(activeSkill.effectList) do
|
|
env.skillsUsed[skillEffect.grantedEffect.name] = true
|
|
end
|
|
if activeSkill.minion then
|
|
for _, activeSkill in ipairs(activeSkill.minion.activeSkillList) do
|
|
env.skillsUsed[activeSkill.activeEffect.grantedEffect.id] = true
|
|
end
|
|
end
|
|
end
|
|
|
|
env.conditionsUsed = { }
|
|
env.multipliersUsed = { }
|
|
env.minionConditionsUsed = { }
|
|
env.enemyConditionsUsed = { }
|
|
env.enemyMultipliersUsed = { }
|
|
local function addCond(out, var, mod)
|
|
if not out[var] then
|
|
out[var] = { }
|
|
end
|
|
t_insert(out[var], mod)
|
|
end
|
|
local function addCondTag(out, tag, mod)
|
|
if tag.varList then
|
|
for _, var in ipairs(tag.varList) do
|
|
addCond(out, var, mod)
|
|
end
|
|
else
|
|
addCond(out, tag.var, mod)
|
|
end
|
|
end
|
|
local function addMult(out, var, mod)
|
|
if not out[var] then
|
|
out[var] = { }
|
|
end
|
|
t_insert(out[var], mod)
|
|
end
|
|
local function addMultTag(out, tag, mod)
|
|
if tag.varList then
|
|
for _, var in ipairs(tag.varList) do
|
|
addMult(out, var, mod)
|
|
end
|
|
else
|
|
addMult(out, tag.var, mod)
|
|
end
|
|
end
|
|
local function addModTags(actor, mod)
|
|
for _, tag in ipairs(mod) do
|
|
if tag.type == "IgnoreCond" then
|
|
break
|
|
elseif tag.type == "Condition" then
|
|
if actor == env.player then
|
|
addCondTag(env.conditionsUsed, tag, mod)
|
|
else
|
|
addCondTag(env.minionConditionsUsed, tag, mod)
|
|
end
|
|
elseif tag.type == "ActorCondition" and tag.actor == "enemy" then
|
|
addCondTag(env.enemyConditionsUsed, tag, mod)
|
|
elseif tag.type == "Multiplier" or tag.type == "MultiplierThreshold" then
|
|
if not tag.actor then
|
|
if actor == env.player then
|
|
addMultTag(env.multipliersUsed, tag, mod)
|
|
end
|
|
elseif tag.actor == "enemy" then
|
|
addMultTag(env.enemyMultipliersUsed, tag, mod)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
for _, actor in ipairs({env.player, env.minion}) do
|
|
for modName, modList in pairs(actor.modDB.mods) do
|
|
for _, mod in ipairs(modList) do
|
|
addModTags(actor, mod)
|
|
end
|
|
end
|
|
end
|
|
for _, activeSkill in pairs(env.player.activeSkillList) do
|
|
for _, mod in ipairs(activeSkill.baseSkillModList) do
|
|
addModTags(env.player, mod)
|
|
end
|
|
if activeSkill.minion then
|
|
for _, activeSkill in pairs(activeSkill.minion.activeSkillList) do
|
|
for _, mod in ipairs(activeSkill.baseSkillModList) do
|
|
addModTags(env.minion, mod)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
for modName, modList in pairs(env.enemyDB.mods) do
|
|
for _, mod in ipairs(modList) do
|
|
for _, tag in ipairs(mod) do
|
|
if tag.type == "IgnoreCond" then
|
|
break
|
|
elseif tag.type == "Condition" then
|
|
addCondTag(env.enemyConditionsUsed, tag, mod)
|
|
elseif tag.type == "Multiplier" or tag.type == "MultiplierThreshold" then
|
|
if not tag.actor then
|
|
addMultTag(env.enemyMultipliersUsed, tag, mod)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
-- ConPrintf("=== Cond ===")
|
|
-- ConPrintTable(env.conditionsUsed)
|
|
-- ConPrintf("=== Mult ===")
|
|
-- ConPrintTable(env.multipliersUsed)
|
|
-- ConPrintf("=== Minion Cond ===")
|
|
-- ConPrintTable(env.minionConditionsUsed)
|
|
-- ConPrintf("=== Enemy Cond ===")
|
|
-- ConPrintTable(env.enemyConditionsUsed)
|
|
-- ConPrintf("=== Enemy Mult ===")
|
|
-- ConPrintTable(env.enemyMultipliersUsed)
|
|
elseif mode == "CALCS" then
|
|
local buffList = { }
|
|
local combatList = { }
|
|
local curseList = { }
|
|
if output.PowerCharges > 0 then
|
|
t_insert(combatList, s_format("%d Power Charges", output.PowerCharges))
|
|
end
|
|
if output.FrenzyCharges > 0 then
|
|
t_insert(combatList, s_format("%d Frenzy Charges", output.FrenzyCharges))
|
|
end
|
|
if output.EnduranceCharges > 0 then
|
|
t_insert(combatList, s_format("%d Endurance Charges", output.EnduranceCharges))
|
|
end
|
|
if output.SiphoningCharges > 0 then
|
|
t_insert(combatList, s_format("%d Siphoning Charges", output.SiphoningCharges))
|
|
end
|
|
if output.ChallengerCharges > 0 then
|
|
t_insert(combatList, s_format("%d Challenger Charges", output.ChallengerCharges))
|
|
end
|
|
if output.BlitzCharges > 0 then
|
|
t_insert(combatList, s_format("%d Blitz Charges", output.BlitzCharges))
|
|
end
|
|
if output.InspirationCharges > 0 then
|
|
t_insert(combatList, s_format("%d Inspiration Charges", output.InspirationCharges))
|
|
end
|
|
if output.GhostShrouds > 0 then
|
|
t_insert(combatList, s_format("%d Ghost Shrouds", output.GhostShrouds))
|
|
end
|
|
if output.CrabBarriers > 0 then
|
|
t_insert(combatList, s_format("%d Crab Barriers", output.CrabBarriers))
|
|
end
|
|
if env.modDB:Flag(nil, "Fortify") then
|
|
t_insert(combatList, "Fortify")
|
|
end
|
|
if env.modDB:Flag(nil, "Onslaught") then
|
|
t_insert(combatList, "Onslaught")
|
|
end
|
|
if env.modDB:Flag(nil, "UnholyMight") then
|
|
t_insert(combatList, "Unholy Might")
|
|
end
|
|
if env.modDB:Flag(nil, "Tailwind") then
|
|
t_insert(combatList, "Tailwind")
|
|
end
|
|
if env.modDB:Flag(nil, "Adrenaline") then
|
|
t_insert(combatList, "Adrenaline")
|
|
end
|
|
if env.modDB:Flag(nil, "AlchemistsGenius") then
|
|
t_insert(combatList, "Alchemist's Genius")
|
|
end
|
|
if env.modDB:Flag(nil, "HerEmbrace") then
|
|
t_insert(combatList, "Her Embrace")
|
|
end
|
|
for name in pairs(env.buffs) do
|
|
t_insert(buffList, name)
|
|
end
|
|
if env.modDB:Flag(nil, "Elusive") then
|
|
t_insert(combatList, "Elusive")
|
|
end
|
|
table.sort(buffList)
|
|
env.player.breakdown.SkillBuffs = { modList = { } }
|
|
for _, name in ipairs(buffList) do
|
|
for _, mod in ipairs(env.buffs[name]) do
|
|
local value = env.modDB:EvalMod(mod)
|
|
if value and value ~= 0 then
|
|
t_insert(env.player.breakdown.SkillBuffs.modList, {
|
|
mod = mod,
|
|
value = value,
|
|
})
|
|
end
|
|
end
|
|
end
|
|
env.player.breakdown.SkillDebuffs = { modList = { } }
|
|
for name, modList in pairs(env.debuffs) do
|
|
t_insert(curseList, name)
|
|
end
|
|
table.sort(curseList)
|
|
for index, name in ipairs(curseList) do
|
|
for _, mod in ipairs(env.debuffs[name]) do
|
|
local value = env.enemy.modDB:EvalMod(mod)
|
|
if value and value ~= 0 then
|
|
t_insert(env.player.breakdown.SkillDebuffs.modList, {
|
|
mod = mod,
|
|
value = value,
|
|
})
|
|
end
|
|
end
|
|
local stackCount = env.debuffs[name]:Sum("BASE", nil, "Multiplier:"..name.."Stack")
|
|
if stackCount > 0 then
|
|
curseList[index] = name .. " (" .. stackCount .. " stack" .. (stackCount > 1 and "s" or "") .. ")"
|
|
end
|
|
end
|
|
for _, slot in ipairs(env.curseSlots) do
|
|
t_insert(curseList, slot.name)
|
|
if slot.modList then
|
|
for _, mod in ipairs(slot.modList) do
|
|
local value = env.enemy.modDB:EvalMod(mod)
|
|
if value and value ~= 0 then
|
|
t_insert(env.player.breakdown.SkillDebuffs.modList, {
|
|
mod = mod,
|
|
value = value,
|
|
})
|
|
end
|
|
end
|
|
end
|
|
end
|
|
output.BuffList = table.concat(buffList, ", ")
|
|
output.CombatList = table.concat(combatList, ", ")
|
|
output.CurseList = table.concat(curseList, ", ")
|
|
if env.minion then
|
|
local buffList = { }
|
|
local combatList = { }
|
|
if output.Minion.PowerCharges > 0 then
|
|
t_insert(combatList, s_format("%d Power Charges", output.Minion.PowerCharges))
|
|
end
|
|
if output.Minion.FrenzyCharges > 0 then
|
|
t_insert(combatList, s_format("%d Frenzy Charges", output.Minion.FrenzyCharges))
|
|
end
|
|
if output.Minion.EnduranceCharges > 0 then
|
|
t_insert(combatList, s_format("%d Endurance Charges", output.Minion.EnduranceCharges))
|
|
end
|
|
if env.minion.modDB:Flag(nil, "Fortify") then
|
|
t_insert(combatList, "Fortify")
|
|
end
|
|
if env.minion.modDB:Flag(nil, "Onslaught") then
|
|
t_insert(combatList, "Onslaught")
|
|
end
|
|
if env.minion.modDB:Flag(nil, "UnholyMight") then
|
|
t_insert(combatList, "Unholy Might")
|
|
end
|
|
if env.minion.modDB:Flag(nil, "Tailwind") then
|
|
t_insert(combatList, "Tailwind")
|
|
end
|
|
for name in pairs(env.minionBuffs) do
|
|
t_insert(buffList, name)
|
|
end
|
|
table.sort(buffList)
|
|
env.minion.breakdown.SkillBuffs = { modList = { } }
|
|
for _, name in ipairs(buffList) do
|
|
for _, mod in ipairs(env.minionBuffs[name]) do
|
|
local value = env.minion.modDB:EvalMod(mod)
|
|
if value and value ~= 0 then
|
|
t_insert(env.minion.breakdown.SkillBuffs.modList, {
|
|
mod = mod,
|
|
value = value,
|
|
})
|
|
end
|
|
end
|
|
end
|
|
env.minion.breakdown.SkillDebuffs = env.player.breakdown.SkillDebuffs
|
|
output.Minion.BuffList = table.concat(buffList, ", ")
|
|
output.Minion.CombatList = table.concat(combatList, ", ")
|
|
output.Minion.CurseList = output.CurseList
|
|
end
|
|
|
|
infoDump(env)
|
|
end
|
|
|
|
return env
|
|
end
|
|
|
|
return calcs |