diff --git a/API General.txt b/API General.txt index 5d7d8c5d7..cf8b38b71 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 9f73657d2..3737369ac 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 e13921928..5d82174a9 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 7bd6d2f59..f8c6c409f 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/Libs/LibLuaServer/LibLuaServer.lua b/Libs/LibLuaServer/LibLuaServer.lua index a94734945..383806006 100644 --- a/Libs/LibLuaServer/LibLuaServer.lua +++ b/Libs/LibLuaServer/LibLuaServer.lua @@ -369,6 +369,8 @@ function LibStub:IterateLibraries()end ---@alias actorid string unique id of a unit (GUID) ---@alias serial string unique id of a unit (GUID) ---@alias guid string unique id of a unit (GUID) +---@alias guildname string name of the guild +---@alias date string date in the format "YYYY-MM-DD" ---@alias keylevel number the level of a mythic dungeon key ---@alias mapid number each map in the game has a unique map id, this id can be used to identify a map. ---@alias challengemapid number each challenge mode map in the game has a unique map id, this id can be used to identify a challenge mode map. @@ -387,6 +389,7 @@ function LibStub:IterateLibraries()end ---@alias unixtime number a number that represents the number of seconds that have elapsed since 00:00:00 Coordinated Universal Time (UTC), Thursday, 1 January 1970, not counting leap seconds. ---@alias timestring string refers to a string showing a time value, such as "1:23" or "1:23:45". ---@alias combattime number elapsed time of a combat or time in seconds that a unit has been in combat. +---@alias servertime number unixtime on the server ---@alias auraduration number ---@alias gametime number number of seconds that have elapsed since the start of the game session. ---@alias milliseconds number a number in milliseconds, usually need to divide by 1000 to get the seconds. @@ -1667,6 +1670,9 @@ function bit.rol(x, y) return 0 end ---@return number The bitwise rotate right of the number. function bit.ror(x, y) return 0 end +---return the epoch time in seconds from the server. +---@return number +function GetServerTime() return 0 end C_Timer = {} @@ -3526,8 +3532,9 @@ GetGuildCharterCost = function() return 0 end ---@return string, string, number, number, number, boolean, boolean, string, string GetGuildEventInfo = function(index) return "", "", 0, 0, 0, true, true, "", "" end +---@param unit string ---@return string, string, number, number, number, number, string, string, number, string -GetGuildInfo = function() return "", "", 0, 0, 0, 0, "", "", 0, "" end +GetGuildInfo = function(unit) return "", "", 0, 0, 0, 0, "", "", 0, "" end ---@return string GetGuildInfoText = function() return "" end diff --git a/boot.lua b/boot.lua index ea2d2fd78..c4f542bde 100644 --- a/boot.lua +++ b/boot.lua @@ -112,6 +112,14 @@ [153292] = true, --stormwind } + ---@type details_storage_feature + ---@diagnostic disable-next-line: missing-fields + local storage = { + DiffNames = {"normal", "heroic", "mythic"}, + DiffNamesHash = {normal = 14, heroic = 15, mythic = 16}, + } + Details222.storage = storage + --namespace for damage spells (spellTable) Details222.DamageSpells = {} --namespace for texture @@ -584,8 +592,8 @@ do {Name = "Report What is Shown In the Window", Desc = "Report the current data shown in the window, the number 1 is the window number, replace it to report another window.", MacroText = "/script Details:FastReportWindow(1)"}, } - --current instances of the exp (need to maintain) - _detalhes.InstancesToStoreData = { --mapId + --current instances of the exp (need to maintain) - deprecated july 2024 - should do this automatically + Details.InstancesToStoreData = { --mapId [2657] = true, --Nerub-ar Palace v11 T1 [2294] = true, --Nerub-ar Palace v11 T1 } diff --git a/classes/class_combat.lua b/classes/class_combat.lua index af571abdf..f5b400ce9 100644 --- a/classes/class_combat.lua +++ b/classes/class_combat.lua @@ -177,8 +177,18 @@ local segmentTypeToString = { return rawget(self, "is_trash") end + local diffNumberToName = { + [14] = "normal", + [15] = "heroic", + [16] = "mythic", + } + 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 +951,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 239c6fa3d..15b7c425c 100644 --- a/classes/class_damage.lua +++ b/classes/class_damage.lua @@ -5301,7 +5301,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() @@ -5310,14 +5310,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 == 15 or diff == 16)) 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 bf1b694b5..b9613dae1 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" + ------------------------------------------------------------------------------------------------------------ --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,75 @@ 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 = { + [14] = "normal", + [15] = "heroic", + [16] = "mythic", +} + +local createStorageTables = function() + local storageDatabase = DetailsDataStorage + + if (not storageDatabase and Details.CreateStorageDB) then + storageDatabase = Details:CreateStorageDB() + if (not storageDatabase) then + return + end -function Details.storage:OpenRaidStorage() + 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 C_AddOns.IsAddOnLoaded(CONST_ADDONNAME_DATASTORAGE)) then local loaded, reason = C_AddOns.LoadAddOn(CONST_ADDONNAME_DATASTORAGE) @@ -839,111 +903,126 @@ function Details.storage:OpenRaidStorage() end end - do return 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" - 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 + role = "DAMAGER" + end + + ---@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 @@ -952,22 +1031,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) - - local db = Details.storage:OpenRaidStorage() +---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() - 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 @@ -975,156 +1065,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" - end - - local playerScore = {} - - 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 - - if (not playerScore [playerName]) then - playerScore [playerName] = {0, 0, {}} + role = "DAMAGER" + end + + ---@class details_storage_unitscore : table + ---@field total number + ---@field persecond number + ---@field storageUnitResult details_storage_unitresult? + ---@field encounterKillInfo details_encounterkillinfo? + ---@field unitName actorname? + + ---@type table + local unitScores = {} + + ---@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" - 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 + role = "DAMAGER" + end + + 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 @@ -1144,51 +1236,100 @@ 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() +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 bossIndexedTable, bossInfoTable, raidInfoTable = Details:GetExpansionBossList() + 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 _, bossInfoTable, raidInfoTable = Details:GetExpansionBossList() + for bossId, bossTable in pairs(bossInfoTable) do + ---@cast bossTable details_bossinfo + currentExpZoneIds[bossTable.uiMapId] = true + currentExpZoneIds[bossTable.instanceId] = true + currentExpZoneIds[bossTable.journalInstanceId] = true + end + + for raidInstanceID, raidTable in pairs(raidInfoTable) do + currentExpZoneIds[raidInstanceID] = true + currentExpZoneIds[raidTable.raidMapID] = true + 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 @@ -1204,10 +1345,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 @@ -1218,18 +1361,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 @@ -1242,10 +1382,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 @@ -1259,8 +1401,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 @@ -1268,16 +1414,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 @@ -1298,15 +1446,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 @@ -1314,39 +1476,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 (_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 (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 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 @@ -1356,7 +1515,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 @@ -1365,104 +1524,62 @@ function Details.storage:AddGuildSyncData(data, source) end end -function Details.storage:ListDiffs() - local db = Details.storage:OpenRaidStorage() - - if (not db) then - return - end +---@param difficulty string +---@return encounterid[] +function Details222.storage.ListEncounters(difficulty) + ---@type details_storage? + local savedData = Details222.storage.OpenRaidStorage() - local resultTable = {} - for difficultyId in pairs(db) do - tinsert(resultTable, difficultyId) + if (not savedData) then + return {} end - return resultTable -end - -function Details.storage:ListEncounters(difficultyId) - local db = Details.storage:OpenRaidStorage() - if (not db) then - return + if (not difficulty) then + return {} end + ---@type encounterid[] 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 + assert(type(unitName) == "string", "unitName must be a string.") + assert(type(dungeonEncounterID) == "number", "dungeonEncounterID must be a string.") + + ---@type details_storage_unitresult[] local resultTable = {} - assert(type(playerName) == "string", "playerName 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_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 @@ -1471,88 +1588,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 C_AddOns.IsAddOnLoaded(CONST_ADDONNAME_DATASTORAGE)) then - local loaded, reason = C_AddOns.LoadAddOn(CONST_ADDONNAME_DATASTORAGE) - if (not loaded) then + local bSuccessLoaded, reason = C_AddOns.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 (C_AddOns.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 @@ -1569,11 +1673,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 C_AddOns.IsAddOnLoaded(CONST_ADDONNAME_DATASTORAGE)) then --can't open it during combat @@ -1592,10 +1694,10 @@ function Details.OpenStorage() return end - local db = createStorageTables() + local savedData = createStorageTables() - if (db and C_AddOns.IsAddOnLoaded(CONST_ADDONNAME_DATASTORAGE)) then - Details.StorageLoaded = true + if (savedData and C_AddOns.IsAddOnLoaded(CONST_ADDONNAME_DATASTORAGE)) then + Details222.storageLoaded = true end return DetailsDataStorage @@ -1607,9 +1709,8 @@ 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 not loaded yet and try to load it if (not C_AddOns.IsAddOnLoaded(CONST_ADDONNAME_DATASTORAGE)) then local loaded, reason = C_AddOns.LoadAddOn(CONST_ADDONNAME_DATASTORAGE) @@ -1622,39 +1723,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,9 +1762,9 @@ function Details.Database.StoreWipe(combat) return end - local name, type, difficulty, difficultyName, maxPlayers, playerDifficulty, isDynamicInstance, mapID, instanceGroupSize = GetInstanceInfo() + local name, type, zoneDifficulty, difficultyName, maxPlayers, playerDifficulty, isDynamicInstance, mapID, instanceGroupSize = GetInstanceInfo() - if (not instancesToStoreData[mapID]) then + if (not Details:IsZoneIdFromCurrentExpansion(mapID)) then if (Details.debug) then print("|cFFFFFF00Details! Storage|r: instance not allowed.") end @@ -1683,32 +1782,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, @@ -1720,17 +1808,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() @@ -1744,7 +1830,7 @@ function Details.Database.StoreEncounter(combat) local name, type, difficulty, difficultyName, maxPlayers, playerDifficulty, isDynamicInstance, mapID, instanceGroupSize = GetInstanceInfo() - if (not instancesToStoreData[mapID]) then + if (not Details:IsZoneIdFromCurrentExpansion(mapID)) then if (Details.debug) then print("|cFFFFFF00Details! Storage|r: instance not allowed.") end @@ -1752,9 +1838,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 @@ -1762,89 +1848,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 - --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} + ---@type details_encounterkillinfo[] + local allEncountersStored = encountersTable[encounterId] + if (not allEncountersStored) then + encountersTable[encounterId] = {} + allEncountersStored = encountersTable[encounterId] + end - local bossData = totalkills_database[encounter_id][diff] - local encounterElapsedTime = combat:GetCombatTime() + --total kills in a boss on raid or dungeon + local totalkillsTable = Details.Database.GetBossKillsDB(savedData) - --kills amount - bossData.kills = bossData.kills + 1 + --store total kills on this boss + --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 + } - --best time - if (encounterElapsedTime > bossData.time_fasterkill) then - bossData.time_fasterkill = encounterElapsedTime - bossData.time_fasterkill_when = time() - end + ---@type details_bosskillinfo + local bossData = totalkillsTable[encounterId][diff] + ---@type combattime + local encounterElapsedTime = elapsedCombatTime - --total time in combat - bossData.time_incombat = bossData.time_incombat + encounterElapsedTime + --kills amount + bossData.kills = bossData.kills + 1 - --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 + --best time + if (encounterElapsedTime < bossData.time_fasterkill) then + bossData.time_fasterkill = encounterElapsedTime + bossData.time_fasterkill_when = time() + 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 + --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 == 15 or diff == 16 or diff == 14)) 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 @@ -1860,97 +1984,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 == "DAMAGER" or role == "TANK") then - local player_name = Details:GetFullName("raid" .. i) - local _, _, class = Details:GetUnitClassFull(player_name) - - 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 playerName = Details:GetFullName(cachedUnitIds[i]) + local _, _, class = Details:GetUnitClassFull(playerName) + + 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") then - local player_name = Details:GetFullName("raid" .. i) - - local _, _, class = Details:GetUnitClassFull(player_name) - - 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} + local playerName = Details:GetFullName(cachedUnitIds[i]) + local _, _, class = Details:GetUnitClassFull(playerName) + + local healingActor = healingContainer:GetActor(playerName) + if (healingActor) then + local guid = UnitGUID(cachedUnitIds[i]) + + ---@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 @@ -2440,8 +2581,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 ce2354a39..fd41005c6 100644 --- a/core/network.lua +++ b/core/network.lua @@ -401,7 +401,7 @@ --return false end - local IDs = Details.storage:GetIDsToGuildSync() + local IDs = Details222.storage.GetIDsToGuildSync() if (IDs and IDs [1]) then local from = UnitName("player") @@ -413,7 +413,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") @@ -423,7 +423,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) @@ -452,7 +452,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 2c8002b1b..f1fc456ea 100755 --- a/core/parser.lua +++ b/core/parser.lua @@ -677,10 +677,16 @@ --this block won't execute if the storage isn't loaded --self is a timer reference from C_Timer + local diffNumberToName = { + [14] = "normal", + [15] = "heroic", + [16] = "mythic", + } + local encounterID = self.Boss local diff = self.Diff - if (diff == 15 or diff == 16) then + if (diff == 15 or diff == 16) then --might give errors local value, rank, combatTime = 0, 0, 0 if (encounterID == lastRecordFound.id and diff == lastRecordFound.diff) then @@ -691,12 +697,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 @@ -5367,7 +5375,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 @@ -5375,8 +5382,8 @@ 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 - --Details.ScheduleLoadStorage() + if (Details:IsZoneIdFromCurrentExpansion(zoneMapID)) then + Details.ScheduleLoadStorage() end end @@ -5431,7 +5438,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 @@ -5451,7 +5458,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 @@ -5749,11 +5756,10 @@ 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 - --Details.ScheduleLoadStorage() + Details.ScheduleLoadStorage() end --store a boss encounter when out of combat since it might need to load the storage diff --git a/frames/window_forge.lua b/frames/window_forge.lua index 1a2caa3fd..751aa3758 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 efcb31f41..574589a87 100644 --- a/frames/window_main.lua +++ b/frames/window_main.lua @@ -2208,19 +2208,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") @@ -2449,13 +2450,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 23c609b75..07e135ca0 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 309eefffc..806d56b63 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,