view CrossRealmAssist.lua @ 11:421508c17712

Started implementing new interface
author ShadowTheAge
date Tue, 10 Mar 2015 00:16:23 +0300
parents 9fa8442dd547
children 0deef890ec99
line wrap: on
line source
CrossRealmAssist = LibStub("AceAddon-3.0"):NewAddon("CrossRealmAssist", "AceEvent-3.0", "AceConsole-3.0", "AceTimer-3.0")
local AceGUI = LibStub("AceGUI-3.0")
local addon = CrossRealmAssist;
local wholib = LibStub:GetLibrary('LibWho-2.0'):Library()

local homeRealm, realmSep, gui, btRefresh, btQuick, btManual, lbRealm, lbStatus, playerName

local curRealmStat
local scanstate=0
local recentRealms={}
local active=false

local lfgGroups={
    6, -- Custom
    10, -- Ashran
    1, -- Quests
    3, -- Raids
    8 -- BGs
}

local lfgTabs
local curLfgGroup
local sheduledScan
local wasInGroup
local wasInInstance
local lfgScanInProgress=false

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

function addon:OnInitialize()

end

function addon:OnEnable()
    local tabCount = table.getn(lfgGroups)
    lfgTabs = {}
    for i=1,tabCount do
        local cat = lfgGroups[i]
        table.insert(lfgTabs,{value=cat,text=(C_LFGList.GetCategoryInfo(cat))})
    end
    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_FAILED", "LfgScanFailed")
    --addon:RegisterEvent("GROUP_ROSTER_UPDATE", "updatePartyInfo")
    --addon:RegisterEvent("LFG_LIST_APPLICATION_STATUS_UPDATED", "updateAppStatus")
end

