Mercurial > wow > guild-delta
view gd.lua @ 3:7b31138c3c8e
- Add option to print message even on no changes.
- Split up options panel into tabs.
- Make localization (such as it is) table accessible through addon table.
author | Farmbuyer of US-Kilrogg <farmbuyer@gmail.com> |
---|---|
date | Mon, 29 Nov 2010 01:52:48 +0000 |
parents | fb9a91642a60 |
children | 9527583a842b |
line wrap: on
line source
local addon = CreateFrame("Frame") --select(2,...) local SV local DEFAULT_CHAT = 2 -- combat log (no constant predefined for that) --if GetLocale() == "enUS" then addon.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", ["NOCHANGE"] = "No changes to tracked fields have been detected.", -- 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', childGroups = 'tab', 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, }, general = { name = "General", desc = "Tracking options", type = 'group', order = 10, args = { fields = { name = "Fields", desc = "Track changes to these player fields", type = 'multiselect', order = 10, -- 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, }, nochange = { name = "No Change", desc = "Print a message even when no changes are detected", type = 'toggle', order = 15, get = function() return not not SV.notify_nochange end, set = function(info,val) SV.notify_nochange = val end, }, spacer2 = { name = '', type = 'description', cmdHidden = true, width = 'full', order = 19, }, guilds = { name = "Guilds", desc = "Guilds for which a roster is known", type = 'select', order = 20, 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 = 22, 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, }, }, }, output = { name = "Output", desc = "What and where to print", type = 'group', order = 20, args = { reset = { name = "Reset Output", desc = "Restores default output settings", type = 'execute', order = 10, func = "SetChat", arg = DEFAULT_CHAT, }, spacer1 = { name = '', type = 'description', cmdHidden = true, width = 'full', order = 11, }, 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 = 20, }, chatframe_num = { name = "Output Chatframe", desc = "Which chat window to prefer 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 = 25, }, 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 = 26, }, }, }, persist = { name = "Logging", desc = "Storing changes for later review", type = 'group', order = 30, args = { note = { --name = filled in locals section type = 'description', cmdHidden = true, width = 'full', order = 10, }, 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 = 15, 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 = 20, 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. Scroll down if needed.", type = 'input', order = 25, 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.output.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" 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, the changes 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 {}, --notify_nochange = nil by default --logging = nil by default } 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 -- Remove everything. function addon:unload() self:cleanup() LibStub("AceAddon-3.0").addons["GuildDelta"] = 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 self:RegisterEvent("GUILD_ROSTER_UPDATE") 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(self) 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 -- 1 arg: (self) called during startup to initialize -- (other) resetting via menus to defaults (see arg field) -- 2 args: setting via menus to new value, number or custom name (see value) function addon:SetChat (info, value) local n, nframe local isauto = info == self if isauto then n = SV.chatframe else -- coming via menu n = info.arg or value end if type(n) == 'number' then if isauto then -- start at the preferred number and find a non-minimized and -- potentially visible frame n = math.min(n,NUM_CHAT_WINDOWS) local cf,cft for i = n, 1, -1 do cf = _G['ChatFrame'..i] cft = _G[cf:GetName()..'Tab'] if cft:IsVisible() then n = i break end end end nframe = _G['ChatFrame'..n] else nframe = _G[n] -- advanced name end if type(nframe) == 'table' and type(nframe.AddMessage) == 'function' then if not isauto then SV.chatframe = n self:Print("Now printing to chat frame", n, (type(nframe.name)=='string' and ("(".. nframe.name .. ")") or "")) end 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 if SV.logtext and #SV.logtext > 20000 then self:Print("Your accumulated logfile has grown rather large; you should consider copying it out and clearing it.") end local guild, realm = (GetGuildInfo("player")), GetRealmName() local members = SV.members local l10n = self.l10n 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 self:cleanup() end -- table.insert with notes if available -- concatentation of string-only args faster than string.format of same 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, changes if #left > 0 then changes = true m = l10n.LEFT .. table.concat(left, ", ") cprt(m) end if #joined > 0 then changes = true m = l10n.JOINED .. table.concat(joined, ", ") cprt(m) end if #rank > 0 then changes = true cprt(l10n.RANK) for i = 1, #rank do cprt(rank[i][1]..': '..rank[i][2]) end end if #level > 0 then changes = true cprt(l10n.LEVEL) for i = 1, #level do cprt(level[i][1]..': '..level[i][2]) end end if #notes > 0 then changes = true 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 if SV.notify_nochange and (not changes) then cprt(l10n.NOCHANGE) end members[realm][guild] = current self:cleanup() end function addon:MakeFieldList() if not fieldlist then fieldlist = {} for name in pairs(SV.fields) do fieldlist[name] = self.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