Mercurial > wow > guild-delta
comparison 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 |
comparison
equal
deleted
inserted
replaced
0:a3b67ffe00a0 | 1:f7c747904387 |
---|---|
1 | |
2 local addon = CreateFrame("Frame") --select(2,...) | |
3 local SV | |
4 | |
5 local DEFAULT_CHAT = 2 -- combat log (no constant predefined for that) | |
6 | |
7 local l10n | |
8 --if GetLocale() == "enUS" then | |
9 l10n= { | |
10 ["LEFT"] = "Players who have left the guild: ", | |
11 ["JOINED"] = "Players who have joined the guild: ", | |
12 ["RANK"] = "Players whose rank has changed:", | |
13 ["LEVEL"] = "Players whose level has changed:", | |
14 ["NOTES"] = "Players whose notes have changed:", | |
15 | |
16 ["Oabbrev"] = "O", -- abbreviation for "Officer", specifically the non-public note | |
17 ["FIELD_rank"] = "Rank", | |
18 ["FIELD_level"] = "Level", | |
19 ["FIELD_notes"] = "Player/Officer notes", | |
20 | |
21 -- Something needs to be done about the walls of description text below. | |
22 } | |
23 --elseif .... end | |
24 | |
25 local guild_selection, log_ever_shown | |
26 addon.options = { | |
27 name = "Guild Delta", | |
28 type = 'group', | |
29 handler = addon, -- functions listed as strings called as addon:func | |
30 args = { | |
31 version = { | |
32 --name = filled in during OnEnable | |
33 type = 'description', | |
34 fontSize = "large", | |
35 --image = "Interface\\AddOns\\GuildDelta\\test2", | |
36 cmdHidden = true, | |
37 order = 1, | |
38 }, | |
39 note = { | |
40 --name = filled in locals section | |
41 type = 'description', | |
42 cmdHidden = true, | |
43 width = 'full', | |
44 order = 2, | |
45 }, | |
46 reset = { | |
47 name = "Reset Output", | |
48 desc = "Restores default output settings", | |
49 type = 'execute', | |
50 order = 5, | |
51 func = "SetChat", | |
52 arg = DEFAULT_CHAT, | |
53 }, | |
54 spacer2 = { | |
55 name = '', | |
56 type = 'description', | |
57 cmdHidden = true, | |
58 width = 'full', | |
59 order = 6, | |
60 }, | |
61 print_chatframes = { | |
62 name = "Print Chatframe Numbers", | |
63 desc = "Print each chat window number in its frame, for easy reference in the next slider option", | |
64 type = 'execute', | |
65 --func = print_chatframes filled in below | |
66 order = 10, | |
67 }, | |
68 chatframe_num = { | |
69 name = "Output Chatframe", | |
70 desc = "Which chat window to use for printing all the output during login", | |
71 type = 'range', | |
72 min = 1, | |
73 max = NUM_CHAT_WINDOWS, | |
74 step = 1, | |
75 get = function() return tonumber(SV.chatframe) or --[[in case of custom name]]DEFAULT_CHAT end, | |
76 set = "SetChat", | |
77 order = 15, | |
78 }, | |
79 chatframe_name = { | |
80 name = "Chatframe Override", | |
81 desc = "<Advanced> If blank, uses the numerical slider. If set, it is the NAME of a frame with AddMessage capability to use for output.", | |
82 type = 'input', | |
83 get = function() | |
84 return type(SV.chatframe) == 'string' and SV.chatframe or nil | |
85 end, | |
86 set = "SetChat", | |
87 order = 16, | |
88 }, | |
89 fields = { | |
90 name = "Fields", | |
91 desc = "Track changes to these player fields", | |
92 type = 'multiselect', | |
93 order = 20, | |
94 -- these need to be of function type rather than string keys of members | |
95 values = function(info) return addon:MakeFieldList() end, | |
96 get = function(info,x) return SV.fields[x] end, | |
97 set = function(info,x,val) SV.fields[x] = val end, | |
98 }, | |
99 spacer1 = { | |
100 name = '', | |
101 type = 'description', | |
102 cmdHidden = true, | |
103 width = 'full', | |
104 order = 29, | |
105 }, | |
106 guilds = { | |
107 name = "Guilds", | |
108 desc = "Guilds for which a roster is known", | |
109 type = 'select', | |
110 order = 30, | |
111 width = 'double', | |
112 values = function(info) return addon:MakeGuildList() end, | |
113 get = function(info) return guild_selection end, | |
114 set = function(info,val) guild_selection = val end, | |
115 }, | |
116 clearguild = { | |
117 name = "Reset Guild", | |
118 desc = "Erase stored data for selected guild; information will be scanned from scratch on next login.", | |
119 type = 'execute', | |
120 order = 32, | |
121 disabled = function() return not guild_selection end, | |
122 func = function() | |
123 assert(type(guild_selection)=='string') | |
124 local g,r = guild_selection:match("<([^>]+)> %- (.*)") | |
125 local m = SV.members[r] | |
126 if m then | |
127 m[g] = nil | |
128 else | |
129 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.") | |
130 end | |
131 end, | |
132 }, | |
133 persist = { | |
134 name = "Logging", | |
135 type = 'group', | |
136 inline = true, | |
137 order = 60, | |
138 args = { | |
139 note = { | |
140 --name = filled in locals section | |
141 type = 'description', | |
142 cmdHidden = true, | |
143 width = 'full', | |
144 order = 1, | |
145 }, | |
146 enable = { | |
147 name = "Enable Logging", | |
148 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.]], | |
149 type = 'toggle', | |
150 order = 2, | |
151 get = function() return SV.logging end, | |
152 set = function(i,v) | |
153 SV.logging = v | |
154 log_ever_shown = SV.logging or log_ever_shown | |
155 end, | |
156 }, | |
157 clearlog = { | |
158 name = "Reset Log", | |
159 desc = "Erase accumulated deltas.", | |
160 type = 'execute', | |
161 order = 3, | |
162 hidden = function() return not (SV.logging or log_ever_shown) end, | |
163 disabled = function() return not SV.logging end, | |
164 func = function() | |
165 SV.logtext = nil | |
166 end, | |
167 }, | |
168 log = { | |
169 name = "Log", | |
170 desc = "If you make changes, don't forget to click 'Accept' to save them.", | |
171 type = 'input', | |
172 order = 10, | |
173 multiline = 15, | |
174 width = 'full', | |
175 hidden = function() return not (SV.logging or log_ever_shown) end, | |
176 disabled = function() return not SV.logging end, | |
177 get = function() return SV.logtext end, | |
178 set = function(i,t) SV.logtext = t end, | |
179 }, | |
180 }, | |
181 }, | |
182 } | |
183 } | |
184 | |
185 | |
186 ----------------------------------------------------------------------------- | |
187 -- other locals | |
188 local tinsert, GetGuildRosterInfo = _G.table.insert, _G.GetGuildRosterInfo | |
189 local chatframe, fieldlist | |
190 local function prt (...) | |
191 return chatframe:AddMessage(...) | |
192 end | |
193 local function cprt (txt) | |
194 if SV.logging then addon:AddLogNote(txt) end | |
195 return prt(txt, 255/255, 26/255, 160/255) | |
196 end | |
197 | |
198 function addon:current_guild_info (N) | |
199 local ret = {} | |
200 for i = 1, N do | |
201 local name,rank_as_string,_,level,_,_,publicnote,officernote = GetGuildRosterInfo(i) | |
202 -- This technically does not work out to the same as A?B:C, combined | |
203 -- with the logic below. It does, however, still result in the entry | |
204 -- not appearing in the returned table. | |
205 publicnote = publicnote ~= "" and publicnote or nil | |
206 officernote = officernote ~= "" and officernote or nil | |
207 if name then -- redundant, but apparently happens on extreme lag | |
208 tinsert(ret, | |
209 {name = name, | |
210 rank = SV.fields.rank and rank_as_string or nil, | |
211 level = SV.fields.level and level or nil, | |
212 pnote = SV.fields.notes and publicnote or nil, | |
213 onote = SV.fields.notes and officernote or nil, | |
214 }) | |
215 end | |
216 end | |
217 table.sort(ret, function (l,r) return l.name < r.name end) | |
218 return ret | |
219 end | |
220 | |
221 function addon.options.args.print_chatframes.func() | |
222 for i = 1, NUM_CHAT_WINDOWS do | |
223 local cf = _G["ChatFrame"..i] | |
224 if not cf then break end | |
225 addon:Print(cf, "This is frame number", i) | |
226 end | |
227 end | |
228 | |
229 addon.options.args.note.name = | |
230 "You can use the '/guilddelta' command to open the options window.\n\n".. | |
231 "The guild roster has already been scanned by the time you see this. Therefore, ".. | |
232 "if you make any changes to the Fields section below, you should probably relog ".. | |
233 "immediately to begin tracking the changed fields. Changes to the contents *OF* ".. | |
234 "those fields will not be noticed until the first login after *that*.\n\n" | |
235 | |
236 addon.options.args.persist.args.note.name = | |
237 "Enabling logging will accumulate the text of the 'deltas' as you see them. ".. | |
238 "This can grow large over time, depending on the activity of your guilds, so you ".. | |
239 "should use the Reset Log button below from time to time.\n\n".. | |
240 "If you click inside the text area below, you can use Control-A to select all ".. | |
241 "the text, and Control-C to copy it to your computer's clipboard. If you make ".. | |
242 "any changes to the text, they will be preserved. (You can remove uninteresting ".. | |
243 "changes, add reminders to yourself, and so forth.)\n\n" | |
244 | |
245 | |
246 ----------------------------------------------------------------------------- | |
247 addon = LibStub("AceAddon-3.0"):NewAddon(addon, "GuildDelta", | |
248 "AceConsole-3.0") | |
249 | |
250 function addon:OnInitialize() | |
251 if _G.GuildDeltaSV == nil then | |
252 -- Defaults need to transition from potential older savedvars | |
253 _G.GuildDeltaSV = { | |
254 chatframe = _G.GuildDelta_chatframe or DEFAULT_CHAT, | |
255 fields = _G.GuildDelta_fields or { rank = true, level = false, notes = true }, | |
256 members = _G.GuildDelta_memberdata or {} | |
257 } | |
258 end | |
259 SV = _G.GuildDeltaSV | |
260 end | |
261 | |
262 -- Remove anything that normal operation doesn't need after finishing its work. | |
263 function addon:cleanup() | |
264 prt = nil; cprt = nil | |
265 self.current_guild_info = nil | |
266 self.cleanup = nil | |
267 self.unload = nil | |
268 self.logquay = nil | |
269 self.AddLogNote = nil | |
270 self.FinishLog = nil | |
271 end | |
272 function addon:unload() | |
273 self:cleanup() | |
274 LibStub("AceAddon-3.0").addons["GuildDelta"] = nil | |
275 l10n = nil; addon = nil; | |
276 -- put the userdata back so it counts as a Frame object again | |
277 local ud = self[0] | |
278 table.wipe(self) | |
279 self[0] = ud | |
280 end | |
281 | |
282 function addon:OnEnable() | |
283 if not IsInGuild() then | |
284 self:Print("You are not in a guild, not loading.") | |
285 return self:unload() | |
286 end | |
287 | |
288 AutoCompleteInfoDelayer:HookScript("OnFinished", | |
289 function() self:RegisterEvent("GUILD_ROSTER_UPDATE") end) | |
290 self:SetScript("OnEvent", self.GuildUpdate) | |
291 | |
292 self.options.args.version.name = | |
293 "|cff30adffVersion " .. (GetAddOnMetadata("GuildDelta", "Version") or "?") .. "|r" | |
294 LibStub("AceConfig-3.0"):RegisterOptionsTable("GuildDelta", self.options) | |
295 --[[self.optionsFrame =]] LibStub("AceConfigDialog-3.0"):AddToBlizOptions("GuildDelta", "Guild Delta") | |
296 self:RegisterChatCommand("guilddelta", "OnChatCommand") | |
297 self:SetChat(false) | |
298 log_ever_shown = SV.logging | |
299 self.OnEnable = nil | |
300 end | |
301 | |
302 | |
303 function addon:OnChatCommand (input) | |
304 if not input or input:trim() == "" then | |
305 LibStub("AceConfigDialog-3.0"):Open("GuildDelta") | |
306 else | |
307 LibStub("AceConfigCmd-3.0").HandleCommand(self, "guilddelta", "GuildDelta", input) | |
308 end | |
309 end | |
310 | |
311 | |
312 -- 0 args: called during startup to initialize | |
313 -- 1 arg: resetting via menus to defaults (see arg field) | |
314 -- 2 args: setting via menus to new value, number or custom name | |
315 function addon:SetChat (info, value) | |
316 local n, nframe | |
317 if info then -- coming via menu | |
318 n = info.arg or value | |
319 else | |
320 n = SV.chatframe | |
321 end | |
322 if type(n) == 'number' then | |
323 nframe = _G["ChatFrame"..n] | |
324 else | |
325 nframe = _G[n] | |
326 end | |
327 if type(nframe) == 'table' and type(nframe.AddMessage) == 'function' then | |
328 if type(info) ~= 'boolean' then | |
329 self:Print("Now printing to chat frame", n, | |
330 (type(nframe.name)=='string' and ("(".. nframe.name .. ")") or "")) | |
331 end | |
332 SV.chatframe = n | |
333 chatframe = nframe | |
334 else | |
335 self:Printf("EEEEEEEK! '%s' was not a valid chat frame number/name, no change has been made.", n) | |
336 end | |
337 end | |
338 | |
339 | |
340 -- Not a "normal" PLAYER_LOGOUT handler; this only fires if the player is in | |
341 -- a guild and the update has already run. | |
342 function addon:PLAYER_LOGOUT() | |
343 if not SV.logging then | |
344 SV.logtext = nil | |
345 end | |
346 end | |
347 | |
348 | |
349 function addon:GuildUpdate() | |
350 local current_n = GetNumGuildMembers(true) | |
351 if current_n <= 0 then | |
352 -- catch the hell up, servers... | |
353 return GuildRoster() | |
354 end | |
355 self:UnregisterEvent("GUILD_ROSTER_UPDATE") | |
356 self:SetScript("OnEvent", self.PLAYER_LOGOUT) -- keepin' it real^H^H^H^Hsmall and kludgey | |
357 self:RegisterEvent("PLAYER_LOGOUT") | |
358 self.GuildUpdate = nil | |
359 | |
360 local guild, realm = (GetGuildInfo("player")), GetRealmName() | |
361 local members = SV.members | |
362 if members[realm] | |
363 and members[realm][guild] | |
364 and #(members[realm][guild]) > 0 | |
365 then | |
366 -- moved the normal case below | |
367 else | |
368 -- new user, or new guild, or any number of things | |
369 self:Print("GuildDelta initializing roster...") | |
370 members[realm] = members[realm] or {} | |
371 members[realm][guild] = self:current_guild_info(current_n) | |
372 return | |
373 end | |
374 | |
375 -- table.insert with notes if available | |
376 -- concatentation of all strings faster than string.format | |
377 local function tins (t, x) | |
378 local s = x.name | |
379 if x.onote and (x.onote ~= "") then | |
380 s = s .. "(" .. l10n.Oabbrev .. ": " .. x.onote .. ")" | |
381 end | |
382 if x.pnote and (x.pnote ~= "") then | |
383 s = s .. "(" .. x.pnote .. ")" | |
384 end | |
385 tinsert(t, s) | |
386 end | |
387 | |
388 -- build the current list | |
389 local previous, current = members[realm][guild], | |
390 self:current_guild_info(current_n) | |
391 local previous_n = #previous | |
392 | |
393 -- walk both and do equivalence comparison | |
394 local joined, left, rank, level, notes = {}, {}, {}, {}, {} | |
395 local p, c = 1, 1 | |
396 while p <= previous_n and c <= current_n do | |
397 local P, C = previous[p], current[c] | |
398 | |
399 if P.name == C.name then | |
400 -- normal case | |
401 p = p + 1 | |
402 c = c + 1 | |
403 -- but can now compare details | |
404 if C.rank and P.rank and (P.rank ~= C.rank) then | |
405 tinsert(rank, {C.name, P.rank.." --> "..C.rank}) | |
406 end | |
407 if C.level and P.level and (P.level ~= C.level) then | |
408 tinsert(level, {C.name, P.level.." --> "..C.level}) | |
409 end | |
410 if C.pnote and (P.pnote ~= C.pnote) then | |
411 tinsert(notes, {C.name, C.pnote}) | |
412 end | |
413 if C.onote and (P.onote ~= C.onote) then | |
414 tinsert(notes, {C.name, "["..l10n.Oabbrev.."]: "..C.onote}) | |
415 end | |
416 | |
417 elseif P.name < C.name then | |
418 -- entry at index p not at c -> somebody has left | |
419 tins (left, P) | |
420 p = p + 1 | |
421 | |
422 else | |
423 -- entry at index c not at p -> somebody has joined | |
424 tins (joined, C) | |
425 c = c + 1 | |
426 end | |
427 end | |
428 | |
429 -- leftovers | |
430 for i = p, previous_n do | |
431 tins (left, previous[i]) | |
432 end | |
433 for i = c, current_n do | |
434 tins (joined, current[i]) | |
435 end | |
436 | |
437 -- show results | |
438 if SV.logging then self.logquay = {} end | |
439 local m | |
440 if #left > 0 then | |
441 m = l10n.LEFT .. table.concat(left, ", ") | |
442 cprt(m) | |
443 end | |
444 | |
445 if #joined > 0 then | |
446 m = l10n.JOINED .. table.concat(joined, ", ") | |
447 cprt(m) | |
448 end | |
449 | |
450 if #rank > 0 then | |
451 cprt(l10n.RANK) | |
452 for i = 1, #rank do | |
453 cprt(rank[i][1]..': '..rank[i][2]) | |
454 end | |
455 end | |
456 | |
457 if #level > 0 then | |
458 cprt(l10n.LEVEL) | |
459 for i = 1, #level do | |
460 cprt(level[i][1]..': '..level[i][2]) | |
461 end | |
462 end | |
463 | |
464 if #notes > 0 then | |
465 cprt(l10n.NOTES) | |
466 for i = 1, #notes do | |
467 cprt(notes[i][1]..': "'..notes[i][2]..'"') | |
468 end | |
469 end | |
470 | |
471 if SV.logging then self:FinishLog(guild,realm) end | |
472 members[realm][guild] = current | |
473 end | |
474 | |
475 | |
476 function addon:MakeFieldList() | |
477 if not fieldlist then | |
478 fieldlist = {} | |
479 for name in pairs(SV.fields) do | |
480 fieldlist[name] = l10n["FIELD_"..name] | |
481 end | |
482 end | |
483 return fieldlist | |
484 end | |
485 | |
486 function addon:MakeGuildList() | |
487 local list = {} | |
488 local K | |
489 for rname,rdata in pairs(SV.members) do | |
490 for g in pairs(rdata) do | |
491 K = ("<%s> - %s"):format(g,rname) | |
492 list[K] = K | |
493 end | |
494 end | |
495 return list | |
496 end | |
497 | |
498 function addon:AddLogNote (txt) | |
499 tinsert(self.logquay,txt) | |
500 end | |
501 | |
502 function addon:FinishLog(g,r) | |
503 if #self.logquay > 0 then | |
504 Calendar_LoadUI() | |
505 local _,M,D,Y = CalendarGetDate() | |
506 local h,m = GetGameTime() | |
507 local timestamp = ("%.4d/%.2d/%.2d %.2d:%.2d <%s> - %s\n"):format(Y,M,D,h,m,g,r) | |
508 SV.logtext = timestamp | |
509 .. table.concat(self.logquay, '\n') | |
510 .. (SV.logtext and ('\n\n'..SV.logtext) or '') | |
511 end | |
512 end | |
513 | |
514 -- vim:noet |