Thread Tools Display Modes
07-11-09, 10:51 AM   #1
Morant
A Deviate Faerie Dragon
 
Morant's Avatar
Join Date: Feb 2009
Posts: 18
Talking Code Help - Event Sounds

EDIT: Like a nub I didn't read the FULL section description - Please move this to the correct section. Apologies.

I prefer a clean UI and allow as little as possible on the screen. I have tried several mods to get the following effect but they always have bells and whistles that clutter things up or they are complicated and hard to configure.

That said I found a little addon called ShotProc a guy made for his hunter. Makes a sound when a buff aura is applied to you. I managed to read enough through Google and forums to alter that code to the following for my mage. Simple and effective except that in raids I have to turn it off or it lags me terrible when several sounds proc at the same time. PVP is no problem but full raid DPS procs cause a lag. (I think I found at least a partial reason for the lag reading these forums today and will convert all my chosen sounds from .wav to .mp3.)

Code:
Reset_Interval = 900.0
Seal_Interval = 0.2
local Sealinc = 0
local sound = 1
local soundt = "|cff33ff99 on"

function Checker(self, elapsed)
  self.TimeSinceLastUpdate = self.TimeSinceLastUpdate + elapsed;
  
  if (self.TimeSinceLastUpdate > Reset_Interval) then
  	self.TimeSinceLastUpdate = 0
  end
end


function Shotprocc_Start()
	SLASH_SHOTPROCCALARM1 = "/shotproc"
	SlashCmdList["SHOTPROCCALARM"] = Shotprocchelp
end

function Shotprocchelp(msg)

	if (msg=="sound on") then
		sound = 1
		soundt = "|cff33ff99 On"
	end

	if (msg=="sound off") then
		sound = 0
		soundt= "|cffffff78 Off"
	end
	if (msg=="") then
			DEFAULT_CHAT_FRAME:AddMessage("Sound Control: /shotproc sound on/off")
	end
	DEFAULT_CHAT_FRAME:AddMessage("Current Configurations: Sound="..soundt)
end

function Potent() 
	this:RegisterEvent("COMBAT_LOG_EVENT_UNFILTERED") 
	this:SetScript("OnEvent", function(self, event, ...) 
		local timestamp, eventType, srcGUID, srcName, srcFlags, dstGUID, dstName, dstFlags = select(1, ...) 
		local arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11 = select(9, ...) 
		

		if eventType == "SPELL_AURA_APPLIED" then 
		if srcName == UnitName("player") then 
				if (arg2 == "Missile Barrage") then
					if (sound > 0) then
						PlaySoundFile("Interface\\AddOns\\ShotProc\\Sounds\\misslebarrage.mp3")
					end
					self.TimeSinceLastUpdate = 0
				end
				if (arg2 == "Clearcasting") then
					if (sound > 0) then
						PlaySoundFile("Interface\\AddOns\\ShotProc\\Sounds\\clearcasting.mp3")
					end
					self.TimeSinceLastUpdate = 0
				end
				if (arg2 == "Fingers of Frost") then
					if (sound > 0) then
						PlaySoundFile("Interface\\AddOns\\ShotProc\\Sounds\\fingersoffrost.wav")
					end
					self.TimeSinceLastUpdate = 0
				end
				if (arg2 == "Fireball!") then
					if (sound > 0) then
						PlaySoundFile("Interface\\AddOns\\ShotProc\\Sounds\\firball.wav")
					end
					self.TimeSinceLastUpdate = 0
				end
		end
		end
	end)
end

Now the reason for my post. I can not figure out what I need to do to be able to hard code in events other than aura procs. In your replies please keep in mind that I only got this far with a Monkey-See-Monkey-CutPasteEdit method of programming, so go easy on me. If I can get a simple functional bit of code I can replicate and edit to add the events of my choice I am one happy guy!

I would like help with the following types of events.

**When my target applies or activates an aura or buff (eg Spell Reflect)
**When items have cooled off (PVP Trinket)
**When Spells/Abilities are ready (Frost Nova is cooled off)

The first one is really the priority. There are several abilities classes have that I would like a sound bit alert for when they go up. The stuff shows in the combat log so it should be doable? As I said, I have found mods that DO these things just, not in a simple way like I want them.

Thanks for reading!

Last edited by Morant : 07-11-09 at 11:44 AM.
  Reply With Quote
07-11-09, 04:18 PM   #2
Waverian
A Chromatic Dragonspawn
AddOn Author - Click to view addons
Join Date: Dec 2006
Posts: 188
Addons recieve cues, or triggers, when things happen in UI. These are called events (list of events). To detect these things, our addon needs to create a frame element.

Code:
local frame = CreateFrame("Frame")
We have a frame, but it doesn't do anything yet. Frames do not listen to any events unless we explicitly tell the frame to listen to a given event. We tell our frame to listen by calling the :RegisterEvent("SOME_EVENT") function on our frame. The event that is triggered when something happens in the combat log is COMBAT_LOG_EVENT_UNFILTERED.

Code:
frame:RegisterEvent("COMBAT_LOG_EVENT_UNFILTERED")
So we've got a frame, and it's listening to combat log events.. But it doesn't know what to do when this event occurs, so we tell it what to do.

Code:
frame:SetScript("OnEvent", function(self, event, _, eventType, sourceGUID, _, _, _, _, _, spellID)
  -- We tell our frame what to do here.
end)
This is where it gets a little more complicated. In the function declaration, there's a ton of stuff. Some events send the frame data when the event occurs, and all of those variables in the function declaration represents that data. We know what these values means because of empirical testing. You can read documentation about what each of these values represents at wowwiki. The underscores ( _ ) are just a disposable variable we keep writing over, since we don't need those values for this purpose.

In your example code, the function declaration is function(self, event, ...). The ... is something called a vararg (or variable argument). I won't explain that very much here, but basically that represents all of the values the event passes us, but in a large 'list.' If you have a fixed number of arguments, you shouldn't use ..., as the method I'm showing is a bit more performance conscious.

