Re-add Oils to tree Add support for Ghost Shrouds Add support for increased Ancestor Totem buff effect Add support for Ritual of Awakening Hierophant node Add support for 3 mods on Watchers Eye Add Impale damage to Combined DPS sort option Update Boss resistance values to 3.9 levels Add Bleed DPS to sorting option Add new spectres to the spectre library Kraityn's Sentry, Chrome-infused Chimeral, Vaal Slayer, Primeval Hunter, Archer Statue, Crazed Driver Add new spectre skills to library Blast Rain, Elemental Hit Fire, Barrage, Ice Shot, Unearth, Tornado Shot, Flame Surge Fix mod support for Chains of Command Add Astral Projector Unique Ring (3.9 preview) Fix rage granting damage to unarmed attacks Fix UI on passive tree being too large for some resolutions Fix node power not respecting your colour choice for highlighting Add area tag to Perforate Fix uniques with duplicate mods Re-add Death's Harp back in Update Snakepit Unique Ring Add Vaal Arc Lucky buff to export script
1072 lines
42 KiB
Lua
1072 lines
42 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_max = math.max
|
|
local m_ceil = math.ceil
|
|
local m_floor = math.floor
|
|
local m_modf = math.modf
|
|
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] = new("ModList")
|
|
end
|
|
local dest = destTable[destKey]
|
|
for _, mod in ipairs(src) do
|
|
local match = false
|
|
if mod.type ~= "LIST" then
|
|
for index, destMod in ipairs(dest) do
|
|
if modLib.compareModParams(mod, destMod) then
|
|
if type(destMod.value) == "number" and mod.value > destMod.value then
|
|
dest[index] = mod
|
|
end
|
|
match = true
|
|
break
|
|
end
|
|
end
|
|
end
|
|
if not match then
|
|
t_insert(dest, mod)
|
|
end
|
|
end
|
|
end
|
|
|
|
-- Merge keystone modifiers
|
|
local function mergeKeystones(env)
|
|
local modDB = env.modDB
|
|
|
|
for _, name in ipairs(modDB:List(nil, "Keystone")) do
|
|
if not env.keystonesAdded[name] then
|
|
env.keystonesAdded[name] = true
|
|
modDB:AddList(env.spec.tree.keystoneMap[name].modList)
|
|
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.itemList["Weapon 2"] and actor.itemList["Weapon 2"].type == "Shield") or (actor == env.player and env.aegisModList) then
|
|
condList["UsingShield"] = true
|
|
end
|
|
if not actor.itemList["Weapon 2"] then
|
|
condList["OffHandIsEmpty"] = true
|
|
end
|
|
if actor.weaponData1.type == "None" then
|
|
condList["Unarmed"] = true
|
|
else
|
|
local info = env.data.weaponTypeInfo[actor.weaponData1.type]
|
|
condList["Using"..info.flag] = true
|
|
if actor.weaponData1.countsAsAll1H then
|
|
condList["UsingAxe"] = true
|
|
condList["UsingSword"] = true
|
|
condList["UsingDagger"] = true
|
|
condList["UsingMace"] = true
|
|
condList["UsingClaw"] = true
|
|
end
|
|
if info.melee then
|
|
condList["UsingMeleeWeapon"] = true
|
|
end
|
|
if info.oneHand then
|
|
condList["UsingOneHandedWeapon"] = true
|
|
else
|
|
condList["UsingTwoHandedWeapon"] = true
|
|
end
|
|
end
|
|
if actor.weaponData2.type then
|
|
local info = env.data.weaponTypeInfo[actor.weaponData2.type]
|
|
condList["Using"..info.flag] = true
|
|
if actor.weaponData2.countsAsAll1H then
|
|
condList["UsingAxe"] = true
|
|
condList["UsingSword"] = true
|
|
condList["UsingDagger"] = true
|
|
condList["UsingMace"] = true
|
|
condList["UsingClaw"] = true
|
|
end
|
|
if info.melee then
|
|
condList["UsingMeleeWeapon"] = true
|
|
end
|
|
if info.oneHand then
|
|
condList["UsingOneHandedWeapon"] = true
|
|
else
|
|
condList["UsingTwoHandedWeapon"] = true
|
|
end
|
|
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 env.mode_combat then
|
|
if not modDB:Flag(nil, "NeverCrit") then
|
|
condList["CritInPast8Sec"] = true
|
|
end
|
|
if not actor.mainSkill.skillData.triggered and not actor.mainSkill.skillFlags.trap and not actor.mainSkill.skillFlags.mine and not actor.mainSkill.skillFlags.totem then
|
|
if actor.mainSkill.skillFlags.attack then
|
|
condList["AttackedRecently"] = true
|
|
elseif actor.mainSkill.skillFlags.spell then
|
|
condList["CastSpellRecently"] = true
|
|
end
|
|
if actor.mainSkill.skillTypes[SkillType.MovementSkill] then
|
|
condList["UsedMovementSkillRecently"] = true
|
|
end
|
|
if actor.mainSkill.skillFlags.minion then
|
|
condList["UsedMinionSkillRecently"] = true
|
|
end
|
|
if actor.mainSkill.skillTypes[SkillType.Vaal] then
|
|
condList["UsedVaalSkillRecently"] = true
|
|
end
|
|
if actor.mainSkill.skillTypes[SkillType.Channelled] then
|
|
condList["Channelling"] = 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.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] = m_max(round(calcLib.val(modDB, stat)), 0)
|
|
if breakdown then
|
|
breakdown[stat] = breakdown.simple(nil, nil, output[stat], stat)
|
|
end
|
|
end
|
|
|
|
output.LowestAttribute = m_min(output.Str, output.Dex, output.Int)
|
|
condList["DexHigherThanInt"] = output.Dex > output.Int
|
|
condList["StrHigherThanDex"] = output.Str > output.Dex
|
|
condList["IntHigherThanStr"] = output.Int > output.Str
|
|
|
|
-- Add attribute bonuses
|
|
if not modDB:Flag(nil, "NoStrBonusToLife") then
|
|
modDB:NewMod("Life", "BASE", m_floor(output.Str / 2), "Strength")
|
|
end
|
|
local strDmgBonusRatioOverride = modDB:Sum("BASE", nil, "StrDmgBonusRatioOverride")
|
|
if strDmgBonusRatioOverride > 0 then
|
|
actor.strDmgBonus = round((output.Str + modDB:Sum("BASE", nil, "DexIntToMeleeBonus")) * strDmgBonusRatioOverride)
|
|
else
|
|
actor.strDmgBonus = round((output.Str + modDB:Sum("BASE", nil, "DexIntToMeleeBonus")) / 5)
|
|
end
|
|
modDB:NewMod("PhysicalDamage", "INC", actor.strDmgBonus, "Strength", ModFlag.Melee)
|
|
modDB:NewMod("Accuracy", "BASE", output.Dex * 2, "Dexterity")
|
|
if not modDB:Flag(nil, "IronReflexes") then
|
|
modDB:NewMod("Evasion", "INC", round(output.Dex / 5), "Dexterity")
|
|
end
|
|
if not modDB:Flag(nil, "NoIntBonusToMana") then
|
|
modDB:NewMod("Mana", "BASE", round(output.Int / 2), "Intelligence")
|
|
end
|
|
modDB:NewMod("EnergyShield", "INC", round(output.Int / 5), "Intelligence")
|
|
|
|
-- Life/mana pools
|
|
if modDB: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: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
|
|
for _, value in ipairs(modDB: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.PowerChargesMin = modDB:Sum("BASE", nil, "PowerChargesMin")
|
|
output.PowerChargesMax = modDB:Sum("BASE", nil, "PowerChargesMax")
|
|
output.FrenzyChargesMin = modDB:Sum("BASE", nil, "FrenzyChargesMin")
|
|
output.FrenzyChargesMax = modDB:Flag(nil, "MaximumFrenzyChargesIsMaximumPowerCharges") and output.PowerChargesMax or modDB:Sum("BASE", nil, "FrenzyChargesMax")
|
|
output.EnduranceChargesMin = modDB:Sum("BASE", nil, "EnduranceChargesMin")
|
|
output.EnduranceChargesMax = modDB:Flag(nil, "MaximumEnduranceChargesIsMaximumFrenzyCharges") and output.FrenzyChargesMax or modDB:Sum("BASE", nil, "EnduranceChargesMax")
|
|
output.SiphoningChargesMax = modDB:Sum("BASE", nil, "SiphoningChargesMax")
|
|
output.ChallengerChargesMax = modDB:Sum("BASE", nil, "ChallengerChargesMax")
|
|
output.BlitzChargesMax = modDB:Sum("BASE", nil, "BlitzChargesMax")
|
|
output.InspirationChargesMax = modDB:Sum("BASE", nil, "InspirationChargesMax")
|
|
output.CrabBarriersMax = modDB:Sum("BASE", nil, "CrabBarriersMax")
|
|
if modDB:Flag(nil, "UsePowerCharges") then
|
|
output.PowerCharges = modDB:Override(nil, "PowerCharges") or output.PowerChargesMax
|
|
else
|
|
output.PowerCharges = 0
|
|
end
|
|
output.PowerCharges = m_max(output.PowerCharges, output.PowerChargesMin)
|
|
output.RemovablePowerCharges = output.PowerCharges - output.PowerChargesMin
|
|
if modDB:Flag(nil, "UseFrenzyCharges") then
|
|
output.FrenzyCharges = modDB:Override(nil, "FrenzyCharges") or output.FrenzyChargesMax
|
|
else
|
|
output.FrenzyCharges = 0
|
|
end
|
|
output.FrenzyCharges = m_max(output.FrenzyCharges, output.FrenzyChargesMin)
|
|
output.RemovableFrenzyCharges = output.FrenzyCharges - output.FrenzyChargesMin
|
|
if modDB:Flag(nil, "UseEnduranceCharges") then
|
|
output.EnduranceCharges = modDB:Override(nil, "EnduranceCharges") or output.EnduranceChargesMax
|
|
else
|
|
output.EnduranceCharges = 0
|
|
end
|
|
output.EnduranceCharges = m_max(output.EnduranceCharges, output.EnduranceChargesMin)
|
|
output.RemovableEnduranceCharges = output.EnduranceCharges - output.EnduranceChargesMin
|
|
if modDB:Flag(nil, "UseSiphoningCharges") then
|
|
output.SiphoningCharges = modDB:Override(nil, "SiphoningCharges") or output.SiphoningChargesMax
|
|
else
|
|
output.SiphoningCharges = 0
|
|
end
|
|
if modDB:Flag(nil, "UseChallengerCharges") then
|
|
output.ChallengerCharges = modDB:Override(nil, "ChallengerCharges") or output.ChallengerChargesMax
|
|
else
|
|
output.ChallengerCharges = 0
|
|
end
|
|
if modDB:Flag(nil, "UseBlitzCharges") then
|
|
output.BlitzCharges = modDB:Override(nil, "BlitzCharges") or output.BlitzChargesMax
|
|
else
|
|
output.BlitzCharges = 0
|
|
end
|
|
if modDB:Flag(nil, "UseInspirationCharges") then
|
|
output.InspirationCharges = modDB:Override(nil, "InspirationCharges") or output.InspirationChargesMax
|
|
else
|
|
output.InspirationCharges = 0
|
|
end
|
|
if modDB:Flag(nil, "UseGhostShrouds") then
|
|
output.GhostShrouds = modDB:Override(nil, "GhostShrouds") or 3
|
|
else
|
|
output.GhostShrouds = 0
|
|
end
|
|
output.CrabBarriers = m_max(modDB:Override(nil, "CrabBarriers") or output.CrabBarriersMax, output.CrabBarriersMax)
|
|
modDB.multipliers["PowerCharge"] = output.PowerCharges
|
|
modDB.multipliers["RemovablePowerCharge"] = output.RemovablePowerCharges
|
|
modDB.multipliers["FrenzyCharge"] = output.FrenzyCharges
|
|
modDB.multipliers["RemovableFrenzyCharge"] = output.RemovableFrenzyCharges
|
|
modDB.multipliers["EnduranceCharge"] = output.EnduranceCharges
|
|
modDB.multipliers["RemovableEnduranceCharge"] = output.RemovableEnduranceCharges
|
|
modDB.multipliers["SiphoningCharge"] = output.SiphoningCharges
|
|
modDB.multipliers["ChallengerCharge"] = output.ChallengerCharges
|
|
modDB.multipliers["BlitzCharge"] = output.BlitzCharges
|
|
modDB.multipliers["InspirationCharge"] = output.InspirationCharges
|
|
modDB.multipliers["GhostShroud"] = output.GhostShrouds
|
|
modDB.multipliers["CrabBarrier"] = output.CrabBarriers
|
|
|
|
-- Process enemy modifiers
|
|
for _, value in ipairs(modDB:List(nil, "EnemyModifier")) do
|
|
enemyDB:AddMod(value.mod)
|
|
end
|
|
|
|
-- Add misc buffs/debuffs
|
|
if env.mode_combat then
|
|
if modDB:Flag(nil, "Fortify") then
|
|
local effect = m_floor(20 * (1 + modDB:Sum("INC", nil, "FortifyEffectOnSelf", "BuffEffectOnSelf") / 100))
|
|
modDB:NewMod("DamageTakenWhenHit", "MORE", -effect, "Fortify")
|
|
modDB.multipliers["BuffOnSelf"] = (modDB.multipliers["BuffOnSelf"] or 0) + 1
|
|
end
|
|
if modDB: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:Flag(nil, "UnholyMight") then
|
|
local effect = m_floor(30 * (1 + modDB:Sum("INC", nil, "BuffEffectOnSelf") / 100))
|
|
modDB:NewMod("PhysicalDamageGainAsChaos", "BASE", effect, "Unholy Might")
|
|
end
|
|
if modDB:Flag(nil, "Tailwind") then
|
|
local effect = m_floor(10 * (1 + modDB:Sum("INC", nil, "TailwindEffectOnSelf", "BuffEffectOnSelf") / 100))
|
|
modDB:NewMod("ActionSpeed", "INC", effect, "Tailwind")
|
|
end
|
|
if modDB:Flag(nil, "Adrenaline") then
|
|
local effectMod = 1 + modDB:Sum("INC", nil, "BuffEffectOnSelf") / 100
|
|
modDB:NewMod("Damage", "INC", m_floor(100 * effectMod), "Adrenaline")
|
|
modDB:NewMod("Speed", "INC", m_floor(25 * effectMod), "Adrenaline")
|
|
modDB:NewMod("MovementSpeed", "INC", m_floor(25 * effectMod), "Adrenaline")
|
|
modDB:NewMod("PhysicalDamageReduction", "BASE", m_floor(10 * effectMod), "Adrenaline")
|
|
end
|
|
if modDB:Flag(nil, "HerEmbrace") then
|
|
condList["HerEmbrace"] = true
|
|
modDB:NewMod("AvoidStun", "BASE", 100, "Her Embrace")
|
|
modDB:NewMod("PhysicalDamageGainAsFire", "BASE", 123, "Her Embrace", ModFlag.Sword)
|
|
modDB:NewMod("AvoidFreeze", "BASE", 100, "Her Embrace")
|
|
modDB:NewMod("AvoidChill", "BASE", 100, "Her Embrace")
|
|
modDB:NewMod("AvoidIgnite", "BASE", 100, "Her Embrace")
|
|
modDB:NewMod("Speed", "INC", 20, "Her Embrace")
|
|
modDB:NewMod("MovementSpeed", "INC", 20, "Her Embrace")
|
|
end
|
|
if modDB:Flag(nil, "Elusive") then
|
|
local effect = 1 + modDB:Sum("INC", nil, "ElusiveEffect", "BuffEffectOnSelf") / 100
|
|
condList["Elusive"] = true
|
|
modDB:NewMod("AttackDodgeChance", "BASE", m_floor(20 * effect), "Elusive")
|
|
modDB:NewMod("SpellDodgeChance", "BASE", m_floor(20 * effect), "Elusive")
|
|
modDB:NewMod("MovementSpeed", "INC", m_floor(40 * effect), "Elusive")
|
|
end
|
|
if modDB:Flag(nil, "Chill") then
|
|
local effect = m_max(m_floor(30 * calcLib.mod(modDB, nil, "SelfChillEffect")), 0)
|
|
modDB:NewMod("ActionSpeed", "INC", effect * (modDB:Flag(nil, "SelfChillEffectIsReversed") and 1 or -1), "Chill")
|
|
end
|
|
if modDB:Flag(nil, "Freeze") then
|
|
local effect = m_max(m_floor(70 * calcLib.mod(modDB, nil, "SelfChillEffect")), 0)
|
|
modDB:NewMod("ActionSpeed", "INC", -effect, "Freeze")
|
|
end
|
|
if modDB:Flag(nil, "CanLeechLifeOnFullLife") then
|
|
condList["Leeching"] = true
|
|
condList["LeechingLife"] = true
|
|
env.configInput.conditionLeeching = true
|
|
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
|
|
env.keystonesAdded = { }
|
|
mergeKeystones(env)
|
|
|
|
-- Build minion skills
|
|
for _, activeSkill in ipairs(env.player.activeSkillList) do
|
|
activeSkill.skillModList = new("ModList", activeSkill.baseSkillModList)
|
|
if activeSkill.minion then
|
|
activeSkill.minion.modDB = new("ModDB")
|
|
activeSkill.minion.modDB.actor = activeSkill.minion
|
|
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.modDB.multipliers["Level"] = env.minion.level
|
|
calcs.initModDB(env, env.minion.modDB)
|
|
env.minion.modDB:NewMod("Life", "BASE", m_floor(env.minion.lifeTable[env.minion.level] * env.minion.minionData.life), "Base")
|
|
if env.minion.minionData.energyShield then
|
|
env.minion.modDB:NewMod("EnergyShield", "BASE", m_floor(env.data.monsterAllyLifeTable[env.minion.level] * env.minion.minionData.life * env.minion.minionData.energyShield), "Base")
|
|
end
|
|
if env.minion.minionData.armour then
|
|
env.minion.modDB:NewMod("Armour", "BASE", m_floor((10 + env.minion.level * 2) * env.minion.minionData.armour * 1.038 ^ env.minion.level), "Base")
|
|
end
|
|
env.minion.modDB:NewMod("Evasion", "BASE", round((30 + env.minion.level * 5) * 1.03 ^ env.minion.level), "Base")
|
|
env.minion.modDB:NewMod("Accuracy", "BASE", round((17 + env.minion.level / 2) * (env.minion.minionData.accuracy or 1) * 1.03 ^ env.minion.level), "Base")
|
|
env.minion.modDB:NewMod("CritMultiplier", "BASE", 30, "Base")
|
|
env.minion.modDB:NewMod("CritDegenMultiplier", "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")
|
|
if env.build.targetVersion ~= "2_6" then
|
|
env.minion.modDB:NewMod("Damage", "MORE", -50, "Base", 0, KeywordFlag.Poison)
|
|
env.minion.modDB:NewMod("Damage", "MORE", -50, "Base", 0, KeywordFlag.Ignite)
|
|
env.minion.modDB:NewMod("SkillData", "LIST", { key = "bleedBasePercent", value = 70/6 }, "Base")
|
|
end
|
|
env.minion.modDB:NewMod("Damage", "MORE", 500, "Base", 0, KeywordFlag.Bleed, { type = "ActorCondition", actor = "enemy", var = "Moving" })
|
|
for _, mod in ipairs(env.minion.minionData.modList) do
|
|
env.minion.modDB:AddMod(mod)
|
|
end
|
|
for _, mod in ipairs(env.player.mainSkill.extraSkillModList) do
|
|
env.minion.modDB:AddMod(mod)
|
|
end
|
|
if env.aegisModList then
|
|
env.minion.itemList["Weapon 3"] = env.player.itemList["Weapon 2"]
|
|
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 env.minion.itemSet or env.minion.uses then
|
|
for slotName, slot in pairs(env.build.itemsTab.slots) do
|
|
if env.minion.uses[slotName] then
|
|
local item
|
|
if env.minion.itemSet then
|
|
if slot.weaponSet == 1 and env.minion.itemSet.useSecondWeaponSet then
|
|
slotName = slotName .. " Swap"
|
|
end
|
|
item = env.build.itemsTab.items[env.minion.itemSet[slotName].selItemId]
|
|
else
|
|
item = env.player.itemList[slotName]
|
|
end
|
|
if item then
|
|
env.minion.itemList[slotName] = item
|
|
env.minion.modDB:AddList(item.modList or item.slotModList[slot.slotNum])
|
|
end
|
|
end
|
|
end
|
|
end
|
|
if modDB:Flag(nil, "StrengthAddedToMinions") then
|
|
env.minion.modDB:NewMod("Str", "BASE", round(calcLib.val(modDB, "Str")), "Player")
|
|
end
|
|
if modDB:Flag(nil, "HalfStrengthAddedToMinions") then
|
|
env.minion.modDB:NewMod("Str", "BASE", round(calcLib.val(modDB, "Str") * 0.5), "Player")
|
|
end
|
|
end
|
|
if env.aegisModList then
|
|
env.player.itemList["Weapon 2"] = nil
|
|
end
|
|
|
|
for _, activeSkill in ipairs(env.player.activeSkillList) do
|
|
if activeSkill.skillFlags.golem then
|
|
local limit = activeSkill.skillModList:Sum("BASE", nil, "ActiveGolemLimit")
|
|
output.ActiveGolemLimit = m_max(limit, output.ActiveGolemLimit or 0)
|
|
end
|
|
if activeSkill.skillFlags.totem then
|
|
local limit = activeSkill.skillModList:Sum("BASE", nil, "ActiveTotemLimit")
|
|
output.ActiveTotemLimit = m_max(limit, output.ActiveTotemLimit or 0)
|
|
end
|
|
end
|
|
|
|
local breakdown
|
|
if env.mode == "CALCS" then
|
|
-- Initialise breakdown module
|
|
breakdown = LoadModule(calcs.breakdownModule, modDB, output, env.player)
|
|
env.player.breakdown = breakdown
|
|
if env.minion then
|
|
env.minion.breakdown = LoadModule(calcs.breakdownModule, 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 = new("ModList")
|
|
srcList:ScaleAddList(item.buffModList, effectMod)
|
|
mergeBuff(srcList, flaskBuffs, item.baseName)
|
|
end
|
|
if item.modList[1] then
|
|
local srcList = 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:Flag(nil, "FlasksDoNotApplyToPlayer") then
|
|
for _, buffModList in pairs(flaskBuffs) do
|
|
modDB.conditions["UsingFlask"] = true
|
|
modDB:AddList(buffModList)
|
|
end
|
|
end
|
|
if env.minion and modDB: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
|
|
|
|
-- Merge keystones again to catch any that were added by flasks
|
|
mergeKeystones(env)
|
|
|
|
-- 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.player.activeSkillList) do
|
|
if activeSkill.skillTypes[SkillType.ManaCostReserved] and not activeSkill.skillFlags.totem then
|
|
local skillModList = activeSkill.skillModList
|
|
local skillCfg = activeSkill.skillCfg
|
|
local suffix = activeSkill.skillTypes[SkillType.ManaCostPercent] and "Percent" or "Base"
|
|
local baseVal = activeSkill.skillData.manaCostOverride or activeSkill.activeEffect.grantedEffectLevel.manaCost or 0
|
|
local mult = skillModList:More(skillCfg, "SupportManaMultiplier")
|
|
local more = skillModList:More(skillCfg, "ManaReserved")
|
|
local inc = skillModList:Sum("INC", skillCfg, "ManaReserved")
|
|
local base = m_floor(baseVal * mult)
|
|
local cost
|
|
if activeSkill.skillData.manaCostForced then
|
|
cost = activeSkill.skillData.manaCostForced
|
|
else
|
|
cost = m_max(base - m_modf(base * -m_floor((100 + inc) * more - 100) / 100), 0)
|
|
end
|
|
if activeSkill.activeMineCount then
|
|
cost = cost * activeSkill.activeMineCount
|
|
end
|
|
local pool
|
|
if skillModList:Flag(skillCfg, "BloodMagic", "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.activeEffect.grantedEffect.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 _, value in ipairs(env.player.mainSkill.skillModList:List(env.player.mainSkill.skillCfg, "MinionModifier")) do
|
|
if not value.type or env.minion.type == value.type then
|
|
env.minion.modDB:AddMod(value.mod)
|
|
end
|
|
end
|
|
for _, name in ipairs(env.minion.modDB:List(nil, "Keystone")) do
|
|
env.minion.modDB:AddList(env.spec.tree.keystoneMap[name].modList)
|
|
end
|
|
doActorAttribsPoolsConditions(env, env.minion)
|
|
end
|
|
|
|
-- Process attribute requirements
|
|
do
|
|
local reqMult = calcLib.mod(modDB, nil, "GlobalAttributeRequirements")
|
|
for _, attr in ipairs({"Str","Dex","Int"}) do
|
|
if env.mode == "CALCS" then
|
|
breakdown["Req"..attr] = {
|
|
rowList = { },
|
|
colList = {
|
|
{ label = attr, key = "req" },
|
|
{ label = "Source", key = "source" },
|
|
{ label = "Source Name", key = "sourceName" },
|
|
}
|
|
}
|
|
end
|
|
local out = 0
|
|
for _, reqSource in ipairs(env.requirementsTable) do
|
|
if reqSource[attr] and reqSource[attr] > 0 then
|
|
local req = m_floor(reqSource[attr] * reqMult)
|
|
out = m_max(out, req)
|
|
if env.mode == "CALCS" then
|
|
local row = {
|
|
req = req > output[attr] and colorCodes.NEGATIVE..req or req,
|
|
reqNum = req,
|
|
source = reqSource.source,
|
|
}
|
|
if reqSource.source == "Item" then
|
|
local item = reqSource.sourceItem
|
|
row.sourceName = colorCodes[item.rarity]..item.name
|
|
row.sourceNameTooltip = function(tooltip)
|
|
env.build.itemsTab:AddItemTooltip(tooltip, item, reqSource.sourceSlot)
|
|
end
|
|
elseif reqSource.source == "Gem" then
|
|
row.sourceName = s_format("%s%s ^7%d/%d", reqSource.sourceGem.color, reqSource.sourceGem.nameSpec, reqSource.sourceGem.level, reqSource.sourceGem.quality)
|
|
end
|
|
t_insert(breakdown["Req"..attr].rowList, row)
|
|
end
|
|
end
|
|
end
|
|
output["Req"..attr] = out
|
|
if env.mode == "CALCS" then
|
|
output["Req"..attr.."String"] = out > output[attr] and colorCodes.NEGATIVE..out or out
|
|
table.sort(breakdown["Req"..attr].rowList, function(a, b)
|
|
if a.reqNum ~= b.reqNum then
|
|
return a.reqNum > b.reqNum
|
|
elseif a.source ~= b.source then
|
|
return a.source < b.source
|
|
else
|
|
return a.sourceName < b.sourceName
|
|
end
|
|
end)
|
|
end
|
|
end
|
|
end
|
|
|
|
-- Check for extra modifiers to apply to aura skills
|
|
local extraAuraModList = { }
|
|
for _, value in ipairs(modDB:List(nil, "ExtraAuraEffect")) do
|
|
t_insert(extraAuraModList, value.mod)
|
|
end
|
|
|
|
-- Combine buffs/debuffs
|
|
output.EnemyCurseLimit = modDB:Sum("BASE", nil, "EnemyCurseLimit")
|
|
local buffs = { }
|
|
env.buffs = buffs
|
|
local minionBuffs = { }
|
|
env.minionBuffs = minionBuffs
|
|
local debuffs = { }
|
|
env.debuffs = debuffs
|
|
local curses = {
|
|
limit = output.EnemyCurseLimit,
|
|
}
|
|
local minionCurses = {
|
|
limit = 1,
|
|
}
|
|
local affectedByAura = { }
|
|
for _, activeSkill in ipairs(env.player.activeSkillList) do
|
|
local skillModList = activeSkill.skillModList
|
|
local skillCfg = activeSkill.skillCfg
|
|
for _, buff in ipairs(activeSkill.buffList) do
|
|
if buff.cond and not skillModList:GetCondition(buff.cond, skillCfg) then
|
|
-- Nothing!
|
|
elseif buff.enemyCond and not enemyDB:GetCondition(buff.enemyCond) then
|
|
-- Also nothing :/
|
|
elseif buff.type == "Buff" then
|
|
if env.mode_buffs and (not activeSkill.skillFlags.totem or buff.allowTotemBuff) then
|
|
local skillCfg = buff.activeSkillBuff and skillCfg
|
|
local modStore = buff.activeSkillBuff and skillModList or modDB
|
|
if not buff.applyNotPlayer then
|
|
activeSkill.buffSkill = true
|
|
modDB.conditions["AffectedBy"..buff.name:gsub(" ","")] = true
|
|
local srcList = new("ModList")
|
|
local inc = modStore:Sum("INC", skillCfg, "BuffEffect", "BuffEffectOnSelf", "BuffEffectOnPlayer")
|
|
local more = modStore:More(skillCfg, "BuffEffect", "BuffEffectOnSelf")
|
|
srcList:ScaleAddList(buff.modList, (1 + inc / 100) * more)
|
|
mergeBuff(srcList, buffs, buff.name)
|
|
if activeSkill.skillData.thisIsNotABuff then
|
|
buffs[buff.name].notBuff = true
|
|
end
|
|
end
|
|
if env.minion and (buff.applyMinions or buff.applyAllies) then
|
|
activeSkill.minionBuffSkill = true
|
|
env.minion.modDB.conditions["AffectedBy"..buff.name] = true
|
|
local srcList = new("ModList")
|
|
local inc = modStore:Sum("INC", skillCfg, "BuffEffect") + env.minion.modDB:Sum("INC", nil, "BuffEffectOnSelf")
|
|
local more = modStore:More(skillCfg, "BuffEffect") * env.minion.modDB:More(nil, "BuffEffectOnSelf")
|
|
srcList:ScaleAddList(buff.modList, (1 + inc / 100) * more)
|
|
mergeBuff(srcList, minionBuffs, buff.name)
|
|
end
|
|
end
|
|
elseif buff.type == "Aura" then
|
|
if env.mode_buffs then
|
|
if not activeSkill.skillData.auraCannotAffectSelf then
|
|
activeSkill.buffSkill = true
|
|
affectedByAura[env.player] = true
|
|
modDB.conditions["AffectedBy"..buff.name:gsub(" ","")] = true
|
|
local srcList = new("ModList")
|
|
local inc = skillModList:Sum("INC", skillCfg, "AuraEffect", "BuffEffect", "BuffEffectOnSelf", "AuraEffectOnSelf")
|
|
local more = skillModList:More(skillCfg, "AuraEffect", "BuffEffect", "BuffEffectOnSelf", "AuraEffectOnSelf")
|
|
srcList:ScaleAddList(buff.modList, (1 + inc / 100) * more)
|
|
srcList:ScaleAddList(extraAuraModList, (1 + inc / 100) * more)
|
|
mergeBuff(srcList, buffs, buff.name)
|
|
end
|
|
if env.minion and not modDB:Flag(nil, "SelfAurasCannotAffectAllies") then
|
|
activeSkill.minionBuffSkill = true
|
|
affectedByAura[env.minion] = true
|
|
env.minion.modDB.conditions["AffectedBy"..buff.name] = true
|
|
local srcList = new("ModList")
|
|
local inc = skillModList:Sum("INC", skillCfg, "AuraEffect", "BuffEffect") + env.minion.modDB:Sum("INC", nil, "BuffEffectOnSelf", "AuraEffectOnSelf")
|
|
local more = skillModList:More(skillCfg, "AuraEffect", "BuffEffect") * env.minion.modDB:More(nil, "BuffEffectOnSelf", "AuraEffectOnSelf")
|
|
srcList:ScaleAddList(buff.modList, (1 + inc / 100) * more)
|
|
srcList:ScaleAddList(extraAuraModList, (1 + inc / 100) * more)
|
|
mergeBuff(srcList, minionBuffs, buff.name)
|
|
end
|
|
end
|
|
elseif buff.type == "Debuff" or buff.type == "AuraDebuff" then
|
|
local stackCount
|
|
if buff.stackVar then
|
|
stackCount = skillModList:Sum("BASE", skillCfg, "Multiplier:"..buff.stackVar)
|
|
if buff.stackLimit then
|
|
stackCount = m_min(stackCount, buff.stackLimit)
|
|
elseif buff.stackLimitVar then
|
|
stackCount = m_min(stackCount, skillModList:Sum("BASE", skillCfg, "Multiplier:"..buff.stackLimitVar))
|
|
end
|
|
else
|
|
stackCount = activeSkill.skillData.stackCount or 1
|
|
end
|
|
if env.mode_effective and stackCount > 0 then
|
|
activeSkill.debuffSkill = true
|
|
modDB.conditions["AffectedBy"..buff.name:gsub(" ","")] = true
|
|
local srcList = new("ModList")
|
|
local mult = 1
|
|
if buff.type == "AuraDebuff" then
|
|
local inc = skillModList:Sum("INC", skillCfg, "AuraEffect", "BuffEffect")
|
|
local more = skillModList:More(skillCfg, "AuraEffect", "BuffEffect")
|
|
mult = (1 + inc / 100) * more
|
|
end
|
|
srcList:ScaleAddList(buff.modList, mult * stackCount)
|
|
if activeSkill.skillData.stackCount or buff.stackVar then
|
|
srcList:NewMod("Multiplier:"..buff.name.."Stack", "BASE", stackCount, buff.name)
|
|
end
|
|
mergeBuff(srcList, debuffs, buff.name)
|
|
end
|
|
elseif buff.type == "Curse" or buff.type == "CurseBuff" then
|
|
if env.mode_effective and (not enemyDB:Flag(nil, "Hexproof") or modDB:Flag(nil, "CursesIgnoreHexproof")) then
|
|
local curse = {
|
|
name = buff.name,
|
|
fromPlayer = true,
|
|
priority = activeSkill.skillTypes[SkillType.Aura] and 3 or 1,
|
|
}
|
|
local inc = skillModList:Sum("INC", skillCfg, "CurseEffect") + enemyDB:Sum("INC", nil, "CurseEffectOnSelf")
|
|
local more = skillModList:More(skillCfg, "CurseEffect") * enemyDB:More(nil, "CurseEffectOnSelf")
|
|
if buff.type == "Curse" then
|
|
curse.modList = new("ModList")
|
|
curse.modList:ScaleAddList(buff.modList, (1 + inc / 100) * more)
|
|
else
|
|
-- Curse applies a buff; scale by curse effect, then buff effect
|
|
local temp = new("ModList")
|
|
temp:ScaleAddList(buff.modList, (1 + inc / 100) * more)
|
|
curse.buffModList = new("ModList")
|
|
local buffInc = modDB:Sum("INC", skillCfg, "BuffEffectOnSelf")
|
|
local buffMore = modDB:More(skillCfg, "BuffEffectOnSelf")
|
|
curse.buffModList:ScaleAddList(temp, (1 + buffInc / 100) * buffMore)
|
|
if env.minion then
|
|
curse.minionBuffModList = new("ModList")
|
|
local buffInc = env.minion.modDB:Sum("INC", nil, "BuffEffectOnSelf")
|
|
local buffMore = env.minion.modDB:More(nil, "BuffEffectOnSelf")
|
|
curse.minionBuffModList:ScaleAddList(temp, (1 + buffInc / 100) * buffMore)
|
|
end
|
|
end
|
|
t_insert(curses, curse)
|
|
end
|
|
end
|
|
end
|
|
if activeSkill.minion then
|
|
local castingMinion = activeSkill.minion
|
|
for _, activeSkill in ipairs(activeSkill.minion.activeSkillList) do
|
|
local skillModList = activeSkill.skillModList
|
|
local skillCfg = activeSkill.skillCfg
|
|
for _, buff in ipairs(activeSkill.buffList) do
|
|
if buff.type == "Buff" then
|
|
if env.mode_buffs and activeSkill.skillData.enable then
|
|
local skillCfg = buff.activeSkillBuff and skillCfg
|
|
local modStore = buff.activeSkillBuff and skillModList or env.minion.modDB
|
|
if buff.applyAllies then
|
|
modDB.conditions["AffectedBy"..buff.name] = true
|
|
local srcList = new("ModList")
|
|
local inc = modStore:Sum("INC", skillCfg, "BuffEffect") + modDB:Sum("INC", nil, "BuffEffectOnSelf")
|
|
local more = modStore:More(skillCfg, "BuffEffect") * modDB:More(nil, "BuffEffectOnSelf")
|
|
srcList:ScaleAddList(buff.modList, (1 + inc / 100) * more)
|
|
mergeBuff(srcList, buffs, buff.name)
|
|
end
|
|
if env.minion and (env.minion == castingMinion or buff.applyAllies) then
|
|
env.minion.modDB.conditions["AffectedBy"..buff.name:gsub(" ","")] = true
|
|
local srcList = new("ModList")
|
|
local inc = modStore:Sum("INC", skillCfg, "BuffEffect", "BuffEffectOnSelf")
|
|
local more = modStore:More(skillCfg, "BuffEffect", "BuffEffectOnSelf")
|
|
srcList:ScaleAddList(buff.modList, (1 + inc / 100) * more)
|
|
mergeBuff(srcList, minionBuffs, buff.name)
|
|
end
|
|
end
|
|
elseif buff.type == "Aura" then
|
|
if env.mode_buffs and activeSkill.skillData.enable then
|
|
if not modDB:Flag(nil, "AlliesAurasCannotAffectSelf") then
|
|
local srcList = new("ModList")
|
|
local inc = skillModList:Sum("INC", skillCfg, "AuraEffect", "BuffEffect") + modDB:Sum("INC", nil, "BuffEffectOnSelf", "AuraEffectOnSelf")
|
|
local more = skillModList:More(skillCfg, "AuraEffect", "BuffEffect") + modDB:More(nil, "BuffEffectOnSelf", "AuraEffectOnSelf")
|
|
srcList:ScaleAddList(buff.modList, (1 + inc / 100) * more)
|
|
mergeBuff(srcList, buffs, buff.name)
|
|
end
|
|
if env.minion and (env.minion ~= activeSkill.minion or not activeSkill.skillData.auraCannotAffectSelf) then
|
|
local srcList = new("ModList")
|
|
local inc = skillModList:Sum("INC", skillCfg, "AuraEffect", "BuffEffect") + env.minion.modDB:Sum("INC", nil, "BuffEffectOnSelf", "AuraEffectOnSelf")
|
|
local more = skillModList:More(skillCfg, "AuraEffect", "BuffEffect") + env.minion.modDB:More(nil, "BuffEffectOnSelf", "AuraEffectOnSelf")
|
|
srcList:ScaleAddList(buff.modList, (1 + inc / 100) * more)
|
|
mergeBuff(srcList, minionBuffs, buff.name)
|
|
end
|
|
end
|
|
elseif buff.type == "Curse" then
|
|
if env.mode_effective and activeSkill.skillData.enable and not enemyDB:Flag(nil, "Hexproof") then
|
|
local curse = {
|
|
name = buff.name,
|
|
priority = 1,
|
|
}
|
|
local inc = skillModList:Sum("INC", skillCfg, "CurseEffect") + enemyDB:Sum("INC", nil, "CurseEffectOnSelf")
|
|
local more = skillModList:More(skillCfg, "CurseEffect") + enemyDB:More(nil, "CurseEffectOnSelf")
|
|
curse.modList = new("ModList")
|
|
curse.modList:ScaleAddList(buff.modList, (1 + inc / 100) * more)
|
|
t_insert(minionCurses, curse)
|
|
end
|
|
elseif buff.type == "Debuff" then
|
|
local stackCount
|
|
if buff.stackVar then
|
|
stackCount = modDB:Sum("BASE", skillCfg, "Multiplier:"..buff.stackVar)
|
|
if buff.stackLimit then
|
|
stackCount = m_min(stackCount, buff.stackLimit)
|
|
elseif buff.stackLimitVar then
|
|
stackCount = m_min(stackCount, modDB:Sum("BASE", skillCfg, "Multiplier:"..buff.stackLimitVar))
|
|
end
|
|
else
|
|
stackCount = activeSkill.skillData.stackCount or 1
|
|
end
|
|
if env.mode_effective and stackCount > 0 then
|
|
activeSkill.debuffSkill = true
|
|
local srcList = new("ModList")
|
|
srcList:ScaleAddList(buff.modList, stackCount)
|
|
if activeSkill.skillData.stackCount then
|
|
srcList:NewMod("Multiplier:"..buff.name.."Stack", "BASE", activeSkill.skillData.stackCount, buff.name)
|
|
end
|
|
mergeBuff(srcList, debuffs, buff.name)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
-- Check for extra curses
|
|
for dest, modDB in pairs({[curses] = modDB, [minionCurses] = env.minion and env.minion.modDB}) do
|
|
for _, value in ipairs(modDB:List(nil, "ExtraCurse")) do
|
|
local gemModList = new("ModList")
|
|
local grantedEffect = env.data.skills[value.skillId]
|
|
if grantedEffect then
|
|
calcs.mergeSkillInstanceMods(env, gemModList, {
|
|
grantedEffect = grantedEffect,
|
|
level = value.level,
|
|
quality = 0,
|
|
})
|
|
local curseModList = { }
|
|
for _, mod in ipairs(gemModList) do
|
|
for _, tag in ipairs(mod) 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
|
|
if modDB:Sum("BASE", nil, "AvoidCurse") < 100 then
|
|
modDB.conditions["Cursed"] = true
|
|
modDB.multipliers["CurseOnSelf"] = (modDB.multipliers["CurseOnSelf"] or 0) + 1
|
|
modDB.conditions["AffectedBy"..grantedEffect.name:gsub(" ","")] = true
|
|
local cfg = { skillName = grantedEffect.name }
|
|
local inc = modDB:Sum("INC", cfg, "CurseEffectOnSelf") + gemModList:Sum("INC", nil, "CurseEffectAgainstPlayer")
|
|
local more = modDB:More(cfg, "CurseEffectOnSelf")
|
|
modDB:ScaleAddList(curseModList, (1 + inc / 100) * more)
|
|
end
|
|
elseif not enemyDB:Flag(nil, "Hexproof") or modDB:Flag(nil, "CursesIgnoreHexproof") then
|
|
local curse = {
|
|
name = grantedEffect.name,
|
|
fromPlayer = (dest == curses),
|
|
priority = 2,
|
|
}
|
|
curse.modList = new("ModList")
|
|
curse.modList:ScaleAddList(curseModList, (1 + enemyDB:Sum("INC", nil, "CurseEffectOnSelf") / 100) * enemyDB:More(nil, "CurseEffectOnSelf"))
|
|
t_insert(dest, curse)
|
|
end
|
|
end
|
|
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)
|
|
if not modList.notBuff then
|
|
modDB.multipliers["BuffOnSelf"] = (modDB.multipliers["BuffOnSelf"] or 0) + 1
|
|
end
|
|
if env.minion then
|
|
for _, value in ipairs(modList:List(env.player.mainSkill.skillCfg, "MinionModifier")) do
|
|
if not value.type or env.minion.type == value.type then
|
|
env.minion.modDB:AddMod(value.mod)
|
|
end
|
|
end
|
|
end
|
|
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:List(nil, "ExtraAura")) do
|
|
local modList = { value.mod }
|
|
if not value.onlyAllies then
|
|
local inc = modDB:Sum("INC", nil, "BuffEffectOnSelf", "AuraEffectOnSelf")
|
|
local more = modDB:More(nil, "BuffEffectOnSelf", "AuraEffectOnSelf")
|
|
modDB:ScaleAddList(modList, (1 + inc / 100) * more)
|
|
if not value.notBuff then
|
|
modDB.multipliers["BuffOnSelf"] = (modDB.multipliers["BuffOnSelf"] or 0) + 1
|
|
end
|
|
end
|
|
if env.minion and not modDB:Flag(nil, "SelfAurasCannotAffectAllies") then
|
|
local inc = env.minion.modDB:Sum("INC", nil, "BuffEffectOnSelf", "AuraEffectOnSelf")
|
|
local more = env.minion.modDB: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:List(nil, "AffectedByAuraMod")) do
|
|
for actor in pairs(affectedByAura) do
|
|
actor.modDB:AddMod(value.mod)
|
|
end
|
|
end
|
|
for _, value in ipairs(modDB:List(nil, "AffectedByCurseMod")) do
|
|
for actor in pairs(affectedByCurse) do
|
|
actor.modDB:AddMod(value.mod)
|
|
end
|
|
end
|
|
|
|
-- Merge keystones again to catch any that were added by buffs
|
|
mergeKeystones(env)
|
|
|
|
-- Special handling for Dancing Dervish
|
|
if modDB:Flag(nil, "DisableWeapons") then
|
|
env.player.weaponData1 = copyTable(env.data.unarmedWeaponData[env.classId])
|
|
modDB.conditions["Unarmed"] = true
|
|
elseif env.weaponModList1 then
|
|
modDB:AddList(env.weaponModList1)
|
|
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, env.player.mainSkill)
|
|
if env.minion then
|
|
calcs.defence(env, env.minion)
|
|
calcs.offence(env, env.minion, env.minion.mainSkill)
|
|
end
|
|
end |