Hekili Priority Helper

Hekili Priority Helper

44M Downloads

setRegenModel

thunderchylde opened this issue ยท 9 comments

commented

I'm looking to add the focus regen for Dire Beast/Dire Frenzy but I'm dumb and can't figure out how the code works - any assistance greatly appreciated.

commented

I'm assuming something like:

        dire_beast = {
            resource = 'focus',
                
            spec = 'beast_mastery',
            
            last = ???,
            stop = ???,
            
            interval = 1,
            value = 12,
        }

but I don't know what last and stop do.

commented

So, setRegenModel is for telling the addon how to forecast non-regen based resource changes when trying to decide what it should recommend in the future.

Here's an example from Sub Rogue:


            goremaws_bite = {
                resource = 'energy',

                spec = 'subtlety',
                aura = 'goremaws_bite',

                last = function ()
                    return state.buff.goremaws_bite.applied + floor( state.query_time - state.buff.goremaws_bite.applied )
                end,

                interval = 1,
                value = 5,
            },

So this relates to energy regeneration for Subtlety, and the aura it looks for is Goremaw's Bite. Goremaw's Bite generates 5 (value) energy every 1 (interval) seconds. That tick occurs each second from when GB was applied. The last function tells the addon when the last tick would have occurred before the resource modeling functions kick in. Since GB ticks every 1 second, we just count how many full seconds have passed before the query_time (query_time is the equivalent of "now" but however much time in the future the addon is calculating things).

A 'stop' entry is for things like Fury of Air, which tick down, but fall off if you don't have enough Maelstrom for it to continue ticking. That generally doesn't apply to positive resource generation.

commented

Hi,

with this explanation, it would be something like this:

            dire_beast= {
                resource = "focus",

                spec = "beast_mastery",
                notalent = "dire_frenzy",
                aura = "dire_beast",

                last = function()
                    return state.buff.dire_beast.applied + floor( state.query_time - state.buff.dire_beast.applied)
                end,

                interval = 1,
                value = function()
                    if state.talent.dire_stable.enabled then
                        return 3
                    else
                        return 1.5
                    end
                end
            }

The value is connected to the dire_stable talent, so we have do select here what to return. I select dire_beast here, because this is tracked as a standalone aura, so we have the regen for every aura active on the character.
I'm not sure if the last function will work, because there can be multiple buffs on the player..

With dire_frenzy, there is only the stacking buff on the pet (tracked by the fake aura on the player). There we have to account the stacks, not the number of auras active. Something like this:

            dire_frenzy = {
                resource = "focus",

                spec = "beast_mastery",
                talent = "dire_frenzy",
                aura = "dire_frenzy",

                last = function()
                    return state.buff.dire_frenzy.applied + floor( state.query_time - state.buff.dire_frenzy.applied )
                end,

                interval = 1,
                value = function()
                    if state.talent.dire_stable.enabled then
                        return 4.5 * state.buff.dire_frenzy.count
                    else
                        return 3 * state.buff.dire_frenzy.count
                    end
                end
            }

edit: corrected the values of dire frenzy base values ( never realized, that dire_frenzy will regenerate more focus than dire_beast baseline)
I'm just not exactly sure, how to test this regen function ingame. I added these into my hunter.lua but the snapshots are not really helping here unfortunately.

Is there a way to get current state.focus values or should this be printed out for debugging?

commented

The challenge is that both of the buffs can stack afaik, if you cast Dire Frenzy when you're already at 3 stacks you still get the 24 focus. Same with Dire Beast - you get 12 focus each time you cast even if you already have the buff up (you get a second buff). Does the code create an object/event for each cast that's independent? or do we need to create a data structure that keeps track of the overlapping casts?

I'll take a look at a combat log on my hunter for the focus stuff...

commented

Dire Frenzy is tricky because there's the buff that stacks that's actually applied to your pet, and then a few auras for what is applied to your character. The solution I've been working on is to handle dire_frenzy as follows:

        addAura( 'dire_frenzy_2', 246851, 'duration', 8 )
        addAura( 'dire_frenzy', 217200, 'duration', 8, 'max_stack', 8, 'feign', function ()
            -- This is a real aura, but it is applied to our pet.
            local up, _, _, count, _, duration, expires, caster, _, _, spellID, _, _, _, _, timeMod, v1, v2, v3 = UnitBuff( "pet", class.abilities.dire_frenzy.name )

            buff.dire_frenzy.name = up or class.abilities.dire_frenzy.name
            buff.dire_frenzy.count = up and count or 0
            buff.dire_frenzy.expires = up and expires or 0
            buff.dire_frenzy.applied = up and ( expires - duration ) or 0
            buff.dire_frenzy.caster = up and caster or "player"
        end )

