Deriving/calculating GCD - WoWInterface
Thread Tools Display Modes
06-07-18, 02:57 AM   #1
A Flamescale Wyrmkin
Join Date: Feb 2018
Posts: 148
Deriving/calculating GCD

Is there a way I can ask the game engine to tell me the current GCD?

I have been using

local startTime, duration, enabled = GetSpellCooldown(61304);

however this will give me the current/recent GCD only if I call it during the GCD! Otherwise I get 0s. Of course.

Any ideas ?
  Reply With Quote
06-07-18, 03:48 AM   #2
A Pyroguard Emberseer
myrroddin's Avatar
AddOn Author - Click to view addons
Join Date: Oct 2008
Posts: 1,135
If you are asking about the global cooldown, that's easy. It is 1.5 seconds less the haste percentage. Each 10% of haste reduces the GCD by 0.1 seconds, therefore 50% haste makes the GCD 1 second. Yes, you can get some really small fractions, but they do count.

Edit: the documentation page goes on to explain how to get the global cooldown using spellID 61304.

Now, if you are asking about the recycle cooldown of a spell, that's different. Read the code snippet for the API documentation for GetSpellCooldown() https://wow.gamepedia.com/API_GetSpellCooldown

Last edited by myrroddin : 06-07-18 at 03:50 AM. Reason: more information
  Reply With Quote
06-07-18, 08:44 AM   #3
A Flamescale Wyrmkin
Join Date: Feb 2018
Posts: 148
Hi, thanks.

I am asking about the GCD and I do not want to presume to calculate it since the game engine knows better. You have hastes and delays, and a million other things, let the engine tell you, if it can.

My question is "what is the GCD that will be applied on my next cast after everything is factored in?". I do not want to cast the spell and find out, I want to know before, if possible. I do realise that my spell, if I cast it, might change the GCD, and that a few ticks action delay on my part might cause some game mechanic to change everything and throw my previous GCD calculation out of the window.

My workaround : do a "local startTime, duration, enabled = GetSpellCooldown(61304);" pretty often, and its "duration", if not 0, is my "current GCD".
  Reply With Quote
06-07-18, 02:03 PM   #4
A Pyroguard Emberseer
AddOn Author - Click to view addons
Join Date: Mar 2010
Posts: 1,290
GetSpellCooldown(61304) is the most accurate way, however it only returns it's value while the GCD is already triggered.
  Reply With Quote