function addon:Deactivate()
    if not active then return end
    active = false
    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_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",nil,nil,"InsetFrameTemplate3"), {SetFrameStrata="DIALOG",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", addon.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"),{EnableMouse=true,SetWidth=90,SetHeight=20,SetText="Quick join"},4,36)
    btManual = setupWidget(CreateFrame("Button",nil,gui,"UIMenuButtonStretchTemplate"),{EnableMouse=true,SetWidth=90,SetHeight=20,SetText="Manual join"},94,36)
    setupWidget(CreateFrame("Button",nil,gui,"UIPanelCloseButton"),{EnableMouse=true,SetWidth=20,SetHeight=20},188,0)

    btRefresh = setupWidget(CreateFrame("Button",nil,gui,"UIPanelSquareButton"),{EnableMouse=true,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)

    --[[gui = addon:AddUI(nil,"Window",{SetTitle="Cross Realm Assist",EnableResize=false,SetLayout="Flow"},true,{OnClose=addon.Deactivate})

    local tabgroup = addon:AddUI(gui,"TabGroup",{SetFullHeight=true,SetTabs=lfgTabs,SetLayout="Fill"},true,{OnGroupSelected=addon.LfgScan})
    tabgroup:SelectTab(lfgGroups[1])
    lfgContainer = addon:AddUI(tabgroup,"ScrollFrame")

    local hgroupc = addon:AddUI(gui,"InlineGroup",{SetLayout="Fill",SetFullHeight=true,SetTitle="Realm info"},true)
    hgroup = addon:AddUI(hgroupc,"ScrollFrame")

    addon:AddUI(hgroup,"Heading",{SetText="Current realm"})
    currealm = addon:AddUI(hgroup,"SimpleGroup");
    addon:AddUI(hgroup,"Button",{SetText="Refresh",SetRelativeWidth=0.5,SetHeight=20},true,{OnClick=addon.ScanRealm})

    addon:AddUI(hgroup,"Heading",{SetText="Party statistics"})
    partyrealm = addon:AddUI(hgroup,"SimpleGroup");
    leavebtn = addon:AddUI(hgroup,"Button",{SetText="Leave",SetRelativeWidth=0.5,SetHeight=20,SetDisabled=not IsInGroup()},true,{OnClick=LeaveParty})

    addon:AddUI(hgroup,"Heading",{SetText="Recent realms"})
    recrealm = addon:AddUI(hgroup,"SimpleGroup");
    addon:AddUI(hgroup,"Button",{SetText="Clear",SetRelativeWidth=0.5,SetHeight=20},true,{OnClick=addon.ClearRecentRealms})

    gui:DoLayout()
    gui:PauseLayout()

    tabgroup:SetPoint("BOTTOMRIGHT", gui.content, "BOTTOMRIGHT", -200, 0)
    hgroupc:SetPoint("TOPLEFT", gui.content, "TOPRIGHT", -200, 0)
    hgroupc:SetPoint("BOTTOMRIGHT", gui.content, "BOTTOMRIGHT")

    if curRealmStat then addon:updateCurrentRealm() end
    addon:updatePartyInfo()
    addon:updateRecentRealms()]]
end

-- LFG scanning routine

--[[function addon.refreshLfgCurrent()
    addon.LfgScan(nil,nil,curLfgGroup)
end

function addon.LfgScan(widget, callback, tab)
    if not tab then return end
    lfgScanInProgress = true
    if lfgContainer then lfgContainer:ReleaseChildren() end
    curLfgGroup = tab
    C_LFGList.Search(curLfgGroup,"")
end

function addon:LfgScanFailed(event, reason)
    print("scan failed "..reason)
    if reason == "throttled" then
        addon:ScheduleTimer(addon.LfgScan, 2, nil, nil, curLfgGroup)
    end
end

local function WeightLfgItem(item)
    local leaderRealm = 3
    if (item.realm ~= "???") then leaderRealm = (5 - (recentRealms[item.realm] or 0)) end
    local weight =
        (item.autoinv and 5 or 2) *
                ((item.friends > 0) and 2 or 3) *
                ((item.ilvl > 0) and 2 or 3) *
                ((item.voice ~= "") and 2 or 4)
    local count = item.pcount;
    local countWeight
    if count >= 39 or count <= 1 then countWeight = 1
    elseif count >= 35 or count <= 4 then countWeight = 2
    elseif count >= 30 or count <= 10 then countWeight = 3
    else countWeight = 4 end
    return weight * leaderRealm * countWeight;
end

function addon:LfgResponseData()
    lfgScanInProgress = false;
    addon:UpdateCurrentLfgInfo()
end

function addon:UpdateCurrentLfgInfo(repeated)
    if lfgScanInProgress then return end
    local count, list = C_LFGList.GetSearchResults()
    local lfgList = {}
    local lfgEntries = 0
    local hasUnknowns = false
    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)
        if not fullname then
            fullname = "???-???"
            hasUnknowns = true
        end
        if not delisted then
            local pname, realm = strsplit(realmSep, fullname)
            realm = realm or homeRealm
            local item = {
                rid=rid,
                action=action,
                caption=caption,
                desc=desc,
                ilvl=ilvl,
                voice=voice,
                time=time,
                friends=bnetfr+charfr+guild,
                name=pname,
                realm = realm,
                pcount=pcount,
                autoinv=autoinv
            }
            item.weight = WeightLfgItem(item)
            table.insert(lfgList, item)
            lfgEntries = lfgEntries + 1
        end
    end
    addon:refreshLFGList(lfgList, lfgEntries);
    if hasUnknowns and repeated ~= true then addon:ScheduleTimer("UpdateCurrentLfgInfo", 1, true) end
end

function addon:refreshLFGList(list, count)
    if not lfgContainer then return end
    lfgContainer:PauseLayout()
    lfgContainer:ReleaseChildren()
    table.sort(list, addon.SortLfgItems)

    local canJoin = addon.canJoinGroup()

    for i=1,count do
        local data = list[i];
        local renderer = addon:AddUI(lfgContainer,"SimpleGroup",{SetLayout="Flow",SetHeight=20,SetAutoAdjustHeight=false});

        addon:AddUI(renderer,"Label",{SetText=data.caption,SetRelativeWidth=0.4},true)
        addon:AddUI(renderer,"Label",{SetText=data.pcount,SetRelativeWidth=0.05},true)
        local realm = addon:AddUI(renderer,"Label",{SetText=data.realm,SetRelativeWidth=0.25},true)
        if recentRealms[data.realm] then
            local r,g,b = GetItemQualityColor(recentRealms[data.realm]);
            realm:SetColor(r,g,b)
        end

        local tooltip = {}
        if data.voice ~= "" then table.insert(tooltip,"Voice: "..data.voice) end
        if data.friends > 0 then table.insert(tooltip,"Friends: "..data.friends) end
        if data.ilvl > 0 then table.insert(tooltip,"Min. Ilvl: "..data.ilvl) end
        addon:AddIcon(renderer, "Interface/GossipFrame/ActiveQuestIcon", 16, tooltip[1] ~= nil, tooltip)
        addon:AddIcon(renderer, READY_CHECK_READY_TEXTURE, 16, data.autoinv, {"Auto invite!"})
        local btn = addon:AddUI(renderer,"Button",{SetText="Join",SetRelativeWidth=0.2,SetHeight=20,SetDisabled=not canJoin},true,{OnClick=addon.joinGroup})
        btn:SetUserData('rid',data.rid)
    end
    addon:AddUI(lfgContainer,"Button",{SetText="Refresh",SetWidth=100,SetHeight=20},true,{OnClick=addon.refreshLfgCurrent})

    lfgContainer:ResumeLayout()
    lfgContainer:DoLayout()
end

function addon.SortLfgItems(a,b)
    return a.weight > b.weight
end

function addon.canJoinGroup()
    return (not IsInGroup()) or (UnitIsGroupLeader('player') and not IsInRaid())
end

function addon.joinGroup(widget)
    if not addon.canJoinGroup() then return end
    local rid = widget:GetUserData('rid');
    widget:SetDisabled(true)
    C_LFGList.ApplyToGroup(rid, "", C_LFGList.GetAvailableRoles())
end

function addon:updateAppStatus(event, id, status, oldstatus)
    if status == "invited" then
        LFGListInviteDialog_Accept(LFGListInviteDialog)
    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()
    if (inst or instType ~= "none") then
        lbRealm:SetText("Instanced zone")
        curRealmStat = nil
        btQuick:Disable()
    else
        btQuick:Enable()
        if scanstate == 0 then
            addon:ScanRealm()
        elseif shedule == true and scanstate == 2 then
            addon:SetStatus("Rescan sheduled...")
            scanstate = 3
        end
    end
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,5)
    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,5)
        end
    end
    curRealmStat.threshold = prevThreshold

    if (mixCount > 0) then
        lbRealm:SetText("Mixed "..bestRealm.realm.." +"..mixCount)
    else
        lbRealm:SetText(bestRealm.realm)
    end
