adam@0: local _, AskMrRobot = ... yellowfive@11: local L = AskMrRobot.L; adam@0: adam@0: -- initialize the ExportTab class adam@0: AskMrRobot.CombatLogTab = AskMrRobot.inheritsFrom(AskMrRobot.Frame) adam@0: yellowfive@11: -- these are valid keys in AmrLogData, all others will be deleted yellowfive@11: local _logDataKeys = { yellowfive@11: ["_logging"] = true, yellowfive@11: ["_autoLog"] = true, yellowfive@11: ["_lastZone"] = true, yellowfive@11: ["_lastDiff"] = true, yellowfive@11: ["_current2"] = true, yellowfive@11: ["_history2"] = true, yellowfive@11: ["_wipes"] = true, yellowfive@11: ["_lastWipe"] = true, yellowfive@11: ["_currentExtra"] = true, yellowfive@11: ["_historyExtra"] = true yellowfive@11: }; yellowfive@11: yellowfive@11: local _undoButton = false yellowfive@11: adam@0: -- helper to create text for this tab adam@0: local function CreateText(tab, font, relativeTo, xOffset, yOffset, text) adam@0: local t = tab:CreateFontString(nil, "ARTWORK", font) adam@0: t:SetPoint("TOPLEFT", relativeTo, "BOTTOMLEFT", xOffset, yOffset) yellowfive@11: t:SetPoint("RIGHT", tab, "RIGHT", -5, 0) adam@0: t:SetWidth(t:GetWidth()) adam@0: t:SetJustifyH("LEFT") adam@0: t:SetText(text) adam@0: adam@0: return t adam@0: end adam@0: adam@0: local function newCheckbox(tab, label, tooltipTitle, description, onClick) adam@0: local check = CreateFrame("CheckButton", "AmrCheck" .. label, tab, "InterfaceOptionsCheckButtonTemplate") adam@0: check:SetScript("OnClick", function(self) adam@0: PlaySound(self:GetChecked() and "igMainMenuOptionCheckBoxOn" or "igMainMenuOptionCheckBoxOff") adam@0: onClick(self, self:GetChecked() and true or false) adam@0: end) adam@0: check.label = _G[check:GetName() .. "Text"] adam@0: check.label:SetText(label) adam@0: check.tooltipText = tooltipTitle adam@0: check.tooltipRequirement = description adam@0: return check adam@0: end adam@0: adam@0: function AskMrRobot.CombatLogTab:new(parent) adam@0: yellowfive@11: local tab = AskMrRobot.Frame:new(nil, parent) adam@0: setmetatable(tab, { __index = AskMrRobot.CombatLogTab }) adam@0: tab:SetPoint("TOPLEFT") adam@0: tab:SetPoint("BOTTOMRIGHT") adam@0: tab:Hide() adam@0: yellowfive@11: -- tab header adam@0: local text = tab:CreateFontString(nil, "ARTWORK", "GameFontNormalLarge") adam@0: text:SetPoint("TOPLEFT", 0, -5) adam@0: text:SetText("Combat Logging") yellowfive@11: yellowfive@11: --scrollframe yellowfive@11: tab.scrollframe = AskMrRobot.ScrollFrame:new(nil, tab) yellowfive@11: tab.scrollframe:SetPoint("TOPLEFT", tab, "TOPLEFT", 0, -30) yellowfive@11: tab.scrollframe:SetPoint("BOTTOMRIGHT", tab, "BOTTOMRIGHT", -30, 10) yellowfive@11: yellowfive@11: local content = tab.scrollframe.content yellowfive@11: content:SetHeight(730) adam@0: yellowfive@11: local btn = CreateFrame("Button", "AmrCombatLogStart", content, "UIPanelButtonTemplate") yellowfive@11: btn:SetPoint("TOPLEFT", content, "TOPLEFT", 0, 0) adam@0: btn:SetText("Start Logging") adam@0: btn:SetWidth(120) adam@0: btn:SetHeight(30) adam@0: tab.btnStart = btn adam@0: adam@0: btn:SetScript("OnClick", function() yellowfive@11: tab:ToggleLogging() adam@0: end) adam@0: adam@0: yellowfive@11: text = content:CreateFontString(nil, "ARTWORK", "GameFontWhite") yellowfive@11: text:SetPoint("LEFT", btn, "RIGHT", 10, 0) yellowfive@11: tab.loggingStatus = text; adam@0: yellowfive@11: local autoChk = newCheckbox(content, yellowfive@11: L.AMR_COMBATLOGTAB_CHECKBOX_AUTOLOG_SOO_LABEL, yellowfive@11: L.AMR_COMBATLOGTAB_CHECKBOX_AUTOLOG_SOO_TOOLTIP_TITLE, yellowfive@11: L.AMR_COMBATLOGTAB_CHECKBOX_AUTOLOG_SOO_DESCRIPTION, adam@0: function(self, value) adam@0: if value then adam@0: AmrLogData._autoLog[AskMrRobot.instanceIds.SiegeOfOrgrimmar] = "enabled" adam@0: else adam@0: AmrLogData._autoLog[AskMrRobot.instanceIds.SiegeOfOrgrimmar] = "disabled" adam@0: end adam@0: adam@0: AmrLogData._lastZone = nil adam@0: AmrLogData._lastDiff = nil adam@0: tab:UpdateAutoLogging() adam@0: end adam@0: ) adam@0: autoChk:SetChecked(AmrLogData._autoLog[AskMrRobot.instanceIds.SiegeOfOrgrimmar] == "enabled") yellowfive@11: autoChk:SetPoint("TOPLEFT", btn, "BOTTOMLEFT", 0, -10) adam@0: autoChk:SetHeight(30) adam@0: adam@0: yellowfive@11: local text = CreateText(content, "GameFontNormalLarge", autoChk, 0, -20, L.AMR_COMBATLOGTAB_INFIGHT) yellowfive@11: yellowfive@11: btn = CreateFrame("Button", "AmrCombatLogWipe", autoChk, "UIPanelButtonTemplate") yellowfive@11: btn:SetPoint("TOPLEFT", text, "BOTTOMLEFT", 0, -10) yellowfive@11: btn:SetText("Wipe") yellowfive@11: btn:SetWidth(70) yellowfive@11: btn:SetHeight(30) yellowfive@11: btn:SetScript("OnClick", function() yellowfive@11: tab:LogWipe() yellowfive@11: end) yellowfive@11: yellowfive@11: tab.btnWipe = btn yellowfive@11: yellowfive@11: local text2 = CreateText(content, "GameFontWhite", text, 80, -12, L.AMR_COMBATLOGTAB_WIPE_1) yellowfive@11: text2 = CreateText(content, "GameFontWhite", text2, 0, -2, L.AMR_COMBATLOGTAB_WIPE_2) yellowfive@11: text2 = CreateText(content, "GameFontWhite", text2, 0, -2, L.AMR_COMBATLOGTAB_WIPE_3) yellowfive@11: yellowfive@11: btn = CreateFrame("Button", "AmrCombatLogUnWipe", content, "UIPanelButtonTemplate") yellowfive@11: btn:SetPoint("LEFT", text, "LEFT", 0, 0) yellowfive@11: btn:SetPoint("TOP", text2, "BOTTOM", 0, -10) yellowfive@11: btn:SetText("Undo") yellowfive@11: btn:SetWidth(70) yellowfive@11: btn:SetHeight(30) yellowfive@11: btn:Hide() -- initially hidden yellowfive@11: btn:SetScript("OnClick", function() yellowfive@11: tab:LogUnwipe() yellowfive@11: end) yellowfive@11: tab.btnUnwipe = btn yellowfive@11: yellowfive@11: text = content:CreateFontString(nil, "ARTWORK", "GameFontWhite") yellowfive@11: text:SetPoint("LEFT", btn, "LEFT", 80, 0) yellowfive@11: tab.lastWipeLabel = text yellowfive@11: yellowfive@11: text = CreateText(tab, "GameFontNormalLarge", btn, 0, -20, L.AMR_COMBATLOGTAB_HEADLINE_OVER_BUTTON) yellowfive@11: yellowfive@11: btn = CreateFrame("Button", "AmrCombatLogSaveCharData", content, "UIPanelButtonTemplate") adam@0: btn:SetPoint("TOPLEFT", text, "BOTTOMLEFT", 0, -5) yellowfive@11: btn:SetText(L.AMR_COMBATLOGTAB_SAVE_CHARACTER) adam@0: btn:SetWidth(150) adam@0: btn:SetHeight(30) adam@0: yellowfive@11: -- reload the UI will save character data to disk yellowfive@11: btn:SetScript("OnClick", ReloadUI) adam@0: yellowfive@11: text = CreateText(content, "GameFontWhite", btn, 0, -15, L.AMR_COMBATLOGTAB_SAVE_CHARACTER_INFO) adam@0: yellowfive@11: text = CreateText(content, "GameFontNormalLarge", text, 0, -30, L.AMR_COMBATLOGTAB_INSTRUCTIONS) yellowfive@11: text = CreateText(content, "GameFontWhite", text, 0, -10, L.AMR_COMBATLOGTAB_INSTRUCTIONS_1) yellowfive@11: text = CreateText(content, "GameFontWhite", text, 0, -10, L.AMR_COMBATLOGTAB_INSTRUCTIONS_2) yellowfive@11: text = CreateText(content, "GameFontWhite", text, 0, -10, L.AMR_COMBATLOGTAB_INSTRUCTIONS_3) yellowfive@11: text = CreateText(content, "GameFontWhite", text, 0, -10, L.AMR_COMBATLOGTAB_INSTRUCTIONS_4) yellowfive@11: yellowfive@11: text = CreateText(content, "GameFontNormalSmall", text, 0, -30, L.AMR_COMBATLOGTAB_INSTRUCTIONS_5) yellowfive@11: text = CreateText(content, "GameFontNormalSmall", text, 0, -10, L.AMR_COMBATLOGTAB_INSTRUCTIONS_6) yellowfive@11: text = CreateText(content, "GameFontNormalSmall", text, 0, -10, L.AMR_COMBATLOGTAB_INSTRUCTIONS_7) adam@0: adam@0: btn = CreateFrame("Button", "AmrCombatLogTest", tab, "UIPanelButtonTemplate") adam@0: btn:SetPoint("TOPLEFT", text, "BOTTOMLEFT", 0, -15) adam@0: btn:SetText("Test") adam@0: btn:SetWidth(120) adam@0: btn:SetHeight(30) adam@0: adam@0: btn:SetScript("OnClick", function() yellowfive@11: yellowfive@11: local t = time() yellowfive@11: AskMrRobot.SaveAll() yellowfive@11: AskMrRobot.ExportToAddonChat(t) yellowfive@11: AskMrRobot.ExportLoggingData(t) adam@0: end) adam@0: adam@0: -- when we start up, ensure that logging is still enabled if it was enabled when they last used the addon adam@0: if (tab:IsLogging()) then adam@0: SetCVar("advancedCombatLogging", 1) adam@0: LoggingCombat(true) adam@0: end adam@0: adam@0: -- if auto-logging is enabled, do a check when the addon is loaded to make sure that state is set correctly adam@0: if AmrLogData._autoLog[AskMrRobot.instanceIds.SiegeOfOrgrimmar] == "enabled" then adam@0: tab:UpdateAutoLogging() adam@0: end adam@0: adam@0: tab:SetScript("OnShow", function() adam@0: tab:Update() adam@0: end) adam@0: adam@0: return tab adam@0: end adam@0: adam@0: function AskMrRobot.CombatLogTab:IsLogging() adam@0: return AmrLogData._logging == true adam@0: end adam@0: adam@0: function AskMrRobot.CombatLogTab:StartLogging() adam@0: yellowfive@11: local now = time() yellowfive@11: local oldDuration = 60 * 60 * 24 * 10 yellowfive@11: adam@0: -- archive the current logging session so that users don't accidentally blow away data before uploading it adam@0: if AmrLogData._current2 ~= nil then adam@0: if not AmrLogData._history2 then AmrLogData._history2 = {} end adam@0: adam@0: -- add new entries adam@0: for name, timeList in AskMrRobot.spairs(AmrLogData._current2) do adam@0: if not AmrLogData._history2[name] then AmrLogData._history2[name] = {} end adam@0: for timestamp, dataString in AskMrRobot.spairs(timeList) do adam@0: AmrLogData._history2[name][timestamp] = dataString adam@0: end adam@0: end adam@0: adam@0: -- delete entries that are more than 10 days old adam@0: for name, timeList in AskMrRobot.spairs(AmrLogData._history2) do adam@0: for timestamp, dataString in AskMrRobot.spairs(timeList) do yellowfive@11: if difftime(now, tonumber(timestamp)) > oldDuration then adam@0: timeList[timestamp] = nil adam@0: end adam@0: end adam@0: adam@0: local count = 0 adam@0: for timestamp, dataString in pairs(timeList) do adam@0: count = count + 1 adam@0: end adam@0: if count == 0 then adam@0: AmrLogData._history2[name] = nil adam@0: end adam@0: end adam@0: end adam@0: yellowfive@11: -- same idea with extra info (auras, pets, whatever we end up adding to it) yellowfive@11: if AmrLogData._currentExtra ~= nil then yellowfive@11: if not AmrLogData._historyExtra then AmrLogData._historyExtra = {} end yellowfive@11: yellowfive@11: -- add new entries yellowfive@11: for name, timeList in AskMrRobot.spairs(AmrLogData._currentExtra) do yellowfive@11: if not AmrLogData._historyExtra[name] then AmrLogData._historyExtra[name] = {} end yellowfive@11: for timestamp, dataString in AskMrRobot.spairs(timeList) do yellowfive@11: AmrLogData._historyExtra[name][timestamp] = dataString yellowfive@11: end yellowfive@11: end yellowfive@11: yellowfive@11: -- delete entries that are more than 10 days old yellowfive@11: for name, timeList in AskMrRobot.spairs(AmrLogData._historyExtra) do yellowfive@11: for timestamp, dataString in AskMrRobot.spairs(timeList) do yellowfive@11: if difftime(now, tonumber(timestamp)) > oldDuration then yellowfive@11: timeList[timestamp] = nil yellowfive@11: end yellowfive@11: end yellowfive@11: yellowfive@11: local count = 0 yellowfive@11: for timestamp, dataString in pairs(timeList) do yellowfive@11: count = count + 1 yellowfive@11: end yellowfive@11: if count == 0 then yellowfive@11: AmrLogData._historyExtra[name] = nil yellowfive@11: end yellowfive@11: end yellowfive@11: end yellowfive@11: yellowfive@11: yellowfive@11: -- delete _wipes entries that are more than 10 days old yellowfive@11: if AmrLogData._wipes then yellowfive@11: local i = 1 yellowfive@11: while i <= #AmrLogData._wipes do yellowfive@11: local t = AmrLogData._wipes[i] yellowfive@11: if difftime(now, t) > oldDuration then yellowfive@11: tremove(AmrLogData._wipes, i) yellowfive@11: else yellowfive@11: i = i + 1 yellowfive@11: end yellowfive@11: end yellowfive@11: end yellowfive@11: yellowfive@11: -- delete the _lastWipe if it is more than 10 days old yellowfive@11: if AmrLogData._lastWipe and difftime(now, AmrLogData._lastWipe) > oldDuration then yellowfive@11: AmrLogData_lastWipe = nil yellowfive@11: end yellowfive@11: adam@0: -- clean up old-style logging data from previous versions of the addon adam@0: for k, v in AskMrRobot.spairs(AmrLogData) do yellowfive@11: if not _logDataKeys[k] then adam@0: AmrLogData[k] = nil adam@0: end adam@0: end adam@0: adam@0: -- start a new logging session adam@0: AmrLogData._current2 = {} yellowfive@11: AmrLogData._currentExtra = {} adam@0: AmrLogData._logging = true adam@0: adam@0: -- always enable advanced combat logging via our addon, gathers more detailed data for better analysis adam@0: SetCVar("advancedCombatLogging", 1) adam@0: adam@0: LoggingCombat(true) adam@0: self:Update() yellowfive@11: yellowfive@11: AskMrRobot.AmrUpdateMinimap() adam@0: yellowfive@11: print(L.AMR_COMBATLOGTAB_IS_LOGGING) adam@0: end adam@0: adam@0: function AskMrRobot.CombatLogTab:StopLogging() adam@0: LoggingCombat(false) adam@0: AmrLogData._logging = false adam@0: self:Update() adam@0: yellowfive@11: AskMrRobot.AmrUpdateMinimap() yellowfive@11: yellowfive@11: print(L.AMR_COMBATLOGTAB_STOPPED_LOGGING) yellowfive@11: end yellowfive@11: yellowfive@11: function AskMrRobot.CombatLogTab:ToggleLogging() yellowfive@11: if self:IsLogging() then yellowfive@11: self:StopLogging() yellowfive@11: else yellowfive@11: self:StartLogging() yellowfive@11: end adam@0: end adam@0: adam@0: -- update the panel and state adam@0: function AskMrRobot.CombatLogTab:Update() adam@0: local isLogging = self:IsLogging() adam@0: adam@0: if isLogging then yellowfive@11: self.btnStart:SetText(L.AMR_COMBATLOGTAB_STOP_LOGGING) yellowfive@11: self.loggingStatus:SetText(L.AMR_COMBATLOGTAB_CURRENTLY_LOGGING) adam@0: else yellowfive@11: self.btnStart:SetText(L.AMR_COMBATLOGTAB_START_LOGGING) yellowfive@11: self.loggingStatus:SetText("") adam@0: end yellowfive@11: yellowfive@11: if AmrLogData._lastWipe then yellowfive@11: self.lastWipeLabel:SetText(L.AMR_COMBATLOGTAB_LASTWIPE:format(date('%B %d', AmrLogData._lastWipe), date('%I:%M %p', AmrLogData._lastWipe))) yellowfive@11: self.btnUnwipe:Show() yellowfive@11: else yellowfive@11: self.lastWipeLabel:SetText("") yellowfive@11: self.btnUnwipe:Hide() yellowfive@11: end yellowfive@11: adam@0: end adam@0: adam@0: -- called to update logging state when auto-logging is enabled adam@0: function AskMrRobot.CombatLogTab:UpdateAutoLogging() adam@0: adam@0: -- get the info about the instance adam@0: --local zone, zonetype, difficultyIndex, difficultyName, maxPlayers, dynamicDifficulty, isDynamic, instanceMapID = GetInstanceInfo() adam@0: local zone, _, difficultyIndex, _, _, _, _, instanceMapID = GetInstanceInfo() adam@0: --local difficulty = difficultyIndex adam@0: -- Unless Blizzard fixes scenarios to not return nil, let's hardcode this into returning "scenario" -Znuff adam@0: --if zonetype == nil and difficultyIndex == 1 then adam@0: --zonetype = "scenario" adam@0: --end adam@0: adam@0: if zone == AmrLogData._lastZone and difficultyIndex == AmrLogData._lastDiff then adam@0: -- do nothing if the zone hasn't actually changed, otherwise we may override the user's manual enable/disable adam@0: return adam@0: end adam@0: adam@0: AmrLogData._lastZone = zone adam@0: AmrLogData._lastDiff = difficultyIndex adam@0: adam@0: if AmrLogData._autoLog[AskMrRobot.instanceIds.SiegeOfOrgrimmar] == "enabled" then adam@0: if tonumber(instanceMapID) == AskMrRobot.instanceIds.SiegeOfOrgrimmar then adam@0: -- if in SoO, make sure logging is on adam@0: if not self:IsLogging() then adam@0: self:StartLogging() adam@0: end adam@0: else adam@0: -- not in SoO, turn logging off adam@0: if self:IsLogging() then adam@0: self:StopLogging() adam@0: end adam@0: end adam@0: end adam@0: adam@0: end adam@0: yellowfive@11: local function RaidChatType() yellowfive@11: if UnitIsGroupAssistant("player") or UnitIsGroupLeader("player") then yellowfive@11: return "RAID_WARNING" yellowfive@11: else yellowfive@11: return "RAID" yellowfive@11: end yellowfive@11: end yellowfive@11: yellowfive@11: -- used to store wipes to AmrLogData so that we trim data after the wipe yellowfive@11: function AskMrRobot.CombatLogTab:LogWipe() yellowfive@11: local t = time() yellowfive@11: tinsert(AmrLogData._wipes, t) yellowfive@11: AmrLogData._lastWipe = t yellowfive@11: yellowfive@11: if GetNumGroupMembers() > 0 then yellowfive@11: SendChatMessage(L.AMR_COMBATLOGTAB_WIPE_CHAT, RaidChatType()) yellowfive@11: end yellowfive@11: print(string.format(L.AMR_COMBATLOGTAB_WIPE_MSG, date('%I:%M %p', t))) yellowfive@11: yellowfive@11: self:Update() yellowfive@11: --AskMrRobot.wait(301, AskMrRobot.CombatLogTab.Update, self) yellowfive@11: end yellowfive@11: yellowfive@11: -- used to undo the wipe command yellowfive@11: function AskMrRobot.CombatLogTab:LogUnwipe() yellowfive@11: local t = AmrLogData._lastWipe yellowfive@11: if not t then yellowfive@11: print(L.AMR_COMBATLOGTAB_NOWIPES) yellowfive@11: else yellowfive@11: tremove(AmrLogData._wipes) yellowfive@11: AmrLogData._lastWipe = nil yellowfive@11: print(string.format(L.AMR_COMBATLOGTAB_UNWIPE_MSG, date('%I:%M %p', t))) yellowfive@11: end yellowfive@11: self:Update() yellowfive@11: end yellowfive@11: yellowfive@11: -- initialize the AmrLogData variable yellowfive@11: function AskMrRobot.CombatLogTab.InitializeVariable() yellowfive@11: if not AmrLogData then AmrLogData = {} end yellowfive@11: if not AmrLogData._autoLog then AmrLogData._autoLog = {} end yellowfive@11: if not AmrLogData._autoLog[AskMrRobot.instanceIds.SiegeOfOrgrimmar] then yellowfive@11: AmrLogData._autoLog[AskMrRobot.instanceIds.SiegeOfOrgrimmar] = "disabled" yellowfive@11: end yellowfive@11: AmrLogData._wipes = AmrLogData._wipes or {} yellowfive@11: end yellowfive@11: yellowfive@11: function AskMrRobot.CombatLogTab.SaveExtras(data, timestamp) yellowfive@11: yellowfive@11: for name,val in pairs(data) do yellowfive@11: -- record aura stuff, we never check for duplicates, need to know it at each point in time yellowfive@11: if AmrLogData._currentExtra[name] == nil then yellowfive@11: AmrLogData._currentExtra[name] = {} yellowfive@11: end yellowfive@11: AmrLogData._currentExtra[name][timestamp] = val yellowfive@11: end yellowfive@11: end yellowfive@11: adam@0: -- read a message sent to the addon channel with a player's info at the time an encounter started adam@0: function AskMrRobot.CombatLogTab:ReadAddonMessage(message) adam@0: adam@0: -- message will be of format: timestamp\nrealm\nname\n[stuff] adam@0: local parts = {} adam@0: for part in string.gmatch(message, "([^\n]+)") do adam@0: tinsert(parts, part) adam@0: end adam@0: adam@0: local timestamp = parts[1] adam@0: local name = parts[2] .. ":" .. parts[3] adam@0: local data = parts[4] adam@0: adam@0: if (data == "done") then adam@0: -- we have finished receiving this message; now process it to reduce the amount of duplicate data adam@0: local setup = AmrLogData._current2[name][timestamp] yellowfive@11: adam@0: if (AmrLogData._previousSetup == nil) then adam@0: AmrLogData._previousSetup = {} adam@0: end adam@0: adam@0: local previousSetup = AmrLogData._previousSetup[name] adam@0: adam@0: if (previousSetup == setup) then adam@0: -- if the last-seen setup for this player is the same as the current one, we don't need this entry adam@0: AmrLogData._current2[name][timestamp] = nil adam@0: else adam@0: -- record the last-seen setup adam@0: AmrLogData._previousSetup[name] = setup adam@0: end yellowfive@11: adam@0: else adam@0: -- concatenate messages with the same timestamp+name adam@0: if (AmrLogData._current2[name] == nil) then adam@0: AmrLogData._current2[name] = {} adam@0: end adam@0: adam@0: if (AmrLogData._current2[name][timestamp] == nil) then adam@0: AmrLogData._current2[name][timestamp] = data adam@0: else adam@0: AmrLogData._current2[name][timestamp] = AmrLogData._current2[name][timestamp] .. data adam@0: end adam@0: end adam@0: end