diff --git a/GuildSkadaHighScore.toc b/GuildSkadaHighScore.toc index e30a889..b826101 100644 --- a/GuildSkadaHighScore.toc +++ b/GuildSkadaHighScore.toc @@ -1,6 +1,6 @@ ## Interface: 60000 -## Author: Jultomten -## Version: 9000.1 +## Author: Verath (Peter Eliasson) +## Version: 0.01 ## Title: Guild Skada High Score ## Notes: Ranking for dps/hps for raid bosses in a guild. ## Dependencies: Skada diff --git a/inspect.lua b/inspect.lua index e69de29..2130e4e 100644 --- a/inspect.lua +++ b/inspect.lua @@ -0,0 +1,214 @@ +local addonName, addonTable = ... + +local addon = addonTable[1]; +local inspect = addon.inspect; + +-- Constants +local INSPECT_CACHE_TIMEOUT = 900; +local INVENTORY_SLOT_NAMES = { + "HeadSlot","NeckSlot","ShoulderSlot","BackSlot","ChestSlot","WristSlot", + "HandsSlot","WaistSlot","LegsSlot","FeetSlot","Finger0Slot","Finger1Slot", + "Trinket0Slot","Trinket1Slot","MainHandSlot","SecondaryHandSlot" +} + +inspect.inspectQueue = {}; +inspect.notifyInspectTimer = nil; +inspect.currentInspectPlayerId = nil; + +local playerGUID = UnitGUID("player"); +local inspectCache = {}; + +local function getTalentSpec(unitName) + if unitName == "player" then + local spec = GetSpecialization(); + if spec and spec > 0 then + local _, name = GetSpecializationInfo(spec); + return name; + end + else + local spec = GetInspectSpecialization(unitName) + if spec and spec > 0 then + local role = GetSpecializationRoleByID(spec); + if role then + local _, name = GetSpecializationInfoByID(spec); + return name + end + end + end +end + +local function getItemLevel(unitName) + if unitName == "player" then + local _, equipped = GetAverageItemLevel(); + return floor(equipped); + else + local total, numItems = 0, 0; + for i = 1, #INVENTORY_SLOT_NAMES do + local slotName = INVENTORY_SLOT_NAMES[i]; + local slotId = GetInventorySlotInfo(slotName); + local itemLink = GetInventoryItemLink(unitName, slotId); + + if itemLink then + local _, _, _, itemLevel = GetItemInfo(itemLink) + if itemLevel and itemLevel > 0 then + numItems = numItems + 1; + total = total + itemLevel; + end + end + end + if total < 1 or numItems < 15 then + return nil + else + return floor(total / numItems) + end + end +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 + else + return true; + end +end + +local function setPlayerInspectCache(playerId, specName, itemLevel) + inspectCache[playerId] = {}; + inspectCache[playerId].time = GetTime(); + inspectCache[playerId].specName = specName; + inspectCache[playerId].itemLevel = itemLevel; +end + +function inspect:StartNotifyInspectTimer() + if not self.notifyInspectTimer then + self.notifyInspectTimer = addon:ScheduleRepeatingTimer(function() + self:NOTIFY_INSPECT_TIMER_DONE(); + end, 1) + end +end + +function inspect:StopNotifyInspectTimer() + if self.notifyInspectTimer then + addon:CancelTimer(self.notifyInspectTimer); + self.notifyInspectTimer = nil; + end +end + +function inspect:IsPlayerInInspectQueue(player) + local playerInQueue = false + for _, p in ipairs(self.notifyInspectQueue) do + if p.name == player.name then + playerInQueue = true; + break; + end + end + return playerInQueue; +end + +function inspect:QueueInspect(player, callback) + addon:Debug("QueueNotifyInspect " .. player.name) + + if not self.inspectQueue[player.id] then + self.inspectQueue[player.id] = {player = player, callbacks = {}}; + end + tinsert(self.inspectQueue[player.id].callbacks, callback); + + self:StartNotifyInspectTimer(); +end + +function inspect:SetCachedInspectDataForPlayer(player) + if player.id == playerGUID then + player.specName = getTalentSpec("player") + player.itemLevel = getItemLevel("player"); + return true + elseif hasPlayerInspectCache(player.id, false) then + player.specName = inspectCache[player.id].specName; + player.itemLevel = inspectCache[player.id].itemLevel; + return true + elseif hasPlayerInspectCache(player.id, true) then + -- Expired cache, but better than nothing + player.specName = inspectCache[player.id].specName; + player.itemLevel = inspectCache[player.id].itemLevel; + return false + else + return false + end +end + +function inspect:GetInspectDataForPlayer(player, callback) + if self:SetCachedInspectDataForPlayer(player) then + return callback() + elseif not CanInspect(player.name, false) then + return callback() + else + self:QueueInspect(player, callback); + end +end + +function inspect:GetInspectDataForPlayers(players, callback) + local totalCallbacks = #players; + local doneCallbacks = 0; + + for _, player in ipairs(players) do + self:GetInspectDataForPlayer(player, function() + doneCallbacks = doneCallbacks + 1; + if doneCallbacks == totalCallbacks then + callback(); + end + end) + end +end + +function inspect:ResolveInspect(playerId, success) + addon:Debug("ResolveInspect " .. playerId .. " " .. (success and "success" or "fail")) + + if not self.inspectQueue[playerId] then + return + end + + local player = self.inspectQueue[playerId].player; + local callbacks = self.inspectQueue[playerId].callbacks; + self.inspectQueue[playerId] = nil; + + if success then + if not hasPlayerInspectCache(player.id) then + local specName = getTalentSpec(player.name); + local itemLevel = getItemLevel(player.name); + setPlayerInspectCache(player.id, specName, itemLevel); + end + + player.specName = inspectCache[player.id].specName; + player.itemLevel = inspectCache[player.id].itemLevel; + end + + for _,callback in ipairs(callbacks) do + callback(); + end +end + +function inspect:NOTIFY_INSPECT_TIMER_DONE() + addon:Debug("NOTIFY_INSPECT_TIMER_DONE"); + + -- Timeout any current inspection + if self.currentInspectPlayerId then + self:ResolveInspect(self.currentInspectPlayerId, false); + self.currentInspectPlayerId = nil; + end + + local playerId, inspectData = next(self.inspectQueue); + if playerId then + NotifyInspect(inspectData.player.name); + self.currentInspectPlayerId = playerId; + else + self:StopNotifyInspectTimer(); + end +end + +function inspect:INSPECT_READY(evt, GUID) + if self.currentInspectPlayerId == GUID then + self:ResolveInspect(self.currentInspectPlayerId, true) + self.currentInspectPlayerId = nil; + end +end \ No newline at end of file diff --git a/main.lua b/main.lua index 998909e..3606815 100644 --- a/main.lua +++ b/main.lua @@ -13,22 +13,9 @@ tinsert(addonTable, addon); _G[addonName] = addon addon.currentEncounter = nil; +addon.currentZoneId = nil; addon.guildName = nil; addon.inspect = {}; -addon.notifyInspectQueue = {}; -addon.notifyInspectTimer = nil; -addon.currentInspectPlayer = nil; -addon.pendingInspects = {}; - -local INSPECT_CACHE_TIMEOUT = 900; -local INVENTORY_SLOT_NAMES = { - "HeadSlot","NeckSlot","ShoulderSlot","BackSlot","ChestSlot","WristSlot", - "HandsSlot","WaistSlot","LegsSlot","FeetSlot","Finger0Slot","Finger1Slot", - "Trinket0Slot","Trinket1Slot","MainHandSlot","SecondaryHandSlot" -} - -local playerGUID = UnitGUID("player"); -local inspectCache = {}; local function getDifficultyNameById(difficultyId) if difficultyId == 7 or difficultyId == 17 then @@ -59,6 +46,20 @@ function addon:UpdateMyGuildName() end end +function addon:SetCurrentEncounter(encounterId, encounterName, difficultyId, raidSize) + self.currentEncounter = { + id = encounterId, + name = encounterName, + difficultyId = difficultyId, + difficultyName = getDifficultyNameById(difficultyId), + raidSize = raidSize + } +end + +function addon:UnsetCurrentEncounter() + self.currentEncounter = nil +end + function addon:IsInMyGuild(playerName) if 1 then return true end if self.guildName then @@ -81,224 +82,16 @@ function addon:GetGuildPlayersFromSet(skadaSet) return players end -function addon:GetTalentSpec(unitName) - if unitName == "player" then - local spec = GetSpecialization(); - if spec and spec > 0 then - local _, name = GetSpecializationInfo(spec); - return name; - end - else - local spec = GetInspectSpecialization(unitName) - if spec and spec > 0 then - local role = GetSpecializationRoleByID(spec); - if role then - local _, name = GetSpecializationInfoByID(spec); - return name - end - end - end -end - -function addon:GetItemLevel(unitName) - if unitName == "player" then - local _, equipped = GetAverageItemLevel(); - return floor(equipped); - else - local total, numItems = 0, 0; - for i = 1, #INVENTORY_SLOT_NAMES do - local slotName = INVENTORY_SLOT_NAMES[i]; - local slotId = GetInventorySlotInfo(slotName); - local itemLink = GetInventoryItemLink(unitName, slotId); - - if itemLink then - local _, _, _, itemLevel = GetItemInfo(itemLink) - if itemLevel and itemLevel > 0 then - numItems = numItems + 1; - total = total + itemLevel; - end - end - end - if total < 1 or numItems < 15 then - return nil - else - return floor(total / numItems) - end - end -end - -function addon:UpdatePlayerInspectCache(playerId, unitName) - if UnitExists(unitName) then - inspectCache[playerId] = {} - inspectCache[playerId].time = GetTime() - inspectCache[playerId].specName = self:GetTalentSpec(unitName) - inspectCache[playerId].itemLevel = self:GetItemLevel(unitName) - end -end - -function addon:HasPlayerInspectCache(playerId) - if not inspectCache[playerId] then - return false; -- No entry for player id - elseif (GetTime() - inspectCache[playerId].time) > INSPECT_CACHE_TIMEOUT then - inspectCache[playerId] = nil; - return false; -- Cache entry is too old - else - return true; - end -end - -function addon:FetchInformationForPlayers(players, callback) - local playersToInspect = {} - +function addon:SetRoleForPlayers(players) for _, player in ipairs(players) do - if player.id == playerGUID then - player.specName = self:GetTalentSpec("player"); - player.itemLevel = self:GetItemLevel("player") - elseif self:HasPlayerInspectCache(player.id) then - player.specName = inspectCache[player.id].specName; - player.itemLevel = inspectCache[player.id].itemLevel; - else - inspectCache[player.id] = nil; - if CanInspect(player.name, false) then - tinsert(playersToInspect, player) - end - end - end - - if #playersToInspect == 0 then - callback(players); - else - local pendingInspectData = {callback = callback, players = players, pendingIds = {}}; - for _, player in ipairs(playersToInspect) do - tinsert(pendingInspectData.pendingIds, player.id); - end - tinsert(self.pendingInspects, pendingInspectData); - - for _, player in ipairs(playersToInspect) do - self:QueueInspect(player); - end - - end -end - -function addon:SetCurrentEncounter(encounterId, encounterName, difficultyId, raidSize) - self.currentEncounter = { - id = encounterId, - name = encounterName, - difficultyId = difficultyId, - difficultyName = getDifficultyNameById(difficultyId), - raidSize = raidSize - } -end - -function addon:UnsetCurrentEncounter() - self.currentEncounter = nil -end - -function addon:StartNotifyInspectTimer() - if not self.notifyInspectTimer then - self.notifyInspectTimer = self:ScheduleRepeatingTimer("NOTIFY_INSPECT_TIMER_DONE", 1) - end -end - -function addon:StopNotifyInspectTimer() - if self.notifyInspectTimer then - self:CancelTimer(self.notifyInspectTimer) - end -end - -function addon:QueueInspect(player) - self:Debug("QueueNotifyInspect " .. player.name) - - local playerInQueue = false - for _, p in ipairs(self.notifyInspectQueue) do - if p.name == player.name then - playerInQueue = true; - break; - end - end - - if not playerInQueue then - tinsert(self.notifyInspectQueue, player); - self:StartNotifyInspectTimer(); - end -end - -function addon:ResolveInspect(player, success) - self:Debug("ResolveInspect " .. player.name .. " " .. (success and "success" or "fail")) - - local finishedInspectIndexes = {}; - - for inspectIndex, pendingInspectData in ipairs(self.pendingInspects) do - local players = pendingInspectData.players; - local pendingIds = pendingInspectData.pendingIds; - local callback = pendingInspectData.callback; - - -- Search for a pending id matching the inspected player id, remove - -- if found - local playerFound = false - for idx, id in ipairs(pendingIds) do - if id == player.id then - tremove(pendingIds, idx); - playerFound = true; - break - end - end - - if playerFound then - if success then - -- Update inspect data, unless already updated - if not self:HasPlayerInspectCache(player.id) then - self:UpdatePlayerInspectCache(player.id, player.name); - end - ClearInspectPlayer(); - player.specName = inspectCache[player.id].specName; - player.itemLevel = inspectCache[player.id].itemLevel; - else - if self:HasPlayerInspectCache(player.id) then - player.specName = inspectCache[player.id].specName; - player.itemLevel = inspectCache[player.id].itemLevel; - end - end - - -- If this was the last pending player, this pending inspect - -- group is now done - if #pendingIds == 0 then - tinsert(finishedInspectIndexes, inspectIndex); - end - end - end - - for _, idx in ipairs(finishedInspectIndexes) do - local finishedInspect = tremove(self.pendingInspects, idx); - finishedInspect.callback(finishedInspect.players); - end -end - -function addon:NOTIFY_INSPECT_TIMER_DONE() - self:Debug("NOTIFY_INSPECT_TIMER_DONE"); - - if self.currentInspectPlayer then - addon:ResolveInspect(self.currentInspectPlayer, false) - self.currentInspectPlayer = nil; - end - - if #self.notifyInspectQueue > 0 then - local inspectPlayer = tremove(self.notifyInspectQueue); - NotifyInspect(inspectPlayer.name); - self.currentInspectPlayer = inspectPlayer; - else - return + player.role = UnitGroupRolesAssigned(player.name); end end function addon:INSPECT_READY(evt, GUID) self:Debug("INSPECT_READY (" .. GUID .. ")") - if self.currentInspectPlayer and self.currentInspectPlayer.id == GUID then - addon:ResolveInspect(self.currentInspectPlayer, true) - self.currentInspectPlayer = nil; - end + self.inspect:INSPECT_READY(evt, GUID); end function addon:PLAYER_GUILD_UPDATE(evt, unitId) @@ -325,20 +118,23 @@ end function addon:EndSegment() self:Debug("EndSegment") - if not Skada.last.gotboss then + if not self.currentEncounter or not Skada.last.gotboss then self:Debug("Not a boss") return end local encounter = self.currentEncounter local players = self:GetGuildPlayersFromSet(Skada.last); - self:FetchInformationForPlayers(players, function(players) + self:SetRoleForPlayers(players); + self.inspect:GetInspectDataForPlayers(players, function() for i, player in ipairs(players) do local name = player.name; - local damage = player.damage; + 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" - self:Debug(name .. " " .. itemLevel .. " " .. specName .. " " .. damage); + -- "(DAMAGE) Saniera - 20.1k (560 Shadow)" + self:Debug(format("(%s) %s - %s (%d %s)", role, name, damage, itemLevel, specName)); end end) diff --git a/main.xml b/main.xml index e6a30dd..2af183a 100644 --- a/main.xml +++ b/main.xml @@ -1,3 +1,4 @@ <Ui xsi:schemaLocation="http://www.blizzard.com/wow/ui/ ..\..\FrameXML\UI.xsd"> <Script file="main.lua"/> + <Script file="inspect.lua"/> </Ui> \ No newline at end of file