end

function addon: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

--[[function addon:addLabels(container, stats, epict, whitet, grayt, emptytext)
    if stats.realms == 0 then
        addon:AddLabel(container, emptytext)
    else
        local players = stats.players
        local pleft = players;
        for i=1,stats.realms do
            local data = stats[i]
            local count = data.count;
            local percent = math.ceil(100 * count / players - 0.5)
            local label = data.realm .. " (" .. percent .. "%)";
            local color
            if count < grayt then break
            elseif count < whitet then color = 1
            elseif count < epict then color = 2
            else color = 4 end
            pleft = pleft - count
            addon:AddLabel(container, label, color);
        end
        if pleft > 0 then
            addon:AddLabel(container, "Other" .. " (" .. math.ceil(100 * pleft / players - 0.5) .. "%)", 0);
        end
    end
end

function addon:updateRecentRealms()
    if not recrealm then return end
    recrealm:ReleaseChildren()
    local nothing = true;
    for realm,color in pairs(recentRealms) do
        addon:AddLabel(recrealm, realm,color);
        nothing = false;
    end
    if nothing then
        addon:AddLabel(recrealm, "No recent realms");
    end
end

function addon:ClearRecentRealms()
    recentRealms = {}
    addon:UpdateCurrentLfgInfo()
    addon:updateRecentRealms()
end

function addon:updatePartyInfo()
    if not partyrealm then return end
    partyrealm:ReleaseChildren()
    local realms = {}
    local inGroup = IsInGroup()
    if IsInGroup() 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:SheduleScan()
        wasInGroup = inGroup
        addon:UpdateCurrentLfgInfo()
        leavebtn:SetDisabled(not inGroup)
    end
    local partyStat = addon:GetRealmStat(realms);
    addon:addLabels(partyrealm, partyStat, partyStat.players/2, 2, 0, "Not in party");
end]]