Release 1.2.35
- Split hit damage calculations into 2 passes for crit and non-crit - Added support for Inevitable Judgement - Improved DoT code to allow for different chances to inflict on crit and non-crit
This commit is contained in:
@@ -184,7 +184,7 @@ return {
|
||||
{ 1, "Bleed", 1, "Bleed", data.colorCodes.OFFENCE, {
|
||||
extra = "{0:output:BleedChance}% {1:output:BleedDPS} {2:output:BleedDuration}s",
|
||||
flag = "bleed",
|
||||
{ label = "Chance to Bleed", { format = "{0:output:BleedChance}%", { modName = "BleedChance", cfg = "skill" }, }, },
|
||||
{ label = "Chance to Bleed", { format = "{0:output:BleedChance}%", { breakdown = "BleedChance" }, { modName = "BleedChance", cfg = "skill" }, }, },
|
||||
{ label = "Total Increased", { format = "{0:mod:1}%", { modName = { "Damage", "PhysicalDamage" }, modType = "INC", cfg = "bleed" }, }, },
|
||||
{ label = "Total More", { format = "{0:mod:1}%", { modName = { "Damage", "PhysicalDamage" }, modType = "MORE", cfg = "bleed" }, }, },
|
||||
{ label = "Effective DPS Mod", flag = "effective", { format = "x {3:output:BleedEffMult}", { breakdown = "BleedEffMult" }, { label = "Enemy modifiers", modName = { "DamageTaken", "DotTaken", "PhysicalDamageTaken", "PhysicalDamageReduction" }, enemy = true }, }, },
|
||||
@@ -194,7 +194,7 @@ return {
|
||||
{ 1, "Poison", 1, "Poison", data.colorCodes.OFFENCE, {
|
||||
extra = "{0:output:PoisonChance}% {1:output:PoisonDPS} {2:output:PoisonDuration}s",
|
||||
flag = "poison",
|
||||
{ label = "Chance to Poison", { format = "{0:output:PoisonChance}%", { modName = "PoisonChance", cfg = "skill" }, }, },
|
||||
{ label = "Chance to Poison", { format = "{0:output:PoisonChance}%", { breakdown = "PoisonChance" }, { modName = "PoisonChance", cfg = "skill" }, }, },
|
||||
{ label = "Total Increased", { format = "{0:mod:1}%", { modName = { "Damage", "ChaosDamage" }, modType = "INC", cfg = "poison" }, }, },
|
||||
{ label = "Total More", { format = "{0:mod:1}%", { modName = { "Damage", "ChaosDamage" }, modType = "MORE", cfg = "poison" }, }, },
|
||||
{ label = "Effective DPS Mod", flag = "effective", { format = "x {3:output:PoisonEffMult}", { breakdown = "PoisonEffMult" }, { label = "Enemy modifiers", modName = { "ChaosResist", "DamageTaken", "DotTaken", "ChaosDamageTaken" }, enemy = true }, }, },
|
||||
@@ -205,7 +205,7 @@ return {
|
||||
{ 1, "Ignite", 1, "Ignite", data.colorCodes.OFFENCE, {
|
||||
extra = "{0:output:IgniteChance}% {1:output:IgniteDPS} {2:output:IgniteDuration}s",
|
||||
flag = "ignite",
|
||||
{ label = "Chance to Ignite", { format = "{0:output:IgniteChance}%", { label = "Player modifiers", modName = "EnemyIgniteChance", cfg = "skill" }, { label = "Enemy modifiers", modName = "SelfIgniteChance", enemy = true }, }, },
|
||||
{ label = "Chance to Ignite", { format = "{0:output:IgniteChance}%", { breakdown = "IgniteChance" }, { label = "Player modifiers", modName = "EnemyIgniteChance", cfg = "skill" }, { label = "Enemy modifiers", modName = "SelfIgniteChance", enemy = true }, }, },
|
||||
{ label = "Total Increased", { format = "{0:mod:1}%", { modName = { "Damage", "FireDamage", "ElementalDamage" }, modType = "INC", cfg = "ignite" }, }, },
|
||||
{ label = "Total More", { format = "{0:mod:1}%", { modName = { "Damage", "FireDamage", "ElementalDamage" }, modType = "MORE", cfg = "ignite" }, }, },
|
||||
{ label = "Effective DPS Mod", flag = "effective", { format = "x {3:output:IgniteEffMult}", { breakdown = "IgniteEffMult" }, { label = "Enemy modifiers", modName = { "FireResist", "ElementalResist", "DamageTaken", "DotTaken", "FireDamageTaken", "BurningDamageTaken", "ElementalDamageTaken" }, enemy = true }, }, },
|
||||
@@ -215,10 +215,10 @@ return {
|
||||
} },
|
||||
{ 1, "MiscEffects", 1, "Other Effects", data.colorCodes.OFFENCE, {
|
||||
flag = "hit",
|
||||
{ label = "Chance to Shock", flag = "shock", { format = "{0:output:ShockChance}%", { label = "Player modifiers", modName = "EnemyShockChance", cfg = "skill" }, { label = "Enemy modifiers", modName = "SelfShockChance", enemy = true }, }, },
|
||||
{ label = "Shock Dur. Mod", flag = "shock", { format = "x {2:output:ShockDurationMod}", { label = "Player modifiers", modName = "EnemyShockDuration", cfg = "skill" }, { label = "Enemy modifiers", modName = "SelfShockDuration", enemy = true }, }, },
|
||||
{ label = "Chance to Freeze", flag = "freeze", { format = "{0:output:FreezeChance}%", { label = "Player modifiers", modName = "EnemyFreezeChance", cfg = "skill" }, { label = "Enemy modifiers", modName = "SelfFreezeChance", enemy = true }, }, },
|
||||
{ label = "Freeze Dur. Mod", flag = "freeze", { format = "x {2:output:FreezeDurationMod}", { label = "Player modifiers", modName = "EnemyFreezeDuration", cfg = "skill" }, { label = "Enemy modifiers", modName = "SelfFreezeDuration", enemy = true }, }, },
|
||||
{ label = "Chance to Shock", flag = "shock", { format = "{0:output:ShockChance}%", { breakdown = "ShockChance" }, { label = "Player modifiers", modName = "EnemyShockChance", cfg = "skill" }, { label = "Enemy modifiers", modName = "SelfShockChance", enemy = true }, }, },
|
||||
{ label = "Shock Dur. Mod", flag = "shock", { format = "x {2:output:ShockDurationMod}", { breakdown = "ShockDPS" }, { label = "Player modifiers", modName = "EnemyShockDuration", cfg = "skill" }, { label = "Enemy modifiers", modName = "SelfShockDuration", enemy = true }, }, },
|
||||
{ label = "Chance to Freeze", flag = "freeze", { format = "{0:output:FreezeChance}%", { breakdown = "FreezeChance" }, { label = "Player modifiers", modName = "EnemyFreezeChance", cfg = "skill" }, { label = "Enemy modifiers", modName = "SelfFreezeChance", enemy = true }, }, },
|
||||
{ label = "Freeze Dur. Mod", flag = "freeze", { format = "x {2:output:FreezeDurationMod}", { breakdown = "FreezeDPS" }, { label = "Player modifiers", modName = "EnemyFreezeDuration", cfg = "skill" }, { label = "Enemy modifiers", modName = "SelfFreezeDuration", enemy = true }, }, },
|
||||
{ label = "Stun Thresh. Mod", { format = "x {2:output:EnemyStunThresholdMod}", { modName = "EnemyStunThreshold", cfg = "skill" }, }, },
|
||||
{ label = "Stun Duration", { format = "{2:output:EnemyStunDuration}s", { breakdown = "EnemyStunDuration" }, { label = "Player modifiers", modName = { "EnemyStunDuration" }, cfg = "skill" }, { label = "Enemy modifiers", modName = { "StunRecovery" }, enemy = true }, }, },
|
||||
{ label = "Inc. Item Quantity", { format = "{0:mod:1}%", { modName = "LootQuantity", modType = "INC", cfg = "skill" }, }, },
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
-- Module: Calcs
|
||||
-- Performs all the offense and defense calculations.
|
||||
-- Here be dragons!
|
||||
-- This file is 2400 lines long, over half of which is in one function...
|
||||
-- This file is 2700 lines long, over half of which is in one function...
|
||||
--
|
||||
|
||||
local pairs = pairs
|
||||
@@ -933,7 +933,7 @@ local function mergeMainMods(env, repSlotName, repItem)
|
||||
end
|
||||
|
||||
-- Finalise environment and perform the calculations
|
||||
-- This function is 1300 lines long. Enjoy!
|
||||
-- This function is 1600 lines long. Enjoy!
|
||||
local function performCalcs(env)
|
||||
local modDB = env.modDB
|
||||
local enemyDB = env.enemyDB
|
||||
@@ -1811,6 +1811,32 @@ local function performCalcs(env)
|
||||
env.conversionTable[damageType] = dmgTable
|
||||
end
|
||||
env.conversionTable["Chaos"] = { mult = 1 }
|
||||
|
||||
-- Calculate mana cost (may be slightly off due to rounding differences)
|
||||
do
|
||||
local more = m_floor(modDB:Sum("MORE", skillCfg, "ManaCost") * 100 + 0.0001) / 100
|
||||
local inc = modDB:Sum("INC", skillCfg, "ManaCost")
|
||||
local base = modDB:Sum("BASE", skillCfg, "ManaCost")
|
||||
output.ManaCost = m_floor(m_max(0, (skillData.manaCost or 0) * more * (1 + inc / 100) + base))
|
||||
if env.mainSkill.skillTypes[SkillType.ManaCostPercent] and skillFlags.totem then
|
||||
output.ManaCost = m_floor(output.Mana * output.ManaCost / 100)
|
||||
end
|
||||
if breakdown and output.ManaCost ~= (skillData.manaCost or 0) then
|
||||
breakdown.ManaCost = {
|
||||
s_format("%d ^8(base mana cost)", skillData.manaCost or 0)
|
||||
}
|
||||
if more ~= 1 then
|
||||
t_insert(breakdown.ManaCost, s_format("x %.2f ^8(mana cost multiplier)", more))
|
||||
end
|
||||
if inc ~= 0 then
|
||||
t_insert(breakdown.ManaCost, s_format("x %.2f ^8(increased/reduced mana cost)", 1 + inc/100))
|
||||
end
|
||||
if base ~= 0 then
|
||||
t_insert(breakdown.ManaCost, s_format("- %d ^8(- mana cost)", -base))
|
||||
end
|
||||
t_insert(breakdown.ManaCost, s_format("= %d", output.ManaCost))
|
||||
end
|
||||
end
|
||||
|
||||
-- Calculate hit chance
|
||||
output.Accuracy = calcVal(modDB, "Accuracy", skillCfg)
|
||||
@@ -1925,9 +1951,7 @@ local function performCalcs(env)
|
||||
end
|
||||
end
|
||||
end
|
||||
if modDB:Sum("FLAG", skillCfg, "NoCritDamage") then
|
||||
output.CritMultiplier = 0
|
||||
elseif modDB:Sum("FLAG", skillCfg, "NoCritMultiplier") then
|
||||
if modDB:Sum("FLAG", skillCfg, "NoCritMultiplier") then
|
||||
output.CritMultiplier = 1
|
||||
else
|
||||
local extraDamage = 0.5 + modDB:Sum("BASE", skillCfg, "CritMultiplier") / 100
|
||||
@@ -1961,8 +1985,11 @@ local function performCalcs(env)
|
||||
end
|
||||
|
||||
-- Calculate hit damage for each damage type
|
||||
local totalMin, totalMax = 0, 0
|
||||
do
|
||||
local totalHitMin, totalHitMax = 0, 0
|
||||
local totalCritMin, totalCritMax = 0, 0
|
||||
for pass = 1, 2 do
|
||||
-- Pass 1 is critical strike damage, pass 2 is non-critical strike
|
||||
condList["CriticalStrike"] = (pass == 1)
|
||||
local hitSource = (env.mode_skillType == "ATTACK") and env.weaponData1 or env.mainSkill.skillData
|
||||
for _, damageType in ipairs(dmgTypeList) do
|
||||
local min, max
|
||||
@@ -1983,6 +2010,11 @@ local function performCalcs(env)
|
||||
end
|
||||
min = min * convMult
|
||||
max = max * convMult
|
||||
if pass == 1 then
|
||||
-- Apply crit multiplier
|
||||
min = min * output.CritMultiplier
|
||||
max = max * output.CritMultiplier
|
||||
end
|
||||
if (min ~= 0 or max ~= 0) and env.mode_effective then
|
||||
-- Apply enemy resistances and damage taken modifiers
|
||||
local preMult
|
||||
@@ -2001,7 +2033,10 @@ local function performCalcs(env)
|
||||
if skillFlags.projectile then
|
||||
taken = taken + enemyDB:Sum("INC", nil, "ProjectileDamageTaken")
|
||||
end
|
||||
local effMult = (1 - (resist - pen) / 100) * (1 + taken / 100)
|
||||
local effMult = (1 + taken / 100)
|
||||
if not isElemental[damageType] or not modDB:Sum("FLAG", skillCfg, "IgnoreElementalResistances") then
|
||||
effMult = effMult * (1 - (resist - pen) / 100)
|
||||
end
|
||||
min = min * effMult
|
||||
max = max * effMult
|
||||
if env.mode == "CALCS" then
|
||||
@@ -2023,25 +2058,31 @@ local function performCalcs(env)
|
||||
}
|
||||
end
|
||||
end
|
||||
if env.mode == "CALCS" then
|
||||
output[damageType.."Min"] = min
|
||||
output[damageType.."Max"] = max
|
||||
if pass == 1 then
|
||||
output[damageType.."CritAverage"] = (min + max) / 2
|
||||
totalCritMin = totalCritMin + min
|
||||
totalCritMax = totalCritMax + max
|
||||
else
|
||||
if env.mode == "CALCS" then
|
||||
output[damageType.."Min"] = min
|
||||
output[damageType.."Max"] = max
|
||||
end
|
||||
output[damageType.."HitAverage"] = (min + max) / 2
|
||||
totalHitMin = totalHitMin + min
|
||||
totalHitMax = totalHitMax + max
|
||||
end
|
||||
output[damageType.."Average"] = (min + max) / 2
|
||||
totalMin = totalMin + min
|
||||
totalMax = totalMax + max
|
||||
end
|
||||
end
|
||||
output.TotalMin = totalMin
|
||||
output.TotalMax = totalMax
|
||||
output.TotalMin = totalHitMin
|
||||
output.TotalMax = totalHitMax
|
||||
|
||||
-- Update enemy hit-by-damage-type conditions
|
||||
enemyDB.conditions.HitByFireDamage = output.FireAverage > 0
|
||||
enemyDB.conditions.HitByColdDamage = output.ColdAverage > 0
|
||||
enemyDB.conditions.HitByLightningDamage = output.LightningAverage > 0
|
||||
enemyDB.conditions.HitByFireDamage = output.FireHitAverage > 0
|
||||
enemyDB.conditions.HitByColdDamage = output.ColdHitAverage > 0
|
||||
enemyDB.conditions.HitByLightningDamage = output.LightningHitAverage > 0
|
||||
|
||||
-- Calculate average damage and final DPS
|
||||
output.AverageHit = (totalMin + totalMax) / 2 * output.CritEffect
|
||||
output.AverageHit = (totalHitMin + totalHitMax) / 2 * (1 - output.CritChance / 100) + (totalCritMin + totalCritMax) / 2 * output.CritChance / 100
|
||||
output.AverageDamage = output.AverageHit * output.HitChance / 100
|
||||
output.TotalDPS = output.AverageDamage * (output.HitSpeed or output.Speed) * (skillData.dpsMultiplier or 1)
|
||||
if env.mode == "CALCS" then
|
||||
@@ -2054,8 +2095,8 @@ local function performCalcs(env)
|
||||
if breakdown then
|
||||
if output.CritEffect ~= 1 then
|
||||
breakdown.AverageHit = {
|
||||
s_format("%.1f ^8(non-crit average)", (totalMin + totalMax) / 2),
|
||||
s_format("x %.3f ^8(crit effect modifier)", output.CritEffect),
|
||||
s_format("%.1f x (1 - %.4f) ^8(damage from non-crits)", (totalHitMin + totalHitMax) / 2, output.CritChance / 100),
|
||||
s_format("+ %.1f x %.4f ^8(damage from crits)", (totalCritMin + totalCritMax) / 2, output.CritChance / 100),
|
||||
s_format("= %.1f", output.AverageHit),
|
||||
}
|
||||
end
|
||||
@@ -2081,32 +2122,6 @@ local function performCalcs(env)
|
||||
t_insert(breakdown.TotalDPS, s_format("= %.1f", output.TotalDPS))
|
||||
end
|
||||
|
||||
-- Calculate mana cost (may be slightly off due to rounding differences)
|
||||
do
|
||||
local more = m_floor(modDB:Sum("MORE", skillCfg, "ManaCost") * 100 + 0.0001) / 100
|
||||
local inc = modDB:Sum("INC", skillCfg, "ManaCost")
|
||||
local base = modDB:Sum("BASE", skillCfg, "ManaCost")
|
||||
output.ManaCost = m_floor(m_max(0, (skillData.manaCost or 0) * more * (1 + inc / 100) + base))
|
||||
if env.mainSkill.skillTypes[SkillType.ManaCostPercent] and skillFlags.totem then
|
||||
output.ManaCost = m_floor(output.Mana * output.ManaCost / 100)
|
||||
end
|
||||
if breakdown and output.ManaCost ~= (skillData.manaCost or 0) then
|
||||
breakdown.ManaCost = {
|
||||
s_format("%d ^8(base mana cost)", skillData.manaCost or 0)
|
||||
}
|
||||
if more ~= 1 then
|
||||
t_insert(breakdown.ManaCost, s_format("x %.2f ^8(mana cost multiplier)", more))
|
||||
end
|
||||
if inc ~= 0 then
|
||||
t_insert(breakdown.ManaCost, s_format("x %.2f ^8(increased/reduced mana cost)", 1 + inc/100))
|
||||
end
|
||||
if base ~= 0 then
|
||||
t_insert(breakdown.ManaCost, s_format("- %d ^8(- mana cost)", -base))
|
||||
end
|
||||
t_insert(breakdown.ManaCost, s_format("= %d", output.ManaCost))
|
||||
end
|
||||
end
|
||||
|
||||
-- Calculate skill DOT components
|
||||
local dotCfg = {
|
||||
skillName = skillCfg.skillName,
|
||||
@@ -2133,13 +2148,14 @@ local function performCalcs(env)
|
||||
if damageType == "Physical" then
|
||||
resist = enemyDB:Sum("INC", nil, "PhysicalDamageReduction")
|
||||
else
|
||||
resist = enemyDB:Sum("BASE", nil, damageType.."Resist")
|
||||
if isElemental[damageType] then
|
||||
resist = resist + enemyDB:Sum("BASE", nil, "ElementalResist")
|
||||
taken = taken + enemyDB:Sum("INC", nil, "ElementalDamageTaken")
|
||||
end
|
||||
if damageType == "Fire" then
|
||||
taken = taken + enemyDB:Sum("INC", nil, "BurningDamageTaken")
|
||||
end
|
||||
resist = output["Enemy"..damageType.."Resist"]
|
||||
end
|
||||
effMult = (1 - resist / 100) * (1 + taken / 100)
|
||||
output[damageType.."DotEffMult"] = effMult
|
||||
@@ -2159,148 +2175,241 @@ local function performCalcs(env)
|
||||
end
|
||||
end
|
||||
|
||||
-- Calculate bleeding chance and damage
|
||||
skillFlags.bleed = false
|
||||
output.BleedChance = m_min(100, modDB:Sum("BASE", skillCfg, "BleedChance"))
|
||||
if canDeal.Physical and not modDB:Sum("FLAG", skillCfg, "CannotBleed") and output.BleedChance > 0 and output.PhysicalAverage > 0 then
|
||||
skillFlags.bleed = true
|
||||
skillFlags.duration = true
|
||||
local dotCfg = {
|
||||
slotName = skillCfg.slotName,
|
||||
flags = bor(band(skillCfg.flags, ModFlag.SourceMask), ModFlag.Dot, skillData.dotIsSpell and ModFlag.Spell or 0),
|
||||
keywordFlags = bor(skillCfg.keywordFlags, KeywordFlag.Bleed)
|
||||
}
|
||||
env.mainSkill.bleedCfg = dotCfg
|
||||
local baseVal = output.PhysicalAverage * output.CritEffect * 0.1
|
||||
local effMult = 1
|
||||
if env.mode_effective then
|
||||
local resist = enemyDB:Sum("INC", nil, "PhysicalDamageReduction")
|
||||
local taken = enemyDB:Sum("INC", dotCfg, "DamageTaken", "PhysicalDamageTaken", "DotTaken")
|
||||
effMult = (1 - resist / 100) * (1 + taken / 100)
|
||||
output["BleedEffMult"] = effMult
|
||||
if breakdown and effMult ~= 1 then
|
||||
breakdown.BleedEffMult = effMultBreakdown("Physical", resist, 0, taken, effMult)
|
||||
-- Calculate chance to inflict secondary dots/status effects
|
||||
condList["CriticalStrike"] = true
|
||||
if modDB:Sum("FLAG", skillCfg, "CannotBleed") then
|
||||
output.BleedChanceOnCrit = 0
|
||||
else
|
||||
output.BleedChanceOnCrit = m_min(100, modDB:Sum("BASE", skillCfg, "BleedChance"))
|
||||
end
|
||||
output.PoisonChanceOnCrit = m_min(100, modDB:Sum("BASE", skillCfg, "PoisonChance"))
|
||||
if modDB:Sum("FLAG", skillCfg, "CannotIgnite") then
|
||||
output.IgniteChanceOnCrit = 0
|
||||
else
|
||||
output.IgniteChanceOnCrit = 100
|
||||
end
|
||||
if modDB:Sum("FLAG", skillCfg, "CannotShock") then
|
||||
output.ShockChanceOnCrit = 0
|
||||
else
|
||||
output.ShockChanceOnCrit = 100
|
||||
end
|
||||
if modDB:Sum("FLAG", skillCfg, "CannotFreeze") then
|
||||
output.FreezeChanceOnCrit = 0
|
||||
else
|
||||
output.FreezeChanceOnCrit = 100
|
||||
end
|
||||
condList["CriticalStrike"] = false
|
||||
if modDB:Sum("FLAG", skillCfg, "CannotBleed") then
|
||||
output.BleedChanceOnHit = 0
|
||||
else
|
||||
output.BleedChanceOnHit = m_min(100, modDB:Sum("BASE", skillCfg, "BleedChance"))
|
||||
end
|
||||
output.PoisonChanceOnHit = m_min(100, modDB:Sum("BASE", skillCfg, "PoisonChance"))
|
||||
if modDB:Sum("FLAG", skillCfg, "CannotIgnite") then
|
||||
output.IgniteChanceOnHit = 0
|
||||
else
|
||||
output.IgniteChanceOnHit = m_min(100, modDB:Sum("BASE", skillCfg, "EnemyIgniteChance") + enemyDB:Sum("BASE", nil, "SelfIgniteChance"))
|
||||
end
|
||||
if modDB:Sum("FLAG", skillCfg, "CannotShock") then
|
||||
output.ShockChanceOnHit = 0
|
||||
else
|
||||
output.ShockChanceOnHit = m_min(100, modDB:Sum("BASE", skillCfg, "EnemyShockChance") + enemyDB:Sum("BASE", nil, "SelfShockChance"))
|
||||
end
|
||||
if modDB:Sum("FLAG", skillCfg, "CannotFreeze") then
|
||||
output.FreezeChanceOnHit = 0
|
||||
else
|
||||
output.FreezeChanceOnHit = m_min(100, modDB:Sum("BASE", skillCfg, "EnemyFreezeChance") + enemyDB:Sum("BASE", nil, "SelfFreezeChance"))
|
||||
if modDB:Sum("FLAG", skillCfg, "CritsDontAlwaysFreeze") then
|
||||
output.FreezeChanceOnCrit = output.FreezeChanceOnHit
|
||||
end
|
||||
end
|
||||
|
||||
local function calcSecondaryEffectBase(type, sourceHitDmg, sourceCritDmg)
|
||||
-- Calculate the inflict chance and base damage of a secondary effect (bleed/poison/ignite/shock/freeze)
|
||||
local chanceOnHit, chanceOnCrit = output[type.."ChanceOnHit"], output[type.."ChanceOnCrit"]
|
||||
local chanceFromHit = chanceOnHit * (1 - output.CritChance / 100)
|
||||
local chanceFromCrit = chanceOnCrit * output.CritChance / 100
|
||||
local chance = chanceFromHit + chanceFromCrit
|
||||
output[type.."Chance"] = chance
|
||||
local baseFromHit = sourceHitDmg * chanceFromHit / (chanceFromHit + chanceFromCrit)
|
||||
local baseFromCrit = sourceCritDmg * chanceFromCrit / (chanceFromHit + chanceFromCrit)
|
||||
local baseVal = baseFromHit + baseFromCrit
|
||||
if breakdown and chance ~= 0 then
|
||||
local breakdownChance = {
|
||||
s_format("Chance on Non-crit: %d%%", chanceOnHit),
|
||||
s_format("Chance on Crit: %d%%", chanceOnCrit),
|
||||
}
|
||||
if chanceOnHit ~= chanceOnCrit then
|
||||
t_insert(breakdownChance, "Combined chance:")
|
||||
t_insert(breakdownChance, s_format("%d x (1 - %.4f) ^8(chance from non-crits)", chanceOnHit, output.CritChance/100))
|
||||
t_insert(breakdownChance, s_format("+ %d x %.4f ^8(chance from crits)", chanceOnCrit, output.CritChance/100))
|
||||
t_insert(breakdownChance, s_format("= %.2f", chance))
|
||||
end
|
||||
breakdown[type.."Chance"] = breakdownChance
|
||||
end
|
||||
if breakdown and baseVal > 0 then
|
||||
local breakdownDPS = breakdown[type.."DPS"]
|
||||
if sourceHitDmg == sourceCritDmg then
|
||||
t_insert(breakdownDPS, "Base damage:")
|
||||
t_insert(breakdownDPS, s_format("%.1f ^8(source damage)",sourceHitDmg))
|
||||
else
|
||||
if baseFromHit > 0 then
|
||||
t_insert(breakdownDPS, "Base from Non-crits:")
|
||||
t_insert(breakdownDPS, s_format("%.1f ^8(source damage from non-crits)", sourceHitDmg))
|
||||
t_insert(breakdownDPS, s_format("x %.3f ^8(portion of instances created by non-crits)", chanceFromHit / (chanceFromHit + chanceFromCrit)))
|
||||
t_insert(breakdownDPS, s_format("= %.1f", baseFromHit))
|
||||
end
|
||||
if baseFromCrit > 0 then
|
||||
t_insert(breakdownDPS, "Base from Crits:")
|
||||
t_insert(breakdownDPS, s_format("%.1f ^8(source damage from crits)", sourceCritDmg))
|
||||
t_insert(breakdownDPS, s_format("x %.3f ^8(portion of instances created by crits)", chanceFromCrit / (chanceFromHit + chanceFromCrit)))
|
||||
t_insert(breakdownDPS, s_format("= %.1f", baseFromCrit))
|
||||
end
|
||||
if baseFromHit > 0 and baseFromCrit > 0 then
|
||||
t_insert(breakdownDPS, "Total base damage:")
|
||||
t_insert(breakdownDPS, s_format("%.1f + %.1f", baseFromHit, baseFromCrit))
|
||||
t_insert(breakdownDPS, s_format("= %.1f", baseVal))
|
||||
end
|
||||
end
|
||||
end
|
||||
local inc = modDB:Sum("INC", dotCfg, "Damage", "PhysicalDamage")
|
||||
local more = round(modDB:Sum("MORE", dotCfg, "Damage", "PhysicalDamage"), 2)
|
||||
output.BleedDPS = baseVal * (1 + inc/100) * more * effMult
|
||||
output.BleedDuration = 5 * calcMod(modDB, dotCfg, "Duration") * debuffDurationMult
|
||||
if breakdown then
|
||||
breakdown.BleedDPS = {
|
||||
"Base damage:",
|
||||
s_format("%.1f ^8(average physical non-crit damage)", output.PhysicalAverage)
|
||||
return baseVal
|
||||
end
|
||||
|
||||
-- Calculate bleeding chance and damage
|
||||
skillFlags.bleed = false
|
||||
if canDeal.Physical and (output.BleedChanceOnHit + output.BleedChanceOnCrit) > 0 then
|
||||
local sourceHitDmg = output.PhysicalHitAverage
|
||||
local sourceCritDmg = output.PhysicalCritAverage
|
||||
if sourceHitDmg + sourceCritDmg > 0 then
|
||||
skillFlags.bleed = true
|
||||
skillFlags.duration = true
|
||||
local dotCfg = {
|
||||
slotName = skillCfg.slotName,
|
||||
flags = bor(band(skillCfg.flags, ModFlag.SourceMask), ModFlag.Dot, skillData.dotIsSpell and ModFlag.Spell or 0),
|
||||
keywordFlags = bor(skillCfg.keywordFlags, KeywordFlag.Bleed)
|
||||
}
|
||||
if output.CritEffect ~= 1 then
|
||||
t_insert(breakdown.BleedDPS, s_format("x %.3f ^8(crit effect modifier)", output.CritEffect))
|
||||
env.mainSkill.bleedCfg = dotCfg
|
||||
if breakdown then
|
||||
breakdown.BleedDPS = { }
|
||||
end
|
||||
t_insert(breakdown.BleedDPS, "x 0.1 ^8(bleed deals 10% per second)")
|
||||
t_insert(breakdown.BleedDPS, s_format("= %.1f", baseVal, 1))
|
||||
t_insert(breakdown.BleedDPS, "Bleed DPS:")
|
||||
dotBreakdown(breakdown.BleedDPS, baseVal, inc, more, effMult, output.BleedDPS)
|
||||
if output.BleedDuration ~= 5 then
|
||||
breakdown.BleedDuration = {
|
||||
"5.00s ^8(base duration)"
|
||||
}
|
||||
if output.DurationMod ~= 1 then
|
||||
t_insert(breakdown.BleedDuration, s_format("x %.2f ^8(duration modifier)", calcMod(modDB, dotCfg, "Duration")))
|
||||
local baseVal = calcSecondaryEffectBase("Bleed", sourceHitDmg, sourceCritDmg) * 0.1
|
||||
local effMult = 1
|
||||
if env.mode_effective then
|
||||
local resist = enemyDB:Sum("INC", nil, "PhysicalDamageReduction")
|
||||
local taken = enemyDB:Sum("INC", dotCfg, "DamageTaken", "PhysicalDamageTaken", "DotTaken")
|
||||
effMult = (1 - resist / 100) * (1 + taken / 100)
|
||||
output["BleedEffMult"] = effMult
|
||||
if breakdown and effMult ~= 1 then
|
||||
breakdown.BleedEffMult = effMultBreakdown("Physical", resist, 0, taken, effMult)
|
||||
end
|
||||
if debuffDurationMult ~= 1 then
|
||||
t_insert(breakdown.BleedDuration, s_format("/ %.2f ^8(debuff expires slower/faster)", 1 / debuffDurationMult))
|
||||
end
|
||||
local inc = modDB:Sum("INC", dotCfg, "Damage", "PhysicalDamage")
|
||||
local more = round(modDB:Sum("MORE", dotCfg, "Damage", "PhysicalDamage"), 2)
|
||||
output.BleedDPS = baseVal * (1 + inc/100) * more * effMult
|
||||
local durationMod = calcMod(modDB, dotCfg, "Duration")
|
||||
output.BleedDuration = 5 * durationMod * debuffDurationMult
|
||||
if breakdown then
|
||||
t_insert(breakdown.BleedDPS, "x 0.1 ^8(bleed deals 10% per second)")
|
||||
t_insert(breakdown.BleedDPS, s_format("= %.1f", baseVal))
|
||||
t_insert(breakdown.BleedDPS, "Bleed DPS:")
|
||||
dotBreakdown(breakdown.BleedDPS, baseVal, inc, more, effMult, output.BleedDPS)
|
||||
if output.BleedDuration ~= 5 then
|
||||
breakdown.BleedDuration = {
|
||||
"5.00s ^8(base duration)"
|
||||
}
|
||||
if durationMod ~= 1 then
|
||||
t_insert(breakdown.BleedDuration, s_format("x %.2f ^8(duration modifier)", durationMod))
|
||||
end
|
||||
if debuffDurationMult ~= 1 then
|
||||
t_insert(breakdown.BleedDuration, s_format("/ %.2f ^8(debuff expires slower/faster)", 1 / debuffDurationMult))
|
||||
end
|
||||
t_insert(breakdown.BleedDuration, s_format("= %.2fs", output.BleedDuration))
|
||||
end
|
||||
t_insert(breakdown.BleedDuration, s_format("= %.2fs", output.BleedDuration))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Calculate poison chance and damage
|
||||
skillFlags.poison = false
|
||||
output.PoisonChance = m_min(100, modDB:Sum("BASE", skillCfg, "PoisonChance"))
|
||||
if canDeal.Chaos and output.PoisonChance > 0 and (output.PhysicalAverage > 0 or output.ChaosAverage > 0) then
|
||||
skillFlags.poison = true
|
||||
skillFlags.duration = true
|
||||
local dotCfg = {
|
||||
slotName = skillCfg.slotName,
|
||||
flags = bor(band(skillCfg.flags, ModFlag.SourceMask), ModFlag.Dot, skillData.dotIsSpell and ModFlag.Spell or 0),
|
||||
keywordFlags = bor(skillCfg.keywordFlags, KeywordFlag.Poison)
|
||||
}
|
||||
env.mainSkill.poisonCfg = dotCfg
|
||||
local poisonCritEffect = 1 - output.CritChance / 100 + output.CritChance / 100 * output.CritMultiplier * modDB:Sum("MORE", skillCfg, "PoisonDamageOnCrit")
|
||||
local baseVal = (output.PhysicalAverage + output.ChaosAverage) * poisonCritEffect * 0.08
|
||||
local effMult = 1
|
||||
if env.mode_effective then
|
||||
local resist = output["EnemyChaosResist"]
|
||||
local taken = enemyDB:Sum("INC", nil, "DamageTaken", "ChaosDamageTaken", "DotTaken")
|
||||
effMult = (1 - resist / 100) * (1 + taken / 100)
|
||||
output["PoisonEffMult"] = effMult
|
||||
if breakdown then
|
||||
breakdown.PoisonEffMult = effMultBreakdown("Chaos", resist, 0, taken, effMult)
|
||||
end
|
||||
end
|
||||
local inc = modDB:Sum("INC", dotCfg, "Damage", "ChaosDamage")
|
||||
local more = round(modDB:Sum("MORE", dotCfg, "Damage", "ChaosDamage"), 2)
|
||||
output.PoisonDPS = baseVal * (1 + inc/100) * more * effMult
|
||||
local durationBase
|
||||
if skillData.poisonDurationIsSkillDuration then
|
||||
durationBase = skillData.duration
|
||||
else
|
||||
durationBase = 2
|
||||
end
|
||||
output.PoisonDuration = durationBase * calcMod(modDB, dotCfg, "Duration") * debuffDurationMult
|
||||
output.PoisonDamage = output.PoisonDPS * output.PoisonDuration
|
||||
if breakdown then
|
||||
breakdown.PoisonDPS = { }
|
||||
if poisonCritEffect ~= output.CritEffect then
|
||||
t_insert(breakdown.PoisonDPS, "Crit effect modifier for poison base damage:")
|
||||
t_insert(breakdown.PoisonDPS, s_format("(1 - %g) ^8(portion of damage from non-crits)", output.CritChance/100))
|
||||
t_insert(breakdown.PoisonDPS, s_format("+ (%g x %g x %g) ^8(portion of damage from crits)", output.CritChance/100, output.CritMultiplier, modDB:Sum("MORE", skillCfg, "PoisonDamageOnCrit")))
|
||||
t_insert(breakdown.PoisonDPS, s_format("= %.3f", poisonCritEffect))
|
||||
end
|
||||
t_insert(breakdown.PoisonDPS, "Base damage:")
|
||||
t_insert(breakdown.PoisonDPS, s_format("%.1f ^8(average physical + chaos non-crit damage)", output.PhysicalAverage + output.ChaosAverage))
|
||||
if output.CritEffect ~= 1 then
|
||||
t_insert(breakdown.PoisonDPS, s_format("x %.3f ^8(crit effect modifier)", poisonCritEffect))
|
||||
end
|
||||
t_insert(breakdown.PoisonDPS, "x 0.08 ^8(poison deals 8% per second)")
|
||||
t_insert(breakdown.PoisonDPS, s_format("= %.1f", baseVal, 1))
|
||||
t_insert(breakdown.PoisonDPS, "Poison DPS:")
|
||||
dotBreakdown(breakdown.PoisonDPS, baseVal, inc, more, effMult, output.PoisonDPS)
|
||||
if output.PoisonDuration ~= 2 then
|
||||
breakdown.PoisonDuration = {
|
||||
s_format("%.2fs ^8(base duration)", durationBase)
|
||||
}
|
||||
if output.DurationMod ~= 1 then
|
||||
t_insert(breakdown.PoisonDuration, s_format("x %.2f ^8(duration modifier)", calcMod(modDB, dotCfg, "Duration")))
|
||||
end
|
||||
if debuffDurationMult ~= 1 then
|
||||
t_insert(breakdown.PoisonDuration, s_format("/ %.2f ^8(debuff expires slower/faster)", 1 / debuffDurationMult))
|
||||
end
|
||||
t_insert(breakdown.PoisonDuration, s_format("= %.2fs", output.PoisonDuration))
|
||||
end
|
||||
breakdown.PoisonDamage = {
|
||||
s_format("%.1f ^8(damage per second)", output.PoisonDPS),
|
||||
s_format("x %.2fs ^8(poison duration)", output.PoisonDuration),
|
||||
s_format("= %.1f ^8damage per poison stack", output.PoisonDamage),
|
||||
if canDeal.Chaos and (output.PoisonChanceOnHit + output.PoisonChanceOnCrit) > 0 then
|
||||
local sourceHitDmg = output.PhysicalHitAverage + output.ChaosHitAverage
|
||||
local sourceCritDmg = output.PhysicalCritAverage + output.ChaosCritAverage
|
||||
if sourceHitDmg + sourceCritDmg > 0 then
|
||||
skillFlags.poison = true
|
||||
skillFlags.duration = true
|
||||
local dotCfg = {
|
||||
slotName = skillCfg.slotName,
|
||||
flags = bor(band(skillCfg.flags, ModFlag.SourceMask), ModFlag.Dot, skillData.dotIsSpell and ModFlag.Spell or 0),
|
||||
keywordFlags = bor(skillCfg.keywordFlags, KeywordFlag.Poison)
|
||||
}
|
||||
env.mainSkill.poisonCfg = dotCfg
|
||||
if breakdown then
|
||||
breakdown.PoisonDPS = { }
|
||||
end
|
||||
local baseVal = calcSecondaryEffectBase("Poison", sourceHitDmg, sourceCritDmg * modDB:Sum("MORE", skillCfg, "PoisonDamageOnCrit")) * 0.08
|
||||
local effMult = 1
|
||||
if env.mode_effective then
|
||||
local resist = output["EnemyChaosResist"]
|
||||
local taken = enemyDB:Sum("INC", nil, "DamageTaken", "ChaosDamageTaken", "DotTaken")
|
||||
effMult = (1 - resist / 100) * (1 + taken / 100)
|
||||
output["PoisonEffMult"] = effMult
|
||||
if breakdown then
|
||||
breakdown.PoisonEffMult = effMultBreakdown("Chaos", resist, 0, taken, effMult)
|
||||
end
|
||||
end
|
||||
local inc = modDB:Sum("INC", dotCfg, "Damage", "ChaosDamage")
|
||||
local more = round(modDB:Sum("MORE", dotCfg, "Damage", "ChaosDamage"), 2)
|
||||
output.PoisonDPS = baseVal * (1 + inc/100) * more * effMult
|
||||
local durationBase
|
||||
if skillData.poisonDurationIsSkillDuration then
|
||||
durationBase = skillData.duration
|
||||
else
|
||||
durationBase = 2
|
||||
end
|
||||
local durationMod = calcMod(modDB, dotCfg, "Duration")
|
||||
output.PoisonDuration = durationBase * durationMod * debuffDurationMult
|
||||
output.PoisonDamage = output.PoisonDPS * output.PoisonDuration
|
||||
if breakdown then
|
||||
t_insert(breakdown.PoisonDPS, "x 0.08 ^8(poison deals 8% per second)")
|
||||
t_insert(breakdown.PoisonDPS, s_format("= %.1f", baseVal, 1))
|
||||
t_insert(breakdown.PoisonDPS, "Poison DPS:")
|
||||
dotBreakdown(breakdown.PoisonDPS, baseVal, inc, more, effMult, output.PoisonDPS)
|
||||
if output.PoisonDuration ~= 2 then
|
||||
breakdown.PoisonDuration = {
|
||||
s_format("%.2fs ^8(base duration)", durationBase)
|
||||
}
|
||||
if durationMod ~= 1 then
|
||||
t_insert(breakdown.PoisonDuration, s_format("x %.2f ^8(duration modifier)", durationMod))
|
||||
end
|
||||
if debuffDurationMult ~= 1 then
|
||||
t_insert(breakdown.PoisonDuration, s_format("/ %.2f ^8(debuff expires slower/faster)", 1 / debuffDurationMult))
|
||||
end
|
||||
t_insert(breakdown.PoisonDuration, s_format("= %.2fs", output.PoisonDuration))
|
||||
end
|
||||
breakdown.PoisonDamage = {
|
||||
s_format("%.1f ^8(damage per second)", output.PoisonDPS),
|
||||
s_format("x %.2fs ^8(poison duration)", output.PoisonDuration),
|
||||
s_format("= %.1f ^8damage per poison stack", output.PoisonDamage),
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Calculate ignite chance and damage
|
||||
skillFlags.ignite = false
|
||||
skillFlags.igniteCanStack = false
|
||||
if modDB:Sum("FLAG", skillCfg, "CannotIgnite") then
|
||||
output.IgniteChance = 0
|
||||
else
|
||||
local igniteMode = env.configInput.igniteMode or "AVERAGE"
|
||||
output.IgniteChance = m_min(100, modDB:Sum("BASE", skillCfg, "EnemyIgniteChance") + enemyDB:Sum("BASE", nil, "SelfIgniteChance"))
|
||||
local sourceDmg = 0
|
||||
if canDeal.Fire and (output.IgniteChanceOnHit + output.IgniteChanceOnCrit) > 0 then
|
||||
local sourceHitDmg = 0
|
||||
local sourceCritDmg = 0
|
||||
if canDeal.Fire and not modDB:Sum("FLAG", skillCfg, "FireCannotIgnite") then
|
||||
sourceDmg = sourceDmg + output.FireAverage
|
||||
sourceHitDmg = sourceHitDmg + output.FireHitAverage
|
||||
sourceCritDmg = sourceCritDmg + output.FireCritAverage
|
||||
end
|
||||
if canDeal.Cold and modDB:Sum("FLAG", skillCfg, "ColdCanIgnite") then
|
||||
sourceDmg = sourceDmg + output.ColdAverage
|
||||
sourceHitDmg = sourceHitDmg + output.ColdHitAverage
|
||||
sourceCritDmg = sourceCritDmg + output.ColdCritAverage
|
||||
end
|
||||
if canDeal.Fire and (output.IgniteChance > 0 or igniteMode == "CRIT") and sourceDmg > 0 then
|
||||
if sourceHitDmg + sourceCritDmg > 0 then
|
||||
skillFlags.ignite = true
|
||||
local dotCfg = {
|
||||
slotName = skillCfg.slotName,
|
||||
@@ -2308,12 +2417,16 @@ local function performCalcs(env)
|
||||
keywordFlags = skillCfg.keywordFlags,
|
||||
}
|
||||
env.mainSkill.igniteCfg = dotCfg
|
||||
local baseVal
|
||||
local igniteMode = env.configInput.igniteMode or "AVERAGE"
|
||||
if igniteMode == "CRIT" then
|
||||
baseVal = sourceDmg * output.CritMultiplier * 0.2
|
||||
else
|
||||
baseVal = sourceDmg * output.CritEffect * 0.2
|
||||
output.IgniteChanceOnHit = 0
|
||||
end
|
||||
if breakdown then
|
||||
breakdown.IgniteDPS = {
|
||||
s_format("Ignite mode: %s ^8(can be changed in the Configuration tab)", igniteMode == "CRIT" and "Crit Damage" or "Average Damage")
|
||||
}
|
||||
end
|
||||
local baseVal = calcSecondaryEffectBase("Ignite", sourceHitDmg, sourceCritDmg) * 0.2
|
||||
local effMult = 1
|
||||
if env.mode_effective then
|
||||
local resist = m_min(enemyDB:Sum("BASE", nil, "FireResist", "ElementalResist"), 75)
|
||||
@@ -2334,20 +2447,6 @@ local function performCalcs(env)
|
||||
skillFlags.igniteCanStack = true
|
||||
end
|
||||
if breakdown then
|
||||
breakdown.IgniteDPS = {
|
||||
s_format("Ignite mode: %s ^8(can be changed in the Configuration tab)", igniteMode == "CRIT" and "Crit Damage" or "Average Damage"),
|
||||
"Base damage:",
|
||||
s_format("%.1f ^8(average non-crit damage from sources)", sourceDmg),
|
||||
}
|
||||
if igniteMode == "CRIT" then
|
||||
if output.CritMultiplier ~= 1 then
|
||||
t_insert(breakdown.IgniteDPS, s_format("x %.2f ^8(crit multiplier)", output.CritMultiplier))
|
||||
end
|
||||
else
|
||||
if output.CritEffect ~= 1 then
|
||||
t_insert(breakdown.IgniteDPS, s_format("x %.3f ^8(crit effect modifier)", output.CritEffect))
|
||||
end
|
||||
end
|
||||
t_insert(breakdown.IgniteDPS, "x 0.2 ^8(ignite deals 20% per second)")
|
||||
t_insert(breakdown.IgniteDPS, s_format("= %.1f", baseVal, 1))
|
||||
t_insert(breakdown.IgniteDPS, "Ignite DPS:")
|
||||
@@ -2377,43 +2476,59 @@ local function performCalcs(env)
|
||||
|
||||
-- Calculate shock and freeze chance + duration modifier
|
||||
skillFlags.shock = false
|
||||
if modDB:Sum("FLAG", skillCfg, "CannotShock") then
|
||||
output.ShockChance = 0
|
||||
else
|
||||
output.ShockChance = m_min(100, modDB:Sum("BASE", skillCfg, "EnemyShockChance") + enemyDB:Sum("BASE", nil, "SelfShockChance"))
|
||||
local sourceDmg = 0
|
||||
if (output.ShockChanceOnHit + output.ShockChanceOnCrit) > 0 then
|
||||
local sourceHitDmg = 0
|
||||
local sourceCritDmg = 0
|
||||
if canDeal.Lightning and not modDB:Sum("FLAG", skillCfg, "LightningCannotShock") then
|
||||
sourceDmg = sourceDmg + output.LightningAverage
|
||||
sourceHitDmg = sourceHitDmg + output.LightningHitAverage
|
||||
sourceCritDmg = sourceCritDmg + output.LightningCritAverage
|
||||
end
|
||||
if canDeal.Physical and modDB:Sum("FLAG", skillCfg, "PhysicalCanShock") then
|
||||
sourceDmg = sourceDmg + output.PhysicalAverage
|
||||
sourceHitDmg = sourceHitDmg + output.PhysicalHitAverage
|
||||
sourceCritDmg = sourceCritDmg + output.PhysicalCritAverage
|
||||
end
|
||||
if canDeal.Fire and modDB:Sum("FLAG", skillCfg, "FireCanShock") then
|
||||
sourceDmg = sourceDmg + output.FireAverage
|
||||
sourceHitDmg = sourceHitDmg + output.FireHitAverage
|
||||
sourceCritDmg = sourceCritDmg + output.FireCritAverage
|
||||
end
|
||||
if canDeal.Chaos and modDB:Sum("FLAG", skillCfg, "ChaosCanShock") then
|
||||
sourceDmg = sourceDmg + output.ChaosAverage
|
||||
sourceHitDmg = sourceHitDmg + output.ChaosHitAverage
|
||||
sourceCritDmg = sourceCritDmg + output.ChaosCritAverage
|
||||
end
|
||||
if output.ShockChance > 0 and sourceDmg > 0 then
|
||||
if sourceHitDmg + sourceCritDmg > 0 then
|
||||
skillFlags.shock = true
|
||||
if breakdown then
|
||||
breakdown.ShockDPS = { }
|
||||
end
|
||||
local baseVal = calcSecondaryEffectBase("Shock", sourceHitDmg, sourceCritDmg)
|
||||
output.ShockDurationMod = 1 + modDB:Sum("INC", dotCfg, "EnemyShockDuration") / 100 + enemyDB:Sum("INC", nil, "SelfShockDuration") / 100
|
||||
if breakdown then
|
||||
t_insert(breakdown.ShockDPS, s_format("For shock to apply, target must have no more than %d life.", baseVal * 20 * output.ShockDurationMod))
|
||||
end
|
||||
end
|
||||
end
|
||||
skillFlags.freeze = false
|
||||
if modDB:Sum("FLAG", skillCfg, "CannotFreeze") then
|
||||
output.FreezeChance = 0
|
||||
else
|
||||
output.FreezeChance = m_min(100, modDB:Sum("BASE", skillCfg, "EnemyFreezeChance") + enemyDB:Sum("BASE", nil, "SelfFreezeChance"))
|
||||
local sourceDmg = 0
|
||||
if (output.FreezeChanceOnHit + output.FreezeChanceOnCrit) > 0 then
|
||||
local sourceHitDmg = 0
|
||||
local sourceCritDmg = 0
|
||||
if canDeal.Cold and not modDB:Sum("FLAG", skillCfg, "ColdCannotFreeze") then
|
||||
sourceDmg = sourceDmg + output.ColdAverage
|
||||
sourceHitDmg = sourceHitDmg + output.ColdHitAverage
|
||||
sourceCritDmg = sourceCritDmg + output.ColdCritAverage
|
||||
end
|
||||
if canDeal.Lightning and modDB:Sum("FLAG", skillCfg, "LightningCanFreeze") then
|
||||
sourceDmg = sourceDmg + output.LightningAverage
|
||||
sourceHitDmg = sourceHitDmg + output.LightningHitAverage
|
||||
sourceCritDmg = sourceCritDmg + output.LightningCritAverage
|
||||
end
|
||||
if output.FreezeChance > 0 and sourceDmg > 0 then
|
||||
if sourceHitDmg + sourceCritDmg > 0 then
|
||||
skillFlags.freeze = true
|
||||
if breakdown then
|
||||
breakdown.FreezeDPS = { }
|
||||
end
|
||||
local baseVal = calcSecondaryEffectBase("Freeze", sourceHitDmg, sourceCritDmg)
|
||||
output.FreezeDurationMod = 1 + modDB:Sum("INC", dotCfg, "EnemyFreezeDuration") / 100 + enemyDB:Sum("INC", nil, "SelfFreezeDuration") / 100
|
||||
if breakdown then
|
||||
t_insert(breakdown.FreezeDPS, s_format("For freeze to apply, target must have no more than %d life.", baseVal * 20 * output.FreezeDurationMod))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -288,6 +288,7 @@ local modFlagList = {
|
||||
-- List of modifier flags/tags that appear at the start of a line
|
||||
local preFlagList = {
|
||||
["^hits deal "] = { flags = ModFlag.Hit },
|
||||
["^critical strikes deal "] = { tag = { type = "Condition", var = "CriticalStrike" } },
|
||||
["^minions have "] = { keywordFlags = KeywordFlag.Minion },
|
||||
["^minions deal "] = { keywordFlags = KeywordFlag.Minion },
|
||||
["^attacks used by totems have "] = { keywordFlags = KeywordFlag.Totem },
|
||||
@@ -476,6 +477,8 @@ local specialModList = {
|
||||
["(%d+)%% less totem damage per totem"] = function(num) return { mod("Damage", "MORE", -num, nil, 0, KeywordFlag.Totem, { type = "PerStat", stat = "ActiveTotemLimit", div = 1 }) } end,
|
||||
["poison you inflict with critical strikes deals (%d+)%% more damage"] = function(num) return { mod("PoisonDamageOnCrit", "MORE", 100) } end,
|
||||
["bleeding you inflict on maimed enemies deals (%d+)%% more damage"] = function(num) return { mod("Damage", "MORE", num, nil, 0, KeywordFlag.Bleed, { type = "Condition", var = "EnemyMaimed"}) } end,
|
||||
["critical strikes ignore enemy monster elemental resistances"] = { flag("IgnoreElementalResistances", { type = "Condition", var = "CriticalStrike" }) },
|
||||
["non%-critical strikes penetrate (%d+)%% of enemy elemental resistances"] = function(num) return { mod("ElementalPenetration", "BASE", num, { type = "Condition", var = "CriticalStrike", neg = true }) } end,
|
||||
-- Special node types
|
||||
["(%d+)%% of block chance applied to spells"] = function(num) return { mod("BlockChanceConv", "BASE", num) } end,
|
||||
["(%d+)%% additional block chance with staves"] = function(num) return { mod("BlockChance", "BASE", num, { type = "Condition", var = "UsingStaff" }) } end,
|
||||
@@ -501,7 +504,7 @@ local specialModList = {
|
||||
["spells have an additional projectile"] = { mod("ProjectileCount", "BASE", 1, nil, ModFlag.Spell) },
|
||||
["skills chain %+(%d) times"] = function(num) return { mod("ChainCount", "BASE", num) } end,
|
||||
["reflects (%d+) physical damage to melee attackers"] = { },
|
||||
["critical strikes with daggers have a (%d+)%% chance to poison the enemy"] = function(num) return { mod("PoisonChance", "BASE", 0.3, nil, ModFlag.Dagger, { type = "PerStat", stat = "CritChance", div = 1 }) } end,
|
||||
["critical strikes with daggers have a (%d+)%% chance to poison the enemy"] = function(num) return { mod("PoisonChance", "BASE", num, nil, ModFlag.Dagger, { type = "Condition", var = "CriticalStrike" }) } end,
|
||||
-- Special item local modifiers
|
||||
["no physical damage"] = { mod("Misc", "LIST", { type = "WeaponData", key = "PhysicalMin" }), mod("Misc", "LIST", { type = "WeaponData", key = "PhysicalMax" }), mod("Misc", "LIST", { type = "WeaponData", key = "PhysicalDPS" }) },
|
||||
["all attacks with this weapon are critical strikes"] = { mod("Misc", "LIST", { type = "WeaponData", key = "critChance", value = 100 }) },
|
||||
@@ -539,11 +542,16 @@ local specialModList = {
|
||||
["your chaos damage can shock"] = { flag("ChaosCanShock") },
|
||||
["your physical damage can chill"] = { flag("PhysicalCanChill") },
|
||||
["your physical damage can shock"] = { flag("PhysicalCanShock") },
|
||||
["critical strikes do not always freeze"] = { flag("CritsDontAlwaysFreeze") },
|
||||
["your chaos damage poisons enemies"] = { mod("PoisonChance", "BASE", 100) },
|
||||
["you can inflict up to (%d+) ignites on an enemy"] = { flag("IgniteCanStack") },
|
||||
["melee attacks cause bleeding"] = { mod("BleedChance", "BASE", 100, nil, ModFlag.Melee) },
|
||||
["melee attacks poison on hit"] = { mod("PoisonChance", "BASE", 100, nil, ModFlag.Melee) },
|
||||
["attacks cause bleeding when hitting cursed enemies"] = { mod("BleedChance", "BASE", 100, { type = "Condition", var = "EnemyCursed" }) },
|
||||
["melee critical strikes cause bleeding"] = { mod("BleedChance", "BASE", 100, nil, ModFlag.Melee, { type = "Condition", var = "CriticalStrike" }) },
|
||||
["melee critical strikes have (%d+)%% chance to cause bleeding"] = function(num) return { mod("BleedChance", "BASE", num, nil, ModFlag.Melee, { type = "Condition", var = "CriticalStrike" }) } end,
|
||||
["melee critical strikes have (%d+)%% chance to poison the enemy"] = function(num) return { mod("PoisonChance", "BASE", num, nil, ModFlag.Melee, { type = "Condition", var = "CriticalStrike" }) } end,
|
||||
["causes bleeding on melee critical strike"] = { mod("BleedChance", "BASE", num, nil, ModFlag.Melee, { type = "Condition", var = "CriticalStrike" }) },
|
||||
["traps and mines deal (%d+)%-(%d+) additional physical damage"] = function(_, min, max) return { mod("PhysicalMin", "BASE", tonumber(min), nil, 0, bor(KeywordFlag.Trap, KeywordFlag.Mine)), mod("PhysicalMax", "BASE", tonumber(max), nil, 0, bor(KeywordFlag.Trap, KeywordFlag.Mine)) } end,
|
||||
["traps and mines deal (%d+) to (%d+) additional physical damage"] = function(_, min, max) return { mod("PhysicalMin", "BASE", tonumber(min), nil, 0, bor(KeywordFlag.Trap, KeywordFlag.Mine)), mod("PhysicalMax", "BASE", tonumber(max), nil, 0, bor(KeywordFlag.Trap, KeywordFlag.Mine)) } end,
|
||||
["traps and mines have a (%d+)%% chance to poison on hit"] = function(num) return { mod("PoisonChance", "BASE", num, nil, 0, bor(KeywordFlag.Trap, KeywordFlag.Mine)) } end,
|
||||
@@ -568,11 +576,12 @@ local specialModList = {
|
||||
["armour is increased by uncapped fire resistance"] = { mod("Armour", "INC", 1, { type = "PerStat", stat = "FireResistTotal", div = 1 }) },
|
||||
["evasion rating is increased by uncapped cold resistance"] = { mod("Evasion", "INC", 1, { type = "PerStat", stat = "ColdResistTotal", div = 1 }) },
|
||||
["critical strike chance is increased by uncapped lightning resistance"] = { mod("CritChance", "INC", 1, { type = "PerStat", stat = "LightningResistTotal", div = 1 }) },
|
||||
["critical strikes deal no damage"] = { flag("NoCritDamage") },
|
||||
["critical strikes deal no damage"] = { mod("Damage", "MORE", -100, { type = "Condition", var = "CriticalStrike" }) },
|
||||
["enemies chilled by you take (%d+)%% increased burning damage"] = function(num) return { mod("Misc", "LIST", { type = "EnemyModifier", mod = mod("BurningDamageTaken", "INC", num) }, { type = "Condition", var = "EnemyChilled" }) } end,
|
||||
["attacks with this weapon penetrate (%d+)%% elemental resistances"] = function(num) return { mod("ElementalPenetration", "BASE", num, { type = "Condition", var = "XHandAttack" }) } end,
|
||||
["attacks with this weapon deal double damage to chilled enemies"] = { mod("Damage", "MORE", 100, nil, ModFlag.Hit, { type = "Condition", var = "XHandAttack" }, { type = "Condition", var = "EnemyChilled" }) },
|
||||
["(%d+)%% of maximum life converted to energy shield"] = function(num) return { mod("LifeConvertToEnergyShield", "BASE", num) } end,
|
||||
["non%-critical strikes deal (%d+)%% damage"] = function(num) return { mod("Damage", "MORE", -100+num, nil, ModFlag.Hit, { type = "Condition", var = "CriticalStrike", neg = true }) } end,
|
||||
}
|
||||
local keystoneList = {
|
||||
-- List of keystones that can be found on uniques
|
||||
|
||||
Reference in New Issue
Block a user