comparison CombatLog.lua @ 57:01b63b8ed811 v21

total rewrite to version 21
author yellowfive
date Fri, 05 Jun 2015 11:05:15 -0700
parents
children cf2b6b9a8337
comparison
equal deleted inserted replaced
56:75431c084aa0 57:01b63b8ed811
1 local Amr = LibStub("AceAddon-3.0"):GetAddon("AskMrRobot")
2 local L = LibStub("AceLocale-3.0"):GetLocale("AskMrRobot", true)
3 local AceGUI = LibStub("AceGUI-3.0")
4
5 local _btnToggle = nil
6 local _panelUndoWipe = nil
7 local _chkAutoAll = nil
8 local _autoChecks = nil
9
10 local function createDifficultyCheckBox(instanceId, difficultyId)
11 local chk = AceGUI:Create("AmrUiCheckBox")
12 chk:SetText(L.DifficultyNames[difficultyId])
13 chk:SetCallback("OnClick", function(widget)
14 Amr:ToggleAutoLog(instanceId, difficultyId)
15 end)
16
17 _autoChecks[instanceId][difficultyId] = chk
18 return chk
19 end
20
21 -- render a group of controls for auto-logging of a raid zone
22 local function renderAutoLogSection(instanceId, container)
23 _autoChecks[instanceId] = {}
24
25 local lbl = AceGUI:Create("AmrUiLabel")
26 lbl:SetWidth(200)
27 lbl:SetText(L.InstanceNames[instanceId])
28 lbl:SetFont(Amr.CreateFont("Regular", 20, Amr.Colors.White))
29 container:AddChild(lbl)
30
31 local line = AceGUI:Create("AmrUiPanel")
32 line:SetHeight(1)
33 line:SetBackgroundColor(Amr.Colors.White)
34 line:SetPoint("TOPLEFT", lbl.frame, "BOTTOMLEFT", 1, -7)
35 line:SetPoint("TOPRIGHT", lbl.frame, "BOTTOMRIGHT", 0, -7)
36 container:AddChild(line)
37
38 local chkMythic = createDifficultyCheckBox(instanceId, Amr.Difficulties.Mythic)
39 chkMythic:SetPoint("TOPLEFT", line.frame, "BOTTOMLEFT", 0, -8)
40 container:AddChild(chkMythic)
41
42 local chkNormal = createDifficultyCheckBox(instanceId, Amr.Difficulties.Normal)
43 chkNormal:SetPoint("TOPLEFT", line.frame, "BOTTOMLEFT", 0, -30)
44 container:AddChild(chkNormal)
45
46 -- find the widest of mythic/normal
47 local w = math.max(chkMythic:GetWidth(), chkNormal:GetWidth())
48
49 local chkHeroic = createDifficultyCheckBox(instanceId, Amr.Difficulties.Heroic)
50 chkHeroic:SetPoint("TOPLEFT", line.frame, "BOTTOMLEFT", w + 20, -8)
51 container:AddChild(chkHeroic)
52
53 local chkLfr = createDifficultyCheckBox(instanceId, Amr.Difficulties.Lfr)
54 chkLfr:SetPoint("TOPLEFT", line.frame, "BOTTOMLEFT", w + 20, -30)
55 container:AddChild(chkLfr)
56
57 return lbl
58 end
59
60 -- renders the main UI for the Combat Log tab
61 function Amr:RenderTabLog(container)
62
63 -- main commands
64 _btnToggle = AceGUI:Create("AmrUiButton")
65 _btnToggle:SetText(L.LogButtonStartText)
66 _btnToggle:SetBackgroundColor(Amr.Colors.Green)
67 _btnToggle:SetFont(Amr.CreateFont("Bold", 16, Amr.Colors.White))
68 _btnToggle:SetWidth(200)
69 _btnToggle:SetHeight(26)
70 _btnToggle:SetPoint("TOPLEFT", container.content, "TOPLEFT", 0, -40)
71 _btnToggle:SetCallback("OnClick", function() Amr:ToggleLogging() end)
72 container:AddChild(_btnToggle)
73
74 _lblLogging = AceGUI:Create("AmrUiLabel")
75 _lblLogging:SetText(L.LogNote)
76 _lblLogging:SetWidth(200)
77 _lblLogging:SetFont(Amr.CreateFont("Italic", 14, Amr.Colors.BrightGreen))
78 _lblLogging:SetJustifyH("MIDDLE")
79 _lblLogging:SetPoint("TOP", _btnToggle.frame, "BOTTOM", 0, -5)
80 container:AddChild(_lblLogging)
81
82 local btnReload = AceGUI:Create("AmrUiButton")
83 btnReload:SetText(L.LogButtonReloadText)
84 btnReload:SetBackgroundColor(Amr.Colors.Blue)
85 btnReload:SetFont(Amr.CreateFont("Bold", 16, Amr.Colors.White))
86 btnReload:SetWidth(200)
87 btnReload:SetHeight(26)
88 btnReload:SetPoint("TOPLEFT", _btnToggle.frame, "TOPRIGHT", 40, 0)
89 btnReload:SetCallback("OnClick", ReloadUI)
90 container:AddChild(btnReload)
91
92 local lbl = AceGUI:Create("AmrUiLabel")
93 lbl:SetText(L.LogReloadNote)
94 lbl:SetWidth(200)
95 lbl:SetFont(Amr.CreateFont("Italic", 14, Amr.Colors.TextTan))
96 lbl:SetJustifyH("MIDDLE")
97 lbl:SetPoint("TOP", btnReload.frame, "BOTTOM", 0, -5)
98 container:AddChild(lbl)
99
100 -- container for undo wipe so we can hide/show it all
101 _panelUndoWipe = AceGUI:Create("AmrUiPanel")
102 _panelUndoWipe:SetLayout("None")
103 _panelUndoWipe:SetBackgroundColor(Amr.Colors.Black, 0)
104 _panelUndoWipe:SetPoint("TOPLEFT", lbl.frame, "BOTTOMLEFT", 0, -40)
105 container:AddChild(_panelUndoWipe)
106
107 local btnUndoWipe = AceGUI:Create("AmrUiButton")
108 btnUndoWipe:SetText(L.LogButtonUndoWipeText)
109 btnUndoWipe:SetBackgroundColor(Amr.Colors.Orange)
110 btnUndoWipe:SetFont(Amr.CreateFont("Bold", 16, Amr.Colors.White))
111 btnUndoWipe:SetWidth(200)
112 btnUndoWipe:SetHeight(26)
113 btnUndoWipe:SetPoint("TOPLEFT", lbl.frame, "BOTTOMLEFT", 0, -40)
114 btnUndoWipe:SetCallback("OnClick", function() Amr:UndoWipe() end)
115 _panelUndoWipe:AddChild(btnUndoWipe)
116
117 lbl = AceGUI:Create("AmrUiLabel")
118 lbl:SetText(L.LogUndoWipeNote)
119 lbl:SetWidth(200)
120 lbl:SetFont(Amr.CreateFont("Italic", 14, Amr.Colors.TextTan))
121 lbl:SetJustifyH("MIDDLE")
122 lbl:SetPoint("TOP", btnUndoWipe.frame, "BOTTOM", 0, -5)
123 _panelUndoWipe:AddChild(lbl)
124
125 local lbl2 = AceGUI:Create("AmrUiLabel")
126 lbl2:SetText(L.LogUndoWipeDate(date("%B %d", time()), date("%I:%M %p", time())))
127 lbl2:SetWidth(200)
128 lbl2:SetFont(Amr.CreateFont("Italic", 14, Amr.Colors.TextTan))
129 lbl2:SetJustifyH("MIDDLE")
130 lbl2:SetPoint("TOP", lbl.frame, "BOTTOM", 0, -2)
131 _panelUndoWipe:AddChild(lbl2)
132
133 local btnWipe = AceGUI:Create("AmrUiButton")
134 btnWipe:SetText(L.LogButtonWipeText)
135 btnWipe:SetBackgroundColor(Amr.Colors.Orange)
136 btnWipe:SetFont(Amr.CreateFont("Bold", 16, Amr.Colors.White))
137 btnWipe:SetWidth(200)
138 btnWipe:SetHeight(26)
139 btnWipe:SetPoint("TOPRIGHT", btnUndoWipe.frame, "TOPLEFT", -40, 0)
140 btnWipe:SetCallback("OnClick", function() Amr:Wipe() end)
141 container:AddChild(btnWipe)
142
143 lbl = AceGUI:Create("AmrUiLabel")
144 lbl:SetText(L.LogWipeNote)
145 lbl:SetWidth(200)
146 lbl:SetFont(Amr.CreateFont("Italic", 14, Amr.Colors.TextTan))
147 lbl:SetJustifyH("MIDDLE")
148 lbl:SetPoint("TOP", btnWipe.frame, "BOTTOM", 0, -5)
149 container:AddChild(lbl)
150
151 lbl2 = AceGUI:Create("AmrUiLabel")
152 lbl2:SetText(L.LogWipeNote2("/amr wipe"))
153 lbl2:SetWidth(200)
154 lbl2:SetFont(Amr.CreateFont("Italic", 14, Amr.Colors.TextTan))
155 lbl2:SetJustifyH("MIDDLE")
156 lbl2:SetPoint("TOP", lbl.frame, "BOTTOM", 0, -2)
157 container:AddChild(lbl2)
158
159 -- auto-logging controls
160 lbl = AceGUI:Create("AmrUiLabel")
161 lbl:SetWidth(600)
162 lbl:SetText(L.LogAutoTitle)
163 lbl:SetFont(Amr.CreateFont("Bold", 24, Amr.Colors.TextHeaderActive))
164 lbl:SetPoint("TOPLEFT", lbl2.frame, "BOTTOMLEFT", 0, -40)
165 container:AddChild(lbl)
166
167 _chkAutoAll = AceGUI:Create("AmrUiCheckBox")
168 _chkAutoAll:SetText(L.LogAutoAllText)
169 _chkAutoAll:SetPoint("TOPLEFT", lbl.frame, "BOTTOMLEFT", 1, -15)
170 _chkAutoAll:SetCallback("OnClick", function(widget) Amr:ToggleAllAutoLog() end)
171 container:AddChild(_chkAutoAll)
172
173 _autoChecks = {}
174
175 -- go through all supported instances, rendering in a left->right pattern, 2 per row
176 local autoSections = {}
177 for i, instanceId in ipairs(Amr.InstanceIdsOrdered) do
178 local autoSection = renderAutoLogSection(instanceId, container)
179 if i == 1 then
180 autoSection:SetPoint("TOPLEFT", _chkAutoAll.frame, "BOTTOMLEFT", -1, -15)
181 elseif i % 2 == 0 then
182 autoSection:SetPoint("TOPLEFT", autoSections[i - 1].frame, "TOPRIGHT", 40, 0)
183 else
184 autoSection:SetPoint("TOPLEFT", autoSections[i - 2].frame, "BOTTOMLEFT", 0, -15)
185 end
186
187 table.insert(autoSections, autoSection)
188 end
189 autoSections = nil
190
191 -- instructions
192 lbl = AceGUI:Create("AmrUiLabel")
193 lbl:SetText(L.LogInstructionsTitle)
194 lbl:SetWidth(480)
195 lbl:SetFont(Amr.CreateFont("Italic", 24, Amr.Colors.Text))
196 lbl:SetPoint("TOPRIGHT", container.content, "TOPRIGHT", 0, -40)
197 container:AddChild(lbl)
198
199 lbl2 = AceGUI:Create("AmrUiLabel")
200 lbl2:SetText(L.LogInstructions)
201 lbl2:SetWidth(480)
202 lbl2:SetFont(Amr.CreateFont("Italic", 14, Amr.Colors.Text))
203 lbl2:SetPoint("TOPLEFT", lbl.frame, "BOTTOMLEFT", 0, -10)
204 container:AddChild(lbl2)
205
206 -- initialize state of controls
207 Amr:RefreshLogUi()
208 end
209
210 function Amr:ReleaseTabLog()
211 _btnToggle = nil
212 _panelUndoWipe = nil
213 _chkAutoAll = nil
214 _autoChecks = nil
215 end
216
217 local function isAllAutoLoggingEnabled()
218 -- see if all auto-logging options are enabled
219 local allChecked = true
220 for i, instanceId in ipairs(Amr.InstanceIdsOrdered) do
221 for k, difficultyId in pairs(Amr.Difficulties) do
222 if not Amr.db.profile.Logging.Auto[instanceId][difficultyId] then
223 allChecked = false
224 break
225 end
226 end
227 if not allChecked then break end
228 end
229
230 return allChecked
231 end
232
233 -- check current zone and auto-logging settings, and enable logging if appropriate
234 local function updateAutoLogging(force)
235
236 -- get the info about the instance
237 local zone, _, difficultyId, _, _, _, _, instanceId = GetInstanceInfo()
238
239 if not force and zone == Amr.db.char.Logging.LastZone and difficultyId == Amr.db.char.Logging.LastDiff then
240 -- do nothing if the zone hasn't actually changed, otherwise we may override the user's manual enable/disable
241 return
242 end
243
244 Amr.db.char.Logging.LastZone = zone
245 Amr.db.char.Logging.LastDiff = difficultyId
246
247 if Amr.IsSupportedInstanceId(instanceId) and Amr.db.profile.Logging.Auto[tonumber(instanceId)][tonumber(difficultyId)] then
248 -- we are in a supported zone that we want to auto-log, turn logging on
249 -- (supported check is probably redundant, but just in case someone has old settings lying around)
250 if not Amr:IsLogging() then
251 Amr:StartLogging()
252 end
253 else
254 -- not in a zone that we want to auto-log, turn logging off
255 if Amr:IsLogging() then
256 Amr:StopLogging()
257 end
258 end
259 end
260
261 -- refresh the state of the tab based on current settings
262 function Amr:RefreshLogUi()
263 if not _btnToggle then return end
264
265 -- set state of logging button based on whether it is on or off
266 if self:IsLogging() then
267 _btnToggle:SetBackgroundColor(Amr.Colors.Red)
268 _btnToggle:SetText(L.LogButtonStopText)
269 else
270 _btnToggle:SetBackgroundColor(Amr.Colors.Green)
271 _btnToggle:SetText(L.LogButtonStartText)
272 end
273
274 _lblLogging:SetVisible(self:IsLogging())
275
276 -- hide/show undo wipe button based on whether a wipe has been called recently
277 _panelUndoWipe:SetVisible(Amr.db.char.Logging.LastWipe and true or false)
278
279 local all = isAllAutoLoggingEnabled()
280 _chkAutoAll:SetChecked(all)
281
282 for i, instanceId in ipairs(Amr.InstanceIdsOrdered) do
283 for k, difficultyId in pairs(Amr.Difficulties) do
284 _autoChecks[instanceId][difficultyId]:SetChecked(Amr.db.profile.Logging.Auto[instanceId][difficultyId])
285 end
286 end
287 end
288
289 function Amr:IsLogging()
290 return Amr.db.char.Logging.Enabled
291 end
292
293 function Amr:ToggleLogging()
294 if not Amr.db.char.Logging.Enabled then
295 Amr:StartLogging()
296 else
297 Amr:StopLogging()
298 end
299 end
300
301 function Amr:StartLogging()
302
303 local now = time()
304 local oldDuration = 60 * 60 * 24 * 10
305
306 -- prune out entries in log data that are more than 10 days old
307
308 -- player data
309 local playerData = Amr.db.global.Logging.PlayerData
310 if playerData then
311 for name, timeList in pairs(playerData) do
312 for timestamp, dataString in pairs(timeList) do
313 if difftime(now, tonumber(timestamp)) > oldDuration then
314 timeList[timestamp] = nil
315 end
316 end
317
318 if next(timeList) == nil then
319 playerData[name] = nil
320 end
321 end
322 end
323
324 -- same idea with extra info (auras, pets, whatever we end up adding to it)
325 local extraData = Amr.db.global.Logging.PlayerExtras
326 if extraData then
327 for name, timeList in pairs(extraData) do
328 for timestamp, dataString in pairs(timeList) do
329 if difftime(now, tonumber(timestamp)) > oldDuration then
330 timeList[timestamp] = nil
331 end
332 end
333
334 if next(timeList) == nil then
335 extraData[name] = nil
336 end
337 end
338 end
339
340 -- delete wipes that are more than 10 days old
341 if Amr.db.global.Logging.Wipes then
342 local wipes = Amr.db.global.Logging.Wipes
343 local i = 1
344 while i <= #wipes do
345 local t = wipes[i]
346 if difftime(now, t) > oldDuration then
347 table.remove(wipes, i)
348 else
349 i = i + 1
350 end
351 end
352 end
353
354 -- delete the last wipe date if it is more than 10 days old
355 if Amr.db.char.Logging.LastWipe and difftime(now, Amr.db.char.Logging.LastWipe) > oldDuration then
356 Amr.db.char.Logging.LastWipe = nil
357 end
358
359 -- always enable advanced combat logging via our addon, gathers more detailed data for better analysis
360 SetCVar("advancedCombatLogging", 1)
361 LoggingCombat(true)
362 Amr.db.char.Logging.Enabled = true
363
364 self:Print(L.LogChatStart)
365
366 self:UpdateMinimap()
367 self:RefreshLogUi()
368 end
369
370 function Amr:StopLogging()
371
372 LoggingCombat(false)
373 Amr.db.char.Logging.Enabled = false
374
375 self:Print(L.LogChatStop)
376
377 self:UpdateMinimap()
378 self:RefreshLogUi()
379 end
380
381 function Amr:Wipe()
382 local t = time()
383 table.insert(Amr.db.global.Logging.Wipes, t)
384 Amr.db.char.Logging.LastWipe = t
385
386 self:Print(L.LogChatWipe(date('%I:%M %p', t)))
387
388 self:RefreshLogUi()
389 end
390
391 function Amr:UndoWipe()
392
393 local t = Amr.db.char.Logging.LastWipe
394 local wipes = Amr.db.global.Logging.Wipes
395
396 if not t then
397 self:Print(L.LogChatNoWipes)
398 else
399 -- find this wipe and remove it, may not be the last one if this person is raiding on multiple characters
400 for i = #wipes, 1, -1 do
401 if wipes[i] == t then
402 table.remove(wipes, i)
403 break
404 end
405 end
406
407 Amr.db.char.Logging.LastWipe = nil
408 self:Print(L.LogChatUndoWipe(date('%I:%M %p', t)))
409 end
410
411 self:RefreshLogUi()
412 end
413
414 function Amr:ToggleAutoLog(instanceId, difficultyId)
415
416 local byDiff = Amr.db.profile.Logging.Auto[instanceId]
417 byDiff[difficultyId] = not byDiff[difficultyId]
418
419 self:RefreshLogUi()
420
421 -- see if we should turn logging on right now
422 updateAutoLogging(true)
423 end
424
425 function Amr:ToggleAllAutoLog()
426
427 local val = not isAllAutoLoggingEnabled()
428
429 for i, instanceId in ipairs(Amr.InstanceIdsOrdered) do
430 for k, difficultyId in pairs(Amr.Difficulties) do
431 Amr.db.profile.Logging.Auto[instanceId][difficultyId] = val
432 end
433 end
434
435 self:RefreshLogUi()
436
437 -- see if we should turn logging on right now
438 updateAutoLogging(true)
439 end
440
441 function Amr:ProcessPlayerSnapshot(msg)
442 if not self:IsLogging() then return end
443
444 -- message will be of format: timestamp\nregion\nrealm\nname\n[stuff]
445 local parts = {}
446 for part in string.gmatch(msg, "([^\n]+)") do
447 table.insert(parts, part)
448 end
449
450 local timestamp = tonumber(parts[1])
451 local name = parts[2] .. ":" .. parts[3] .. ":" .. parts[4]
452 local setup = parts[5]
453
454 -- initialize the player's table
455 local playerList = Amr.db.global.Logging.PlayerData[name]
456 if not playerList then
457 playerList = {}
458 Amr.db.global.Logging.PlayerData[name] = playerList
459 end
460
461 -- find the most recent setup already recorded for this player
462 local previousSetup = nil
463 local previousTime = 0
464 for t, v in pairs(playerList) do
465 if t > previousTime then
466 previousSetup = v
467 previousTime = t
468 end
469 end
470
471 -- if the previous setup is more than 12 hours old, don't consider it
472 if previousSetup and difftime(timestamp, previousTime) > 60 * 60 * 12 then
473 previousSetup = nil
474 end
475
476 -- we only need to keep this setup if it is different than the previous
477 if setup ~= previousSetup then
478 playerList[timestamp] = setup
479 end
480
481 end
482
483 -- read auras and pet mapping info (pet may not be necessary anymore... but doesn't hurt)
484 local function getPlayerExtraData(data, unitId, petId)
485
486 local guid = UnitGUID(unitId)
487 if guid == nil then return end
488
489 local fields = {}
490
491 local buffs = {}
492 for i=1,40 do
493 local _,_,_,count,_,_,_,_,_,_,spellId = UnitAura(unitId, i, "HELPFUL")
494 table.insert(buffs, spellId)
495 end
496 if not buffs or #buffs == 0 then
497 table.insert(fields, "_")
498 else
499 table.insert(fields, Amr.Serializer:ToCompressedNumberList(buffs))
500 end
501
502 local petGuid = UnitGUID(petId)
503 if petGuid then
504 table.insert(fields, guid .. "," .. petGuid)
505 else
506 table.insert(fields, '_')
507 end
508
509 local name = GetUnitName(unitId, true) -- GetRaidRosterInfo(rosterIndex)
510 local realm = GetRealmName()
511 local region = Amr.RegionNames[GetCurrentRegion()]
512 local splitPos = string.find(name, "-")
513 if splitPos ~= nil then
514 realm = string.sub(name, splitPos + 1)
515 name = string.sub(name, 1, splitPos - 1)
516 end
517
518 data[region .. ":" .. realm .. ":" .. name] = table.concat(fields, ";")
519 end
520
521 local function logPlayerExtraData()
522 if not Amr:IsLogging() or not Amr:IsSupportedInstance() then return end
523
524 local timestamp = time()
525 local units = {}
526 local petUnits = {}
527
528 if IsInRaid() then
529 for i = 1,40 do
530 table.insert(units, "raid" .. i)
531 table.insert(petUnits, "raidpet" .. i)
532 end
533 elseif IsInGroup() then
534 table.insert(units, "player")
535 table.insert(petUnits, "pet")
536 for i = 1,4 do
537 table.insert(units, "party" .. i)
538 table.insert(petUnits, "partypet" .. i)
539 end
540 else
541 return
542 end
543
544 local data = {}
545 for i = 1,#units do
546 getPlayerExtraData(data, units[i], petUnits[i])
547 end
548
549 for name, val in pairs(data) do
550 -- record aura stuff, we never check for duplicates, need to know it at each point in time
551 if Amr.db.global.Logging.PlayerExtras[name] == nil then
552 Amr.db.global.Logging.PlayerExtras[name] = {}
553 end
554 Amr.db.global.Logging.PlayerExtras[name][timestamp] = val
555 end
556 end
557
558 function Amr:InitializeCombatLog()
559 updateAutoLogging()
560 end
561
562 Amr:AddEventHandler("UPDATE_INSTANCE_INFO", updateAutoLogging)
563 Amr:AddEventHandler("PLAYER_DIFFICULTY_CHANGED", updateAutoLogging)
564 Amr:AddEventHandler("PLAYER_REGEN_DISABLED", logPlayerExtraData)