Advertisement
Veyska

RoleIcons.lua

Aug 13th, 2024 (edited)
125
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 48.83 KB | None | 0 0
  1. local addonName, vars = ...
  2. local L = vars.L
  3. local LaddonName = L[addonName]
  4. RoleIcons = vars
  5. local addon = RoleIcons
  6. local _G = getfenv(0)
  7. local string, table, pairs, ipairs, tonumber, wipe =
  8.       string, table, pairs, ipairs, tonumber, wipe
  9. local InCombatLockdown, UnitGroupRolesAssigned, UnitName, UnitClass, UnitLevel, UnitIsPlayer, UnitIsGroupLeader, UnitIsGroupAssistant =
  10.       InCombatLockdown, UnitGroupRolesAssigned, UnitName, UnitClass, UnitLevel, UnitIsPlayer, UnitIsGroupLeader, UnitIsGroupAssistant
  11. local defaults = {
  12.   raid =         { true,  L["Show role icons on the Raid tab"] },
  13.   tooltip =      { true,  L["Show role icons in player tooltips"] },
  14.   hbicon =       { false, L["Show role icons in HealBot bars"] },
  15.   chat =         { true,  L["Show role icons in chat windows"] },
  16.   system =       { true,  L["Show role icons in system messages"] },
  17.   debug =        { false, L["Debug the addon"] },
  18.   classbuttons = { true,  L["Add class summary buttons to the Raid tab"] },
  19.   rolebuttons =  { true,  L["Add role summary buttons to the Raid tab"] },
  20.   serverinfo =   { true,  L["Add server info frame to the Raid tab"] },
  21.   trimserver =   { true,  L["Trim server names in tooltips"] },
  22.   autorole =     { true,  L["Automatically set role and respond to role checks based on your spec"] },
  23.   target =       { true,  L["Show role icons on the target frame (default Blizzard frames)"] },
  24.   focus =        { true,  L["Show role icons on the focus frame (default Blizzard frames)"] },
  25.   popup =        { true,  L["Show role icons in unit popup menus"] },
  26.   map =          { true,  L["Show role icons in map tooltips"] },
  27. }
  28. local settings
  29. local maxlvl = GetMaxLevelForLatestExpansion()
  30. vars.svnrev = {}
  31. vars.svnrev["RoleIcons.lua"] = tonumber(("$Revision: 181 $"):match("%d+"))
  32.  
  33. -- tie-in for third party addons to highlight raid buttons
  34. -- table maps GUID => highlight = boolean
  35. RoleIcons.unitstatus = {}
  36. RoleIcons.unitstatus.refresh = function() addon.UpdateRGF() end
  37.  
  38. local chats = {
  39.     CHAT_MSG_SAY = 1, CHAT_MSG_YELL = 1,
  40.     CHAT_MSG_WHISPER = 1, CHAT_MSG_WHISPER_INFORM = 1,
  41.     CHAT_MSG_PARTY = 1, CHAT_MSG_PARTY_LEADER = 1,
  42.     CHAT_MSG_INSTANCE_CHAT = 1, CHAT_MSG_INSTANCE_CHAT_LEADER = 1,
  43.     CHAT_MSG_RAID = 1, CHAT_MSG_RAID_LEADER = 1, CHAT_MSG_RAID_WARNING = 1,
  44.     CHAT_MSG_BATTLEGROUND_LEADER = 1, CHAT_MSG_BATTLEGROUND = 1,
  45.     }
  46.  
  47. local TTframe, TTfunc
  48.  
  49. local iconsz = 19
  50. local riconsz = iconsz
  51. local role_tex_file = "Interface\\LFGFrame\\UI-LFG-ICON-PORTRAITROLES.blp"
  52. local role_t = "\124T"..role_tex_file..":%d:%d:"
  53. local role_tex = {
  54.    DAMAGER = role_t.."0:0:64:64:20:39:22:41\124t",
  55.    HEALER  = role_t.."0:0:64:64:20:39:1:20\124t",
  56.    TANK    = role_t.."0:0:64:64:0:19:22:41\124t",
  57.    LEADER  = role_t.."0:0:64:64:0:19:1:20\124t",
  58.    SPACE   = role_t.."0:0:64:64:0:0:0:0\124t",
  59.    NONE    = ""
  60. }
  61. local function getRoleTex(role,size)
  62.   local str = role_tex[role]
  63.   if not str or #str == 0 then return "" end
  64.   if not size then size = 0 end
  65.   role_tex[size] = role_tex[size] or {}
  66.   str = role_tex[size][role]
  67.   if not str then
  68.      str = string.format(role_tex[role], size, size)
  69.      role_tex[size][role] = str
  70.   end
  71.   return str
  72. end
  73. -- could also use GetTexCoordsForRole/GetTexCoordsForRoleSmallCircle
  74. local function getRoleTexCoord(role)
  75.   local str = role_tex[role]
  76.   if not str or #str == 0 then return nil end
  77.   local a,b,c,d = string.match(str, ":(%d+):(%d+):(%d+):(%d+)%\124t")
  78.   return a/64,b/64,c/64,d/64
  79. end
  80.  
  81. local function chatMsg(msg)
  82.      DEFAULT_CHAT_FRAME:AddMessage("|cff6060ff"..LaddonName.."|r: "..msg)
  83. end
  84. local function debug(msg)
  85.   if settings and settings.debug then
  86.      chatMsg(msg)
  87.   end
  88. end
  89.  
  90. local function classColor(name, class, unit)
  91.    if not class then return name, nil, name end
  92.    local color = RAID_CLASS_COLORS[class]
  93.    color = color and color.colorStr
  94.    if unit and UnitExists(unit) then
  95.      if not UnitIsConnected(unit) then
  96.        color = "ff808080" -- grey
  97.      elseif UnitIsDeadOrGhost(unit) then
  98.        color = "ffff1919" -- red
  99.      end
  100.    end
  101.    local cname = name
  102.    if color then
  103.      cname = "\124c"..color..name.."\124r"
  104.    end
  105.    return cname, color, name
  106. end
  107.  
  108. local server_prefixes = {
  109.   The=1, Das=1, Der=1, Die=1, La=1, Le=1, Les=1, Los=1, Las=1,
  110. }
  111. local function utfbytewidth(s,b)
  112.   local c = s:byte(b)
  113.   if not c then return 0
  114.   elseif c >= 194 and c <= 223 then return 2
  115.   elseif c >= 224 and c <= 239 then return 3
  116.   elseif c >= 240 and c <= 244 then return 4
  117.   else return 1
  118.   end
  119. end
  120. local function trimServer(name, colorunit)
  121.   local class = colorunit and
  122.                 (type(colorunit) == "string" and UnitExists(colorunit) and select(2,UnitClass(colorunit))) or
  123.         addon.classcache[name] or
  124.         select(2,UnitClass(name))
  125.   name = name:gsub("%s","") -- remove space
  126.   local cname, rname = name:match("^([^-]+)-([^-]+)$") -- split
  127.   if not (cname and rname) then
  128.     cname = name
  129.     rname = nil
  130.   end
  131.   if rname and settings.trimserver then
  132.     local prefix = rname:match("^(%u[^%u]*)%u")
  133.     if prefix and server_prefixes[prefix] and #rname >= #prefix+3 then
  134.       rname = rname:gsub("^"..prefix,"")
  135.     end
  136.     local p = 1 -- trim to first 3 utf8 characters
  137.     for i=1,3 do
  138.       p = p + utfbytewidth(rname, p)
  139.     end
  140.     rname = rname:sub(1, p-1) -- trim
  141.   end
  142.   local ret = cname..(rname and "-"..rname or "")
  143.   return classColor(ret, class, type(colorunit) == "string" and colorunit)
  144. end
  145. function addon:trimServer(...) return trimServer(...) end
  146.  
  147. local sorttemp = {}
  148. local infotemp = {}
  149. local function toonList(role, class)
  150.   wipe(sorttemp)
  151.   wipe(infotemp)
  152.   local base = IsInRaid() and "raid" or "party"
  153.   local num = GetNumGroupMembers()
  154.   local cnt = 0
  155.   for i=1,num do
  156.     local unitid
  157.     if IsInRaid() then
  158.       unitid = "raid"..i
  159.     elseif i == num then
  160.       unitid = "player"
  161.     else
  162.       unitid = "party"..i
  163.     end
  164.     if UnitExists(unitid) then
  165.       local uname = GetUnitName(unitid, true)
  166.       local urole = UnitGroupRolesAssigned(unitid)
  167.       local uclass = select(2,UnitClass(unitid))
  168.       if uname and uclass and
  169.          ((not role)  or (role and role == urole)) and
  170.          ((not class) or (class and class == uclass)) then
  171.      local cname = trimServer(uname, unitid)
  172.          uname = uname:gsub("%s","")
  173.      table.insert(sorttemp,uname)
  174.      if not role and urole and urole ~= "NONE" then
  175.        cname = getRoleTex(urole)..cname
  176.      end
  177.      infotemp[uname] = cname
  178.      cnt = cnt + 1
  179.       end
  180.     end
  181.   end
  182.   table.sort(sorttemp)
  183.   local res
  184.   for _, name in ipairs(sorttemp) do
  185.     res = (res and res..", " or "")..infotemp[name]
  186.   end
  187.   return res, cnt
  188. end
  189.  
  190. local function myDefaultRole()
  191.   local _, class = UnitClass("player")
  192.   if class == "MAGE" or class == "HUNTER" or class == "WARLOCK" or class == "ROGUE" then
  193.     return "DAMAGER"
  194.   end
  195.   local tabIndex = GetSpecialization(false, false)
  196.   if not tabIndex then return nil end -- untalented hybrid
  197.   local role = GetSpecializationRole(tabIndex,false,false)
  198.   return role
  199. end
  200.  
  201. local frame = CreateFrame("Button", addonName.."HiddenFrame", UIParent)
  202. frame:RegisterEvent("ADDON_LOADED");
  203. frame:RegisterEvent("ROLE_POLL_BEGIN");
  204. frame:RegisterEvent("GROUP_ROSTER_UPDATE");
  205. frame:RegisterEvent("PARTY_INVITE_REQUEST");
  206. frame:RegisterEvent("GROUP_JOINED");
  207. frame:RegisterEvent("ACTIVE_TALENT_GROUP_CHANGED");
  208. frame:RegisterEvent("PLAYER_TARGET_CHANGED");
  209. frame:RegisterEvent("PLAYER_FOCUS_CHANGED");
  210. frame:RegisterEvent("PLAYER_REGEN_ENABLED");
  211.  
  212. local function UpdateTT(tt, unit, ttline)
  213.   if not settings.tooltip then return end
  214.   unit = unit or (tt and tt.GetUnit and tt:GetUnit())
  215.   if not unit then return end
  216.   local role = UnitGroupRolesAssigned(unit)
  217.   local leader = GetNumGroupMembers() > 0 and UnitIsGroupLeader(unit)
  218.   if (role and role ~= "NONE") or leader then
  219.      local name = tt:GetName()
  220.      local line = ttline or _G[name.."TextLeft1"]
  221.      if line and line.GetText then
  222.        local txt = line:GetText()
  223.        if txt and not string.find(txt,role_tex_file,1,true) then
  224.          if leader then
  225.            txt = getRoleTex("LEADER",iconsz)..txt
  226.          end
  227.          line:SetText(getRoleTex(role,iconsz)..txt)
  228.        end
  229.      end
  230.   end
  231. end
  232.  
  233. local function UpdateHBTT(unit)
  234.   local gtt = HealBot_Globals and HealBot_Globals.UseGameTooltip
  235.   if unit == "NONE" then return end
  236.   debug("UpdateHBTT: unit="..tostring(unit).." gtt="..tostring(gtt))
  237.   local tt = HealBot_Tooltip
  238.   local ttl = HealBot_TooltipTextL1
  239.   if gtt and gtt ~= 0 then
  240.     tt = GameTooltip
  241.     ttl = GameTooltipTextLeft1
  242.   end
  243.   if true and unit == "player" then -- fix a stray role text added by healbot
  244.     local txt = ttl and ttl.GetText and ttl:GetText()
  245.     if txt then
  246.       txt = txt:gsub("^DAMAGER ",""):gsub("^TANK ",""):gsub("^HEALER ","")
  247.       ttl:SetText(txt)
  248.     end
  249.   end
  250.   UpdateTT(tt, unit, ttl)
  251.   HealBot_Tooltip_Show() -- fix size
  252. end
  253.  
  254. local function VuhdoHook()
  255.   if VuhDoTooltip and VuhDoTooltipTextL1 then
  256.     local unit = VuhDoTooltipTextL1:GetText()
  257.     if not UnitExists(unit) then
  258.       local s = GetMouseFocus()
  259.       s = s and s.GetAttribute and s:GetAttribute("unit")
  260.       if s and UnitExists(s) then unit = s end
  261.     end
  262.     UpdateTT(VuhDoTooltip, unit, VuhDoTooltipTextL1)
  263.   end
  264. end
  265.  
  266. local function HBIconHook(button, ...)
  267.   if not settings.hbicon or not button then return end
  268.   local bar = HealBot_Action_HealthBar and HealBot_Action_HealthBar(button)
  269.   local unit = button and button.unit
  270.   local role = UnitGroupRolesAssigned(unit)
  271.   if not bar or not unit or not role or not UnitExists(unit) then return end
  272.   debug("HBIconHook: "..unit.." "..role)
  273.   local name = bar.GetName and bar:GetName()
  274.   local icon
  275.   for idx=15,16 do
  276.     local i = name and _G[name.."Icon"..idx]
  277.     if not i.GetTexture or not i.GetAlpha then return end
  278.     local curr = i:GetTexture()
  279.     debug(" Icon"..idx..": "..(curr or "nil"))
  280.     if i.SetTexCoord then
  281.       i:SetTexCoord(0,1,0,1)
  282.     end
  283.     if curr and curr:find("PORTRAITROLES") then
  284.       if not icon then
  285.          icon = i
  286.       else -- duplicate role icon
  287.          i:SetAlpha(0)
  288.       end
  289.     elseif curr == nil or i:GetAlpha() == 0 or curr:find("icon_class") then
  290.       if not icon then icon = i end
  291.     end
  292.   end
  293.   if not icon or not icon.SetTexture or not icon.SetTexCoord then return end
  294.   if role == "NONE" then
  295.     icon:SetTexture(0,0,0,0)
  296.     icon:SetTexCoord(0,1,0,1)
  297.   else
  298.     icon:SetTexture(role_tex_file)
  299.     icon:SetTexCoord(getRoleTexCoord(role))
  300.     icon:SetAlpha(1)
  301.   end
  302. end
  303.  
  304. local function RoleMenuInitialize(self)
  305.         UnitPopup_ShowMenu(UIDROPDOWNMENU_OPEN_MENU, "SELECT_ROLE", self.unit, self.name, self.id);
  306. end
  307.  
  308. local function ShowRoleMenu(self)
  309.         HideDropDownMenu(1);
  310.         if ( self.id and self.name ) then
  311.                 FriendsDropDown.name = self.name;
  312.                 FriendsDropDown.id = self.id;
  313.                 FriendsDropDown.unit = self.unit;
  314.                 FriendsDropDown.initialize = RoleMenuInitialize;
  315.                 FriendsDropDown.displayMode = "MENU";
  316.                 ToggleDropDownMenu(1, nil, FriendsDropDown, "cursor");
  317.         end
  318. end
  319.  
  320. local classcnt = {}
  321. local rolecnt = {}
  322.  
  323. local LC = LocalizedClassList(false)
  324. if GetLocale() == "enUS" then
  325.   LC["DEATHKNIGHT"] = "DK"
  326. end
  327. local tokendata = {
  328.   [L["Vanquisher"]] = { { "ROGUE", LC["ROGUE"] }, { "DEATHKNIGHT", LC["DEATHKNIGHT"] }, { "MAGE", LC["MAGE"] }, { "DRUID", LC["DRUID"] } },
  329.   [L["Protector"]] = { { "WARRIOR", LC["WARRIOR"] }, { "HUNTER", LC["HUNTER"] }, { "SHAMAN", LC["SHAMAN"] }, { "MONK", LC["MONK"] } },
  330.   [L["Conqueror"]] = { { "PALADIN", LC["PALADIN"] }, { "PRIEST", LC["PRIEST"] }, { "WARLOCK", LC["WARLOCK"] } },
  331. }
  332. local function DisplayTokenTooltip()
  333.   if not UnitInRaid("player") then return end
  334.  
  335.   GameTooltip:ClearLines()
  336.   --GameTooltip_SetDefaultAnchor(GameTooltip, UIParent);
  337.   GameTooltip:SetOwner(addon.headerFrame)
  338.   TTframe = addon.headerFrame
  339.   TTfunc = DisplayTokenTooltip
  340.   GameTooltip:SetAnchorType("ANCHOR_TOPLEFT")
  341.   local total = 0
  342.   local summstr = ""
  343.   for _,role in ipairs({"TANK","HEALER","DAMAGER"}) do
  344.     local cnt = rolecnt[role] or 0
  345.     summstr = summstr..cnt.." "..getRoleTex(role).."   "
  346.     total = total + cnt
  347.   end
  348.   local none = rolecnt["NONE"]
  349.   if none and none > 0 then
  350.     summstr = summstr..none.." "..L["Unassigned"]
  351.     total = total + none
  352.   end
  353.  
  354.   GameTooltip:AddLine(L["Tier token breakdown:"])
  355.   for token, ti in pairs(tokendata) do
  356.     local tokenstr = ""
  357.     local cnt = 0
  358.     for _,ci in ipairs(ti) do
  359.       local class = ci[1]
  360.       local lclass = ci[2]
  361.       cnt = cnt + (classcnt[class] or 0)
  362.       if #tokenstr >  0 then tokenstr = tokenstr..", " end
  363.       tokenstr = tokenstr..classColor(lclass,class)
  364.     end
  365.     GameTooltip:AddLine("\124cffff0000"..cnt.."\124r".."  \124cffffffff"..token.." (\124r"..tokenstr.."\124cffffffff)\124r")
  366.   end
  367.  
  368.   GameTooltip:AddLine(" ")
  369.   GameTooltip:AddLine(total.." "..L["Players:"].." "..summstr)
  370.   GameTooltip:Show()
  371.  
  372. end
  373.  
  374. local function DisplayServerTooltip()
  375.   addon:UpdateServers(true)
  376.   if not addon.serverList or not addon.serverFrame then return end
  377.  
  378.   GameTooltip:ClearLines()
  379.   --GameTooltip_SetDefaultAnchor(GameTooltip, UIParent);
  380.   GameTooltip:SetOwner(addon.serverFrame)
  381.   TTframe = addon.serverFrame
  382.   TTfunc = DisplayServerTooltip
  383.   GameTooltip:SetAnchorType("ANCHOR_BOTTOMRIGHT",-1*addon.serverFrame:GetWidth())
  384.   GameTooltip:AddLine(L["Server breakdown:"],1,1,1)
  385.   GameTooltip:AddDoubleLine(L["Server"], L["Players"], 1,1,1,1,1,1)
  386.  
  387.   for _,info in ipairs(addon.serverList) do
  388.     local num = info.num
  389.     local name = info.name
  390.     if info.maxlevel > 0 and info.maxlevel ~= maxlvl then
  391.       name = name.." ("..LEVEL.." "..info.maxlevel..")"
  392.     end
  393.     GameTooltip:AddDoubleLine(name, num)
  394.   end
  395.  
  396.   GameTooltip:AddLine(" ")
  397.   GameTooltip:AddLine(L["Left-click to report in chat"],1,1,1)
  398.   GameTooltip:Show()
  399. end
  400.  
  401. function addon:ServerChatString(maxentries)
  402.   local level = addon.serverList[1].maxlevel
  403.   local str = ""
  404.   local cnt = 0
  405.   for _,info in ipairs(addon.serverList) do
  406.     if cnt == maxentries or #str > 200 then
  407.       str = str..", ..."
  408.       break
  409.     end
  410.     cnt = cnt + 1
  411.     if #str > 0 then str = str .. ", " end
  412.     local lvlstr = ""
  413.     if level < maxlvl or info.maxlevel < level then
  414.       lvlstr = "/"..LEVEL.." "..info.maxlevel
  415.     end
  416.     str = str..info.name.."("..info.num..lvlstr..")"
  417.   end
  418.   return addonName..": "..L["Server breakdown:"].." "..str
  419. end
  420.  
  421. local function SortServers(a,b)
  422.   if a.maxlevel ~= b.maxlevel then
  423.     return a.maxlevel > b.maxlevel, true
  424.   elseif a.num ~= b.num then
  425.     return a.num > b.num, true
  426.   else
  427.     return a.name < b.name
  428.   end
  429. end
  430.  
  431. local function UpdateRGF()
  432.   if not RaidFrame then return end
  433.   if UnitIsGroupLeader("player") or UnitIsGroupAssistant("player") then
  434.      if not addon.rolecheckbtn and RaidFrameRaidInfoButton and RaidFrameAllAssistCheckButton then
  435.        local btn = CreateFrame("Button","RaidIconsRoleCheckBtn",RaidFrame,"UIPanelButtonTemplate")
  436.        btn:SetSize(RaidFrameRaidInfoButton:GetSize())
  437.        btn:SetText(ROLE_POLL)
  438.        btn:SetPoint("BOTTOMLEFT", RaidFrameAllAssistCheckButton, "TOPLEFT", 0, 2)
  439.        btn:SetScript("OnClick", function() InitiateRolePoll() end)
  440.        btn:SetNormalFontObject(GameFontNormalSmall)
  441.        btn:SetHighlightFontObject(GameFontHighlightSmall)
  442.        addon.rolecheckbtn = btn
  443.      end
  444.      addon.rolecheckbtn:Show()
  445.   elseif addon.rolecheckbtn then
  446.      addon.rolecheckbtn:Hide()
  447.   end
  448.   wipe(classcnt)
  449.   wipe(rolecnt)
  450.   local guessmaxraidlvl = addon.maxraidlvl or maxlvl
  451.   addon.maxraidlvl = 0
  452.   for i=1,40 do
  453.     local btn = _G["RaidGroupButton"..i]
  454.     if btn then
  455.       if not addon.deftexture then
  456.          addon.deftexture = btn:GetNormalTexture():GetTexture()
  457.       end
  458.       if addon.deftexture then
  459.          btn:GetNormalTexture():SetTexture(addon.deftexture)
  460.       end
  461.     end
  462.     if btn and btn.unit and btn.subframes and btn.subframes.level and btn:IsVisible() then
  463.        local unit = btn.unit
  464.        if unit then
  465.          local role = UnitGroupRolesAssigned(unit)
  466.      local name = UnitName(unit)
  467.          local guid = UnitGUID(unit)
  468.      local lclass,class = UnitClass(unit)
  469.          if (not class or #class == 0) and btn.name then
  470.             lclass,class = UnitClass(btn.name)
  471.          end
  472.      if class then
  473.            classcnt[class] = (classcnt[class] or 0) + 1
  474.      end
  475.          local lvl = UnitLevel(unit)
  476.          if not lvl or lvl == 0 then
  477.            lvl = (btn.name and UnitLevel(btn.name)) or 0
  478.          end
  479.      addon.maxraidlvl = math.max(addon.maxraidlvl, lvl or 0)
  480.  
  481.          if addon.unitstatus[guid] then
  482.             btn:GetNormalTexture():SetTexture(0.5,0,0)
  483.          end
  484.      name = classColor(name, class, unit)
  485.      role = role or "NONE"
  486.      rolecnt[role] = (rolecnt[role] or 0) + 1
  487.          if role ~= "NONE" then
  488.        lclass = lclass or ""
  489.            if settings.raid then
  490.         local txt1 = lvl
  491.         local txt2 = lclass
  492.         if (lvl == guessmaxraidlvl or lvl == 0) then -- sometimes returns 0 during moves
  493.          txt1 = getRoleTex(role,riconsz)
  494.          txt2 = lclass
  495.             else
  496.              --print(unit.." "..lvl)
  497.          txt1 = lvl
  498.          txt2 = getRoleTex(role,riconsz).." "..lclass
  499.         end
  500.             btn.subframes.level:SetDrawLayer("OVERLAY")
  501.             btn.subframes.level:SetText(txt1)
  502.             btn.subframes.class:SetDrawLayer("OVERLAY")
  503.             btn.subframes.class:SetText(txt2)
  504.         if txt1 ~= lvl and btn.subframes.level:IsTruncated() then
  505.            riconsz = riconsz - 1
  506.            debug("Reduced iconsz to: "..riconsz)
  507.            UpdateRGF()
  508.            return
  509.         end
  510.            end
  511.          end
  512.        end
  513.        if not InCombatLockdown() then
  514.          -- extra bonus, make the secure frames targettable
  515.          btn:SetAttribute("type1", "target")
  516.          btn:SetAttribute("unit", btn.unit)
  517.        end
  518.        addon.btnhook = addon.btnhook or {}
  519.        if not addon.btnhook[btn] then
  520.          btn:RegisterForClicks("AnyUp")
  521.      btn:SetScript("OnEnter", function(self) -- override to remove obsolete shift-drag prompt
  522.          RaidGroupButton_OnEnter(self);
  523.          if ( RaidFrame:IsMouseOver() ) then
  524.            GameTooltip:Show()
  525.              end
  526.      end)
  527.          btn:HookScript("OnClick", function(self, button)
  528.        if button == "MiddleButton" then
  529.          ShowRoleMenu(self)
  530.        end
  531.      end)
  532.      addon.btnhook[btn] = true
  533.        end
  534.     end
  535.   end
  536.   if addon.maxraidlvl ~= guessmaxraidlvl then -- cache miss
  537.     UpdateRGF()
  538.     return
  539.   end
  540.   if addon.rolebuttons then
  541.   for role,btn in pairs(addon.rolebuttons) do
  542.     if settings.rolebuttons and UnitInRaid("player") and not RaidInfoFrame:IsShown() then  
  543.       btn.rolecnt = rolecnt[role] or 0
  544.       _G[btn:GetName().."Count"]:SetText(btn.rolecnt)
  545.       btn:Show()
  546.     else
  547.       btn:Hide()
  548.     end
  549.   end
  550.   end
  551.   if addon.classbuttons then
  552.   for i,btn in ipairs(addon.classbuttons) do
  553.     local class = btn.class
  554.     local count = classcnt[class]
  555.     if settings.classbuttons and UnitInRaid("player") and i <= MAX_CLASSES and not RaidInfoFrame:IsShown() then
  556.         btn:Show()
  557.         local icon = _G[btn:GetName().."IconTexture"]
  558.         local fs = _G[btn:GetName().."Count"]
  559.     if count and count > 0 then
  560.       fs:SetTextHeight(12) -- got too small in 5.x for some reason
  561.       fs:SetText(count)
  562.       fs:Show()
  563.       icon:SetAlpha(1)
  564.       SetItemButtonDesaturated(btn, nil)
  565.     else
  566.       fs:Hide()
  567.       icon:SetAlpha(0.5)
  568.       SetItemButtonDesaturated(btn, true)
  569.     end
  570.     else
  571.         btn:Hide()
  572.     end
  573.   end
  574.   end
  575.   if not addon.headerFrame then
  576.     addon.headerFrame = CreateFrame("Button", addonName.."HeaderButton", RaidFrame)
  577.     addon.headerFrame:SetPoint("TOPLEFT",RaidFrame,-10,10)
  578.     addon.headerFrame:SetSize(74,74)
  579.     addon.headerFrame:Show()
  580.     addon.headerFrame:SetScript("OnEnter", function() DisplayTokenTooltip() end)
  581.     addon.headerFrame:SetScript("OnLeave", function() TTframe = nil; GameTooltip:Hide() end)
  582.   end
  583.   addon:UpdateServers()
  584. end
  585. addon.UpdateRGF = UpdateRGF
  586.  
  587. function addon:UpdateServers(intt, groupjoin)
  588.   addon.servers = addon.servers or {}
  589.   addon.levelcache = addon.levelcache or {}
  590.   addon.rolecache = addon.rolecache or {}
  591.   addon.classcache = addon.classcache or {}
  592.   for _,info in pairs(addon.servers) do
  593.     info.num = 0
  594.     info.maxlevel = 0
  595.     info.unknownlevel = false
  596.   end
  597.   local num = GetNumGroupMembers()
  598.   for i=1,num do
  599.     local name, realm, level, class, islead
  600.     if IsInRaid() then
  601.       local _, rank
  602.       name, rank, _, level, _, class = GetRaidRosterInfo(i)
  603.       realm = name and name:match("-([^-]+)$")
  604.       if not level or level == 0 then level = UnitLevel("raid"..i) end -- empty for offline
  605.       if not class or class == "UNKNOWN" then class = select(2,UnitClass("raid"..i)) end -- empty for offline
  606.       islead = (rank == 2)
  607.     else
  608.       local unit = "player"
  609.       if i < num then unit = "party"..i end
  610.       name, realm = UnitName(unit)
  611.       level = UnitLevel(unit)
  612.       class = select(2,UnitClass(unit))
  613.       islead = UnitIsGroupLeader(unit)
  614.     end
  615.     if name and level then
  616.       if islead and level == 0 and name == UNKNOWN and addon.inviteleader then
  617.         -- leader info can be delayed on cross-realm invite
  618.     name, realm = addon.inviteleader:match("^([^-]+)-([^-]+)$")
  619.     if not name then name = addon.inviteleader end -- same realm
  620.       end
  621.       if not realm or realm == "" then realm = GetRealmName() end
  622.       local fullname = name
  623.       if not name:match("-([^-]+)$") then fullname = name.."-"..realm end
  624.       if level > 0 then -- sometimes level is not queryable for offline
  625.         addon.levelcache[fullname] = level
  626.       else
  627.         level = addon.levelcache[fullname] or 0
  628.       end
  629.       local shortname = fullname:gsub("-"..GetRealmName(),"")
  630.       local role = UnitGroupRolesAssigned(shortname)
  631.       if role then
  632.         addon.rolecache[shortname] = role
  633.       end
  634.       if class and class ~= "UNKNOWN" then
  635.         addon.classcache[shortname] = class
  636.       end
  637.       local r = addon.servers[realm] or { num=0, maxlevel=0, name=realm }
  638.       addon.servers[realm] = r
  639.       if level ~= 1 then -- since 6.x level 1's don't affect phasing
  640.         r.num = r.num + 1
  641.       end
  642.       r.maxlevel = math.max(r.maxlevel, level)
  643.       if level == 0 then
  644.         r.unknownlevel = true
  645.       end
  646.       if groupjoin and islead then
  647.         -- joined a new group, default to assuming we are on leader's realm
  648.     debug("leader lastServer = "..realm)
  649.     addon.lastServer = r
  650.       end
  651.     end
  652.     --myprint(i,name,realm,level,islead)
  653.   end
  654.  
  655.   if not addon.lastServer and settings.state and settings.state.lastServer then -- restore lastServer from previous session
  656.     addon.lastServer = addon.servers[settings.state.lastServer]
  657.     settings.state.lastServer = nil -- only once
  658.     debug("Restored lastServer="..tostring(addon.lastServer and addon.lastServer.name))
  659.   end
  660.  
  661.   if not addon.serverFrame then
  662.       addon.serverFrame = CreateFrame("Button", addonName.."ServerButton", RaidFrame, "InsetFrameTemplate3")
  663.       addon.serverFrame:ClearAllPoints()
  664.       addon.serverFrame:SetPoint("TOPRIGHT",RaidFrame,-27,-2)
  665.       addon.serverFrame:SetSize(125,20)
  666.       addon.serverText = addon.serverFrame:CreateFontString(nil,"ARTWORK","GameFontNormalSmall")
  667.       addon.serverText:SetPoint("LEFT",addon.serverFrame,10,0)
  668.       addon.serverFrame:SetScript("OnEnter", function() DisplayServerTooltip() end)
  669.       addon.serverFrame:SetScript("OnLeave", function() TTframe = nil; GameTooltip:Hide() end)
  670.       addon.serverFrame:SetScript("OnClick", function()
  671.         local str = addon:ServerChatString()
  672.     local chat
  673.     if IsInGroup(LE_PARTY_CATEGORY_INSTANCE) then
  674.       chat = "INSTANCE_CHAT"
  675.     elseif IsInRaid() then
  676.       chat = "RAID"
  677.     elseif IsInGroup() then
  678.       chat = "PARTY"
  679.     else
  680.       return
  681.     end
  682.     SendChatMessage(str, chat)
  683.      end)
  684.   end
  685.   local list = wipe(addon.serverList or {})
  686.   addon.serverList = list
  687.   local cnt = 0
  688.   for server, info in pairs(addon.servers) do
  689.     if info.num > 0 then
  690.       table.insert(list, info)
  691.       cnt = cnt + 1
  692.     end
  693.   end
  694.   if cnt < 2 then
  695.     addon.serverFrame:Hide()
  696.     addon.lastServer = list[1] -- nil for ungrouped
  697.     addon.serverText:SetText((addon.lastServer and addon.lastServer.name) or GetRealmName()) -- for 3rd party addons
  698.   else
  699.     table.sort(list, SortServers)
  700.     local old = addon.lastServer
  701.     local curr = list[1].name
  702.     local color
  703.     if old and old.unknownlevel and old.num > 0 then
  704.        debug("level info missing for a group member on lastserver, suppressing possible transfer")
  705.        color = "|cffff1919" -- red
  706.        curr = old.name
  707.     elseif list[1].maxlevel > list[2].maxlevel or
  708.        list[1].num >= list[2].num + 2 then
  709.        color = "|cff19ff19" -- green
  710.        addon.lastServer = list[1]
  711.     elseif list[1].num == list[2].num + 1 and old == list[2] and list[1].unknownlevel then
  712.        debug("level info missing for a group member on newserver, suppressing possible transfer")
  713.        color = "|cffff1919" -- red
  714.        curr = old.name
  715.     elseif list[1].num >= list[2].num + 1 then
  716.        color = "|cffffff00" -- yellow
  717.        addon.lastServer = list[1]
  718.     else
  719.        color = "|cffff1919" -- red
  720.        if old and not select(2,SortServers(list[1],old)) then
  721.          curr = old.name
  722.        else
  723.      curr = curr.." ?"
  724.      addon.lastServer = nil
  725.        end
  726.     end
  727.     local new = addon.lastServer
  728.     if settings.serverinfo and not IsInInstance() and
  729.        new and old and new ~= old then
  730.       debug(L["Probable realm transfer"]..": "..
  731.               old.name.." ("..old.num.." "..L["Players"].." / "..LEVEL.." "..old.maxlevel..")  ->  "..
  732.               new.name.." ("..new.num.." "..L["Players"].." / "..LEVEL.." "..new.maxlevel..")")
  733.     end
  734.     addon.serverText:SetText(color..curr.."|r")
  735.     if settings.serverinfo then
  736.       addon.serverFrame:Show()
  737.     else
  738.       addon.serverFrame:Hide()
  739.     end
  740.   end
  741.   if settings.state then -- save lastServer between sessions
  742.     settings.state.lastServer = addon.lastServer and addon.lastServer.name
  743.   end
  744.   if not intt and TTframe and TTfunc and
  745.      GameTooltip:IsShown() and GameTooltip:GetOwner() == TTframe then -- dynamically update tooltip
  746.      TTfunc(TTframe)
  747.   end
  748. end
  749.  
  750. local system_msgs = {
  751.   ERR_INSTANCE_GROUP_ADDED_S,           -- "%s has joined the instance group."
  752.   ERR_INSTANCE_GROUP_REMOVED_S,     -- "%s has left the instance group."
  753.   ERR_RAID_MEMBER_ADDED_S,              -- "%s has joined the raid group."
  754.   ERR_RAID_MEMBER_REMOVED_S,        -- "%s has left the raid group."
  755.   ERR_BG_PLAYER_LEFT_S,         -- "%s has left the battle"
  756.   RAID_MEMBERS_AFK,             -- "The following players are Away: %s"
  757.   RAID_MEMBER_NOT_READY,        -- "%s is not ready"
  758.   ERR_RAID_LEADER_READY_CHECK_START_S,  -- "%s has initiated a ready check."
  759.   ERR_PLAYER_DIED_S,            -- "%s has died."
  760.   ERR_NEW_GUIDE_S,          -- "%s is now the Dungeon Guide.";
  761.   ERR_NEW_LEADER_S,         -- "%s is now the group leader.";
  762.   ERR_NEW_LOOT_MASTER_S,        -- "%s is now the loot master.";
  763.   LFG_LEADER_CHANGED_WARNING,       -- "%s is now the leader of your group!"; (currently broken via LFG_DisplayGroupLeaderWarning)
  764.   JOINED_PARTY,             -- "%s joins the party."
  765.   LEFT_PARTY,               -- "%s leaves the party."
  766.   ERR_PARTY_LFG_BOOT_VOTE_FAILED,   -- "The vote to kick %s has failed.";
  767.   ERR_PARTY_LFG_BOOT_VOTE_SUCCEEDED,    -- "The vote to kick %s has passed.";
  768. }
  769. local function patconvert(str)
  770.   return "^"..str:gsub("%%%d?%$?s","(.-)").."$" -- replace %s and %4$s with a capture
  771. end
  772. local system_scan = {}
  773. for i, str in ipairs(system_msgs) do
  774.   system_scan[i] = patconvert(str)
  775. end
  776. -- messages that need special handling
  777. table.insert(system_scan, (patconvert(ERR_PARTY_LFG_BOOT_VOTE_REGISTERED):gsub("%%%d?%$?d.+$","")))
  778. table.insert(system_msgs, false)
  779.       -- "Your request to kick %s has been successfully received. %d |4more request is:more requests are; needed to initiate a vote.";
  780. local icon_scan = patconvert(TARGET_ICON_SET:gsub("[%[%]%-]",".")):gsub("%%%d?%$?d","(%%d+)")
  781. local icon_msg = TARGET_ICON_SET:gsub("\124Hplayer.+\124h","%%s"):gsub("%%%d?%$?[ds]","%%s")
  782.       -- "|Hplayer:%s|h[%s]|h sets |TInterface\\TargetingFrame\\UI-RaidTargetingIcon_%d:0|t on %s."
  783.  
  784. function addon:formatToon(toon, nolink, spacenone)
  785.   if not toon then return end
  786.   toon = GetUnitName(toon, true) or toon -- ensure name is fully-qualified
  787.   if not addon.classcache[toon] and
  788.      not (UnitExists(toon) and (UnitInRaid(toon) or UnitInParty(toon))) then return toon end
  789.   local role = UnitGroupRolesAssigned(toon)
  790.   if role == "NONE" then role = addon.rolecache[toon] end -- use cache if player just left raid
  791.   local cname,color,name = trimServer(toon, true)
  792.   if not nolink then
  793.     -- ticket 14: wrap color outside player link for Prat recognition
  794.     --color = color and "\124c"..color or ""
  795.     --cname = "["..color.."\124Hplayer:"..toon..":0\124h"..name.."\124h"..(#color>0 and "\124r" or "").."]"
  796.     cname = string.format("[%s%s\124Hplayer:%s:0\124h%s\124h%s]",
  797.                            color and "\124c" or "", color or "",
  798.                toon, name,
  799.                color and "\124r" or "")
  800.   end
  801.   if (role and role ~= "NONE") then
  802.      cname = getRoleTex(role,0)..cname
  803.   elseif spacenone then
  804.      cname = getRoleTex("SPACE",0)..cname
  805.   end
  806.   return cname
  807. end
  808.  
  809. local function RoleChangedFrame_OnEvent_hook(self, event, changed, from, oldRole, newRole, ...)
  810.   if settings.system then
  811.     changed = addon:formatToon(changed)
  812.     from = addon:formatToon(from)
  813.   end
  814.   return RoleChangedFrame_OnEvent(self, event, changed, from, oldRole, newRole, ...)
  815. end
  816.  
  817. local function SystemMessageFilter(self, event, message, ...)
  818.   if not settings.system then return false end
  819.   if oRA3 and not addon.oRA3hooked then -- oRA3 sends a fake system message when not promoted, catch that too
  820.      local oral = LibStub("AceLocale-3.0", true)
  821.      oral = oral and oral:GetLocale("oRA3", true)
  822.      oral = oral and oral["The following players are not ready: %s"]
  823.      if oral then
  824.        table.insert(system_msgs, oral)
  825.        table.insert(system_scan, patconvert(oral))
  826.        addon.oRA3hooked = true
  827.      end
  828.      local rc = oRA3:GetModule("ReadyCheck",true)
  829.      if rc then rc.stripservers = false end -- tell oRA3 not to strip realms from fake server messages
  830.   end
  831.   if event == "CHAT_MSG_TARGETICONS" then -- special handling for icon message
  832.     local _, src, id, dst = message:match(icon_scan)
  833.     if not (src and id and dst) then return false end
  834.     src = addon:formatToon(src)
  835.     id = addon:formatToon(id) -- some locales reverse the fields
  836.     dst = addon:formatToon(dst)
  837.     return false, icon_msg:format(src, id, dst), ...
  838.   end
  839.   for idx, pat in ipairs(system_scan) do
  840.     local names = message:match(pat)
  841.     local msg = system_msgs[idx]
  842.     if names then
  843.       local newnames = ""
  844.       for toon in string.gmatch(names, "[^,%s]+") do
  845.     newnames = newnames..(#newnames > 0 and ", " or "")..addon:formatToon(toon)
  846.       end
  847.       if msg then -- re-print
  848.         return false, msg:format(newnames), ...
  849.       else -- substitute
  850.         return false, message:gsub(names:gsub("%-","."),newnames), ...
  851.       end
  852.     end
  853.   end
  854.   return false, message, ...
  855. end
  856.  
  857. local function ChatFilter(self, event, message, sender, ...)
  858.   if not settings.chat then return false end
  859.   local role = UnitGroupRolesAssigned(sender)
  860.   if (role and role ~= "NONE") then
  861.     if not string.find(message,role_tex_file,1,true) then
  862.       message = getRoleTex(role,0).." "..message
  863.     end
  864.   end
  865.   return false, message, sender, ...
  866. end
  867.  
  868. local function PratFilter()
  869.   if not settings.chat then return false end
  870.   local sm = Prat.SplitMessageOrg
  871.   --debug("sm.EVENT="..(sm.EVENT or "nil").."  sm.PLAYER="..(sm.PLAYER or "nil"))
  872.   if sm and sm.EVENT and sm.PLAYER and
  873.      (chats[sm.EVENT] or sm.EVENT == "CHAT_MSG_PARTY_GUIDE") then -- nonevent created by Prat
  874.     local role = UnitGroupRolesAssigned(sm.PLAYER)
  875.     if (role and role ~= "NONE") then
  876.       if not string.find(sm.PLAYER,role_tex_file,1,true) then
  877.         sm.PLAYER = getRoleTex(role,0)..sm.PLAYER
  878.       end
  879.     end
  880.   end
  881. end
  882.  
  883. local function UnitPopup_hook(menu, which, unit, name, userData)
  884.   if not settings.popup then return end
  885.   debug("UnitPopup_hook: "..tostring(which))
  886.   if unit and not UnitIsPlayer(unit) then return end
  887.   if which and which:match("^BN_") then return end
  888.   if which == "FOCUS" and unit then name = nil end -- workaround a Blizz hack
  889.   local line = DropDownList1Button1
  890.   local text = line and line.GetText and line:GetText()
  891.   if not text or #text == 0 then return end
  892.   if not name and unit and UnitExists(unit) then name = GetUnitName(unit,true) end
  893.   if not name or not name:match("^"..text) then return end
  894.   local role = UnitGroupRolesAssigned(unit or name)
  895.   if not role or role == "NONE" then role = addon.rolecache[name] end
  896.   local class = (unit and UnitExists(unit) and select(2,UnitClass(unit))) or
  897.         addon.classcache[name] or select(2,UnitClass(name))
  898.   debug("name="..name.." unit="..tostring(unit).." class="..tostring(class).." role="..tostring(role))
  899.   local cname = classColor(name, class, unit)
  900.   if (role and role ~= "NONE") then
  901.     cname = getRoleTex(role,0)..cname
  902.   end
  903.   line:SetText(cname)
  904.   -- might need to stretch the menu width for long names
  905.   local ntext = DropDownList1Button1NormalText
  906.   local minwidth = (ntext:GetStringWidth() or 0)
  907.   local width = DropDownList1 and DropDownList1.maxWidth
  908.   if width and width < minwidth then
  909.     DropDownList1.maxWidth = minwidth
  910.   end
  911. end
  912.  
  913. local function WorldMapUnit_OnEnter_hook()
  914.   if not WorldMapTooltip:IsShown() or not settings.map then return end
  915.   local text = WorldMapTooltipTextLeft1 and WorldMapTooltipTextLeft1:GetText()
  916.   if not text or #text == 0 then return end
  917.   if not addon.mapbuttons then
  918.     addon.mapbuttons = { WorldMapPlayerUpper }
  919.     for i=1, MAX_PARTY_MEMBERS do
  920.       table.insert(addon.mapbuttons, _G["WorldMapParty"..i])
  921.     end
  922.     for i=1, MAX_RAID_MEMBERS do
  923.       table.insert(addon.mapbuttons, _G["WorldMapRaid"..i])
  924.     end
  925.   end
  926.   text = "\n"..text.."\n"
  927.   for _, button in ipairs(addon.mapbuttons) do
  928.     if button:IsVisible() and button:IsMouseOver() and button.unit then
  929.       local name = button.name or UnitName(button.unit)
  930.       local pname = format(PLAYER_IS_PVP_AFK, name)
  931.       local fname = GetUnitName(button.unit, true)
  932.       local toon = addon:formatToon(fname, true, true)
  933.       text = text:gsub("\n"..pname.."\n", "\n"..toon.."\n")
  934.       text = text:gsub("\n"..name.."\n", "\n"..toon.."\n")
  935.     end
  936.   end
  937.   text = strtrim(text)
  938.   WorldMapTooltip:SetText(text)
  939.   WorldMapTooltip:Show()
  940. end
  941.  
  942. function GameTooltip_Minimap_hook()
  943.   if not settings.map or
  944.      not GameTooltip:IsShown() or
  945.      GameTooltip:GetOwner() ~= Minimap or
  946.      GameTooltip:NumLines() ~= 1 then return end
  947.   local otext = GameTooltipTextLeft1 and GameTooltipTextLeft1:GetText()
  948.   if not otext or #otext == 0 then return end
  949.   local text = "\n"..otext.."\n"
  950.   text = text:gsub("(\124TInterface\\Minimap[^\124]+\124t)","%1\n\n") -- ignore up/down arrow textures
  951.   for name in string.gmatch(text,"[^\n]+") do
  952.     if not name:find("\124") then
  953.       debug("GameTooltip_Minimap_hook:"..name)
  954.       local toon = addon:formatToon(strtrim(name), true, true)
  955.       --text = text:gsub("\n"..name.."\n", "\n"..toon.."\n")
  956.       local s,e = text:find("\n"..name.."\n",1,true) -- avoid gsub to prevent problems with special chars
  957.       if s and e then
  958.         text = text:sub(1,s-1).."\n"..toon.."\n"..text:sub(e+1)
  959.       end
  960.     end
  961.   end
  962.   text = strtrim(text)
  963.   text = text:gsub("\n\n","")
  964.   if text ~= otext then
  965.     GameTooltip:SetText(text)
  966.     GameTooltip:Show()
  967.   end
  968. end
  969.  
  970. local GetColoredName_orig
  971. local function GetColoredName_hook(event, arg1, arg2, ...)
  972.   local ret = GetColoredName_orig(event, arg1, arg2, ...)
  973.   if chats[event] and settings.chat then
  974.     local role = UnitGroupRolesAssigned(arg2)
  975.     if role == "NONE" and arg2:match(" *- *"..GetRealmName().."$") then -- ticket 20: ambiguate local toons
  976.       role = UnitGroupRolesAssigned(arg2:gsub(" *-[^-]+$",""))
  977.     end
  978.     if (role and role ~= "NONE") then
  979.         ret = getRoleTex(role,0)..""..ret
  980.     end
  981.   end
  982.   return ret
  983. end
  984.  
  985. local function UpdateTarget(frame)
  986.   local Frame = frame:gsub("^(.)",string.upper)
  987.   addon.frametex = addon.frametex or {}
  988.   local tex = addon.frametex[frame]
  989.   if tex then tex:Hide() end
  990.   if not settings[frame] or not UnitIsPlayer(frame) or not _G[Frame.."Frame"]:IsVisible() then return end
  991.   local role = UnitGroupRolesAssigned(frame)
  992.   if role == "NONE" then return end
  993.   if not tex then
  994.     tex = _G[Frame.."FrameTextureFrame"]:CreateTexture(addonName..Frame.."FrameRole","OVERLAY")
  995.     tex:ClearAllPoints()
  996.     tex:SetPoint("BOTTOMLEFT", _G[Frame.."FrameTextureFrameName"], "TOPRIGHT",0,-8)
  997.     tex:SetTexture(role_tex_file)
  998.     tex:SetSize(20,20)
  999.     addon.frametex[frame] = tex
  1000.   end
  1001.   tex:SetTexCoord(getRoleTexCoord(role))
  1002.   tex:Show()
  1003. end
  1004.  
  1005. local reg = {}
  1006. local function RegisterHooks()
  1007.   if not settings then return end
  1008.   if settings.raid and RaidGroupFrame_Update and not reg["rgb"] then
  1009.     debug("Registering RaidGroupFrame_Update")
  1010.     hooksecurefunc("RaidGroupFrame_Update",UpdateRGF)
  1011.     hooksecurefunc("RaidGroupFrame_UpdateLevel",UpdateRGF)
  1012.     reg["rgb"] = true
  1013.   end
  1014.   if settings.raid and RaidInfoFrame and not reg["rif"] then
  1015.     debug("Registering RaidInfoframe")
  1016.     hooksecurefunc(RaidInfoFrame,"Show",UpdateRGF)
  1017.     hooksecurefunc(RaidInfoFrame,"Hide",UpdateRGF)
  1018.     reg["rif"] = true
  1019.   end
  1020.   if settings.tooltip and GameTooltip and not reg["gtt"] then
  1021.     debug("Registering GameTooltip")
  1022.     TooltipDataProcessor.AddTooltipPostCall(Enum.TooltipDataType.Unit, function(tooltip, data) UpdateTT(tooltip) end)
  1023.     hooksecurefunc(GameTooltipTextLeft1,"SetFormattedText", function() UpdateTT(GameTooltip) end)
  1024.     hooksecurefunc(GameTooltipTextLeft1,"SetText", function() UpdateTT(GameTooltip) end)
  1025.     reg["gtt"] = true
  1026.   end
  1027.   if settings.tooltip and HealBot_Action_RefreshTooltip and not reg["hb"] then
  1028.     hooksecurefunc("HealBot_Action_RefreshTooltip", function() UpdateHBTT(HealBot_Data and HealBot_Data["TIPUNIT"]) end)
  1029.     reg["hb"] = true
  1030.   end
  1031.   if settings.hbicon and not reg["hbicon"]
  1032.      and HealBot_Action_RefreshButton
  1033.      and HealBot_Action_PositionButton and HealBot_RaidTargetUpdate
  1034.      and HealBot_OnEvent_ReadyCheckUpdate
  1035.      then
  1036.        hooksecurefunc("HealBot_Action_PositionButton", HBIconHook)
  1037.        hooksecurefunc("HealBot_RaidTargetUpdate", HBIconHook)
  1038.        hooksecurefunc("HealBot_OnEvent_ReadyCheckUpdate", function(unit)
  1039.          debug("HealBot_OnEvent_ReadyCheckUpdate "..(unit or "nil"))
  1040.          HBIconHook(unit and HealBot_Unit_Button and HealBot_Unit_Button[unit])
  1041.        end)
  1042.     hooksecurefunc("HealBot_Action_RefreshButton", HBIconHook)
  1043.     reg["hbicon"] = true
  1044.   end
  1045.   -- if settings.tooltip and VUHDO_showTooltip and VUHDO_GLOBAL and VUHDO_GLOBAL["VUHDO_showTooltip"] and not reg["vh"] then
  1046.   if settings.tooltip and VUHDO_updateTooltip and not reg["vh"] then
  1047.     hooksecurefunc("VUHDO_updateTooltip", VuhdoHook)
  1048.     reg["vh"] = true
  1049.   end
  1050.   if false and settings.raid and not reg["upm"] then
  1051.      -- add the set role menu to the raid screen popup CAUSES TAINT
  1052.      table.insert(UnitPopupMenus["RAID"],1,"SELECT_ROLE")
  1053.      reg["upm"] = true
  1054.   end
  1055.   if false and settings.chat and not reg["chats"] then
  1056.      for c,_ in pairs(chats) do
  1057.        ChatFrame_AddMessageEventFilter(c, ChatFilter)
  1058.      end
  1059.      reg["chats"] = true
  1060.   end
  1061.   if settings.chat and Prat and not reg["prat"] then
  1062.      hooksecurefunc(Prat,"SplitChatMessage",PratFilter)
  1063.      reg["prat"] = true
  1064.   end
  1065.   if settings.chat and GetColoredName and not reg["gcn"] then
  1066.      GetColoredName_orig = _G.GetColoredName
  1067.      _G.GetColoredName = GetColoredName_hook
  1068.      reg["gcn"] = true
  1069.   end
  1070.   if settings.system and not reg["syschats"] then
  1071.      ChatFrame_AddMessageEventFilter("CHAT_MSG_SYSTEM", SystemMessageFilter)
  1072.      ChatFrame_AddMessageEventFilter("CHAT_MSG_TARGETICONS", SystemMessageFilter)
  1073.      reg["syschats"] = true
  1074.   end
  1075.   if settings.system and RoleChangedFrame and not reg["rolechange"] then
  1076.      RoleChangedFrame:SetScript("OnEvent", RoleChangedFrame_OnEvent_hook)
  1077.      reg["rolechange"] = true
  1078.   end
  1079.   if settings.popup and UnitPopup_ShowMenu and not reg["popup"] then
  1080.      hooksecurefunc("UnitPopup_ShowMenu", UnitPopup_hook)
  1081.      reg["popup"] = true
  1082.   end
  1083.   if settings.map and WorldMapUnit_OnEnter and not reg["map"] then
  1084.      hooksecurefunc("WorldMapUnit_OnEnter", WorldMapUnit_OnEnter_hook)
  1085.  
  1086.      -- minimap tooltips are set and shown from C code and cannot be directly hooked
  1087.      -- intead we hook the events they trigger
  1088.      GameTooltip:HookScript("OnShow", GameTooltip_Minimap_hook)
  1089.      GameTooltip:HookScript("OnSizeChanged", GameTooltip_Minimap_hook)
  1090.      reg["map"] = true
  1091.   end
  1092.   if settings.classbuttons and not addon.classbuttons
  1093.       and RaidClassButton1 then -- for RaidClassButtonTemplate
  1094.       addon.classbuttons = {}
  1095.       local function rcb_onenter(self)
  1096.     local class = self.class
  1097.     local lclass = class and LC[class]
  1098.     GameTooltip:SetOwner(self)
  1099.         TTframe = self
  1100.         TTfunc = rcb_onenter
  1101.     if class and lclass then
  1102.       local list, cnt = toonList(nil, class)
  1103.       GameTooltip:ClearLines()
  1104.       GameTooltip:SetText(classColor(lclass,class).." ("..cnt..")")
  1105.       GameTooltip:AddLine(list,1,1,1,true)
  1106.       GameTooltip:Show()
  1107.     end
  1108.     GameTooltip:ClearAllPoints()
  1109.     GameTooltip:SetPoint("BOTTOMLEFT",self,"TOPRIGHT")
  1110.       end
  1111.       for i = 1,MAX_CLASSES do -- squeeze layout to make everything fit
  1112.         local rcb = CreateFrame("Button", addonName.."RaidClassButton"..i, RaidFrame, "RaidClassButtonTemplate")
  1113.     addon.classbuttons[i] = rcb
  1114.     rcb:SetSize(20,20)
  1115.     rcb:SetScript("OnEnter",rcb_onenter)
  1116.     local bkg = rcb:GetRegions() -- background graphic is off-center and needs to be slid up
  1117.     bkg:ClearAllPoints()
  1118.     --bkg:SetPoint("TOPLEFT",0,7)
  1119.     bkg:SetPoint("TOPLEFT",-2,7)
  1120.     -- more init fixups
  1121.     rcb.class = CLASS_SORT_ORDER[i]
  1122.     local icon = _G[rcb:GetName().."IconTexture"]
  1123.     icon:SetAllPoints()
  1124.     icon:SetTexture("Interface\\Glues\\CharacterCreate\\UI-CharacterCreate-Classes");
  1125.         icon:SetTexCoord(unpack(CLASS_ICON_TCOORDS[rcb.class]))
  1126.       end
  1127.       local lastrcb = addon.classbuttons[MAX_CLASSES]
  1128.       lastrcb:ClearAllPoints()
  1129.       lastrcb:SetPoint("BOTTOMLEFT",RaidFrame,"BOTTOMRIGHT",1,10)
  1130.       for i = MAX_CLASSES-1,1,-1 do -- squeeze layout to make everything fit
  1131.         local rcb = addon.classbuttons[i]
  1132.         rcb:ClearAllPoints()
  1133.         rcb:SetPoint("BOTTOM",lastrcb,"TOP",0,6) -- spacing
  1134.     rcb:Show()
  1135.         lastrcb = rcb
  1136.       end
  1137.   end
  1138.   if settings.rolebuttons and not addon.rolebuttons
  1139.      and RaidClassButton1 then -- for RaidClassButtonTemplate
  1140.     addon.rolebuttons = {}
  1141.     local last
  1142.     for idx,role in ipairs({"TANK","HEALER","DAMAGER"}) do
  1143.       local btn = CreateFrame("Button", addonName.."RoleButton"..role, RaidFrame, "RaidClassButtonTemplate")
  1144.       btn:SetFrameLevel(RaidFrame:GetFrameLevel()+5-idx)
  1145.       local icon = _G[btn:GetName().."IconTexture"];
  1146.       if false then -- low-res
  1147.         icon:SetTexture(role_tex_file)
  1148.         icon:SetTexCoord(getRoleTexCoord(role))
  1149.       else -- hi-res
  1150.         icon:SetTexture("Interface\\LFGFrame\\UI-LFG-ICON-ROLES")
  1151.         --! Fix copied from https://github.com/WeakAuras/WeakAuras2/pull/5039/files
  1152.         if role == "TANK" then
  1153.           icon:SetTexCoord(GetTexCoordsByGrid(1, 2, 256, 256, 67, 67))
  1154.         elseif role == "HEALER" then
  1155.           icon:SetTexCoord(GetTexCoordsByGrid(2, 1, 256, 256, 67, 67))
  1156.         else -- role == "DAMAGER"
  1157.           icon:SetTexCoord(GetTexCoordsByGrid(2, 2, 256, 256, 67, 67))
  1158.         end
  1159.       end
  1160.       icon:SetAllPoints()
  1161.       btn:SetScript("OnLoad",function(self) end)
  1162.       local function ttfn(self)
  1163.         --GameTooltip_SetDefaultAnchor(GameTooltip, UIParent);
  1164.     GameTooltip:SetOwner(self)
  1165.         TTframe = self
  1166.         TTfunc = ttfn
  1167.     GameTooltip:SetAnchorType("ANCHOR_RIGHT")
  1168.         GameTooltip:SetText(getRoleTex(role).._G[role] .. " ("..(btn.rolecnt or 0)..")")
  1169.         GameTooltip:AddLine(toonList(role),1,1,1,true)
  1170.     GameTooltip:Show()
  1171.       end
  1172.       btn:SetScript("OnEnter",ttfn)
  1173.       btn:SetScript("OnUpdate",function(self) self:SetFrameLevel(500) end) -- prevent adjacent panel edge from obscuring
  1174.       btn:SetScript("OnLeave",function() TTframe = nil; GameTooltip:Hide() end)
  1175.       local bkg = btn:GetRegions() -- background graphic is off-center and needs to be slid up
  1176.       bkg:ClearAllPoints()
  1177.       bkg:SetPoint("TOPLEFT",-2,8)
  1178.       btn:ClearAllPoints()
  1179.       if last then
  1180.         btn:SetPoint("TOPLEFT", last, "BOTTOMLEFT",0,-4)
  1181.       end
  1182.       btn:SetSize(20,20)
  1183.       btn:SetScale(1.6)
  1184.       btn:Show()
  1185.       addon.rolebuttons[role] = btn
  1186.       last = btn
  1187.     end
  1188.     addon.rolebuttons["TANK"]:SetPoint("TOPLEFT",FriendsFrameCloseButton,"BOTTOMRIGHT",-1,8)
  1189.   end
  1190.   if RolePollPopup and not reg["rpp"] then
  1191.      addon.rppevent = RolePollPopup:GetScript("OnEvent")
  1192.      if addon.rppevent then
  1193.        RolePollPopup:SetScript("OnEvent", function(self, event, ...)
  1194.          if settings.autorole and
  1195.         UnitGroupRolesAssigned("player") ~= "NONE" and
  1196.         event == "ROLE_POLL_BEGIN" then
  1197.        debug("suppressed a RolePollPopup")
  1198.      else
  1199.        addon.rppevent(self, event, ...)
  1200.      end
  1201.        end)
  1202.        reg["rpp"] = true
  1203.      end
  1204.   end
  1205. end
  1206.  
  1207. local function OnEvent(frame, event, name, ...)
  1208.   if event == "ADDON_LOADED" and string.upper(name) == string.upper(addonName) then
  1209.      debug("ADDON_LOADED: "..name)
  1210.      RoleIconsDB = RoleIconsDB or {}
  1211.      settings = RoleIconsDB
  1212.      for k,v in pairs(defaults) do
  1213.        if settings[k] == nil then
  1214.          settings[k] = defaults[k][1]
  1215.        end
  1216.      end
  1217.      settings.state = settings.state or {}
  1218.      addon:SetupVersion()
  1219.      RegisterHooks()
  1220.      addon:UpdateServers()
  1221.   elseif event == "ADDON_LOADED" then
  1222.      debug("ADDON_LOADED: "..name)
  1223.      RegisterHooks()
  1224.   elseif event == "PLAYER_TARGET_CHANGED" then
  1225.      UpdateTarget("target")
  1226.   elseif event == "PLAYER_FOCUS_CHANGED" then
  1227.      UpdateTarget("focus")
  1228.   elseif event == "ROLE_POLL_BEGIN" or
  1229.          event == "GROUP_ROSTER_UPDATE" or
  1230.      event == "ACTIVE_TALENT_GROUP_CHANGED" or
  1231.      event == "PLAYER_REGEN_ENABLED" then
  1232.      UpdateTarget("target")
  1233.      UpdateTarget("focus")
  1234.      if settings.autorole and not InCombatLockdown() then
  1235.        local currrole = UnitGroupRolesAssigned("player")
  1236.        if (currrole == "NONE" and event ~= "ACTIVE_TALENT_GROUP_CHANGED") or
  1237.           (currrole ~= "NONE" and event == "ACTIVE_TALENT_GROUP_CHANGED") then
  1238.          local role = myDefaultRole()
  1239.      if role and role ~= "NONE" then
  1240.            debug(event.." setting "..role)
  1241.            UnitSetRole("player", role)
  1242.            --RolePollPopup:Hide()
  1243.            StaticPopupSpecial_Hide(RolePollPopup) -- ticket 4
  1244.          end
  1245.        end
  1246.      end
  1247.   end
  1248.   if event == "PARTY_INVITE_REQUEST" then
  1249.     addon.inviteleader = name
  1250.     debug("PARTY_INVITE_REQUEST: "..tostring(addon.inviteleader))
  1251.   end
  1252.   if event == "GROUP_JOINED" then
  1253.     debug("GROUP_JOINED")
  1254.     addon:UpdateServers(false, true)
  1255.   end
  1256.   if event == "GROUP_ROSTER_UPDATE" then
  1257.     addon:UpdateServers()
  1258.   end
  1259. end
  1260. frame:SetScript("OnEvent", OnEvent);
  1261.  
  1262. SLASH_ROLEICONS1 = L["/ri"]
  1263. SlashCmdList["ROLEICONS"] = function(msg)
  1264.         local cmd = msg:lower()
  1265.     if cmd == "check" then
  1266.       InitiateRolePoll()
  1267.         elseif type(settings[cmd]) == "boolean" then
  1268.           settings[cmd] = not settings[cmd]
  1269.           chatMsg(cmd..L[" set to "]..(settings[cmd] and YES or NO))
  1270.       RegisterHooks()
  1271.       UpdateRGF()
  1272.         else
  1273.       local usage = ""
  1274.           chatMsg(LaddonName.." "..addon.version)
  1275.       for c,_ in pairs(defaults) do
  1276.         usage = usage.." | "..c
  1277.       end
  1278.           chatMsg(SLASH_ROLEICONS1.." [ check"..usage.." ]")
  1279.       chatMsg("  "..SLASH_ROLEICONS1.." check  - "..L["Perform a role check (requires assist or leader)"])
  1280.       for c,v in pairs(defaults) do
  1281.         chatMsg("  "..SLASH_ROLEICONS1.." "..c.."  ["..
  1282.           (settings[c] and "|cff00ff00"..YES or "|cffff0000"..NO).."|r] "..v[2])
  1283.       end
  1284.         end
  1285. end
  1286. SLASH_ROLECHECK1 = L["/rolecheck"]
  1287. SlashCmdList["ROLECHECK"] = function(msg) InitiateRolePoll() end
  1288.  
  1289. function addon:SetupVersion()
  1290.    local svnrev = 0
  1291.    local T_svnrev = vars.svnrev
  1292.    T_svnrev["X-Build"] = tonumber((C_AddOns.GetAddOnMetadata(addonName, "X-Build") or ""):match("%d+"))
  1293.    T_svnrev["X-Revision"] = tonumber((C_AddOns.GetAddOnMetadata(addonName, "X-Revision") or ""):match("%d+"))
  1294.    for _,v in pairs(T_svnrev) do -- determine highest file revision
  1295.      if v and v > svnrev then
  1296.        svnrev = v
  1297.      end
  1298.    end
  1299.    addon.revision = svnrev
  1300.  
  1301.    T_svnrev["X-Curse-Packaged-Version"] = C_AddOns.GetAddOnMetadata(addonName, "X-Curse-Packaged-Version")
  1302.    T_svnrev["Version"] = C_AddOns.GetAddOnMetadata(addonName, "Version")
  1303.    addon.version = T_svnrev["X-Curse-Packaged-Version"] or T_svnrev["Version"] or "@"
  1304.    if string.find(addon.version, "@") then -- dev copy uses "@.project-version.@"
  1305.       addon.version = "r"..svnrev
  1306.    end
  1307. end
  1308.  
  1309.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement