Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions src/Data/SkillStatMap.lua
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ return {
skill("triggeredWhileChannelling", true, { type = "SkillType", skillType = SkillType.Triggerable }, { type = "SkillType", skillType = SkillType.Spell }),
},
["skill_triggered_by_snipe"] = {
skill("triggered", true, { type = "SkillType", skillType = SkillType.Triggerable }),
skill("triggeredBySnipe", true, { type = "SkillType", skillType = SkillType.Triggerable }),
},
["triggered_by_spiritual_cry"] = {
skill("triggeredByGeneralsCry", true, { type = "SkillType", skillType = SkillType.Melee }, { type = "SkillType", skillType = SkillType.Attack }),
Expand Down Expand Up @@ -1716,11 +1716,11 @@ return {
["channelled_skill_damage_+%"] = {
mod("Damage", "INC", nil, 0, 0, { type = "SkillType", skillType = SkillType.Channel }),
},
["snipe_triggered_skill_hit_damage_+%_final_per_stage"] = {
mod("Damage", "MORE", nil, ModFlag.Hit, 0, { type = "Multiplier", var = "SnipeStage", limitVar = "SnipeStagesMax" }),
},
["snipe_triggered_skill_ailment_damage_+%_final_per_stage"] = {
mod("Damage", "MORE", nil, ModFlag.Ailment, 0, { type = "Multiplier", var = "SnipeStage", limitVar = "SnipeStagesMax" }),
mod("snipeAilmentMulti", "BASE", nil),
},
["snipe_triggered_skill_hit_damage_+%_final_per_stage"] = {
mod("snipeHitMulti", "BASE", nil),
},
["snipe_triggered_skill_damage_+%_final"] = {
mod("Damage", "MORE", nil),
Expand Down
7 changes: 2 additions & 5 deletions src/Data/Skills/act_dex.lua
Original file line number Diff line number Diff line change
Expand Up @@ -7888,8 +7888,8 @@ skills["ScourgeArrow"] = {
},
statDescriptionScope = "skill_stat_descriptions",
castTime = 1,
initialFunc = function(activeSkill, output)
activeSkill.skillData.dpsMultiplier = 1 / math.max(activeSkill.skillModList:Sum("BASE", cfg, "Multiplier:ScourgeArrowStage"), 1)
preDamageFunc = function(activeSkill, output)
activeSkill.skillData.hitTimeMultiplier = math.max(activeSkill.skillModList:Sum("BASE", cfg, "Multiplier:ScourgeArrowStage") - 0.5, 0.5) --First stage takes 0.5x time to channel compared to subsequent stages
end,
parts = {
{
Expand Down Expand Up @@ -10797,9 +10797,6 @@ skills["ChannelledSnipe"] = {
},
statDescriptionScope = "skill_stat_descriptions",
castTime = 1,
initialFunc = function(activeSkill, output)
activeSkill.skillData.dpsMultiplier = 1 / math.min(math.max(activeSkill.skillModList:Sum("BASE", cfg, "Multiplier:SnipeStage"), 1), activeSkill.skillModList:Sum("BASE", cfg, "Multiplier:SnipeStagesMax"))
end,
statMap = {
["snipe_max_stacks"] = {
mod("Multiplier:SnipeStagesMax", "BASE", nil, 0, 0, { type = "GlobalEffect", effectType = "Buff", unscalable = true }),
Expand Down
18 changes: 15 additions & 3 deletions src/Data/Skills/act_int.lua
Original file line number Diff line number Diff line change
Expand Up @@ -2790,6 +2790,11 @@ skills["DivineTempest"] = {
skillTypes = { [SkillType.Spell] = true, [SkillType.Area] = true, [SkillType.Damage] = true, [SkillType.Channel] = true, [SkillType.Lightning] = true, [SkillType.Totemable] = true, [SkillType.AreaSpell] = true, [SkillType.Physical] = true, },
statDescriptionScope = "skill_stat_descriptions",
castTime = 0.22,
preDamageFunc = function(activeSkill, output)
local skillCfg = activeSkill.skillCfg
local skillModList = activeSkill.skillModList
activeSkill.skillData.hitTimeMultiplier = math.max(skillModList:Sum("BASE", skillCfg, "Multiplier:DivineIreStage") / (1 + skillModList:Sum("BASE", skillCfg, "Multiplier:DivineIreUniqueEnemyCount") + skillModList:Sum("BASE", skillCfg, "NormalEnemyHitMultiplier") * skillModList:Sum("BASE", skillCfg, "Multiplier:DivineIreNormalEnemyCount")), 1)
end,
parts = {
{
name = "Channelling",
Expand All @@ -2811,13 +2816,16 @@ skills["DivineTempest"] = {
["divine_tempest_ailment_damage_+%_final_per_stage"] = {
mod("Damage", "MORE", nil, 0, KeywordFlag.Ailment, { type = "Multiplier", var = "DivineIreStageAfterFirst" }),
},
["divine_tempest_stage_on_hitting_normal_magic_%_chance"] = {
mod("NormalEnemyHitMultiplier", "BASE", nil),
div = 100
},
},
baseFlags = {
spell = true,
area = true,
},
baseMods = {
skill("showAverage", true, { type = "SkillPart", skillPart = 2 }),
mod("Multiplier:DivineIreMaxStages", "BASE", 20, 0, 0, { type = "SkillPart", skillPart = 2 }),
skill("radius", 38),
},
Expand Down Expand Up @@ -4060,6 +4068,9 @@ skills["Flameblast"] = {
skillTypes = { [SkillType.Spell] = true, [SkillType.Damage] = true, [SkillType.Area] = true, [SkillType.Totemable] = true, [SkillType.Fire] = true, [SkillType.Channel] = true, [SkillType.AreaSpell] = true, [SkillType.Cooldown] = true, },
statDescriptionScope = "skill_stat_descriptions",
castTime = 0.2,
preDamageFunc = function(activeSkill, output)
activeSkill.skillData.hitTimeMultiplier = math.max(activeSkill.skillModList:Sum("BASE", activeSkill.skillCfg, "Multiplier:FlameblastStage") - activeSkill.skillModList:Sum("BASE", activeSkill.skillCfg, "Multiplier:FlameblastMinimumStage"), 1)
end,
statMap = {
["charged_blast_spell_damage_+%_final_per_stack"] = {
mod("Damage", "MORE", nil, ModFlag.Hit, 0, { type = "Multiplier", var = "FlameblastStageAfterFirst" }),
Expand All @@ -4085,7 +4096,6 @@ skills["Flameblast"] = {
},
baseMods = {
skill("radius", 2),
skill("showAverage", true),
mod("PvpTvalueMultiplier", "MORE", 100, 0, 0, { type = "Multiplier", var = "FlameblastStageAfterFirst" }),
},
qualityStats = {
Expand Down Expand Up @@ -6186,6 +6196,9 @@ skills["ExpandingFireCone"] = {
skillTypes = { [SkillType.Spell] = true, [SkillType.Damage] = true, [SkillType.Totemable] = true, [SkillType.Fire] = true, [SkillType.Channel] = true, [SkillType.Area] = true, [SkillType.AreaSpell] = true, [SkillType.Cooldown] = true, },
statDescriptionScope = "skill_stat_descriptions",
castTime = 0.2,
preDamageFunc = function(activeSkill, output)
activeSkill.skillData.hitTimeMultiplier = math.max(activeSkill.skillModList:Sum("BASE", activeSkill.skillCfg, "Multiplier:IncinerateStage") - activeSkill.skillModList:Sum("BASE", activeSkill.skillCfg, "Multiplier:IncinerateMinimumStage") - 0.4175, 0.5825) --First stage takes 0.5825x time to channel compared to subsequent stages
end,
parts = {
{
name = "Channelling",
Expand Down Expand Up @@ -6231,7 +6244,6 @@ skills["ExpandingFireCone"] = {
area = true,
},
baseMods = {
skill("showAverage", true, { type = "SkillPart", skillPart = 2 }),
skill("radius", 25),
skill("radiusLabel", "Flame Length:"),
skill("radiusSecondary", 20),
Expand Down
7 changes: 2 additions & 5 deletions src/Export/Skills/act_dex.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1484,8 +1484,8 @@ local skills, mod, flag, skill = ...

#skill ScourgeArrow
#flags attack projectile
initialFunc = function(activeSkill, output)
activeSkill.skillData.dpsMultiplier = 1 / math.max(activeSkill.skillModList:Sum("BASE", cfg, "Multiplier:ScourgeArrowStage"), 1)
preDamageFunc = function(activeSkill, output)
activeSkill.skillData.hitTimeMultiplier = math.max(activeSkill.skillModList:Sum("BASE", cfg, "Multiplier:ScourgeArrowStage") - 0.5, 0.5) --First stage takes 0.5x time to channel compared to subsequent stages
end,
parts = {
{
Expand Down Expand Up @@ -2032,9 +2032,6 @@ local skills, mod, flag, skill = ...

#skill ChannelledSnipe
#flags attack projectile
initialFunc = function(activeSkill, output)
activeSkill.skillData.dpsMultiplier = 1 / math.min(math.max(activeSkill.skillModList:Sum("BASE", cfg, "Multiplier:SnipeStage"), 1), activeSkill.skillModList:Sum("BASE", cfg, "Multiplier:SnipeStagesMax"))
end,
statMap = {
["snipe_max_stacks"] = {
mod("Multiplier:SnipeStagesMax", "BASE", nil, 0, 0, { type = "GlobalEffect", effectType = "Buff", unscalable = true }),
Expand Down
18 changes: 15 additions & 3 deletions src/Export/Skills/act_int.txt
Original file line number Diff line number Diff line change
Expand Up @@ -631,6 +631,11 @@ local skills, mod, flag, skill = ...

#skill DivineTempest
#flags spell area
preDamageFunc = function(activeSkill, output)
local skillCfg = activeSkill.skillCfg
local skillModList = activeSkill.skillModList
activeSkill.skillData.hitTimeMultiplier = math.max(skillModList:Sum("BASE", skillCfg, "Multiplier:DivineIreStage") / (1 + skillModList:Sum("BASE", skillCfg, "Multiplier:DivineIreUniqueEnemyCount") + skillModList:Sum("BASE", skillCfg, "NormalEnemyHitMultiplier") * skillModList:Sum("BASE", skillCfg, "Multiplier:DivineIreNormalEnemyCount")), 1)
end,
parts = {
{
name = "Channelling",
Expand All @@ -652,8 +657,11 @@ local skills, mod, flag, skill = ...
["divine_tempest_ailment_damage_+%_final_per_stage"] = {
mod("Damage", "MORE", nil, 0, KeywordFlag.Ailment, { type = "Multiplier", var = "DivineIreStageAfterFirst" }),
},
["divine_tempest_stage_on_hitting_normal_magic_%_chance"] = {
mod("NormalEnemyHitMultiplier", "BASE", nil),
div = 100
},
},
#baseMod skill("showAverage", true, { type = "SkillPart", skillPart = 2 })
#baseMod mod("Multiplier:DivineIreMaxStages", "BASE", 20, 0, 0, { type = "SkillPart", skillPart = 2 })
#baseMod skill("radius", 38)
#mods
Expand Down Expand Up @@ -882,6 +890,9 @@ local skills, mod, flag, skill = ...

#skill Flameblast
#flags spell area
preDamageFunc = function(activeSkill, output)
activeSkill.skillData.hitTimeMultiplier = math.max(activeSkill.skillModList:Sum("BASE", activeSkill.skillCfg, "Multiplier:FlameblastStage") - activeSkill.skillModList:Sum("BASE", activeSkill.skillCfg, "Multiplier:FlameblastMinimumStage"), 1)
end,
statMap = {
["charged_blast_spell_damage_+%_final_per_stack"] = {
mod("Damage", "MORE", nil, ModFlag.Hit, 0, { type = "Multiplier", var = "FlameblastStageAfterFirst" }),
Expand All @@ -902,7 +913,6 @@ local skills, mod, flag, skill = ...
},
},
#baseMod skill("radius", 2)
#baseMod skill("showAverage", true)
#baseMod mod("PvpTvalueMultiplier", "MORE", 100, 0, 0, { type = "Multiplier", var = "FlameblastStageAfterFirst" })
#mods

Expand Down Expand Up @@ -1367,6 +1377,9 @@ local skills, mod, flag, skill = ...

#skill ExpandingFireCone
#flags spell area
preDamageFunc = function(activeSkill, output)
activeSkill.skillData.hitTimeMultiplier = math.max(activeSkill.skillModList:Sum("BASE", activeSkill.skillCfg, "Multiplier:IncinerateStage") - activeSkill.skillModList:Sum("BASE", activeSkill.skillCfg, "Multiplier:IncinerateMinimumStage") - 0.4175, 0.5825) --First stage takes 0.5825x time to channel compared to subsequent stages
end,
parts = {
{
name = "Channelling",
Expand Down Expand Up @@ -1407,7 +1420,6 @@ local skills, mod, flag, skill = ...
--Display Only
},
},
#baseMod skill("showAverage", true, { type = "SkillPart", skillPart = 2 })
#baseMod skill("radius", 25)
#baseMod skill("radiusLabel", "Flame Length:")
#baseMod skill("radiusSecondary", 20)
Expand Down
1 change: 1 addition & 0 deletions src/Modules/Build.lua
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,7 @@ function buildMode:Init(dbFileName, buildName, buildXML, convertBuild)
{ stat = "Speed", label = "Effective Trigger Rate", fmt = ".2f", compPercent = true, condFunc = function(v,o) return (o.TriggerTime or 0) ~= 0 end },
{ stat = "WarcryCastTime", label = "Cast Time", fmt = ".2fs", compPercent = true, lowerIsBetter = true, flag = "warcry" },
{ stat = "HitSpeed", label = "Hit Rate", fmt = ".2f", compPercent = true, condFunc = function(v,o) return not o.TriggerTime end },
{ stat = "HitTime", label = "Channel Time", fmt = ".2fs", compPercent = true, condFunc = function(v,o) return not o.TriggerTime end },
{ stat = "TrapThrowingTime", label = "Trap Throwing Time", fmt = ".2fs", compPercent = true, lowerIsBetter = true, },
{ stat = "TrapCooldown", label = "Trap Cooldown", fmt = ".3fs", lowerIsBetter = true },
{ stat = "MineLayingTime", label = "Mine Throwing Time", fmt = ".2fs", compPercent = true, lowerIsBetter = true, },
Expand Down
2 changes: 1 addition & 1 deletion src/Modules/CalcActiveSkill.lua
Original file line number Diff line number Diff line change
Expand Up @@ -455,7 +455,7 @@ function calcs.buildActiveSkillModList(env, activeSkill)
activeSkill.skillData.manaReservationPercent = level.manaReservationPercent
end
-- Handle multiple triggers situation and if triggered by a trigger skill save a reference to the trigger.
local match = skillEffect.grantedEffect.addSkillTypes and activeSkill.skillTypes[SkillType.Triggerable] and (not skillFlags.disable)
local match = skillEffect.grantedEffect.addSkillTypes and (not skillFlags.disable)
if match and skillEffect.grantedEffect.isTrigger then
if activeSkill.triggeredBy then
skillFlags.disable = true
Expand Down
91 changes: 50 additions & 41 deletions src/Modules/CalcOffence.lua
Original file line number Diff line number Diff line change
Expand Up @@ -751,10 +751,6 @@ function calcs.offence(env, actor, activeSkill)
end
end
end
--Snipe doesn't grab the max stages multiplier from the gem when granted by Assailum so we add it back here
if skillModList:Flag(nil, "TriggeredByAssailum") and activeSkill.skillTypes[SkillType.Triggerable] then
skillModList:NewMod("Multiplier:SnipeStagesMax", "BASE", 6, "Snipe Max Stages", { type = "GlobalEffect", effectType = "Buff", unscalable = true })
end
if skillModList:Sum("BASE", nil, "CritMultiplierAppliesToDegen") > 0 then
for i, value in ipairs(skillModList:Tabulate("BASE", skillCfg, "CritMultiplier")) do
local mod = value.mod
Expand Down Expand Up @@ -1534,40 +1530,6 @@ function calcs.offence(env, actor, activeSkill)
skillModList:NewMod("PhysicalMax", "BASE", m_floor(output.ManaCost * multiplier), "Sacrificial Zeal", ModFlag.Spell)
end

-- account for Manaforged Arrows
if skillData.triggeredByManaPercentSpent and skillData.triggerSource then
local reqManaCostMulti = skillData.TriggerSkillManaSpentMultiRequirement
local uuid = cacheSkillUUID(skillData.triggerSource)
if GlobalCache.cachedData["CACHE"][uuid] then
local cachedTriggerData = GlobalCache.cachedData["CACHE"][uuid]
local manaThreshold = output.ManaCost * reqManaCostMulti
local manaSpendPerSec = cachedTriggerData.ManaCost * cachedTriggerData.Speed
local manaSpendTriggerRate = manaSpendPerSec / manaThreshold
output.SourceTriggerRate = manaSpendTriggerRate
local trigRate = m_min(manaSpendTriggerRate, skillData.triggerRate)
-- Account for chance to trigger
local manaforgeTriggerChance = 100.0
trigRate = trigRate * manaforgeTriggerChance / 100.0
if breakdown then
t_insert(breakdown.SimData, s_format(""))
t_insert(breakdown.SimData, s_format("and"))
t_insert(breakdown.SimData, s_format(""))
t_insert(breakdown.SimData, s_format("(%d ^8(trigger mana cost)", cachedTriggerData.ManaCost))
t_insert(breakdown.SimData, s_format("x %.2f) ^8(trigger attack speed)", cachedTriggerData.Speed))
t_insert(breakdown.SimData, s_format("/ (%d ^8(triggered skill mana cost)", output.ManaCost))
t_insert(breakdown.SimData, s_format("x %.2f) ^8(manaforge skill multiplier)", reqManaCostMulti))
t_insert(breakdown.SimData, s_format(""))
t_insert(breakdown.SimData, s_format("Trigger Skill Mana-spending trigger rate:"))
t_insert(breakdown.SimData, s_format("= %.2f ^8per second", manaSpendTriggerRate))
breakdown.ServerTriggerRate = {
s_format("%.2f ^8(smaller of 'cap' and 'skill' trigger rates)", trigRate),
}
end
activeSkill.skillData.triggerRate = trigRate
output.ServerTriggerRate = trigRate
end
end

runSkillFunc("preDamageFunc")

-- Handle corpse and enemy explosions
Expand Down Expand Up @@ -1982,7 +1944,7 @@ function calcs.offence(env, actor, activeSkill)
output.Speed = output.Speed * totemActionSpeed
output.CastRate = output.Speed
end
if output.Cooldown then
if output.Cooldown and not activeSkill.skillTypes[SkillType.Channel] then
output.Speed = m_min(output.Speed, 1 / output.Cooldown * output.Repeats)
end
if output.Cooldown and skillFlags.selfCast then
Expand Down Expand Up @@ -2036,9 +1998,14 @@ function calcs.offence(env, actor, activeSkill)
end
elseif skillData.hitTimeMultiplier and output.Time and not skillData.triggeredOnDeath then
output.HitTime = output.Time * skillData.hitTimeMultiplier
output.HitSpeed = 1 / output.HitTime
if output.Cooldown and skillData.triggered then
output.HitSpeed = 1 / (m_max(output.HitTime, output.Cooldown))
elseif output.Cooldown then
output.HitSpeed = 1 / (output.HitTime + output.Cooldown)
else
output.HitSpeed = 1 / output.HitTime
end
end

-- Other Misc DPS multipliers (like custom source)
skillData.dpsMultiplier = ( skillData.dpsMultiplier or 1 ) * ( 1 + skillModList:Sum("INC", cfg, "DPS") / 100 ) * skillModList:More(cfg, "DPS")
if env.configInput.repeatMode == "FINAL" or skillModList:Flag(nil, "OnlyFinalRepeat") then
Expand All @@ -2056,6 +2023,7 @@ function calcs.offence(env, actor, activeSkill)
combineStat("HitChance", "AVERAGE")
combineStat("Speed", "AVERAGE")
combineStat("HitSpeed", "OR")
combineStat("HitTime", "OR")
if output.Speed == 0 then
output.Time = 0
else
Expand All @@ -2073,6 +2041,47 @@ function calcs.offence(env, actor, activeSkill)
}
end
end
if skillData.hitTimeOverride and not skillData.triggeredOnDeath then
output.HitTime = skillData.hitTimeOverride
output.HitSpeed = 1 / output.HitTime
elseif skillData.hitTimeMultiplier and output.Time and not skillData.triggeredOnDeath then
output.HitTime = output.Time * skillData.hitTimeMultiplier
if output.Cooldown and skillData.triggered then
output.HitSpeed = 1 / (m_max(output.HitTime, output.Cooldown))
elseif output.Cooldown then
output.HitSpeed = 1 / (output.HitTime + output.Cooldown)
else
output.HitSpeed = m_min(1 / output.HitTime, data.misc.ServerTickRate)
end
end
end
if breakdown then
if skillData.hitTimeOverride and not skillData.triggeredOnDeath then
breakdown.HitSpeed = { }
t_insert(breakdown.HitSpeed, s_format("1 / %.2f ^8(hit time override)", output.HitTime))
t_insert(breakdown.HitSpeed, s_format("= %.2f", output.HitSpeed))
elseif skillData.hitTimeMultiplier and output.Time and not skillData.triggeredOnDeath then
breakdown.HitTime = { }
if m_floor(skillData.hitTimeMultiplier) ~= skillData.hitTimeMultiplier then
t_insert(breakdown.HitTime, s_format(colorCodes.CUSTOM.."NOTE: First stage has a %.2fx channel time multiplier", skillData.hitTimeMultiplier - m_floor(skillData.hitTimeMultiplier)))
end
if isAttack then
t_insert(breakdown.HitTime, s_format("%.2f ^8(attack time)", output.Time))
else
t_insert(breakdown.HitTime, s_format("%.2f ^8(cast time)", output.Time))
end
t_insert(breakdown.HitTime, s_format("x %.2f ^8(channel time multiplier)", skillData.hitTimeMultiplier))
t_insert(breakdown.HitTime, s_format("= %.2f", output.HitTime))
breakdown.HitSpeed = { }
if output.Cooldown and skillData.triggered then
t_insert(breakdown.HitSpeed, s_format("1 / min(%.2f, %.2f) ^8min(hit time, cooldown)", output.HitTime, output.Cooldown))
elseif output.Cooldown then
t_insert(breakdown.HitSpeed, s_format("1 / (%.2f + %.2f) ^8(hit time + cooldown)", output.HitTime, output.Cooldown))
else
t_insert(breakdown.HitSpeed, s_format("1 / %.2f ^8(hit time)", output.HitTime))
end
t_insert(breakdown.HitSpeed, s_format("= %.2f", output.HitSpeed))
end
end

-- Grab quantity multiplier
Expand Down
2 changes: 2 additions & 0 deletions src/Modules/CalcSections.lua
Original file line number Diff line number Diff line change
Expand Up @@ -500,6 +500,8 @@ return {
{ label = "Skill Trigger Rate", flag = "triggered", notFlag = "focused", { format = "{2:output:SkillTriggerRate}", { breakdown = "SkillTriggerRate" }, { breakdown = "SimData" }, }, },
{ label = "Skill Trigger Rate", flagList = {"triggered", "focused"}, { format = "{2:output:SkillTriggerRate}", { breakdown = "SkillTriggerRate" }, { breakdown = "SimData" }, { modName = "FocusCooldownRecovery", modType = "INC", cfg = "skill", }, }, },
{ label = "Cast time", flag = "spell", notFlag = "triggered", { format = "{2:output:Time}s", }, },
{ label = "Channel time", haveOutput = "HitTime", { format = "{2:output:HitTime}s", { breakdown = "HitTime" } }, },
{ label = "Hit Rate", haveOutput = "HitSpeed", { format = "{2:output:HitSpeed}", { breakdown = "HitSpeed" } }, },
} }
} },
{ 1, "Crit", 1, colorCodes.OFFENCE, {{ defaultCollapsed = false, label = "Crits", data = {
Expand Down
Loading