diff --git a/GuildSkadaHighScore.toc b/GuildSkadaHighScore.toc index b826101..cd633f0 100644 --- a/GuildSkadaHighScore.toc +++ b/GuildSkadaHighScore.toc @@ -3,6 +3,7 @@ ## Version: 0.01 ## Title: Guild Skada High Score ## Notes: Ranking for dps/hps for raid bosses in a guild. +## SavedVariables: GuildSkadaHighScoreDB ## Dependencies: Skada lib/lib.xml diff --git a/highscore.lua b/highscore.lua new file mode 100644 index 0000000..3aee5bc --- /dev/null +++ b/highscore.lua @@ -0,0 +1,113 @@ +local addonName, addonTable = ... + +-- Global functions for faster access +local tinsert = tinsert; +local tContains = tContains; + +-- Set up module +local addon = addonTable[1]; +local highscore = addon:NewModule("highscore", "AceEvent-3.0", "AceTimer-3.0") +addon.highscore = highscore; + +-- db defaults +addon.dbDefaults.realm.modules["highscore"] = { + ["zones"] = { + --[[ + ["*"] = { -- zoneId + zoneName = "Unknown", + encounters = { + ["*"] = { -- encounterId + encounterName = "Unknown", + difficulties = { + ["**"] = { + playerParses = {} -- List of objects "{playerInfo, role, dps, hps}" + }, + ["Normal"] = {}, + ["Heroic"] = {}, + ["Mythic"] = {} + } + } + } + } + --]] + } +} + + +local trackedZoneIds = {994} + +function highscore:GetOrCreateEncounterTable(zoneId, zoneName, encounterId, encounterName, difficultyName) + if not self.db.zones[zoneId] then + self.db.zones[zoneId] = {zoneName = zoneName, encounters = {}} + end + + local zone = self.db.zones[zoneId] + if not zone.encounters[encounterId] then + zone.encounters[encounterId] = { + encounterName = encounterName, + difficulties = { + Normal = {playerParses = {}}, + Heroic = {playerParses = {}}, + Mythic = {playerParses = {}} + }}; + end + + local encounter = zone.encounters[encounterId] + if not encounter.difficulties[difficultyName] then + return nil + else + return encounter.difficulties[difficultyName] + end +end + +function highscore:AddEncounterParseForPlayer(zoneId, zoneName, encounterId, encounterName, difficultyName, player) + local encounterTable = self:GetOrCreateEncounterTable(zoneId, + zoneName, encounterId, encounterName, difficultyName); + + if encounterTable then + local parse = { + playerId = player.id, + playerName = player.name, + role = player.role, + specName = player.specName, + itemLevel = player.itemLevel, + damage = player.damage, + healing = player.healing, + duration = encounter.duration + } + tinsert(encounterTable.playerParses, parse); + end +end + +function highscore:AddEncounterParsesForPlayers(encounter, players) + local zoneId = encounter.zoneId; + local zoneName = encounter.zoneName; + local encounterId = encounter.id; + local encounterName = encounter.name; + local difficultyName = encounter.difficultyName; + + assert(zoneId and zoneId > 1) + assert(zoneName) + assert(encounterId) + assert(encounterName) + assert(difficultyName) + assert(players) + + if not tContains(trackedZoneIds, zoneId) then + self:Debug("Current zone not not in tracked zones"); + return + end + + for _, player in ipairs(players) do + self:AddEncounterParseForPlayer(zoneId, zoneName, + encounterId, encounterName, difficultyName, player) + end +end + +function highscore:OnEnable() + self.db = addon.db.realm.modules["highscore"]; +end + +function highscore:OnDisable() + self.db = nil; +end diff --git a/inspect.lua b/inspect.lua index 2130e4e..dabe888 100644 --- a/inspect.lua +++ b/inspect.lua @@ -1,7 +1,14 @@ local addonName, addonTable = ... +-- Global functions for faster access +local tinsert = tinsert; +local floor = floor; +local wipe = wipe; + +-- Set up module local addon = addonTable[1]; -local inspect = addon.inspect; +local inspect = addon:NewModule("inspect", "AceEvent-3.0", "AceTimer-3.0") +addon.inspect = inspect; -- Constants local INSPECT_CACHE_TIMEOUT = 900; @@ -10,10 +17,7 @@ local INVENTORY_SLOT_NAMES = { "HandsSlot","WaistSlot","LegsSlot","FeetSlot","Finger0Slot","Finger1Slot", "Trinket0Slot","Trinket1Slot","MainHandSlot","SecondaryHandSlot" } - -inspect.inspectQueue = {}; -inspect.notifyInspectTimer = nil; -inspect.currentInspectPlayerId = nil; +local NOOP = function() end local playerGUID = UnitGUID("player"); local inspectCache = {}; @@ -65,12 +69,15 @@ local function getItemLevel(unitName) end local function hasPlayerInspectCache(playerId, ignoreExpired) - if not inspectCache[playerId] then - return false; -- No entry for player id - elseif not ignoreExpired and ((GetTime() - inspectCache[playerId].time) > INSPECT_CACHE_TIMEOUT) then - return false; -- Cache entry is too old + if inspectCache[playerId] and ignoreExpired then + return true -- We have a cache, might be expired + elseif inspectCache[playerId] and inspectCache[playerId].time then + local isExpired = (GetTime() - inspectCache[playerId].time) > INSPECT_CACHE_TIMEOUT + local hasAllAttributes = inspectCache[playerId].specName and inspectCache[playerId].itemLevel; + + return (not isExpired and hasAllAttributes) else - return true; + return false; -- No entry for player id end end @@ -83,15 +90,15 @@ end function inspect:StartNotifyInspectTimer() if not self.notifyInspectTimer then - self.notifyInspectTimer = addon:ScheduleRepeatingTimer(function() - self:NOTIFY_INSPECT_TIMER_DONE(); - end, 1) + self.notifyInspectTimer = self:ScheduleRepeatingTimer(function() + self:NOTIFY_INSPECT_TIMER_DONE() + end, 1); end end function inspect:StopNotifyInspectTimer() if self.notifyInspectTimer then - addon:CancelTimer(self.notifyInspectTimer); + self:CancelTimer(self.notifyInspectTimer); self.notifyInspectTimer = nil; end end @@ -108,7 +115,7 @@ function inspect:IsPlayerInInspectQueue(player) end function inspect:QueueInspect(player, callback) - addon:Debug("QueueNotifyInspect " .. player.name) + self:Debug("QueueNotifyInspect " .. player.name) if not self.inspectQueue[player.id] then self.inspectQueue[player.id] = {player = player, callbacks = {}}; @@ -138,6 +145,9 @@ function inspect:SetCachedInspectDataForPlayer(player) end function inspect:GetInspectDataForPlayer(player, callback) + -- Make sure we always have a callback + callback = callback and callback or NOOP; + if self:SetCachedInspectDataForPlayer(player) then return callback() elseif not CanInspect(player.name, false) then @@ -162,8 +172,6 @@ function inspect:GetInspectDataForPlayers(players, callback) end function inspect:ResolveInspect(playerId, success) - addon:Debug("ResolveInspect " .. playerId .. " " .. (success and "success" or "fail")) - if not self.inspectQueue[playerId] then return end @@ -172,6 +180,8 @@ function inspect:ResolveInspect(playerId, success) local callbacks = self.inspectQueue[playerId].callbacks; self.inspectQueue[playerId] = nil; + self:Debug("ResolveInspect " .. player.name .. " " .. (success and "success" or "fail")) + if success then if not hasPlayerInspectCache(player.id) then local specName = getTalentSpec(player.name); @@ -188,9 +198,20 @@ function inspect:ResolveInspect(playerId, success) end end -function inspect:NOTIFY_INSPECT_TIMER_DONE() - addon:Debug("NOTIFY_INSPECT_TIMER_DONE"); +function inspect:PreInspectGroup() + for i=1, GetNumGroupMembers() do + local playerName = GetRaidRosterInfo(i); + if playerName then + local playerId = UnitGUID(playerName) + if playerId and not hasPlayerInspectCache(playerId) and CanInspect(playerName) then + local player = {name=playerName, id = playerId} + self:QueueInspect(player, NOOP); + end + end + end +end +function inspect:NOTIFY_INSPECT_TIMER_DONE() -- Timeout any current inspection if self.currentInspectPlayerId then self:ResolveInspect(self.currentInspectPlayerId, false); @@ -211,4 +232,42 @@ function inspect:INSPECT_READY(evt, GUID) self:ResolveInspect(self.currentInspectPlayerId, true) self.currentInspectPlayerId = nil; end +end + +function inspect:GROUP_ROSTER_UPDATE(evt) + if not IsInGroup() then + self:Debug("Left group, wiping inspect cache"); + wipe(inspectCache); + wipe(inspect.inspectQueue); + inspect.currentInspectPlayerId = nil; + end +end + +function inspect:ZONE_CHANGED_NEW_AREA(evt) + if IsInInstance() and IsInRaid() then + -- We just zoned into an instance, try pre-inspecting the group + self:PreInspectGroup(); + end +end + +function inspect:OnEnable() + inspect.inspectQueue = {}; + inspect.notifyInspectTimer = nil; + inspect.currentInspectPlayerId = nil; + + self:RegisterEvent("INSPECT_READY"); + self:RegisterEvent("GROUP_ROSTER_UPDATE"); + self:RegisterEvent("ZONE_CHANGED_NEW_AREA"); +end + +function inspect:OnDisable() + self:UnregisterEvent("INSPECT_READY"); + self:UnregisterEvent("GROUP_ROSTER_UPDATE"); + self:UnregisterEvent("ZONE_CHANGED_NEW_AREA"); + + self:StopNotifyInspectTimer(); + + wipe(inspectCache); + wipe(inspect.inspectQueue); + inspect.currentInspectPlayerId = nil; end \ No newline at end of file diff --git a/lib/lib.xml b/lib/lib.xml index 579e7fe..a47ad4c 100644 --- a/lib/lib.xml +++ b/lib/lib.xml @@ -6,4 +6,5 @@ <Include file="Ace3\AceGUI-3.0\AceGUI-3.0.xml"/> <Include file="Ace3\AceHook-3.0\AceHook-3.0.xml"/> <Include file="Ace3\AceTimer-3.0\AceTimer-3.0.xml"/> + <Include file="Ace3\AceDB-3.0\AceDB-3.0.xml"/> </Ui> \ No newline at end of file diff --git a/main.lua b/main.lua index 3606815..de34b7a 100644 --- a/main.lua +++ b/main.lua @@ -1,21 +1,25 @@ local addonName, addonTable = ... local tinsert = tinsert; -local tremove = tremove; -local tContains = tContains; -local floor = floor; -- Create ACE3 addon -local addon = LibStub("AceAddon-3.0"):NewAddon(addonName, - "AceConsole-3.0", "AceEvent-3.0", "AceHook-3.0", "AceTimer-3.0") +local addon = LibStub("AceAddon-3.0"):NewAddon(addonName, "AceConsole-3.0", "AceEvent-3.0", "AceHook-3.0") + +-- Set up a default prototype for all modules +local modPrototype = { Debug = function(self, ...) addon:Debug(...) end } +addon:SetDefaultModulePrototype(modPrototype) + + +-- Db default settings +addon.dbDefaults = { + realm = { + modules = {} + } +} tinsert(addonTable, addon); _G[addonName] = addon -addon.currentEncounter = nil; -addon.currentZoneId = nil; -addon.guildName = nil; -addon.inspect = {}; local function getDifficultyNameById(difficultyId) if difficultyId == 7 or difficultyId == 17 then @@ -46,14 +50,30 @@ function addon:UpdateMyGuildName() end end +function addon:UpdateCurrentZone() + local zoneId, _ = GetCurrentMapAreaID() + local zoneName = GetRealZoneText(); + self.currentZone = {id = zoneId, name = zoneName}; + + if not IsInInstance() then + self:UnsetCurrentEncounter(); + end +end + function addon:SetCurrentEncounter(encounterId, encounterName, difficultyId, raidSize) - self.currentEncounter = { - id = encounterId, - name = encounterName, - difficultyId = difficultyId, - difficultyName = getDifficultyNameById(difficultyId), - raidSize = raidSize - } + + local difficultyName = getDifficultyNameById(difficultyId); + if difficultyName then + self.currentEncounter = { + zoneId = self.currentZone.id, + zoneName = self.currentZone.name, + id = encounterId, + name = encounterName, + difficultyId = difficultyId, + difficultyName = difficultyName, + raidSize = raidSize + } + end end function addon:UnsetCurrentEncounter() @@ -61,7 +81,6 @@ function addon:UnsetCurrentEncounter() end function addon:IsInMyGuild(playerName) - if 1 then return true end if self.guildName then local guildName, _, _ = GetGuildInfo(playerName) return guildName == self.guildName @@ -102,7 +121,7 @@ function addon:PLAYER_GUILD_UPDATE(evt, unitId) end function addon:ENCOUNTER_START(evt, encounterId, encounterName, difficultyId, raidSize) - self:Debug("ENCOUNTER_START") + self:Debug("ENCOUNTER_START " .. encounterId) self:SetCurrentEncounter(encounterId, encounterName, difficultyId, raidSize) end @@ -115,6 +134,11 @@ function addon:ENCOUNTER_END(evt, encounterId, encounterName, difficultyId, raid end end +function addon:ZONE_CHANGED_NEW_AREA(evt) + self:Debug("ZONE_CHANGED_NEW_AREA"); + self:UpdateCurrentZone(); +end + function addon:EndSegment() self:Debug("EndSegment") @@ -123,53 +147,47 @@ function addon:EndSegment() return end - local encounter = self.currentEncounter + local encounter = self.currentEncounter; + encounter.duration = Skada.last.time; + local players = self:GetGuildPlayersFromSet(Skada.last); self:SetRoleForPlayers(players); self.inspect:GetInspectDataForPlayers(players, function() - for i, player in ipairs(players) do - local name = player.name; - local damage = Skada:FormatNumber(player.damage); - local role = player.role; - local itemLevel = player.itemLevel and player.itemLevel or "N/A" - local specName = player.specName and player.specName or "N/A" - -- "(DAMAGE) Saniera - 20.1k (560 Shadow)" - self:Debug(format("(%s) %s - %s (%d %s)", role, name, damage, itemLevel, specName)); - end + self.highscore:AddEncounterParsesForPlayers(encounter, players); end) - --[[ - self:Printf("%s (%s - %d) %dm", - encounter.name, - encounter.difficultyName and encounter.difficultyName or "Unknown", - encounter.difficultyId, - encounter.raidSize); - ]]-- self:UnsetCurrentEncounter(); end - function addon:OnInitialize() + self.db = LibStub("AceDB-3.0"):New("GuildSkadaHighScoreDB", addon.dbDefaults) end function addon:OnEnable() + self.currentEncounter = nil; + self.currentZone = {}; + self.guildName = nil; + self:RegisterEvent("ENCOUNTER_START") self:RegisterEvent("ENCOUNTER_END") self:RegisterEvent("PLAYER_GUILD_UPDATE") - self:RegisterEvent("INSPECT_READY") + self:RegisterEvent("ZONE_CHANGED_NEW_AREA") self:SecureHook(Skada, "EndSegment") self:UpdateMyGuildName() + self:UpdateCurrentZone(); end function addon:OnDisable() - self:UnRegisterEvent("ENCOUNTER_START") - self:UnRegisterEvent("ENCOUNTER_END") - self:UnRegisterEvent("PLAYER_GUILD_UPDATE") - self:UnRegisterEvent("INSPECT_READY") - - self:StopNotifyInspectTimer(); + self.currentEncounter = nil; + self.currentZone = {}; + self.guildName = nil; + + self:UnregisterEvent("ENCOUNTER_START") + self:UnregisterEvent("ENCOUNTER_END") + self:UnregisterEvent("PLAYER_GUILD_UPDATE") + self:UnregisterEvent("ZONE_CHANGED_NEW_AREA") self:UnHook(Skada, "EndSegment") end diff --git a/main.xml b/main.xml index 2af183a..82c4b1f 100644 --- a/main.xml +++ b/main.xml @@ -1,4 +1,5 @@ <Ui xsi:schemaLocation="http://www.blizzard.com/wow/ui/ ..\..\FrameXML\UI.xsd"> <Script file="main.lua"/> <Script file="inspect.lua"/> + <Script file="highscore.lua"/> </Ui> \ No newline at end of file