Code:
frame:SetScript("OnEvent", function(self, event, _, eventType, sourceGUID, _, _, _, _, _, spellID)
  -- Combat log events are a unique type of event, in that they have "sub-events." There is a blanket combat log event, then
  -- a separate event for different combat actions: spell hit, spell miss, buffs applied, etc. The "sub-event" for buffs
  -- applied is SPELL_AURA_APPLIED, so we want to check if the combat log event is a buff being applied.
  
  -- Since you only want this to occur for your target, we'll also only execute our code if the unit triggering the event is
  -- your target. A GUID is global, unique identifier for a unit.
  if eventType == "SPELL_AURA_APPLIED" and sourceGUID == UnitGUID("target") then
    -- This code executes if our target gains a buff or debuff.
  end
end)
Now we need to make sure that it's only triggering on spells we want it to trigger on... Running short on time so I won't explain this is depth, but here's the gist of it.

Code:
local spells = {
  [23920] = [[Interface\AddOns\MyAddonFolder\MySoundsFolder\SpellReflect]],
  [48707] = [[Interface\AddOns\MyAddonFolder\MySoundsFolder\AntiMagicShell]],
}
and you can keep adding spells with that format. The number in brackets is the spellID of the buff you want to watch for. You can find this by searching for the spell on wowhead. The numbers in the url will be the spell's spellID. For example: http://www.wowhead.com/?spell=23920 That's spell reflection, and the numbers in the url are 23920, which is the spellID. The stuff after the = is the filepath of the sound file you want to play.

Code:
frame:SetScript("OnEvent", function(self, event, _, eventType, sourceGUID, _, _, _, _, _, spellID)
  if eventType == "SPELL_AURA_APPLIED" and sourceGUID == UnitGUID("target") then
    if spells[spellID] then
      PlaySoundFile(spells[spellID])
    end
  end
end)

Last edited by Waverian : 07-11-09 at 04:22 PM.
  Reply With Quote
07-11-09, 04:31 PM   #3
Morant
A Deviate Faerie Dragon
 
Morant's Avatar
Join Date: Feb 2009
Posts: 18
Your reply is 1000x's more than I expected. Will absorb this and post additional questions in the future.


I just wanted to tip my hat at your effort in this post before I sponge on this. Thanks.
  Reply With Quote
07-14-09, 10:03 AM   #4
Morant
A Deviate Faerie Dragon
 
Morant's Avatar
Join Date: Feb 2009
Posts: 18
Your effort in your reply has not gone wasted by any means my friend. I am rather stunned to find that bits and pieces of what I am looking at are starting to make a small amount of sense. I was able to implement your snippet into the existing addon with some trial and error and can now freely expand its function. As stated in my OP... I am a happy guy!

Question:

In all of this I have noticed that your code accomplishes pretty much the same objective as the original, only written in a MUCH simpler and efficient manner. So I wanted to take a stab at cleaning that up and eliminate some of the "static". To do this would it be better to duplicate the snippet to listen to its own frame or to just nest another "if" statement in the same frame for player auras? Consider that if I am able to continue to grasp what I am doing here and wish it to listen for events from my focus, party members, pets, and so on. Replicating frames would be the most readable code but is nesting multiple if/then statements in the same frame more efficient or just a bad habit?

I have also discovered that I can use SPELL_CAST_START to listen for my target to begin casting a given spellID in the same manner. I am assuming since this is a different event type we are listening for it needs to be in its own frame. Do I need to name these or parent them in any way? Just something I read on frames and I am not sure I understand it correctly.

I admire you people that readily understand this stuff. I find it difficult and confusing but for some reason I am drawn to push on little by little.
  Reply With Quote
07-14-09, 10:34 AM   #5
Waverian
A Chromatic Dragonspawn
AddOn Author - Click to view addons
Join Date: Dec 2006
Posts: 188
Originally Posted by Morant View Post
In all of this I have noticed that your code accomplishes pretty much the same objective as the original, only written in a MUCH simpler and efficient manner. So I wanted to take a stab at cleaning that up and eliminate some of the "static". To do this would it be better to duplicate the snippet to listen to its own frame or to just nest another "if" statement in the same frame for player auras? Consider that if I am able to continue to grasp what I am doing here and wish it to listen for events from my focus, party members, pets, and so on. Replicating frames would be the most readable code but is nesting multiple if/then statements in the same frame more efficient or just a bad habit?
Efficiency wise, creating a ton of frames is probably not the best. I doubt it will cause any problems, but there's really no reason to have dozens of event handlers firing for the same event and the same addon. The easiest way to do this is just conditional statements. The main block of your event handler should be checking to see if it's X event (spell cast, aura applied, etc), then have nested conditional checks for

Code:
if sourceGUID == UnitGUID("whateverUnit")
...
elseif sourceGUID == UnitGUID("someotherunit")
...
end


I have also discovered that I can use SPELL_CAST_START to listen for my target to begin casting a given spellID in the same manner. I am assuming since this is a different event type we are listening for it needs to be in its own frame. Do I need to name these or parent them in any way? Just something I read on frames and I am not sure I understand it correctly.
Nope, it can all be done in the same frame once agian

Code:
if eventType == "SPELL_AURA_APPLIED" then
-- mass of code
elseif eventType == "SPELL_CAST_START" then
-- another mass of code



Just as an addendum, to elaborate on the thing I couldn't explain in the last post due to time

Code:
local spells = {
  [23920] = [[Interface\AddOns\MyAddonFolder\MySoundsFolder\SpellReflect]],
  [48707] = [[Interface\AddOns\MyAddonFolder\MySoundsFolder\AntiMagicShell]],
}
If you hadn't already figured it out, that's called a table. It's just a lua data structure that holds a "list" of data. The bracket is the key or index of the table, and it's how we access data in the table, or add new data to it. The stuff after the = is the value that we access.

So if we did something like:

Code:
print(spells[23920])
the display would be "Interface\AddOns\MyAddonFolder\MySoundsFolder\SpellReflect." The brackets around the file path is just a type of string.

If we did

Code:
print(spells[5])
it would be nil, since we haven't assigned any value to be at the 5th index of the table. Tables keys also don't have to be numbers, they can be strings, or many other types of data. The lua tables tutorial will probably explain it in-depth better than I could: http://lua-users.org/wiki/TablesTutorial
  Reply With Quote
07-14-09, 11:57 AM   #6
Morant
A Deviate Faerie Dragon
 
Morant's Avatar
Join Date: Feb 2009
Posts: 18
Ok... Sponge mode again.


