Reply
Thread Tools Display Modes
Unread 10-18-13, 08:31 AM   #1
Rainrider
A Rage Talon Dragon Guard
Join Date: Nov 2008
Posts: 315
Encounter Detection

I'm currently writing an addon to report encounter progress to whispers during a boss fight. It would be cool to be able to return Encounter Journal links to the whisperers. My current NYI strat for this is
  1. EJ_GetCurrentInstance() - for instanceID
  2. EJ_GetEncounterInfoByIndex(index, instanceID) - for encounter name, id and link
  3. EJ_GetCreatureInfo(index, encounterID) - for creature name

I'd then compare the list of creatures to the names of the currently visible boss frames by name to get the current encounter in progress.

My questions about this:
  1. Is there an easier way of get the current encounter in progress?
  2. Is there a way to construct a creature link?

Another problem is a boss like Jandice Barov. She disappears at 66% and 33% health, so does her boss frame. I check for a boss presence at INSTANCE_ENCOUNTER_ENGAGE_UNIT and issue a whisper reply if there is a boss frame present. Any idea on how to handle this case? Currently I'm settled on just returning a EJ link to the current encounter, which I could cache at the start of the fight, but it would be nice to be able to return the current health of the boss too.

Here my current WIP (EJ functionality NYI). Code is based on SimpleBossWhisperer by Rabbit:
lua Code:
  1. local addon = ...
  2. local prefix = "<RBW>: "
  3. local dndMsg = prefix .. "I'm busy fighting %s."
  4. local combatEndedMsg = prefix .. "Combat ended."
  5. local bossFormat = "%s (%d%%)" -- name (health%)
  6.  
  7. local playerName = UnitName("player")
  8. local disableChatFilter = true
  9.  
  10. local numBosses = 0
  11. local whisperers = {}
  12.  
  13. local frame = CreateFrame("Frame")
  14. frame:SetScript("OnEvent", function(self, event, msg, ...) self[event](self, msg, ...) end)
  15. frame:RegisterEvent("INSTANCE_ENCOUNTER_ENGAGE_UNIT")
  16. frame:RegisterEvent("PLAYER_REGEN_ENABLED")
  17. frame:RegisterEvent("CHAT_MSG_WHISPER")
  18. frame:RegisterEvent("CHAT_MSG_BN_WHISPER")
  19. frame:RegisterEvent("ADDON_LOADED")
  20.  
  21. local function GetReply(sender, msg, presenceID, client)
  22.     if (not client or client == "WoW") and (type(sender) ~= "string" or playerName == sender or UnitInRaid(sender) or UnitInParty(sender)) then return end
  23.  
  24.     if not whisperers[presenceID or sender] or msg == "status" then
  25.         whisperers[presenceID or sender] = true
  26.         local str = ""
  27.         for i = 1, MAX_BOSS_FRAMES do
  28.             local unit = "boss" .. i
  29.             if UnitExists(unit) then
  30.                 str = str .. string.format(bossFormat, UnitName(unit), math.floor(UnitHealth(unit) / UnitHealthMax(unit) * 100 + 0.5))
  31.             end
  32.         end
  33.         -- TODO: message length should not be > 255 characters (utf8 aware)
  34.         --       SendChatMessage truncates to 255 chars, BNSendWhisper fails silently
  35.         --       use strlenutf8()
  36.         return string.format(dndMsg, str)
  37.     end
  38. end
  39.  
  40. -- XXX: fires when a boss frame toggles visibility
  41. --      does not fire after reloadui
  42. function frame:INSTANCE_ENCOUNTER_ENGAGE_UNIT()
  43.     numBosses = 0
  44.     for i = 1, MAX_BOSS_FRAMES do
  45.         -- Shado-Pan Garrison daily quests display your companion as a boss
  46.         -- TODO: use UnitClassification instead of UnitIsFriend because there are friendly bosses as well
  47.         --       I don't need UnitExists as UnitClassification returns "normal" for non-present units
  48.         if UnitExists("boss" .. i) and not UnitIsFriend("boss" .. i, "player") then
  49.             numBosses = numBosses + 1
  50.         end
  51.     end
  52.     print(prefix, numBosses)
  53. end
  54.  
  55. function frame:PLAYER_REGEN_ENABLED()
  56.     for player in pairs(whisperers) do
  57.         local presenceID = tonumber(player)
  58.         if presenceID then
  59.             BNSendWhisper(presenceID, combatEndedMsg)
  60.         else
  61.             SendChatMessage(combatEndedMsg, "WHISPER", nil, player)
  62.         end
  63.     end
  64.     numBosses = 0
  65.     wipe(whisperers)
  66. end
  67.  
  68. function frame:CHAT_MSG_WHISPER(msg, sender, _, _, _, flag)
  69.     if flag == "GM" or numBosses == 0 then return end
  70.  
  71.     local reply = GetReply(sender, msg)
  72.     if reply then
  73.         SendChatMessage(reply, "WHISPER", nil, sender)
  74.     end
  75. end
  76.  
  77. function frame:CHAT_MSG_BN_WHISPER(msg, sender, _, _, _, _, _, _, _, _, _, _, presenceID)
  78.     if numBosses == 0 then return end
  79.  
  80.     local _, _, _, _, toonName, _, client = BNGetFriendInfoByID(presenceID) -- client: WoW, D3
  81.     local reply = GetReply(toonName, msg, presenceID, client)
  82.     if reply then
  83.         BNSendWhisper(presenceID, reply)
  84.     end
  85. end
  86.  
  87. function frame:ADDON_LOADED(name)
  88.     if name ~= addon then return end
  89.  
  90.     self:UnregisterEvent("ADDON_LOADED")
  91.  
  92.     if not disableChatFilter then
  93.         ChatFrame_AddMessageEventFilter("CHAT_MSG_WHISPER_INFORM", function(self, event, msg)
  94.             if string.find(msg, "^" .. prefix) then return true end
  95.         end)
  96.         ChatFrame_AddMessageEventFilter("CHAT_MSG_BN_WHISPER_INFORM", function(self, event, msg)
  97.             if string.find(msg, "^" .. prefix) then return true end
  98.         end)
  99.     end
  100. end
