From f1880c6f9be9128e4b0459f4bf258e8f28d233ee Mon Sep 17 00:00:00 2001 From: Peter Eliasson Date: Fri, 16 Jan 2015 12:02:27 +0100 Subject: [PATCH] Some structure cleanup. * Moved source files to /src. * Added a README file. --- GuildSkadaHighScore.toc | 6 +- README.md | 3 + gui.lua | 396 ----------------------------------------------- highscore.lua | 326 -------------------------------------- inspect.lua | 275 -------------------------------- lib/lib-include.xml | 11 ++ lib/lib.xml | 11 -- main.lua | 263 ------------------------------- main.xml | 6 - src/gui.lua | 396 +++++++++++++++++++++++++++++++++++++++++++++++ src/highscore.lua | 326 ++++++++++++++++++++++++++++++++++++++ src/inspect.lua | 275 ++++++++++++++++++++++++++++++++ src/main.lua | 263 +++++++++++++++++++++++++++++++ src/src-include.xml | 6 + 14 files changed, 1283 insertions(+), 1280 deletions(-) create mode 100644 README.md delete mode 100644 gui.lua delete mode 100644 highscore.lua delete mode 100644 inspect.lua create mode 100644 lib/lib-include.xml delete mode 100644 lib/lib.xml delete mode 100644 main.lua delete mode 100644 main.xml create mode 100644 src/gui.lua create mode 100644 src/highscore.lua create mode 100644 src/inspect.lua create mode 100644 src/main.lua create mode 100644 src/src-include.xml diff --git a/GuildSkadaHighScore.toc b/GuildSkadaHighScore.toc index 661c429..90a9b82 100644 --- a/GuildSkadaHighScore.toc +++ b/GuildSkadaHighScore.toc @@ -2,9 +2,9 @@ ## Author: Verath (Peter Eliasson) ## Version: 0.0.2 ## Title: Guild Skada High Score -## Notes: Ranking for dps/hps for raid bosses in a guild. +## Notes: Tracking and ranking of dps/hps in a guild. ## SavedVariables: GuildSkadaHighScoreDB ## Dependencies: Skada -lib/lib.xml -main.xml +lib/lib-include.xml +src/src-include.xml diff --git a/README.md b/README.md new file mode 100644 index 0000000..f99ea58 --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# Guild Skada High Score +A World of Warcraft addon for tracking and ranking dps/hps for encounters defeated by a guild. + diff --git a/gui.lua b/gui.lua deleted file mode 100644 index 797ff80..0000000 --- a/gui.lua +++ /dev/null @@ -1,396 +0,0 @@ -local addonName, addonTable = ... - --- Global functions for faster access -local tinsert = tinsert; -local tContains = tContains; - --- Set up module -local addon = addonTable[1]; -local gui = addon:NewModule("gui", "AceHook-3.0") -addon.gui = gui; - --- AceGUI -local AceGUI = LibStub("AceGUI-3.0"); - - -function gui:CreateHighScoreParseEntry(parse, role, rank) - local entryWidget = AceGUI:Create("SimpleGroup"); - entryWidget:SetFullWidth(true); - entryWidget:SetLayout("Flow"); - - local rankLabel = AceGUI:Create("Label"); - rankLabel:SetText(rank); - rankLabel:SetRelativeWidth(0.14); - - local dpsHpsLabel = AceGUI:Create("Label"); - local dpsHps = Skada:FormatNumber((role == "HEALER") and parse.hps or parse.dps); - dpsHpsLabel:SetText(dpsHps); - dpsHpsLabel:SetRelativeWidth(0.14); - - local nameLabel = AceGUI:Create("Label"); - nameLabel:SetText(parse.name); - nameLabel:SetRelativeWidth(0.14); - - local classLabel = AceGUI:Create("Label"); - classLabel:SetText(parse.class); - classLabel:SetRelativeWidth(0.14); - - local specLabel = AceGUI:Create("Label"); - specLabel:SetText(parse.specName or ""); - specLabel:SetRelativeWidth(0.14); - - local ilvlLabel = AceGUI:Create("Label"); - ilvlLabel:SetText(parse.itemLevel or ""); - ilvlLabel:SetRelativeWidth(0.14); - - local dateLabel = AceGUI:Create("Label"); - dateLabel:SetText(date("%m/%d/%y", parse.startTime)); - dateLabel:SetRelativeWidth(0.14); - - entryWidget:AddChild(rankLabel); - entryWidget:AddChild(dpsHpsLabel); - entryWidget:AddChild(nameLabel); - entryWidget:AddChild(classLabel); - entryWidget:AddChild(specLabel); - entryWidget:AddChild(ilvlLabel); - entryWidget:AddChild(dateLabel); - - return entryWidget; -end - -function gui:CreateGuildDropdown() - local dropdown = AceGUI:Create("Dropdown"); - self.guildDropdown = dropdown; - - dropdown:SetLabel("Guild"); - dropdown:SetRelativeWidth(0.25); - dropdown:SetCallback("OnValueChanged", function(widget, evt, guildId) - gui:SetSelectedGuild(guildId); - end) - - local guilds, numGuilds = addon.highscore:GetGuilds(); - if numGuilds > 0 then - dropdown:SetList(guilds); - else - dropdown:SetDisabled(true); - dropdown:SetText("No Guilds."); - end - - return dropdown; -end - -function gui:CreateZoneDropdown() - local dropdown = AceGUI:Create("Dropdown"); - self.zoneDropdown = dropdown; - - dropdown:SetLabel("Zone"); - dropdown:SetRelativeWidth(0.25); - dropdown:SetList(nil); - dropdown:SetDisabled(true); - dropdown:SetCallback("OnValueChanged", function(widget, evt, zoneId) - gui:SetSelectedZone(zoneId); - end) - - return dropdown; -end - -function gui:CreateDifficultyDropdown() - local dropdown = AceGUI:Create("Dropdown"); - self.difficultyDropdown = dropdown; - - dropdown:SetLabel("Difficulty"); - dropdown:SetRelativeWidth(0.25); - dropdown:SetList(nil); - dropdown:SetDisabled(true); - dropdown:SetCallback("OnValueChanged", function(widget, evt, difficultyId) - gui:SetSelectedDifficulty(difficultyId); - end) - - return dropdown; -end - -function gui:CreateEncounterDropdown() - local dropdown = AceGUI:Create("Dropdown"); - self.encounterDropdown = dropdown; - - dropdown:SetLabel("Encounter"); - dropdown:SetRelativeWidth(0.25); - dropdown:SetList(nil); - dropdown:SetDisabled(true); - dropdown:SetCallback("OnValueChanged", function(widget, evt, encounterId) - gui:SetSelectedEncounter(encounterId); - end) - - return dropdown; -end - -function gui:CreateHighScoreScrollFrame() - local scrollFrame = AceGUI:Create("ScrollFrame"); - scrollFrame:SetLayout("Flow"); - scrollFrame:SetFullWidth(true); - scrollFrame:SetFullHeight(true); - - -- Header: - -- Rank | DPS/HPS | Name | Class | Spec | Item Level | Date - local headerContainer = AceGUI:Create("SimpleGroup"); - headerContainer:SetFullWidth(true); - headerContainer:SetLayout("Flow"); - - local rankLabel = AceGUI:Create("Label"); - rankLabel:SetText("Rank"); - rankLabel:SetRelativeWidth(0.14); - local dpsHpsLabel = AceGUI:Create("Label"); - dpsHpsLabel:SetText("DPS/HPS"); - dpsHpsLabel:SetRelativeWidth(0.14); - local nameLabel = AceGUI:Create("Label"); - nameLabel:SetText("Name"); - nameLabel:SetRelativeWidth(0.14); - local classLabel = AceGUI:Create("Label"); - classLabel:SetText("Class"); - classLabel:SetRelativeWidth(0.14); - local specLabel = AceGUI:Create("Label"); - specLabel:SetText("Spec"); - specLabel:SetRelativeWidth(0.14); - local ilvlLabel = AceGUI:Create("Label"); - ilvlLabel:SetText("Item Level"); - ilvlLabel:SetRelativeWidth(0.14); - local dateLabel = AceGUI:Create("Label"); - dateLabel:SetText("Date"); - dateLabel:SetRelativeWidth(0.14); - - headerContainer:AddChild(rankLabel); - headerContainer:AddChild(dpsHpsLabel); - headerContainer:AddChild(nameLabel); - headerContainer:AddChild(classLabel); - headerContainer:AddChild(specLabel); - headerContainer:AddChild(ilvlLabel); - headerContainer:AddChild(dateLabel); - - local parsesContainer = AceGUI:Create("SimpleGroup"); - self.highScoreParsesContainer = parsesContainer; - parsesContainer:SetFullWidth(true); - parsesContainer:SetLayout("Flow"); - - scrollFrame:AddChild(headerContainer); - scrollFrame:AddChild(parsesContainer); - - return scrollFrame; -end - -function gui:CreateHighScoreTabGroup() - local container = AceGUI:Create("TabGroup"); - self.highScoreTabGroup = container; - - container:SetFullWidth(true); - container:SetFullHeight(true); - container:SetLayout("Fill"); - container:SetTabs({ - {value = "DAMAGER", text = "DPSers"}, - {value = "HEALER", text = "Healers"}, - {value = "TANK", text = "Tanks"} - }); - container:SetCallback("OnGroupSelected", function(widget, evt, roleId) - gui:SetSelectedRole(roleId); - end) - - container:AddChild(self:CreateHighScoreScrollFrame()); - - return container; -end - -function gui:CreateMainFrame() - local frame = AceGUI:Create("Frame") - self.mainFrame = frame; - - frame:Hide() - frame:SetWidth(800) - frame:SetHeight(600) - frame:SetTitle("Guild Skada High Score") - frame:SetLayout("Flow") - frame:SetCallback("OnClose", function() - gui:HideMainFrame() - end) - - frame:AddChild(self:CreateGuildDropdown()); - frame:AddChild(self:CreateZoneDropdown()); - frame:AddChild(self:CreateDifficultyDropdown()); - frame:AddChild(self:CreateEncounterDropdown()); - frame:AddChild(self:CreateHighScoreTabGroup()); - - return frame; -end - -function gui:DisplayParses() - local guildName = self.selectedGuild; - local zoneId = self.selectedZone; - local difficultyId = self.selectedDifficulty; - local encounter = self.selectedEncounter; - local roleId = self.selectedRole; - - local parsesContainer = self.highScoreParsesContainer; - parsesContainer:ReleaseChildren(); - - if guildName and zoneId and difficultyId and encounter and roleId then - local parses, numParses = addon.highscore:GetParses(guildName, - zoneId, difficultyId, encounter, roleId); - if numParses > 0 then - for rank, parse in ipairs(parses) do - local entryWidget = self:CreateHighScoreParseEntry(parse, roleId, rank); - parsesContainer:AddChild(entryWidget); - end - return; - end - end - - local noParsesLabel = AceGUI:Create("Label"); - noParsesLabel:SetText("No parses found."); - parsesContainer:AddChild(noParsesLabel); -end - -function gui:SetSelectedRole(roleId, noPropagation) - -- SelectTab, unlike SetValue for dropdowns, triggers the callback - if self.selectedRole ~= roleId then - self.selectedRole = roleId; - self.highScoreTabGroup:SelectTab(roleId); - self:DisplayParses(); - end -end - -function gui:SetSelectedEncounter(encounterId, noPropagation) - self.selectedEncounter = encounterId; - self.encounterDropdown:SetValue(encounterId); - self:DisplayParses(); -end - -function gui:SetSelectedDifficulty(difficultyId, noPropagation) - self.selectedDifficulty = difficultyId; - self.difficultyDropdown:SetValue(difficultyId); - - -- Update encounter dropdown with new guild, zone, difficulty - local encounters, numEncounters = addon.highscore:GetEncounters(self.selectedGuild, self.selectedZone, self.selectedDifficulty); - if numEncounters > 0 then - self.encounterDropdown:SetDisabled(false); - self.encounterDropdown:SetList(encounters); - else - self.encounterDropdown:SetDisabled(true); - self.encounterDropdown:SetList(nil); - self.encounterDropdown:SetText(nil); - end - - if not noPropagation then - if numEncounters == 1 then - -- If only one option, select it. - local encounterId, _ = next(encounters); - self:SetSelectedEncounter(encounterId); - else - self:SetSelectedEncounter(nil); - end - end -end - -function gui:SetSelectedZone(zoneId, noPropagation) - self.selectedZone = zoneId; - self.zoneDropdown:SetValue(zoneId); - - -- Update difficulty dropdown with new guild, zone - local difficulties, numDifficulties = addon.highscore:GetDifficulties(self.selectedGuild, self.selectedZone); - if numDifficulties > 0 then - self.difficultyDropdown:SetDisabled(false); - self.difficultyDropdown:SetList(difficulties); - else - self.difficultyDropdown:SetDisabled(true); - self.difficultyDropdown:SetList(nil); - self.difficultyDropdown:SetText(nil); - end - - if not noPropagation then - if numDifficulties == 1 then - -- If only one option, select it. - local difficultyId, _ = next(difficulties); - self:SetSelectedDifficulty(difficultyId); - else - self:SetSelectedDifficulty(nil); - end - end -end - -function gui:SetSelectedGuild(guildId, noPropagation) - self.selectedGuild = guildId; - self.guildDropdown:SetValue(guildId); - - -- Update zone dropdown for the new guild - local zones, numZones = addon.highscore:GetZones(guildId); - if numZones > 0 then - self.zoneDropdown:SetDisabled(false); - self.zoneDropdown:SetList(zones); - else - self.zoneDropdown:SetDisabled(true); - self.zoneDropdown:SetList(nil); - self.zoneDropdown:SetText(nil); - end - - if not noPropagation then - if numZones == 1 then - -- If only one option, select it. - local zoneId, _ = next(zones); - self:SetSelectedZone(zoneId); - else - self:SetSelectedZone(nil); - end - end -end - -function gui:ShowMainFrame() - if not self.mainFrame then - -- Only show if not already shown - self:CreateMainFrame():Show(); - - if self.selectedGuild then - -- Try to restore to same values as before - gui:SetSelectedGuild(self.selectedGuild, true); - gui:SetSelectedZone(self.selectedZone, true); - gui:SetSelectedDifficulty(self.selectedDifficulty, true); - gui:SetSelectedEncounter(self.selectedEncounter, true); - gui:SetSelectedRole(self.selectedRole, true); - elseif addon.guildName then - -- Try pre-selecting own guild if has one. - gui:SetSelectedGuild(addon.guildName); - end - - -- Have to do special for our tab group as it is never disabled - gui:SetSelectedRole(self.selectedRole or "DAMAGER"); - end -end - -function gui:HideMainFrame() - if self.mainFrame then - self.mainFrame:Release(); - - -- Unset references - self.mainFrame = nil; - self.guildDropdown = nil; - self.zoneDropdown = nil; - self.difficultyDropdown = nil; - self.encounterDropdown = nil; - self.highScoreTabGroup = nil; - end -end - -function gui:OnCloseSpecialWindows() - if self.mainFrame then - self:HideMainFrame() - return true - else - return self.hooks["CloseSpecialWindows"](); - end -end - - -function gui:OnEnable() - self:RawHook("CloseSpecialWindows", "OnCloseSpecialWindows"); -end - -function gui:OnDisable() - self:HideMainFrame(); - self:UnHook("CloseSpecialWindows"); -end \ No newline at end of file diff --git a/highscore.lua b/highscore.lua deleted file mode 100644 index ccd109c..0000000 --- a/highscore.lua +++ /dev/null @@ -1,326 +0,0 @@ -local addonName, addonTable = ... - --- Global functions for faster access -local tinsert = tinsert; -local tContains = tContains; -local sort = sort; -local random = random; -local format = format; - --- 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"] = { - ["guilds"] = { - ["*"] = { -- Guild Name - ["zones"] = { - ["*"] = { -- zoneId - ["difficulties"] = { - ["*"] = { -- difficultyId - ["encounters"] = { - ["*"] = { -- encounterId - --[[ - playerParses is a list of objects: - { - playerId = "", - role = "", - specName = "", - itemLevel = 0, - damage = 0, - healing = 0, - groupParseId = "" - } - --]] - playerParses = {} - } - } - } - } - } - } - } - }, - ["zones"] = { - ["*"] = { -- zoneId - zoneName = nil - } - }, - ["difficulties"] = { - ["*"] = { -- difficultyId - difficultyName = nil - } - }, - ["encounters"] = { - ["*"] = { -- encounterId - encounterName = nil - } - }, - ["players"] = { - ["*"] = { -- playerId - name = nil, - class = nil - } - }, - ["groupParses"] = { - --[[ - ["*"] = { -- groupParseId - startTime = 0, - duration = 0, - } - --]] - } -} - -addon.dbVersion = addon.dbVersion + 4 - --- Constants -local TRACKED_ZONE_IDS = { - 994 -- Highmaul -} - - --- Function for convering a database representation of --- a parse to a parse that can be returned to users. --- Copies all values and adds calculated once (like dps) -local function getReturnableParse(db, parse) - local parseCopy = {}; - for key, val in pairs(parse) do - parseCopy[key] = val; - end - - -- Get duration, statTime from the group parse - local groupParse = db.groupParses[parse["groupParseId"]] - parseCopy["duration"] = groupParse.duration; - parseCopy["startTime"] = groupParse.startTime; - parseCopy["groupParseId"] = nil - - -- Get player name and class - parseCopy["name"] = db.players[parse["playerId"]].name; - parseCopy["class"] = db.players[parse["playerId"]].class; - - -- Calculate dps/hps - parseCopy["dps"] = 0; - parseCopy["hps"] = 0; - if parseCopy["duration"] > 0 then - parseCopy["dps"] = parseCopy["damage"] / parseCopy["duration"]; - parseCopy["hps"] = parseCopy["healing"] / parseCopy["duration"]; - end - - return parseCopy; -end - -local function generateRandomKey() - local r1 = random(0, 1000); - local r2 = random(0, 1000); - local r3 = random(0, 1000); - -- 1000^3 = 1´000´000´000, should be enough for now... - return format("%x-%x-%x", r1, r2, r3); -end - -local function addGroupParse(db, startTime, duration) - -- Find a new unique key for the raid parse - local key = generateRandomKey(); - while db.groupParses[key] do - key = generateRandomKey(); - end - - db.groupParses[key] = { - startTime = startTime, - duration = duration - }; - - return key -end - -local function addZone(db, zoneId, zoneName) - db.zones[zoneId].zoneName = zoneName; -end - -local function addDifficulty(db, difficultyId, difficultyName) - db.difficulties[difficultyId].difficultyName = difficultyName; -end - -local function addEncounter(db, encounterId, encounterName) - db.encounters[encounterId].encounterName = encounterName; -end - -local function addPlayer(db, playerId, playerName, playerClass) - db.players[playerId].name = playerName; - db.players[playerId].class = playerClass; -end - -local function getParsesTable(db, guildName, zoneId, difficultyId, encounterId) - return db - .guilds[guildName] - .zones[zoneId] - .difficulties[difficultyId] - .encounters[encounterId] - .playerParses; -end - -local function addEncounterParseForPlayer(parsesTable, player, groupParseId) - local parse = { - playerId = player.id, - role = player.role, - specName = player.specName, - itemLevel = player.itemLevel, - damage = player.damage, - healing = player.healing, - groupParseId = groupParseId - } - tinsert(parsesTable, parse); -end - -function highscore:AddEncounterParsesForPlayers(guildName, encounter, players) - local zoneId = encounter.zoneId; - local zoneName = encounter.zoneName; - local encounterId = encounter.id; - local encounterName = encounter.name; - local difficultyId = encounter.difficultyId; - local difficultyName = encounter.difficultyName; - local startTime = encounter.startTime; - local duration = encounter.duration; - - assert(guildName) - assert(zoneId) - assert(zoneName) - assert(encounterId) - assert(encounterName) - assert(difficultyId) - assert(difficultyName) - assert(startTime) - assert(duration) - assert(players) - - if not tContains(TRACKED_ZONE_IDS, zoneId) then - self:Debug("AddEncounterParsesForPlayers: Current zone not not in tracked zones"); - return - end - - -- Add zone, difficulty and encounter info - addZone(self.db, zoneId, zoneName); - addDifficulty(self.db, difficultyId, difficultyName); - addEncounter(self.db, encounterId, encounterName); - - -- Add a group parse entry, holding data shared between all players - local groupParseId = addGroupParse(self.db, startTime, duration); - - local parsesTable = getParsesTable(self.db, guildName, zoneId, difficultyId, encounterId); - - for _, player in ipairs(players) do - self:Debug(format("addEncounterParseForPlayer: %s", player.name)); - - addPlayer(self.db, player.id, player.name, player.class); - addEncounterParseForPlayer(parsesTable, player, groupParseId) - end - -end - --- Returns (array of parses, numParses) -function highscore:GetParses(guildName, zoneId, difficultyId, encounterId, role, sortBy) - if (role ~= "TANK" and role ~= "HEALER" and role ~= "DAMAGER") then - return {}, 0; - end - - if not sortBy then - if role == "TANK" or role == "DAMAGER" then - sortBy = "dps" - elseif role == "HEALER" then - sortBy = "hps" - end - end - - local parsesTable = getParsesTable(self.db, guildName, zoneId, difficultyId, encounterId); - - -- Get a *copy* of all parses for the specified role - local parses = {}; - local numParses = 0; - for _, parse in ipairs(parsesTable) do - if parse.role == role then - local parseCopy = getReturnableParse(self.db, parse); - tinsert(parses, parseCopy); - numParses = numParses + 1; - end - end - - sort(parses, function(a, b) - return a[sortBy] > b[sortBy]; - end) - return parses, numParses; -end - --- Returns (array of {encounterId => encounterName}, numEncounters) -function highscore:GetEncounters(guildName, zoneId, difficultyId) - if not guildName or not zoneId or not difficultyId then - return {}, 0; - end - - local encounters = {}; - local numEncounters = 0; - local difficultiesTable = self.db.guilds[guildName].zones[zoneId].difficulties; - - for encounterId, _ in pairs(difficultiesTable[difficultyId].encounters) do - local encounterName = self.db.encounters[encounterId].encounterName; - encounters[encounterId] = encounterName; - numEncounters = numEncounters + 1; - end - return encounters, numEncounters; -end - --- Returns (array of {difficultyId => difficultyName}, numDifficulties) -function highscore:GetDifficulties(guildName, zoneId) - if not guildName or not zoneId then - return {}, 0; - end - - local difficulties = {}; - local numDifficulties = 0; - local difficultiesTable = self.db.guilds[guildName].zones[zoneId].difficulties; - - for difficultyId, _ in pairs(difficultiesTable) do - local difficultyName = self.db.difficulties[difficultyId].difficultyName; - difficulties[difficultyId] = difficultyName; - numDifficulties = numDifficulties + 1; - end - return difficulties, numDifficulties; -end - --- Returns (array of {zoneId => zoneName}, numZones) -function highscore:GetZones(guildName) - if not guildName then - return {}, 0; - end - - local zones = {}; - local numZones = 0; - for zoneId, _ in pairs(self.db.guilds[guildName].zones) do - local zoneName = self.db.zones[zoneId].zoneName; - zones[zoneId] = zoneName; - numZones = numZones + 1; - end - return zones, numZones; -end - --- Returns (array of {guildId => guildName}, numGuilds) -function highscore:GetGuilds() - local guildNames = {}; - local numGuilds = 0; - for guildName, _ in pairs(self.db.guilds) do - -- Actually guildId == guildName - guildNames[guildName] = guildName; - numGuilds = numGuilds + 1; - end - return guildNames, numGuilds; -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 deleted file mode 100644 index dce5075..0000000 --- a/inspect.lua +++ /dev/null @@ -1,275 +0,0 @@ -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:NewModule("inspect", "AceEvent-3.0", "AceTimer-3.0") -addon.inspect = 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" -} -local NOOP = function() end - -local playerGUID = UnitGUID("player"); -local inspectCache = {}; - - - -local function isInPVEInstance() - local isInstance, instanceType = IsInInstance(); - return inInstance and (instanceType == "party" or instanceType == "raid") -end - -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 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 false; -- No entry for player id - 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:Debug("Starting notifyInspectTimer"); - - self.notifyInspectTimer = self:ScheduleRepeatingTimer(function() - self:NOTIFY_INSPECT_TIMER_DONE() - end, 1); - end -end - -function inspect:StopNotifyInspectTimer() - if self.notifyInspectTimer then - self:Debug("Stopping notifyInspectTimer"); - - self:CancelTimer(self.notifyInspectTimer); - self.notifyInspectTimer = nil; - end -end - -function inspect:QueueInspect(player, callback) - self:Debug("QueueInspect", 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) - -- 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 - 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) - if not self.inspectQueue[playerId] then - return - end - - local player = self.inspectQueue[playerId].player; - 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); - 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:PreInspectGroup() - for i=1, GetNumGroupMembers() do - - local playerName = GetRaidRosterInfo(i); - if playerName and addon:IsInMyGuild(playerName) then - - local playerId = UnitGUID(playerName) - if playerId and playerId ~= playerGUID and not hasPlayerInspectCache(playerId) 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); - 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 - -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 isInPVEInstance() 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-include.xml b/lib/lib-include.xml new file mode 100644 index 0000000..973e258 --- /dev/null +++ b/lib/lib-include.xml @@ -0,0 +1,11 @@ + +