I did have one question come to mind while reading though. If I understand correctly that a frame must finish all code in order for that frame to repeat its function. Does this include the amount of time it takes for the sound file to complete playing? This would explain some of my past lag issues. If I chose a sound that was 1 min long, would the addon wait to process any more events till that sound completed or would it just fire the sounds off over the top of one another not caring where it completes?

If I am thinking correctly, due to what I am trying to accomplish with THIS particular addon it could be better to group the events by type into a few unique frames so the sounds for the different types can stack though the output?

I dunno.. my head hurts now.... lol... I may be off in left field somewhere on this but I just was not sure if the frame had to wait till the output completed or not. I have had no time to test any of this and will do so over the next few days. But I could not get this off my mind and wanted to ask.
  Reply With Quote
07-14-09, 12:16 PM   #7
Waverian
A Chromatic Dragonspawn
AddOn Author - Click to view addons
Join Date: Dec 2006
Posts: 188
Originally Posted by Morant View Post
Ok... Sponge mode again.


I did have one question come to mind while reading though. If I understand correctly that a frame must finish all code in order for that frame to repeat its function. Does this include the amount of time it takes for the sound file to complete playing? This would explain some of my past lag issues. If I chose a sound that was 1 min long, would the addon wait to process any more events till that sound completed or would it just fire the sounds off over the top of one another not caring where it completes?

If I am thinking correctly, due to what I am trying to accomplish with THIS particular addon it could be better to group the events by type into a few unique frames so the sounds for the different types can stack though the output?

I dunno.. my head hurts now.... lol... I may be off in left field somewhere on this but I just was not sure if the frame had to wait till the output completed or not. I have had no time to test any of this and will do so over the next few days. But I could not get this off my mind and wanted to ask.
When a function is called, it will keep processing everything until it reaches the end of that block of code (unless you have a break keyword). However, all of this code happens in fractions of a second so it's insignificant, it won't cause any pausing/freezing in gameplay.

Once sounds begin playing, the UI loses control of them. If you start playing one sound, then play another, they will just play over one another rather than waiting for one to complete.
  Reply With Quote
07-16-09, 09:02 AM   #8
Morant
A Deviate Faerie Dragon
 
Morant's Avatar
Join Date: Feb 2009
Posts: 18
Lesson 2 complete and I would like to turn in my homework here for grading. See how I did. I have not actually tested any of this yet as it is a ways from complete, got some spellID's to look up yet. I was really more interested to see if my application of the theory is correct.

-- Wanted to mention that even though they have made it pointless in the game to do so I have found that if someone uses a lower rank of a spell it has a different spellID and no sound is played. It is rare but it has happend.

Code:
local targetauras = {
  [23920] = [[Interface\AddOns\ShotProc\sounds\spellreflect.mp3]],
  [48707] = [[Interface\AddOns\ShotProc\sounds\antimagicshell.mp3]],
  [642] = [[Interface\AddOns\ShotProc\sounds\divineshield.mp3]],
  [1044] = [[Interface\AddOns\ShotProc\sounds\handoffreedom.mp3]],
  [10278] = [[Interface\AddOns\ShotProc\sounds\handofprotection.mp3]],
  [46924] = [[Interface\AddOns\ShotProc\sounds\bladestorm.mp3]],
  [42292] = [[Interface\AddOns\ShotProc\sounds\pvptrinket.mp3]],

}

local myauras = {
  -- MAGE Only
  [44781] = [[Interface\AddOns\ShotProc\sounds\misslebarrage.mp3]],
  [12536] = [[Interface\AddOns\ShotProc\sounds\clearcasting.mp3]],
  [44545] = [[Interface\AddOns\ShotProc\sounds\fingersoffrost.mp3]],
  [57761] = [[Interface\AddOns\ShotProc\sounds\fireball.mp3]],
  
}

local focusauras = {
  -- ???
  [xxx] = [[Interface\AddOns\ShotProc\sounds\xxx.mp3]],
  [xxx] = [[Interface\AddOns\ShotProc\sounds\xxx.mp3]],
  [xxx] = [[Interface\AddOns\ShotProc\sounds\xxx.mp3]],
  [xxx] = [[Interface\AddOns\ShotProc\sounds\xxx.mp3]],
  
}

local targetspells = {
  -- ???
  [xxx] = [[Interface\AddOns\ShotProc\sounds\xxx.mp3]],
  [xxx] = [[Interface\AddOns\ShotProc\sounds\xxx.mp3]],
  [xxx] = [[Interface\AddOns\ShotProc\sounds\xxx.mp3]],
  [xxx] = [[Interface\AddOns\ShotProc\sounds\xxx.mp3]],
  
}

local focusspells = {
  -- ???
  [xxx] = [[Interface\AddOns\ShotProc\sounds\xxx.mp3]],
  [xxx] = [[Interface\AddOns\ShotProc\sounds\xxx.mp3]],
  [xxx] = [[Interface\AddOns\ShotProc\sounds\xxx.mp3]],
  [xxx] = [[Interface\AddOns\ShotProc\sounds\xxx.mp3]],
  
}


local frame = CreateFrame("Frame")
frame:RegisterEvent("COMBAT_LOG_EVENT_UNFILTERED")
frame:SetScript("OnEvent", function(self, event, _, eventType, sourceGUID, _, _, _, _, _, spellID)
  if eventType == "SPELL_AURA_APPLIED" then
	if sourceGUID == UnitGUID("target") and if targetauras[spellID] then
      PlaySoundFile(targetauras[spellID])
    end
	elseif sourceGUID == UnitGUID("player") and if myauras[spellID] then
      PlaySoundFile(myauras[spellID])
    end
	elseif sourceGUID == UnitGUID("focus") and if focusauras[spellID] then
      PlaySoundFile(focusauras[spellID])
    end

  elseif eventType == "SPELL_CAST_START" then
	if sourceGUID == UnitGUID("target") and if targetspells[spellID] then
      PlaySoundFile(targetspells[spellID])
    end
	elseif sourceGUID == UnitGUID("focus") and if focusspells[spellID] then
      PlaySoundFile(focusspells[spellID])
    end
  end
end)
Now, with the above block replacing the bulk of the original code I started with I would like to know if I can finish the job. There are three remaining functions.

