Mercurial > wow > crossrealmassist
comparison CrossRealmAssist.lua @ 3:5e23a92dcde1
Layout once again
| author | ShadowTheAge |
|---|---|
| date | Wed, 04 Mar 2015 22:07:43 +0300 |
| parents | 9fa8442dd547 |
| children | 421508c17712 |
comparison
equal
deleted
inserted
replaced
| 2:c73a7706861e | 3:5e23a92dcde1 |
|---|---|
| 1 CrossRealmAssist = LibStub("AceAddon-3.0"):NewAddon("CrossRealmAssist", "AceEvent-3.0", "AceConsole-3.0", "AceTimer-3.0") | |
| 2 local AceGUI = LibStub("AceGUI-3.0") | |
| 3 local addon = CrossRealmAssist; | |
| 4 local wholib = LibStub:GetLibrary('LibWho-2.0'):Library() | |
| 5 | |
| 6 local hgroup, lfgContainer, currealm, partyrealm, recrealm, curRealmStat, homeRealm, realmSep, gui, leavebtn | |
| 7 | |
| 8 local scanstate=0 | |
| 9 local recentRealms={} | |
| 10 local active=false | |
| 11 | |
| 12 local lfgGroups={ | |
| 13 6, -- Custom | |
| 14 10, -- Ashran | |
| 15 1, -- Quests | |
| 16 3, -- Raids | |
| 17 8 -- BGs | |
| 18 } | |
| 19 | |
| 20 local lfgTabs | |
| 21 local curLfgGroup | |
| 22 local sheduledScan | |
| 23 local wasInGroup | |
| 24 local lfgScanInProgress=false | |
| 25 | |
| 26 function addon:OnInitialize() | |
| 27 | |
| 28 end | |
| 29 | |
| 30 function addon:OnEnable() | |
| 31 local tabCount = table.getn(lfgGroups) | |
| 32 lfgTabs = {} | |
| 33 for i=1,tabCount do | |
| 34 local cat = lfgGroups[i] | |
| 35 table.insert(lfgTabs,{value=cat,text=(C_LFGList.GetCategoryInfo(cat))}) | |
| 36 end | |
| 37 realmSep = _G.REALM_SEPARATORS | |
| 38 homeRealm = GetRealmName() | |
| 39 addon:RegisterChatCommand("cra", "Activate") | |
| 40 addon:RegisterChatCommand("crossrealmassist", "Activate") | |
| 41 end | |
| 42 | |
| 43 function addon:OnDisable() | |
| 44 addon:Deactivate() | |
| 45 end | |
| 46 | |
| 47 function addon:Activate() | |
| 48 if active then return end | |
| 49 active=true | |
| 50 wasInGroup = IsInGroup() | |
| 51 addon:CreateUI() | |
| 52 addon:ScanRealm() | |
| 53 addon:RegisterEvent("ZONE_CHANGED_NEW_AREA", "SheduleScan") | |
| 54 addon:RegisterEvent("LFG_LIST_SEARCH_RESULTS_RECEIVED", "LfgResponseData") | |
| 55 addon:RegisterEvent("LFG_LIST_SEARCH_FAILED", "LfgScanFailed") | |
| 56 addon:RegisterEvent("GROUP_ROSTER_UPDATE", "updatePartyInfo") | |
| 57 addon:RegisterEvent("LFG_LIST_APPLICATION_STATUS_UPDATED", "updateAppStatus") | |
| 58 end | |
| 59 | |
| 60 function addon:Deactivate() | |
| 61 if not active then return end | |
| 62 active = false | |
| 63 scanstate = 0 | |
| 64 lfgScanInProgress = false | |
| 65 gui:Release(); | |
| 66 gui, lfgContainer, hgroup, currealm, partyrealm, recrealm, leavebtn = nil,nil,nil,nil,nil,nil,nil | |
| 67 addon:UnregisterEvent("ZONE_CHANGED_NEW_AREA") | |
| 68 addon:UnregisterEvent("PLAYER_REGEN_ENABLED") | |
| 69 addon:UnregisterEvent("LFG_LIST_SEARCH_RESULTS_RECEIVED") | |
| 70 addon:UnregisterEvent("LFG_LIST_SEARCH_FAILED") | |
| 71 addon:UnregisterEvent("GROUP_ROSTER_UPDATE") | |
| 72 addon:UnregisterEvent("LFG_LIST_APPLICATION_STATUS_UPDATED") | |
| 73 end | |
| 74 | |
| 75 function addon:CreateUI() | |
| 76 gui = addon:AddUI(nil,"Window",{SetTitle="Cross Realm Assist",EnableResize=false,SetLayout="Flow"},true,{OnClose=addon.Deactivate}) | |
| 77 | |
| 78 local tabgroup = addon:AddUI(gui,"TabGroup",{SetFullHeight=true,SetTabs=lfgTabs,SetLayout="Fill"},true,{OnGroupSelected=addon.LfgScan}) | |
| 79 tabgroup:SelectTab(lfgGroups[1]) | |
| 80 lfgContainer = addon:AddUI(tabgroup,"ScrollFrame") | |
| 81 | |
| 82 local hgroupc = addon:AddUI(gui,"InlineGroup",{SetLayout="Fill",SetFullHeight=true,SetTitle="Realm info"},true) | |
| 83 hgroup = addon:AddUI(hgroupc,"ScrollFrame") | |
| 84 | |
| 85 addon:AddUI(hgroup,"Heading",{SetText="Current realm"}) | |
| 86 currealm = addon:AddUI(hgroup,"SimpleGroup"); | |
| 87 addon:AddUI(hgroup,"Button",{SetText="Refresh",SetRelativeWidth=0.5,SetHeight=20},true,{OnClick=addon.ScanRealm}) | |
| 88 | |
| 89 addon:AddUI(hgroup,"Heading",{SetText="Party statistics"}) | |
| 90 partyrealm = addon:AddUI(hgroup,"SimpleGroup"); | |
| 91 leavebtn = addon:AddUI(hgroup,"Button",{SetText="Leave",SetRelativeWidth=0.5,SetHeight=20,SetDisabled=not IsInGroup()},true,{OnClick=LeaveParty}) | |
| 92 | |
| 93 addon:AddUI(hgroup,"Heading",{SetText="Recent realms"}) | |
| 94 recrealm = addon:AddUI(hgroup,"SimpleGroup"); | |
| 95 addon:AddUI(hgroup,"Button",{SetText="Clear",SetRelativeWidth=0.5,SetHeight=20},true,{OnClick=addon.ClearRecentRealms}) | |
| 96 | |
| 97 gui:DoLayout() | |
| 98 gui:PauseLayout() | |
| 99 | |
| 100 tabgroup:SetPoint("BOTTOMRIGHT", gui.content, "BOTTOMRIGHT", -200, 0) | |
| 101 hgroupc:SetPoint("TOPLEFT", gui.content, "TOPRIGHT", -200, 0) | |
| 102 hgroupc:SetPoint("BOTTOMRIGHT", gui.content, "BOTTOMRIGHT") | |
| 103 | |
| 104 if curRealmStat then addon:updateCurrentRealm() end | |
| 105 addon:updatePartyInfo() | |
| 106 addon:updateRecentRealms() | |
| 107 end | |
| 108 | |
| 109 -- LFG scanning routine | |
| 110 | |
| 111 function addon.refreshLfgCurrent() | |
| 112 addon.LfgScan(nil,nil,curLfgGroup) | |
| 113 end | |
| 114 | |
| 115 function addon.LfgScan(widget, callback, tab) | |
| 116 if not tab then return end | |
| 117 lfgScanInProgress = true | |
| 118 if lfgContainer then lfgContainer:ReleaseChildren() end | |
| 119 curLfgGroup = tab | |
| 120 C_LFGList.Search(curLfgGroup,"") | |
| 121 end | |
| 122 | |
| 123 function addon:LfgScanFailed(event, reason) | |
| 124 print("scan failed "..reason) | |
| 125 if reason == "throttled" then | |
| 126 addon:ScheduleTimer(addon.LfgScan, 2, nil, nil, curLfgGroup) | |
| 127 end | |
| 128 end | |
| 129 | |
| 130 local function WeightLfgItem(item) | |
| 131 local leaderRealm = 3 | |
| 132 if (item.realm ~= "???") then leaderRealm = (5 - (recentRealms[item.realm] or 0)) end | |
| 133 local weight = | |
| 134 (item.autoinv and 5 or 2) * | |
| 135 ((item.friends > 0) and 2 or 3) * | |
| 136 ((item.ilvl > 0) and 2 or 3) * | |
| 137 ((item.voice ~= "") and 2 or 4) | |
| 138 local count = item.pcount; | |
| 139 local countWeight | |
| 140 if count >= 39 or count <= 1 then countWeight = 1 | |
| 141 elseif count >= 35 or count <= 4 then countWeight = 2 | |
| 142 elseif count >= 30 or count <= 10 then countWeight = 3 | |
| 143 else countWeight = 4 end | |
| 144 return weight * leaderRealm * countWeight; | |
| 145 end | |
| 146 | |
| 147 function addon:LfgResponseData() | |
| 148 lfgScanInProgress = false; | |
| 149 addon:UpdateCurrentLfgInfo() | |
| 150 end | |
| 151 | |
| 152 function addon:UpdateCurrentLfgInfo(repeated) | |
| 153 if lfgScanInProgress then return end | |
| 154 local count, list = C_LFGList.GetSearchResults() | |
| 155 local lfgList = {} | |
| 156 local lfgEntries = 0 | |
| 157 local hasUnknowns = false | |
| 158 for i = 1,count do | |
| 159 local rid = list[i]; | |
| 160 if not rid then break end | |
| 161 local _, action, caption, desc, voice, ilvl, time, bnetfr, charfr, guild, delisted, fullname, pcount, autoinv = C_LFGList.GetSearchResultInfo(rid) | |
| 162 if not fullname then | |
| 163 fullname = "???-???" | |
| 164 hasUnknowns = true | |
| 165 end | |
| 166 if not delisted then | |
| 167 local pname, realm = strsplit(realmSep, fullname) | |
| 168 realm = realm or homeRealm | |
| 169 local item = { | |
| 170 rid=rid, | |
| 171 action=action, | |
| 172 caption=caption, | |
| 173 desc=desc, | |
| 174 ilvl=ilvl, | |
| 175 voice=voice, | |
| 176 time=time, | |
| 177 friends=bnetfr+charfr+guild, | |
| 178 name=pname, | |
| 179 realm = realm, | |
| 180 pcount=pcount, | |
| 181 autoinv=autoinv | |
| 182 } | |
| 183 item.weight = WeightLfgItem(item) | |
| 184 table.insert(lfgList, item) | |
| 185 lfgEntries = lfgEntries + 1 | |
| 186 end | |
| 187 end | |
| 188 addon:refreshLFGList(lfgList, lfgEntries); | |
| 189 if hasUnknowns and repeated ~= true then addon:ScheduleTimer("UpdateCurrentLfgInfo", 1, true) end | |
| 190 end | |
| 191 | |
| 192 function addon:refreshLFGList(list, count) | |
| 193 if not lfgContainer then return end | |
| 194 lfgContainer:PauseLayout() | |
| 195 lfgContainer:ReleaseChildren() | |
| 196 table.sort(list, addon.SortLfgItems) | |
| 197 | |
| 198 local canJoin = addon.canJoinGroup() | |
| 199 | |
| 200 for i=1,count do | |
| 201 local data = list[i]; | |
| 202 local renderer = addon:AddUI(lfgContainer,"SimpleGroup",{SetLayout="Flow",SetHeight=20,SetAutoAdjustHeight=false}); | |
| 203 | |
| 204 addon:AddUI(renderer,"Label",{SetText=data.caption,SetRelativeWidth=0.4},true) | |
| 205 addon:AddUI(renderer,"Label",{SetText=data.pcount,SetRelativeWidth=0.05},true) | |
| 206 local realm = addon:AddUI(renderer,"Label",{SetText=data.realm,SetRelativeWidth=0.25},true) | |
| 207 if recentRealms[data.realm] then | |
| 208 local r,g,b = GetItemQualityColor(recentRealms[data.realm]); | |
| 209 realm:SetColor(r,g,b) | |
| 210 end | |
| 211 | |
| 212 local tooltip = {} | |
| 213 if data.voice ~= "" then table.insert(tooltip,"Voice: "..data.voice) end | |
| 214 if data.friends > 0 then table.insert(tooltip,"Friends: "..data.friends) end | |
| 215 if data.ilvl > 0 then table.insert(tooltip,"Min. Ilvl: "..data.ilvl) end | |
| 216 addon:AddIcon(renderer, "Interface/GossipFrame/ActiveQuestIcon", 16, tooltip[1] ~= nil, tooltip) | |
| 217 addon:AddIcon(renderer, READY_CHECK_READY_TEXTURE, 16, data.autoinv, {"Auto invite!"}) | |
| 218 local btn = addon:AddUI(renderer,"Button",{SetText="Join",SetRelativeWidth=0.2,SetHeight=20,SetDisabled=not canJoin},true,{OnClick=addon.joinGroup}) | |
| 219 btn:SetUserData('rid',data.rid) | |
| 220 end | |
| 221 addon:AddUI(lfgContainer,"Button",{SetText="Refresh",SetWidth=100,SetHeight=20},true,{OnClick=addon.refreshLfgCurrent}) | |
| 222 | |
| 223 lfgContainer:ResumeLayout() | |
| 224 lfgContainer:DoLayout() | |
| 225 end | |
| 226 | |
| 227 function addon.SortLfgItems(a,b) | |
| 228 return a.weight > b.weight | |
| 229 end | |
| 230 | |
| 231 function addon.canJoinGroup() | |
| 232 return (not IsInGroup()) or (UnitIsGroupLeader('player') and not IsInRaid()) | |
| 233 end | |
| 234 | |
| 235 function addon.joinGroup(widget) | |
| 236 if not addon.canJoinGroup() then return end | |
| 237 local rid = widget:GetUserData('rid'); | |
| 238 widget:SetDisabled(true) | |
| 239 C_LFGList.ApplyToGroup(rid, "", C_LFGList.GetAvailableRoles()) | |
| 240 end | |
| 241 | |
| 242 function addon:updateAppStatus(event, id, status, oldstatus) | |
| 243 if status == "invited" then | |
| 244 LFGListInviteDialog_Accept(LFGListInviteDialog) | |
| 245 end | |
| 246 end | |
| 247 | |
| 248 -- Zone scanning routine | |
| 249 | |
| 250 function addon:LeaveCombat() | |
| 251 addon:UnregisterEvent("PLAYER_REGEN_ENABLED") | |
| 252 scanstate = 0 | |
| 253 addon:ScanRealm(); | |
| 254 end | |
| 255 | |
| 256 function addon:ScanRealm() | |
| 257 if scanstate > 0 then return end | |
| 258 if InCombatLockdown() then | |
| 259 addon:RegisterEvent("PLAYER_REGEN_ENABLED","LeaveCombat") | |
| 260 scanstate = 1 | |
| 261 addon:AddLabel(currealm, "Scan sheduled after combat...", 3); | |
| 262 return | |
| 263 end | |
| 264 scanstate = 2 | |
| 265 addon:AddLabel(currealm, "Scanning...", 3); | |
| 266 local searchString = _G.WHO_TAG_ZONE .. '"' .. GetZoneText() .. '"'; | |
| 267 wholib:Who(searchString, {callback = "ScanResult", handler=addon}) | |
| 268 end | |
| 269 | |
| 270 function addon:ScanResult(query, results, complete) | |
| 271 local realms = {} | |
| 272 for _, player in ipairs(results) do | |
| 273 addon:AddRealmStat(player.Name, realms); | |
| 274 end | |
| 275 curRealmStat = addon:GetRealmStat(realms); | |
| 276 addon:updateCurrentRealm(); | |
| 277 local rescan = scanstate == 3 | |
| 278 scanstate = 0; | |
| 279 if rescan then addon:ScanRealm() end | |
| 280 end | |
| 281 | |
| 282 function addon:AddRealmStat(name, realms) | |
| 283 local _, realm = strsplit(realmSep, name) -- TODO exclude self | |
| 284 realm = realm or homeRealm | |
| 285 realms[realm] = (realms[realm] or 0) + 1 | |
| 286 end | |
| 287 | |
| 288 function addon:AddUnitIdStat(unitid, realms) | |
| 289 local name, realm = UnitName(unitid); | |
| 290 if not name then return end | |
| 291 if realm == "" then realm = homeRealm end | |
| 292 realm = realm or homeRealm | |
| 293 realms[realm] = (realms[realm] or 0) + 1 | |
| 294 end | |
| 295 | |
| 296 function addon:GetRealmStat(realms) | |
| 297 local rcount = 0; | |
| 298 local stat = {}; | |
| 299 local pcount = 0; | |
| 300 for realm,count in pairs(realms) do | |
| 301 table.insert(stat,{realm=realm,count=count}) | |
| 302 rcount = rcount + 1; | |
| 303 pcount = pcount + count; | |
| 304 end | |
| 305 if rcount > 1 then | |
| 306 table.sort(stat, function(a,b) return a.count > b.count end) | |
| 307 stat.max = stat[1].count | |
| 308 else stat.max = 0 end | |
| 309 stat.players = pcount; | |
| 310 stat.realms = rcount; | |
| 311 return stat; | |
| 312 end | |
| 313 | |
| 314 function addon:updateCurrentRealm() | |
| 315 if not currealm then return end | |
| 316 currealm:ReleaseChildren() | |
| 317 if curRealmStat.players < 5 then | |
| 318 addon:AddLabel(currealm, "Not enough players", 0); | |
| 319 end | |
| 320 local sureplayers = curRealmStat.players/2 | |
| 321 local maybeplayers = math.max(curRealmStat.max/4, 3) | |
| 322 local recentRealmUpdated = false | |
| 323 for i=1,curRealmStat.realms do | |
| 324 local data = curRealmStat[i] | |
| 325 if data.count >= maybeplayers then | |
| 326 local realm = data.realm | |
| 327 local rvalue = (data.count >= sureplayers) and 4 or 2 | |
| 328 if (rvalue > (recentRealms[realm] or 0)) then | |
| 329 recentRealmUpdated = true | |
| 330 recentRealms[realm] = rvalue | |
| 331 end | |
| 332 end | |
| 333 end | |
| 334 addon:addLabels(currealm, curRealmStat, sureplayers, maybeplayers, 3, "Realm unknown"); | |
| 335 if recentRealmUpdated then | |
| 336 addon:UpdateCurrentLfgInfo() | |
| 337 addon:updateRecentRealms() | |
| 338 end | |
| 339 end | |
| 340 | |
| 341 function addon:addLabels(container, stats, epict, whitet, grayt, emptytext) | |
| 342 if stats.realms == 0 then | |
| 343 addon:AddLabel(container, emptytext) | |
| 344 else | |
| 345 local players = stats.players | |
| 346 local pleft = players; | |
| 347 for i=1,stats.realms do | |
| 348 local data = stats[i] | |
| 349 local count = data.count; | |
| 350 local percent = math.ceil(100 * count / players - 0.5) | |
| 351 local label = data.realm .. " (" .. percent .. "%)"; | |
| 352 local color | |
| 353 if count < grayt then break | |
| 354 elseif count < whitet then color = 1 | |
| 355 elseif count < epict then color = 2 | |
| 356 else color = 4 end | |
| 357 pleft = pleft - count | |
| 358 addon:AddLabel(container, label, color); | |
| 359 end | |
| 360 if pleft > 0 then | |
| 361 addon:AddLabel(container, "Other" .. " (" .. math.ceil(100 * pleft / players - 0.5) .. "%)", 0); | |
| 362 end | |
| 363 end | |
| 364 end | |
| 365 | |
| 366 function addon:updateRecentRealms() | |
| 367 if not recrealm then return end | |
| 368 recrealm:ReleaseChildren() | |
| 369 local nothing = true; | |
| 370 for realm,color in pairs(recentRealms) do | |
| 371 addon:AddLabel(recrealm, realm,color); | |
| 372 nothing = false; | |
| 373 end | |
| 374 if nothing then | |
| 375 addon:AddLabel(recrealm, "No recent realms"); | |
| 376 end | |
| 377 end | |
| 378 | |
| 379 function addon:ClearRecentRealms() | |
| 380 recentRealms = {} | |
| 381 addon:UpdateCurrentLfgInfo() | |
| 382 addon:updateRecentRealms() | |
| 383 end | |
| 384 | |
| 385 function addon:SheduleScan() | |
| 386 if scanstate == 0 then | |
| 387 addon:ScanRealm() | |
| 388 elseif scanstate == 2 then | |
| 389 scanstate = 3 | |
| 390 end | |
| 391 end | |
| 392 | |
| 393 function addon:updatePartyInfo() | |
| 394 if not partyrealm then return end | |
| 395 partyrealm:ReleaseChildren() | |
| 396 local realms = {} | |
| 397 local inGroup = IsInGroup() | |
| 398 if IsInGroup() then | |
| 399 if IsInRaid() then | |
| 400 for i=1, MAX_RAID_MEMBERS do | |
| 401 addon:AddUnitIdStat("raid"..i, realms) | |
| 402 end | |
| 403 else | |
| 404 for i=1, MAX_PARTY_MEMBERS do | |
| 405 addon:AddUnitIdStat("party"..i, realms) | |
| 406 end | |
| 407 end | |
| 408 end | |
| 409 if inGroup ~= wasInGroup then | |
| 410 addon:SheduleScan() | |
| 411 wasInGroup = inGroup | |
| 412 addon:UpdateCurrentLfgInfo() | |
| 413 leavebtn:SetDisabled(not inGroup) | |
| 414 end | |
| 415 local partyStat = addon:GetRealmStat(realms); | |
| 416 addon:addLabels(partyrealm, partyStat, partyStat.players/2, 2, 0, "Not in party"); | |
| 417 end | |
| 418 | |
| 419 -- Utils functions | |
| 420 | |
| 421 function addon:AddUI(owner, type, parameters, manual, callbacks) | |
| 422 local ui = AceGUI:Create(type) | |
| 423 if parameters then | |
| 424 for key,value in pairs(parameters) do ui[key](ui,value) end | |
| 425 end | |
| 426 if callbacks then | |
| 427 for key,value in pairs(callbacks) do ui:SetCallback(key,value) end | |
| 428 end | |
| 429 if not manual then ui:SetFullWidth(true) end | |
| 430 if owner then owner:AddChild(ui) end | |
| 431 return ui | |
| 432 end | |
| 433 | |
| 434 function addon:AddLabel(owner, text, color, icon) | |
| 435 local label = AceGUI:Create("Label") | |
| 436 label:SetText(text) | |
| 437 label:SetFullWidth(true) | |
| 438 if color ~= nil then | |
| 439 local r,g,b = GetItemQualityColor(color); | |
| 440 label:SetColor(r,g,b) | |
| 441 end | |
| 442 owner:AddChild(label) | |
| 443 return label | |
| 444 end | |
| 445 | |
| 446 function addon:AddIcon(owner, image, size, visible, tooltip) | |
| 447 if visible ~= false then | |
| 448 local icon = addon:AddUI(owner,"Icon",{SetWidth=size,SetHeight=size},true) | |
| 449 icon:SetImage(image) | |
| 450 icon:SetImageSize(size,size) | |
| 451 if tooltip then | |
| 452 icon:SetUserData('tooltip',tooltip) | |
| 453 icon:SetCallback("OnEnter",addon.ShowTooltip) | |
| 454 icon:SetCallback("OnLeave",addon.HideTooltip) | |
| 455 end | |
| 456 else -- add placeholder | |
| 457 addon:AddUI(owner,"Label",{SetWidth=size,SetHeight=size},true) | |
| 458 end | |
| 459 end | |
| 460 | |
| 461 function addon.ShowTooltip(widget) | |
| 462 GameTooltip:SetOwner(widget.frame, "ANCHOR_TOP") | |
| 463 local tooltip = widget:GetUserData('tooltip') | |
| 464 for i=1,#tooltip do | |
| 465 GameTooltip:AddLine(tooltip[i], 1, 1, 1) | |
| 466 end | |
| 467 GameTooltip:Show() | |
| 468 end | |
| 469 | |
| 470 function addon.HideTooltip(widget) | |
| 471 GameTooltip:Hide() | |
| 472 end |