This creates dire_frenzy_1 and dire_frenzy_2 (I don't know if there is a third aura ID for the buffs applied to the player), which we can track independently and are real buffs.

dire_frenzy is implemented as a 'feigned' aura, since the real buff is applied to our pet, it'll pull information from the pet. We can still applyBuff( 'dire_frenzy' ) like any other buff and the addon is fine with it.

When casting dire_frenzy, we'd want to addStack( 'dire_frenzy', 8, 1 ) to update the aura on our pet. We also want to apply whichever dire_frenzy_x player buff is not presently applied, starting with dire_frenzy_1:

            if buff.dire_frenzy_1.up then applyBuff( 'dire_frenzy_2' )
            else applyBuff( 'dire_frenzy_1' ) end

Like I said, I'm not sure it's possible to have a 3rd Dire Frenzy cast within 8 seconds (reset proc for Dire Frenzy somewhere? from a legendary or set bonus?), so I don't know if there are more Dire Frenzy auras, but I can confirm that the IDs listed for dire_frenzy_1 and dire_frenzy_2 are real auras.

From there, you can just have regen models for dire_frenzy_1, dire_frenzy_2, and so forth.

commented

Regarding seeing how the regen model is panning out, you can poke around in state.focus to see where resources ended up after the last recommendations were given. There's no way to really peek into it in the middle of recommendations, because a lot of the information is dynamically generated on demand.

For instance, if I have regen models for two buffs, "one" generates 10 focus/sec and "two" spends 5 focus per 2 sec, and I automatically regen 10 focus per second, the prediction engine iterates through these events in order, any time focus is generated, the addon will forecast out each tick of focus regeneration from these buffs.

We'll assume that "one" ticked 0.5s ago and "two" ticked 1s ago. We have 50 focus now.

+0.0, I currently have 50 focus: forecast[1] = { t = 0, v = 50 }
+0.5s, "one" ticks for +10 focus, +5 from regen: [2] = { t = 0.5, v = 65 }
+1s, "two" ticks for -5 focus, +5 from regen: [3] = { t = 1, v = 65 }
+1.5s, "one" ticks for +10 focus, +5 from regen: [4] = { t = 1.5, v = 80 }
+2.5s, "one" ticks for +10 focus, +10 from regen: [5] = { t = 2.5, v = 100 }
+3s, "two" ticks for -5 focus, +0 from regen (because we were capped): [6] = { t = 3, v = 95 }

That's easy enough to sort out if I need to see how much focus I'd have at any of those specific intervals. But what if the ability I'm testing comes off CD in 1.25s?

The addon finds the closest entry in the forecast without passing the query time. In this case, at +1s, we have 65 focus. It adds the naturally regenerated focus that would occur in the 0.25s, and now entry [4] is { t = 1.25, v = 67,5 }. The former entry 4 is now entry 5, and so forth.

In any case, the state.focus.forecast is calculated any time you you gain or spend focus. Queries are cached in state.focus.times (so if I looked to see how much focus I have in 2.5s, it's cached in that table) and state.focus.values (so if I searched to see when I'll reach 85 focus, focus.values[80] will have that time value cached).

commented

Thanks for the info, I will have a look into the code to check the predictions.

Mechanically you can apply dire_frenzy to 3 stacks and keep them active, because BM has a passive Wild Call which resets dire_frenzy or dire_beast cooldown (can be further increased by talent One With The Pack.

I played Marksman for 2 patches now but now I am switching back to BM, so my experience in the focus regeneration is not very solid, but to my observations ingame it is like this:

Dire Beast: Every Application gets its own buff to the player and every buff gives focus regen. So if you have 2 buffs, you should get double focus, for 3 buffs triple etc. (can be 3 or 4 buffs sometimes if Wild Call is procing stupidly ;) )

Dire Frenzy: This buff is applied on the pet and can only be tracked there currently and of my observations the stacks should multiply the focus regen like Dire Beast (but there I'm not exactly sure, have to test)

commented

please see my pull request with a proposal how it can look like in the code

Both Dire Beast and Dire Frenzy have a buff on the player for the regen and they don't stack on the player.

commented

Go into the game and press Dire Frenzy twice.
Pet will have 2 stacks.
Player will have two separate applications of Dire Frenzy with different spell IDs.