Code:
Reset_Interval = 900.0
Seal_Interval = 0.2
local Sealinc = 0
local sound = 1
local soundt = "|cff33ff99 on"


function Checker(self, elapsed)
  self.TimeSinceLastUpdate = self.TimeSinceLastUpdate + elapsed;
  
  if (self.TimeSinceLastUpdate > Reset_Interval) then
  	self.TimeSinceLastUpdate = 0
  end
end

function Shotprocc_Start()
	SLASH_SHOTPROCCALARM1 = "/shotproc"
	SlashCmdList["SHOTPROCCALARM"] = Shotprocchelp
end

function Shotprocchelp(msg)

	if (msg=="sound on") then
		sound = 1
		soundt = "|cff33ff99 On"
	end

	if (msg=="sound off") then
		sound = 0
		soundt= "|cffffff78 Off"
	end
	if (msg=="") then
			DEFAULT_CHAT_FRAME:AddMessage("Sound Control: /shotproc sound on/off")
	end
	DEFAULT_CHAT_FRAME:AddMessage("Current Configurations: Sound="..soundt)
end
If I understand the first it counts the time since it last counted the time and when the time counted is more than the allowed interval it then sets the count to 0. Wash rinse repeat. It is prolly doing much more than this, but this is what I get out of it. Can you tell me what I am missing here?

The second two are pretty much a settings function. It loads default with the sound on and if the user types "/shotproc sound off" It will set the variable and not play any sounds. It also outputs a message to the chat channel showing things like the saved settings or a help message. So per the above lessons I would add something like this to the very beginning of the our block. Just seems to me best to check for the need to play a sound before going through the trouble to decide what sound to play as the original author did.

Code:
if sound == 1 then
...

end
Unless you feel otherwise I don't think I really need to change anything here except the name of the addon. I have ideas for attempting to add some other functions to this thing and may even want to share it with others at some point. I would like to put my mark on it. Now.. I can easily go through the .lua file and replace "shotproc" with name of choice. What I do not know is if I will need to change or replace the rest of the files. I found a tutorial here on what these files are but have not had time to read through it all. If its really as simple as just going though each file and folder replacing the name, then I am off and running.

I will post the contents of those files if needed but I was trying to not clutter the thread too far off-subject. After this clean-up phase I have a few things I would like to try to do with this thing and get this thread back on track.
  Reply With Quote
07-16-09, 09:53 AM   #9
Waverian
A Chromatic Dragonspawn
AddOn Author - Click to view addons
Join Date: Dec 2006
Posts: 188
Originally Posted by Morant View Post
-- Wanted to mention that even though they have made it pointless in the game to do so I have found that if someone uses a lower rank of a spell it has a different spellID and no sound is played. It is rare but it has happend.
Each rank of a spell has its own unique ID. If you want to fix this, you can use the spell name instead of the ID. http://www.wowwiki.com/API_COMBAT_LOG_EVENT. The SpellName is the 10th argument, after the spellID. You can then index your table by the name (["SomeSpell"] = [[SoundFilePath]]). The downside of this is that it will only work on English clients (or whatever language SomeSpell is). If you're only using this for personal use you can safely do this, but if you're planning to release it publicly you should either localize it or use spellIDs so it works on all language clients.

As for the enable/disable option, you use a slash command and slash command handler function. The way you can make it work as a toggle is just using a variable declared in the main scope of your addon.

Code:
local Enabled = true

-- Create a function that occurs when we use our slash command
SlashCmdList["MYADDON"] = function(msg)
     -- Check if msg is whatever, then act accordingly.
     -- if msg is "on" then set enabled to true, if msg is "off" set it to false.
end
-- Define the actual slash commands.
SLASH_MYADDON1 = "/myaddon"
SLASH_MYADDON2 = "/ma"
In your OnEvent handler, only proceed if Enabled == true.
  Reply With Quote
07-23-09, 09:13 AM   #10
Morant
A Deviate Faerie Dragon
 
Morant's Avatar
Join Date: Feb 2009
Posts: 18
I have made some progress. Over the last few days I have had to work on this little project I have gotten the toggle function working fine. I have not added the --> if Enabled == true part to the OnEvent handler yet greatly because of the following question. Hate to waste space here as I have other things I would like to ask about and implement, but I can't seem to get past this for some reason.


This works perfectly:

Code:
local frame = CreateFrame("Frame")
frame:RegisterEvent("COMBAT_LOG_EVENT_UNFILTERED")
frame:SetScript("OnEvent", function(self, event, _, eventType, sourceGUID, _, _, _, _, _, spellID)
  if eventType == "SPELL_AURA_APPLIED" and sourceGUID == UnitGUID("target") then
    if targetauras[spellID] then
      PlaySoundFile(targetauras[spellID])
    end
    end
  end)
If I replace it with this:

Code:
local frame = CreateFrame("Frame")
frame:RegisterEvent("COMBAT_LOG_EVENT_UNFILTERED")
frame:SetScript("OnEvent", function(self, event, _, eventType, sourceGUID, _, _, _, _, _, spellID)
  if eventType == "SPELL_AURA_APPLIED" then
    if sourceGUID == UnitGUID("target") and targetauras[spellID] then
        PlaySoundFile(targetauras[spellID])
      end
   elseif sourceGUID == UnitGUID("player") and myauras[spellID] then 
        PlaySoundFile(myauras[spellID])
      end
  end
end)
It does not. The error I get is Global call nil value or something. I have read a ton of useful information while trying to search up what syntax error I have made here but none of it has assisted in correcting it. Either I am missing something in my understanding of how the if-elseif stack works, or I am using the "and" incorrectly, or I need some [{()}] in there somewhere. I am close... but no cigar. Must be the nub in me again... I can make it work with multiple frames for each eventType but as discussed prior I am trying to avoid that habit.

***Small side note. It has been good times trying to figure this out while the Authentication drama is unfolding. ...sigh....

Last edited by Morant : 07-23-09 at 09:49 AM. Reason: typo
  Reply With Quote
07-23-09, 02:29 PM   #11
Waverian
A Chromatic Dragonspawn
AddOn Author - Click to view addons
Join Date: Dec 2006
Posts: 188
I'm not entirely sure what's happening there. I'm guessing it's a weird quirk with how table values work in multiple conditional checks.

