---@class UGCGameState_C:BP_UGCGameState_C ---@field WrapperClass UClass ---@field ShootWeaponClass UClass ---@field ActorClass UClass --Edit Below-- UGCGameSystem.UGCRequire('Script.Common.ue_enum_custom') require('Script.Global.Global') local UGCGameState = {}; --- 服务器时间 UGCGameState.ServerTime = 0; --- 服务器时间 - 客户端时间的差值;如果想在服务器求客户端时间:当前时间 - 该值; UGCGameState.ServerTimeDiff = 0; --- 地图数据 UGCGameState.MapData = { SelectMapIndex = {}; }; ---@type table> 玩家 Key 列表 UGCGameState.PlayerList = {}; --- 游戏进程数据 UGCGameState.CountDownTime = 0; UGCGameState.AccountTable = {}; UGCGameState.ArchiveTable = {}; UGCGameState.PlayerKDAs = {}; function UGCGameState:ReceiveBeginPlay() --- 初始化 GlobalInit.InitGlobalVar(); if IsServer then self.ServerTime = 1; -- 设置好 DOREPONCE(self, "ServerTime") end self.bIsOpenShovelingAbility = DefaultSettings.OpenShovel; if MiniGameManager == nil then MiniGameManager = require("Script.Blueprint.Mini.MiniGameManager") end if GlobalBeginTool then GlobalBeginTool:ReceiveBeginPlay(); end table.func(MiniGameManager, "ReceiveBeginPlay", self); end function UGCGameState:ReceiveTick(DeltaTime) if GlobalTickTool then GlobalTickTool:ReceiveTick(DeltaTime, self:GetServerTime()); end end function UGCGameState:ReceiveEndPlay() DefaultSettings = nil; EventTypes = nil; GameState = nil; LocalPlayerKey = nil; LocalPlayerController = nil; UnableTable(); UnableTool(); UnableWidgetManager(); UGCGameSystem.GameState = nil; end function UGCGameState:GetAvailableServerRPCs() return "LoadMap" , "CheckServerTime" , "SelectLevelMode" , "SelectMap" , "MiniGameReceiveRPC" end function UGCGameState:GetReplicatedProperties() return { "MapData", "Lazy" } , { "CountDownTime", "Lazy" } , { "ServerTime", "Lazy" } , { "LoadMapName", "Lazy" } , { "CurrMiniType", "Lazy" } , { "MiniInfo", "Lazy" } , { "ArchiveTable", "Lazy" } , { "AccountTable", "Lazy" } , { "PlayerKDAs", "Lazy" } end ----------------------------------------- 玩家属性 ----------------------------------------- ---@param InPlayerKey PlayerKey ---@param InArchiveData table ---@param InAccountData table function UGCGameState:HandlePlayerDatas(InPlayerKey, InArchiveData, InAccountData, IgnoreList) InArchiveData.GameTimes = (InArchiveData.GameTimes or 0) + 1; -- 又多玩了一局游戏 self.ArchiveTable[InPlayerKey] = TableHelper.DeepCopy(InArchiveData); if ArchiveTable[InPlayerKey] == nil then UGCLogSystem.Log("[UGCGameState:HandlePlayerDatas] 执行") ArchiveTable[InPlayerKey] = InArchiveData; end for i, v in pairs(IgnoreList) do self.ArchiveTable[InPlayerKey][v] = nil; end self.AccountTable[InPlayerKey] = InAccountData; DOREPONCE(self, "ArchiveTable"); DOREPONCE(self, "AccountTable"); end function UGCGameState:OnRep_PlayerKDAs() if table.isEmpty(self.PlayerKDAs) then return end UGCEventSystem.SendEvent(EventTypes.AllPlayerKDAChange, self:HandleAllKDAs()); end function UGCGameState:HandleAllKDAs() if table.isEmpty(self.PlayerKDAs) then return ; end local AllKDAs = {}; local Max = 0; for PlayerKey, KD in pairs(self.PlayerKDAs) do local Kill = KD[1] or 0; local Dead = KD[2] or 0; table.insert(AllKDAs, { Kill = Kill, Dead = Dead, IsSelf = LocalPlayerKey == PlayerKey, PlayerKey = PlayerKey, }); if PlayerKey == LocalPlayerKey then LocalKillNum = Kill; end if Max < Kill then Max = Kill; end end MaxKillNum = Max; table.sort(AllKDAs, function(a, b) if a.Kill == b.Kill then if a.Dead == b.Dead then return a.IsSelf; end return a.Dead < b.Dead; end return a.Kill > b.Kill; end); local No, Kill = -1, 1; for i = 1, #AllKDAs do local Item = AllKDAs[i]; if Kill == Item.Kill then else No = i; Kill = Item.Kill; end AllKDAs[i].No = No; end UGCLogSystem.LogTree(string.format("[UGCGameState:OnRep_PlayerKDAs] AllKDAs ="), AllKDAs) return AllKDAs; end function UGCGameState:OnRep_ArchiveTable() ArchiveTable = self.ArchiveTable; if table.isEmpty(ArchiveTable) then return ; end if LocalPlayerKey == nil then return ; end if ArchiveTable[LocalPlayerKey] == nil then return ; end --local GameTimes = ArchiveTable[LocalPlayerKey].GameTimes --if GameTimes ~= nil and GameTimes <= DefaultSettings.ShowFaceNoticeGameTimes then -- if not DefaultSettings.EnableTest then -- WidgetManager:ShowPanel(WidgetConfig.EUIType.FaceNotice, false); -- end --end end function UGCGameState:OnRep_AccountTable() AccountTable = self.AccountTable; end ----------------------------------------- 地图加载 ----------------------------------------- UGCGameState.PlayerLevelModes = {}; function UGCGameState:SelectLevelMode(InLevelMode, InPlayerKey) if IsServer then self.PlayerLevelModes[InPlayerKey] = InLevelMode; else end end --- 已经加载了的地图名称 UGCGameState.LoadMapName = {}; --- 加载地图(服务器/客户端都可以执行) ---@param MapNameList table 地图索引 function UGCGameState:LoadMap(MapNameList) -- 找到对应地图,然后进行加载 if not table.isEmpty(self.LoadMapName) then -- 先卸载,再安装 local List = {}; for i, v in pairs(self.LoadMapName) do table.insert(List, v.Name); end self:UnloadMap(List); end for i, v in pairs(MapNameList) do table.insert(self.LoadMapName, { Name = v, Load = false, }); end LevelStreamTool.LoadStreamLevels(MapNameList, { Object = self, Func = self.LoadMapFinish }, false); DOREPONCE(self, "LoadMapName") end function UGCGameState:OnRep_LoadMapName() if table.isEmpty(self.LoadMapName) then return ; end UGCLogSystem.LogTree(string.format("[UGCGameState:OnRep_LoadMapName] self.LoadMapName ="), self.LoadMapName); local LoadedMaps = {}; for i, v in pairs(self.LoadMapName) do if v.Load then table.insert(LoadedMaps, v.Name); end end if table.isEmpty(LoadedMaps) then return ; end UGCEventSystem.SendEvent(EventTypes.OnMapLoaded, LoadedMaps); MiniGameManager:OnMapLoadComplete() -- 加载小地图 UGCLogSystem.Log("[UGCGameState:OnRep_LoadMapName] 开始加载小地图") for i, v in pairs(LevelTable.LevelInfo) do if v.MapName == self.LoadMapName[1].Name then UGCLogSystem.Log("[UGCGameState:OnRep_LoadMapName] 执行") self:LoadMinimap(i); break ; end end end --- 随机加载地图 ---@param function UGCGameState:LoadRandomMap(InIndex) if IsServer then self:UnloadMap(); -- 判断当前是不是随即关卡 UGCLogSystem.LogTree(string.format("[UGCGameState:LoadMap] LevelTable.LevelInfo ="), LevelTable.LevelInfo) if InIndex == nil or InIndex == LevelTable.ELevelType.Random then self.MapData.SelectMapIndex = LevelTable.GetRandomLevel(true, 1); else self.MapData.SelectMapIndex = { InIndex }; end local MapNames = {}; for i, v in pairs(self.MapData.SelectMapIndex) do table.insert(MapNames, LevelTable.LevelInfo[v].MapName); end LevelStreamTool.LoadStreamLevels(MapNames, { Object = self, Func = self.LoadMapFinish }, false); DOREPONCE(self, "MapData"); else -- 发送 RPC 到服务器 UnrealNetwork.CallUnrealRPC(LocalPlayerController, self, "LoadMap", InIndex); end end --- 卸载地图 function UGCGameState:UnloadMap() if table.isEmpty(self.MapData.SelectMapIndex) then local MapNames = {}; for i, v in pairs(self.MapData.SelectMapIndex) do table.insert(MapNames, LevelTable.LevelInfo[v].MapName) end LevelStreamTool.UnLoadStreamLevels(MapNames, { Object = self, Func = self.UnLoadMapFinish }, false); self.MapData.SelectMapIndex = {}; -- 重置 ClientAlready self.bInitOnce = false; end end function UGCGameState:OnRep_MapData() if table.isEmpty(self.MapData.SelectMapIndex) then return end --self:LoadMinimap(self.MapData.SelectMapIndex); end --- 地图加载完成 function UGCGameState:LoadMapFinish() UGCLogSystem.Log("[UGCGameState:LoadMapFinish] 地图加载完成"); -- 写一个通知 UGCEventSystem.SendEvent(EventTypes.MapLoad, self.MapData.SelectMapIndex, true); self:OnMapLoaded(); end function UGCGameState:UnLoadMapFinish() UGCLogSystem.Log("[UGCGameState:UnLoadMapFinish] 地图卸载成功") MiniGameManager:OnMapUnLoadedComplete(); end --- 加载小地图 function UGCGameState:LoadMinimap(InType) UGCLogSystem.Log("[UGCGameState:LoadMinimap] 开始加载 %s 的小地图", LevelTable.LevelInfo[InType].MapName); local MinimapInfo = LevelTable.LevelInfo[InType].MiniMapInfo; if not table.isEmpty(MinimapInfo) then UGCLogSystem.Log("[UGCGameState:LoadMinimap] 开始添加") UGCWidgetManagerSystem.ChangeMap(MinimapInfo.MapPath, MinimapInfo.MapCentre, MinimapInfo.MapSize, MinimapInfo.MapScale); end end function UGCGameState:OnMapLoaded() --- 加载地图上的东西 UGCLogSystem.Log("[UGCGameState:OnMapLoaded] 执行") UGCEventSystem.SetTimer(self, function() self:PostOnMapLoaded(); end, 2); -- 检测一下是否有默认地图 MiniGameManager:OnMapLoadComplete(); for i, v in pairs(self.LoadMapName) do v.Load = true; end DOREPONCE(self, "LoadMapName"); end ---@type table 玩家中心点 UGCGameState.CenterActors = {}; ---@return table 玩家中心点 function UGCGameState:FindCenters() if table.isEmpty(self.CenterActors) then UE.FindActorsByClass(ObjectPath.BP_ActorStart, self.CenterActors, function(InIndex, InActor) return InActor:ActorHasTag("Poison"); end); end return self.CenterActors; end function UGCGameState:PostOnMapLoaded() end ----------------------------------------- 游戏进程 ----------------------------------------- ---@param InTime int32 初始化倒计时 function UGCGameState:OnGameActive(InTime) if MiniGameManager then table.func(MiniGameManager, "OnGameActive", InTime); end end function UGCGameState:OnBeforeGameStart(InTime) if MiniGameManager then table.func(MiniGameManager, "OnBeforeGameStart", InTime); end end --- 游戏结束 function UGCGameState:OnGameEnded() -- 打开结算界面 if IsServer then for i, v in pairs(UGCGameSystem.GetAllPlayerController(false)) do local PlayerKey = v.PlayerKey; -- 保存一下存档 UGCPlayerStateSystem.SavePlayerArchiveData(AccountTable[PlayerKey].UID, ArchiveTable[PlayerKey]); UGCGameSystem.SendPlayerSettlement(PlayerKey); end UGCLogSystem.Log("[UGCGameState:OnGameEnded] 执行") for i, v in pairs(UGCGameSystem.GetAllPlayerPawn()) do UGCPawnSystem.LeavePawnState(v, EPawnState.Move); v:K2_DestroyActor() end self:ShowUIByType(WidgetConfig.EUIType.GameEnd, true) end end --- 倒计时 function UGCGameState:OnGameProgress_Tick(InTime) self.CountDownTime = InTime; DOREPONCE(self, "CountDownTime"); end function UGCGameState:OnRep_CountDownTime() UGCEventSystem.SendEvent(EventTypes.CountDownTimeChange, self.CountDownTime); -- 执行客户端调用 if MiniGameManager then MiniGameManager:OnMiniGameTimeCount(self.CountDownTime); end end --- 当前游戏时间,以服务器为基准(因为服务器不会改变) ---客户端准备好之后会发送一个 RPC 过来,之后就是 function UGCGameState:CheckServerTime(InPlayerKey, InTime) if IsServer then local PC = UGCGameSystem.GetPlayerControllerByPlayerKey(InPlayerKey) UnrealNetwork.CallUnrealRPC(PC, self, "CheckClientTime", UE.GetCurrentTime()); self:OnClientAlready(); else UnrealNetwork.CallUnrealRPC(LocalPlayerController, self, "CheckServerTime", InPlayerKey, InTime); end end function UGCGameState:OnRep_ServerTime() -- 接收到之后就立刻发送 RPC 到服务器,或者也可以等一会 if self.ServerTime == 1 then if LocalPlayerKey == nil or LocalPlayerController == nil then UGCEventSystem.SetTimer(self, function() self:CheckServerTime(LocalPlayerKey, UE.GetCurrentTime()) end, 1.); return ; end self:CheckServerTime(LocalPlayerKey, UE.GetCurrentTime()) elseif self.ServerTime == 0 then else local Diff = self.ServerTime - UE.GetCurrentTime(); UGCLogSystem.Log("[UGCGameState:CheckClientTime] Diff = %f", Diff) self:OnClientAlready(); end end function UGCGameState:CheckClientTime(InTime) self.ServerTimeDiff = InTime - UE.GetCurrentTime(); UGCLogSystem.Log("[UGCGameState:CheckClientTime] self.ServerTimeDiff = %f", self.ServerTimeDiff); self:OnClientAlready(); end UGCGameState.bInitOnce = false; --- 是否有毒圈:0表示未知,1表示存在,-1 表示不存在 UGCGameState.HasPoison = 0; function UGCGameState:OnClientAlready() if self.bInitOnce then return end UGCEventSystem.SendEvent(EventTypes.ClientAlready) local ServerTime = self:GetServerTime() UGCLogSystem.Log("[UGCGameState:OnClientAlready] 执行 ServerTime = %f", ServerTime); if IsServer then UGCLogSystem.Log("[UGCGameState:OnClientAlready] 执行") self:OnGameActive(-1) end if GlobalBeginTool then GlobalBeginTool:ReceiveClientAlready() GlobalBeginTool = nil; end if MiniGameManager then table.func(MiniGameManager, "OnClientAlready"); end self.bInitOnce = true; end --- S & C : 获取服务器时间 ---@return float|nil function UGCGameState:GetServerTime() if IsServer then return UE.GetCurrentTime(); else if self.ServerTimeDiff == 0 then return nil; else return self.ServerTimeDiff + UE.GetCurrentTime(); end end end --- 获取客户端时间 function UGCGameState:GetClientTime(InPlayerKey) if IsClient then return UE.GetCurrentTime(); else end end function UGCGameState:ClearAll() UnableTable() end --- 提前重置操作 function UGCGameState:BeforeReset(InTime) for i, v in pairs(UGCGameSystem.GetAllPlayerController(false)) do local PlayerKey = v.PlayerKey; local Pawn = UGCGameSystem.GetPlayerPawnByPlayerKey(PlayerKey); if UE.IsValidPawn(Pawn) then Pawn:K2_DestroyActor(); end UE.RespawnPlayer(PlayerKey, InTime); end self:ResetKill(); end function UGCGameState:ResetKill() -- 重置其他的 for i, v in pairs(UGCGameSystem.GetAllPlayerController(false)) do v:ResetGame(); end end --- 重置游戏 function UGCGameState:ResetGame() if IsServer then UnrealNetwork.CallUnrealRPC_Multicast(self, "ResetGame"); -- 重置击杀数 for i, v in pairs(UGCGameSystem.GetAllPlayerState(false)) do v:ResetKillNum(); end else WidgetManager:GetPanel(WidgetConfig.EUIType.Main):OnGameStart(); end end ----------------------------------------- 界面 UI ----------------------------------------- --- 显示 UI ---@param InUIType EUIType ---@param IsShow boolean function UGCGameState:ShowUIByType(InUIType, IsShow, ...) if IsServer then UnrealNetwork.CallUnrealRPC_Multicast(self, "ShowUIByType", InUIType, IsShow, ...); else if IsShow then WidgetManager:ShowPanel(InUIType, false, ...); else WidgetManager:ClosePanel(InUIType); end end end ----------------------------------------- 场景物品 ----------------------------------------- -- 清除场景上的 Wrappers function UGCGameState:ClearWrappers() local InTable = {}; UE.FindActorsByClass(self:GetWrapperClass(), InTable, function(InIndex, InActor) return InActor:GetOwner() == nil; end); for i, v in pairs(InTable) do v:K2_DestroyActor(); end end ----------------------------------------- 武器 ----------------------------------------- ---@return UClass function UGCGameState:GetWrapperClass() return self.WrapperClass; end ----------------------------------------- 载具 ----------------------------------------- --- 清空地面上的载具 function UGCGameState:ClearVehicles() if table.isEmpty(self.Vehicles) then self:FindVehicles(); end for i, v in pairs(self.Vehicles) do v:K2_DestroyActor(); end self.Vehicles = {}; end --- 加载地面上的载具,通过 ActorStart 进行查找 function UGCGameState:LoadVehicles() self:ClearVehicles(); local AllStarts = {}; UE.FindActorsByClass(ObjectPath.BP_ActorStart, AllStarts, function(InIndex, InActor) table.insert(self.Vehicles, UGCVehicleSystem.SpawnVehicleNew(VehicleTable.Car[InActor.Index].Path, InActor:K2_GetActorLocation(), InActor:K2_GetActorRotation(), false, false)); return true; end) end -- 查找载具 function UGCGameState:FindVehicles() if table.isEmpty(self.Vehicles) then self:LoadVehicles(); end return self.Vehicles; end ----------------------------------------- FUNCTIONAL ----------------------------------------- --- 发送自定义事件 function UGCGameState:SendEvent(IsSend, InPlayerKey, InEvent, ...) if IsServer then if IsSend then if InPlayerKey == nil then UnrealNetwork.CallUnrealRPC_Multicast(self, "SendEvent", false, nil, InEvent, ...); else UnrealNetwork.CallUnrealRPC(UGCGameSystem.GetPlayerControllerByPlayerKey(InPlayerKey), UGCGameSystem.GameState, "SendEvent", false, InPlayerKey, InEvent, ...); end else UGCEventSystem.SendEvent(InEvent, InPlayerKey, ...) end else -- 从客户端发送出去 if IsSend then UnrealNetwork.CallUnrealRPC(DefaultSettings.LocalPlayerController, UGCGameSystem.GameState, "SendEvent", false, DefaultSettings.LocalPlayerKey, InEvent, ...); else UGCEventSystem.SendEvent(InEvent, InPlayerKey, ...); end end end ----------------------------------------- MINI ----------------------------------------- --- 当前选择的 MiniType UGCGameState.CurrMiniType = -1; ---@param InMiniType int32 function UGCGameState:SelectMiniType(InMiniType) self.CurrMiniType = InMiniType; DOREPONCE(self, "CurrMiniType"); end function UGCGameState:OnRep_CurrMiniType() if self.CurrMiniType < 0 then return ; end if MiniGameManager ~= nil then MiniGameManager:LoadMiniGame(self.CurrMiniType); end end --- 发送 MiniGame RPC ---@param InFuncName string 函数名称 ---@vararg any function UGCGameState:SendMiniGameRPC(InFuncName, ...) if IsServer then UnrealNetwork.CallUnrealRPC_Multicast(self, "MiniGameReceiveRPC", InFuncName, true, ...); else UnrealNetwork.CallUnrealRPC(LocalPlayerController, self, "MiniGameReceiveRPC", InFuncName, false, ...); end end --- Mini Game 接收到 RPC ---@param InFuncName string 函数名称 ---@param SendFromServer bool 是否发自服务器 function UGCGameState:MiniGameReceiveRPC(InFuncName, SendFromServer, ...) if table.hasFunc(MiniGameManager, InFuncName) then table.func(MiniGameManager, InFuncName, ...) else if MiniGameManager ~= nil and MiniGameManager.CurrMiniMode then table.func(MiniGameManager.CurrMiniMode, InFuncName, ...) end end end --- 小游戏需要同步的数据 UGCGameState.MiniInfo = {}; function UGCGameState:SetMiniInfo(InMiniInfo) self.MiniInfo = InMiniInfo; DOREPONCE(self, "MiniInfo"); end function UGCGameState:OnRep_MiniInfo(InOld) if table.isEmpty(self.MiniInfo) then return ; end -- 发送出去 UGCLogSystem.LogTree(string.format("[UGCGameState:OnRep_MiniInfo] MiniInfo"), self.MiniInfo) if MiniGameManager then MiniGameManager.MiniInfo = self.MiniInfo; MiniGameManager:OnRep_MiniInfo(InOld); end end --- 获取 MiniGame 的数据 function UGCGameState:GetMiniInfo(Name) if MiniGameManager then return table.func(MiniGameManager, "GetMiniInfo", Name); end return nil; end --- 获取配置 function UGCGameState:GetMiniConfig(Name) if MiniGameManager then return table.func(MiniGameManager, "GetMiniInfo", Name); end return nil; end function UGCGameState:MiniFunc(InFuncName, ...) UGCLogSystem.Log("[UGCGameState:MiniFunc] FuncName = %s", InFuncName) if MiniGameManager then UGCLogSystem.Log("[UGCGameState:MiniFunc] 执行") return table.func(MiniGameManager, "DoMiniFunc", InFuncName, ...) end end ----------------------------------------- 自定义函数 ----------------------------------------- ---显示TipsUI function UGCGameState:ShowTipsUI(InStr, ...) if IsServer then UnrealNetwork.CallUnrealRPC_Multicast_Unreliable(self, "ShowTipsUI", string.format(InStr, ...)); else UGCWidgetManagerSystem.ShowTipsUI(string.format(InStr, ...)); end end function UGCGameState:CustomLog(Color, InStr, ...) if DefaultSettings.EnableTest then if IsServer then -- 组装一下 if self.CustomLogList == nil then self.CustomLogList = {}; GlobalTickTool:AddTick(self, function(o, dt, st) if table.isEmpty(o.CustomLogList) then return ; end -- 发送 RPC UnrealNetwork.CallUnrealRPC_Multicast(self, "CustomLog", o.CustomLogList); o.CustomLogList = {}; end, 1); end -- 开始添加,然后启动函数 local Item = { c = Color, s = string.format('[Server]' .. string.format('[%0.1f]', UE.GetServerTime()) .. InStr:format(...)), }; table.insert(self.CustomLogList, Item); else if type(Color) == 'table' then WidgetManager:GetPanel(WidgetConfig.EUIType.Main, function(Widget) Widget:AddLogItems(Color); end); else local Params = { ... }; WidgetManager:GetPanel(WidgetConfig.EUIType.Main, function(Widget) Widget:AddLogItem(Color, InStr, table.unpackTable(Params)); end); end end end end return UGCGameState;