Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for Holy Relic Nova triggering #4051

Merged
merged 9 commits into from
Feb 11, 2022
3 changes: 3 additions & 0 deletions src/Data/SkillStatMap.lua
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,9 @@ return {
["triggered_by_spiritual_cry"] = {
skill("triggeredByGeneralsCry", true, { type = "SkillType", skillType = SkillType.Melee }, { type = "SkillType", skillType = SkillType.Attack }),
},
["holy_relic_trigger_on_parent_attack_%"] = {
skill("triggeredByParentAttack", true, { type = "SkillType", skillType = SkillType.Triggerable }),
},
["skill_can_own_mirage_archers"] = {
skill("triggeredByMirageArcher", true, { type = "SkillType", skillType = SkillType.MirageArcherCanUse }),
},
Expand Down
10 changes: 9 additions & 1 deletion src/Modules/Build.lua
Original file line number Diff line number Diff line change
Expand Up @@ -427,8 +427,10 @@ function buildMode:Init(dbFileName, buildName, buildXML, convertBuild)
}
self.minionDisplayStats = {
{ stat = "AverageDamage", label = "Average Damage", fmt = ".1f", compPercent = true },
{ stat = "Speed", label = "Attack/Cast Rate", fmt = ".2f", compPercent = true },
{ stat = "Speed", label = "Attack/Cast Rate", fmt = ".2f", compPercent = true, condFunc = function(v,o) return v > 0 and (o.TriggerTime or 0) == 0 end },
{ stat = "HitSpeed", label = "Hit Rate", fmt = ".2f" },
{ stat = "ServerTriggerRate", label = "Trigger Rate", fmt = ".2f", compPercent = true, condFunc = function(v,o) return (o.TriggerTime or 0) ~= 0 end },
{ stat = "Speed", label = "Effective Trigger Rate", fmt = ".2f", compPercent = true, condFunc = function(v,o) return (o.TriggerTime or 0) ~= 0 and o.ServerTriggerRate ~= o.Speed end },
{ stat = "TotalDPS", label = "Total DPS", fmt = ".1f", compPercent = true },
{ stat = "TotalDot", label = "DoT DPS", fmt = ".1f", compPercent = true },
{ stat = "WithDotDPS", label = "Total DPS inc. DoT", fmt = ".1f", compPercent = true, condFunc = function(v,o) return v ~= o.TotalDPS and (o.PoisonDPS or 0) == 0 and (o.IgniteDPS or 0) == 0 and (o.ImpaleDPS or 0) == 0 and (o.BleedDPS or 0) == 0 end },
Expand Down Expand Up @@ -1370,6 +1372,12 @@ function buildMode:RefreshStatList()
end
if self.calcsTab.mainEnv.minion then
t_insert(statBoxList, { height = 18, "^7Minion:" })
if self.calcsTab.mainEnv.minion.mainSkill.infoMessage then
t_insert(statBoxList, { height = 14, align = "CENTER_X", x = 140, colorCodes.CUSTOM .. self.calcsTab.mainEnv.minion.mainSkill.infoMessage})
if self.calcsTab.mainEnv.minion.mainSkill.infoMessage2 then
t_insert(statBoxList, { height = 14, align = "CENTER_X", x = 140, "^8" .. self.calcsTab.mainEnv.minion.mainSkill.infoMessage2})
end
end
self:AddDisplayStatList(self.minionDisplayStats, self.calcsTab.mainEnv.minion)
t_insert(statBoxList, { height = 10 })
t_insert(statBoxList, { height = 18, "^7Player:" })
Expand Down
2 changes: 1 addition & 1 deletion src/Modules/CalcOffence.lua
Original file line number Diff line number Diff line change
Expand Up @@ -389,7 +389,7 @@ function calcs.offence(env, actor, activeSkill)

runSkillFunc("initialFunc")

local isTriggered = skillData.triggeredWhileChannelling or skillData.triggeredByCoC or skillData.triggeredByMeleeKill or skillData.triggeredByCospris or skillData.triggeredByMjolner or skillData.triggeredByUnique or skillData.triggeredByFocus or skillData.triggeredByCraft or skillData.triggeredByManaSpent
local isTriggered = skillData.triggeredWhileChannelling or skillData.triggeredByCoC or skillData.triggeredByMeleeKill or skillData.triggeredByCospris or skillData.triggeredByMjolner or skillData.triggeredByUnique or skillData.triggeredByFocus or skillData.triggeredByCraft or skillData.triggeredByManaSpent or skillData.triggeredByParentAttack
skillCfg.skillCond["SkillIsTriggered"] = skillData.triggered or isTriggered
if skillCfg.skillCond["SkillIsTriggered"] then
skillFlags.triggered = true
Expand Down
89 changes: 79 additions & 10 deletions src/Modules/CalcPerform.lua
Original file line number Diff line number Diff line change
Expand Up @@ -40,24 +40,30 @@ local function findTriggerSkill(env, skill, source, triggerRate, reqManaCost)
end

-- Calculate Trigger Rate Cap accounting for ICDR
local function getTriggerActionTriggerRate(baseActionCooldown, env, breakdown, focus)
local function getTriggerActionTriggerRate(baseActionCooldown, env, breakdown, focus, minion)
local icdr = 1
local cooldownOverride = false
if focus then
icdr = calcLib.mod(env.player.mainSkill.skillModList, env.player.mainSkill.skillCfg, "FocusCooldownRecovery")
env.player.mainSkill.skillData.focused = true
elseif minion then
icdr = calcLib.mod(env.minion.mainSkill.skillModList, env.minion.mainSkill.skillCfg, "CooldownRecovery")
cooldownOverride = env.minion.mainSkill.skillModList:Override(env.minion.mainSkill.skillCfg, "CooldownRecovery")
else
icdr = calcLib.mod(env.player.mainSkill.skillModList, env.player.mainSkill.skillCfg, "CooldownRecovery")
cooldownOverride = env.player.mainSkill.skillModList:Override(env.player.mainSkill.skillCfg, "CooldownRecovery")
end

-- check if there is a hard cooldown recovery override
local cooldownOverride = env.player.mainSkill.skillModList:Override(env.player.mainSkill.skillCfg, "CooldownRecovery")

local modActionCooldown = cooldownOverride or (baseActionCooldown / icdr)
local rateCapAdjusted = m_ceil(modActionCooldown * data.misc.ServerTickRate) / data.misc.ServerTickRate
local extraICDRNeeded = m_ceil((modActionCooldown - rateCapAdjusted + data.misc.ServerTickTime) * icdr * 1000)
if breakdown then
if cooldownOverride then
env.player.mainSkill.skillFlags.hasOverride = true
if minion then
env.minion.mainSkill.skillFlags.hasOverride = true
else
env.player.mainSkill.skillFlags.hasOverride = true
end
breakdown.ActionTriggerRate = {
s_format("%.2f ^8(hard override of cooldown of triggered skill)", cooldownOverride),
s_format(""),
Expand Down Expand Up @@ -97,7 +103,6 @@ local function calcMultiSpellRotationImpact(env, skillRotation, sourceAPS)
local next_trigger = 0
local trigger_increment = 1 / sourceAPS
local wasted = 0

while time < SIM_TIME do
local currIndex = index

Expand Down Expand Up @@ -144,16 +149,17 @@ local function calcMultiSpellRotationImpact(env, skillRotation, sourceAPS)
trigRateTable.extraSimInfo = "Good Job! There are no wasted trigger opportunities"
end
for _, sd in ipairs(skillRotation) do
if cacheSkillUUID(env.player.mainSkill) == sd.uuid then
if cacheSkillUUID(env.player.mainSkill) == sd.uuid or env.minion and cacheSkillUUID(env.minion.mainSkill) == sd.uuid then
mainRate = sd.count / SIM_TIME
end
t_insert(trigRateTable.rates, { name = sd.uuid, rate = sd.count / SIM_TIME })
end

return mainRate, trigRateTable
end

-- Calculate the actual Trigger rate of active skill causing the trigger
local function calcActualTriggerRate(env, source, sourceAPS, spellCount, output, breakdown, dualWield)
local function calcActualTriggerRate(env, source, sourceAPS, spellCount, output, breakdown, dualWield, minion)
-- Get action trigger rate
if sourceAPS == nil and source.skillTypes[SkillType.Channel] then
output.ActionTriggerRate = 1 / (source.skillData.triggerTime or 1)
Expand All @@ -167,7 +173,11 @@ local function calcActualTriggerRate(env, source, sourceAPS, spellCount, output,
}
end
else
output.ActionTriggerRate = getTriggerActionTriggerRate(env.player.mainSkill.skillData.cooldown, env, breakdown)
if minion then
output.ActionTriggerRate = getTriggerActionTriggerRate(env.minion.mainSkill.skillData.cooldown, env, breakdown, false, minion)
else
output.ActionTriggerRate = getTriggerActionTriggerRate(env.player.mainSkill.skillData.cooldown, env, breakdown, false, minion)
end
end
local trigRate
local skillRotationImpact = #spellCount
Expand Down Expand Up @@ -2312,7 +2322,7 @@ function calcs.perform(env, avoidCache)
env.player.mainSkill.infoTrigger = "Cospri"
end
end

-- Mjolner
if env.player.mainSkill.skillData.triggeredByMjolner and not env.player.mainSkill.skillFlags.minion then
local spellCount = {}
Expand Down Expand Up @@ -2853,6 +2863,65 @@ function calcs.perform(env, avoidCache)
end
end

-- Triggered by parent attack
if env.minion and env.player.mainSkill.minion then
if env.minion.mainSkill.skillData.triggeredByParentAttack then
local spellCount = {}
local trigRate = 0
local source = nil
for _, skill in ipairs(env.player.activeSkillList) do
if skill.skillTypes[SkillType.Attack] and skill ~= env.player.mainSkill then
source, trigRate = findTriggerSkill(env, skill, source, trigRate)
end
end

local icdr = calcLib.mod(env.minion.mainSkill.skillModList, env.minion.mainSkill.skillCfg, "CooldownRecovery")
t_insert(spellCount, { uuid = cacheSkillUUID(env.minion.mainSkill), cd = env.minion.mainSkill.skillData.cooldown / icdr, next_trig = 0, count = 0 })

if not source then
env.minion.mainSkill.skillData.triggeredByParentAttack = nil
env.minion.mainSkill.infoMessage = "No triggering Skill Found"
env.minion.mainSkill.infoMessage2 = "DPS reported assuming regular cast"
env.minion.mainSkill.infoTrigger = ""
else
env.minion.mainSkill.skillData.triggered = true
local uuid = cacheSkillUUID(source)

local sourceAPS = GlobalCache.cachedData["CACHE"][uuid].Speed

-- Get action trigger rate
-- Ugly hack to get data to go into minion actor instead of player
local breakdownBak = breakdown
local outputBak = output
breakdown = env.minion.breakdown
output = env.minion.output

trigRate = calcActualTriggerRate(env, source, sourceAPS, spellCount, output, breakdown, false, true)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason:
trigRate = calcActualTriggerRate(env, source, sourceAPS, spellCount, output, breakdown, false, true)
isn't
trigRate = calcActualTriggerRate(env, source, sourceAPS, spellCount, env.minion.output, env.minion.breakdown, false, true)?

That should work and eliminate the need for the "ugly hack".


-- Account for chance to hit
local sourceHitChance = GlobalCache.cachedData["CACHE"][uuid].HitChance
trigRate = trigRate * sourceHitChance / 100
if breakdown then
breakdown.Speed = {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use env.minion.breakdown instead of breakdown

s_format("%.2fs ^8(adjusted trigger rate)", output.ServerTriggerRate),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use env.minion.output instead of output

s_format("x %.2f%% ^8(%s Hit chance)", sourceHitChance, source.activeEffect.grantedEffect.name),
s_format("= %.2f ^8per second", trigRate),
}
end

breakdown = breakdownBak
output = outputBak
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unnecessary if we do it the other way


-- Account for Trigger-related INC/MORE modifiers
addTriggerIncMoreMods(env.minion.mainSkill, env.minion.mainSkill)
env.minion.mainSkill.skillData.triggerRate = trigRate
env.minion.mainSkill.skillData.triggerSource = source
env.minion.mainSkill.infoMessage = "Triggering Skill: " .. source.activeEffect.grantedEffect.name
env.minion.mainSkill.infoTrigger = "Parent attack"
end
end
end

-- Fix the configured impale stacks on the enemy
-- If the config is missing (blank), then use the maximum number of stacks
-- If the config is larger than the maximum number of stacks, replace it with the correct maximum
Expand Down