Code:
if <something> and targetauras[spellID] then
     -- if there is no index for spellID, it will return nil.
end
but yeah, it's not something I've really investigated. There's no harm in using nested conditional checks though (other than losing a tiny bit of readability at most), so go with what works.
  Reply With Quote
07-23-09, 03:16 PM   #12
Vrul
A Scalebane Royal Guard
 
Vrul's Avatar
AddOn Author - Click to view addons
Join Date: Nov 2007
Posts: 404
Is that code cut-and-paste or from memory? Seems like there are one too many "end" in that second code block.
  Reply With Quote
07-23-09, 05:18 PM   #13
Verissi
Premium Member
 
Verissi's Avatar
AddOn Author - Click to view addons
Join Date: Oct 2006
Posts: 99
Originally Posted by Morant View Post
Code:
local frame = CreateFrame("Frame")
frame:RegisterEvent("COMBAT_LOG_EVENT_UNFILTERED")
frame:SetScript("OnEvent", function(self, event, _, eventType, sourceGUID, _, _, _, _, _, spellID)
  if eventType == "SPELL_AURA_APPLIED" then
    if sourceGUID == UnitGUID("target") and targetauras[spellID] then
        PlaySoundFile(targetauras[spellID])
      end
   elseif sourceGUID == UnitGUID("player") and myauras[spellID] then 
        PlaySoundFile(myauras[spellID])
      end
  end
end)
Originally Posted by Vrul View Post
Is that code cut-and-paste or from memory? Seems like there are one too many "end" in that second code block.
I noticed the same. It looks like your elseif is being triggered when eventType ~= "SPELL_AURA_APPLIED", which is probably causing problems when evaluating those expressions.
__________________
"I can calculate the motions of the heavenly bodies, but not the madness of people." - Sir Isaac Newton
"Half of twice as intimidating as Saurfang is still one whole Saurfang worth of intimidation." - Anticlaus, Gorefiend server
  Reply With Quote
07-24-09, 11:41 AM   #14
Morant
A Deviate Faerie Dragon
 
Morant's Avatar
Join Date: Feb 2009
Posts: 18
Thanks for the input. With your help I got things partially working. The global nil thing was because I left a " , " off of a table entry... *sigh*

I still could not get the nested "If this == that and those == these then" working properly. I am just too nub for that concept yet I think so I simplified it for now by just nesting one if per line. It's not pretty and not gonna leave it that way, but like he said above... it is partially working atm... which is good. The following is from memory, but I just got it working this morning so it is pretty fresh. The double end in the middle solved the elseif in the wrong part of the stack.

Code:
local frame = CreateFrame("Frame")
frame:RegisterEvent("COMBAT_LOG_EVENT_UNFILTERED")
frame:SetScript("OnEvent", function(self, event, _, eventType, sourceGUID, _, _, _, _, _, spellID)
  if eventType == "SPELL_AURA_APPLIED" then
      if sourceGUID == UnitGUID("target") then
          if targetauras[spellID] then
             PlaySoundFile(targetauras[spellID])
           end
       end
 elseif sourceGUID == UnitGUID("player") then
      if myauras[spellID] then 
           PlaySoundFile(myauras[spellID])
      end
 end
end)

The first part where it checks for target auras is functioning with the two events I have listed... PVP Trinket and Spell Reflect. Keeping it simple till it all works.

Little more trouble I am having with the second part. I have two events listed... Dampen Magic (for testing) and Fingers of Frost. Both Auras applied to self but I only get an alert for the Dampen.. not the fingers.

I think it has to do with the fact that I assumed
Code:
sourceGUID == UnitGUID("player")
would do the same thing as the original addon with the editing.
Code:
srcName == UnitName("player")
This known as that "Monkey-See-Monkey-CutPasteEdit method of programming" as stated earlier.

I tried to reason this out for myself and what I came up with is it must have to do with that fact that when I cast the Dampen "player" is the source and the target. With the Fingers though I suspect that "player" is not the value in either sourceGUID or unitGUID when applied. If I am correct that I likely could run into the same issue with auras applied targets if they have the same difference in values.

Soooooo..... because I wanted to feel like I was actually learning something I tried in vain to just add a simple print(unitGUID/sourceGUID) in a correct location and syntax to see what that was when fingers proc'ed... but was not able to get it to do so. Great idea (stolen... I read it somewhere) but failed in application. Feel pretty stupid about it too.. seems easy enough.... *shrug*

Feel like I am rambling... I'll let people smarter than me talk now...
  Reply With Quote
07-24-09, 11:47 PM   #15
Morant
A Deviate Faerie Dragon
 
Morant's Avatar
Join Date: Feb 2009
Posts: 18
Had some time to work on this. No matter how I programmed it I could not get a sound to play using Fingers of Frost spellID in the tables as above. The problem has been that spellID of Fingers of Frost the whole damn time.

I have been taught here that there are 8 base arguments passed with the event and some sub arguments depending on the event. I did some testing with print...

Code:
if spellName == "Fingers of Frost" then
		xxx = sourceGUID
		yyy = sourceName
		ZZZ = spellID
		www = destGUID
		vvv = destName
                uuu = eventType
		print("sourceGUID")
		print(xxx)
		print("sourceName")
		print(yyy)
		print("spellID")
		print(zzz)
		print("destGUID") 
		print(www)
		print("destName")
		print(vvv)
		print("eventType")
		print(uuu)
	end
Another mage was on the target dummies at the same time and his output from my log was the same with the exception of the unique GUIDs and names. This is the output of that:

sourceGUID
0x0100000000601164
sourceName
Morant
spellID
333
destGUID
0x0100000000601164
destName
nil
eventType
SPELL_AURA_APPLIED

Early posts on the subject say that all source information is set to nil on SPELL_AURA_APPLIED (and some others) but there is clearly source information with respect to the GUIDs. I can't find anything that says when but they must have changed things later on. Here is the b1tch though... Wowhead says the spellID's for rank 1 and 2 of Fingers of Frost are 44543 & 44545 respectively. Why is the the client returning a spellID of 333 that does not exist? (also according to wowhead) Got the same spellID of 333 with Missle Barrage and Clearcasting as well. I hung out at the target dummies a while and was able to verify that the shaman version of Clearcasting returned 333 as well.

