Skip to content

Commit 783a530

Browse files
committed
Add support for Kinetic Fusillade
Introduce support for Kinetic Fusillade's sequential fire damage mod. Add support for DPS calculations considering all available projectiles while factoring in duration and attack rate for effective dps calcs. Signed-off-by: Justin Stitt <jstitt007@gmail.com>
1 parent 58aef43 commit 783a530

File tree

2 files changed

+96
-0
lines changed

2 files changed

+96
-0
lines changed

src/Export/Skills/act_int.txt

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2265,6 +2265,100 @@ local skills, mod, flag, skill = ...
22652265

22662266
#skill KineticFusillade
22672267
#flags attack projectile area duration
2268+
parts = {
2269+
{
2270+
name = "1 Projectile"
2271+
},
2272+
{
2273+
name = "All Projectiles",
2274+
},
2275+
},
2276+
preDamageFunc = function(activeSkill, output, breakdown)
2277+
local skillData = activeSkill.skillData
2278+
local t_insert = table.insert
2279+
local s_format = string.format
2280+
2281+
if activeSkill.skillPart == 2 then
2282+
-- Set base dpsMultiplier for projectile count
2283+
activeSkill.skillData.dpsMultiplier = output.ProjectileCount
2284+
2285+
-- Calculate average damage scaling for sequential projectiles
2286+
-- Each projectile does more damage based on how many came before it
2287+
local moreDamagePerProj = skillData.KineticFusilladeSequentialDamage or 0
2288+
if moreDamagePerProj ~= 0 and output.ProjectileCount > 1 then
2289+
-- Average multiplier: sum of (0, X, 2X, 3X, ..., (n-1)X) / n
2290+
-- This equals: X * (0 + 1 + 2 + ... + (n-1)) / n = X * n(n-1)/2 / n = X * (n-1)/2
2291+
local avgMoreMult = moreDamagePerProj * (output.ProjectileCount - 1) / 2
2292+
activeSkill.skillModList:NewMod("Damage", "MORE", avgMoreMult, "Skill:KineticFusillade", ModFlag.Hit)
2293+
2294+
-- Store the average multiplier for display
2295+
output.KineticFusilladeAvgMoreMult = avgMoreMult
2296+
2297+
if breakdown then
2298+
local breakdownSequential = {}
2299+
t_insert(breakdownSequential, s_format("^8Each projectile deals^7 %d%%^8 more damage per previous projectile", moreDamagePerProj))
2300+
t_insert(breakdownSequential, s_format("^8With^7 %d^8 projectiles, damage progression is:^7", output.ProjectileCount))
2301+
for i = 1, output.ProjectileCount do
2302+
local projMult = moreDamagePerProj * (i - 1)
2303+
t_insert(breakdownSequential, s_format(" ^8Projectile %d:^7 %d%%^8 more damage", i, projMult))
2304+
end
2305+
t_insert(breakdownSequential, s_format("^8Average more multiplier:^7 %.1f%%", avgMoreMult))
2306+
breakdown.KineticFusilladeSequentialBreakdown = breakdownSequential
2307+
end
2308+
end
2309+
end
2310+
end,
2311+
postCritFunc = function(activeSkill, output, breakdown)
2312+
if activeSkill.skillPart == 2 then
2313+
local skillData = activeSkill.skillData
2314+
local t_insert = table.insert
2315+
local s_format = string.format
2316+
2317+
-- Calculate effective attack rate accounting for delayed projectile firing
2318+
-- Projectiles orbit for base_skill_effect_duration before firing
2319+
-- Recasting resets the timer, so attacking too fast wastes potential damage
2320+
local baseDuration = skillData.duration
2321+
local actualDuration = output.Duration or baseDuration
2322+
local ticksNeeded = math.ceil(actualDuration / data.misc.ServerTickTime)
2323+
local effectiveDelay = ticksNeeded * data.misc.ServerTickTime
2324+
local maxEffectiveAPS = 1 / effectiveDelay
2325+
local currentAPS = output.Speed
2326+
2327+
output.KineticFusilladeMaxEffectiveAPS = maxEffectiveAPS
2328+
2329+
if breakdown then
2330+
local breakdownAPS = {}
2331+
t_insert(breakdownAPS, s_format("^8Projectiles orbit for %.3fs before firing", actualDuration))
2332+
t_insert(breakdownAPS, s_format("^8Server tick time:^7 %.3fs", data.misc.ServerTickTime))
2333+
t_insert(breakdownAPS, s_format("^8Ticks needed:^7 %d ^8(rounded up)", ticksNeeded))
2334+
t_insert(breakdownAPS, s_format("^8Effective delay:^7 %.3fs", effectiveDelay))
2335+
t_insert(breakdownAPS, s_format("^8Max effective attack rate:^7 1 / %.3f = %.2f", effectiveDelay, maxEffectiveAPS))
2336+
if currentAPS and currentAPS > maxEffectiveAPS then
2337+
t_insert(breakdownAPS, "")
2338+
t_insert(breakdownAPS, s_format("^1Current attack rate (%.2f) exceeds max effective rate!", currentAPS))
2339+
t_insert(breakdownAPS, s_format("^1DPS is reduced by %.1f%%", (1 - maxEffectiveAPS / currentAPS) * 100))
2340+
elseif currentAPS then
2341+
t_insert(breakdownAPS, "")
2342+
t_insert(breakdownAPS, s_format("^2Current attack rate (%.2f) is within effective limits", currentAPS))
2343+
end
2344+
breakdown.KineticFusilladeMaxEffectiveAPS = breakdownAPS
2345+
end
2346+
2347+
-- Adjust dpsMultiplier if attacking too fast
2348+
if currentAPS and currentAPS > maxEffectiveAPS then
2349+
local efficiencyRatio = maxEffectiveAPS / currentAPS
2350+
local originalMultiplier = skillData.dpsMultiplier or output.ProjectileCount
2351+
skillData.dpsMultiplier = originalMultiplier * efficiencyRatio
2352+
end
2353+
end
2354+
end,
2355+
statMap = {
2356+
["kinetic_fusillade_damage_+%_final_per_projectile_fired"] = {
2357+
skill("KineticFusilladeSequentialDamage", nil),
2358+
},
2359+
["quality_display_kinetic_fusillade_is_gem"] = {
2360+
},
2361+
},
22682362
#mods
22692363

