Mercurial > wow > crossrealmassist
view CrossRealmAssist.lua @ 12:0deef890ec99
Updated version
| author | ShadowTheAge |
|---|---|
| date | Wed, 11 Mar 2015 22:51:00 +0300 |
| parents | 421508c17712 |
| children | 1f68e8154947 |
line wrap: on
line source
CrossRealmAssist = LibStub("AceAddon-3.0"):NewAddon("CrossRealmAssist", "AceEvent-3.0", "AceConsole-3.0", "AceTimer-3.0") local addon = CrossRealmAssist; local wholib = LibStub:GetLibrary('LibWho-2.0'):Library() local ScrollingTable = LibStub("ScrollingTable"); local homeRealm, realmSep, gui, btRefresh, btQuick, btManual, lbRealm, lbStatus, playerName, lfgui, lfgTabSelected, lfgTable, lbThrottle local tabs = {} local curRealmStat local scanstate=0 local recentRealms={} local recentgroups={} local active=false local lfgGroups={ 6, -- Custom 10, -- Ashran 1, -- Quests 3 -- Raids } local StatusTable = { ["invited"]="Invited", ["declined"]="Declined", ["timedout"]="Timed out", ["applied"]="Applied", ["cancelled"]="Join cancelled", ["inviteaccepted"]="Joined", ["_init"]="Join request" } local curLfgGroup local sheduledScan local wasInGroup local inInstance -- Utils functions local function PlayerName(fullname) fullname = fullname or "???-???" local name, realm = strsplit(realmSep, fullname) realm = realm or homeRealm; return name, realm; end local function canJoinGroup() return (not IsInGroup()) or (UnitIsGroupLeader('player') and not IsInRaid()) end local function sortByWeight(a,b) return b.weight < a.weight end -- UI functions local function createIcon(owner, icon, id) local image = owner:CreateTexture(nil, "BACKGROUND") image:SetWidth(16) image:SetHeight(16) image:SetTexture(icon) image:SetPoint("TOPLEFT",owner,"TOPLEFT",id*16,0) return image end local function setupWidget(widget, parameters, x, y) if parameters then for key,value in pairs(parameters) do widget[key](widget,value) end end if y then widget:SetPoint("TOPLEFT",widget:GetParent(),"TOPLEFT",x,-y) end return widget end local function ShowTooltip(widget) GameTooltip:SetOwner(widget, widget.TooltipAnchor) GameTooltip:ClearLines() widget:TooltipFactory() GameTooltip:Show() end local function HideTooltip(widget) GameTooltip:Hide() end local function setupTooltip(widget, anchor, factory) widget.TooltipAnchor = anchor; widget.TooltipFactory = factory; widget:SetScript("OnEnter", ShowTooltip) widget:SetScript("OnLeave", HideTooltip) end local function SelectLfgTab(tab) PanelTemplates_DeselectTab(lfgTabSelected) lfgTabSelected = tab addon.LfgScan(tab.searchID) PanelTemplates_SelectTab(lfgTabSelected) end local function realmToolip() if not curRealmStat then return end local players = curRealmStat.players; local threshold = curRealmStat.threshold GameTooltip:AddLine("Players in zone: "..players) if not curRealmStat.complete then GameTooltip:AppendText("+") end for i=1,curRealmStat.realms do local data = curRealmStat[i] local percent = math.ceil(100 * data.count / players - 0.5) .. "%"; if data.count >= threshold then GameTooltip:AddDoubleLine(data.realm, percent, 0,1,0,0,1,0) else GameTooltip:AddDoubleLine(data.realm, percent, 0.5,0.5,0.5,0.5,0.5,0.5) end end end -- LFG list data providers, filters and tooltips local lfgScanInProgress=false local hasLfgListChanges=false local function addTooltipLineIcon(predicat, text, icon, r, g, b, wrap) if predicat then GameTooltip:AddLine(text, r, g, b, wrap) if icon then GameTooltip:AddTexture(icon) end end end local function WeightLfgItem(id, forAutoJoin) local _, action, caption, desc, voice, ilvl, time, bnetfr, charfr, guild, delisted, fullname, pcount, autoinv = C_LFGList.GetSearchResultInfo(id) if delisted then return 0 end local name,realm = PlayerName(fullname); if forAutoJoin then if not autoinv or pcount >= 35 or pcount <= 5 or recentgroups[fullname] then return 0 end end local autoinvWeight = autoinv and 5 or 2 local visCoef = 1 if recentgroups[fullname] then local ago = GetTime() - recentgroups[fullname].time; visCoef = math.min(1,ago/600) end local leaderRealm = 3 -- recently visited realms if (realm ~= "???") then leaderRealm = (5 - (recentRealms[realm] or 0)) end local countWeight = 4 -- count weight if pcount >= 39 or pcount <= 1 then countWeight = 1 elseif pcount >= 35 or pcount <= 4 then countWeight = 2 elseif pcount >= 30 or pcount <= 10 then countWeight = 3 else countWeight = 4 end local ageWeight = 2 if time > 1200 then ageWeight = 4 elseif time > 300 then ageWeight = 3 end return leaderRealm * countWeight * visCoef * autoinvWeight * ageWeight; end local function ShowLfgInfo(rowFrame, cellFrame, data, cols, row, realrow, column, scrollingTable, ...) if not realrow then return end local rowdata = scrollingTable:GetRow(realrow); local _, action, caption, desc, voice, ilvl, time, bnetfr, charfr, guild, delisted, fullname, pcount, autoinv = C_LFGList.GetSearchResultInfo(rowdata.id) local friends = bnetfr+charfr+guild GameTooltip:SetOwner(rowFrame, "ANCHOR_BOTTOMLEFT",-10,25) GameTooltip:ClearLines() GameTooltip:AddLine(caption,1,1,1) addTooltipLineIcon(desc ~= "", desc, nil, 0.5, 0.5, 0.5, true) local name, realm = PlayerName(fullname) GameTooltip:AddDoubleLine("Leader:",name) GameTooltip:AddDoubleLine("Leader realm:",realm) GameTooltip:AddLine(" ") GameTooltip:AddDoubleLine("Players:",pcount) addTooltipLineIcon(ilvl > 0, "Min. ilvl: "..ilvl, READY_CHECK_WAITING_TEXTURE, 1, 1, 0) addTooltipLineIcon(voice ~= "", "Voice: "..voice, READY_CHECK_WAITING_TEXTURE, 1, 1, 0) addTooltipLineIcon(friends > 0, "Friends: "..friends, READY_CHECK_WAITING_TEXTURE, 1, 1, 0) addTooltipLineIcon(autoinv, "Autoinvite!", READY_CHECK_READY_TEXTURE, 0, 1, 0) local visitinfo = recentgroups[fullname] if visitinfo then local ago = GetTime() - visitinfo.time; GameTooltip:AddDoubleLine(StatusTable[visitinfo.status] or visitinfo.status,SecondsToTime(ago, false, false, 1, false).." ago",1,0,0,1,0,0) GameTooltip:AddTexture(READY_CHECK_NOT_READY_TEXTURE) end GameTooltip:Show() end local function updateTableData(rowFrame, cellFrame, data, cols, row, realrow, column, fShow, table, ...) if fShow then local rowdata = table:GetRow(realrow); local icons = cellFrame.icons local _, action, caption, desc, voice, ilvl, time, bnetfr, charfr, guild, delisted, fullname, pcount, autoinv = C_LFGList.GetSearchResultInfo(rowdata.id) if not icons then local miscdata = createIcon(cellFrame,READY_CHECK_WAITING_TEXTURE,0) local autoinv = createIcon(cellFrame,READY_CHECK_READY_TEXTURE, 1) local visited = createIcon(cellFrame,READY_CHECK_NOT_READY_TEXTURE, 2) icons = {misc=miscdata, autoinv=autoinv, visited=visited} cellFrame.icons = icons end icons.misc:SetShown(voice ~= "" or bnetfr > 0 or charfr > 0 or guild > 0 or ilvl > 0) icons.autoinv:SetShown(autoinv) icons.visited:SetShown(recentgroups[fullname]) if GameTooltip:GetOwner() == rowFrame then ShowLfgInfo(rowFrame, cellFrame, data, cols, row, realrow, column, table) end end end local function updateGroupName(id) return select(3, C_LFGList.GetSearchResultInfo(id)) end local function updateGroupCount(id) return select(13, C_LFGList.GetSearchResultInfo(id)) end local function updateGroupRealm(id) local name = select(12, C_LFGList.GetSearchResultInfo(id)) local pname, realm = PlayerName(name); return realm end local function filterTable(self, row) local delisted = select(11, C_LFGList.GetSearchResultInfo(row.id)) return not delisted; end function addon:UpdateResponseData(event, result) if select(11, C_LFGList.GetSearchResultInfo(result)) then lfgTable:SetFilter(filterTable) end hasLfgListChanges = true end local function refreshLFGList() if lfgScanInProgress or not lfgTable then return end local count, list = C_LFGList.GetSearchResults() local tableData = {} for i = 1,count do local rid = list[i]; if not rid then break end local data = list[i]; local cols = {} local row = {rid} cols[1] = {value=updateGroupName,args=row} cols[2] = {value=updateGroupCount,args=row} cols[3] = {value=updateGroupRealm,args=row} cols[4] = {} table.insert(tableData, {cols=cols,id=rid,weight=WeightLfgItem(rid)}) end table.sort(tableData, sortByWeight); lfgTable:SetData(tableData) end local function JoinGroup(rowFrame, cellFrame, data, cols, row, realrow, column, scrollingTable, ...) if not realrow or not canJoinGroup() then return end local rowdata = scrollingTable:GetRow(realrow); local tank, heal, dd = C_LFGList.GetAvailableRoles() addon:SetGroupJoinStatus(rowdata.id, "_init") C_LFGList.ApplyToGroup(rowdata.id, "", tank, heal, dd) end -- Addon functions and gui constructors function addon:OnEnable() local tabCount = table.getn(lfgGroups) realmSep = _G.REALM_SEPARATORS playerName = UnitName("player") homeRealm = GetRealmName() addon:RegisterChatCommand("cra", "Activate") addon:RegisterChatCommand("crossrealmassist", "Activate") addon:ScheduleTimer("Activate", 0.5) end function addon:OnDisable() addon:Deactivate() end function addon:Activate() if active then return end active=true wasInGroup = IsInGroup() if not gui then addon:CreateUI() end addon:updateCurrentRealm(); addon:ForceRefreshZone(); gui:Show() addon:RegisterEvent("ZONE_CHANGED_NEW_AREA", "ForceRefreshZone") addon:RegisterEvent("SCENARIO_UPDATE", "ForceRefreshZone") addon:RegisterEvent("LFG_LIST_SEARCH_RESULTS_RECEIVED", "LfgResponseData") addon:RegisterEvent("LFG_LIST_SEARCH_RESULT_UPDATED", "UpdateResponseData") addon:RegisterEvent("LFG_LIST_SEARCH_FAILED", "LfgScanFailed") addon:RegisterEvent("GROUP_ROSTER_UPDATE", "updatePartyInfo") addon:RegisterEvent("LFG_LIST_APPLICATION_STATUS_UPDATED", "updateAppStatus") addon.LfgScan(lfgGroups[1]) end function addon:Deactivate() if not active then return end active = false gui:Hide() if lfgui then lfgui:Hide() end scanstate = 0 lfgScanInProgress = false addon:UnregisterEvent("ZONE_CHANGED_NEW_AREA") addon:UnregisterEvent("PLAYER_REGEN_ENABLED") addon:UnregisterEvent("LFG_LIST_SEARCH_RESULTS_RECEIVED") addon:UnregisterEvent("LFG_LIST_SEARCH_RESULT_UPDATED") addon:UnregisterEvent("LFG_LIST_SEARCH_FAILED") addon:UnregisterEvent("GROUP_ROSTER_UPDATE") addon:UnregisterEvent("LFG_LIST_APPLICATION_STATUS_UPDATED") end function addon:SetStatus(status) if status then lbStatus:SetText(status) else lbStatus:SetText("") -- TODO end end function addon:CreateUI() gui = setupWidget(CreateFrame("Frame","CrossRealmAssistMainUI",nil,"InsetFrameTemplate3"), {SetFrameStrata="LOW",SetWidth=208,SetHeight=60,EnableMouse=true,SetMovable=true}) local title = setupWidget(CreateFrame("Frame",nil,gui), {SetWidth=190,SetHeight=18,EnableMouse=true,RegisterForDrag="LeftButton"}, 0, 6); title:SetScript("OnDragStart", function() gui:StartMoving() end) title:SetScript("OnDragStop", function() gui:StopMovingOrSizing() end) gui:SetScript("OnHide", addon.Deactivate) setupTooltip(title, "ANCHOR_TOP", realmToolip) lbRealm = setupWidget(gui:CreateFontString(nil,"BACKGROUND", "GameFontNormal"), {SetWidth=200,SetHeight=18}, 0, 6) lbStatus = setupWidget(gui:CreateFontString(nil,"BACKGROUND", "GameFontHighlightSmallLeft"), {SetWidth=200,SetHeight=10}, 6, 23) btQuick = setupWidget(CreateFrame("Button",nil,gui,"UIMenuButtonStretchTemplate"),{SetWidth=90,SetHeight=20,SetText="Quick join"},4,36) btQuick:SetScript("OnClick",addon.DoAutoAction) btManual = setupWidget(CreateFrame("Button",nil,gui,"UIMenuButtonStretchTemplate"),{SetWidth=90,SetHeight=20,SetText="Manual join"},94,36) btManual:SetScript("OnClick",addon.ShowManualLfg) setupWidget(CreateFrame("Button",nil,gui,"UIPanelCloseButton"),{EnableMouse=true,SetWidth=20,SetHeight=20},188,0) btRefresh = setupWidget(CreateFrame("Button",nil,gui,"UIPanelSquareButton"),{SetWidth=20,SetHeight=20},184,36) btRefresh.icon:SetTexture("Interface/BUTTONS/UI-RefreshButton") btRefresh.icon:SetTexCoord(0,1,0,1); btRefresh:SetScript("OnClick",addon.RefreshZone) gui:SetPoint("CENTER",0,0) addon:UpdateAutoButtonStatus() end function addon:ShowManualLfg() if not lfgui then addon:CreateLFGUI() end lfgui:Show() end function addon.lfgUpdate() if hasLfgListChanges then lfgTable:Refresh() hasLfgListChanges = false end end function addon:CreateLFGUI() lfgui = setupWidget(CreateFrame("Frame","CrossRealmAssistJoinUI",nil,"UIPanelDialogTemplate"), {SetFrameStrata="DIALOG",SetWidth=405,SetHeight=300,EnableMouse=true,SetMovable=true}) lfgui.title:SetText("Click to join group") lfgui:SetScript("OnUpdate",addon.lfgUpdate) local titlereg = lfgui:CreateTitleRegion() titlereg:SetAllPoints(lfgui.title) addon:CreateTabs() lfgTable = ScrollingTable:CreateST({ {name="Title",width=160}, {name="#",width=30,align="CENTER"}, {name="Realm",width=120,align="RIGHT"}, {name="",width=50,DoCellUpdate=updateTableData} },15,16,nil,lfgui); lfgTable:RegisterEvents({OnEnter=ShowLfgInfo,OnLeave=HideTooltip,OnClick=JoinGroup}) setupWidget(lfgTable.frame, nil, 10, 45); lfgTable.frame:SetBackdrop(nil) lfgui:SetPoint("CENTER",0,0) refreshLFGList() lbThrottle = setupWidget(lfgui:CreateFontString(nil,"BACKGROUND", "GameFontHighlightSmall"), {SetWidth=300}, 50, 100) lbThrottle:Hide(); local btRefresh = setupWidget(CreateFrame("Button",nil,lfgui,"UIPanelSquareButton"),{SetWidth=22,SetHeight=22},379,27) btRefresh.icon:SetTexture("Interface/BUTTONS/UI-RefreshButton") btRefresh.icon:SetTexCoord(0,1,0,1); btRefresh:SetScript("OnClick",addon.refreshLfgCurrent) end function addon:CreateTabs() local prevTab for i=1,#lfgGroups do local tab = CreateFrame("Button","$parentTab"..i,lfgui,"CharacterFrameTabButtonTemplate") tab:SetText((C_LFGList.GetCategoryInfo(lfgGroups[i]))); tab.searchID = lfgGroups[i]; tab:SetID(i); PanelTemplates_TabResize(tab, 0) if i == 1 then tab:SetPoint("TOPLEFT", lfgui, "BOTTOMLEFT", 10, 7) lfgTabSelected = tab PanelTemplates_SelectTab(tab) else tab:SetPoint("TOPLEFT", prevTab, "TOPRIGHT",-15,0) PanelTemplates_DeselectTab(tab) end tab:SetScript("OnClick", SelectLfgTab) tabs[i] = tab prevTab = tab end end -- LFG scanning routine function addon.refreshLfgCurrent() addon.LfgScan(curLfgGroup) end function addon.LfgScan(group) if lfgTable then lfgTable:SetData({}) end lfgScanInProgress = true curLfgGroup = group C_LFGList.Search(curLfgGroup,"") end function addon:LfgScanFailed(event, reason) if reason == "throttled" then addon:ScheduleTimer(addon.LfgScan, 2, curLfgGroup) lbThrottle:SetText("LFG scan is delayed (throttled).\nThis page will update automatically...") else lbThrottle:SetText("Scan failed ("..reason..")") end lbThrottle:Show() end function addon:LfgResponseData() lfgScanInProgress = false; if lbThrottle then lbThrottle:Hide() end refreshLFGList() end function addon:updateAppStatus(event, id, status, oldstatus) if status == "invited" then LFGListInviteDialog_Accept(LFGListInviteDialog) end addon:UpdateAutoButtonStatus() addon:SetGroupJoinStatus(id, status) end function addon:SetGroupJoinStatus(id, status) local name = select(12, C_LFGList.GetSearchResultInfo(id)) recentgroups[name] = {status=status, time=GetTime()} end function addon:updatePartyInfo() local realms = {} local inGroup = IsInGroup() if inGroup then if IsInRaid() then for i=1, MAX_RAID_MEMBERS do addon:AddUnitIdStat("raid"..i, realms) end else for i=1, MAX_PARTY_MEMBERS do addon:AddUnitIdStat("party"..i, realms) end end end if inGroup ~= wasInGroup then addon:ForceRefreshZone() wasInGroup = inGroup addon:UpdateAutoButtonStatus() end end -- Zone scanning routine function addon:LeaveCombat() addon:UnregisterEvent("PLAYER_REGEN_ENABLED") scanstate = 0 addon:ScanRealm(); end function addon:ForceRefreshZone() addon:RefreshZone(true) end function addon:RefreshZone(shedule) local inst, instType = IsInInstance() inInstance = (inst or instType ~= "none") if inInstance then lbRealm:SetText("Instanced zone") curRealmStat = nil inInstance = true else inInstance = false; if scanstate == 0 then addon:ScanRealm() elseif shedule == true and scanstate == 2 then addon:SetStatus("Rescan sheduled...") scanstate = 3 end end addon:UpdateAutoButtonStatus() end function addon:ScanRealm() if scanstate > 0 then return end if InCombatLockdown() then addon:RegisterEvent("PLAYER_REGEN_ENABLED","LeaveCombat") scanstate = 1 addon:SetStatus("Scan sheduled after combat..."); return end scanstate = 2 addon:SetStatus("Scanning current realm..."); local searchString = _G.WHO_TAG_ZONE .. '"' .. GetZoneText() .. '"'; wholib:Who(searchString, {callback = "ScanResult", handler=addon}) end function addon:ScanResult(query, results, complete) local realms = {} for _, player in ipairs(results) do addon:AddRealmStat(player.Name, realms); end curRealmStat = addon:GetRealmStat(realms); curRealmStat.complete = complete addon:updateCurrentRealm(); local rescan = scanstate == 3 scanstate = 0; if rescan then addon:ScanRealm() end end function addon:AddRealmStat(name, realms) if (name == playerName) then return end local _, realm = strsplit(realmSep, name) realm = realm or homeRealm realms[realm] = (realms[realm] or 0) + 1 end function addon:AddUnitIdStat(unitid, realms) local name, realm = UnitName(unitid); if not name then return end if realm == "" then realm = homeRealm end realm = realm or homeRealm realms[realm] = (realms[realm] or 0) + 1 end function addon:GetRealmStat(realms) local rcount = 0; local stat = {}; local pcount = 0; for realm,count in pairs(realms) do table.insert(stat,{realm=realm,count=count}) rcount = rcount + 1; pcount = pcount + count; end if rcount > 1 then table.sort(stat, function(a,b) return a.count > b.count end) stat.max = stat[1].count else stat.max = 0 end stat.players = pcount; stat.realms = rcount; return stat; end function addon:updateCurrentRealm() addon:SetStatus(); if not curRealmStat or curRealmStat.realms < 1 then lbRealm:SetText("Unknown realm (No players)") return; end local bestRealm = curRealmStat[1] local prevThreshold = math.min(bestRealm.count/3,10) local mixCount = 0 for i=2,curRealmStat.realms do local data = curRealmStat[i] if data.count >= prevThreshold then mixCount = mixCount + 1 prevThreshold = math.min(data.count/3,10) end end curRealmStat.threshold = prevThreshold if (mixCount > 0) then lbRealm:SetText("Mixed "..bestRealm.realm.." +"..mixCount) else lbRealm:SetText(bestRealm.realm) end end -- Auto action button local action; local function CancelJoin() local apps = C_LFGList.GetApplications(); if apps[1] then C_LFGList.CancelApplication(apps[1]) end end local function findGroupToJoin() local count, list = C_LFGList.GetSearchResults() local tableData = {} for i = 1,count do local rid = list[i]; if not rid then break end local _, action, caption, desc, voice, ilvl, time, bnetfr, charfr, guild, delisted, fullname, pcount, autoinv = C_LFGList.GetSearchResultInfo(rid) local data = list[i]; local cols = {} local row = {rid} cols[1] = {value=updateGroupName,args=row} cols[2] = {value=updateGroupCount,args=row} cols[3] = {value=updateGroupRealm,args=row} cols[4] = {} table.insert(tableData, {cols=cols,id=rid}) end end local function QuickJoin() end function addon:DoAutoAction() if action then action() end addon:UpdateAutoButtonStatus() end function addon:UpdateAutoButtonStatus() btQuick:Enable() if IsInGroup() then action = LeaveParty; btQuick:SetText("Leave group") elseif C_LFGList.GetNumApplications() > 0 then action = CancelJoin; btQuick:SetText("Cancel join") else if inInstance then btQuick:Disable() end btQuick:SetText("Quick join") end end
