Thread Tools Display Modes
03-06-17, 01:18 PM   #1
Ketho
A Pyroguard Emberseer
 
Ketho's Avatar
AddOn Author - Click to view addons
Join Date: Mar 2010
Posts: 1,026
Nameplate tracking SPELL_CAST_START destination unit

Recently there was this question which I was wondering for years.

The SPELL_CAST_START CLEU event doesn't include a destination.
http://wow.gamepedia.com/COMBAT_LOG_EVENT

But now with the Legion nameplate unit ids we can kind of still get that information.

With the added footnote that it won't work for most cases, like
  • mouseover casting
  • @unit macros
  • AoE casts
  • most boss abilities
  • when the nameplates are toggled off

Rough example where unit(s) are casting a spell and they are "targeting" the player; by comparing nameplateNtarget
Lua Code:
  1. local np = C_NamePlate
  2. local f = CreateFrame("Frame")
  3.  
  4. function f:OnEvent(event, timestamp, subevent, _, sourceGUID, sourceName, _, _, destGUID, destName, _, _, spellID, spellName)
  5.     if subevent == "SPELL_CAST_START" then
  6.         for _, v in pairs(np.GetNamePlates()) do
  7.             if np.GetNamePlateForUnit(v.namePlateUnitToken.."target") == np.GetNamePlateForUnit("player") then
  8.                 print(format("%s is casting %s on me", sourceName, GetSpellLink(spellID)))
  9.             end
  10.         end
  11.     end
  12. end
  13.  
  14. f:RegisterEvent("COMBAT_LOG_EVENT_UNFILTERED")
  15. f:SetScript("OnEvent", f.OnEvent)

So I'm wondering if this is already sort of implemented in WeakAuras or Boss Mods? It would probably be handy if not.


Last edited by Ketho : 03-06-17 at 01:25 PM.
  Reply With Quote
03-06-17, 01:26 PM   #2
Banknorris
A Chromatic Dragonspawn
 
Banknorris's Avatar
AddOn Author - Click to view addons
Join Date: Oct 2014
Posts: 153
Err, wouldn't your code print all name plates who have you as a target and not only the one casting on you?
__________________
"In this world nothing can be said to be certain, except that fractional reserve banking is a Ponzi scheme and that you won't believe it." - Mandrill
  Reply With Quote
03-06-17, 01:28 PM   #3
Ketho
A Pyroguard Emberseer
 
Ketho's Avatar
AddOn Author - Click to view addons
Join Date: Mar 2010
Posts: 1,026
Originally Posted by Banknorris View Post
Err, wouldn't your code print all name plates who have you as a target and not only the one casting on you?

Yeah it would, nice find! It's a barebones proof of concept example I took from that other post
  Reply With Quote
03-06-17, 04:31 PM   #4
SDPhantom
A Pyroguard Emberseer
 
SDPhantom's Avatar
AddOn Author - Click to view addons
Join Date: Jul 2006
Posts: 2,323
Something like this would work much better without involving the NamePlate API. NamePlates may still need to be enabled, but it'll run more efficient and allows you to add more units. You also can prioritize which groups of units are checked first by rearranging their order.

