Thread Tools Display Modes
05-04-20, 06:28 PM   #1
save-disk
A Murloc Raider
AddOn Author - Click to view addons
Join Date: Feb 2020
Posts: 9
Help with using expirationTime

Hey there! It's me again- Didn't know whether it's best to make a new thread for a new question or not.

I've been having difficulty with getting the remaining time on a buff - I'm probably missing something obvious but all my many google searches keep bringing me to threads with solutions that don't work, but I noticed that these threads are from many years ago and things may have changed since then.

Essentially I just want something to happen when a particular buff has only 1 minute left (or 10 seconds, as i've been using for testing purposes on a shorter buff). I'm aware that expirationTime needs to be used along with GetTime() to convert it to seconds- but the methods that i've tried for doing this haven't worked. I had some luck using this snippet from a much older thread on this forum:

Code:
for i=1,40 do
    local name, icon, _, _, _, etime = UnitBuff("player",i)
    if name == "Summon Chauffeur" or name == "Ban-Lu, Grandmaster's Companion" then
        --do things
    end
end
But my attempts to change it to perform a function once the buff is about to run out have failed so many times I thought it best to just ask.

My last try was this: (please excuse my messy/probably wrong code, I'm a beginner at coding but I tried my best!)

Code:
for i=1,40 do
    local name, icon, _, _, _, etime = UnitBuff("player",i)
    timeleft = etime - GetTime()
    if timeleft <= 10 then
        print "test"
    end
end
Thanks again for your time, i've learned a lot making this.
  Reply With Quote
05-05-20, 12:51 AM   #2
Urtgard
A Fallenroot Satyr
AddOn Author - Click to view addons
Join Date: Apr 2016
Posts: 25
Is this all of your code? The loop will only run once on login/reload.

You could take a look at the events when you apply or update a buff.
Then check when the buff expires and use C_Timer to show the warning at the right time.

Or run your loop with an OnUpdate script or some other event.

I've never really worked with buffs, so not sure what and how often the events are called.


A bit about events: https://wow.gamepedia.com/Handling_events
  Reply With Quote
05-05-20, 05:13 AM   #3
Xrystal
nUI Maintainer
 
Xrystal's Avatar
Premium Member
AddOn Author - Click to view addons
Join Date: Feb 2006
Posts: 5,877
This appears to be how nUI does it and I haven't seen it be too far out with its calculations

Lua Code:
  1. for i=1,40 do
  2.         local aura.name, aura.icon, aura.count, aura.type, aura.max_time, aura.end_time, aura.caster, aura.is_stealable = UnitAura( unit_id, i, filter );
  3.         if aura.max_time and aura.max_time > 0 then
  4.         aura.start_time = aura.end_time - aura.max_time;
  5.     elseif aura.end_time and aura.end_time > 0 then
  6.         aura.start_time = GetTime();
  7.         aura.max_time   = aura.end_time - aura.start_time;
  8.     else
  9.         aura.max_time   = nil;
  10.         aura.start_time = nil;
  11.         aura.end_time   = nil;
  12.     end
  13. end

nUI calls the function that contains this code using a function ( updateAuraInfo( unit_id, unit_info ) ) for each unit. Which is called whenever the custom OnAuraUpdate function is run every update cycle and there is something to process in its UpdateQueue.

Our OnAuraEvent function handles the following events:
frame:RegisterEvent( "ADDON_LOADED" );
frame:RegisterEvent( "PLAYER_ENTERING_WORLD" );
frame:RegisterEvent( "UNIT_AURA" );

In ADDON_LOADED we set up the aura buttons and refresh the callbacks ( which then updates the auras )
In PLAYER_ENTERING_WORLD we refresh the callbacks again
In UNIT_AURA we add the aura and unit it has affected to an update queue to be acted on the next time the game does an update cycle.

I haven't played with C_Timer myself so not sure what restrictions if any there are in how it can be used but in *theory* you could do something like the following but I suspect it would just set up multiple timers, so you may have to have some sort of list of timers and whether they can be re-used:

Lua Code:
  1. for i=1,40 do
  2.         local name, icon, count, type, maxTime, endTime, caster, isStealable = UnitAura( unit_id, i, filter );
  3.         if name then
  4.             local startTime = GetTime();
  5.             local timeLeft = endTime - startTime
  6.             local timeBufferImminent = 10
  7.             local timeBufferSoon = 60
  8.             C_Timer.After(timeLeft - timeBufferImminent,function() AuraExpireImminent(unit_id,name,i) end)
  9.             C_Timer.After(timeLeft - timeBufferSoon,function() AuraExpireSoon(unit_id, name,i) end)
  10.         end
  11. end

I've added name in there in case there is a glitch and aura i is no longer aura name. How that would occur I don't know. How much of the aura system is accessible I don't know myself either. I recall limits on only seeing your own auras on other players or npcs. But not sure if that was Classic or Retail.
__________________

Last edited by Xrystal : 05-05-20 at 05:44 AM.
  Reply With Quote
05-05-20, 07:41 AM   #4
SDPhantom
A Pyroguard Emberseer
 
SDPhantom's Avatar
AddOn Author - Click to view addons
Join Date: Jul 2006
Posts: 2,308
Originally Posted by Xrystal View Post
Lua Code:
  1. for i=1,40 do
  2.         local name, icon, count, type, maxTime, endTime, caster, isStealable = UnitAura( unit_id, i, filter );
  3.         if name then
  4.             local startTime = GetTime();
  5.             local timeLeft = endTime - startTime
  6.             local timeBufferImminent = 10
  7.             local timeBufferSoon = 60
  8.             C_Timer.After(timeLeft - timeBufferImminent,function() AuraExpireImminent(unit_id,name,i) end)
  9.             C_Timer.After(timeLeft - timeBufferSoon,function() AuraExpireSoon(unit_id, name,i) end)
  10.         end
  11. end

I've added name in there in case there is a glitch and aura i is no longer aura name. How that would occur I don't know. How much of the aura system is accessible I don't know myself either. I recall limits on only seeing your own auras on other players or npcs. But not sure if that was Classic or Retail.
name will be nil when you run out of buffs in your scanning. For example, you're scanning for 40 buffs, but only have 3, when you scan for buff #4, it'll return nil because there is no buff #4.
__________________
WoWInterface AddOns
"All I want is a pretty girl, a decent meal, and the right to shoot lightning at fools."
-Anders (Dragon Age: Origins - Awakening)
  Reply With Quote
05-05-20, 10:26 AM   #5
Xrystal
nUI Maintainer
 
Xrystal's Avatar
Premium Member
AddOn Author - Click to view addons
Join Date: Feb 2006
Posts: 5,877
Originally Posted by SDPhantom View Post
name will be nil when you run out of buffs in your scanning. For example, you're scanning for 40 buffs, but only have 3, when you scan for buff #4, it'll return nil because there is no buff #4.
Yep, that is the other reason. I was thinking more the case that between the aura's being read and the timers being set that perhaps the aura at no 3 wasn't the same as the one initially at no 3. I'm not sure if things get shuffled around by wow based on expiration order.
__________________
  Reply With Quote
05-05-20, 01:23 PM   #6
save-disk
A Murloc Raider
AddOn Author - Click to view addons
Join Date: Feb 2020
Posts: 9
Thank you for your explanations and your help, it's very kind of you! I think I may be understanding wrong, because I tried both solutions you posted and I can't seem to get them to work - This was the little test I wrote, am I doing something stupid ive just overlooked here?

Lua Code:
  1. frame:RegisterEvent("UNIT_AURA");
  2. local function eventHandler(self, event, ...)
  3.   print "test1"
  4.  
  5. for i=1,40 do
  6.         local aura.name, aura.icon, aura.count, aura.type, aura.max_time, aura.end_time, aura.caster, aura.is_stealable = UnitAura("player", i);
  7.         if aura.max_time and aura.max_time > 0 then
  8.         aura.start_time = aura.end_time - aura.max_time;
  9.     elseif aura.end_time and aura.end_time > 0 then
  10.         aura.start_time = GetTime();
  11.         aura.max_time   = aura.end_time - aura.start_time;
  12.     else
  13.         aura.max_time   = nil;
  14.         aura.start_time = nil;
  15.         aura.end_time   = nil;
  16.     end
  17.    
  18.   if aura.name == Power Word: Shield then
  19.     print "test2"
  20.     end
  21. end
  22. end
  23. end
  Reply With Quote
05-05-20, 02:15 PM   #7
Seerah
Fishing Trainer
 
Seerah's Avatar
WoWInterface Super Mod
Featured
Join Date: Oct 2006
Posts: 10,860
Where are you creating the frame, and where are you telling it to run your function on that event?
__________________
"You'd be surprised how many people violate this simple principle every day of their lives and try to fit square pegs into round holes, ignoring the clear reality that Things Are As They Are." -Benjamin Hoff, The Tao of Pooh

  Reply With Quote
05-05-20, 03:01 PM   #8
Fizzlemizz
I did that?
 
Fizzlemizz's Avatar
Premium Member
AddOn Author - Click to view addons
Join Date: Dec 2011
Posts: 1,857
UNIT_AURA tells you when you gain/loose an aura (you might be waiting a while for this to fire). To update the time left on an aura you need to use an OnUpdate script (which happens every time the frame is updated ie. lots)

Lua Code:
  1. frame.TimeToCheck = 1 -- check buff timers every second
  2. frame:SetScript("OnUpdate", function(self, elapsed)
  3.     self.TimeToCheck = self.TimeToCheck - elapsed
  4.     if self.TimeToCheck > 0 then
  5.         return -- We haven't counted down to zero yet so do nothing
  6.     end
  7.     self.TimeToCheck = 1 -- We've waited a second so reset the timer
  8.     for i=1, 40 do
  9.         -- check your buff/debuff times
  10.     end
  11. end)

Or a C_Timer as mentioned above but you need to check more often or at a known time rather than when a random event fires.
__________________
Fizzlemizz
Maintainer of Discord Unit Frames and Discord Art.
Author of FauxMazzle, FauxMazzleHUD and Move Pad Plus.

Last edited by Fizzlemizz : 05-05-20 at 03:50 PM.
  Reply With Quote
05-05-20, 06:29 PM   #9
save-disk
A Murloc Raider
AddOn Author - Click to view addons
Join Date: Feb 2020
Posts: 9
Thanks everybody for the extra pointers, I'm very grateful! Seerah, here is my complete code right now (pardon how ugly my button looks, i'm planning to pretty it up once it all works. I also plan to find the aura by name in that if statement, not just by only having ten seconds remaining.) I put all the suggestions in, to my best ability - it's not working but I'm certain that's just because of a rookie error on my end.

Lua Code:
  1. local button = CreateFrame("Button", "myMacroButton", UIparent, "SecureActionButtonTemplate")
  2.  
  3. frame:RegisterEvent("UNIT_AURA");
  4. local function timeleft(self, event, ...)
  5.  
  6. frame.TimeToCheck = 1 -- check buff timers every second
  7. frame:SetScript("OnUpdate", function(self, elapsed)
  8.     self.TimeToCheck = self.TimeToCheck - elapsed
  9.     if self.TimeToCheck > 0 then
  10.         return -- We haven't counted down to zero yet so do nothing
  11.     end
  12.     self.TimeToCheck = 1 -- We've waited a second so reset the timer
  13.  
  14. for i=1,40 do
  15.         local aura.name, aura.icon, aura.count, aura.type, aura.max_time, aura.end_time, aura.caster, aura.is_stealable = UnitAura("player", i);
  16.         if aura.max_time and aura.max_time > 0 then
  17.         aura.start_time = aura.end_time - aura.max_time;
  18.     elseif aura.end_time and aura.end_time > 0 then
  19.         aura.start_time = GetTime();
  20.         aura.max_time   = aura.end_time - aura.start_time;
  21.     else
  22.         aura.max_time   = nil;
  23.         aura.start_time = nil;
  24.         aura.end_time   = nil;
  25.     end
  26.     return aura.end_time - aura.start_time
  27.    
  28.      if timeleft <= 10 then
  29.     print "test"
  30.     popup()
  31.     end
  32.  end
  33.  end
  34.  
  35.  
  36.     end
  37. end)
  38.  
  39.    
  40.   local function popup()
  41.    
  42.             button:Show()
  43.             PlaySound(1221)
  44.             FlashClientIcon()
  45.             button:SetAttribute("type1", "macro") -- left click causes macro
  46.             button:SetAttribute("macrotext1", "/cast Reflecting Prism")
  47.             button:SetPoint("CENTER", mainframe, "CENTER", 0, 0)
  48.  
  49.        
  50.            button:SetText("Your Reflecting Prism will expire in 1 minute.")
  51.                 button:SetNormalFontObject("GameFontNormalSmall")
  52.        
  53.                 button:SetNormalTexture("Interface/Icons/Inv_jewelcrafting_prism")
  54.                 button:SetHighlightTexture("Interface/ICONS/Spell_nature_wispsplode")
  55.                 button:SetPushedTexture("Interface/Icons/Spell_nature_wispsplode")
  56.    
  57.             button:SetAttribute("type", "spell")
  58.             button:SetAttribute("spell", spell)
  59.             button:SetPoint('CENTER')
  60.             button:SetSize(30, 30)
  61.  
  62.             button:EnableMouse(true)
  63.             button:RegisterForClicks("LeftButtonUp")
  64.             button:SetScript("PostClick", function(self, arg1)
  65.                 print "x"
  66.                 button:Hide()
  67.                 button:SetParent(nil)
  68.                 end)
  69.         end
  Reply With Quote
05-05-20, 09:37 PM   #10
Fizzlemizz
I did that?
 
Fizzlemizz's Avatar
Premium Member
AddOn Author - Click to view addons
Join Date: Dec 2011
Posts: 1,857
I think this is more what you're looking for (I'm using Arcane intellect if you have a Mage or choose another spell to test that doesn't cost). I'm only counting down a couple of seconds from the start of the buff being applied so do with the timing as you will (x seconds before it runs out or whatever). Also printing the countdown so you can see it running.

Once a frame is hidden it's OnUpdate stops so I'm using a second frame for that and checking the event then using it to show the button if it finds the buff. This is where C_Timer is possibly a useful alternative but, this is just a basic starter example of how it might work.

I've started with the button hidden but that's up to you if you want it to be the primary source of the buff rather than just a popup reminder.

functions, variables etc. declared as local can't be used until after they're declared and only within the same code chunk.

Lua is case sensitive so UIparent is not the same as UIParent.

There are a bunch of other errors, if you don't have them installed I would suggest getting BuggGrabber and Bugsack so you can see when and mostly where/what your errors are.

Lua Code:
  1. -- Setup the button
  2. local button = CreateFrame("Button", "save-disk_MacroButton", UIParent, "SecureActionButtonTemplate, ActionButtonTemplate")
  3. self:SetAlpha(0.3)
  4. button:SetSize(24, 24)
  5. --button:SetPoint("CENTER", mainframe, "CENTER", 0, 0)
  6. button:SetPoint("CENTER")
  7. button:SetAttribute("type1", "macro") -- left click causes macro
  8. --button:SetAttribute("macrotext", "/cast Reflecting Prism")
  9. button:SetAttribute("macrotext", "/cast Arcane Intellect")
  10. button:SetText("Your Reflecting Prism will expire in 1 minute.")
  11. button:SetNormalFontObject("GameFontNormalSmall")
  12. button:SetNormalTexture("Interface/Icons/Inv_jewelcrafting_prism")
  13. button:SetHighlightTexture("Interface/ICONS/Spell_nature_wispsplode")
  14. button:SetPushedTexture("Interface/Icons/Spell_nature_wispsplode")
  15. button:EnableMouse(true)
  16. button:RegisterForClicks("LeftButtonUp")
  17. button:SetScript("PostClick", function(self, arg1)
  18.     self:SetAlpha(0.3)
  19. end)
  20.  
  21. -- Show button function (code could be rolled into the timer)
  22. -- could be used to change the macro, icon, text depending on buff
  23. -- could be used for other stuff.
  24. local function popup(self)
  25.     self:SetAlpha(1)
  26.     PlaySound(1221)
  27.     FlashClientIcon()
  28. end
  29.  
  30. -- Timer function
  31. local function MyOnUpdate(self, elapsed)
  32.     self.TimeToCheck = self.TimeToCheck - elapsed
  33.     if self.TimeToCheck > 0 then
  34.         return -- We haven't counted down to zero yet so do nothing
  35.     end
  36.     self.TimeToCheck = 1 -- We've waited a second so reset the timer
  37.     for i=1,40 do
  38.             local name, icon, count, type, max_time, end_time, caster, buttonis_stealable = UnitAura("player", i);
  39.         if not name then break end -- no name means no more buffs to check
  40. --          if name == "Reflecting Prism" then
  41.             if name == "Arcane Intellect" then
  42.             local TimeLeft = end_time - GetTime()-- check to see how much timer is left
  43.             print(name, TimeLeft)
  44.             if TimeLeft < 3595 then -- if we're in the zone
  45.                 self:SetScript("OnUpdate", nil)  -- cancel, the timer
  46.                 if not button:IsShown() then
  47.                     popup(button) -- show the button if required
  48.                 end
  49.                 return
  50.             end
  51.         end
  52.     end
  53. end
  54.  
  55. -- Buff detection/timer starter frame
  56. local Auraframe = CreateFrame("Frame")
  57. Auraframe.TimeToCheck = 1
  58. Auraframe:RegisterEvent("UNIT_AURA");
  59. Auraframe:SetScript("OnEvent", function(self, event, ...)
  60. --  if AuraUtil.FindAuraByName("Reflecting Prism", "player") then
  61.     if AuraUtil.FindAuraByName("Arcane Intellect", "player") then -- detect the buff has been applied
  62.         self:SetScript("OnUpdate", MyOnUpdate) -- start the timer
  63.     end
  64. end)
__________________
Fizzlemizz
Maintainer of Discord Unit Frames and Discord Art.
Author of FauxMazzle, FauxMazzleHUD and Move Pad Plus.

Last edited by Fizzlemizz : 05-06-20 at 03:55 PM.
  Reply With Quote
05-06-20, 02:22 PM   #11
SDPhantom
A Pyroguard Emberseer
 
SDPhantom's Avatar
AddOn Author - Click to view addons
Join Date: Jul 2006
Posts: 2,308
Originally Posted by Xrystal View Post
Yep, that is the other reason. I was thinking more the case that between the aura's being read and the timers being set that perhaps the aura at no 3 wasn't the same as the one initially at no 3. I'm not sure if things get shuffled around by wow based on expiration order.
They get shuffled around based on start time. Note refreshing an existing buff doesn't change its position.
Also, you should consider buffs getting reapplied before their timer expires or the user canceling buffs. Both of these make C_Timer.After() a poor candidate.


Originally Posted by Fizzlemizz View Post
Code:
button:SetScript("PostClick", function(self, arg1)
	self:Hide()
end)
Code:
local function popup(self)
	self:Show()
	PlaySound(1221)
	FlashClientIcon()
end
SecureActionButtonTemplate is a protected template, meaning you can't show or hide the button while in combat.
__________________
WoWInterface AddOns
"All I want is a pretty girl, a decent meal, and the right to shoot lightning at fools."
-Anders (Dragon Age: Origins - Awakening)

Last edited by SDPhantom : 05-06-20 at 02:40 PM.
  Reply With Quote
05-06-20, 02:37 PM   #12
Xrystal
nUI Maintainer
 
Xrystal's Avatar
Premium Member
AddOn Author - Click to view addons
Join Date: Feb 2006
Posts: 5,877
Originally Posted by SDPhantom View Post
They get shuffled around based on start time. Note refreshing an existing buff doesn't change its position.
Also, you should consider buffs getting reapplied before their timer expires or the user canceling buffs. Both of these make C_Timer.After() a poor candidate.

I did say it was a theory

My theory was that C_Timer may be an alternative to the update routine as long as you do the appropriate checks and include changes based on the UnitAura event to make adjustments accordingly ... adjusting the timer ( if possible ) to reflect the new value due to early re-application and cancel the timer ( again if possible ) so that it's function doesn't get called.

nUI currently uses the update routine and UnitAura and works fine for what it does with them. But it's good to know that C_Timer may not be the best option for some areas of wow for if and when I ever consider it.
__________________
  Reply With Quote
05-06-20, 02:47 PM   #13
SDPhantom
A Pyroguard Emberseer
 
SDPhantom's Avatar
AddOn Author - Click to view addons
Join Date: Jul 2006
Posts: 2,308
While C_Timer.After() can't be canceled, Blizzard has a Lua-implemented extension that can, C_Timer.NewTimer(). This returns a ticker object with a the method ticker:Cancel().

Note, the base C_Timer.After() still fires, but the middleman function then decides whether or not to pass it along to your callback.
__________________
WoWInterface AddOns
"All I want is a pretty girl, a decent meal, and the right to shoot lightning at fools."
-Anders (Dragon Age: Origins - Awakening)
  Reply With Quote
05-06-20, 03:18 PM   #14
Fizzlemizz
I did that?
 
Fizzlemizz's Avatar
Premium Member
AddOn Author - Click to view addons
Join Date: Dec 2011
Posts: 1,857
Originally Posted by SDPhantom View Post
SecureActionButtonTemplate is a protected template, meaning you can't show or hide the button while in combat.
That's one of the problems with the code although it was never intended as the "total" solution .
__________________
Fizzlemizz
Maintainer of Discord Unit Frames and Discord Art.
Author of FauxMazzle, FauxMazzleHUD and Move Pad Plus.
  Reply With Quote
05-07-20, 03:30 AM   #15
save-disk
A Murloc Raider
AddOn Author - Click to view addons
Join Date: Feb 2020
Posts: 9
Thank you fizzlemizz for correcting some of my mistakes- I'll install that debugger and go through it soon to find the others! Consider my original question resolved as I don't want to bother you all anymore with fixing my code for me in your own time

EDIT:
I managed to iron out the bugs and have a working addon now, thank you so much! I don't plan on sharing it widely except with friends, but I've added credit at the top to all of you just in case.

Last edited by save-disk : 05-07-20 at 08:44 PM. Reason: Didn't want to doublepost
  Reply With Quote

WoWInterface » AddOns, Compilations, Macros » AddOn Help/Support » Help with using expirationTime

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