Mercurial > wow > guild-delta
view gd.lua @ 1:f7c747904387
adding pkgmeta
author | Farmbuyer of US-Kilrogg <farmbuyer@gmail.com> |
---|---|
date | Mon, 01 Nov 2010 18:36:46 +0000 |
parents | |
children | fb9a91642a60 |
line wrap: on
line source
local addon = CreateFrame("Frame") --select(2,...) local SV local DEFAULT_CHAT = 2 -- combat log (no constant predefined for that) local l10n --if GetLocale() == "enUS" then l10n= { ["LEFT"] = "Players who have left the guild: ", ["JOINED"] = "Players who have joined the guild: ", ["RANK"] = "Players whose rank has changed:", ["LEVEL"] = "Players whose level has changed:", ["NOTES"] = "Players whose notes have changed:", ["Oabbrev"] = "O", -- abbreviation for "Officer", specifically the non-public note ["FIELD_rank"] = "Rank", ["FIELD_level"] = "Level", ["FIELD_notes"] = "Player/Officer notes", -- Something needs to be done about the walls of description text below. } --elseif .... end local guild_selection, log_ever_shown addon.options = { name = "Guild Delta", type = 'group', handler = addon, -- functions listed as strings called as addon:func args = { version = { --name = filled in during OnEnable type = 'description', fontSize = "large", --image = "Interface\\AddOns\\GuildDelta\\test2", cmdHidden = true, order = 1, }, note = { --name = filled in locals section type = 'description', cmdHidden = true, width = 'full', order = 2, }, reset = { name = "Reset Output", desc = "Restores default output settings", type = 'execute', order = 5, func = "SetChat", arg = DEFAULT_CHAT, }, spacer2 = { name = '', type = 'description', cmdHidden = true, width = 'full', order = 6, }, print_chatframes = { name = "Print Chatframe Numbers", desc = "Print each chat window number in its frame, for easy reference in the next slider option", type = 'execute', --func = print_chatframes filled in below order = 10, }, chatframe_num = { name = "Output Chatframe", desc = "Which chat window to use for printing all the output during login", type = 'range', min = 1, max = NUM_CHAT_WINDOWS, step = 1, get = function() return tonumber(SV.chatframe) or --[[in case of custom name]]DEFAULT_CHAT end, set = "SetChat", order = 15, }, chatframe_name = { name = "Chatframe Override", desc = "<Advanced> If blank, uses the numerical slider. If set, it is the NAME of a frame with AddMessage capability to use for output.", type = 'input', get = function() return type(SV.chatframe) == 'string' and SV.chatframe or nil end, set = "SetChat", order = 16, }, fields = { name = "Fields", desc = "Track changes to these player fields", type = 'multiselect', order = 20, -- these need to be of function type rather than string keys of members values = function(info) return addon:MakeFieldList() end, get = function(info,x) return SV.fields[x] end, set = function(info,x,val) SV.fields[x] = val end, }, spacer1 = { name = '', type = 'description', cmdHidden = true, width = 'full', order = 29, }, guilds = { name = "Guilds", desc = "Guilds for which a roster is known", type = 'select', order = 30, width = 'double', values = function(info) return addon:MakeGuildList() end, get = function(info) return guild_selection end, set = function(info,val) guild_selection = val end, }, clearguild = { name = "Reset Guild", desc = "Erase stored data for selected guild; information will be scanned from scratch on next login.", type = 'execute', order = 32, disabled = function() return not guild_selection end, func = function() assert(type(guild_selection)=='string') local g,r = guild_selection:match("<([^>]+)> %- (.*)") local m = SV.members[r] if m then m[g] = nil else addon:Print("Hm, error.", r, "can't be matched as a realm name. Please report this as a bug, including the name of the realm and guild.") end end, }, persist = { name = "Logging", type = 'group', inline = true, order = 60, args = { note = { --name = filled in locals section type = 'description', cmdHidden = true, width = 'full', order = 1, }, enable = { name = "Enable Logging", desc = [[Accumulate all deltas, including a timestamp. |cffFF0000WARNING|r: if logging was enabled and you turn it off, the log itself will not be saved when exiting the game.]], type = 'toggle', order = 2, get = function() return SV.logging end, set = function(i,v) SV.logging = v log_ever_shown = SV.logging or log_ever_shown end, }, clearlog = { name = "Reset Log", desc = "Erase accumulated deltas.", type = 'execute', order = 3, hidden = function() return not (SV.logging or log_ever_shown) end, disabled = function() return not SV.logging end, func = function() SV.logtext = nil end, }, log = { name = "Log", desc = "If you make changes, don't forget to click 'Accept' to save them.", type = 'input', order = 10, multiline = 15, width = 'full', hidden = function() return not (SV.logging or log_ever_shown) end, disabled = function() return not SV.logging end, get = function() return SV.logtext end, set = function(i,t) SV.logtext = t end, }, }, }, } } ----------------------------------------------------------------------------- -- other locals local tinsert, GetGuildRosterInfo = _G.table.insert, _G.GetGuildRosterInfo local chatframe, fieldlist local function prt (...) return chatframe:AddMessage(...) end local function cprt (txt) if SV.logging then addon:AddLogNote(txt) end return prt(txt, 255/255, 26/255, 160/255) end function addon:current_guild_info (N) local ret = {} for i = 1, N do local name,rank_as_string,_,level,_,_,publicnote,officernote = GetGuildRosterInfo(i) -- This technically does not work out to the same as A?B:C, combined -- with the logic below. It does, however, still result in the entry -- not appearing in the returned table. publicnote = publicnote ~= "" and publicnote or nil officernote = officernote ~= "" and officernote or nil if name then -- redundant, but apparently happens on extreme lag tinsert(ret, {name = name, rank = SV.fields.rank and rank_as_string or nil, level = SV.fields.level and level or nil, pnote = SV.fields.notes and publicnote or nil, onote = SV.fields.notes and officernote or nil, }) end end table.sort(ret, function (l,r) return l.name < r.name end) return ret end function addon.options.args.print_chatframes.func() for i = 1, NUM_CHAT_WINDOWS do local cf = _G["ChatFrame"..i] if not cf then break end addon:Print(cf, "This is frame number", i) end end addon.options.args.note.name = "You can use the '/guilddelta' command to open the options window.\n\n".. "The guild roster has already been scanned by the time you see this. Therefore, ".. "if you make any changes to the Fields section below, you should probably relog ".. "immediately to begin tracking the changed fields. Changes to the contents *OF* ".. "those fields will not be noticed until the first login after *that*.\n\n" addon.options.args.persist.args.note.name = "Enabling logging will accumulate the text of the 'deltas' as you see them. ".. "This can grow large over time, depending on the activity of your guilds, so you ".. "should use the Reset Log button below from time to time.\n\n".. "If you click inside the text area below, you can use Control-A to select all ".. "the text, and Control-C to copy it to your computer's clipboard. If you make ".. "any changes to the text, they will be preserved. (You can remove uninteresting ".. "changes, add reminders to yourself, and so forth.)\n\n" ----------------------------------------------------------------------------- addon = LibStub("AceAddon-3.0"):NewAddon(addon, "GuildDelta", "AceConsole-3.0") function addon:OnInitialize() if _G.GuildDeltaSV == nil then -- Defaults need to transition from potential older savedvars _G.GuildDeltaSV = { chatframe = _G.GuildDelta_chatframe or DEFAULT_CHAT, fields = _G.GuildDelta_fields or { rank = true, level = false, notes = true }, members = _G.GuildDelta_memberdata or {} } end SV = _G.GuildDeltaSV end -- Remove anything that normal operation doesn't need after finishing its work. function addon:cleanup() prt = nil; cprt = nil self.current_guild_info = nil self.cleanup = nil self.unload = nil self.logquay = nil self.AddLogNote = nil self.FinishLog = nil end function addon:unload() self:cleanup() LibStub("AceAddon-3.0").addons["GuildDelta"] = nil l10n = nil; addon = nil; -- put the userdata back so it counts as a Frame object again local ud = self[0] table.wipe(self) self[0] = ud end function addon:OnEnable() if not IsInGuild() then self:Print("You are not in a guild, not loading.") return self:unload() end AutoCompleteInfoDelayer:HookScript("OnFinished", function() self:RegisterEvent("GUILD_ROSTER_UPDATE") end) self:SetScript("OnEvent", self.GuildUpdate) self.options.args.version.name = "|cff30adffVersion " .. (GetAddOnMetadata("GuildDelta", "Version") or "?") .. "|r" LibStub("AceConfig-3.0"):RegisterOptionsTable("GuildDelta", self.options) --[[self.optionsFrame =]] LibStub("AceConfigDialog-3.0"):AddToBlizOptions("GuildDelta", "Guild Delta") self:RegisterChatCommand("guilddelta", "OnChatCommand") self:SetChat(false) log_ever_shown = SV.logging self.OnEnable = nil end function addon:OnChatCommand (input) if not input or input:trim() == "" then LibStub("AceConfigDialog-3.0"):Open("GuildDelta") else LibStub("AceConfigCmd-3.0").HandleCommand(self, "guilddelta", "GuildDelta", input) end end -- 0 args: called during startup to initialize -- 1 arg: resetting via menus to defaults (see arg field) -- 2 args: setting via menus to new value, number or custom name function addon:SetChat (info, value) local n, nframe if info then -- coming via menu n = info.arg or value else n = SV.chatframe end if type(n) == 'number' then nframe = _G["ChatFrame"..n] else nframe = _G[n] end if type(nframe) == 'table' and type(nframe.AddMessage) == 'function' then if type(info) ~= 'boolean' then self:Print("Now printing to chat frame", n, (type(nframe.name)=='string' and ("(".. nframe.name .. ")") or "")) end SV.chatframe = n chatframe = nframe else self:Printf("EEEEEEEK! '%s' was not a valid chat frame number/name, no change has been made.", n) end end -- Not a "normal" PLAYER_LOGOUT handler; this only fires if the player is in -- a guild and the update has already run. function addon:PLAYER_LOGOUT() if not SV.logging then SV.logtext = nil end end function addon:GuildUpdate() local current_n = GetNumGuildMembers(true) if current_n <= 0 then -- catch the hell up, servers... return GuildRoster() end self:UnregisterEvent("GUILD_ROSTER_UPDATE") self:SetScript("OnEvent", self.PLAYER_LOGOUT) -- keepin' it real^H^H^H^Hsmall and kludgey self:RegisterEvent("PLAYER_LOGOUT") self.GuildUpdate = nil local guild, realm = (GetGuildInfo("player")), GetRealmName() local members = SV.members if members[realm] and members[realm][guild] and #(members[realm][guild]) > 0 then -- moved the normal case below else -- new user, or new guild, or any number of things self:Print("GuildDelta initializing roster...") members[realm] = members[realm] or {} members[realm][guild] = self:current_guild_info(current_n) return end -- table.insert with notes if available -- concatentation of all strings faster than string.format local function tins (t, x) local s = x.name if x.onote and (x.onote ~= "") then s = s .. "(" .. l10n.Oabbrev .. ": " .. x.onote .. ")" end if x.pnote and (x.pnote ~= "") then s = s .. "(" .. x.pnote .. ")" end tinsert(t, s) end -- build the current list local previous, current = members[realm][guild], self:current_guild_info(current_n) local previous_n = #previous -- walk both and do equivalence comparison local joined, left, rank, level, notes = {}, {}, {}, {}, {} local p, c = 1, 1 while p <= previous_n and c <= current_n do local P, C = previous[p], current[c] if P.name == C.name then -- normal case p = p + 1 c = c + 1 -- but can now compare details if C.rank and P.rank and (P.rank ~= C.rank) then tinsert(rank, {C.name, P.rank.." --> "..C.rank}) end if C.level and P.level and (P.level ~= C.level) then tinsert(level, {C.name, P.level.." --> "..C.level}) end if C.pnote and (P.pnote ~= C.pnote) then tinsert(notes, {C.name, C.pnote}) end if C.onote and (P.onote ~= C.onote) then tinsert(notes, {C.name, "["..l10n.Oabbrev.."]: "..C.onote}) end elseif P.name < C.name then -- entry at index p not at c -> somebody has left tins (left, P) p = p + 1 else -- entry at index c not at p -> somebody has joined tins (joined, C) c = c + 1 end end -- leftovers for i = p, previous_n do tins (left, previous[i]) end for i = c, current_n do tins (joined, current[i]) end -- show results if SV.logging then self.logquay = {} end local m if #left > 0 then m = l10n.LEFT .. table.concat(left, ", ") cprt(m) end if #joined > 0 then m = l10n.JOINED .. table.concat(joined, ", ") cprt(m) end if #rank > 0 then cprt(l10n.RANK) for i = 1, #rank do cprt(rank[i][1]..': '..rank[i][2]) end end if #level > 0 then cprt(l10n.LEVEL) for i = 1, #level do cprt(level[i][1]..': '..level[i][2]) end end if #notes > 0 then cprt(l10n.NOTES) for i = 1, #notes do cprt(notes[i][1]..': "'..notes[i][2]..'"') end end if SV.logging then self:FinishLog(guild,realm) end members[realm][guild] = current end function addon:MakeFieldList() if not fieldlist then fieldlist = {} for name in pairs(SV.fields) do fieldlist[name] = l10n["FIELD_"..name] end end return fieldlist end function addon:MakeGuildList() local list = {} local K for rname,rdata in pairs(SV.members) do for g in pairs(rdata) do K = ("<%s> - %s"):format(g,rname) list[K] = K end end return list end function addon:AddLogNote (txt) tinsert(self.logquay,txt) end function addon:FinishLog(g,r) if #self.logquay > 0 then Calendar_LoadUI() local _,M,D,Y = CalendarGetDate() local h,m = GetGameTime() local timestamp = ("%.4d/%.2d/%.2d %.2d:%.2d <%s> - %s\n"):format(Y,M,D,h,m,g,r) SV.logtext = timestamp .. table.concat(self.logquay, '\n') .. (SV.logtext and ('\n\n'..SV.logtext) or '') end end -- vim:noet