The only conclusion I can make is that spells you have to physically cast and target with (Dampen Magic, PVP Trinket, Spell Reflect, ect) have the full set arguments passed with the event SPELL_AURA_APPLIED. Those that are of the "proc" in casting type have some values set to nil?

Someone please tell me if they think I am correct and not just a nubbynubbincakes on crack... cause this has been driving me insane thinking *I* was doing something wrong all this time!!! I prolly had it working in several different variations and didn't know it because I tried to call the sound file by the *^&%^$#@!$ spellID EVERYTIME! It was never gonna work...
  Reply With Quote
07-30-09, 08:55 AM   #16
Morant
A Deviate Faerie Dragon
 
Morant's Avatar
Join Date: Feb 2009
Posts: 18
Back again and a bit stumped atm. Can anyone tell me why each event plays twice? Odd behavior and I dunno what I did.. but..

I if I set alerts/shouts:

1/1 = Double
1/0 = Single
0/0 = Single
0/1 = Single
* Text outputs at least. I really can't tell any sound difference they are so close together.

None of this really HURTS anything except that basically makes it to where you can't turn the mod off. Plays one.. plays two.. but it plays. There is something I am missing in my limited understanding of what I am doing and I have been unable to locate any reading material that helps at all. I want to try to add an option to turn on and off the different types of alerts but until this is solved there is no point. My pea brain says that if alerts = 0... silence. *shrug*

Thanks in advance for taking the time to help out all. It has gotten me a long way. I have dubbed my project "Snitch" and have released it as testable with my guild mates. The feedback has been very positive and helps motivate me. This stuff is every BIT as hard as I thought... sigh.

Code:
local frame = CreateFrame("Frame")
frame:RegisterEvent("COMBAT_LOG_EVENT_UNFILTERED")
frame:SetScript("OnEvent", function(self, event, _, eventType, sourceGUID, sourceName, _, destGUID, _, _, spellID, spellName)
if (alerts == 1) then	    
	if eventType == "SPELL_AURA_APPLIED" then
    	if procauras[spellName] and sourceGUID == UnitGUID("player") then
                PlaySoundFile(procauras[spellName])
				if (shouts == 1) then
				   shoutspell = spellName
				   UIErrorsFrame:AddMessage(shoutspell, 1.0, 1.0, 1.0, 1.0, UIERRORS_HOLD_TIME);
				end
		elseif targetauras[spellID] and destGUID == UnitGUID("target") then
	           PlaySoundFile(targetauras[spellID])
		    	if (shouts == 1) then
				   shoutspell = spellName
				   UIErrorsFrame:AddMessage(shoutspell, 1.0, 1.0, 1.0, 1.0, UIERRORS_HOLD_TIME);
				end		
	    else
	    end
	elseif eventType == "SPELL_CAST_SUCCESS" then
		if sourceGUID == UnitGUID("target") and targetinstants[spellID] then
				PlaySoundFile(targetinstants[spellID])
			    if (shouts == 1) then
				   shoutspell = spellName
				   UIErrorsFrame:AddMessage(shoutspell, 1.0, 1.0, 1.0, 1.0, UIERRORS_HOLD_TIME);
				end
		end
	elseif eventType == "SPELL_CAST_START" then
		if sourceGUID == UnitGUID("target") and targetspells[spellID] then 
				PlaySoundFile(targetspells[spellID])
			    if (shouts == 1) then
			      shoutspell = spellName
			      UIErrorsFrame:AddMessage(shoutspell, 1.0, 1.0, 1.0, 1.0, UIERRORS_HOLD_TIME);
			    end
		end
	else
	end  
    
end
shoutspell = 0
end)
  Reply With Quote
07-30-09, 10:11 AM   #17
Xrystal
nUI Maintainer
 
Xrystal's Avatar
Premium Member
AddOn Author - Click to view addons
Join Date: Feb 2006
Posts: 5,934
I would personally make it output a slightly different message for each sound being played so you can find out which ones are making the sound happen and then you will know which ones you weren't expecting to happen.

When I first start out with combat log parsing for my addons I set up a debug message containing the event information and its key parameters but only for the unit I am watching for, be it player or target. Once I know what information is being grabbed I then know what processes I need to set up to use that information.
__________________


Characters:
Gwynedda - 70 - Demon Warlock
Galaviel - 65 - Resto Druid
Gamaliel - 61 - Disc Priest
Gwynytha - 60 - Survival Hunter
Lienae - 60 - Resto Shaman
Plus several others below level 60

Info Panel IDs : http://www.wowinterface.com/forums/s...818#post136818
  Reply With Quote
07-30-09, 11:09 PM   #18
Morant
A Deviate Faerie Dragon
 
Morant's Avatar
Join Date: Feb 2009
Posts: 18
Ok.. so this is what I attempted. Sorry if this is seen as just the ranting babble of a newb... but.. well.. it is.

I added a print command for all arguments.

Keep in mind that both of these entries were produced with one trigger of the pvp trinket:


self = table: 11D22C98
event = COMBAT_LOG_EVENT_UNFILTERED
TimeStamp = 1249007265.959
eventType = SPELL_AURA_APPLIED
sourceGUID = 0x010000000060114
sourceName = Morant
sourceFlags = 66833
destGUID = 0x010000000060114
destName = Morant
destFlags = nil
spellID = 42292
spellName = PvP Trinket

table: 11D22EF0
COMBAT_LOG_EVENT_UNFILTERED
1249007265.959
SPELL_AURA_APPLIED
0x010000000060114
Morant
66833
0x010000000060114
Morant
(nil)
42292
PvP Trinket

The only differences is the (self).

If I wait the 2 mins cd and do it again I get the exact same output except obviously the timestamps differ.

If I set alerts = 0 in the above code block I get only this output..

table: 11D22C98
COMBAT_LOG_EVENT_UNFILTERED
1249008499.463
SPELL_AURA_APPLIED
0x010000000060114
Morant
66833
0x010000000060114
Morant
(nil)
42292
PvP Trinket

The first table number is used and the second is ignored. Odd how the code ignores the "if (alerts == 1) then" for the first table:# but enforces it for the second.