22702364
#skill KineticRain

src/Modules/CalcSections.lua

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -737,6 +737,8 @@ return {
737737
{ label = "Normal Hits/Cast", haveOutput = "NormalHitsPerCast", { format = "{3:output:NormalHitsPerCast}", { breakdown = "NormalHitsPerCast" }, }, },
738738
{ label = "Super Hits/Cast", haveOutput = "SuperchargedHitsPerCast", { format = "{3:output:SuperchargedHitsPerCast}", { breakdown = "SuperchargedHitsPerCast" }, }, },
739739
{ label = "DPS Multiplier", haveOutput = "SkillDPSMultiplier", { format = "{3:output:SkillDPSMultiplier}", { breakdown = "SkillDPSMultiplier" }, }, },
740+
{ label = "Average Seq More", haveOutput = "KineticFusilladeAvgMoreMult", { format = "{1:output:KineticFusilladeAvgMoreMult}%", { breakdown = "KineticFusilladeSequentialBreakdown" }, }, },
741+
{ label = "Max Effective APS", haveOutput = "KineticFusilladeMaxEffectiveAPS", { format = "{2:output:KineticFusilladeMaxEffectiveAPS}", { breakdown = "KineticFusilladeMaxEffectiveAPS" }, }, },
740742
-- Traps
741743
{ label = "Avg. Active Traps", haveOutput = "AverageActiveTraps", { format = "{2:output:AverageActiveTraps}", { breakdown = "AverageActiveTraps" }, }, },
742744
{ label = "Active Trap Limit", flag = "trap", { format = "{0:output:ActiveTrapLimit}", { modName = "ActiveTrapLimit", cfg = "skill" }, }, },

0 commit comments

Comments
 (0)