Skip to content

Commit e1d74ed

Browse files
authored
Fix Warcry trigger rate calculations (#8025)
* FIX: use variables instead of constants for trigger simulation * FIX: trigger rate calculations for warcry triggers * FIX: spelling
1 parent a6130c8 commit e1d74ed

File tree

1 file changed

+62
-30
lines changed

1 file changed

+62
-30
lines changed

src/Modules/CalcTriggers.lua

Lines changed: 62 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -65,19 +65,21 @@ local function packageSkillDataForSimulation(skill, env)
6565
return { uuid = cacheSkillUUID(skill, env), cd = skill.skillData.cooldown, cdOverride = skill.skillModList:Override(skill.skillCfg, "CooldownRecovery"), addsCastTime = processAddedCastTime(skill), icdr = calcLib.mod(skill.skillModList, skill.skillCfg, "CooldownRecovery"), addedCooldown = skill.skillModList:Sum("BASE", skill.skillCfg, "CooldownRecovery")}
6666
end
6767

68+
local function defaultComparer(env, uuid, source, triggerRate)
69+
local cachedSpeed = GlobalCache.cachedData[env.mode][uuid].HitSpeed or GlobalCache.cachedData[env.mode][uuid].Speed
70+
return (not source and cachedSpeed) or (cachedSpeed and cachedSpeed > (triggerRate or 0))
71+
end
72+
6873
-- Identify the trigger action skill for trigger conditions, take highest Attack Per Second
6974
local function findTriggerSkill(env, skill, source, triggerRate, comparer)
70-
local comparer = comparer or function(uuid, source, triggerRate)
71-
local cachedSpeed = GlobalCache.cachedData[env.mode][uuid].HitSpeed or GlobalCache.cachedData[env.mode][uuid].Speed
72-
return (not source and cachedSpeed) or (cachedSpeed and cachedSpeed > (triggerRate or 0))
73-
end
75+
local comparer = comparer or defaultComparer
7476

7577
local uuid = cacheSkillUUID(skill, env)
7678
if not GlobalCache.cachedData[env.mode][uuid] or env.mode == "CALCULATOR" then
7779
calcs.buildActiveSkill(env, env.mode, skill, uuid)
7880
end
7981

80-
if GlobalCache.cachedData[env.mode][uuid] and comparer(uuid, source, triggerRate) and (skill.skillFlags and not skill.skillFlags.disable) and (skill.skillCfg and not skill.skillCfg.skillCond["usedByMirage"]) and not skill.skillTypes[SkillType.OtherThingUsesSkill] then
82+
if GlobalCache.cachedData[env.mode][uuid] and comparer(env, uuid, source, triggerRate) and (skill.skillFlags and not skill.skillFlags.disable) and (skill.skillCfg and not skill.skillCfg.skillCond["usedByMirage"]) and not skill.skillTypes[SkillType.OtherThingUsesSkill] then
8183
return skill, GlobalCache.cachedData[env.mode][uuid].HitSpeed or GlobalCache.cachedData[env.mode][uuid].Speed, uuid
8284
end
8385
return source, triggerRate, source and cacheSkillUUID(source, env)
@@ -106,7 +108,7 @@ function calcMultiSpellRotationImpact(env, skillRotation, sourceRate, triggerCD,
106108
if skillRotation[currentIndex].next_trig <= next_trigger then -- Skill at current index off cooldown, Trigger it.
107109
skillRotation[currentIndex].count = skillRotation[currentIndex].count + 1
108110
-- Cooldown starts at the beginning of current tick and ends at the next tick after cooldown expiration
109-
skillRotation[currentIndex].next_trig = ceil_b(floor_b(next_trigger, 0.033) + skillRotation[currentIndex].cd, 0.033)
111+
skillRotation[currentIndex].next_trig = ceil_b(floor_b(next_trigger, data.misc.ServerTickTime) + skillRotation[currentIndex].cd, data.misc.ServerTickTime)
110112
break
111113
end
112114
currentIndex = (currentIndex % skillCount) + 1 -- Current skill on cooldown, try the next one.
@@ -425,7 +427,7 @@ local function defaultTriggerHandler(env, config)
425427
t_insert(breakdown.EffectiveSourceRate, s_format("%.2f ^8(%s %s)", trigRate, config.sourceName or source.activeEffect.grantedEffect.name, config.useCastRate and "cast rate" or "attack rate"))
426428
end
427429
end
428-
430+
429431
-- Dual wield triggers
430432
if trigRate and source and env.player.weaponData1.type and env.player.weaponData2.type and not source.skillData.doubleHitsWhenDualWielding and (source.skillTypes[SkillType.Melee] or source.skillTypes[SkillType.Attack]) and actor.mainSkill.triggeredBy and actor.mainSkill.triggeredBy.grantedEffect.support and actor.mainSkill.triggeredBy.grantedEffect.fromItem then
431433
trigRate = trigRate / 2
@@ -447,24 +449,6 @@ local function defaultTriggerHandler(env, config)
447449
end
448450
end
449451

450-
-- Battlemage's Cry uptime
451-
if actor.mainSkill.skillData.triggeredByBattleMageCry and GlobalCache.cachedData[env.mode][uuid] and source and source.skillTypes[SkillType.Melee] then
452-
local battleMageUptime = GlobalCache.cachedData[env.mode][uuid].Env.player.output.BattlemageUpTimeRatio or 100
453-
trigRate = trigRate * battleMageUptime / 100
454-
if breakdown then
455-
t_insert(breakdown.EffectiveSourceRate, s_format("x %d%% ^8(Battlemage's Cry uptime)", battleMageUptime))
456-
end
457-
end
458-
459-
-- Infernal Cry uptime
460-
if actor.mainSkill.activeEffect.grantedEffect.name == "Combust" and GlobalCache.cachedData[env.mode][uuid] and source and source.skillTypes[SkillType.Melee] then
461-
local InfernalUpTime = GlobalCache.cachedData[env.mode][uuid].Env.player.output.InfernalUpTimeRatio or 100
462-
trigRate = trigRate * InfernalUpTime / 100
463-
if breakdown then
464-
t_insert(breakdown.EffectiveSourceRate, s_format("x %d%% ^8(Infernal Cry uptime)", InfernalUpTime))
465-
end
466-
end
467-
468452
--Account for skills that can hit multiple times per use
469453
if source and GlobalCache.cachedData[env.mode][uuid] and source.skillPartName and source.skillPartName:match("(.*)All(.*)Projectiles(.*)") and source.skillFlags.projectile then
470454
local multiHitDpsMult = GlobalCache.cachedData[env.mode][uuid].Env.player.output.ProjectileCount or 1
@@ -484,6 +468,44 @@ local function defaultTriggerHandler(env, config)
484468
end
485469
end
486470

471+
-- Battlemage's Cry uptime
472+
if actor.mainSkill.skillData.triggeredByBattleMageCry and GlobalCache.cachedData[env.mode][uuid] and source and source.skillTypes[SkillType.Melee] then
473+
local battleMageExertsCount = GlobalCache.cachedData[env.mode][uuid].Env.player.output.BattleCryExertsCount
474+
local battleMageDuration = ceil_b(GlobalCache.cachedData[env.mode][uuid].Env.player.output.BattleMageCryDuration, data.misc.ServerTickTime)
475+
local battleMageCastTime = GlobalCache.cachedData[env.mode][uuid].Env.player.output.BattleMageCryCastTime
476+
local battleMageCooldown = ceil_b(GlobalCache.cachedData[env.mode][uuid].Env.player.output.BattleMageCryCooldown, data.misc.ServerTickTime)
477+
478+
-- Cap the number of hits that happen during the duration
479+
local battleMageHits = m_max(m_min(trigRate * battleMageDuration, battleMageExertsCount), 0)
480+
481+
if breakdown then
482+
t_insert(breakdown.EffectiveSourceRate, s_format("^8(min(%.2f * %.2f, %d) = %.2f average number of exerted attacks capped by exerted count)", trigRate, battleMageDuration, battleMageExertsCount, battleMageHits))
483+
t_insert(breakdown.EffectiveSourceRate, s_format("= %.2f / (%.2f + %.2f) ^8(the calculated number of exerted attacks happens every cooldown + duration)", battleMageHits, battleMageCastTime, battleMageCooldown))
484+
end
485+
486+
-- The hits happen every battlemage cooldown + duration
487+
trigRate = battleMageHits / (battleMageCastTime + battleMageCooldown)
488+
end
489+
490+
-- Infernal Cry uptime
491+
if actor.mainSkill.activeEffect.grantedEffect.name == "Combust" and GlobalCache.cachedData[env.mode][uuid] and source and source.skillTypes[SkillType.Melee] then
492+
local infernalCryExertsCount = GlobalCache.cachedData[env.mode][uuid].Env.player.output.InfernalExertsCount
493+
local infernalCryDuration = ceil_b(GlobalCache.cachedData[env.mode][uuid].Env.player.output.InfernalCryDuration, data.misc.ServerTickTime)
494+
local infernalCryCastTime = GlobalCache.cachedData[env.mode][uuid].Env.player.output.InfernalCryCastTime
495+
local infernalCryCooldown = ceil_b(GlobalCache.cachedData[env.mode][uuid].Env.player.output.InfernalCryCooldown, data.misc.ServerTickTime)
496+
497+
-- Cap the number of hits that happen during the duration
498+
local infernalCryHits = m_max(m_min(trigRate * infernalCryDuration, infernalCryExertsCount), 0)
499+
500+
if breakdown then
501+
t_insert(breakdown.EffectiveSourceRate, s_format("^8(min(%.2f * %.2f, %d) = %.2f average number of exerted attacks capped by exerted count)", trigRate, infernalCryDuration, infernalCryExertsCount, infernalCryHits))
502+
t_insert(breakdown.EffectiveSourceRate, s_format("= %.2f / (%.2f + %.2f) ^8(the calculated number of exerted attacks happens every cooldown + duration)", infernalCryHits, infernalCryCastTime, infernalCryCooldown))
503+
end
504+
505+
-- The hits happen every Infernal Cry cooldown + duration
506+
trigRate = infernalCryHits / (infernalCryCastTime + infernalCryCooldown)
507+
end
508+
487509
-- Handling for mana spending rate for Manaforged Arrows Support
488510
if actor.mainSkill.skillData.triggeredByManaforged and trigRate > 0 then
489511
local triggeredUUID = cacheSkillUUID(actor.mainSkill, env)
@@ -525,7 +547,7 @@ local function defaultTriggerHandler(env, config)
525547
local triggerCD = actor.mainSkill.triggeredBy and env.player.mainSkill.triggeredBy.grantedEffect.levels[env.player.mainSkill.triggeredBy.level].cooldown
526548
triggerCD = triggerCD or source.triggeredBy and source.triggeredBy.grantedEffect.levels[source.triggeredBy.level].cooldown
527549
local triggeredCD = actor.mainSkill.skillData.cooldown
528-
550+
529551
if actor.mainSkill.skillData.triggeredByBrand then
530552
triggerCD = actor.mainSkill.triggeredBy.mainSkill.skillData.repeatFrequency / actor.mainSkill.triggeredBy.activationFreqMore / actor.mainSkill.triggeredBy.activationFreqInc
531553
triggerCD = triggerCD * icdr -- cancels out division by icdr lower, brand activation rate is not affected by icdr
@@ -1037,7 +1059,7 @@ local configTable = {
10371059
local requiredManaCost = env.player.modDB:Sum("BASE", nil, "KitavaRequiredManaCost")
10381060
return {triggerChance = env.player.modDB:Sum("BASE", nil, "KitavaTriggerChance"),
10391061
triggerName = "Kitava's Thirst",
1040-
comparer = function(uuid, source, triggerRate)
1062+
comparer = function(env, uuid, source, triggerRate)
10411063
local cachedSpeed = GlobalCache.cachedData[env.mode][uuid].HitSpeed or GlobalCache.cachedData[env.mode][uuid].Speed
10421064
local cachedManaCost = GlobalCache.cachedData[env.mode][uuid].ManaCost
10431065
return ( (not source and cachedSpeed) or (cachedSpeed and cachedSpeed > (triggerRate or 0)) ) and ( (cachedManaCost or 0) > requiredManaCost )
@@ -1190,6 +1212,11 @@ local configTable = {
11901212
["battlemage's cry"] = function(env)
11911213
if env.player.mainSkill.activeEffect.grantedEffect.name ~= "Battlemage's Cry" then
11921214
return {triggerSkillCond = function(env, skill) return skill.skillTypes[SkillType.Melee] end,
1215+
comparer = function(env, uuid, source, triggerRate)
1216+
-- Skills with no uptime ratio are not exerted by battlemage so should not be considered.
1217+
local uptimeRatio = GlobalCache.cachedData[env.mode][uuid].Env.player.output.BattlemageUpTimeRatio
1218+
return defaultComparer(env, uuid, source, triggerRate) and uptimeRatio
1219+
end,
11931220
triggeredSkillCond = function(env, skill) return skill.skillData.triggeredByBattleMageCry and slotMatch(env, skill) end}
11941221
end
11951222
end,
@@ -1219,7 +1246,12 @@ local configTable = {
12191246
env.player.mainSkill.infoMessage = env.player.mainSkill.activeEffect.grantedEffect.name .. " Triggered on Death"
12201247
end,
12211248
["combust"] = function(env)
1222-
return {triggerSkillCond = function(env, skill) return skill.skillTypes[SkillType.Melee] end}
1249+
return {triggerSkillCond = function(env, skill) return skill.skillTypes[SkillType.Melee] end,
1250+
comparer = function(env, uuid, source, triggerRate)
1251+
-- Skills with no uptime ratio are not exerted by infernal cry so should not be considered.
1252+
local uptimeRatio = GlobalCache.cachedData[env.mode][uuid].Env.player.output.InfernalUpTimeRatio
1253+
return defaultComparer(env, uuid, source, triggerRate) and uptimeRatio
1254+
end,}
12231255
end,
12241256
["prismatic burst"] = function(env)
12251257
return {triggerSkillCond = function(env, skill) return skill.skillTypes[SkillType.Attack] and slotMatch(env, skill) end}
@@ -1324,7 +1356,7 @@ local configTable = {
13241356
end,
13251357
["avenging flame"] = function(env)
13261358
return {triggerSkillCond = function(env, skill) return skill.skillFlags.totem and slotMatch(env, skill) end,
1327-
comparer = function(uuid, source, currentTotemLife)
1359+
comparer = function(env, uuid, source, currentTotemLife)
13281360
local totemLife = GlobalCache.cachedData[env.mode][uuid].Env.player.output.TotemLife
13291361
return (not source and totemLife) or (totemLife and totemLife > (currentTotemLife or 0))
13301362
end,
@@ -1395,4 +1427,4 @@ function calcs.triggers(env, actor)
13951427
actor.mainSkill.skillData.triggered = nil
13961428
end
13971429
end
1398-
end
1430+
end

0 commit comments

Comments
 (0)