Firebird Rotation cycle calculation / guide

Part 0: Motivation

I wanted to play LoD Hydra this season, but finding two firebird primals and the unclear mechanic of the liar soul shard made me change plans. Now i play firebirds with fireblades.

First we channel desintegration till 50 combustion stacks. Then we cast blades during which the stacks drop 1 per sec until we repeat.

Now the question is: how much do we want to let the combustion stacks drop until we channel again for an optimal damage/time ratio?

Part 1: The Setup

Before we start: I know desintegration is a channeling spell, but for simplicitys sake we’re calculating it as if it had attacks. Trust me, it works the same as if we were considering it a channeling spell, nothing changes. Also i don’t display units here. Everything time-related either uses seconds or stacks/second, depending on the formula context.

Also, everything we’re doing here is school mathematics, not that complicated.

Ok first, let me define some constants and variables:

  • Let Donce be the sets 6pieces bonus of 3000% wpn dmg with every other multiplier but without the combustion stack multiplier
  • Let Dtotal be the entire firebird 6piece-caused dmg dealt to one enemy per rotation with all multipliers including combustion stacks
  • Let td be your desintegration channeling time per rotation
  • Let tb be your blade casting time per rotation
  • Let tc be the casting delay (time that takes place everytime you switch from blades to desintegrate or vice versa during which you don't do damage)
  • Let tr be the duration of one rotation
  • Let A be your attack speed
  • Let Ab be your blades attack speed
  • Let C(t) be your combustion stacks
  • Let Cd be the amount of combustion stacks you're gaining per desintegration attack. This is independent of attack speed.
  • Now we’re aiming for an optimized Dtotal/tr ratio in terms of tb and what interests us in the end is the amount of stacks we have after tb so we know when to switch.

    So let’s calculate Dtotal and tr by putting the variables into relation

Part 2: calculating tr

tr=td+tb+2tc
Now we want everything in terms of tb. tc is constant in tb, it doesn’t depend on it, but td does:
We’re losing 1 combustion stack per second during tc+tb+tc. So we need to choose td long enough to generate exactly this amount of stacks:
td=(tb+2tc)/(Cd*A)
=> tr=(tb+2tc)/(Cd*A)+tb+2tc
=> tr=(1+1/(Cd*A))tb+(1+1/(Cd*A))2tc

Part 3: calculating Dtotal

Dtotal=sum of Donce*Ab*C(t) from t=td+tc to t=td+tc+tb-1

Now C(t) looks like this:
https://i.imgur.com/axMnUk8.jpeg
(Sorry can’t post links/images yet. The dimensions of the graph should be on point actually since i created it after solving the problem. I estimated tc=0.5, Cd=7 and A=1.)

Since we’re only interested in C(t) from t=td+tc to t=td+tc+tb[-1], we can just assume that C(t) is linear:
C(t)=a*t+b with C(td)=50 and a=-1 since we’re losing 1 stack per second.
From there we can conclude that C(t)=50+td-t.

So now we have:
Dtotal=sum of Donce*Ab*(50+td-t) from t=td+tc to t=td+tc+tb-1
=> Dtotal=Donce*Ab*(sum of (50+td-t) from t=td+tc to t=td+tc+tb-1)
=> Dtotal=Donce*Ab*(tb*(50+td)-sum of t from t=td+tc to t=td+tc+tb-1)
Now we can use the Gauss sum formula:
=> Dtotal=Donce*Ab*tb*(50-tc-(tb-1)/2)

Part 4: optimizing the ratio

I spare you the whole calculation, it’s long and tedious, all we’re doing is taking the derivative of the ratio in terms of tb, setting it to 0. We get:

d/dtb (Dtotal/tr)=0
=> tb2+4tctb-202tc+4tc2=0

As you might have noticed, it doesn’t depend on Donce nor Ab nor Cd anymore, all these variables got reduced in the calculation!

Let’s solve for tb:

tb=-2tc+sqrt(202tc)

Now if we want our stack count as an indicator on when to switch, we simply take C(tr)=50-2tc-tb and we’re done!

Part 5: Full formula, request to find out tc and final thoughts

C(tr)=50-sqrt(202tc)
With an estimate of tc~0.5 we get
C(tr)~39.95~40 stacks

With better estimates for tc we get better . Can you give us the value of these constants please?

Also when the casting delay, tc, goes to 0, the formula indicates that we should switch as fast as humanly possible. At the beginning i wasn’t sure to whether this recast time even exists, but i can’t imagine that switching as fast as possible would increase our damage output just from my game sense, so i think it exists.

The only open question right now is: “How big is it?” which includes the question: “Does it depend on other values such as attack speed?”. I’m not sure, but if so, i’d have to redo the calculation in order for it to be accurate.

1 Like

Rise! Rise Firebird!

2 Likes

Perfect! Thanks a lot! :heart:


I looked for mistakes because the first formula i had would give quirky results if we set tc to 0.5 or lower. The calculation was flawed actually, i made no mistakes with the maths, but i made two interpretation mistakes:

I forgot the second tc phase, we lose 1 combustion stack per second during tc+tb+tc

To see this mistake, imagine tb being 0, the resulting value should also be 0 but it isn’t. The correct formula is
Dtotal=sum of Donce*Ab*C(t) from t=td+tc to t=td+tc+tb-1

Now you might think it wouldn’t work if you now imagine tb being 0, but for all sums if the upper bound is equal to the lower bound - 1, the sum is equal to 0. To understand why that is, you’d have to study Maths or Physics, i’m sorry.

I already updated the OP, the resulting formula is way simpler and much more surprising (but not quirky, the results lay in the expected value range).

Now please, it would be so good if we had exact values for the Casting delays. The diablo wiki has so precise values for Diablo II, why not for Diablo III?

1 Like

Casting delays aren’t really a thing in D3. There are skill-specific attack speed breakpoints though. Attacks can only occur over a discrete number of frames, not partial frames. And when I say “frames” here I don’t mean the rendered frames on your monitor, but rather at the tick rate of the D3 engine (i.e. 60 frames per second).

So attack rate doesn’t scale exactly linearly with attack speed, it’s a stair-step function. Your attack rate doesn’t change until you increase your attack speed enough to reduce the casting animation by another whole frame.

Then it gets a little more complicated because Disintegrate is a channeling skill, so instead of attacking once per attack interval (roughly 1/APS but adjusted so it occurs over a whole number of frames), it actually ticks at a rate of about three times per 1/APS interval.

I’m not sure exactly what happens when stopping channeling to switch to a different skill. There’s some weirdness that happens where I think you can cancel channeling a bit early and it doesn’t have to do the entire tick animation, but I’ve never investigated the specifics. You’d need to figure this out too in order to get an exact solution. I’m not sure exactly how quickly you can cast Spectral Blade after channeling Disintegrate, but I think there’s a bit of weirdness there.

In any case, here are the Disintegrate breakpoints:

Min APS FPT
1.053 18
1.112 17
1.177 16
1.250 15
1.334 14
1.429 13
1.539 12
1.667 11
1.819 10
2.000 9
2.223 8
2.500 7
2.858 6
3.334 5
4.000 4

Min APS it the minimum amount of APS you would need displayed in your character sheet in order to move up to the next breakpoint and have Disintegrate attack a little faster.

To calculate these directly without rounding error: min APS = 20/(fpt+1)

And here are the Spectral Blade breakpoints, depending on whether you have Fragment of Destiny (FoD), Shame of Delsere (SoD), or both.

fpa Min APS (no FoD, no SoD) FoD or SoD but not both both FoD and SoD
57 0.987
56 1.005
55 1.023
54 1.042
53 1.062
52 1.082
51 1.103
50 1.125
49 1.148
48 1.172
47 1.197
46 1.223
45 1.25
44 1.279
43 1.309
42 1.34
41 1.372
40 1.407
39 1.443
38 1.481 0.987
37 1.521 1.014
36 1.563 1.042
35 1.608 1.072
34 1.655 1.103
33 1.705 1.137
32 1.758 1.172
31 1.815 1.21
30 1.875 1.25
29 1.94 1.294
28 2.009 1.34
27 2.084 1.389
26 2.164 1.443
25 2.25 1.5 1
24 2.344 1.563 1.042
23 2.446 1.631 1.087
22 2.557 1.705 1.137
21 2.679 1.786 1.191
20 2.813 1.875 1.25
19 2.961 1.974 1.316
18 3.125 2.084 1.389
17 3.309 2.206 1.471
16 3.516 2.344 1.563
15 3.75 2.5 1.667
14 4.018 2.679 1.786
13 4.327 2.885 1.924
12 4.688 3.125 2.084
11 3.41 2.273
10 3.75 2.5
9 4.167 2.778
8 4.688 3.125
7 3.572
6 4.167

fpa=frames per attack. Spectral Blade hits three times per attack, so this is per set of three blades. Unlike with channeling with ticks, the animation always includes all three blade strikes.

3 Likes

Now first of all, what an awesome answer! Thanks a lot!

If this is true, then the answer is simple: you want to get the desintegration channeling time per rotation to be as short as possible. Let me explain with an example (numbers not on scale, just to make a point) :

Imagine having a total amount of blade casting time of 10 sec during which you lose 10 stacks and a total amount of desintegration channeling time of 2 sec during which you gain 10 stacks. Now you can either leave it at
10s blade - 2s desint
or you can split them up to, let’s say,
5s blade - 1s desint - 5s blade - 1s desint
One can easily see that without the casting delay penalty, splitting it up increases your average amount of stacks without increasing the amount of total casting time.

Therefore if it weren’t for the breakpoints, switching as fast as humanly possible would be recommended but since there are breakpoints, you just want to decrease the desintegration channeling time as much as possible.

By the way you gain 6 stacks per desintegration attack interval (1. i’ve tested it, 2. it fits because it’s divisible by three and there’s the

thing).

All what would be left now is the question on how long to cast blades.

If we want a tick-exact solution (and i’m all in for that!), then we’d also need to know how the “lose 1 stack per sec”-mechanic works. If you stop channeling desint at a non-divisible-by-60 tick, do you lose a stack after 60 more ticks or when the next tick occurs that’s divisible by 60? Also the question “is it first attack then animation or first animation then attack?” would need to be answered.

If both are case one, then i could provide an answer (but without channeling-blade-animation wierdness you mentioned):
Let x be your blades casting time in frames
Let n be your blades fpa
Let s be your desint fpa
Let a(k) be ÎŁf(p) from p=0 to k
with f(k) being a recursively defined function with
f(0)=ceil(60/n); f(k)=(x/n)-a(k-1) if ceil((k+1)*60)>>x or else f(k)=ceil((k+1)*60/n-a(k-1))
Then x needs to be chosen so that the function
(1/(x+s))(ÎŁ((50-k)*f(k) from k = 0 to ceil(x/60)-1; 0<<x<<360; x divisible by n
is maximized. I can write a lua script that solves the problem. But it will take time.

But from my testing i’m not fully convinced that Casting delays aren’t a thing yet. I wish there would be some information from the devs side, it’s incredibly difficult to test it ingame (at least for me).

Why not simplify it a bit?

Before I continue, let me ask if you are using focus and restraint.

If you don’t stop reading here and only refer to Tinne’s reply.

If you do then there is that to keep in mind. 3 seconds of each bonus.

Your tactics is to tag your enemies, as many as you are comfortable killing, gather them and start hitting with the blades.
What is now more important than ever is your attack speed, see Tinne’s reply as I have never mastered this particular mechanic, the number of hits you do per second is what will keep your damage up.

First of all you have 3 second windows of the rings damage bonus and secondly the set stack won’t drop to low in 3seconds or less.

So hitting as fast as possible per second and not letting your stacks drop to low is a good way to maximize damage.

The other spells you use OoiD/EB is also something I didn’t mention but it also something you need to work in to your rhythm.

Lastly using disintegrate with high AS can be harmful since it can drain your Arcane Power fast.
So learning to release teleport around, casting other spells before build up is something we have to find our way of doing well.

Hope it helps a bit.

Explosive Blast can be cast without an animation (you can cast either Disintegrate or Flame Blades simultaneously as Explosive Blast without issue). So it doesn’t factor in to the equation.

The buff lasts 5 seconds, not 3. The Focus (generator) part will be continuously refreshed by Flame Blades. The Restraint (spender) part can be refreshed by EB, so that means you can keep up the Restaint part continuously too without having to factor it in to timing for your Disintegrate/Flame Blades rotation. So regardless of whether or not you are using Focus/Restraint, it doesn’t really factor in at all to the optimal Disintegrate/Flame Blades rotation for maximum DPS. Since you’ll always cast Flame Blades at least once every 5 seconds, and continuously casting Explosive Blast will keep up the other part and doesn’t require an attack animation. Even if you go more than 5 seconds without Disintegrating, you’ll still have Restraint up via Explosive Blast. EB has a 6 second base cooldown, and basically every setup will have enough CDR to get the cooldown under 5 secounds (often more like 3 second cooldown with 50+% CDR).

Oh I forgot to mention this:

No matter what setup you use dropping stacks is easy to calculate how your damage is harmed.

Going from 50 to 45 is a 10% damage loss, letting the stacks to drop to 40 equals a 20% damage loss.

That is what I based my reply on.

Keeping up the stacks up and attacking with blades is needs to have a balance.

I sure hope this makes sense and we avoid any misunderstanding or misinterpretation.

1 EB, you are absolutely right I wasn’t trying to say anything different.

  1. The F/R set bonus, that made me laugh btw, it is like you said 5 seconds. My Alzheimer’s kicked in.

And you pointed out is absolutely true.

My second respond I believe is also something to keep in mind, that was why I committed the EB interaction in my first post

Keep in mind the you only need a quick cast to keep the channeling spell to keep up the stacks.
Depending how low they have dropped of course.

I’ve done it. First of all, i’ve tested the code extensively, unfortunately it’d be quite surprising if if there’s a mistake in the code. I say unfortunately because the results are really disappointing. Basically you want to always keep 50 stacks.

Here’s my code:

local maxticks = 360
local s = 19
local n = 25
local best = 0
local value = 0
local ticks = {}

function ticksetup(q)
    do ticks = {} end
    do ticks[0] = math.ceil(60 / n) end
    for p = 1, q do
        local a = 0
        for k = 0, (p - 1) do
            a = a + ticks[k]
        end
        local b = math.ceil(((p + 1) * (60 / n)) - a)
        if (a + b) > q then
            do ticks[p] = (q - a) end
            break
        end
        do ticks[p] = b end
    end
end

function  Summiere(y)
    local a = 0
    for k = 0, (math.ceil(y/60) - 1) do
        a = a + ((50 - k) * ticks[k])
    end
    do a = a / (y + s) end
    return a
end

function mainlogic (x)
    for q = 1, math.floor(x / n) do
        do ticksetup(q) end
        local b = Summiere(q * n)
        if b > best then
            do best = b end
            do value = q end
        end
    end
end

do mainlogic(maxticks) end
do print(tostring(value)) end

You can copy this code into https://www.lua.org/cgi-bin/demo if you don’t have lua and want to play around with it. n has to be less than or equal to 60 in order for it to work reliably.

I varied s and n for all the different possible combinations from @TinneOnnMuin’s breakpoint lists. The result is always 1 which means you want to cast blades for 1 stack duration (60 frames). Different results only if the hero had like 0.000001 APS (prob exaggerated, but still).