diff --git a/API General.txt b/API General.txt index 5d7d8c5d..cf8b38b7 100644 --- a/API General.txt +++ b/API General.txt @@ -24,22 +24,19 @@ return a new numeric table with sorted in decreasing order: Raid History ======================================= -Details.storage:OpenRaidStorage() +Details222.storage.OpenRaidStorage() get the table containing all stored data. -Details.storage:ListDiffs() -return a indexed table with dificulty numbers. - -Details.storage:ListEncounters (diff) +Details222.storage.ListEncounters (diff) return a indexed table with all encounters stored for the dificulty. -Details.storage:GetEncounterData (diff, encounterId, guildname) +Details222.storage.GetEncounterData (diff, encounterId, guildname) return a indexed table with encounter tables playd by the guild. -Details.storage:GetPlayerData (diff, encounterId, playername) +Details222.storage.GetUnitData (diff, encounterId, role, playername) return a indexed table with player tables for the player. -Details.storage:GetBestFromPlayer (diff, encounterId, role, playername) +Details222.storage.GetBestFromPlayer (diff, encounterId, role, playername) return the best result from the player. Structure: diff --git a/API.lua b/API.lua index 9f73657d..3737369a 100644 --- a/API.lua +++ b/API.lua @@ -559,7 +559,7 @@ Details:GetSourceFromNpcId (npcId) return the npc name for the specific npcId. this is a expensive function, once you get a valid result, store the npc name somewhere. -bestResult, encounterTable = Details.storage:GetBestFromPlayer (encounterDiff, encounterId, playerRole, playerName) +bestResult, encounterTable = Details222.storage.GetBestFromPlayer (encounterDiff, encounterId, playerRole, playerName) query the storage for the best result of the player on the encounter. encounterDiff = raid difficult ID (15 for heroic, 16 for mythic). encounterId = may be found on "id" member getting combat:GetBossInfo(). @@ -568,7 +568,7 @@ playerName = name of the player to query (with server name if the player is from bestResult = integer, best damage or healing done on the boss made by the player. encounterTable = {["date"] = formated time() ["time"] = time() ["elapsed"] = combat time ["guild"] = guild name ["damage"] = all damage players ["healing"] = all healers} -heal_or_damage_done = Details.storage:GetPlayerData (encounterDiff, encounterId, playerName) +heal_or_damage_done = Details222.storage.GetUnitData (encounterDiff, encounterId, role, playerName) query the storage for previous ecounter data for the player. returns a numeric table with the damage or healing done by the player on all encounters found. encounterDiff = raid difficult ID (15 for heroic, 16 for mythic). diff --git a/API.txt b/API.txt index e1392192..5d82174a 100644 --- a/API.txt +++ b/API.txt @@ -221,6 +221,9 @@ returns a table containing information about alternate power gains from players. Other Calls: +Details:GetItemLevelFromGuid(guid) +return the item level of a player passing the player guid, if the player is not found, returns 0. + Details:GetCombatNumber() returns the current unique combat number counter. combat number is a unique number given to each combat started, this number won't @@ -564,7 +567,7 @@ Details:GetSourceFromNpcId (npcId) return the npc name for the specific npcId. this is a expensive function, once you get a valid result, store the npc name somewhere. -bestResult, encounterTable = Details.storage:GetBestFromPlayer (encounterDiff, encounterId, playerRole, playerName) +bestResult, encounterTable = Details222.storage.GetBestFromPlayer (encounterDiff, encounterId, playerRole, playerName) query the storage for the best result of the player on the encounter. encounterDiff = raid difficult ID (15 for heroic, 16 for mythic). encounterId = may be found on "id" member getting combat:GetBossInfo(). @@ -573,7 +576,7 @@ playerName = name of the player to query (with server name if the player is from bestResult = integer, best damage or healing done on the boss made by the player. encounterTable = {["date"] = formated time() ["time"] = time() ["elapsed"] = combat time ["guild"] = guild name ["damage"] = all damage players ["healing"] = all healers} -heal_or_damage_done = Details.storage:GetPlayerData (encounterDiff, encounterId, playerName) +heal_or_damage_done = Details222.storage.GetUnitData (encounterDiff, encounterId, role, playerName) query the storage for previous ecounter data for the player. returns a numeric table with the damage or healing done by the player on all encounters found. encounterDiff = raid difficult ID (15 for heroic, 16 for mythic). diff --git a/Definitions.lua b/Definitions.lua index 131bb4be..f762f791 100644 --- a/Definitions.lua +++ b/Definitions.lua @@ -282,54 +282,54 @@ ---@field PhaseData table ---@field player_last_events table record the latest events of each player, latter used to build the death log ---@field ----@field GetCurrentPhase fun(combat: combat) : number return the current phase of the combat or the phase where the combat ended +---@field GetTotal fun(self: combat, attribute: number, subAttribute: number?, onlyGroup: boolean?) : number return the total amount of the requested attribute +---@field GetCurrentPhase fun(self: combat) : number return the current phase of the combat or the phase where the combat ended ---@field StoreTalents fun(self:combat) ----@field FindEnemyName fun(combat: combat) : string attempt to get the name of the enemy in the combat by getting the top most damaged unit by the player ----@field GetTryNumber fun(combat: combat) : number? ----@field GetFormattedCombatTime fun(combat: combat) : string ----@field GetMSTime fun(combat: combat) : number, number ----@field GetSegmentSlotId fun(combat: combat) : segmentid ----@field GetCombatName fun(combat: combat, bOnlyName: boolean?, bTryFind: boolean?) : string, number?, number?, number?, number? get the name of the combat ----@field GetCombatIcon fun(combat: combat) : df_atlasinfo, df_atlasinfo? +---@field FindEnemyName fun(self: combat) : string attempt to get the name of the enemy in the combat by getting the top most damaged unit by the player +---@field GetTryNumber fun(self: combat) : number? +---@field GetFormattedCombatTime fun(self: combat) : string +---@field GetMSTime fun(self: combat) : number, number +---@field GetSegmentSlotId fun(self: combat) : segmentid +---@field GetCombatName fun(self: combat, bOnlyName: boolean?, bTryFind: boolean?) : string, number?, number?, number?, number? get the name of the combat +---@field GetCombatIcon fun(self: combat) : df_atlasinfo, df_atlasinfo? ---@field GetTrinketProcsForPlayer fun(self: combat, playerName: string) : table return a key|value table containing the spellId as key and a table with information about the trinket as value ----@field IsMythicDungeon fun(combat: combat) : boolean, number return a boolean indicating if the combat is from a mythic+ dungeon, if true, also return the runId ----@field GetMythicDungeonInfo fun(combat: combat) : mythicdungeoninfo ----@field GetCombatType fun(combat: combat) : number ----@field GetCombatUID fun(combat: combat) : uniquecombatid ----@field GetTimeData fun(combat: combat, dataName: string) : table ----@field GetPhases fun(combat: combat) : table ----@field GetCombatTime fun(comba: combat) : number ----@field GetRunTime fun(combat: combat) : number return the elapsed time of a mythic+ dungeon run, if not exists, return the combat time ----@field GetRunTimeNoDefault fun(combat: combat) : number return the elapsed time of a mythic+ dungeon run, nil if not exists ----@field GetDeaths fun(combat) : table --get the table which contains the deaths of the combat ----@field GetStartTime fun(combat: combat) : number ----@field SetStartTime fun(combat: combat, time: number) ----@field GetEndTime fun(combat: combat) : number ----@field GetDifficulty fun(combat: combat) : number return the dungeon or raid difficulty for boss fights ----@field GetEncounterCleuID fun(combat: combat) : number return the encounterId for boss fights, this number is gotten from the ENCOUNTER_START event ----@field GetBossInfo fun(combat: combat) : bossinfo a table containing many informations about the boss fight ----@field SetEndTime fun(combat: combat, time: number) +---@field IsMythicDungeon fun(self: combat) : boolean, number return a boolean indicating if the combat is from a mythic+ dungeon, if true, also return the runId +---@field GetMythicDungeonInfo fun(self: combat) : mythicdungeoninfo +---@field GetCombatType fun(self: combat) : number +---@field GetCombatUID fun(self: combat) : uniquecombatid +---@field GetTimeData fun(self: combat, dataName: string) : table +---@field GetPhases fun(self: combat) : table +---@field GetCombatTime fun(self: combat) : number +---@field GetRunTime fun(self: combat) : number return the elapsed time of a mythic+ dungeon run, if not exists, return the combat time +---@field GetRunTimeNoDefault fun(self: combat) : number return the elapsed time of a mythic+ dungeon run, nil if not exists +---@field GetDeaths fun(self: combat) : table --get the table which contains the deaths of the combat +---@field GetStartTime fun(self: combat) : number +---@field SetStartTime fun(self: combat, time: number) +---@field GetEndTime fun(self: combat) : number +---@field GetDifficulty fun(self: combat) : number, string return the dungeon or raid difficulty for boss fights as a number, the string is an english difficulty name in lower case which is not always present +---@field GetEncounterCleuID fun(self: combat) : number return the encounterId for boss fights, this number is gotten from the ENCOUNTER_START event +---@field GetBossInfo fun(self: combat) : bossinfo a table containing many informations about the boss fight +---@field SetEndTime fun(self: combat, time: number) ---@field CopyDeathsFrom fun(combat1: combat, combat2: combat, bMythicPlus: boolean) copy the deaths from combat2 to combat1, use true on bMythicPlus if the combat is from a mythic plus run ----@field GetContainer fun(combat: combat, containerType: containertype) : actorcontainer get an actorcontainer, containerType can be 1 for damage, 2 heal, 3 resources, 4 utility ----@field GetSpellCastAmount fun(combat: combat, actorName: string, spellName: string) : number get the amount of times a spell was casted ----@field RemoveActorFromSpellCastTable fun(combat: combat, actorName: string) ----@field GetSpellCastTable fun(combat: combat, actorName: string|nil) : table ----@field GetSpellUptime fun(combat: combat, actorName: string, spellId: number, auraType: string|nil) : number get the uptime of a buff or debuff ----@field GetActor fun(combat: combat, containerType: number, playerName: string) : actor ----@field CreateAlternatePowerTable fun(combat: combat, actorName: string) : alternatepowertable ----@field GetCombatNumber fun(combat: combat) : number get a unique number representing the combatId, each combat has a unique number ----@field SetDate fun(combat: combat, startDate: string?, endDate: string?) set the start and end date of the combat, format: "H:M:S" ----@field GetDate fun(combat: combat) : string, string get the start and end date of the combat, format: "H:M:S" ----@field GetRoster fun(combat: combat) : table get the roster of the combat, the table contains the names of the players in the combat ----@field GetInstanceType fun(combat: combat) : instancetype get the instance type of the combat, can be "raid" or "party" or "pvp" or "arena" or "none" ----@field IsTrash fun(combat: combat) : boolean is true if the combat is a trash combat ----@field GetEncounterName fun(combat: combat) : string get the name of the encounter ----@field GetBossImage fun(combat: combat) : texturepath|textureid get the icon of the encounter ----@field SetDateToNow fun(combat: combat, bSetStartDate: boolean?, bSetEndDate: boolean?) set the date to the current time. format: "H:M:S" ----@field GetBossHealth fun(combat: combat) : number get the percentage of the boss health when the combat ended ----@field GetBossHealthString fun(combat: combat) : string get the percentage of the boss health when the combat ended as a string ----@field GetBossName fun(combat: combat) : string? return the name of the unitId "boss1", nil if the unit doesn't existed during the combat - +---@field GetContainer fun(self: combat, containerType: containertype) : actorcontainer get an actorcontainer, containerType can be 1 for damage, 2 heal, 3 resources, 4 utility +---@field GetSpellCastAmount fun(self: combat, actorName: string, spellName: string) : number get the amount of times a spell was casted +---@field RemoveActorFromSpellCastTable fun(self: combat, actorName: string) +---@field GetSpellCastTable fun(self: combat, actorName: string|nil) : table +---@field GetSpellUptime fun(self: combat, actorName: string, spellId: number, auraType: string|nil) : number get the uptime of a buff or debuff +---@field GetActor fun(self: combat, containerType: number, playerName: string) : actor +---@field CreateAlternatePowerTable fun(self: combat, actorName: string) : alternatepowertable +---@field GetCombatNumber fun(self: combat) : number get a unique number representing the combatId, each combat has a unique number +---@field SetDate fun(self: combat, startDate: string?, endDate: string?) set the start and end date of the combat, format: "H:M:S" +---@field GetDate fun(self: combat) : string, string get the start and end date of the combat, format: "H:M:S" +---@field GetRoster fun(self: combat) : table get the roster of the combat, the table contains the names of the players in the combat +---@field GetInstanceType fun(self: combat) : instancetype get the instance type of the combat, can be "raid" or "party" or "pvp" or "arena" or "none" +---@field IsTrash fun(self: combat) : boolean is true if the combat is a trash combat +---@field GetEncounterName fun(self: combat) : string get the name of the encounter +---@field GetBossImage fun(self: combat) : texturepath|textureid get the icon of the encounter +---@field SetDateToNow fun(self: combat, bSetStartDate: boolean?, bSetEndDate: boolean?) set the date to the current time. format: "H:M:S" +---@field GetBossHealth fun(self: combat) : number get the percentage of the boss health when the combat ended +---@field GetBossHealthString fun(self: combat) : string get the percentage of the boss health when the combat ended as a string +---@field GetBossName fun(self: combat) : string? return the name of the unitId "boss1", nil if the unit doesn't existed during the combat ---@class actorcontainer : table contains two tables _ActorTable and _NameIndexTable, the _ActorTable contains the actors, the _NameIndexTable contains the index of the actors in the _ActorTable, making quick to reorder them without causing overhead ---@field need_refresh boolean when true the container is dirty and needs to be refreshed diff --git a/boot.lua b/boot.lua index 0e839786..4148f1c2 100644 --- a/boot.lua +++ b/boot.lua @@ -101,6 +101,14 @@ [666953] = true, --Outland Single Target Training Dummy } + ---@type details_storage_feature + ---@diagnostic disable-next-line: missing-fields + local storage = { + DiffNames = {"normal", "heroic", "mythic", "ascended"}, + DiffNamesHash = {normal = 1, heroic = 2, mythic = 3, ascended = 4}, + } + Details222.storage = storage + --namespace for damage spells (spellTable) Details222.DamageSpells = {} --namespace for texture diff --git a/classes/class_combat.lua b/classes/class_combat.lua index af571abd..12e47b86 100644 --- a/classes/class_combat.lua +++ b/classes/class_combat.lua @@ -177,8 +177,19 @@ local segmentTypeToString = { return rawget(self, "is_trash") end + local diffNumberToName = { + [1] = "normal", + [2] = "heroic", + [3] = "mythic", + [4] = "ascended", + } + function classCombat:GetDifficulty() - return self.is_boss and self.is_boss.diff + local bossInfo = self:GetBossInfo() + if (bossInfo) then + local difficultyId = bossInfo.diff + return difficultyId, diffNumberToName[difficultyId] + end end function classCombat:GetEncounterCleuID() @@ -941,7 +952,7 @@ local segmentTypeToString = { ---return the total of a specific attribute, example: total damage, total healing, total resources, etc ---@param attribute number - ---@param subAttribute number + ---@param subAttribute number? ---@param onlyGroup boolean? ---@return number function classCombat:GetTotal(attribute, subAttribute, onlyGroup) diff --git a/classes/class_damage.lua b/classes/class_damage.lua index 3c164f97..c4d0ba3a 100644 --- a/classes/class_damage.lua +++ b/classes/class_damage.lua @@ -4910,7 +4910,7 @@ function damageClass:MontaInfoDamageDone() --I guess this fills the list of spel ---@type combat local combatObject = instance:GetCombat() ---@type number - local diff = combatObject:GetDifficulty() + local diff, diffEngName = combatObject:GetDifficulty() ---@type string local playerName = actorObject:Name() @@ -4919,14 +4919,15 @@ function damageClass:MontaInfoDamageDone() --I guess this fills the list of spel --guild ranking on a boss --check if is a raid encounter and if is heroic or mythic do - if (diff and (diff == 15 or diff == 16)) then + if (diff and (diff == 1 or diff == 2 or diff == 3 or diff == 4)) then --this might give errors local db = Details.OpenStorage() if (db) then - local bestRank, encounterTable = Details.storage:GetBestFromPlayer(diff, combatObject:GetBossInfo().id, "damage", playerName, true) + ---@type details_storage_unitresult, details_encounterkillinfo + local bestRank, encounterTable = Details222.storage.GetBestFromPlayer(diffEngName, combatObject:GetBossInfo().id, "DAMAGER", playerName, true) if (bestRank) then --discover which are the player position in the guild rank - local playerTable, onEncounter, rankPosition = Details.storage:GetPlayerGuildRank (diff, combatObject:GetBossInfo().id, "damage", playerName, true) - local text1 = playerName .. " Guild Rank on " .. (combatObject:GetBossInfo().name or "") .. ": |cFFFFFF00" .. (rankPosition or "x") .. "|r Best Dps: |cFFFFFF00" .. Details:ToK2((bestRank[1] or 0) / encounterTable.elapsed) .. "|r (" .. encounterTable.date:gsub(".*%s", "") .. ")" + local rankPosition = Details222.storage.GetUnitGuildRank(diffEngName, combatObject:GetBossInfo().id, "DAMAGER", playerName, true) + local text1 = playerName .. " Guild Rank on " .. (combatObject:GetBossInfo().name or "") .. ": |cFFFFFF00" .. (rankPosition or "x") .. "|r Best Dps: |cFFFFFF00" .. Details:ToK2((bestRank.total or SMALL_NUMBER) / encounterTable.elapsed) .. "|r (" .. encounterTable.date:gsub(".*%s", "") .. ")" breakdownWindowFrame:SetStatusbarText (text1, 10, "gray") else breakdownWindowFrame:SetStatusbarText() diff --git a/core/gears.lua b/core/gears.lua index e09ad4cd..40fac46b 100644 --- a/core/gears.lua +++ b/core/gears.lua @@ -16,7 +16,6 @@ local CONST_SPELLBOOK_GENERAL_TABID = 1 local CONST_SPELLBOOK_CLASSSPELLS_TABID = 2 local storageDebug = false --remember to turn this to false! -local instancesToStoreData = Details.InstancesToStoreData function Details:UpdateGears() Details:UpdateParser() @@ -24,6 +23,8 @@ function Details:UpdateGears() Details:UpdateCombat() end +---@alias raid_difficulty_eng_name_lowercase "normal" | "heroic" | "mythic" | "ascended" + ------------------------------------------------------------------------------------------------------------ --chat hooks @@ -378,7 +379,7 @@ function Details:TrackSpecsNow(bTrackEverything) local currentCombat = Details:GetCurrentCombat() if (not bTrackEverything) then - local damageContainer = currentCombat:GetContainer(DETAILS_ATTRIBUTE_DAMAGE) --DETAILS_ATTRIBUTE_DAMAGE is the integer 1, container 1 store damage data + local damageContainer = currentCombat:GetContainer(DETAILS_ATTRIBUTE_DAMAGE) --DETAILS_ATTRIBUTE_DAMAGE is the integer 1, container 1 store DAMAGER data for _, actor in damageContainer:ListActors() do ---@cast actor actor if (actor:IsPlayer()) then @@ -825,12 +826,76 @@ end) ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --storage stuff ~storage +---@class details_storage_unitresult : table +---@field total number +---@field itemLevel number +---@field classId number + +---@class details_encounterkillinfo : table +---@field guild guildname +---@field time unixtime +---@field date date +---@field elapsed number +---@field HEALER table +---@field servertime unixtime +---@field DAMAGER table + +---@class details_bosskillinfo : table +---@field kills number +---@field wipes number +---@field time_fasterkill number +---@field time_fasterkill_when unixtime +---@field time_incombat number +---@field dps_best number +---@field dps_best_when unixtime +---@field dps_best_raid number +---@field dps_best_raid_when unixtime + +---@class details_storage : table +---@field VERSION number the database version +---@field normal table +---@field heroic table +---@field mythic table +---@field mythic_plus table +---@field saved_encounters table +---@field totalkills table> + +---@class details_storage_feature : table +---@field diffNames string[] {"normal", "heroic", "mythic"} +---@field OpenRaidStorage fun():details_storage +---@field HaveDataForEncounter fun(difficulty:string, encounterId:number, guildName:string|boolean):boolean +---@field GetBestFromGuild fun(difficulty:string, encounterId:number, role:role, dps:boolean, guildName:string):actorname, details_storage_unitresult, details_encounterkillinfo +---@field GetUnitGuildRank fun(difficulty:string, encounterId:number, role:role, guildName:guildname, unitName:actorname):number?, details_storage_unitresult?, details_encounterkillinfo? +---@field GetBestFromPlayer fun(difficulty:string, encounterId:number, role:role, dps:boolean, playerName:actorname):details_storage_unitresult, details_encounterkillinfo +---@field DBGuildSync fun() + local CONST_ADDONNAME_DATASTORAGE = "Details_DataStorage" ---global database -Details.storage = {} +local diffNumberToName = { + [1] = "normal", + [2] = "heroic", + [3] = "mythic", + [4] = "ascended", +} -function Details.storage:OpenRaidStorage() +local createStorageTables = function() + local storageDatabase = DetailsDataStorage + + if (not storageDatabase and Details.CreateStorageDB) then + storageDatabase = Details:CreateStorageDB() + if (not storageDatabase) then + return + end + + elseif (not storageDatabase) then + return + end + + return storageDatabase +end + +---@return details_storage? +function Details222.storage.OpenRaidStorage() --check if the storage is already loaded if (not IsAddOnLoaded(CONST_ADDONNAME_DATASTORAGE)) then local loaded, reason = LoadAddOn(CONST_ADDONNAME_DATASTORAGE) @@ -840,108 +905,125 @@ function Details.storage:OpenRaidStorage() end --get the storage table - local db = DetailsDataStorage + local savedData = DetailsDataStorage - if (not db and Details.CreateStorageDB) then - db = Details:CreateStorageDB() - if (not db) then + if (not savedData and Details.CreateStorageDB) then + savedData = Details:CreateStorageDB() + if (not savedData) then return end - elseif (not db) then + + elseif (not savedData) then return end - return db + return savedData end -function Details.storage:HaveDataForEncounter(diff, encounter_id, guild_name) - local db = Details.storage:OpenRaidStorage() - - if (not db) then - return +---check if there is data for a specific encounter and difficulty, if a guildName is passed, check if there is data for the guild +---@param difficulty string +---@param encounterId number +---@param guildName string|boolean +---@return boolean bHasData +function Details222.storage.HaveDataForEncounter(difficulty, encounterId, guildName) + ---@type details_storage? + local savedData = Details222.storage.OpenRaidStorage() + if (not savedData) then + return false end - if (guild_name and type(guild_name) == "boolean") then - guild_name = GetGuildInfo("player") + difficulty = diffNumberToName[difficulty] or difficulty + + if (guildName and type(guildName) == "boolean") then + guildName = GetGuildInfo("player") end - local table = db [diff] - if (table) then - local encounters = table [encounter_id] - if (encounters) then + ---@type table + local encountersTable = savedData[difficulty] + if (encountersTable) then + local allEncountersStored = encountersTable[encounterId] + if (allEncountersStored) then --didn't requested a guild name, so just return 'we have data for this encounter' - if (not guild_name) then + if (not guildName) then return true end --data for a specific guild is requested, check if there is data for the guild - for index, encounter in ipairs(encounters) do - if (encounter.guild == guild_name) then + for index, encounterKillInfo in ipairs(allEncountersStored) do + if (encounterKillInfo.guild == guildName) then return true end end end end + + return false end -function Details.storage:GetBestFromGuild(diff, encounter_id, role, dps, guild_name) - local db = Details.storage:OpenRaidStorage() +---find the best unit from a specific role from a specific guild in a specific encounter and difficulty +---check all encounters saved for the guild and difficulty and return the unit with the best performance +---@param difficulty string +---@param encounterId number +---@param role role +---@param dps boolean? +---@param guildName string +---@return boolean|string playerName +---@return boolean|details_storage_unitresult storageUnitResult +---@return boolean|details_encounterkillinfo encounterKillInfo +function Details222.storage.GetBestFromGuild(difficulty, encounterId, role, dps, guildName) + ---@type details_storage? + local savedData = Details222.storage.OpenRaidStorage() - if (not db) then - return + if (not savedData) then + return false, false, false end - if (not guild_name) then - guild_name = GetGuildInfo("player") + if (not guildName) then + guildName = GetGuildInfo("player") end - if (not guild_name) then + if (not guildName) then if (Details.debug) then Details:Msg("(debug) GetBestFromGuild() guild name invalid.") end - return + return false, false, false end local best = 0 - local bestdps = 0 - local bestplayername - local onencounter - local bestactor + local bestDps = 0 + local bestEncounterKillInfo + local bestUnitName + local bestStorageResultTable if (not role) then - role = "damage" - end - role = string.lower(role) - if (role == "damager") then - role = "damage" - elseif (role == "healer") then - role = "healing" + role = "DAMAGER" end - local table = db [diff] - if (table) then - local encounters = table [encounter_id] - if (encounters) then - for index, encounter in ipairs(encounters) do - if (encounter.guild == guild_name) then - local players = encounter [role] - if (players) then - for playername, t in pairs(players) do + ---@type table + local encountersTable = savedData[difficulty] + if (encountersTable) then + local allEncountersStored = encountersTable[encounterId] + if (allEncountersStored) then + for index, encounterKillInfo in ipairs(allEncountersStored) do + if (encounterKillInfo.guild == guildName) then + ---@type table + local unitListFromRole = encounterKillInfo[role] + if (unitListFromRole) then + for unitName, storageUnitResult in pairs(unitListFromRole) do if (dps) then - if (t[1]/encounter.elapsed > bestdps) then - bestdps = t[1]/encounter.elapsed - bestplayername = playername - onencounter = encounter - bestactor = t + if (storageUnitResult.total / encounterKillInfo.elapsed > bestDps) then + bestDps = storageUnitResult.total / encounterKillInfo.elapsed + bestUnitName = unitName + bestEncounterKillInfo = encounterKillInfo + bestStorageResultTable = storageUnitResult end else - if (t[1] > best) then - best = t [1] - bestplayername = playername - onencounter = encounter - bestactor = t + if (storageUnitResult.total > best) then + best = storageUnitResult.total + bestUnitName = unitName + bestEncounterKillInfo = encounterKillInfo + bestStorageResultTable = storageUnitResult end - end end end @@ -950,22 +1032,33 @@ function Details.storage:GetBestFromGuild(diff, encounter_id, role, dps, guild_n end end - return t, onencounter + return bestUnitName, bestStorageResultTable, bestEncounterKillInfo end -function Details.storage:GetPlayerGuildRank(diff, encounter_id, role, playername, dps, guild_name) +---find and return the rank position of a unit among all other players guild +---the rank is based on the biggest total amount of damage or healing (role) done in a specific encounter and difficulty +---@param difficulty string +---@param encounterId number +---@param role role +---@param unitName actorname +---@param dps boolean? +---@param guildName guildname +---@return number positionIndex? +---@return details_storage_unitresult storageUnitResult? +---@return details_encounterkillinfo encounterKillInfo? +function Details222.storage.GetUnitGuildRank(difficulty, encounterId, role, unitName, dps, guildName) + ---@type details_storage? + local savedData = Details222.storage.OpenRaidStorage() - local db = Details.storage:OpenRaidStorage() - - if (not db) then + if (not savedData) then return end - if (not guild_name) then - guild_name = GetGuildInfo("player") + if (not guildName) then + guildName = GetGuildInfo("player") end - if (not guild_name) then + if (not guildName) then if (Details.debug) then Details:Msg("(debug) GetBestFromGuild() guild name invalid.") end @@ -973,156 +1066,158 @@ function Details.storage:GetPlayerGuildRank(diff, encounter_id, role, playername end if (not role) then - role = "damage" - end - role = string.lower(role) - if (role == "damager") then - role = "damage" - elseif (role == "healer") then - role = "healing" + role = "DAMAGER" end - local playerScore = {} + ---@class details_storage_unitscore : table + ---@field total number + ---@field persecond number + ---@field storageUnitResult details_storage_unitresult? + ---@field encounterKillInfo details_encounterkillinfo? + ---@field unitName actorname? - local _table = db [diff] - if (_table) then - local encounters = _table [encounter_id] - if (encounters) then - for index, encounter in ipairs(encounters) do - if (encounter.guild == guild_name) then - local roleTable = encounter [role] - for playerName, playerTable in pairs(roleTable) do + ---@type table + local unitScores = {} - if (not playerScore [playerName]) then - playerScore [playerName] = {0, 0, {}} + ---@type table + local encountersTable = savedData[difficulty] + if (encountersTable) then + local allEncountersStored = encountersTable[encounterId] + if (allEncountersStored) then + for index, encounterKillInfo in ipairs(allEncountersStored) do + if (encounterKillInfo.guild == guildName) then + local roleTable = encounterKillInfo[role] + for thisUnitName, storageUnitResult in pairs(roleTable) do + ---@cast storageUnitResult details_storage_unitresult + if (not unitScores[thisUnitName]) then + unitScores[thisUnitName] = { + total = 0, + persecond = 0, + unitName = thisUnitName, + } end - local total = playerTable[1] - local persecond = total / encounter.elapsed + --in this part the code is searching what is the performance of each unit in + --all encounters saved for the guild in the specific difficulty and role + + local total = storageUnitResult.total + local persecond = total / encounterKillInfo.elapsed if (dps) then - if (persecond > playerScore [playerName][2]) then - playerScore [playerName][1] = total - playerScore [playerName][2] = total / encounter.elapsed - playerScore [playerName][3] = playerTable - playerScore [playerName][4] = encounter + if (persecond > unitScores[thisUnitName].persecond) then + unitScores[thisUnitName].total = total + unitScores[thisUnitName].persecond = total / encounterKillInfo.elapsed + unitScores[thisUnitName].storageUnitResult = storageUnitResult + unitScores[thisUnitName].encounterKillInfo = encounterKillInfo end else - if (total > playerScore [playerName][1]) then - playerScore [playerName][1] = total - playerScore [playerName][2] = total / encounter.elapsed - playerScore [playerName][3] = playerTable - playerScore [playerName][4] = encounter + if (total > unitScores[thisUnitName].total) then + unitScores[thisUnitName].total = total + unitScores[thisUnitName].persecond = total / encounterKillInfo.elapsed + unitScores[thisUnitName].storageUnitResult = storageUnitResult + unitScores[thisUnitName].encounterKillInfo = encounterKillInfo end end end end end - if (not playerScore [playername]) then + --if the unit requested in the function parameter is not in the unitScores table, return + if (not unitScores[unitName]) then return end - local t = {} - for playerName, playerTable in pairs(playerScore) do - playerTable [5] = playerName - tinsert(t, playerTable) + local sortedResults = {} + for playerName, playerTable in pairs(unitScores) do + playerTable[1] = playerTable.total + playerTable[2] = playerTable.persecond + tinsert(sortedResults, playerTable) end - table.sort(t, dps and Details.Sort2 or Details.Sort1) + table.sort(sortedResults, dps and Details.Sort2 or Details.Sort1) - for i = 1, #t do - if (t[i][5] == playername) then - return t[i][3], t[i][4], i + for positionIndex = 1, #sortedResults do + if (sortedResults[positionIndex].unitName == unitName) then + local result = {positionIndex, sortedResults[positionIndex].storageUnitResult, sortedResults[positionIndex].encounterKillInfo} + Details:Destroy(unitScores) + Details:Destroy(sortedResults) + return unpack(result) end end end end - end -function Details.storage:GetBestFromPlayer(diff, encounter_id, role, playername, dps) - local db = Details.storage:OpenRaidStorage() - if (not db) then - print("DB noot found on GetBestFromPlayer()") +---find and return the best result from a specific unit in a specific encounter and difficulty +---@param difficulty string +---@param encounterId number +---@param role role +---@param unitName actorname +---@param dps boolean? +---@return details_storage_unitresult storageUnitResult? +---@return details_encounterkillinfo encounterKillInfo? +function Details222.storage.GetBestFromPlayer(difficulty, encounterId, role, unitName, dps) + ---@type details_storage? + local savedData = Details222.storage.OpenRaidStorage() + + if (not savedData) then return end - local best - local onencounter - local topdps + ---@type details_storage_unitresult + local bestStorageUnitResult + ---@type details_encounterkillinfo + local bestEncounterKillInfo + local topPerSecond if (not role) then - role = "damage" - end - role = string.lower(role) - if (role == "damager") then - role = "damage" - elseif (role == "healer") then - role = "healing" + role = "DAMAGER" end - local table = db [diff] - if (table) then - local encounters = table [encounter_id] - if (encounters) then - for index, encounter in ipairs(encounters) do - local player = encounter [role] and encounter [role] [playername] - if (player) then - if (best) then + local encountersTable = savedData[difficulty] + if (encountersTable) then + local allEncountersStored = encountersTable[encounterId] + if (allEncountersStored) then + for index, encounterKillInfo in ipairs(allEncountersStored) do + local storageUnitResult = encounterKillInfo[role] and encounterKillInfo[role] [unitName] + if (storageUnitResult) then + if (bestStorageUnitResult) then if (dps) then - if (player[1]/encounter.elapsed > topdps) then - onencounter = encounter - best = player - topdps = player[1]/encounter.elapsed + if (storageUnitResult.total/encounterKillInfo.elapsed > topPerSecond) then + bestEncounterKillInfo = encounterKillInfo + bestStorageUnitResult = storageUnitResult + topPerSecond = storageUnitResult.total/encounterKillInfo.elapsed end else - if (player[1] > best[1]) then - onencounter = encounter - best = player + if (storageUnitResult.total > bestStorageUnitResult.total) then + bestEncounterKillInfo = encounterKillInfo + bestStorageUnitResult = storageUnitResult end end else - onencounter = encounter - best = player - topdps = player[1]/encounter.elapsed + bestEncounterKillInfo = encounterKillInfo + bestStorageUnitResult = storageUnitResult + topPerSecond = storageUnitResult.total/encounterKillInfo.elapsed end end end end end - return best, onencounter + return bestStorageUnitResult, bestEncounterKillInfo end -function Details.storage:DBGuildSync() - +--network +function Details222.storage.DBGuildSync() Details:SendGuildData("GS", "R") - end -local OnlyFromCurrentRaidTier = true -local encounter_is_current_tier = function(encounterID) - if (OnlyFromCurrentRaidTier) then - local mapID = Details:GetInstanceIdFromEncounterId(encounterID) - if (mapID) then - --if isn'y the mapID in the table to save data - if (not Details.InstancesToStoreData [mapID]) then - return false - end - else - return false - end - end - return true -end - -local hasEncounterByEncounterSyncId = function(db, encounterSyncId) +local hasEncounterByEncounterSyncId = function(savedData, encounterSyncId) local minTime = encounterSyncId - 120 local maxTime = encounterSyncId + 120 - for difficultyId, encounterIdTable in pairs(db or {}) do + for difficultyId, encounterIdTable in pairs(savedData or {}) do if (type(encounterIdTable) == "table") then for dungeonEncounterID, encounterTable in pairs(encounterIdTable) do for index, encounter in ipairs(encounterTable) do @@ -1142,51 +1237,122 @@ local hasEncounterByEncounterSyncId = function(db, encounterSyncId) return false end +local recentRequestedIDs = {} local hasRecentRequestedEncounterSyncId = function(encounterSyncId) local minTime = encounterSyncId - 120 local maxTime = encounterSyncId + 120 - for requestedID in pairs(Details.RecentRequestedIDs) do + for requestedID in pairs(recentRequestedIDs) do if (requestedID >= minTime and requestedID <= maxTime) then return true end end end -local getBossIdsForCurrentExpansion = function() - --make a list of raids and bosses that belong to the current expansion - local bossIndexedTable, bossInfoTable, raidInfoTable = Details:GetExpansionBossList() - local allowedBosses = {} - for bossId, bossTable in pairs(bossInfoTable) do - allowedBosses[bossTable.dungeonEncounterID] = true +local allowedBossesCached = nil +local getBossIdsForCurrentExpansion = function() --need to check this! + if (allowedBossesCached) then + return allowedBossesCached end + + --make a list of raids and bosses that belong to the current expansion + local _, bossInfoTable = Details:GetExpansionBossList() + local allowedBosses = {} + + for bossId, bossTable in pairs(bossInfoTable) do + ---@cast bossTable details_bossinfo + allowedBosses[bossTable.dungeonEncounterID] = true + allowedBosses[bossTable.journalEncounterID] = true + allowedBosses[bossId] = true + end + + allowedBossesCached = allowedBosses return allowedBosses end ---remote call RoS -function Details.storage:GetIDsToGuildSync() - local db = Details.storage:OpenRaidStorage() +function Details:IsBossIdFromCurrentExpansion(bossId) + local allowedBosses = getBossIdsForCurrentExpansion() + return allowedBosses[bossId] +end - if (not db) then - return +local currentExpZoneIds = nil +function Details:IsZoneIdFromCurrentExpansion(zoneId) + if (currentExpZoneIds) then + return currentExpZoneIds[zoneId] + end + + currentExpZoneIds = {} + + local expansion = GetExpansionLevel() + if expansion == Enum.Expansion.TBC then + currentExpZoneIds = { --GetActiveMapID() + [532] = true, --Karazhan + [565] = true, --Gruul's Lair + [544] = true, --Magtheridon's Lair + [548] = true, --Serpentshrine Cavern + [550] = true, --Tempest Keep + [534] = true, --Battle for Mount Hyjal + [564] = true, --Black Temple + [580] = true, --Sunwell Plateau + } + elseif expansion == Enum.Expansion.WoTLK then + currentExpZoneIds = { --GetActiveMapID() + [533] = true, --Naxxramas + [615] = true, --Obsidian Sanctum + [616] = true, --Eye of Eternity + [624] = true, --Vault of Achavon + [603] = true, --Ulduar + [249] = true, --Onyxia's Lair + [649] = true, --Trial of the Crusader + [631] = true, --Icecrown Citadel + [724] = true, --Ruby Sanctum + } + else -- vanilla + currentExpZoneIds = { --GetActiveMapID() + [409] = true, --Molten Core + [469] = true, --Blackwing Lair + [249] = true, --Onyxia's Lair + [509] = true, --Ruins of Ahn'Qiraj + [531] = true, --Temple of Ahn'Qiraj + [533] = true, --Naxxramas + } + end + + return currentExpZoneIds[zoneId] +end + +---remote call RoS +---get the server time of each encounter defeated by the guild +---@return servertime[] +function Details222.storage.GetIDsToGuildSync() + ---@type details_storage? + local savedData = Details222.storage.OpenRaidStorage() + + if (not savedData) then + return {} end - local encounterSyncIds = {} local myGuildName = GetGuildInfo("player") + if (not myGuildName) then + return {} + end --myGuildName = "Patifaria" + ---@type servertime[] + local encounterSyncIds = {} local allowedBosses = getBossIdsForCurrentExpansion() --build the encounter synchronized ID list - for difficultyId, encounterIdTable in pairs(db or {}) do - if (type(encounterIdTable) == "table") then - for dungeonEncounterID, encounterTable in pairs(encounterIdTable) do - if (allowedBosses[dungeonEncounterID]) then - for index, encounter in ipairs(encounterTable) do - if (encounter.servertime) then - if (myGuildName == encounter.guild) then - tinsert(encounterSyncIds, encounter.servertime) - end + for i, diffName in ipairs(Details222.storage.DiffNames) do + ---@type table + local encountersTable = savedData[diffName] + + for dungeonEncounterID, allEncountersStored in pairs(encountersTable) do + if (allowedBosses[dungeonEncounterID]) then + for index, encounterKillInfo in ipairs(allEncountersStored) do + if (encounterKillInfo.servertime) then + if (myGuildName == encounterKillInfo.guild) then + tinsert(encounterSyncIds, encounterKillInfo.servertime) end end end @@ -1202,10 +1368,12 @@ function Details.storage:GetIDsToGuildSync() end --local call RoC - received the encounterSyncIds - need to know which fights is missing -function Details.storage:CheckMissingIDsToGuildSync(encounterSyncIds) - local db = Details.storage:OpenRaidStorage() +---@param encounterSyncIds servertime[] +function Details222.storage.CheckMissingIDsToGuildSync(encounterSyncIds) + ---@type details_storage? + local savedData = Details222.storage.OpenRaidStorage() - if (not db) then + if (not savedData) then return end @@ -1216,18 +1384,15 @@ function Details.storage:CheckMissingIDsToGuildSync(encounterSyncIds) return end - --prevent to request the same fight from multiple people - Details.RecentRequestedIDs = Details.RecentRequestedIDs or {} - --store the IDs which need to be sync local requestEncounterSyncIds = {} --check missing IDs for index, encounterSyncId in ipairs(encounterSyncIds) do - if (not hasEncounterByEncounterSyncId(db, encounterSyncId)) then + if (not hasEncounterByEncounterSyncId(savedData, encounterSyncId)) then if (not hasRecentRequestedEncounterSyncId(encounterSyncId)) then tinsert(requestEncounterSyncIds, encounterSyncId) - Details.RecentRequestedIDs[encounterSyncId] = true + recentRequestedIDs[encounterSyncId] = true end end end @@ -1240,10 +1405,12 @@ function Details.storage:CheckMissingIDsToGuildSync(encounterSyncIds) end --remote call RoS - build the encounter list from the encounterSyncIds -function Details.storage:BuildEncounterDataToGuildSync(encounterSyncIds) - local db = Details.storage:OpenRaidStorage() +---@param encounterSyncIds servertime[] +function Details222.storage.BuildEncounterDataToGuildSync(encounterSyncIds) + ---@type details_storage? + local savedData = Details222.storage.OpenRaidStorage() - if (not db) then + if (not savedData) then return end @@ -1257,8 +1424,12 @@ function Details.storage:BuildEncounterDataToGuildSync(encounterSyncIds) local amtToSend = 0 local maxAmount = 0 + ---@type table>[] local encounterList = {} + + ---@type table> local currentTable = {} + tinsert(encounterList, currentTable) if (Details.debug) then @@ -1266,16 +1437,18 @@ function Details.storage:BuildEncounterDataToGuildSync(encounterSyncIds) end for index, encounterSyncId in ipairs(encounterSyncIds) do - for difficultyId, encounterIdTable in pairs(db or {}) do - if (type(encounterIdTable) == "table") then - for dungeonEncounterID, encounterTable in pairs(encounterIdTable) do - for index, encounter in ipairs(encounterTable) do - if (encounterSyncId == encounter.time or encounterSyncId == encounter.servertime) then --the time here is always exactly + for difficulty, encountersTable in pairs(savedData) do + ---@cast encountersTable details_encounterkillinfo[] + if (Details222.storage.DiffNamesHash[difficulty]) then --this ensures that the difficulty is valid + for dungeonEncounterID, allEncountersStored in pairs(encountersTable) do + for index, encounterKillInfo in ipairs(allEncountersStored) do + ---@cast encounterKillInfo details_encounterkillinfo + if (encounterSyncId == encounterKillInfo.time or encounterSyncId == encounterKillInfo.servertime) then --the time here is always exactly --send this encounter - currentTable[difficultyId] = currentTable[difficultyId] or {} - currentTable[difficultyId][dungeonEncounterID] = currentTable[difficultyId][dungeonEncounterID] or {} + currentTable[difficulty] = currentTable[difficulty] or {} + currentTable[difficulty][dungeonEncounterID] = currentTable[difficulty][dungeonEncounterID] or {} - tinsert(currentTable[difficultyId][dungeonEncounterID], encounter) + tinsert(currentTable[difficulty][dungeonEncounterID], encounterKillInfo) amtToSend = amtToSend + 1 maxAmount = maxAmount + 1 @@ -1296,15 +1469,29 @@ function Details.storage:BuildEncounterDataToGuildSync(encounterSyncIds) Details:Msg("(debug) [RoS-EncounterSync] sending " .. amtToSend .. " encounters.") end + --the resulting table is a table with subtables, each subtable has a maximum of 3 encounters on indexes 1, 2 and 3 + --resulting in + --{ + -- {[raid_difficulty_eng_name_lowercase][encounterid] = {details_encounterkillinfo, details_encounterkillinfo, details_encounterkillinfo}}, + -- {[raid_difficulty_eng_name_lowercase][encounterid] = {details_encounterkillinfo, details_encounterkillinfo, details_encounterkillinfo}} + --} return encounterList end --local call RoC - add the fights to the client db -function Details.storage:AddGuildSyncData(data, source) - local db = Details.storage:OpenRaidStorage() +function Details222.storage.AddGuildSyncData(data, source) + ---@type details_storage? + local savedData = Details222.storage.OpenRaidStorage() - if (not db) then + if (not savedData) then + return + end + + if (not data or type(data) ~= "table") then + if (Details.debug) then + Details:Msg("(debug) [RoC-AddGuildSyncData] data isn't a table.") + end return end @@ -1312,39 +1499,36 @@ function Details.storage:AddGuildSyncData(data, source) Details.LastGuildSyncReceived = GetTime() local allowedBosses = getBossIdsForCurrentExpansion() - for difficultyId, encounterIdTable in pairs(data) do - if (type(difficultyId) == "number" and type(encounterIdTable) == "table") then - for dungeonEncounterID, encounterTable in pairs(encounterIdTable) do - if (type(dungeonEncounterID) == "number" and type(encounterTable) == "table") then - for index, encounter in ipairs(encounterTable) do + ---@cast data raid_difficulty_eng_name_lowercase, table + + for difficulty, encounterIdTable in pairs(data) do + ---@cast encounterIdTable table + + if (Details222.storage.DiffNamesHash[difficulty] and type(encounterIdTable) == "table") then + for dungeonEncounterID, allEncountersStored in pairs(encounterIdTable) do + if (type(dungeonEncounterID) == "number" and type(allEncountersStored) == "table" and allowedBosses[dungeonEncounterID]) then + for index, encounterKillInfo in ipairs(allEncountersStored) do --validate the encounter - if (type(encounter.servertime) == "number" and type(encounter.time) == "number" and type(encounter.guild) == "string" and type(encounter.date) == "string" and type(encounter.healing) == "table" and type(encounter.elapsed) == "number" and type(encounter.damage) == "table") then - --check if the encounter is from the current raiding tier - if (allowedBosses[dungeonEncounterID]) then - --check if this encounter already has been added from another sync - if (not hasEncounterByEncounterSyncId(db, encounter.servertime)) then - db[difficultyId] = db[difficultyId] or {} - db[difficultyId][dungeonEncounterID] = db[difficultyId][dungeonEncounterID] or {} - tinsert(db[difficultyId][dungeonEncounterID], encounter) + if (type(encounterKillInfo.servertime) == "number" and type(encounterKillInfo.time) == "number" and type(encounterKillInfo.guild) == "string" and type(encounterKillInfo.date) == "string" and type(encounterKillInfo.HEALER) == "table" and type(encounterKillInfo.elapsed) == "number" and type(encounterKillInfo.DAMAGER) == "table") then + --check if this encounter already has been added from another sync + if (not hasEncounterByEncounterSyncId(savedData, encounterKillInfo.servertime)) then + savedData[difficulty] = savedData[difficulty] or {} + savedData[difficulty][dungeonEncounterID] = savedData[difficulty][dungeonEncounterID] or {} + tinsert(savedData[difficulty][dungeonEncounterID], encounterKillInfo) - if (_G.DetailsRaidHistoryWindow and _G.DetailsRaidHistoryWindow:IsShown()) then - _G.DetailsRaidHistoryWindow:Refresh() - end - - addedAmount = addedAmount + 1 - else - if (Details.debug) then - Details:Msg("(debug) [RoS-EncounterSync] received a duplicated encounter table.") - end + if (_G.DetailsRaidHistoryWindow and _G.DetailsRaidHistoryWindow:IsShown()) then + _G.DetailsRaidHistoryWindow:Refresh() end + + addedAmount = addedAmount + 1 else if (Details.debug) then - Details:Msg("(debug) [RoS-EncounterSync] received an old tier encounter.") + Details:Msg("(debug) [RoC-AddGuildSyncData] received a duplicated encounter table.") end end else if (Details.debug) then - Details:Msg("(debug) [RoS-EncounterSync] received an invalid encounter table.") + Details:Msg("(debug) [RoC-AddGuildSyncData] received an invalid encounter table.") end end end @@ -1354,7 +1538,7 @@ function Details.storage:AddGuildSyncData(data, source) end if (Details.debug) then - Details:Msg("(debug) [RoS-EncounterSync] added " .. addedAmount .. " to database.") + Details:Msg("(debug) [RoC-AddGuildSyncData] added " .. addedAmount .. " to database.") end if (_G.DetailsRaidHistoryWindow and _G.DetailsRaidHistoryWindow:IsShown()) then @@ -1363,104 +1547,62 @@ function Details.storage:AddGuildSyncData(data, source) end end -function Details.storage:ListDiffs() - local db = Details.storage:OpenRaidStorage() +---@param difficulty string +---@return encounterid[] +function Details222.storage.ListEncounters(difficulty) + ---@type details_storage? + local savedData = Details222.storage.OpenRaidStorage() - if (not db) then - return + if (not savedData) then + return {} end + if (not difficulty) then + return {} + end + + ---@type encounterid[] local resultTable = {} - for difficultyId in pairs(db) do - tinsert(resultTable, difficultyId) - end - return resultTable -end -function Details.storage:ListEncounters(difficultyId) - local db = Details.storage:OpenRaidStorage() - - if (not db) then - return - end - - local resultTable = {} - if (difficultyId) then - local encounterIdTable = db[difficultyId] - if (encounterIdTable) then - for dungeonEncounterID in pairs(encounterIdTable) do - tinsert(resultTable, {difficultyId, dungeonEncounterID}) - end - end - else - for difficultyId, encounterIdTable in pairs(db) do - for dungeonEncounterID in pairs(encounterIdTable) do - tinsert(resultTable, {difficultyId, dungeonEncounterID}) - end + local encountersTable = savedData[difficulty] + if (encountersTable) then + for dungeonEncounterID in pairs(encountersTable) do + tinsert(resultTable, dungeonEncounterID) end end return resultTable end -function Details.storage:GetPlayerData(difficultyId, dungeonEncounterID, playerName) - local db = Details.storage:OpenRaidStorage() +---@param difficulty string +---@param dungeonEncounterID encounterid +---@param role role +---@param unitName actorname +---@return details_storage_unitresult[] +function Details222.storage.GetUnitData(difficulty, dungeonEncounterID, role, unitName) + local savedData = Details222.storage.OpenRaidStorage() - if (not db) then - return + if (not savedData) then + return {} end - local resultTable = {} - assert(type(playerName) == "string", "playerName must be a string.") + assert(type(unitName) == "string", "unitName must be a string.") + assert(type(dungeonEncounterID) == "number", "dungeonEncounterID must be a string.") - if (not difficultyId) then - for difficultyId, encounterIdTable in pairs(db) do - if (dungeonEncounterID) then - local encounters = encounterIdTable[dungeonEncounterID] - if (encounters) then - for i = 1, #encounters do - local encounter = encounters[i] - local playerData = encounter.healing[playerName] or encounter.damage[playerName] - if (playerData) then - tinsert(resultTable, playerData) - end - end - end - else - for dungeonEncounterID, encounters in pairs(encounterIdTable) do - for i = 1, #encounters do - local encounter = encounters[i] - local playerData = encounter.healing[playerName] or encounter.damage[playerName] - if (playerData) then - tinsert(resultTable, playerData) - end - end - end - end - end - else - local encounterIdTable = db[difficultyId] - if (encounterIdTable) then - if (dungeonEncounterID) then - local encounters = encounterIdTable[dungeonEncounterID] - if (encounters) then - for i = 1, #encounters do - local encounter = encounters[i] - local playerData = encounter.healing[playerName] or encounter.damage[playerName] - if (playerData) then - tinsert(resultTable, playerData) - end - end - end - else - for dungeonEncounterID, encounters in pairs(encounterIdTable) do - for i = 1, #encounters do - local encounter = encounters[i] - local playerData = encounter.healing[playerName] or encounter.damage[playerName] - if (playerData) then - tinsert(resultTable, playerData) - end - end + ---@type details_storage_unitresult[] + local resultTable = {} + + ---@type details_encounterkillinfo[] + local encountersTable = savedData[difficulty] + if (encountersTable) then + local allEncountersStored = encountersTable[dungeonEncounterID] + if (allEncountersStored) then + for i = 1, #allEncountersStored do + ---@type details_encounterkillinfo + local encounterKillInfo = allEncountersStored[i] + local playerData = encounterKillInfo[role][unitName] + if (playerData) then + tinsert(resultTable, playerData) end end end @@ -1469,88 +1611,75 @@ function Details.storage:GetPlayerData(difficultyId, dungeonEncounterID, playerN return resultTable end -function Details.storage:GetEncounterData(difficultyId, dungeonEncounterID, guildName) - local db = Details.storage:OpenRaidStorage() +---return a table with all encounters saved for a specific guild in a specific difficulty for a specific encounter +---@param difficulty string +---@param dungeonEncounterID encounterid +---@param guildName guildname +---@return details_encounterkillinfo[] +function Details222.storage.GetEncounterData(difficulty, dungeonEncounterID, guildName) + ---@type details_storage? + local savedData = Details222.storage.OpenRaidStorage() - if (not db) then + if (not savedData) then return end - if (not difficultyId) then - return db - end + local encountersTable = savedData[difficulty] - local encounterIdTable = db[difficultyId] + assert(encountersTable, "Difficulty not found. Use: normal, heroic or mythic.") + assert(type(dungeonEncounterID) == "number", "dungeonEncounterID must be a number.") - assert(encounterIdTable, "Difficulty not found. Use: 14, 15 or 16.") - assert(type(dungeonEncounterID) == "number", "EncounterId must be a number.") + ---@type details_encounterkillinfo[] + local allEncountersStored = encountersTable[dungeonEncounterID] - local encounters = encounterIdTable[dungeonEncounterID] local resultTable = {} - if (not encounters) then + if (not allEncountersStored) then return resultTable end - for i = 1, #encounters do - local encounter = encounters[i] - - if (guildName) then - if (encounter.guild == guildName) then - tinsert(resultTable, encounter) - end - else - tinsert(resultTable, encounter) + for i = 1, #allEncountersStored do + local encounterKillInfo = allEncountersStored[i] + if (encounterKillInfo.guild == guildName) then + tinsert(resultTable, encounterKillInfo) end end return resultTable end -local createStorageTables = function() - --get the storage table - local storageDatabase = DetailsDataStorage - - if (not storageDatabase and Details.CreateStorageDB) then - storageDatabase = Details:CreateStorageDB() - if (not storageDatabase) then - return - end - - elseif (not storageDatabase) then +---load the storage addon when the player leave combat, this function is also called from the parser when the player has its regen enabled +function Details.ScheduleLoadStorage() + --check first if the storage is already loaded + if (C_AddOns.IsAddOnLoaded(CONST_ADDONNAME_DATASTORAGE)) then + Details.schedule_storage_load = nil + Details222.storageLoaded = true return end - return storageDatabase -end - -function Details.ScheduleLoadStorage() - do return end - if (InCombatLockdown() or UnitAffectingCombat("player")) then if (Details.debug) then print("|cFFFFFF00Details! storage scheduled to load (player in combat).") end - + --load when the player leave combat Details.schedule_storage_load = true return else if (not IsAddOnLoaded(CONST_ADDONNAME_DATASTORAGE)) then - local loaded, reason = LoadAddOn(CONST_ADDONNAME_DATASTORAGE) - if (not loaded) then + local bSuccessLoaded, reason = LoadAddOn(CONST_ADDONNAME_DATASTORAGE) + if (not bSuccessLoaded) then if (Details.debug) then print("|cFFFFFF00Details! Storage|r: can't load storage, may be the addon is disabled.") end return end - createStorageTables() end end if (IsAddOnLoaded(CONST_ADDONNAME_DATASTORAGE)) then Details.schedule_storage_load = nil - Details.StorageLoaded = true + Details222.storageLoaded = true if (Details.debug) then print("|cFFFFFF00Details! storage loaded.") end @@ -1567,11 +1696,9 @@ function Details.GetStorage() end --this function is used on the breakdown window to show ranking and on the main window when hovering over the spec icon +--if the storage is not loaded, it will try to load it even if the player is in combat function Details.OpenStorage() --if the player is in combat, this function return false, if failed to load by other reason it returns nil - - do return end - --check if the storage is already loaded if (not IsAddOnLoaded(CONST_ADDONNAME_DATASTORAGE)) then --can't open it during combat @@ -1590,10 +1717,10 @@ function Details.OpenStorage() return end - local db = createStorageTables() + local savedData = createStorageTables() - if (db and IsAddOnLoaded(CONST_ADDONNAME_DATASTORAGE)) then - Details.StorageLoaded = true + if (savedData and IsAddOnLoaded(CONST_ADDONNAME_DATASTORAGE)) then + Details222.storageLoaded = true end return DetailsDataStorage @@ -1605,10 +1732,9 @@ end Details.Database = {} --this function is called on storewipe and storeencounter +---@return details_storage? function Details.Database.LoadDB() - do return end - - --check if the storage is already loaded + --check if the storage is not loaded yet and try to load it if (not IsAddOnLoaded(CONST_ADDONNAME_DATASTORAGE)) then local loaded, reason = LoadAddOn(CONST_ADDONNAME_DATASTORAGE) if (not loaded) then @@ -1620,39 +1746,37 @@ function Details.Database.LoadDB() end --get the storage table - local db = _G.DetailsDataStorage + local savedData = _G.DetailsDataStorage - if (not db and Details.CreateStorageDB) then - db = Details:CreateStorageDB() - if (not db) then + if (not savedData and Details.CreateStorageDB) then + savedData = Details:CreateStorageDB() + if (not savedData) then if (Details.debug) then print("|cFFFFFF00Details! Storage|r: can't save the encounter, couldn't load DataStorage, may be the addon is disabled.") end return end - elseif (not db) then + + elseif (not savedData) then if (Details.debug) then print("|cFFFFFF00Details! Storage|r: can't save the encounter, couldn't load DataStorage, may be the addon is disabled.") end return end - return db + return savedData end -function Details.Database.GetBossKillsDB(db) - --total kills in a boss on raid or dungeon - local totalKillsDataBase = db["totalkills"] - if (not totalKillsDataBase) then - db["totalkills"] = {} - totalKillsDataBase = db["totalkills"] - end - return totalKillsDataBase +---@param savedData details_storage +function Details.Database.GetBossKillsDB(savedData) + return savedData.totalkills end ----@param combat combat +---@param combat combat? function Details.Database.StoreWipe(combat) - combat = combat or Details.tabela_vigente + if (not combat) then + combat = Details:GetCurrentCombat() + end if (not combat) then if (Details.debug) then @@ -1663,7 +1787,7 @@ function Details.Database.StoreWipe(combat) local _, _, _, _, _, _, _, mapID = GetInstanceInfo() - if (not instancesToStoreData[mapID]) then + if (not Details:IsZoneIdFromCurrentExpansion(mapID)) then if (Details.debug) then print("|cFFFFFF00Details! Storage|r: instance not allowed.") end @@ -1681,32 +1805,21 @@ function Details.Database.StoreWipe(combat) end --get the difficulty - local difficultyId = combat:GetDifficulty() + local _, difficulty = combat:GetDifficulty() --load database - local db = Details.Database.LoadDB() - if (not db) then + ---@type details_storage? + local savedData = Details.Database.LoadDB() + if (not savedData) then return end - local encounterIdTable = db[difficultyId] - if (not encounterIdTable) then - db [difficultyId] = {} - encounterIdTable = db[difficultyId] - end - - local encounters = encounterIdTable[dungeonEncounterID] - if (not encounters) then - encounterIdTable[dungeonEncounterID] = {} - encounters = encounterIdTable[dungeonEncounterID] - end - - --total kills in a boss on raid or dungeon - local totalKillsDataBase = Details.Database.GetBossKillsDB(db) - if (IsInRaid()) then - totalKillsDataBase[dungeonEncounterID] = totalKillsDataBase[dungeonEncounterID] or {} - totalKillsDataBase[dungeonEncounterID][difficultyId] = totalKillsDataBase[dungeonEncounterID][difficultyId] or { + --total kills in a boss on raid or dungeon + local totalKillsDataBase = Details.Database.GetBossKillsDB(savedData) + + totalKillsDataBase[difficulty] = totalKillsDataBase[difficulty] or {} + totalKillsDataBase[difficulty][dungeonEncounterID] = totalKillsDataBase[difficulty][dungeonEncounterID] or { kills = 0, wipes = 0, time_fasterkill = 0, @@ -1718,17 +1831,15 @@ function Details.Database.StoreWipe(combat) dps_best_raid_when = 0 } - local bossData = totalKillsDataBase[dungeonEncounterID][difficultyId] + local bossData = totalKillsDataBase[difficulty][dungeonEncounterID] bossData.wipes = bossData.wipes + 1 - - --wipes amount - if (bossData.wipes % 10 == 0) then - --nah player does not want to know that - --Details:Msg("Wipe stored, you have now " .. bossData.wipes .. " wipes on this boss.") - end end end + +---PAREI AQUI + + ---@param combat combat function Details.Database.StoreEncounter(combat) combat = combat or Details:GetCurrentCombat() @@ -1742,7 +1853,7 @@ function Details.Database.StoreEncounter(combat) local _, _, _, _, _, _, _, mapID = GetInstanceInfo() - if (not instancesToStoreData[mapID]) then + if (not Details:IsZoneIdFromCurrentExpansion(mapID)) then if (Details.debug) then print("|cFFFFFF00Details! Storage|r: instance not allowed.") end @@ -1750,9 +1861,9 @@ function Details.Database.StoreEncounter(combat) end local encounterInfo = combat:GetBossInfo() - local encounter_id = encounterInfo and encounterInfo.id + local encounterId = encounterInfo and encounterInfo.id - if (not encounter_id) then + if (not encounterId) then if (Details.debug) then print("|cFFFFFF00Details! Storage|r: encounter ID not found.") end @@ -1760,89 +1871,127 @@ function Details.Database.StoreEncounter(combat) end --get the difficulty - local diff = combat:GetDifficulty() + local diffId, diff = combat:GetDifficulty() --database - local db = Details.Database.LoadDB() - if (not db) then - return - end + ---@type details_storage? + local savedData = Details.Database.LoadDB() + if (not savedData) then + return + end - local diff_storage = db [diff] - if (not diff_storage) then - db [diff] = {} - diff_storage = db [diff] - end + --[=[ + savedData[mythic] = { + [encounterId] = { --indexed table + [1] = { + DAMAGER = { + [actorname] = details_storage_unitresult + }, + HEALER = { + [actorname] = details_storage_unitresult + }, + date = date("%H:%M %d/%m/%y"), + time = time(), + servertime = GetServerTime(), + elapsed = combat:GetCombatTime(), + guild = guildName, + } + } + } + --]=] - local encounter_database = diff_storage [encounter_id] - if (not encounter_database) then - diff_storage [encounter_id] = {} - encounter_database = diff_storage [encounter_id] - end + ---@type combattime + local elapsedCombatTime = combat:GetCombatTime() - --total kills in a boss on raid or dungeon - local totalkills_database = Details.Database.GetBossKillsDB(db) + ---@type table + local encountersTable = savedData[diff] + if (not encountersTable) then + savedData[diff] = {} + encountersTable = savedData[diff] + end + + ---@type details_encounterkillinfo[] + local allEncountersStored = encountersTable[encounterId] + if (not allEncountersStored) then + encountersTable[encounterId] = {} + allEncountersStored = encountersTable[encounterId] + end + + --total kills in a boss on raid or dungeon + local totalkillsTable = Details.Database.GetBossKillsDB(savedData) --store total kills on this boss - --if the player is facing a raid boss - if (IsInRaid()) then - totalkills_database[encounter_id] = totalkills_database[encounter_id] or {} - totalkills_database[encounter_id][diff] = totalkills_database[encounter_id][diff] or {kills = 0, wipes = 0, time_fasterkill = 0, time_fasterkill_when = 0, time_incombat = 0, dps_best = 0, dps_best_when = 0, dps_best_raid = 0, dps_best_raid_when = 0} + --if the player is facing a raid boss + if (IsInRaid()) then + totalkillsTable[encounterId] = totalkillsTable[encounterId] or {} + totalkillsTable[encounterId][diff] = totalkillsTable[encounterId][diff] or { + kills = 0, + wipes = 0, + time_fasterkill = 1000000, + time_fasterkill_when = 0, + time_incombat = 0, + dps_best = 0, --player best dps + dps_best_when = 0, --when the player did the best dps + dps_best_raid = 0, + dps_best_raid_when = 0 + } - local bossData = totalkills_database[encounter_id][diff] - local encounterElapsedTime = combat:GetCombatTime() + ---@type details_bosskillinfo + local bossData = totalkillsTable[encounterId][diff] + ---@type combattime + local encounterElapsedTime = elapsedCombatTime - --kills amount - bossData.kills = bossData.kills + 1 - - --best time - if (encounterElapsedTime > bossData.time_fasterkill) then - bossData.time_fasterkill = encounterElapsedTime - bossData.time_fasterkill_when = time() - end - - --total time in combat - bossData.time_incombat = bossData.time_incombat + encounterElapsedTime - - --player best dps - local player = combat(DETAILS_ATTRIBUTE_DAMAGE, Details.playername) - if (player) then - local playerDps = player.total / encounterElapsedTime - if (playerDps > bossData.dps_best) then - bossData.dps_best = playerDps - bossData.dps_best_when = time() - end - end - - --raid best dps - local raidTotalDamage = combat:GetTotal(DETAILS_ATTRIBUTE_DAMAGE, false, true) - local raidDps = raidTotalDamage / encounterElapsedTime - if (raidDps > bossData.dps_best_raid) then - bossData.dps_best_raid = raidDps - bossData.dps_best_raid_when = time() - end + --kills amount + bossData.kills = bossData.kills + 1 + --best time + if (encounterElapsedTime < bossData.time_fasterkill) then + bossData.time_fasterkill = encounterElapsedTime + bossData.time_fasterkill_when = time() end + --total time in combat + bossData.time_incombat = bossData.time_incombat + encounterElapsedTime + + --player best dps + ---@actor + local playerActorObject = combat(DETAILS_ATTRIBUTE_DAMAGE, Details.playername) + if (playerActorObject) then + local playerDps = playerActorObject.total / encounterElapsedTime + if (playerDps > bossData.dps_best) then + bossData.dps_best = playerDps + bossData.dps_best_when = time() + end + end + + --raid best dps + local raidTotalDamage = combat:GetTotal(DETAILS_ATTRIBUTE_DAMAGE, nil, true) + local raidDps = raidTotalDamage / encounterElapsedTime + if (raidDps > bossData.dps_best_raid) then + bossData.dps_best_raid = raidDps + bossData.dps_best_raid_when = time() + end + end --check for heroic and mythic - if (storageDebug or (diff == 1 or diff == 2 or diff == 3 or diff == 4)) then --test on raid finder: ' or diff == 17' -- normal mode: diff == 14 or - + if (storageDebug or Details222.storage.DiffNamesHash[diff]) then --check the guild name local match = 0 local guildName = GetGuildInfo("player") local raidSize = GetNumGroupMembers() or 0 + local cachedUnitIds = Details222.UnitIdCache.Raid + if (not storageDebug) then if (guildName) then for i = 1, raidSize do - local gName = GetGuildInfo("raid" .. i) or "" + local gName = GetGuildInfo(cachedUnitIds[i]) or "" if (gName == guildName) then match = match + 1 end end - if (match < raidSize * 0.75 and not storageDebug) then + if (match < raidSize * 0.75) then if (Details.debug) then print("|cFFFFFF00Details! Storage|r: can't save the encounter, need at least 75% of players be from your guild.") end @@ -1858,97 +2007,114 @@ function Details.Database.StoreEncounter(combat) guildName = "Test Guild" end - local this_combat_data = { - damage = {}, - healing = {}, + ---@type details_encounterkillinfo + local combatResultData = { + DAMAGER = {}, + HEALER = {}, date = date("%H:%M %d/%m/%y"), time = time(), servertime = GetServerTime(), - elapsed = combat:GetCombatTime(), + elapsed = elapsedCombatTime, guild = guildName, } - local damage_container_hash = combat [1]._NameIndexTable - local damage_container_pool = combat [1]._ActorTable - - local healing_container_hash = combat [2]._NameIndexTable - local healing_container_pool = combat [2]._ActorTable + local damageContainer = combat:GetContainer(DETAILS_ATTRIBUTE_DAMAGE) + local healingContainer = combat:GetContainer(DETAILS_ATTRIBUTE_HEAL) for i = 1, GetNumGroupMembers() do + local role = UnitGroupRolesAssigned(cachedUnitIds[i]) - local role = UnitGroupRolesAssigned("raid" .. i) - - if (UnitIsInMyGuild ("raid" .. i)) then + if (UnitIsInMyGuild(cachedUnitIds[i])) then if (role == "NONE" or role == "DAMAGER" or role == "TANK") then - local player_name = Details:GetFullName("raid" .. i) - local _, _, class = Details:GetUnitClassFull(player_name) + local playerName = Details:GetFullName(cachedUnitIds[i]) + local _, _, class = Details:GetUnitClassFull(playerName) - local damage_actor = damage_container_pool [damage_container_hash [player_name]] - if (damage_actor) then - local guid = UnitGUID("raid" .. i) - this_combat_data.damage [player_name] = {floor(damage_actor.total), Details.item_level_pool [guid] and Details.item_level_pool [guid].ilvl or 0, class or 0} + local damagerActor = damageContainer:GetActor(playerName) + if (damagerActor) then + local guid = UnitGUID(cachedUnitIds[i]) + + ---@type details_storage_unitresult + local unitResultInfo = { + total = floor(damagerActor.total), + itemLevel = Details:GetItemLevelFromGuid(guid), + classId = class or 0 + } + combatResultData.DAMAGER[playerName] = unitResultInfo end elseif (role == "HEALER" or role == "SUPPORT") then - local player_name = Details:GetFullName("raid" .. i) + local playerName = Details:GetFullName(cachedUnitIds[i]) + local _, _, class = Details:GetUnitClassFull(playerName) - local _, _, class = Details:GetUnitClassFull(player_name) + local healingActor = healingContainer:GetActor(playerName) + if (healingActor) then + local guid = UnitGUID(cachedUnitIds[i]) - local heal_actor = healing_container_pool [healing_container_hash [player_name]] - if (heal_actor) then - local guid = UnitGUID("raid" .. i) - this_combat_data.healing [player_name] = {floor(heal_actor.total), Details.item_level_pool [guid] and Details.item_level_pool [guid].ilvl or 0, class or 0} + ---@type details_storage_unitresult + local unitResultInfo = { + total = floor(healingActor.total), + itemLevel = Details:GetItemLevelFromGuid(guid), + classId = class or 0 + } + combatResultData.HEALER[playerName] = unitResultInfo end end end end --add the encounter data - tinsert(encounter_database, this_combat_data) + tinsert(allEncountersStored, combatResultData) if (Details.debug) then print("|cFFFFFF00Details! Storage|r: combat data added to encounter database.") end - local myrole = UnitGroupRolesAssigned("player") - local mybest, onencounter = Details.storage:GetBestFromPlayer(diff, encounter_id, myrole, Details.playername, true) --get dps or hps - local mybest2 = mybest and mybest[1] or 0 + local playerRole = UnitGroupRolesAssigned("player") + ---@type details_storage_unitresult, details_encounterkillinfo + local bestRank, encounterKillInfo = Details222.storage.GetBestFromPlayer(diff, encounterId, playerRole, Details.playername, true) --get dps or hps - if (mybest and onencounter) then - local myBestDps = mybest2 / onencounter.elapsed + if (bestRank and encounterKillInfo) then + local registeredBestTotal = bestRank and bestRank.total or 0 + local registeredBestPerSecond = registeredBestTotal / encounterKillInfo.elapsed - local d_one = 0 - if (myrole == "DAMAGER" or myrole == "TANK") then - d_one = combat(1, Details.playername) and combat(1, Details.playername).total / combat:GetCombatTime() - elseif (myrole == "HEALER") then - d_one = combat(2, Details.playername) and combat(2, Details.playername).total / combat:GetCombatTime() + local currentPerSecond = 0 + if (playerRole == "DAMAGER" or playerRole == "TANK") then + ---@actor + local playerActorObject = damageContainer:GetActor(Details.playername) + if (playerActorObject) then + currentPerSecond = playerActorObject.total / elapsedCombatTime + end + elseif (playerRole == "HEALER") then + ---@actor + local playerActorObject = healingContainer:GetActor(Details.playername) + if (playerActorObject) then + currentPerSecond = playerActorObject.total / elapsedCombatTime + end end - if (myBestDps > d_one) then + if (registeredBestPerSecond > currentPerSecond) then if (not Details.deny_score_messages) then - print(Loc ["STRING_DETAILS1"] .. format(Loc ["STRING_SCORE_NOTBEST"], Details:ToK2(d_one), Details:ToK2(myBestDps), onencounter.date, mybest[2])) + print(Loc ["STRING_DETAILS1"] .. format(Loc ["STRING_SCORE_NOTBEST"], Details:ToK2(currentPerSecond), Details:ToK2(registeredBestPerSecond), encounterKillInfo.date, bestRank[2])) end else if (not Details.deny_score_messages) then - print(Loc ["STRING_DETAILS1"] .. format(Loc ["STRING_SCORE_BEST"], Details:ToK2(d_one))) + print(Loc ["STRING_DETAILS1"] .. format(Loc ["STRING_SCORE_BEST"], Details:ToK2(currentPerSecond))) end end end - local lower_instance = Details:GetLowerInstanceNumber() - if (lower_instance) then - local instance = Details:GetInstance(lower_instance) - if (instance) then - local my_role = UnitGroupRolesAssigned("player") - if (my_role == "TANK") then - my_role = "DAMAGER" + local lowerInstanceId = Details:GetLowerInstanceNumber() + if (lowerInstanceId) then + local instanceObject = Details:GetInstance(lowerInstanceId) + if (instanceObject) then + if (playerRole == "TANK") then + playerRole = "DAMAGER" end - local raid_name = GetInstanceInfo() - local func = {Details.OpenRaidHistoryWindow, Details, raid_name, encounter_id, diff, my_role, guildName} - --local icon = {[[Interface\AddOns\Details\images\icons]], 16, 16, false, 434/512, 466/512, 243/512, 273/512} - local icon = {[[Interface\PvPRankBadges\PvPRank08]], 16, 16, false, 0, 1, 0, 1} + local raidName = GetInstanceInfo() + local func = {Details.OpenRaidHistoryWindow, Details, raidName, encounterId, diff, playerRole, guildName} + local icon = {[[Interface\PvPRankBadges\PvPRank08]], 16, 16, false, 0, 1, 0, 1} if (not Details.deny_score_messages) then - instance:InstanceAlert(Loc ["STRING_GUILDDAMAGERANK_WINDOWALERT"], icon, Details.update_warning_timeout, func, true) + instanceObject:InstanceAlert(Loc ["STRING_GUILDDAMAGERANK_WINDOWALERT"], icon, Details.update_warning_timeout, func, true) end end end @@ -2373,8 +2539,12 @@ function Details.ilevel:GetPool() return Details.item_level_pool end +function Details:GetItemLevelFromGuid(guid) + return Details.item_level_pool[guid] and Details.item_level_pool[guid].ilvl or 0 +end + function Details.ilevel:GetIlvl(guid) - return Details.item_level_pool [guid] + return Details.item_level_pool[guid] end function Details.ilevel:GetInOrder() diff --git a/core/network.lua b/core/network.lua index e16df605..95f6e38f 100644 --- a/core/network.lua +++ b/core/network.lua @@ -314,7 +314,7 @@ --return false end - local IDs = Details.storage:GetIDsToGuildSync() + local IDs = Details222.storage.GetIDsToGuildSync() if (IDs and IDs [1]) then local from = UnitName("player") @@ -326,7 +326,7 @@ return true elseif (type == "L") then --RoC - the player received the IDs list and send back which IDs he doesn't have - local missingIds = Details.storage:CheckMissingIDsToGuildSync(data) + local missingIds = Details222.storage.CheckMissingIDsToGuildSync(data) if (missingIds and missingIds[1]) then local from = UnitName ("player") @@ -336,7 +336,7 @@ return true elseif (type == "G") then --RoS - the 'server' send the encounter dps table to the player which requested - local encounterData = Details.storage:BuildEncounterDataToGuildSync(data) + local encounterData = Details222.storage.BuildEncounterDataToGuildSync(data) if (encounterData and encounterData[1]) then local task = C_Timer.NewTicker(4, function(task) @@ -365,7 +365,7 @@ return true elseif (type == "A") then --RoC - the player received the dps table and should now add it to the db - Details.storage:AddGuildSyncData(data, player) + Details222.storage.AddGuildSyncData(data, player) return true end end diff --git a/core/parser.lua b/core/parser.lua index e6647a2c..12c1dea8 100755 --- a/core/parser.lua +++ b/core/parser.lua @@ -394,10 +394,17 @@ --this block won't execute if the storage isn't loaded --self is a timer reference from C_Timer + local diffNumberToName = { + [1] = "normal", + [2] = "heroic", + [3] = "mythic", + [4] = "ascended", + } + local encounterID = self.Boss local diff = self.Diff - if (diff == 15 or diff == 16) then + if (diff == 1 or diff == 2 or diff == 3 or diff == 4) then --might give errors local value, rank, combatTime = 0, 0, 0 if (encounterID == lastRecordFound.id and diff == lastRecordFound.diff) then @@ -408,12 +415,14 @@ local role = _UnitGroupRolesAssigned("player") local isDamage = (role == "DAMAGER") or (role == "TANK") --or true - local bestRank, encounterTable = Details.storage:GetBestFromPlayer (diff, encounterID, isDamage and "damage" or "healing", Details.playername, true) + ---@type details_storage_unitresult, details_encounterkillinfo + local bestRank, encounterTable = Details222.storage.GetBestFromPlayer(diffNumberToName[diff], encounterID, isDamage and "DAMAGER" or "HEALER", Details.playername, true) if (bestRank) then - local playerTable, onEncounter, rankPosition = Details.storage:GetPlayerGuildRank (diff, encounterID, isDamage and "damage" or "healing", Details.playername, true) + ---@type number + local rankPosition = Details222.storage.GetUnitGuildRank(diffNumberToName[diff], encounterID, isDamage and "DAMAGER" or "HEALER", Details.playername, true) - value = bestRank[1] or 0 + value = bestRank.total or 0 rank = rankPosition or 0 combatTime = encounterTable.elapsed @@ -4809,7 +4818,6 @@ local SPELL_POWER_PAIN = SPELL_POWER_PAIN or (PowerEnum and PowerEnum.Pain) or 1 Details.is_in_arena = true Details:EnteredInArena() - else local inInstance = IsInInstance() if ((zoneType == "raid" or zoneType == "party") and inInstance) then @@ -4817,7 +4825,7 @@ local SPELL_POWER_PAIN = SPELL_POWER_PAIN or (PowerEnum and PowerEnum.Pain) or 1 --if the current raid is current tier raid, pre-load the storage database if (zoneType == "raid") then - if (Details.InstancesToStoreData[zoneMapID]) then + if (Details:IsZoneIdFromCurrentExpansion(zoneMapID)) then Details.ScheduleLoadStorage() end end @@ -4873,7 +4881,7 @@ local SPELL_POWER_PAIN = SPELL_POWER_PAIN or (PowerEnum and PowerEnum.Pain) or 1 end end - if (Details.InstancesToStoreData[zoneMapID]) then + if (Details:IsZoneIdFromCurrentExpansion(zoneMapID)) then Details.current_exp_raid_encounters[encounterID] = true end @@ -4893,7 +4901,7 @@ local SPELL_POWER_PAIN = SPELL_POWER_PAIN or (PowerEnum and PowerEnum.Pain) or 1 end end - if (IsInGuild() and IsInRaid() and Details.announce_damagerecord.enabled and Details.StorageLoaded) then + if (IsInGuild() and IsInRaid() and Details.announce_damagerecord.enabled and Details222.storageLoaded) then Details.TellDamageRecord = C_Timer.NewTimer(0.6, Details.PrintEncounterRecord) Details.TellDamageRecord.Boss = encounterID Details.TellDamageRecord.Diff = difficultyID @@ -5189,7 +5197,6 @@ local SPELL_POWER_PAIN = SPELL_POWER_PAIN or (PowerEnum and PowerEnum.Pain) or 1 --Details:Msg("(debug) running scheduled events after combat end.") end - --when the user requested data from the storage but is in combat lockdown if (Details.schedule_storage_load) then Details.schedule_storage_load = nil diff --git a/frames/window_forge.lua b/frames/window_forge.lua index 91520e50..905e00a6 100644 --- a/frames/window_forge.lua +++ b/frames/window_forge.lua @@ -59,7 +59,7 @@ function Details:OpenForge() local have_plugins_enabled for id, instanceTable in pairs(Details.EncounterInformation) do - if (Details.InstancesToStoreData [id]) then + if (Details:IsZoneIdFromCurrentExpansion(id)) then have_plugins_enabled = true break end diff --git a/frames/window_main.lua b/frames/window_main.lua index 4e6bf46a..f4fe27a5 100644 --- a/frames/window_main.lua +++ b/frames/window_main.lua @@ -2160,19 +2160,20 @@ local iconFrame_OnEnter = function(self) end local combat = instance:GetShowingCombat() - local diff = combat:GetDifficulty() + local diff, diffEngName = combat:GetDifficulty() local attribute, subattribute = instance:GetDisplay() --check if is a raid encounter and if is heroic or mythic - if (diff and (diff == 15 or diff == 16) and (attribute == 1 or attribute == 2)) then + if (diff and (diff == 15 or diff == 16) and (attribute == 1 or attribute == 2)) then --might give errors local db = Details.OpenStorage() if (db) then - local bestRank, encounterTable = Details.storage:GetBestFromPlayer(diff, combat:GetBossInfo().id, attribute == 1 and "damage" or "healing", name, true) + ---@type details_storage_unitresult, details_encounterkillinfo + local bestRank, encounterTable = Details222.storage.GetBestFromPlayer(diffEngName, combat:GetBossInfo().id, attribute == 1 and "DAMAGER" or "HEALER", name, true) if (bestRank) then --discover which are the player position in the guild rank - local playerTable, onEncounter, rankPosition = Details.storage:GetPlayerGuildRank(diff, combat:GetBossInfo().id, attribute == 1 and "damage" or "healing", name, true) + local rankPosition = Details222.storage.GetUnitGuildRank(diffEngName, combat:GetBossInfo().id, attribute == 1 and "DAMAGER" or "HEALER", name, true) - GameCooltip:AddLine("Best Score:", Details:ToK2((bestRank[1] or 0) / encounterTable.elapsed) .. " [|cFFFFFF00Rank: " .. (rankPosition or "#") .. "|r]", 1, "white") + GameCooltip:AddLine("Best Score:", Details:ToK2((bestRank.total or 0) / encounterTable.elapsed) .. " [|cFFFFFF00Rank: " .. (rankPosition or "#") .. "|r]", 1, "white") Details:AddTooltipBackgroundStatusbar() GameCooltip:AddLine("|TInterface\\TUTORIALFRAME\\UI-TUTORIAL-FRAME:14:12:0:1:512:512:8:70:224:306|t Open Rank", "|TInterface\\TUTORIALFRAME\\UI-TUTORIAL-FRAME:14:12:0:1:512:512:8:70:328:409|t Refresh Talents", 1, "white", "white") @@ -2333,13 +2334,13 @@ local icon_frame_on_click_up = function(self, button) if (instance) then local attribute, subattribute = instance:GetDisplay() local combat = instance:GetShowingCombat() - local diff = combat:GetDifficulty() + local diff, diffName = combat:GetDifficulty() local bossInfo = combat:GetBossInfo() if ((attribute == 1 or attribute == 2) and bossInfo) then --if bossInfo is nil, means the combat isn't a boss local db = Details.OpenStorage() if (db and bossInfo.id) then - local haveData = Details.storage:HaveDataForEncounter (diff, bossInfo.id, true) --attempt to index local 'bossInfo' (a nil value) + local haveData = Details222.storage.HaveDataForEncounter (diffName, bossInfo.id, true) --attempt to index local 'bossInfo' (a nil value) if (haveData) then Details:OpenRaidHistoryWindow (bossInfo.zone, bossInfo.id, diff, attribute == 1 and "damage" or "healing", true, 1, false, 2) end diff --git a/frames/window_statistics.lua b/frames/window_statistics.lua index 15ffaa56..080b955f 100644 --- a/frames/window_statistics.lua +++ b/frames/window_statistics.lua @@ -43,7 +43,7 @@ function Details:OpenRaidHistoryWindow(raidName, bossEncounterId, difficultyId, table.insert(UISpecialFrames, "DetailsRaidHistoryWindow") function statisticsFrame.OpenDB() - local db = Details.storage:OpenRaidStorage() + local db = Details222.storage.OpenRaidStorage() if (not db) then Details:Msg(Loc ["STRING_GUILDDAMAGERANK_DATABASEERROR"]) return @@ -128,7 +128,7 @@ function Details:OpenRaidHistoryWindow(raidName, bossEncounterId, difficultyId, statisticsFrame.DownloadedSize = 0 statisticsFrame.SyncStartTime = time() - Details.storage:DBGuildSync() + Details222.storage.DBGuildSync() statisticsFrame.GuildSyncButton:Disable() if (not statisticsFrame.SyncTexture) then diff --git a/functions/boss.lua b/functions/boss.lua index 309eefff..806d56b6 100644 --- a/functions/boss.lua +++ b/functions/boss.lua @@ -379,6 +379,19 @@ do --this cache is local and isn't shared with other components of the addon local expansionBossList_Cache = {build = false} + ---@class details_bossinfo : table + ---@field bossName string name + ---@field journalEncounterID number journalEncounterID + ---@field bossRaidName string instanceName + ---@field bossIcon number iconImage + ---@field bossIconCoords table {0, 1, 0, 0.95} + ---@field bossIconSize table {70, 36} + ---@field instanceId number raidInstanceID + ---@field uiMapId number UiMapID + ---@field instanceIndex number instanceIndex + ---@field journalInstanceId number journalInstanceID + ---@field dungeonEncounterID number dungeonEncounterID + function Details:GetExpansionBossList() --~bosslist - load on demand from gears-gsync and statistics-valid boss for exp if (expansionBossList_Cache.build) then return expansionBossList_Cache.bossIndexedTable, expansionBossList_Cache.bossInfoTable, expansionBossList_Cache.raidInfoTable @@ -426,6 +439,7 @@ do local name, description, journalEncounterID, rootSectionID, link, journalInstanceID, dungeonEncounterID, UiMapID = EJ_GetEncounterInfoByIndex(i, raidInstanceID) if (name) then local id, creatureName, creatureDescription, displayInfo, iconImage = EJ_GetCreatureInfo(1, journalEncounterID) + ---@type details_bossinfo local thisbossIndexedTable = { bossName = name, journalEncounterID = journalEncounterID,