Files
PathOfBuilding/Modules/Calcs.lua
LocalIdentity f6f0e152cb Add parsing for a bunch of nodes
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
2020-07-22 05:11:51 +10:00

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