From f899fd3e0ad22d71a5fa71bde64534c3ef655b84 Mon Sep 17 00:00:00 2001 From: Tercio Jose Date: Sun, 15 Sep 2024 11:55:00 -0300 Subject: [PATCH] Added a feature which should create a overall segment after a dungeon heroic and mythic0 run --- Definitions.lua | 16 ++- boot.lua | 253 +++++++++++++++++++++++++++++++++++ classes/class_combat.lua | 31 +++++ classes/container_actors.lua | 3 +- core/control.lua | 2 +- core/parser.lua | 8 +- startup.lua | 5 +- 7 files changed, 312 insertions(+), 6 deletions(-) diff --git a/Definitions.lua b/Definitions.lua index 1a23c8d67..06a4793b6 100644 --- a/Definitions.lua +++ b/Definitions.lua @@ -247,7 +247,7 @@ ---@field training_dummy boolean if true, the combat is against a training dummy ---@field playerTalents table [playerName] = "talent string" ---@field bossName string? the name of the boss, if the combat has no unitId "boss1", this value is nil ----@field +---@field context string? for the context manager ---@field ---@field __call table ---@field __index table @@ -284,6 +284,7 @@ ---@field player_last_events table record the latest events of each player, latter used to build the death log ---@field ---@field LockActivityTime fun(self: combat) +---@field AddCombat fun(self: combat, givingCombat: combat, bSetStartDate:boolean?, bSetEndDate:boolean?) ---@field CutDeathEventsByTime fun(self: combat, time: number?) remove death events by time, default 10 seconds ---@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 @@ -826,9 +827,22 @@ ---@field StopTime fun(actor: actor) stop the time of the actor ---@field SetOrGetPauseState fun(actor: actor, bPause: boolean|nil) : boolean|nil set or get the pause state of the actor, if bPause is nil, then it will return the current pause state +---@class instancedifficulty : table +---@field DungeonNormal number +---@field DungeonHeroic number +---@field DungeonMythic number +---@field DungeonMythicPlus number +---@field RaidLFR number +---@field RaidNormal number +---@field RaidHeroic number +---@field RaidMythic number + + ---@class details222 : table ---@field TimeMachine timemachine ---@field PetContainer petcontainer +---@field InstanceDifficulty instancedifficulty +---@field ContextManager contextmanager ---@class profile_breakdown_settings : table ---@field font_size number diff --git a/boot.lua b/boot.lua index c65eb67b6..21ae2ba52 100644 --- a/boot.lua +++ b/boot.lua @@ -213,6 +213,257 @@ --aura scanner Details222.AuraScan = {} + ---@type instancedifficulty + Details222.InstanceDifficulty = { + ["DungeonNormal"] = 1, + ["DungeonHeroic"] = 2, + ["DungeonMythic"] = 23, + ["DungeonMythicPlus"] = 8, + ["RaidLFR"] = 17, + ["RaidNormal"] = 14, + ["RaidHeroic"] = 15, + ["RaidMythic"] = 16, + } + + local emptyFunction = function()end + local emptyTable = {} + + ---context manager is a system that evaluates where the player is and create a set of extra rules that fit the content the player is doing + ---@class contextmanager : table + ---@field instanceType string + ---@field instanceName string + ---@field instanceId number + ---@field instanceDifficulty number + ---@field lastInstanceType string + ---@field lastInstanceName string + ---@field lastInstanceDifficulty number + ---@field contextId string + ---@field bContextStarted boolean + ---@field bContextFinished boolean + ---@field bHasContext boolean + ---@field fHasLostInterest function + ---@field fOnContextFinished function + ---@field fOnCombatFinished function + ---@field eventFrame frame + ---@field DetailsEventListener table + ---@field contextEventTable table + ---@field StartContext function + ---@field CheckContextInterest function + ---@field FinishContext function + ---@field GetContext function + + --tells what is the activity the player is doing + Details222.ContextManager = { + instanceType = "INIT", + instanceName = "INIT", + instanceDifficulty = 0, + lastInstanceType = "INIT", + lastInstanceName = "INIT", + lastInstanceDifficulty = 0, + contextId = "INIT", + bContextStarted = false, + bContextFinished = false, + bHasContext = false, + fOnContextFinished = emptyFunction, + fHasLostInterest = emptyFunction, + fOnCombatFinished = emptyFunction, + contextEventTable = emptyTable, + + eventFrame = CreateFrame("frame"), + + ---start a new context, this is called from the CheckContextInterest() function + ---@param self contextmanager + ---@param instanceId number + ---@param instanceName string + ---@param instanceType string + ---@param difficultyId number + ---@param contextEventTable table + ---@param fOnCombatFinished function run when details! finishes a combat + ---@param fOnContextFinished function run when the context is finished + ---@param fHasLostInterest function run when CheckContextInterest() fails to find a context + StartContext = function(self, instanceId, instanceName, instanceType, difficultyId, contextEventTable, fOnCombatFinished, fOnContextFinished, fHasLostInterest) + self.instanceType = instanceType + self.instanceName = instanceName + self.instanceId = instanceId + self.instanceDifficulty = difficultyId + self.bContextStarted = true + self.bContextFinished = false + self.bHasContext = true + self.fOnContextFinished = fOnContextFinished + self.fHasLostInterest = fHasLostInterest + self.fOnCombatFinished = fOnCombatFinished + self.contextEventTable = contextEventTable + + --create an event listener to grab the event when Details! finishes a combat + if (not self.DetailsEventListener) then + self.DetailsEventListener = Details:CreateEventListener() + end + self.DetailsEventListener:UnregisterEvent("COMBAT_PLAYER_LEAVE") + --register the onFinishCombat for the context + self.DetailsEventListener:RegisterEvent("COMBAT_PLAYER_LEAVE", fOnCombatFinished) + + --unregister all events + self.eventFrame:UnregisterAllEvents() + + --register the events that the context require + for i = 1, #contextEventTable.events do + self.eventFrame:RegisterEvent(contextEventTable.events[i]) + end + + --if the callback function returns true, the context is finished + self.eventFrame:SetScript("OnEvent", function(eventFrame, event, ...) + if (contextEventTable.callback(event, ...)) then + Details222.DebugMsg("context manager event", event) + --context completed + Details222.DebugMsg("Context Completed!") + C_Timer.After(1, fOnContextFinished) + C_Timer.After(1.1, function() self:FinishContext() end) + end + end) + + Details222.DebugMsg("a new context has been set.") + end, + + ---check if the player is in a context of interest + ---@param self contextmanager + ---@param instanceId number + ---@param instanceName string + ---@param instanceType string + ---@param difficultyId number + CheckContextInterest = function(self, instanceId, instanceName, instanceType, difficultyId) + Details222.DebugMsg("Checking for new context:", instanceId, instanceName, instanceType, difficultyId) + --normal, heroic and mythic0 dungeons on Retail + local diffTable = Details222.InstanceDifficulty + if (difficultyId == diffTable.DungeonNormal or difficultyId == diffTable.DungeonHeroic or difficultyId == diffTable.DungeonMythic) then + if (DetailsFramework.IsDragonflightAndBeyond()) then + --check if the player is in the same context + if (self.bHasContext and self.instanceId == instanceId and self.instanceType == instanceType and self.instanceName == instanceName and self.instanceDifficulty == difficultyId) then + return + end + + --if a context is found, finishes it before a new one is created + if (self.bHasContext) then + --discard the context + Details222.DebugMsg("had an active context, finishing it.") + self:FinishContext() + end + + --set a new context where at the end of the dungeon it creates an overall segment for the run + --function to verify if context is finished, in this case if all objectives of the dungeon has been completed by listening to the SCENARIO_COMPLETED event + local contextEventTable = { + events = {"SCENARIO_COMPLETED"}, + callback = function(...) + --when a context return true, the context is finished and will trigger a call on the fOnContextFinished function + return true + end + } + + --create a contextId to tag combats that are part of the same context + self.contextId = instanceName .. tostring(time()) + + --called when a combat finishes and this context is still active + local fOnCombatFinished = function() + local currentCombat = Details:GetCurrentCombat() + currentCombat.context = self.contextId + end + + ---this function evaluates if this context has lost its interest and should be discarded, return true if the context is no longer valid + local fHasLostInterest = function(instanceId, instanceName, instanceType, difficultyId) + --check if the player is still in the same context + if (self.instanceId ~= instanceId or self.instanceType ~= instanceType or self.instanceName ~= instanceName or self.instanceDifficulty ~= difficultyId) then + return true + end + end + + --will ba called when the context finishes, in this case when the SCENARIO_COMPLETED event is triggered + local fOnContextFinished = function() + ---@type combat[] + local interestCombats = {} + --get all segments + local segments = Details:GetCombatSegments() + for i = 1, #segments do + local segment = segments[i] + if (segment.context == self.contextId) then + interestCombats[#interestCombats+1] = segment + end + end + + if (#interestCombats > 0) then + --start a new combat + Details222.StartCombat() + + Details222.DebugMsg("merging", #interestCombats, "combats into a single combat.") + + ---@type combat + local currentCombat = Details:GetCurrentCombat() + + --iterate over all interest combats + for i = 1, #interestCombats do + local interestCombat = interestCombats[i] + --add the combat to the new combat + currentCombat:AddCombat(interestCombat, i == 1, i == #interestCombats) + end + + Details222.DebugMsg("combat time:", currentCombat:GetCombatTime()) + + --finish the new combat + Details:EndCombat() + end + + Details222.DebugMsg("overall segment has been created.") + end + + self:StartContext(instanceId, instanceName, instanceType, difficultyId, contextEventTable, fOnCombatFinished, fOnContextFinished, fHasLostInterest) + + return + end + else + --if no context is found, check if there is a current context and check if it lost its interest + if (self.bHasContext) then + if (self.fHasLostInterest(self, instanceId, instanceName, instanceType, difficultyId)) then + Details222.DebugMsg("no context found, but context is active, finishing the current context.") + --discard the context + self:FinishContext() + end + end + end + end, + + ---finish the current context + ---@param self contextmanager + FinishContext = function(self) + if (not self.bHasContext or not self.bContextStarted or self.bContextFinished) then + return + end + + --mark this context as finished + self.bContextFinished = true + + --reset context + self.instanceType = "INIT" + self.instanceName = "INIT" + self.contextId = "INIT" + self.instanceId = -1 + self.instanceDifficulty = 0 + self.bContextStarted = false + self.bHasContext = false + self.fOnContextFinished = emptyFunction + self.fHasLostInterest = emptyFunction + self.fOnCombatFinished = emptyFunction + self.contextEventTable = emptyTable + end, + + ---return the current contextIndex + ---@param self contextmanager + ---@return number|boolean, string?, string?, number? + GetContext = function(self) + if (self.bHasContext) then + return self.instanceId, self.instanceName, self.instanceType, self.instanceDifficulty + end + return false + end, + } + local GetSpellInfo = C_Spell and C_Spell.GetSpellInfo or GetSpellInfo Details222.GetSpellInfo = GetSpellInfo @@ -623,6 +874,8 @@ Made Details! survive for another expansion (Details! Team). _detalhes.debug_chr = false _detalhes.opened_windows = 0 _detalhes.last_combat_time = 0 + _detalhes.last_zone_type = "INIT" + _detalhes.last_zone_id = -1 --store functions to create options frame Details.optionsSection = {} diff --git a/classes/class_combat.lua b/classes/class_combat.lua index 82daa2dd5..35e6928fa 100644 --- a/classes/class_combat.lua +++ b/classes/class_combat.lua @@ -959,6 +959,37 @@ local segmentTypeToString = { end end + ---@param self combat + ---@param givingCombat combat + ---@param bSetStartDate boolean if true, the start date of the receiving combat will be set to the start date of the giving combat + ---@param bSetEndDate boolean if true, the end date of the receiving combat will be set to the end date of the giving combat + ---@return combat + function classCombat:AddCombat(givingCombat, bSetStartDate, bSetEndDate) + local receivingCombat = self + + local timeInCombat = 0 + + receivingCombat:CopyDeathsFrom(givingCombat, false) + timeInCombat = timeInCombat + givingCombat:GetCombatTime() + + receivingCombat = receivingCombat + givingCombat + + local startDate, endDate = givingCombat:GetDate() + local startTime, endTime = givingCombat:GetStartTime(), givingCombat:GetEndTime() + if (bSetStartDate) then + receivingCombat:SetDate(startDate, endDate) + receivingCombat:SetStartTime(startTime) + receivingCombat:SetEndTime(endTime) + else + if (bSetEndDate) then + receivingCombat:SetDate(false, endDate) + receivingCombat:SetEndTime(endTime) + end + end + + return receivingCombat + end + --return the total of a specific attribute local power_table = {0, 1, 3, 6, 0, "alternatepower"} diff --git a/classes/container_actors.lua b/classes/container_actors.lua index 66443a56b..4c0f8fc9a 100644 --- a/classes/container_actors.lua +++ b/classes/container_actors.lua @@ -789,10 +789,9 @@ unitNameTitles[#unitNameTitles+1] = unitNameTitles[1]:gsub(PET_TYPE_PET, PET_TYP else --anything else that isn't a player or a pet actorObject.displayName = actorName - local npcID = Details:GetNpcIdFromGuid(actorSerial) if (npcID) then - if (npcID == 210759 or npcID == 216287) then + if (npcID == 210759 or npcID == 216287) then --210759 --flag 0x2111 actorObject.grupo = true end end diff --git a/core/control.lua b/core/control.lua index b7705ed8d..2a3c7c710 100644 --- a/core/control.lua +++ b/core/control.lua @@ -1524,7 +1524,7 @@ if (instance.rows_showing == 0 and instance:GetSegment() == -1) then -- -1 overall data if (not instance:IsShowingOverallDataWarning()) then local tutorial = Details:GetTutorialCVar("OVERALLDATA_WARNING1") or 0 - if ((type(tutorial) == "number") and (tutorial < 60)) then + if ((type(tutorial) == "number") and (tutorial < 10)) then Details:SetTutorialCVar ("OVERALLDATA_WARNING1", tutorial + 1) instance:ShowOverallDataWarning (true) end diff --git a/core/parser.lua b/core/parser.lua index d1d8df395..0d8bf23a5 100755 --- a/core/parser.lua +++ b/core/parser.lua @@ -5327,18 +5327,24 @@ local SPELL_POWER_PAIN = SPELL_POWER_PAIN or (PowerEnum and PowerEnum.Pain) or 1 end end + function Details.parser_functions:SCENARIO_COMPLETED(...) + + end + function Details.parser_functions:ZONE_CHANGED_NEW_AREA(...) return Details.Schedules.After(1, Details.Check_ZONE_CHANGED_NEW_AREA) end --~zone ~area function Details:Check_ZONE_CHANGED_NEW_AREA() - local zoneName, zoneType, _, _, _, _, _, zoneMapID = GetInstanceInfo() + local zoneName, zoneType, difficultyID, difficultyName, _, _, _, zoneMapID = GetInstanceInfo() Details.zone_type = zoneType Details.zone_id = zoneMapID Details.zone_name = zoneName + Details222.ContextManager:CheckContextInterest(zoneMapID, zoneName, zoneType, difficultyID) + _in_resting_zone = IsResting() if (_in_resting_zone) then diff --git a/startup.lua b/startup.lua index 3a1fff73f..04a950646 100644 --- a/startup.lua +++ b/startup.lua @@ -279,6 +279,10 @@ function Details222.StartUp.StartMeUp() Details.listener:RegisterEvent("ZONE_CHANGED_NEW_AREA") Details.listener:RegisterEvent("PLAYER_ENTERING_WORLD") + if (C_EventUtils.IsEventValid("SCENARIO_COMPLETED")) then + Details.listener:RegisterEvent("SCENARIO_COMPLETED") + end + Details.listener:RegisterEvent("ENCOUNTER_START") Details.listener:RegisterEvent("ENCOUNTER_END") @@ -336,7 +340,6 @@ function Details222.StartUp.StartMeUp() Details:SendEvent("GROUP_ONLEAVE") end - Details.last_zone_type = "INIT" Details.parser_functions:ZONE_CHANGED_NEW_AREA() Details.AnnounceStartup = nil end