I would also like to note:
The table: #'s did not change if it was a different target triggering a different sound under any of the [tables] I made for the sounds. Once loaded the table: #'s do not change unless /reloadui. I did a few BG's and added the print list to all output sounds of the BG. All of them had the same two table: #'s irrelevant to who was targeted or which sound was triggered.
_________________
EDIT:
I did 6 /reloadui in a row this morning and recorded the tables in order. Prolly useless information but I thought it worth it to mention.
11734110 - 11734368
114DFA08 - 114DFC60
11376400 - 11376658
11940DF0 - 11941048
11AE1120 - 11AE1378
1A665DC8 - 1A666020
__________________

I have searched for something to explain to me what both (self) and its output "table: #######" means specifically but not had much luck. In trying to reason things out for myself I first come to the notion it is the IDnumber assingned to the "Frame" upon its creation. I am fairly sure that is not quite right as I only created a single frame. The other thing I thought maybe it's a temporary ID # the event(s) my addon is listening too? Does the event fire twice simultaneously (timestamp) as a redundancy check perhaps? The more I think about it.. the better my beer tastes. /cheers

With or without an understanding of this I surmise that a way to fix this is something that compares the two events and supress/kills/ignores the first one and allows the user to disable the mod with the toggle on the second? How do I tell them apart when they occur? The only difference is the table: #'s...so which one is primary or whatever? Might code something that disables BOTH table: #'s when (alerts = 0)? Even this would only work after some event has fired and I was able to store the table: #'s for the current session.

I really am frustrated and fearing that I am looking straight at Waldo and still can't see the damn thing... Have I learned anything here?

Last edited by Morant : 07-31-09 at 08:15 AM.
  Reply With Quote
08-01-09, 11:31 AM   #19
Morant
A Deviate Faerie Dragon
 
Morant's Avatar
Join Date: Feb 2009
Posts: 18
Cargor said:
__________________
So, could you please post your whole code? Including the aura-tables, including the debug-output and other function your addon maybe has?

P.S.: Just a thought - is your 'alerts'-variable local? Because if it's not, maybe something else modifies it ...
__________________
Per your request the debug version of Snitch I used. I have not completed the "HealerMode" part of it yet until this issue is resolved.

NOTE: Posting the entire thing like this for the first time feels a lot like PE coach's jock inspection back in middle school.

Code:
function SnitchStart()
	if( DEFAULT_CHAT_FRAME ) then
		DEFAULT_CHAT_FRAME:AddMessage("Snitch Loaded.");
	end
	UIErrorsFrame:AddMessage("Snitch Loaded.", 1.0, 1.0, 1.0, 1.0, UIERRORS_HOLD_TIME);
end


local alerts = 1
local shouts = 1
local shoutspell = 0
local mode = 0
local onmsg = "|cff33ff99 Snitch is: |cffffff78 ON"
local offmsg = "|cff33ff99 Snitch is: |cffffff78 OFF"
local shoutsonmsg = "|cff33ff99 Snitch Shouts are: |cffffff78 ON"
local shoutsoffmsg = "|cff33ff99 Snitch Shouts are: |cffffff78 OFF"
local healermodeonmsg = "|cff33ff99 Snitch Healer Mode: |cffffff78 ON"
local healermodeoffmsg = "|cff33ff99 Snitch Healer Mode: |cffffff78 OFF"
-- Mode 0 = Default  1 = Healer
SLASH_SNITCH1 = "/snitch"
SlashCmdList["SNITCH"] = function(msg)
    if (msg=="on") then
       alerts = 1
       DEFAULT_CHAT_FRAME:AddMessage(onmsg)
	   print(alerts)
    elseif (msg=="off") then
       alerts = 0
       DEFAULT_CHAT_FRAME:AddMessage(offmsg)
	   print(alerts)
    elseif (msg=="shouts") then
		if (shouts == 0) then
		 shouts = 1
		 DEFAULT_CHAT_FRAME:AddMessage(shoutsonmsg)
		elseif (shouts == 1) then 
		 shouts = 0
		 DEFAULT_CHAT_FRAME:AddMessage(shoutsoffmsg)
		else
		end
	elseif (msg=="healer") then
	    if (mode == 0) then
	     mode = 1
	     DEFAULT_CHAT_FRAME:AddMessage(healermodeonmsg)
	    elseif (mode == 1) then 
          mode = 0
  		  DEFAULT_CHAT_FRAME:AddMessage(healermodeoffmsg)
		else
		end
	else  
      DEFAULT_CHAT_FRAME:AddMessage("|cff33ff99 USAGE: /snitch on/off  /snitch shouts  /snitch healer")
        if (alerts == 1) then
          DEFAULT_CHAT_FRAME:AddMessage(onmsg)
        end
        if (alerts == 0) then  
          DEFAULT_CHAT_FRAME:AddMessage(offmsg)
        end
		if (shouts == 1) then
		  DEFAULT_CHAT_FRAME:AddMessage(shoutsonmsg)
		end
		if (shouts == 0) then
		  DEFAULT_CHAT_FRAME:AddMessage(shoutsoffmsg)
		end
		if (mode == 0) then
		  DEFAULT_CHAT_FRAME:AddMessage(healermodeoffmsg)
		end
		if (mode == 1) then 
		  DEFAULT_CHAT_FRAME:AddMessage(healermodeonmsg)
		end
	end	
end

 local targetauras = {
  [42292] = [[Interface\AddOns\Snitch\sounds\pvptrinket.mp3]],
  [23920] = [[Interface\AddOns\Snitch\sounds\spellreflect.mp3]],
  [46924] = [[Interface\AddOns\Snitch\sounds\bladestorm.mp3]],
}

local focusauras = {
  [42292] = [[Interface\AddOns\Snitch\sounds\focuspvptrinket.mp3]],
}

local targetspells = {
  [6215] = [[Interface\AddOns\Snitch\sounds\fear.mp3]],
  [60043] = [[Interface\AddOns\Snitch\sounds\lavaburst.mp3]],
}

local targetinstants = {
  [8177] = [[Interface\AddOns\Snitch\sounds\groundingtotem.mp3]],
}

