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 |