Lua Code:
  1. local UnitTable={};
  2. for i=1,4 do table.insert(UnitTable,"boss"..i); end
  3. for i=1,5 do table.insert(UnitTable,"arena"..i); end
  4. for i=1,30 do table.insert(UnitTable,"nameplate"..i); end
  5.  
  6. local EventFrame=CreateFrame("Frame");
  7. EventFrame:RegisterEvent("COMBAT_LOG_EVENT_UNFILTERED");
  8. EventFrame:SetScript("OnEvent",function(self,event,_,subevent,_,guid,name,_,_,_,_,_,_,spellid)
  9.     if subevent=="SPELL_CAST_START" then
  10.         for _,unit in ipairs(UnitTable) do
  11.             if UnitIsUnit(unit.."target","player") then
  12.                 if UnitIsEnemy("player",unit) then
  13.                     print(("%s is casting %s on me"):format(name,GetSpellLink(spellid)));
  14.                 end
  15.                 break;
  16.             end
  17.         end
  18.     end
  19. end
__________________
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 : 03-06-17 at 04:43 PM.
  Reply With Quote
03-06-17, 05:25 PM   #5
Lombra
A Molten Giant
 
Lombra's Avatar
AddOn Author - Click to view addons
Join Date: Nov 2006
Posts: 554
Well, you've still got exactly the same limitations as before, only more likely that a uni token for the target exists. I certainly wouldn't rely on it for any sort of PvP purposes. Could probably work as a complement for boss mods, since they already use every conceivable channel for gathering of information, but on its own, much too unreliable.
__________________
Grab your sword and fight the Horde!
  Reply With Quote
03-07-17, 08:52 AM   #6
SDPhantom
A Pyroguard Emberseer
 
SDPhantom's Avatar
AddOn Author - Click to view addons
Join Date: Jul 2006
Posts: 2,323
I didn't exactly say that it fixes the caveats of macros and mouseovers. It's just a more direct way of detection than previously posted. This is done by building a list of UnitIDs and using UnitIsUnit() to check if they're targeting us rather than loading up a list of NamePlates, grabbing their NamePlate UnitID, attempting to grab the NamePlate for their target, and check if it's the same as the NamePlate grabbed for the player.

Though in PvP, it would be more useful to skip the target filtering and trigger a warning if an enemy starts casting a CC ability. No matter who it's cast on, it's the same result. You or a team member is out of the fight for a few seconds.
__________________
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 : 03-07-17 at 09:07 AM.
  Reply With Quote
03-07-17, 09:55 AM   #7
Ketho
A Pyroguard Emberseer
 
Ketho's Avatar
AddOn Author - Click to view addons
Join Date: Mar 2010
Posts: 1,026
UnitIsUnit seems indeed more efficient, I'd personally do it like this. I don't really understand when a nameplate id is active or not :s
Lua Code:
  1. for _, v in pairs(C_NamePlate.GetNamePlates()) do
  2.     if UnitIsUnit(v.namePlateUnitToken.."target", "player") then
  3.         -- do stuff
  4.     end
  5. end

Originally Posted by SDPhantom View Post
Though in PvP, it would be more useful to skip the target filtering and trigger a warning if an enemy starts casting a CC ability. No matter who it's cast on, it's the same result. You or a team member is out of the fight for a few seconds.
Yeah, it's mostly not really important who it's casting on in PvP/Arena, while it's prone to error to get the actual destination unit.
Just that it could be useful in some cases.

Last edited by Ketho : 03-07-17 at 10:04 AM.
  Reply With Quote
03-07-17, 11:50 AM   #8
SDPhantom
A Pyroguard Emberseer
 
SDPhantom's Avatar
AddOn Author - Click to view addons
Join Date: Jul 2006
Posts: 2,323
If the NamePlate UnitID isn't active, the unit simply doesn't exist and would return false anyway. Though UnitIsUnit() may have problems if both units don't exist, this issue won't happen since player always points to yourself.

PS: You should still filter enemy casts unless you want to be spammed about party members healing you. Also consider breaking the loop when you find a match or ideally check the unit's casting info to see if they're casting the same spell. Otherwise it'll spam you with every unit targeting you, even those not casting.
__________________
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 : 03-07-17 at 12:02 PM.
  Reply With Quote
03-07-17, 01:19 PM   #9
Ketho
A Pyroguard Emberseer
 
Ketho's Avatar
AddOn Author - Click to view addons
Join Date: Mar 2010
Posts: 1,026
Originally Posted by SDPhantom View Post
PS: You should still filter enemy casts unless you want to be spammed about party members healing you. Also consider breaking the loop when you find a match or ideally check the unit's casting info to see if they're casting the same spell. Otherwise it'll spam you with every unit targeting you, even those not casting.

Yes, this is how I would filter it
Lua Code:
  1. local f = CreateFrame("Frame")
  2.  
  3. function f:OnEvent(event, timestamp, subevent, _, sourceGUID, sourceName, sourceFlags, _, destGUID, destName, _, _, spellID, spellName)
  4.     if subevent == "SPELL_CAST_START" then
  5.         for _, v in pairs(C_NamePlate.GetNamePlates()) do
  6.             if UnitGUID(v.namePlateUnitToken) == sourceGUID then
  7.                 -- source is targeting player, source is hostile
  8.                 if UnitIsUnit(v.namePlateUnitToken.."target", "player") and bit.band(sourceFlags, COMBATLOG_OBJECT_REACTION_HOSTILE) > 0 then
  9.                     print( format("%s is casting %s on me", sourceName, GetSpellLink(spellID)) )
  10.                 end
  11.                 break
  12.             end
  13.         end
  14.     end
  15. end
  16.  
  17. f:RegisterEvent("COMBAT_LOG_EVENT_UNFILTERED")
  18. f:SetScript("OnEvent", f.OnEvent)

Originally Posted by SDPhantom View Post
Something like this would work much better without involving the NamePlate API. NamePlates may still need to be enabled, but it'll run more efficient and allows you to add more units. You also can prioritize which groups of units are checked first by rearranging their order.

That works too, but I'm not sure how much more efficient it is
Since nameplates get recycled/removed you have to check all 40 nameplate unit ids, instead of getting the actives ones through the NamePlate API
Code:
/run for i = 1, 40 do print("nameplate"..i, UnitName("nameplate"..i)) end
Lua Code:
  1. "nameplate1", "Blamedatank", "Outland"
  2. "nameplate2", "Pluex", "Outland"
  3. "nameplate3", "Thomas Miller", nil
  4. "nameplate4", "Maawi", "Outland"
  5. "nameplate5", "Stormwind City Guard", nil
  6. "nameplate6", "Edna Mullby", nil
  7. "nameplate7", nil, nil
  8. "nameplate8", "Dankzter", "Outland"
  9. "nameplate9", nil, nil
  10. "nameplate10", nil, nil
  11. "nameplate11", "Renato Gallina", nil
  12. "nameplate12", nil, nil
  13. "nameplate13", "Stephanie Turner", nil
  14. "nameplate14", nil, nil
  15. "nameplate15", "Stormwind City Patroller", nil
  16. "nameplate16", nil, nil
  17. "nameplate17", nil, nil
  18. "nameplate18", "Blackfirez", "DunModr"
  19. "nameplate19", nil, nil
  20. "nameplate20", "Stormwind City Guard", nil
  21. "nameplate21", "\195\146\195\164k", "Outland"
  22. "nameplate22", nil, nil
  23. "nameplate23", "Thurman Mullby", nil
  24. "nameplate24", nil, nil
  25. "nameplate25", "Varcher", "Stormscale"
  26. "nameplate26", nil, nil
  27. "nameplate27", nil, nil
  28. "nameplate28", nil, nil
  29. "nameplate29", "Bay\195\184n\195\170ta", "Outland"
  30. "nameplate30", nil, nil
  31. "nameplate31", nil, nil
  32. "nameplate32", nil, nil
  33. "nameplate33", nil, nil
  34. "nameplate34", nil, nil
  35. "nameplate35", nil, nil
  36. "nameplate36", "Elling Trias", nil
  37. "nameplate37", nil, nil
  38. "nameplate38", "Jusraalin", "Outland"
  39. "nameplate39", nil, nil
  40. "nameplate40", "Golden King", nil

Last edited by Ketho : 03-07-17 at 06:11 PM.
  Reply With Quote
03-07-17, 07:38 PM   #10
Banknorris
A Chromatic Dragonspawn
 
Banknorris's Avatar
AddOn Author - Click to view addons
Join Date: Oct 2014
Posts: 153
Just another aproach, not using any loops.
Lua Code:
  1. local f = CreateFrame("Frame")
  2. local unit_from_plate = {}
  3. local plate_from_unit = {}
  4.  
  5. f:SetScript("OnEvent",function(self,event,...)
  6.     if event=="NAME_PLATE_UNIT_ADDED" then
  7.         local plate = "NamePlate"..string.match(...,"%a+(%d+)")
  8.         local unit = GetUnitName(plate,true)
  9.         if unit then
  10.             plate_from_unit[unit] = plate
  11.             unit_from_plate[plate] = unit
  12.         end
  13.     elseif event=="NAME_PLATE_UNIT_REMOVED" then
  14.         local plate = ...
  15.         if plate then
  16.             local unit = unit_from_plate[plate]
  17.             if unit then
  18.                 plate_from_unit[unit] = nil
  19.             end
  20.             unit_from_plate[plate] = nil
  21.         end
  22.     elseif event=="COMBAT_LOG_EVENT_UNFILTERED" then
  23.         local timestamp,sub_event,_,_,source_name,_,_,_,_,_,_,spell_id = ...
  24.         if sub_event=="SPELL_CAST_START" then
  25.             local plate = plate_from_unit[source_name]
  26.             if plate and UnitIsEnemy("player",plate) and UnitIsUnit(plate.."target","player") then
  27.                 print(("%s is casting %s on me"):format(sourceName,GetSpellLink(spell_id)))
  28.             end
  29.         end    
  30.     end
  31. end)
  32. f:RegisterEvent("NAME_PLATE_UNIT_ADDED")
  33. f:RegisterEvent("NAME_PLATE_UNIT_REMOVED")
  34. f:RegisterEvent("COMBAT_LOG_EVENT_UNFILTERED")
__________________
"In this world nothing can be said to be certain, except that fractional reserve banking is a Ponzi scheme and that you won't believe it." - Mandrill
  Reply With Quote
03-07-17, 11:05 PM   #11
Ketho
A Pyroguard Emberseer
 
Ketho's Avatar
AddOn Author - Click to view addons
Join Date: Mar 2010
Posts: 1,026
Originally Posted by Banknorris View Post
Just another aproach, not using any loops.

That approach is even better, I'll steal that
It looks like UnitGUID still returns valid information when nameplates get removed
Lua Code:
  1. local guids = {}
  2. local f = CreateFrame("Frame")
  3.  
  4. function f:NAME_PLATE_UNIT_ADDED(unit)
  5.     guids[UnitGUID(unit)] = unit
  6. end
  7.  
  8. function f:NAME_PLATE_UNIT_REMOVED(unit)
  9.     guids[UnitGUID(unit)] = nil
  10. end
  11.  
  12. function f:COMBAT_LOG_EVENT_UNFILTERED(timestamp, subevent, _, sourceGUID, sourceName, sourceFlags, _, destGUID, destName, _, _, spellID, spellName)
  13.     if subevent == "SPELL_CAST_START" then
  14.         local unit = guids[sourceGUID]
  15.         if unit and UnitIsUnit(unit.."target", "player") and bit.band(sourceFlags, COMBATLOG_OBJECT_REACTION_HOSTILE) > 0 then
  16.             print( format("%s is casting %s on me", sourceName, GetSpellLink(spellID)) )
  17.         end
  18.     end
  19. end
  20.  
  21. f:SetScript("OnEvent", function(self, event, ...)
  22.     f[event](self, ...)
  23. end)
  24.  
  25. f:RegisterEvent("NAME_PLATE_UNIT_ADDED")
  26. f:RegisterEvent("NAME_PLATE_UNIT_REMOVED")
  27. f:RegisterEvent("COMBAT_LOG_EVENT_UNFILTERED")
  Reply With Quote

WoWInterface » Developer Discussions » General Authoring Discussion » Nameplate tracking SPELL_CAST_START destination unit


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