local focusspells = {
  [6215] = [[Interface\AddOns\Snitch\sounds\fear.mp3]],
}
local procauras = {
  ["Fingers of Frost"] = [[Interface\AddOns\Snitch\sounds\fingersoffrost.mp3]],
      ["Clearcasting"] = [[Interface\AddOns\Snitch\sounds\clearcasting.mp3]],
   ["Missile Barrage"] = [[Interface\AddOns\Snitch\sounds\missilebarrage.mp3]],
	     ["Fireball!"] = [[Interface\AddOns\Snitch\sounds\fireball.mp3]],
}

local frame = CreateFrame("Frame")
frame:RegisterEvent("COMBAT_LOG_EVENT_UNFILTERED")
frame:SetScript("OnEvent", function(self, event, TimeStamp, eventType, sourceGUID, sourceName, sourceFlags, destGUID, destName, destFlags, spellID, spellName)
if (alerts == 1) then	    
	if eventType == "SPELL_AURA_APPLIED" then
    	if procauras[spellName] and sourceGUID == UnitGUID("player") then
				print(self)
				print(event)
				print(TimeStamp)
				print(eventType)
				print(sourceGUID)
				print(sourceName)
				print(sourceFlags)
				print(destGUID)
				print(destName)
				print(destFlags)
				print(spellID)
				print(spellName)
			PlaySoundFile(procauras[spellName])
				if (shouts == 1) then
				   shoutspell = spellName
				   UIErrorsFrame:AddMessage(shoutspell, 1.0, 1.0, 1.0, 1.0, UIERRORS_HOLD_TIME);
				   print("ProcAura")
				end
		elseif targetauras[spellID] and destGUID == UnitGUID("target") then
				print(self)
				print(event)
				print(TimeStamp)
				print(eventType)
				print(sourceGUID)
				print(sourceName)
				print(sourceFlags)
				print(destGUID)
				print(destName)
				print(destFlags)
				print(spellID)
				print(spellName) 
	        PlaySoundFile(targetauras[spellID])
		    	if (shouts == 1) then
				   shoutspell = spellName
				   UIErrorsFrame:AddMessage(shoutspell, 1.0, 1.0, 1.0, 1.0, UIERRORS_HOLD_TIME);
				   print("TargetAura")
				end		
	    else
	    end
	elseif eventType == "SPELL_CAST_SUCCESS" then
		if sourceGUID == UnitGUID("target") and targetinstants[spellID] then
                print(self)
				print(event)
				print(TimeStamp)
				print(eventType)
				print(sourceGUID)
				print(sourceName)
				print(sourceFlags)
				print(destGUID)
				print(destName)
				print(destFlags)
				print(spellID)
				print(spellName)
			PlaySoundFile(targetinstants[spellID])
			    if (shouts == 1) then
				   shoutspell = spellName
				   UIErrorsFrame:AddMessage(shoutspell, 1.0, 1.0, 1.0, 1.0, UIERRORS_HOLD_TIME);
				   print("TargetInstant")
				end
		end
	elseif eventType == "SPELL_CAST_START" then
		if sourceGUID == UnitGUID("target") and targetspells[spellID] then 
				print(self)
				print(event)
				print(TimeStamp)
				print(eventType)
				print(sourceGUID)
				print(sourceName)
				print(sourceFlags)
				print(destGUID)
				print(destName)
				print(destFlags)
				print(spellID)
				print(spellName)
			  PlaySoundFile(targetspells[spellID])
			    if (shouts == 1) then
			      shoutspell = spellName
			      UIErrorsFrame:AddMessage(shoutspell, 1.0, 1.0, 1.0, 1.0, UIERRORS_HOLD_TIME);
				  print("TargetSpells")
			    end
		end
	else
	end  
    
end
shoutspell = 0
end)
In case it makes a difference the .xml file...which is an edited down version of the orginal addon I started with. Since I have done little reading about how to change it, you still have to load it as an out of date addon.

Code:
<Ui xmlns="http://www.blizzard.com/wow/ui/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.blizzard.com/wow/ui/">

	<Script file="snitch.lua"/>

	<Frame name="Main" toplevel="true" hidden="false" parent="UIParent">
		<Size>
			<AbsDimension x="1" y="1"/>
		</Size>
		<Anchors>
			<Anchor point="CENTER"/>
		</Anchors>
		<Layers>
			<Layer level="BACKGROUND">
				<Texture name="$parentBackground" setAllPoints="true">
					<Color r="0" g="0" b="0" a="0"/>
				</Texture>
			</Layer>
		</Layers>
		<Scripts>
			<OnLoad>
				SnitchStart();
			</OnLoad>
			<OnUpdate>
					
			</OnUpdate>
			<OnEvent>
			</OnEvent>
		</Scripts>
	</Frame>

</Ui>
That's it... Minus the debug stuff... add the toc and my (sorta) custom sound bits and you have Snitch v1.11b pretty much as released to my guild for testing. There are alot more table entries for each kind of alert in the release version. I keep em slim for testing. I have a long list of improvements and what not I want to add to this but kinda stuck here till I know what the heck I did THIS time..
  Reply With Quote
08-02-09, 07:20 AM   #20
xConStruct
A Chromatic Dragonspawn
 
xConStruct's Avatar
AddOn Author - Click to view addons
Join Date: May 2008
Posts: 199
Ha! This was a hard one, but I think I've found your problem.

This is just a guess, but you did add both of your files (the lua and the xml) in your .toc-file, right?

This causes the .lua-file to load twice, because you've got "<Script file="snitch.lua"/>" in your xml-file. So, WoW loads the lua-file based on the .toc and the .xml. Just remove one of them and it should work.

And because I like to think about what happened and explain it to others, you can hear the whole story what likely happened:
Two loads result in two event-frames which have both their own local "alerts"-variable. You've toggled the alerts-variable via slash-command and so only one alerts-variable will be set while the other in the background still has the default value.
So you get two outputs if both are enabled - and one if you disabled the other one.

-----------
By the way, you can remove the complete xml-file and add at the bottom of your lua-file this small function-call "SnitchStart()" - because this is the online thing your xml basically does. I would prefer doing it this way, because it should fix your bug alongside.
__________________
« Website | GitHub »

Oh hai!
  Reply With Quote

WoWInterface » Developer Discussions » Lua/XML Help » Code Help - Event Sounds


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