local LuaSystemBase = require_ex('LuaSystem/LuaSystemBase') MainHudActionQueueSystem = HL.Class('MainHudActionQueueSystem', LuaSystemBase.LuaSystemBase) MainHudActionQueueSystem._InitConfigs = HL.Method() << function(self) self.configs = { Cinematic = { order = CS.Beyond.MainHudActionQueueConsts.CINEMATIC_ORDER_FIRST, needWait = true, isCinematic = true, dropWhenChangeScene = true, }, DashBarUpgrade = { order = 0, needWait = true, ignoreCinematicInterrupt = true, checkGuideOnStart = true, }, CashShopOrderSettle = { order = 1, needWait = true, finishWhenInterrupt = true, checkGuideOnStart = true, }, MonthlyPassPopup = { order = 1, needWait = true, finishWhenInterrupt = true, checkGuideOnStart = true, }, FacTechPointGained = { order = 1, needWait = true, finishWhenInterrupt = true, preloadPanelId = PanelId.FacTechPointGainedToast, }, ChapterPanelWithoutMissionHud = { order = CS.Beyond.MainHudActionQueueConsts.BLOCKER_ORDER, needWait = true, finishWhenInterrupt = true, dropWhenChangeScene = true, }, MissionHudCompleteBlocker = { order = CS.Beyond.MainHudActionQueueConsts.BLOCKER_ORDER, needWait = true, finishWhenInterrupt = false, dropWhenChangeScene = true, }, CenterRewards = { order = 2, needWait = true, preloadPanelId = PanelId.RewardsPopupCenter, }, EquipFormulaRewardPopup = { order = 2, needWait = true, finishWhenInterrupt = true, preloadPanelId = PanelId.EquipFormulaRewardPopup, }, WorldLevelPreview = { order = 2.1, needWait = true, finishWhenInterrupt = true, dropWhenChangeScene = true, checkGuideOnStart = true, }, SettlementToast = { order = 2.5, needWait = true, finishWhenInterrupt = true, }, ImportantReward = { order = 3, needWait = true, ignoreCinematicInterrupt = true, preloadPanelId = PanelId.ImportantRewardPopup, checkGuideOnStart = true, }, AdventureLevelUp = { order = 4, needWait = true, finishWhenInterrupt = false, preloadPanelId = PanelId.AdventureLevelUp, checkGuideOnStart = true, }, AfterLevelUpSNS = { order = 4.1, needWait = true, dropWhenChangeScene = true, }, PuzzlePickup = { order = 6, needWait = true, finishWhenInterrupt = true, preloadPanelId = PanelId.PuzzlePickupToast, }, EndingToast = { order = 6, needWait = true, finishWhenInterrupt = true, }, MissionHudResumeInfo = { order = 7, needWait = false, }, CommonPOIUpgradeToast = { order = 8, needWait = true, finishWhenInterrupt = true, preloadPanelId = PanelId.CommonPOIUpgradeToast, }, DomainDepotDeliverToast = { order = 9, needWait = true, finishWhenInterrupt = true, preloadPanelId = PanelId.DomainDepotDeliverToast, }, DomainUpgrade = { order = 10, needWait = true, finishWhenInterrupt = true, preloadPanelId = PanelId.DomainUpgrade, }, MapRegionToast = { order = 98, needWait = true, finishWhenInterrupt = true, dropWhenChangeScene = true, preloadPanelId = PanelId.MapRegionToast, }, SpaceshipHudTips = { order = 99, needWait = true, dropWhenChangeScene = true, }, SNSNormalNotice = { order = 100, needWait = false, dropWhenChangeScene = true, }, GetItemToast = { order = 100, needWait = false, }, FirstGotItem = { order = 100, needWait = false, }, UnlockPRTS = { order = 100, needWait = false, }, DomainUpgradeToast = { order = 100, needWait = false, }, WeeklyRaidEnter = { order = 100, needWait = true, finishWhenInterrupt = false, dropWhenChangeScene = true, }, } for k, v in pairs(self.configs) do v.name = k end end MainHudActionQueueSystem.m_pendingRequests = HL.Field(HL.Table) MainHudActionQueueSystem.configs = HL.Field(HL.Table) MainHudActionQueueSystem.m_nextRequestId = HL.Field(HL.Number) << 1 MainHudActionQueueSystem.m_isShowing = HL.Field(HL.Boolean) << false MainHudActionQueueSystem.m_tryStartActionCor = HL.Field(HL.Thread) MainHudActionQueueSystem.m_curShowingActionOrder = HL.Field(HL.Number) << math.mininteger MainHudActionQueueSystem.MainHudActionQueueSystem = HL.Constructor() << function(self) self:_InitConfigs() self.m_pendingRequests = {} self.m_playIgnoreMainHudActionTypes = {} self:RegisterMessage(MessageConst.ON_ONE_MAIN_HUD_ACTION_FINISHED, function(arg) local name if type(arg) == "table" then name = unpack(arg) else name = arg end self:OnOneMainHudActionFinish(name) end) self:RegisterMessage(MessageConst.ON_IN_MAIN_HUD_CHANGED, function(arg) local inMainHud = unpack(arg) if inMainHud then self:_TryAddStartActionCor() else if next(self.m_playIgnoreMainHudActionTypes) then local nextRequestIndex = self:_GetNextRequestIndex(false) if nextRequestIndex then if nextRequestIndex == 1 then if self.m_isShowing then return else self:_TryStartAction() return end else self:Interrupt() self:_TryStartAction() return end end end self:Interrupt() end end) self:RegisterMessage(MessageConst.ON_PHASE_LEVEL_DESTROYED, function(arg) self:Interrupt(true) self:_DropRequestWhenChangeScene() end) self:RegisterMessage(MessageConst.ON_GUIDE_STOPPED, function(arg) local isForce = unpack(arg) if isForce then self:_TryAddStartActionCor() end end) self:RegisterMessage(MessageConst.ON_ONE_COMMON_TASK_REQUEST_FINISH, function() self:_TryAddStartActionCor() end) self:RegisterMessage(MessageConst.INTERRUPT_MAIN_HUD_ACTION_ON_COMMON_BLEND_IN, function() self:Interrupt() end) self:RegisterMessage(MessageConst.ON_COMMON_BLEND_OUT, function() self:_TryAddStartActionCor() end) self:RegisterMessage(MessageConst.COMMON_START_BLOCK_MAIN_HUD_ACTION_QUEUE, function() self:Interrupt() end) self:RegisterMessage(MessageConst.COMMON_END_BLOCK_MAIN_HUD_ACTION_QUEUE, function() self:_TryAddStartActionCor() end) self:RegisterMessage(MessageConst.ON_LEAVE_TOWER_DEFENSE_DEFENDING_PHASE, function() self:Interrupt() end) self:RegisterMessage(MessageConst.ON_TOWER_DEFENSE_LEVEL_REWARDS_FINISHED, function() self:_TryAddStartActionCor() end) self:RegisterMessage(MessageConst.MAIN_HUD_ACTION_QUEUE_TOGGLE_IGNORE_MAIN_HUD_CS, function(args) local actionType, active = unpack(args) self:ToggleActionPlayIgnoreMainHud(actionType, active) end) self:RegisterMessage(MessageConst.PRE_LEVEL_START, function() if Utils.isInBlackbox() then self:ManuallyDropAllRequests() end end) self:RegisterMessage(MessageConst.ON_CONFIRM_CHANGE_INPUT_DEVICE_TYPE, function() self:Interrupt() end) self:RegisterMessage(MessageConst.ON_SWITCH_LANGUAGE, function() self:Interrupt() end) end MainHudActionQueueSystem.AddRequest = HL.Method(HL.String, HL.Function, HL.Opt(HL.Function, HL.Boolean, HL.Function)) << function(self, type, action, getOrder, startImmediately, onDrop) logger.important(CS.Beyond.EnableLogType.MainHudActionQueue, "MainHudActionQueueSystem.AddRequest", type, startImmediately) local requestId = self.m_nextRequestId self.m_nextRequestId = self.m_nextRequestId + 1 local cfg = self.configs[type] local request = { id = requestId, type = type, action = action, getOrder = getOrder, onDrop = onDrop, cfg = cfg, } setmetatable(request, { __index = cfg }) table.insert(self.m_pendingRequests, request) if request.isCinematic then Notify(MessageConst.FAC_BUILD_EXIT_CUR_MODE, true) Notify(MessageConst.FAC_EXIT_DESTROY_MODE, true) LuaSystemManager.factory:ToggleTopView(false, true) if GameWorld.worldInfo.inMainHud or self.m_playIgnoreMainHudActionTypes[type] then local curRequest = self.m_pendingRequests[1] if curRequest and not curRequest.isCinematic and not curRequest.ignoreCinematicInterrupt then if self.m_isShowing then self:Interrupt() curRequest.order = nil local nextRequestIndex = self:_GetNextRequestIndex(true) self:_MoveRequestToTop(nextRequestIndex) self:_StartFirstAction() return else curRequest.order = nil end end end end self:_SortRequest(true) request.startImmediately = startImmediately if startImmediately then if self.m_isShowing or self.m_pendingRequests[1] ~= request then logger.warn("MainHudActionQueueSystem.AddRequest", type, "startImmediately 失败:当前正在播放", request.type) else self:_TryStartAction() return end end self:_TryAddStartActionCor() end MainHudActionQueueSystem._SortRequest = HL.Method(HL.Boolean) << function(self, forceSort) local needSort = forceSort for k, request in ipairs(self.m_pendingRequests) do if request.getOrder and (not self.m_isShowing or k > 1) then needSort = true request.order = request.getOrder() end end if not needSort then return end table.sort(self.m_pendingRequests, Utils.genSortFunction({ "order", "id" }, true)) logger.important(CS.Beyond.EnableLogType.MainHudActionQueue, "MainHudActionQueueSystem._SortRequest") end MainHudActionQueueSystem.IsShowing = HL.Method().Return(HL.Boolean) << function(self) return self.m_isShowing end MainHudActionQueueSystem.GetCurQueueFirstRequest = HL.Method().Return(HL.Opt(HL.Table)) << function(self) return self.m_pendingRequests[1] end MainHudActionQueueSystem.GetCurQueueFirstRequestType = HL.Method().Return(HL.Opt(HL.String)) << function(self) local request = self.m_pendingRequests[1] if request then return request.type end end MainHudActionQueueSystem._TryAddStartActionCor = HL.Method() << function(self) if self.m_isShowing or self.m_pendingRequests[1] == nil then logger.important(CS.Beyond.EnableLogType.MainHudActionQueue, "MainHudActionQueueSystem._TryAddStartActionCor Skipped") return end if self.m_tryStartActionCor then logger.important(CS.Beyond.EnableLogType.MainHudActionQueue, "MainHudActionQueueSystem._TryAddStartActionCor Duplicated") return end if self.m_lastFinishShowingFrameCount == Time.frameCount then logger.important(CS.Beyond.EnableLogType.MainHudActionQueue, "MainHudActionQueueSystem._TryAddStartActionCor 当帧刚结束,所以直接开始,确保播放之间是无缝的") self:_TryStartAction() elseif self.m_pendingRequests[1].startImmediately then logger.important(CS.Beyond.EnableLogType.MainHudActionQueue, "MainHudActionQueueSystem._TryAddStartActionCor startImmediately") self:_TryStartAction() else logger.important(CS.Beyond.EnableLogType.MainHudActionQueue, "MainHudActionQueueSystem._TryAddStartActionCor Succ") self.m_tryStartActionCor = self:_StartCoroutine(function() coroutine.step() coroutine.step() self:_TryStartAction() end) end end MainHudActionQueueSystem._TryStartAction = HL.Method() << function(self) logger.important(CS.Beyond.EnableLogType.MainHudActionQueue, "MainHudActionQueueSystem._TryStartAction") self.m_tryStartActionCor = self:_ClearCoroutine(self.m_tryStartActionCor) if GameInstance.player.guide.isInForceGuide and not Utils.isInBlackbox() then return end local nextRequestIndex = self:_GetNextRequestIndex(false) if not nextRequestIndex then return end if GameInstance.player.inventory.isProcessingRewardToastData then return end if UIManager:IsShow(PanelId.MissionCompletePop) then return end if LuaSystemManager.commonTaskTrackSystem:HasRequest() then return end if Utils.isInSettlementDefense() then return end self:_MoveRequestToTop(nextRequestIndex) self:_StartFirstAction() end MainHudActionQueueSystem.m_curPreloadId = HL.Field(HL.Any) MainHudActionQueueSystem.m_nextPreloadId = HL.Field(HL.Number) << 1 MainHudActionQueueSystem._StartFirstAction = HL.Method() << function(self) self.m_isShowing = true local request = self.m_pendingRequests[1] request.order = self.m_curShowingActionOrder local cfg = self.configs[request.type] if cfg.checkGuideOnStart and GameInstance.player.guide.isInForceGuide and not Utils.isInBlackbox() then logger.important(CS.Beyond.EnableLogType.MainHudActionQueue, "MainHudActionQueueSystem._StartFirstAction Failed Because Of Guide", request.type, request) self:Interrupt(false, true) return else logger.important(CS.Beyond.EnableLogType.MainHudActionQueue, "MainHudActionQueueSystem._StartFirstAction", request.type, request) end local isResume = request.haveBeenInterrupted if request.preloadPanelId then local pId = self.m_nextPreloadId self.m_nextPreloadId = self.m_nextPreloadId + 1 self.m_curPreloadId = pId UIManager:PreloadPanelAsset(request.preloadPanelId, "MainHudActionQueue", function() if self.m_curPreloadId == pId then request.action(isResume) if not cfg.needWait then self:OnOneMainHudActionFinish(request.type) end end end) else self.m_curPreloadId = nil request.action(isResume) if not cfg.needWait then self:OnOneMainHudActionFinish(request.type) end end end MainHudActionQueueSystem._CheckAllMainHudActionFinish = HL.Method() << function(self) if self.m_pendingRequests[1] ~= nil then return end Notify(MessageConst.ALL_MAIN_HUD_ACTION_FINISH) end MainHudActionQueueSystem.m_lastFinishShowingFrameCount = HL.Field(HL.Number) << -1 MainHudActionQueueSystem.OnOneMainHudActionFinish = HL.Method(HL.String) << function(self, type) logger.important(CS.Beyond.EnableLogType.MainHudActionQueue, "MainHudActionQueueSystem.OnOneMainHudActionFinish", type) if not self.m_isShowing then logger.error("OnOneMainHudActionFinish: 当前不在播放中", type) return end local request = self.m_pendingRequests[1] if request.type ~= type then logger.error("OnOneMainHudActionFinish: 并不是正在播放的类型", type, request) return end table.remove(self.m_pendingRequests, 1) if self.m_pendingRequests[1] then local nextRequestIndex = self:_GetNextRequestIndex(false) if nextRequestIndex then self:_MoveRequestToTop(nextRequestIndex) self:_StartFirstAction() return end end self.m_isShowing = false self.m_lastFinishShowingFrameCount = Time.frameCount logger.important(CS.Beyond.EnableLogType.MainHudActionQueue, "MainHudActionQueueSystem showing finished") self:_CheckAllMainHudActionFinish() CS.Beyond.Gameplay.Conditions.OnMainHudActionFinished.Trigger(false) end MainHudActionQueueSystem.Interrupt = HL.Method(HL.Opt(HL.Boolean, HL.Boolean)) << function(self, includeCinematic, forceNotFinish) if not includeCinematic then if not self.m_isShowing and self.m_tryStartActionCor == nil then return end local request = self.m_pendingRequests[1] if request and request.isCinematic then return end end self.m_tryStartActionCor = self:_ClearCoroutine(self.m_tryStartActionCor) if not self.m_isShowing then return end local request = self.m_pendingRequests[1] logger.important(CS.Beyond.EnableLogType.MainHudActionQueue, "MainHudActionQueueSystem.Interrupt", includeCinematic, request.type) self.m_curPreloadId = nil self.m_isShowing = false Notify(MessageConst.INTERRUPT_MAIN_HUD_ACTION_QUEUE) if request.finishWhenInterrupt and not forceNotFinish then table.remove(self.m_pendingRequests, 1) self:_CheckAllMainHudActionFinish() else request.haveBeenInterrupted = true end CS.Beyond.Gameplay.Conditions.OnMainHudActionFinished.Trigger(true) end MainHudActionQueueSystem._DropRequestWhenChangeScene = HL.Method() << function(self) local newRequests = {} for _, request in ipairs(self.m_pendingRequests) do if not request.dropWhenChangeScene then table.insert(newRequests, request) else logger.important(CS.Beyond.EnableLogType.MainHudActionQueue, "MainHudActionQueueSystem._DropRequestWhenChangeScene", request.name) if request.onDrop then request.onDrop() end end end self.m_pendingRequests = newRequests end MainHudActionQueueSystem.RemoveActionsOfType = HL.Method(HL.String) << function(self, actionType) logger.important(CS.Beyond.EnableLogType.MainHudActionQueue, "MainHudActionQueueSystem.RemoveActionsOfType", actionType) local needRestart = false local requestCnt = #self.m_pendingRequests if requestCnt == 0 then return end if self.m_isShowing then local currentAction = self.m_pendingRequests[1] if currentAction.type == actionType then if currentAction.finishWhenInterrupt then requestCnt = requestCnt - 1 end self:Interrupt() needRestart = true end end local reservedCnt = 0 local newPendingRequests = {} for i = 1, requestCnt do local request = self.m_pendingRequests[i] if request.type == actionType then if request.onDrop then request.onDrop() end else reservedCnt = reservedCnt + 1 newPendingRequests[reservedCnt] = request end end self.m_pendingRequests = newPendingRequests if reservedCnt == 0 then self:Interrupt() elseif needRestart then self:_TryStartAction() end end MainHudActionQueueSystem.HasRequest = HL.Method(HL.String).Return(HL.Boolean) << function(self, actionType) for _, request in ipairs(self.m_pendingRequests) do if request.type == actionType then return true end end return false end MainHudActionQueueSystem.m_playIgnoreMainHudActionTypes = HL.Field(HL.Table) MainHudActionQueueSystem.ToggleActionPlayIgnoreMainHud = HL.Method(HL.String, HL.Boolean) << function(self, actionType, ignoreMainHud) local curIsActive = self.m_playIgnoreMainHudActionTypes[actionType] == true if curIsActive == ignoreMainHud then return end logger.important(CS.Beyond.EnableLogType.MainHudActionQueue, "MainHudActionQueueSystem.ToggleActionPlayIgnoreMainHud", actionType, ignoreMainHud) if ignoreMainHud then self.m_playIgnoreMainHudActionTypes[actionType] = true else self.m_playIgnoreMainHudActionTypes[actionType] = nil end if ignoreMainHud and not self.m_isShowing and self.m_pendingRequests[1] then self:_TryStartAction() end end MainHudActionQueueSystem._GetNextRequestIndex = HL.Method(HL.Boolean).Return(HL.Opt(HL.Number)) << function(self, forceSort) self:_SortRequest(forceSort) if GameWorld.worldInfo.inMainHud and not CameraManager:IsCommonTempControllerActive() then return 1 else if next(self.m_playIgnoreMainHudActionTypes) then for k, v in ipairs(self.m_pendingRequests) do if self.m_playIgnoreMainHudActionTypes[v.type] then return k end end end end end MainHudActionQueueSystem._MoveRequestToTop = HL.Method(HL.Opt(HL.Number)) << function(self, index) if index and index ~= 1 then local request = self.m_pendingRequests[index] for k = index, 2, -1 do self.m_pendingRequests[k] = self.m_pendingRequests[k - 1] end self.m_pendingRequests[1] = request end end MainHudActionQueueSystem.ManuallyDropAllRequests = HL.Method() << function(self) if self:IsShowing() then logger.error("MainHudActionQueueSystem.ManuallyDropAllRequests FAIL because: IsShowing") return end for _, request in ipairs(self.m_pendingRequests) do logger.important(CS.Beyond.EnableLogType.MainHudActionQueue, "MainHudActionQueueSystem.ManuallyDropAllRequests", request.name) if request.onDrop then request.onDrop() end end self.m_pendingRequests = {} end HL.Commit(MainHudActionQueueSystem) return MainHudActionQueueSystem