Files
PathOfBuilding/Modules/CalcPerform.lua
Openarl 8c01c85f1f Release 1.4.12
- Added shared item list
- Added options screen
- Added toasts
- Program now always updates on first run, but continues if update check fails
- Updated libcurl to 7.54.0
2017-05-19 14:50:33 +10:00

723 lines
28 KiB
Lua

-- Path of Building
--
-- Module: Calc Perform
-- Manages the offence/defence calculations.
--
local calcs = ...
local pairs = pairs
local ipairs = ipairs
local t_insert = table.insert
local m_min = math.min
local m_ceil = math.ceil
local m_floor = math.floor
local s_format = string.format
local tempTable1 = { }
-- Merge an instance of a buff, taking the highest value of each modifier
local function mergeBuff(src, destTable, destKey)
if not destTable[destKey] then
destTable[destKey] = { }
end
local dest = destTable[destKey]
for _, mod in ipairs(src) do
local param = modLib.formatModParams(mod)
for index, destMod in ipairs(dest) do
if param == modLib.formatModParams(destMod) then
if type(destMod.value) == "number" and mod.value > destMod.value then
dest[index] = mod
end
param = nil
break
end
end
if param then
t_insert(dest, mod)
end
end
end
-- Calculate attributes and life/mana pools, and set conditions
local function doActorAttribsPoolsConditions(env, actor)
local modDB = actor.modDB
local output = actor.output
local breakdown = actor.breakdown
local condList = modDB.conditions
-- Set conditions
if actor.weaponData1.type == "Staff" then
condList["UsingStaff"] = true
end
if actor.weaponData1.type == "Bow" then
condList["UsingBow"] = true
end
if actor.itemList["Weapon 2"] and actor.itemList["Weapon 2"].type == "Shield" then
condList["UsingShield"] = true
end
if actor.weaponData1.type and actor.weaponData2.type then
condList["DualWielding"] = true
if actor.weaponData1.type == "Claw" and actor.weaponData2.type == "Claw" then
condList["DualWieldingClaws"] = true
end
end
if actor.weaponData1.type == "None" then
condList["Unarmed"] = true
end
if (modDB.multipliers["NormalItem"] or 0) > 0 then
condList["UsingNormalItem"] = true
end
if (modDB.multipliers["MagicItem"] or 0) > 0 then
condList["UsingMagicItem"] = true
end
if (modDB.multipliers["RareItem"] or 0) > 0 then
condList["UsingRareItem"] = true
end
if (modDB.multipliers["UniqueItem"] or 0) > 0 then
condList["UsingUniqueItem"] = true
end
if (modDB.multipliers["CorruptedItem"] or 0) > 0 then
condList["UsingCorruptedItem"] = true
else
condList["NotUsingCorruptedItem"] = true
end
if env.mode_combat then
if not modDB:Sum("FLAG", nil, "NeverCrit") then
condList["CritInPast8Sec"] = true
end
if not actor.mainSkill.skillData.triggered then
if actor.mainSkill.skillFlags.attack then
condList["AttackedRecently"] = true
elseif actor.mainSkill.skillFlags.spell then
condList["CastSpellRecently"] = true
end
end
if actor.mainSkill.skillFlags.hit and not actor.mainSkill.skillFlags.trap and not actor.mainSkill.skillFlags.mine and not actor.mainSkill.skillFlags.totem then
condList["HitRecently"] = true
end
if actor.mainSkill.skillFlags.movement then
condList["UsedMovementSkillRecently"] = true
end
if actor.mainSkill.skillFlags.totem then
condList["HaveTotem"] = true
condList["SummonedTotemRecently"] = true
end
if actor.mainSkill.skillFlags.mine then
condList["DetonatedMinesRecently"] = true
end
end
-- Calculate attributes
for _, stat in pairs({"Str","Dex","Int"}) do
output[stat] = round(calcLib.val(modDB, stat))
if breakdown then
breakdown[stat] = breakdown.simple(nil, nil, output[stat], stat)
end
end
-- Add attribute bonuses
modDB:NewMod("Life", "BASE", m_floor(output.Str / 2), "Strength")
actor.strDmgBonus = round((output.Str + modDB:Sum("BASE", nil, "DexIntToMeleeBonus")) / 5)
modDB:NewMod("PhysicalDamage", "INC", actor.strDmgBonus, "Strength", ModFlag.Melee)
modDB:NewMod("Accuracy", "BASE", output.Dex * 2, "Dexterity")
if not modDB:Sum("FLAG", nil, "IronReflexes") then
modDB:NewMod("Evasion", "INC", round(output.Dex / 5), "Dexterity")
end
modDB:NewMod("Mana", "BASE", round(output.Int / 2), "Intelligence")
modDB:NewMod("EnergyShield", "INC", round(output.Int / 5), "Intelligence")
-- Life/mana pools
if modDB:Sum("FLAG", nil, "ChaosInoculation") then
output.Life = 1
condList["FullLife"] = true
else
local base = modDB:Sum("BASE", nil, "Life")
local inc = modDB:Sum("INC", nil, "Life")
local more = modDB:Sum("MORE", nil, "Life")
local conv = modDB:Sum("BASE", nil, "LifeConvertToEnergyShield")
output.Life = round(base * (1 + inc/100) * more * (1 - conv/100))
if breakdown then
if inc ~= 0 or more ~= 1 or conv ~= 0 then
breakdown.Life = { }
breakdown.Life[1] = s_format("%g ^8(base)", base)
if inc ~= 0 then
t_insert(breakdown.Life, s_format("x %.2f ^8(increased/reduced)", 1 + inc/100))
end
if more ~= 1 then
t_insert(breakdown.Life, s_format("x %.2f ^8(more/less)", more))
end
if conv ~= 0 then
t_insert(breakdown.Life, s_format("x %.2f ^8(converted to Energy Shield)", 1 - conv/100))
end
t_insert(breakdown.Life, s_format("= %g", output.Life))
end
end
end
output.Mana = round(calcLib.val(modDB, "Mana"))
if breakdown then
breakdown.Mana = breakdown.simple(nil, nil, output.Mana, "Mana")
end
-- Life/mana reservation
for _, pool in pairs({"Life", "Mana"}) do
local max = output[pool]
local reserved
if max > 0 then
reserved = (actor["reserved_"..pool.."Base"] or 0) + m_ceil(max * (actor["reserved_"..pool.."Percent"] or 0) / 100)
output[pool.."Reserved"] = reserved
output[pool.."ReservedPercent"] = reserved / max * 100
output[pool.."Unreserved"] = max - reserved
output[pool.."UnreservedPercent"] = (max - reserved) / max * 100
if (max - reserved) / max <= 0.35 then
condList["Low"..pool] = true
end
else
reserved = 0
end
if reserved == 0 then
condList["No"..pool.."Reserved"] = true
end
for _, value in ipairs(modDB:Sum("LIST", nil, "GrantReserved"..pool.."AsAura")) do
local auraMod = copyTable(value.mod)
auraMod.value = m_floor(auraMod.value * m_min(reserved, max))
modDB:NewMod("ExtraAura", "LIST", { mod = auraMod })
end
end
end
-- Process charges, enemy modifiers, and other buffs
local function doActorMisc(env, actor)
local modDB = actor.modDB
local enemyDB = actor.enemy.modDB
local output = actor.output
local condList = modDB.conditions
-- Calculate current and maximum charges
output.PowerChargesMax = modDB:Sum("BASE", nil, "PowerChargesMax")
output.FrenzyChargesMax = modDB:Sum("BASE", nil, "FrenzyChargesMax")
output.EnduranceChargesMax = modDB:Sum("BASE", nil, "EnduranceChargesMax")
if modDB:Sum("FLAG", nil, "UsePowerCharges") then
output.PowerCharges = output.PowerChargesMax
else
output.PowerCharges = 0
end
if modDB:Sum("FLAG", nil, "UseFrenzyCharges") then
output.FrenzyCharges = output.FrenzyChargesMax
else
output.FrenzyCharges = 0
end
if modDB:Sum("FLAG", nil, "UseEnduranceCharges") then
output.EnduranceCharges = output.EnduranceChargesMax
else
output.EnduranceCharges = 0
end
modDB.multipliers["PowerCharge"] = output.PowerCharges
modDB.multipliers["FrenzyCharge"] = output.FrenzyCharges
modDB.multipliers["EnduranceCharge"] = output.EnduranceCharges
if output.PowerCharges == 0 then
condList["HaveNoPowerCharges"] = true
end
if output.PowerCharges == output.PowerChargesMax then
condList["AtMaxPowerCharges"] = true
end
if output.FrenzyCharges == 0 then
condList["HaveNoFrenzyCharges"] = true
end
if output.FrenzyCharges == output.FrenzyChargesMax then
condList["AtMaxFrenzyCharges"] = true
end
if output.EnduranceCharges == 0 then
condList["HaveNoEnduranceCharges"] = true
end
if output.EnduranceCharges == output.EnduranceChargesMax then
condList["AtMaxEnduranceCharges"] = true
end
-- Process enemy modifiers
for _, value in ipairs(modDB:Sum("LIST", nil, "EnemyModifier")) do
enemyDB:AddMod(value.mod)
end
-- Add misc buffs
if env.mode_combat then
if modDB:Sum("FLAG", nil, "Fortify") then
local effect = m_floor(20 * (1 + modDB:Sum("INC", nil, "FortifyEffectOnSelf", "BuffEffectOnSelf") / 100))
modDB:NewMod("DamageTakenWhenHit", "INC", -20, "Fortify")
end
if modDB:Sum("FLAG", nil, "Onslaught") then
local effect = m_floor(20 * (1 + modDB:Sum("INC", nil, "OnslaughtEffect", "BuffEffectOnSelf") / 100))
modDB:NewMod("Speed", "INC", effect, "Onslaught")
modDB:NewMod("MovementSpeed", "INC", effect, "Onslaught")
end
if modDB:Sum("FLAG", nil, "UnholyMight") then
local effect = m_floor(30 * (1 + modDB:Sum("INC", nil, "BuffEffectOnSelf") / 100))
modDB:NewMod("PhysicalDamageGainAsChaos", "BASE", effect, "Unholy Might")
end
end
end
-- Finalises the environment and performs the stat calculations:
-- 1. Merges keystone modifiers
-- 2. Initialises minion skills
-- 3. Initialises the main skill's minion, if present
-- 4. Merges flask effects
-- 5. Calculates reservations
-- 6. Sets conditions and calculates attributes and life/mana pools (doActorAttribsPoolsConditions)
-- 7. Processes buffs and debuffs
-- 8. Processes charges and misc buffs (doActorMisc)
-- 9. Calculates defence and offence stats (calcs.defence, calcs.offence)
function calcs.perform(env)
local modDB = env.modDB
local enemyDB = env.enemyDB
-- Merge keystone modifiers
do
local keystoneList = wipeTable(tempTable1)
for _, name in ipairs(modDB:Sum("LIST", nil, "Keystone")) do
keystoneList[name] = true
end
for name in pairs(keystoneList) do
modDB:AddList(env.build.tree.keystoneMap[name].modList)
end
end
-- Build minion skills
for _, activeSkill in ipairs(env.activeSkillList) do
if activeSkill.minion then
calcs.createMinionSkills(env, activeSkill)
end
end
env.player.output = { }
env.enemy.output = { }
local output = env.player.output
env.minion = env.player.mainSkill.minion
if env.minion then
-- Initialise minion modifier database
output.Minion = { }
env.minion.output = output.Minion
env.minion.enemy = env.enemy
env.minion.modDB = common.New("ModDB")
env.minion.modDB.actor = env.minion
env.minion.modDB.multipliers["Level"] = env.minion.level
calcs.initModDB(env, env.minion.modDB)
env.minion.modDB:NewMod("Life", "BASE", m_floor(data.monsterLifeTable[env.minion.level] * env.minion.minionData.life), "Base")
if env.minion.minionData.energyShield then
env.minion.modDB:NewMod("EnergyShield", "BASE", m_floor(data.monsterLifeTable[env.minion.level] * env.minion.minionData.life * env.minion.minionData.energyShield), "Base")
end
env.minion.modDB:NewMod("Evasion", "BASE", data.monsterEvasionTable[env.minion.level], "Base")
env.minion.modDB:NewMod("Accuracy", "BASE", data.monsterAccuracyTable[env.minion.level], "Base")
env.minion.modDB:NewMod("CritMultiplier", "BASE", 30, "Base")
env.minion.modDB:NewMod("FireResist", "BASE", env.minion.minionData.fireResist, "Base")
env.minion.modDB:NewMod("ColdResist", "BASE", env.minion.minionData.coldResist, "Base")
env.minion.modDB:NewMod("LightningResist", "BASE", env.minion.minionData.lightningResist, "Base")
env.minion.modDB:NewMod("ChaosResist", "BASE", env.minion.minionData.chaosResist, "Base")
env.minion.modDB:NewMod("CritChance", "INC", 200, "Base", { type = "Multiplier", var = "PowerCharge" })
env.minion.modDB:NewMod("Speed", "INC", 15, "Base", { type = "Multiplier", var = "FrenzyCharge" })
env.minion.modDB:NewMod("Damage", "MORE", 4, "Base", { type = "Multiplier", var = "FrenzyCharge" })
env.minion.modDB:NewMod("MovementSpeed", "INC", 5, "Base", { type = "Multiplier", var = "FrenzyCharge" })
env.minion.modDB:NewMod("PhysicalDamageReduction", "BASE", 15, "Base", { type = "Multiplier", var = "EnduranceCharge" })
env.minion.modDB:NewMod("ElementalResist", "BASE", 15, "Base", { type = "Multiplier", var = "EnduranceCharge" })
env.minion.modDB:NewMod("ProjectileCount", "BASE", 1, "Base")
for _, mod in ipairs(env.minion.minionData.modList) do
env.minion.modDB:AddMod(mod)
end
if env.aegisModList then
env.minion.itemList["Weapon 2"] = env.player.itemList["Weapon 2"]
env.player.itemList["Weapon 2"] = nil
env.minion.modDB:AddList(env.aegisModList)
end
if env.player.mainSkill.skillData.minionUseBowAndQuiver then
if env.player.weaponData1.type == "Bow" then
env.minion.modDB:AddList(env.player.itemList["Weapon 1"].slotModList[1])
end
if env.player.itemList["Weapon 2"] and env.player.itemList["Weapon 2"].type == "Quiver" then
env.minion.modDB:AddList(env.player.itemList["Weapon 2"].modList)
end
end
if modDB:Sum("FLAG", nil, "StrengthAddedToMinions") then
env.minion.modDB:NewMod("Str", "BASE", round(calcLib.val(modDB, "Str")), "Player")
end
end
local breakdown
if env.mode == "CALCS" then
-- Initialise breakdown module
breakdown = LoadModule("Modules/CalcBreakdown", modDB, output, env.player)
env.player.breakdown = breakdown
if env.minion then
env.minion.breakdown = LoadModule("Modules/CalcBreakdown", env.minion.modDB, env.minion.output, env.minion)
end
end
-- Merge flask modifiers
if env.mode_combat then
local effectInc = modDB:Sum("INC", nil, "FlaskEffect")
local flaskBuffs = { }
for item in pairs(env.flasks) do
-- Avert thine eyes, lest they be forever scarred
-- I have no idea how to determine which buff is applied by a given flask,
-- so utility flasks are grouped by base, unique flasks are grouped by name, and magic flasks by their modifiers
local effectMod = 1 + (effectInc + item.flaskData.effectInc) / 100
if item.buffModList[1] then
local srcList = common.New("ModList")
srcList:ScaleAddList(item.buffModList, effectMod)
mergeBuff(srcList, flaskBuffs, item.baseName)
end
if item.modList[1] then
local srcList = common.New("ModList")
srcList:ScaleAddList(item.modList, effectMod)
local key
if item.rarity == "UNIQUE" then
key = item.title
else
key = ""
for _, mod in ipairs(item.modList) do
key = key .. modLib.formatModParams(mod) .. "&"
end
end
mergeBuff(srcList, flaskBuffs, key)
end
end
if not modDB:Sum("FLAG", nil, "FlasksDoNotApplyToPlayer") then
for _, buffModList in pairs(flaskBuffs) do
modDB.conditions["UsingFlask"] = true
modDB:AddList(buffModList)
end
end
if env.minion and modDB:Sum("FLAG", env.player.mainSkill.skillCfg, "FlasksApplyToMinion") then
for _, buffModList in pairs(flaskBuffs) do
env.minion.modDB.conditions["UsingFlask"] = true
env.minion.modDB:AddList(buffModList)
end
end
end
-- Calculate skill life and mana reservations
env.player.reserved_LifeBase = 0
env.player.reserved_LifePercent = modDB:Sum("BASE", nil, "ExtraLifeReserved")
env.player.reserved_ManaBase = 0
env.player.reserved_ManaPercent = 0
if breakdown then
breakdown.LifeReserved = { reservations = { } }
breakdown.ManaReserved = { reservations = { } }
end
for _, activeSkill in ipairs(env.activeSkillList) do
if activeSkill.skillTypes[SkillType.ManaCostReserved] and not activeSkill.skillFlags.totem then
local skillModList = activeSkill.skillModList
local skillCfg = activeSkill.skillCfg
local baseVal = activeSkill.skillData.manaCostOverride or activeSkill.skillData.manaCost or 0
local suffix = activeSkill.skillTypes[SkillType.ManaCostPercent] and "Percent" or "Base"
local mult = skillModList:Sum("MORE", skillCfg, "ManaCost")
local more = modDB:Sum("MORE", skillCfg, "ManaReserved") * skillModList:Sum("MORE", skillCfg, "ManaReserved")
local inc = modDB:Sum("INC", skillCfg, "ManaReserved") + skillModList:Sum("INC", skillCfg, "ManaReserved")
local base = m_floor(baseVal * mult)
local cost = base - m_floor(base * -m_floor((100 + inc) * more - 100) / 100)
local pool
if modDB:Sum("FLAG", skillCfg, "BloodMagic", "SkillBloodMagic") or skillModList:Sum("FLAG", skillCfg, "SkillBloodMagic") then
pool = "Life"
else
pool = "Mana"
end
env.player["reserved_"..pool..suffix] = env.player["reserved_"..pool..suffix] + cost
if breakdown then
t_insert(breakdown[pool.."Reserved"].reservations, {
skillName = activeSkill.activeGem.name,
base = baseVal .. (activeSkill.skillTypes[SkillType.ManaCostPercent] and "%" or ""),
mult = mult ~= 1 and ("x "..mult),
more = more ~= 1 and ("x "..more),
inc = inc ~= 0 and ("x "..(1 + inc/100)),
total = cost .. (activeSkill.skillTypes[SkillType.ManaCostPercent] and "%" or ""),
})
end
end
end
-- Calculate attributes and life/mana pools
doActorAttribsPoolsConditions(env, env.player)
if env.minion then
for _, source in ipairs({modDB, env.player.mainSkill.skillModList}) do
for _, value in ipairs(source:Sum("LIST", env.player.mainSkill.skillCfg, "MinionModifier")) do
env.minion.modDB:AddMod(value.mod)
end
end
doActorAttribsPoolsConditions(env, env.minion)
end
-- Check for extra modifiers to apply to aura skills
local extraAuraModList = { }
for _, value in ipairs(modDB:Sum("LIST", nil, "ExtraAuraEffect")) do
t_insert(extraAuraModList, value.mod)
end
-- Combine buffs/debuffs
output.EnemyCurseLimit = modDB:Sum("BASE", nil, "EnemyCurseLimit")
local buffs = { }
local minionBuffs = { }
local debuffs = { }
local curses = {
limit = output.EnemyCurseLimit,
}
local minionCurses = {
limit = 1,
}
local affectedByAura = { }
for _, activeSkill in ipairs(env.activeSkillList) do
local skillModList = activeSkill.skillModList
local skillCfg = activeSkill.skillCfg
if env.mode_buffs then
if activeSkill.buffModList and
not activeSkill.skillFlags.curse and
(not activeSkill.skillFlags.totem or activeSkill.skillData.allowTotemBuff) then
if not activeSkill.skillData.buffNotPlayer then
activeSkill.buffSkill = true
local srcList = common.New("ModList")
local inc = modDB:Sum("INC", skillCfg, "BuffEffect", "BuffEffectOnSelf")
local more = modDB:Sum("MORE", skillCfg, "BuffEffect", "BuffEffectOnSelf")
srcList:ScaleAddList(activeSkill.buffModList, (1 + inc / 100) * more)
mergeBuff(srcList, buffs, activeSkill.activeGem.name)
end
if env.minion and (activeSkill.skillData.buffMinions or activeSkill.skillData.buffAllies) then
activeSkill.minionBuffSkill = true
local srcList = common.New("ModList")
local inc = modDB:Sum("INC", skillCfg, "BuffEffect") + env.minion.modDB:Sum("INC", nil, "BuffEffectOnSelf")
local more = modDB:Sum("MORE", skillCfg, "BuffEffect") * env.minion.modDB:Sum("MORE", nil, "BuffEffectOnSelf")
srcList:ScaleAddList(activeSkill.buffModList, (1 + inc / 100) * more)
mergeBuff(srcList, minionBuffs, activeSkill.activeGem.name)
end
end
if activeSkill.auraModList then
if not activeSkill.skillData.auraCannotAffectSelf then
activeSkill.buffSkill = true
affectedByAura[env.player] = true
local srcList = common.New("ModList")
local inc = modDB:Sum("INC", skillCfg, "AuraEffect", "BuffEffectOnSelf", "AuraEffectOnSelf") + skillModList:Sum("INC", skillCfg, "AuraEffect")
local more = modDB:Sum("MORE", skillCfg, "AuraEffect", "BuffEffectOnSelf", "AuraEffectOnSelf") * skillModList:Sum("MORE", skillCfg, "AuraEffect")
srcList:ScaleAddList(activeSkill.auraModList, (1 + inc / 100) * more)
srcList:ScaleAddList(extraAuraModList, (1 + inc / 100) * more)
mergeBuff(srcList, buffs, activeSkill.activeGem.name)
end
if env.minion and not modDB:Sum("FLAG", nil, "YourAurasCannotAffectAllies") then
activeSkill.minionBuffSkill = true
affectedByAura[env.minion] = true
local srcList = common.New("ModList")
local inc = modDB:Sum("INC", skillCfg, "AuraEffect") + env.minion.modDB:Sum("INC", nil, "BuffEffectOnSelf", "AuraEffectOnSelf") + skillModList:Sum("INC", skillCfg, "AuraEffect")
local more = modDB:Sum("MORE", skillCfg, "AuraEffect") * env.minion.modDB:Sum("MORE", nil, "BuffEffectOnSelf", "AuraEffectOnSelf") * skillModList:Sum("MORE", skillCfg, "AuraEffect")
srcList:ScaleAddList(activeSkill.auraModList, (1 + inc / 100) * more)
srcList:ScaleAddList(extraAuraModList, (1 + inc / 100) * more)
mergeBuff(srcList, minionBuffs, activeSkill.activeGem.name)
end
end
if activeSkill.minion then
for _, activeSkill in ipairs(activeSkill.minion.activeSkillList) do
local skillModList = activeSkill.skillModList
local skillCfg = activeSkill.skillCfg
if activeSkill.auraModList and activeSkill.skillData.enable then
if not modDB:Sum("FLAG", nil, "AlliesAurasCannotAffectSelf") then
local srcList = common.New("ModList")
local inc = modDB:Sum("INC", skillCfg, "BuffEffectOnSelf", "AuraEffectOnSelf") + skillModList:Sum("INC", skillCfg, "AuraEffect")
local more = modDB:Sum("MORE", skillCfg, "BuffEffectOnSelf", "AuraEffectOnSelf") * skillModList:Sum("MORE", skillCfg, "AuraEffect")
srcList:ScaleAddList(activeSkill.auraModList, (1 + inc / 100) * more)
mergeBuff(srcList, buffs, activeSkill.activeGem.data.id)
end
if env.minion and (env.minion ~= activeSkill.minion or not activeSkill.skillData.auraCannotAffectSelf) then
local srcList = common.New("ModList")
local inc = env.minion.modDB:Sum("INC", nil, "BuffEffectOnSelf", "AuraEffectOnSelf") + skillModList:Sum("INC", skillCfg, "AuraEffect")
local more = env.minion.modDB:Sum("MORE", nil, "BuffEffectOnSelf", "AuraEffectOnSelf") * skillModList:Sum("MORE", skillCfg, "AuraEffect")
srcList:ScaleAddList(activeSkill.auraModList, (1 + inc / 100) * more)
mergeBuff(srcList, minionBuffs, activeSkill.activeGem.data.id)
end
end
end
end
end
if env.mode_effective then
if activeSkill.debuffModList then
activeSkill.debuffSkill = true
local srcList = common.New("ModList")
srcList:ScaleAddList(activeSkill.debuffModList, activeSkill.skillData.stackCount or 1)
mergeBuff(srcList, debuffs, activeSkill.activeGem.name)
end
if (activeSkill.curseModList or (activeSkill.skillFlags.curse and activeSkill.buffModList))
and (not enemyDB:Sum("FLAG", nil, "Hexproof") or modDB:Sum("FLAG", nil, "CursesIgnoreHexproof")) then
local curse = {
name = activeSkill.activeGem.name,
fromPlayer = true,
priority = activeSkill.skillTypes[SkillType.Aura] and 3 or 1,
}
local inc = modDB:Sum("INC", skillCfg, "CurseEffect") + enemyDB:Sum("INC", nil, "CurseEffectOnSelf") + skillModList:Sum("INC", skillCfg, "CurseEffect")
local more = modDB:Sum("MORE", skillCfg, "CurseEffect") * enemyDB:Sum("MORE", nil, "CurseEffectOnSelf") * skillModList:Sum("MORE", skillCfg, "CurseEffect")
if activeSkill.curseModList then
curse.modList = common.New("ModList")
curse.modList:ScaleAddList(activeSkill.curseModList, (1 + inc / 100) * more)
end
if activeSkill.buffModList then
-- Curse applies a buff; scale by curse effect, then buff effect
local temp = common.New("ModList")
temp:ScaleAddList(activeSkill.buffModList, (1 + inc / 100) * more)
curse.buffModList = common.New("ModList")
local buffInc = modDB:Sum("INC", skillCfg, "BuffEffectOnSelf")
local buffMore = modDB:Sum("MORE", skillCfg, "BuffEffectOnSelf")
curse.buffModList:ScaleAddList(temp, (1 + buffInc / 100) * buffMore)
if env.minion then
curse.minionBuffModList = common.New("ModList")
local buffInc = env.minion.modDB:Sum("INC", nil, "BuffEffectOnSelf")
local buffMore = env.minion.modDB:Sum("MORE", nil, "BuffEffectOnSelf")
curse.minionBuffModList:ScaleAddList(temp, (1 + buffInc / 100) * buffMore)
end
end
t_insert(curses, curse)
end
if activeSkill.minion then
for _, activeSkill in ipairs(activeSkill.minion.activeSkillList) do
local skillModList = activeSkill.skillModList
local skillCfg = activeSkill.skillCfg
if activeSkill.curseModList and activeSkill.skillData.enable and not enemyDB:Sum("FLAG", nil, "Hexproof") then
local curse = {
name = activeSkill.activeGem.name,
priority = 1,
}
local inc = enemyDB:Sum("INC", nil, "CurseEffectOnSelf") + skillModList:Sum("INC", skillCfg, "CurseEffect")
local more = enemyDB:Sum("MORE", nil, "CurseEffectOnSelf") * skillModList:Sum("MORE", skillCfg, "CurseEffect")
curse.modList = common.New("ModList")
curse.modList:ScaleAddList(activeSkill.curseModList, (1 + inc / 100) * more)
t_insert(minionCurses, curse)
end
end
end
end
end
-- Check for extra curses
for _, value in ipairs(modDB:Sum("LIST", nil, "ExtraCurse")) do
local gemModList = common.New("ModList")
calcs.mergeGemMods(gemModList, {
level = value.level,
quality = 0,
data = data.gems[value.name],
})
local curseModList = { }
for _, mod in ipairs(gemModList) do
for _, tag in ipairs(mod.tagList) do
if tag.type == "GlobalEffect" and tag.effectType == "Curse" then
t_insert(curseModList, mod)
break
end
end
end
if value.applyToPlayer then
-- Sources for curses on the player don't usually respect any kind of limit, so there's little point bothering with slots
modDB.multipliers["CurseOnSelf"] = (modDB.multipliers["CurseOnSelf"] or 0) + 1
modDB:ScaleAddList(curseModList, (1 + modDB:Sum("INC", nil, "CurseEffectOnSelf") / 100) * modDB:Sum("MORE", nil, "CurseEffectOnSelf"))
elseif not enemyDB:Sum("FLAG", nil, "Hexproof") or modDB:Sum("FLAG", nil, "CursesIgnoreHexproof") then
local curse = {
name = value.name,
fromPlayer = true,
priority = 2,
}
curse.modList = common.New("ModList")
curse.modList:ScaleAddList(curseModList, (1 + enemyDB:Sum("INC", nil, "CurseEffectOnSelf") / 100) * enemyDB:Sum("MORE", nil, "CurseEffectOnSelf"))
t_insert(curses, curse)
end
end
-- Assign curses to slots
local curseSlots = { }
env.curseSlots = curseSlots
for _, source in ipairs({curses, minionCurses}) do
for _, curse in ipairs(source) do
local slot
for i = 1, source.limit do
if not curseSlots[i] then
slot = i
break
elseif curseSlots[i].name == curse.name then
if curseSlots[i].priority < curse.priority then
slot = i
else
slot = nil
end
break
elseif curseSlots[i].priority < curse.priority then
slot = i
end
end
if slot then
curseSlots[slot] = curse
end
end
end
-- Apply buff/debuff modifiers
for _, modList in pairs(buffs) do
modDB:AddList(modList)
end
if env.minion then
for _, modList in pairs(minionBuffs) do
env.minion.modDB:AddList(modList)
end
end
for _, modList in pairs(debuffs) do
enemyDB:AddList(modList)
end
modDB.multipliers["CurseOnEnemy"] = #curseSlots
local affectedByCurse = { }
for _, slot in ipairs(curseSlots) do
enemyDB.conditions["Cursed"] = true
if slot.fromPlayer then
affectedByCurse[env.enemy] = true
end
if slot.modList then
enemyDB:AddList(slot.modList)
end
if slot.buffModList then
modDB:AddList(slot.buffModList)
end
if slot.minionBuffModList then
env.minion.modDB:AddList(slot.minionBuffModList)
end
end
-- Check for extra auras
for _, value in ipairs(modDB:Sum("LIST", nil, "ExtraAura")) do
local modList = { value.mod }
if not value.onlyAllies then
local inc = modDB:Sum("INC", nil, "BuffEffectOnSelf", "AuraEffectOnSelf")
local more = modDB:Sum("MORE", nil, "BuffEffectOnSelf", "AuraEffectOnSelf")
modDB:ScaleAddList(modList, (1 + inc / 100) * more)
end
if env.minion and not modDB:Sum("FLAG", nil, "SelfAurasCannotAffectAllies") then
local inc = env.minion.modDB:Sum("INC", nil, "BuffEffectOnSelf", "AuraEffectOnSelf")
local more = env.minion.modDB:Sum("MORE", nil, "BuffEffectOnSelf", "AuraEffectOnSelf")
env.minion.modDB:ScaleAddList(modList, (1 + inc / 100) * more)
end
end
-- Check for modifiers to apply to actors affected by player auras or curses
for _, value in ipairs(modDB:Sum("LIST", nil, "AffectedByAuraMod")) do
for actor in pairs(affectedByAura) do
actor.modDB:AddMod(value.mod)
end
end
for _, value in ipairs(modDB:Sum("LIST", nil, "AffectedByCurseMod")) do
for actor in pairs(affectedByCurse) do
actor.modDB:AddMod(value.mod)
end
end
-- Process misc buffs/modifiers
doActorMisc(env, env.player)
if env.minion then
doActorMisc(env, env.minion)
end
doActorMisc(env, env.enemy)
-- Defence/offence calculations
calcs.defence(env, env.player)
calcs.offence(env, env.player)
if env.minion then
calcs.defence(env, env.minion)
calcs.offence(env, env.minion)
end
end