06-07-18, 11:29 PM   #5
A Warpwood Thunder Caller
aallkkaa's Avatar
AddOn Author - Click to view addons
Join Date: Jun 2017
Posts: 98
On ReadySpells, which I'm working on to release an up to date version (and taking way much longer than I'd initially thought ), I came up with this:
Lua Code:
  1. -- One-time GCD getter; to be called only by getGCD() bellow
  2. local gcd = 0;  -- gcd == 0 means the GCD is not currently running
  3. local getGCDonce = function()
  4.     -- SpellID:61304 -> (Dummy)SpellName:"Global Cooldown"
  5.     local _, duration = GetSpellCooldown(61304);
  6.     gcd = duration or 0; -- in seconds, with three decimal places
  7.     -- print(AddonName.. ": GCD = ".. gcd);
  8. end
  9. -- This function will be called upon UNIT_SPELLCAST_SENT, which fires at the  start of onGCD-
  10. --  instant-cast, onGCD-overtime-cast, offGCD-cast and channeled spells.
  11. local getGCD = function()
  12.     getGCDonce();
  13.     -- We also need to update the value of the GCD when the spell has finished casting. It should
  14.     -- then be 0(ZERO), but I think it's plausible that, if a new spell had been queued by the game
  15.     -- client (within less than the set lag's time), UNIT_SPELLCAST_SENT may fire before our timer
  16.     -- elapses. Thus, instead of just doing 'gcd = 0', we'll again fetch the value from the server.
  17.     if gcd > 0 then
  18.         C_Timer.After(gcd, getGCDonce);
  19.         -- TODO: Implement routine to fetch latency and set the timer above to be "gcd - latency".
  20.         --  On second thought, this may just return that the GCD hasn't expired yet (?). INSTEAD,
  21.         --  make the function(s) that use this variable account for the latency, leaving the 'gcd'
  22.         --  var itself untouched !
  23.     end
  24. end
Pretty much all my thoughts on this subject are in that snippet. Just adding that, on ReadySpells, the "gcd" variable is used to grey out the next ready spell while the GCD is runnin, so the above (assuming the "TODO" implemented) is good enough.

I don't think there's a practical way to achieve what you want. You could cache the results of each spell the first time they're cast, but you'd need to cast each at least once.
And then there's all the game mechanics, like e.g. casting a spell while under Heroism/Bloodlust would obviously change the return value of GetSpellCooldown(61304). So... you'd basically have to take into account all the possible factors that might change the GCD every time you did your calculation (for example, last GCD-fetch Heroism was on, now it's not, you'd add 0.3 seconds to your last GCD). And, honestly, to do that, then you'd be better off following myrroddin's suggestion and adding in the other factors... Which still seems like a very bad idea to me (factoring in everythin on top of the calculation myrroddin's calculation).

IMHO either do as myrroddin suggested or as you're already doing it. Doesn't look to me that you can feasibly improve on those options. Unless BfA brings some important change to this (of which I'm not aware, nor have I done any testing).
  Reply With Quote
06-08-18, 12:06 AM   #6
An Aku'mai Servant
AddOn Author - Click to view addons
Join Date: Apr 2008
Posts: 33
GetSpellCooldown(61304) after event ACTIONBAR_UPDATE_COOLDOWN will do the trick

also, all spell durations < 1.5 sec are gcd, if you are willing to use GetActionCooldown on your bar actions
  Reply With Quote
06-08-18, 12:19 PM   #7
A Molten Giant
Aftermathhqt's Avatar
AddOn Author - Click to view addons
Join Date: Dec 2009
Posts: 784
Here's my GCD addon that i've made, hope it can help!

Lua Code:
  1. local A, C, L = select(2, ...):unpack()
  2. local GCDBar = CreateFrame("Frame")
  3. local DataBars = A["DataBars"]
  4. local Panels = A["Panels"]
  6. -- Lib Globals
  7. local select = select
  8. local unpack = unpack
  9. local floor = math.floor
  11. -- WoW Globals
  12. local GetSpellCooldown = GetSpellCooldown
  14. -- Locals
  15. GCDBar.StartTime = 0
  16. GCDBar.Duration = 0
  18. function GCDBar:Check(event, unit, spell)
  19.     if not (unit == "player") then
  20.         return
  21.     end
  23.     local StartTime, Duration = GetSpellCooldown(spell)
  25.     if (Duration and Duration > 0 and Duration <= 1.5) then
  26.         GCDBar.StartTime = StartTime
  27.         GCDBar.Duration = Duration
  28.         self.Bar:Show()
  29.     else
  30.         self.Bar:Hide()
  31.     end
  32. end
  34. function GCDBar:Update()
  35.     local Time = (GetTime() - GCDBar.StartTime) / GCDBar.Duration
  37.     if (Time > 1) then
  38.         self.Bar:Hide()
  39.     else
  40.         self.Bar:SetMinMaxValues(0, 1)
  41.         self.Bar:SetValue(Time)
  42.     end
  43. end
  45. function GCDBar:CreateBar()
  46.     local Bar = CreateFrame("StatusBar", nil, UIParent)
  47.     Bar:Size(372, 4)
  48.     Bar:Point("BOTTOM", UIParent, 0, 112)
  49.     Bar:SetStatusBarTexture(C.Media.Blank)
  50.     Bar:SetStatusBarColor(1, 0.8, 0)
  51.     Bar:CreateBackdrop()
  52.     Bar:CreateShadow()
  53.     Bar:Hide()
  55.     Bar.Spark = Bar:CreateTexture(nil, "OVERLAY", nil)
  56.     Bar.Spark:Width(Bar:GetHeight())
  57.     Bar.Spark:Point("CENTER", Bar:GetStatusBarTexture(), "RIGHT", 0, 0)
  58.     Bar.Spark:SetTexture([[Interface\CastingBar\UI-CastingBar-Spark]])
  59.     Bar.Spark:SetBlendMode("ADD")
  60.     Bar.Spark:SetVertexColor(0.55, 0.55, 0.55, 0.8)
  62.     local Animation = A["LibAnimSmooth"]
  63.     Animation:AddSmooth(Bar)
  65.     self:RegisterEvent("UNIT_SPELLCAST_SENT")
  66.     self:RegisterEvent("UNIT_SPELLCAST_START")
  67.     self:RegisterEvent("UNIT_SPELLCAST_SUCCEEDED")
  68.     self:RegisterEvent("PLAYER_ENTERING_WORLD")
  69.     self:SetScript("OnEvent", self.Check)
  70.     self:SetScript("OnUpdate", self.Update)
  72.     self.Bar = Bar
  73. end
  75. function GCDBar:Enable()
  76.     if not (C.DataBars.GCDBar) then
  77.         return
  78.     end
  80.     if not (self.IsCreated) then
  81.         self:CreateBar()
  82.         self.IsCreated = true
  83.     end
  84. end
  86. DataBars.GCDBar = GCDBar
  Reply With Quote
06-08-18, 02:20 PM   #8
A Pyroguard Emberseer
myrroddin's Avatar
AddOn Author - Click to view addons
Join Date: Oct 2008
Posts: 1,135
Also, if you ever run into a situation where a spell's cooldown is shorter than the global cooldown, then yes, the spell will finish before the GCD, but you will still need to wait for the GCD to finish before the spell can be used. I can't think of a current example, but there used to be cases on my Shadow Priest where Mind Flay would recycle faster than the GCD, and thus I had to wait to cast again.

This is because the GCD cannot drop below 1 second no matter how much haste you have (thus having more than 50% haste is pointless) unless something has changed during Legion or BfA.

Or, think of it this way: Mind Flay has a cast time with 0 haste of 1.5 seconds, with a recycle time of 1.5 seconds, both exactly the same as the GCD. The lowest the GCD can go is 1 second yet I could get 70% or more haste between heroism, buffs, boss debuffs, and my own haste. That knocked Mind Flay to less than a second to cast, but I still had to wait the balance of that second to cast Mind Flay again, and because the GCD was working, neither could I cast any other spell. For between 0.25-0.5 of a second I literally could cast nothing, could not swing my staff, do nothing other than look there looking all priestly.
  Reply With Quote

WoWInterface » Developer Discussions » Lua/XML Help » Deriving/calculating GCD

Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

vB code is On
Smilies are On
[IMG] code is On
HTML code is Off