Rainrider is offline   Reply With Quote
Unread 10-19-13, 10:12 AM   #2
Rainrider
A Rage Talon Dragon Guard
Join Date: Nov 2008
Posts: 315
This is what I have so far: https://github.com/Rainrider/rainBossWhisperer
I still have to work some minors out yet and test this thoroughly, but I think the concept is ok and working. Would be very glad if you guys could review the code, as I'm rather an inexperienced programmer. Could it be done better? Do you spot any flaws? Thank you for your feedback in advance!
Rainrider is offline   Reply With Quote
Unread 10-25-13, 01:03 AM   #3
exemplar
A Defias Bandit
Join Date: Oct 2013
Posts: 2
I was working on something like this the other day and thought of a neat solution for any instance in the dungeon journal,
Code:
local function distance2d_squared(x1, y1, x2, y2)
        return abs(x2 - x1)^2 + abs(y2 - y1)^2
end

local function GetClosestEncounter()
        local px, py = GetPlayerMapPosition('player')
        local index, distance_lowest, distance_current = 1, 1, 1
        SetMapToCurrentZone()
        local x, y, instanceID, name, description, encounterID = EJ_GetMapEncounter(index, true)
        local closest_encounterID
        while name do
                local distance_current = distance2d_squared(px, py, x, 1 - y) -- pin y uses bottom left as 0,0 GetPlayerMapPosition uses top left as 0,0 origin
                if distance_current < distance_lowest then
                        distance_lowest = distance_current
                        closest_encounterID = encounterID
                end
                index = index + 1
                x, y, instanceID, name, description, encounterID = EJ_GetMapEncounter(index, true)
        end
        return closest_encounterID
end
GetClosestEncounter() will return nil or the encounterID for EJ_GetEncounterInfo() of the closest encounter to the player on the map current map. NB: This will return nil if you're in a zone where there is no encounter pins on the map (for example, an area you're teleported to for an encounter like the transition phases on Garrosh), so you'd need to set it when the encounter starts for best effect. Also it will return an encounter even if you're on the other side of the map doing trash, so you need to do a little bit more to make sure you're actually in an encounter.

Combining this with the event INSTANCE_ENCOUNTER_ENGAGE_UNIT (which fires whenever a unit is added or removed to a boss frame) and IsEncounterInProgress() which returns 1 when you're in a fight you can't release from (ie, an encounter) and you should get something close to what you're after.

This is what I was using it for - https://github.com/eggsampler/tattletale (my code is nowhere near finished or working yet though :P)

Last edited by exemplar : 10-25-13 at 01:15 AM.
exemplar is offline   Reply With Quote
Unread 10-25-13, 01:55 AM   #4
Malsomnus
A Chromatic Dragonspawn
AddOn Author - Click to view addons
Join Date: Apr 2013
Posts: 166
Personally, I do encounter detection by having a table of mobs associated with each boss encounter, which is a bit of manual work but not too much (I expect it to become more work when I switch from mob names to mob IDs, as I ought to). It might not be the most elegant solution, but it seems to work easily enough and makes no assumption regarding boss frames and such. You can check it out in this add-on.
__________________
SanityCheck - Never again find yourself tanking Garrosh Hellscream in a wedding dress and a fishing rod!

Remember, every time you post a comment on an add-on, a kitten gets its wings!
Malsomnus is offline   Reply With Quote
Unread 10-26-13, 09:01 PM   #5
Rainrider
A Rage Talon Dragon Guard
Join Date: Nov 2008
Posts: 315
@exemplar
Your approach might be helpful to get the encounter in case there are no boss frames spawn initially or the shown bosses are not in the encounter's list of creatures. You'd probably better register PLAYER_REGEN_DISABLE, call IsEncounterInProgress() and check further if so. Are the x, y returns from EJ_GetMapEncounter influenced by map scale?

@Malsomnus
Combat log scanning is way beyond what I want to do with my addon. Maybe you could embed LibBossIDs to get the mobIDs.
Rainrider is offline   Reply With Quote
Unread 11-24-13, 02:54 AM   #6
exemplar
A Defias Bandit
Join Date: Oct 2013
Posts: 2
Sorry, maybe I didn't make it clear.

IsEncounterInProgress() and InCombatLockdown() always return false and nil respectively when PLAYER_REGEN_DISABLED fires, the information isn't available to the UI at that point. The easiest way I've found to get both is when INSTANCE_ENCOUNTER_ENGAGE_UNIT fires which is every encounter I've seen.
exemplar is offline   Reply With Quote
Reply

Go BackWoWInterface » Developer Discussions » General Authoring Discussion » Encounter Detection

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