---@type FMiniGameManager local MiniGameManager = {}; ---@private string 这是玩家选择的地图,按从前往后开始游玩 MiniGameManager.ScriptPath = "Script.Blueprint.Mini.Script.Mini_"; ---@type table 当前所有玩家选择的地图 MiniGameManager.SelectMaps = {}; ---@type FMiniGameMapIndex 当前选择的地图 MiniGameManager.CurrSelectMap = nil; ---@type UGCGameState_C 默认是 GameSate,如果不是可以在具体实例里面实现对应方法 MiniGameManager.Owner = nil; ---@type table 游戏配置,对应着 MiniGameConfig[self.CurrSelectMap] MiniGameManager.GameConfig = nil; ---@type MiniGameMode 当前小玩法的具体实现,统一入口出口管理 MiniGameManager.CurrMiniMode = nil; ---@type int32 目标小游戏数量,低于这个数量会自动添加到这个数量 MiniGameManager.TargetMiniGameCount = 1; MiniGameManager.State = 0; ---@type table 玩家数据 MiniGameManager.PlayerDatas = {}; ---@type table 小游戏需要同步的数据,会自动执行 OnRep_xxx 函数 MiniGameManager.MiniInfo = {}; MiniGameManager.PlayerKDAs = {}; MiniGameManager.PlayerDamages = {}; MiniGameManager.PlayerAssistTimes = {}; ----------------------------------- 主要函数 ----------------------------------- --- 这是游戏启动,做一些初始化的操作即可 function MiniGameManager:ReceiveBeginPlay(InOwner) UGCLogSystem.Log("[MiniGameManager:ReceiveBeginPlay] 执行成功 self = %s", tostring(self)); self.Owner = InOwner; self.GameConfig = require('Script.Blueprint.Mini.MiniGameConfig') GlobalTickTool:AddTick(self, self.OnTick); UGCEventSystem.AddListener(EventTypes.ClientAlready, self.OnClientAlready, self) if IsServer then self:SetState(MiniGameState.SELECT_GAMES); self:HandleAddTick(self:GetCurrStateTotalTime(), self.OnGameStart); else -- 执行客户端逻辑 self:MakeCachedMiniInfo(); end -- 组装一下具体函数 self:MakeRepProps(); end function MiniGameManager:OnTick(InDeltaTime, InServerTime) end function MiniGameManager:MakeRepProps() local Func = function(...) return { ... }; end local Props = Func(self:GetReplicatedProperties()); if self.Props == nil then self.Props = {}; end for i, v in pairs(Props) do self.Props[v] = true; end UGCLogSystem.LogTree(string.format("[MiniGameManager:MakeRepProps] self.Props ="), self.Props); end function MiniGameManager:IsRepProp(InName) return self.Props[InName] == true; end --- 本身需要同步的数据放进来 function MiniGameManager:GetReplicatedProperties() return "State" , "SelectMaps" , "CurrSelectMap" , "PlayerKDAs" , "PlayerDamages" end --- 客户端准备好了,此时开始有 ServerTime function MiniGameManager:OnClientAlready() -- 显示 if IsClient then UGCLogSystem.Log("[MiniGameManager:OnGameBegin] 显示 UI") end end --- 可以改变任意时刻的时间 function MiniGameManager:ChangeTime(InTime) GlobalTickTool:UpdateInternalCount(self, InTime); end --- 准备结束 function MiniGameManager:OnPrepareEnd() if IsServer then self:SetMiniInfo(); end self:DoMiniFunc("OnPrepareEnd") self:OnMiniRoundPrepare(); end function MiniGameManager:SendRPC(InRPCName, ...) UGCLogSystem.Log("[MiniGameManager:SendRPC] InRPCName = %s", InRPCName); self.Owner:SendMiniGameRPC(InRPCName, ...); end --- 当游戏激活之时 function MiniGameManager:OnGameActive() -- 这个函数几乎是紧挨着 ReceiveBeginPlay 之后执行 end --- 在游戏进行之前执行 ---@param InTime int32 从游戏开始到游戏正式开始的倒计时 function MiniGameManager:OnBeforeGameStart(InTime) UGCLogSystem.Log("[MiniGameManager:OnBeforeGameStart] InTime = %s", tostring(InTime)); --if InTime == 3 then -- -- 玩家死亡重生 -- GameState:BeforeReset(2); --end end --- 游戏正式开始 function MiniGameManager:OnGameStart() UGCLogSystem.Log("[MiniGameManager:OnGameStart] 执行") if IsServer then -- 检查当前选择的地图是否小于对应数量,如果小于,那么就随机选择为选择的 -- 获取选择了的 if table.getCount(MiniGameConfig) == 1 then for i, v in pairs(MiniGameConfig) do table.insert(self.SelectMaps, i); end else local SortTable = self.Owner:SortMapData(); for i, v in pairs(SortTable) do table.insert(self.SelectMaps, v.Index); end end local NeedCount = self.TargetMiniGameCount - table.getCount(self.SelectMaps); if NeedCount > 0 then for i = 1, NeedCount do table.insert(self.SelectMaps, math.random(table.getCount(MiniGameConfig))) end end UGCLogSystem.LogTree(string.format("[MiniGameManager:OnGameStart] self.SelectMaps ="), self.SelectMaps) -- 表示如果第一个加载不成,那么就依次完后加载,直到全部加载都不行 local bSuccess = false; repeat bSuccess = self:LoadMiniGame() until bSuccess or table.isEmpty(self.SelectMaps); if not bSuccess then self:OnGameEnd(); end else WidgetManager:ClosePanel(WidgetConfig.EUIType.SelectMap); WidgetManager:ShowPanel(WidgetConfig.EUIType.Main, false); end end function MiniGameManager:OnGameEnd() UGCLogSystem.Log("[MiniGameManager:OnGameEnd] 游戏结束") if IsServer then self:SetState(MiniGameState.ENDED); GameState:OnGameEnded(MiniGameTimes.GameEnd); UGCLogSystem.Log("[MiniGameManager:OnGameEnd] GameEnd Time = %f", MiniGameTimes.GameEnd) UGCEventSystem.SetTimer(self.Owner, function() LuaQuickFireEvent("CloseServer", self.Owner) end, MiniGameTimes.GameEnd + 2); else -- 显示结算界面 WidgetManager:ShowPanel(WidgetConfig.EUIType.GameEnd, false); end end ---@param InMiniType MiniGameMapType Mini 游戏类型,只会在客户端存在 function MiniGameManager:LoadMiniGame(InMiniType) if IsServer then self:SetState(MiniGameState.MINI_PREPARE); self:AllPlayerDead(); UGCLogSystem.Log("[MiniGameManager:LoadMiniGame] 执行") if table.isEmpty(self.SelectMaps) then return false; end UGCLogSystem.Log("[MiniGameManager:LoadMiniGame] 开始执行流程") self.CurrSelectMap = self.SelectMaps[1]; if self.CurrSelectMap == nil then return false; end table.remove(self.SelectMaps, 1); -- 加载 CurrMiniMode UGCLogSystem.Log("[MiniGameManager:LoadMiniGame] 加载 Mini Mode") if not self:DoMiniFunc("Init", self) then return false; end self:LoadMap(self.CurrSelectMap); -- 发送到客户端表示当前已经同步了 self.Owner:SelectMiniType(self.CurrSelectMap); -- 这样写是确保一定可以执行成功 UGCLogSystem.Log("[MiniGameManager:LoadMiniGame] Mini Mode 加载成功") self:CurrMiniModeInit(); if not table.hasFunc(self.CurrMiniMode, "DOREPONCE") then self.CurrMiniMode["DOREPONCE"] = function(o, Name) -- 检查是否有区别,如果有区别才改变 o.Owner:SetGameInfo(Name, o[Name]); end end if self.CurrMiniMode.RoundTimes == nil then self.CurrMiniMode.RoundTimes = 1; end self:SetRoundTimes(self.CurrMiniMode.RoundTimes); UGCLogSystem.Log("[MiniGameManager:LoadMiniGame] Time = %s", tostring(Time)) self:HandleAddTick(self:GetCurrStateTotalTime(), self.OnPrepareEnd); UGCLogSystem.Log("[MiniGameManager:LoadMiniGame] 全部加载完成") return true; else UGCLogSystem.Log("[MiniGameManager:LoadMiniGame] 客户端加载") self.CurrSelectMap = InMiniType; self:DoMiniFunc("Init", self); self:CurrMiniModeInit(); end end --- 服务器客户端初始化的共同操作 function MiniGameManager:CurrMiniModeInit() --- 默认设置一些变量供使用或者驱动 self.CurrMiniMode.Owner = self; self.CurrMiniMode.MapIndex = self.CurrSelectMap; self.CurrMiniMode.CreateTime = UE.GetServerTime(); self.CurrMiniMode.bGameEnd = false; self.CurrMiniMode.bRoundEnd = false; self.CurrMiniMode.RoundTimes = self.GameConfig[self.CurrSelectMap].RoundTimes; -- 当前回合数 self.CurrMiniMode.TotalRoundTimes = self.GameConfig[self.CurrSelectMap].RoundTimes; -- 总回合数 self.CurrMiniMode.bCanRespawn = self.GameConfig[self.CurrSelectMap].bCanRespawn; -- 是否可以重生 -- 玩家击杀数据 for i, v in pairs(self.MiniInfo) do if type(v) == 'table' then self.CurrMiniMode[i] = TableHelper.DeepCopyTable(v); else self.CurrMiniMode[i] = v; end end UGCLogSystem.Log("[MiniGameManager:CurrMiniModeInit] 开始加载地图") -- 加载 Tick GlobalTickTool:AddTick(self.CurrMiniMode, self.CurrMiniMode.OnTick, nil, function(o) return not (o.bEnableTick == false); end); -- 进入准备 self:DoMiniFunc("OnPrepare"); end --- 处理 Tick function MiniGameManager:HandleAddTick(InTime, lf, InFunc, ...) local Time = math.abs(InTime); GlobalTickTool:AddInternalCount(self, function(o, dt, st, t) -- 如果 InTime < 0 if InTime > 0 then t = InTime - t; end o:OnMiniGameTimeCount(t, Time); if InFunc then InFunc(o, dt, st, t); end end, 1, Time, lf, ...); end function MiniGameManager:UnLoadMiniGame() -- 先取消上一个的 if IsServer then if self.CurrMiniMode ~= nil then GlobalTickTool:RemoveTick(self.CurrMiniMode); -- 然后赶紧除去地图并且加载 self:UnLoadMap(); end end end function MiniGameManager:OnMiniGameTimeCount(InVal, InTotal) if IsServer then self:DoMiniFunc("OnTimeCountDown", InVal, InTotal); InVal = math.floor(InVal); --- 执行 self.Owner:OnGameProgress_Tick(InVal); end end function MiniGameManager:OnMiniGameEnd() UGCLogSystem.Log("[MiniGameManager:OnMiniGameEnd] ") self:SetState(MiniGameState.MINI_END); -- 从小局中取出来 self.MiniInfo.Scores = self:DoMiniFunc("GetScore") self:DoMiniFunc("OnGameEnd", self.MiniInfo.Scores); self:SetMiniInfo(); local Time = self:GetCurrStateTotalTime(); UGCLogSystem.Log("[MiniGameManager:OnMiniGameEnd] Time = %f", Time); self:HandleAddTick(Time, self.OnMiniGameSwitch) end --- 切换到下一个 MiniGame function MiniGameManager:OnMiniGameSwitch() -- 检查是否还有下一个了 UGCLogSystem.Log("[MiniGameManager:OnMiniGameSwitch] 执行") if table.isEmpty(self.SelectMaps) then return self:OnGameEnd(); end self:UnLoadMiniGame(); self:LoadMiniGame(); end function MiniGameManager:SetState(InState) if IsServer then self.State = InState; self.MiniInfo.State = InState; UGCLogSystem.Log("[MiniGameManager:SetState] CurrState = %s", TableHelper.printEnum(MiniGameState, self.State)); self:SetMiniInfo(); if self.CurrMiniMode ~= nil then self.CurrMiniMode.State = InState; end if self.GameConfig then self:DoMiniFunc('SetState', InState) end end end function MiniGameManager:SetRoundTimes(InTimes) if IsServer then self.MiniInfo.RoundTimes = InTimes; self:SetMiniInfo(); if self.CurrMiniMode ~= nil then self.CurrMiniMode.RoundTimes = InTimes; end end end function MiniGameManager:OnRep_RoundTimes(InOld) if self.CurrMiniMode ~= nil then self.CurrMiniMode.RoundTimes = self.MiniInfo.RoundTimes; end end function MiniGameManager:OnRep_State(InOld) UGCLogSystem.Log("[MiniGameManager:OnRep_State] self.State = %s, StateName = %s", tostring(self.MiniInfo.State), TableHelper.printEnum(MiniGameState, self.State)); self.State = self.MiniInfo.State; UGCEventSystem.SendEvent(EventTypes.MiniStateChange, self.State); if self.State == MiniGameState.MINI_PREPARE then elseif self.State == MiniGameState.ROUND_PREPARE then self:OnPrepareEnd(); elseif self.State == MiniGameState.ROUND_GAMING then self:OnMiniRoundFormalStart(); elseif self.State == MiniGameState.ROUND_END then self:OnMiniRoundEnd(); elseif self.State == MiniGameState.MINI_END then self:OnMiniGameEnd(); elseif self.State == MiniGameState.ENDED then self:OnGameEnd(); end end --- 设置同步 MiniInfo, function MiniGameManager:SetMiniInfo() self.Owner:SetMiniInfo(self.MiniInfo); end --- 设置游戏数据 ---@vararg function MiniGameManager:SetGameInfo(InName, InInfo) UGCLogSystem.LogTree(string.format("[MiniGameManager:SetGameInfo] InInfo ="), InInfo) if not table.compare(self.MiniInfo[InName], InInfo) then if type(InInfo) == 'table' then self.MiniInfo[InName] = TableHelper.DeepCopyTable(InInfo); else self.MiniInfo[InName] = InInfo; end self:SetMiniInfo(); end end function MiniGameManager:DOREPONCE_Mini(Name) if self.CurrMiniMode then table.func(self.CurrMiniMode, "DOREPONCE", "PlayerKDAs"); end end -- 给予子模块的数据同步 function MiniGameManager:DOREPONCE(Name) if self.CurrMiniMode then self:SetGameInfo(Name, self.CurrMiniMode[Name]); end end function MiniGameManager:DoMiniFunc(InName, ...) if self.CurrMiniMode then return table.func(self.CurrMiniMode, InName, ...); else if self.CurrSelectMap then -- 加载一下 self.CurrMiniMode = require(self.ScriptPath .. self.GameConfig[self.CurrSelectMap].Script); if self.CurrMiniMode == nil then return false; end GlobalMiniMode = GlobalMiniMode or self.CurrMiniMode; if not table.isEmpty(self.GameConfig[self.CurrSelectMap].Params) then for i, v in pairs(self.GameConfig[self.CurrSelectMap].Params) do assert(self.CurrMiniMode[i] == nil); self.CurrMiniMode[i] = v; end end return self:DoMiniFunc(InName, ...); end end end ----------------------------------- 地图函数 ----------------------------------- MiniGameManager.CurrLoadMapName = nil; --- 加载地图 function MiniGameManager:LoadMap(InIndex) -- 检查当前是什么东西,如果是随机的话 local MapNames = MiniGameConfig[InIndex].Map.MapName; local LoadMap = ""; if type(MapNames) == 'table' then local Total = 0; local Maps = {}; for Name, Rate in pairs(MapNames) do if type(Name) == 'string' then Total = Total + Rate; table.insert(Maps, { Name = Name, Rate = Rate, }); elseif type(Name) == 'number' then table.insert(Maps, { Name = Rate, Rate = 1, }) end end local MapCount = table.getCount(Maps); if Total == 0 then -- 从这两个中随机一个 LoadMap = Maps[math.random(MapCount)].Name; else local RandomNum = math.pop() * Total; for i = 1, table.getCount(Maps) do RandomNum = RandomNum - Maps[i].Rate; if RandomNum <= 0 then LoadMap = Maps[i].Name; break ; end end end elseif type(MapNames) == 'string' then LoadMap = MapNames; end -- 在此之前调用一下内部函数 local Map = table.func(self.CurrMiniMode, "SelectLevel", LoadMap) if Map ~= nil then LoadMap = Map; end self.CurrLoadMapName = LoadMap; self.Owner:LoadMap({ LoadMap }); end --- 完成加载地图 function MiniGameManager:OnMapLoadComplete() UGCLogSystem.Log("[MiniGameManager:OnMapLoadComplete] 地图加载完成") -- 查看是否加载成功了对应的东西,如果没加载成功,就不需要管了 self:ResetPlayers(); self:OnMapLoaded(); end --- 加载地图 function MiniGameManager:OnMapLoaded() UGCLogSystem.Log("[MiniGameManager:OnMapLoaded] 加载地图成功") self:DoMiniFunc("OnAlready"); -- 重生一下玩家 if IsClient then -- 加载是否有 GodCamera self.CurrMiniMode.GodCamera = UE.FindActorByClass(ObjectPath.BP_GodCamera) else LuaQuickFireEvent("AllPlayerReset", self.Owner); --if not self:Test_CheckActorStarts() then -- self.CheckStartTimer = UGCEventSystem.SetTimerLoop(GameState, function() -- if self:Test_CheckActorStarts() then -- if self.CheckStartTimer then -- UGCEventSystem.StopTimer(self.CheckStartTimer); -- self.CheckStartTimer = nil; -- end -- end -- end, 1); --end --if not self:Test_CheckPlayerStart() then -- self.CheckPlayerStartTimer = UGCEventSystem.SetTimerLoop(GameState, function() -- if self:Test_CheckPlayerStart() then -- if self.CheckPlayerStartTimer then -- UGCEventSystem.StopTimer(self.CheckPlayerStartTimer); -- self.CheckPlayerStartTimer = nil; -- end -- end -- end, 1); --end GameState:TogglePoison(1); -- 检查是否存在 self:InitPlayerData(); end end function MiniGameManager:Test_CheckActorStarts() -- 查找一下是否存在 ActorStart local AllStarts = {}; UE.FindActorsByClass(ObjectPath.BP_ActorStart, AllStarts) for i, v in pairs(AllStarts) do UGCLogSystem.Log("[MiniGameManager:Test_CheckActorStarts] Actor Name = %s", UE.GetName(v)); end return not table.isEmpty(AllStarts); end function MiniGameManager:Test_CheckPlayerStart() local AllStarts = {}; UE.FindActorsByClass(ObjectPath.BP_PlayerStartBase, AllStarts); for i, v in pairs(AllStarts) do UGCLogSystem.Log("[MiniGameManager:Test_CheckPlayerStart] Actor Name = %s", UE.GetName(v)); end return not table.isEmpty(AllStarts); end --- 卸载地图 function MiniGameManager:UnLoadMap() -- 然后过一会再重新加载地图 self.Owner:UnloadMap({ self.CurrLoadMapName }); end function MiniGameManager:OnMapUnLoadedComplete() UGCLogSystem.Log("[MiniGameManager:OnMapUnLoadedComplete] 异步卸载地图完成") end --- 玩家选择地图 function MiniGameManager:OnPlayerSelectMap(InPlayer, InMapIndex) if IsServer then table.insert(self.SelectMaps, InMapIndex); -- 发送 RPC self:SendRPC("OnPlayerSelectMap", self.SelectMaps) else UGCEventSystem.SendEvent(EventTypes.MiniGame_SelectMap, self.SelectMaps); end end ----------------------------------- 回合函数 ----------------------------------- --- 开启小局 function MiniGameManager:OnMiniRoundPrepare() -- 小局次数 - 1 if IsServer then self:SetState(MiniGameState.ROUND_PREPARE); local PreTime = self:GetCurrStateTotalTime() self:HandleAddTick(PreTime, self.OnMiniRoundFormalStart, function(o, dt, st, t) o:OnBeforeGameStart(t); end); self.CurrMiniMode.RoundTimes = self.CurrMiniMode.RoundTimes - 1; self.CurrMiniMode.bGameEnd = false; self.CurrMiniMode.bRoundEnd = false; self:SetRoundTimes(self.CurrMiniMode.RoundTimes); else -- 看看是否要执行什么 UGCLogSystem.Log("[MiniGameManager:OnMiniRoundPrepare] CurrSelectMap = %s", tostring(self.CurrSelectMap)); end self:DoMiniFunc("OnRoundStart", self:GetCurrRoundTimes()) end --- 小局正式开始 function MiniGameManager:OnMiniRoundFormalStart() -- 这是正式开启了 UGCLogSystem.Log("[MiniGameManager:OnMiniRoundFormalStart] 正式开始游戏") if self.CurrMiniMode then self.CurrMiniMode.RoundStartTime = UE.GetServerTime(); end if IsServer then -- 启动计时,让小游戏有一个时限 self:SetState(MiniGameState.ROUND_GAMING); self:HandleAddTick(self:GetCurrStateTotalTime(), self.OnMiniRoundEnd, nil, function(o) return o.CurrMiniMode.bGameEnd or o.CurrMiniMode.bRoundEnd; end); -- 解除玩家封禁状态 self:SetAllPawnsCanMove(true); -- 检查玩家身上是否有武器,如果没有武器,检查一下是否 local InitItems = self:GetInitItemIds(); UE.ForeachAllPawns(function(Pawn) -- 检查第一个即可,如果第一个已经有了,其他的也会有 if not ItemTool.CheckPawnHasItemId(Pawn, InitItems[1]) then Pawn:AddInitItems(InitItems.Init); end end); -- 重置一下数据 self:ResetPlayers(); end self:InitPlayerData(); self:DoMiniFunc("OnRoundFormalStart", self:GetCurrRoundTimes(), self.CurrMiniMode.RoundStartTime); end function MiniGameManager:GetInitItemIds() local InitItems = self.GameConfig[self.CurrSelectMap].InitWeapons if InitItems then if InitItems.Init then local ItemIds = {}; for i, v in pairs(InitItems.Init) do local Name = v[1]; UGCLogSystem.Log("[MiniGameManager:GetInitItemIds] Item Name = %s", Name); if EnglishNamedWeapon[Name] then local ItemId = EnglishNamedWeapon[Name].ItemId; table.insert(ItemIds, ItemId); end end return ItemIds; end end return nil; end function MiniGameManager:OnMiniRoundEnd() -- 通知结束了 UGCLogSystem.Log("[MiniGameManager:OnMiniRoundEnd] 结束") self.CurrMiniMode.RoundEndTime = UE.GetServerTime(); local Winner = 0; if IsServer then self:SetState(MiniGameState.ROUND_END); if self.CurrMiniMode.RoundTimes <= 0 or self.CurrMiniMode.bGameEnd then UGCLogSystem.Log("[MiniGameManager:OnMiniRoundEnd] 回合结束") self:HandleAddTick(self:GetCurrStateTotalTime(), self.OnMiniGameEnd); else -- 继续 UGCLogSystem.Log("[MiniGameManager:OnMiniRoundEnd] 下一小局开始") self:HandleAddTick(self:GetCurrStateTotalTime(), self.OnMiniRoundPrepare); end if table.hasFunc(self.CurrMiniMode, 'GetRoundWinner') then self.MiniInfo.RoundWinner = self:DoMiniFunc("GetRoundWinner", self:GetCurrRoundTimes()); end Winner = self.MiniInfo.RoundWinner; -- 检查是否需要关闭 self:SetAllPawnsCanMove(false); else -- 显示回合成功或者失败,执行到此一定可以拿到Winner是谁 Winner = self.MiniInfo.RoundWinner end UGCLogSystem.Log("[MiniGameManager:OnMiniRoundEnd] RoundWinner = %s", tostring(Winner)); self:DoMiniFunc("OnRoundEnd", self:GetCurrRoundTimes(), Winner, self.CurrMiniMode.RoundEndTime - self.CurrMiniMode.RoundStartTime); end ----------------------------------- 玩家函数 ----------------------------------- --- 当添加玩家的时候执行 function MiniGameManager:OnPlayerLogin(InPlayerKey) UGCLogSystem.Log("[MiniGameManager:OnPlayerLogin] 玩家:%d 登录", InPlayerKey); self:DoMiniFunc("OnPlayerLogin", InPlayerKey); self.PlayerDatas[InPlayerKey] = { IsConnect = true; }; if self.PlayerDamages[InPlayerKey] == nil then self.PlayerDamages[InPlayerKey] = 0; self:SetGameInfo("PlayerDamages", self.PlayerDamages); end self.PlayerAssistTimes[InPlayerKey] = {}; if self.PlayerKDAs[InPlayerKey] == nil then self.PlayerKDAs[InPlayerKey] = UE.InitKDA(); self:SetGameInfo("PlayerKDAs", self.PlayerKDAs); else -- 说明玩家重新登陆 self:OnPlayerRelogin(InPlayerKey); end end --- 玩家重新登录 function MiniGameManager:OnPlayerRelogin(InPlayerKey) -- 玩家重复登录执行 self:DoMiniFunc("OnPlayerRelogin", InPlayerKey); end --- 当玩家退出的时候执行 function MiniGameManager:OnPlayerLeave(InPlayerKey) UGCLogSystem.Log("[MiniGameManager:OnPlayerLeave] 玩家:%d 登出", InPlayerKey); self.PlayerDatas[InPlayerKey].IsConnect = false; return self:DoMiniFunc("OnPlayerLeave", InPlayerKey); end --- 选择玩家出生点 ---@param PlayerStartList table> ---@param Controller UGCPlayerController_C function MiniGameManager:SelectPlayerStart(PlayerStartList, Controller) return self:DoMiniFunc('SelectPlayerStart', PlayerStartList, Controller); end --- S & C 玩家在加载好之后进行的初始化操作,该操作是在 Pawn 一秒钟后执行 function MiniGameManager:OnPawnInit(Pawn) if IsServer then UGCLogSystem.Log("[MiniGameManager:OnPawnInit] self.CurrSelectMap = %s", tostring(self.CurrSelectMap)) if self.CurrSelectMap == nil then return ; end local Weapons = self.GameConfig[self.CurrSelectMap].InitWeapons; UGCLogSystem.LogTree(string.format("[MiniGameManager:OnPawnInit] Weapons ="), Weapons) self:DoMiniFunc('OnPawnInit', Pawn, Weapons); else self:DoMiniFunc('OnPawnInit', Pawn); end end --- 修改伤害函数 ---@param DamagedActor UGCPlayerPawn_C ---@param Damage float ---@param DamageType EDamageType ---@param EventInstigator UGCPlayerController_C function MiniGameManager:OnPlayerDamage(DamagedActor, Damage, DamageType, EventInstigator) UGCLogSystem.Log("[MiniGameManager:OnPlayerDamage] DamagedActor = %s, Damage = %f", UE.GetName(DamagedActor), Damage); local CalDamage = self:DoMiniFunc('OnPlayerTakeDamage', DamagedActor, Damage, DamageType, EventInstigator) if CalDamage == nil then return Damage; end return CalDamage; end --- S & C 接受到伤害函数 function MiniGameManager:BPReceiveDamage(DamagedActor, Damage, DamageType, InstigatedBy, DamageCauser) if IsServer then self:DoMiniFunc('BPReceiveDamage', DamagedActor, Damage, DamageType, InstigatedBy, DamageCauser) if self.PlayerAssistTimes[DamagedActor.PlayerKey] == nil then self.PlayerAssistTimes[DamagedActor.PlayerKey] = {}; end if InstigatedBy ~= nil and InstigatedBy.PlayerKey ~= DamagedActor.PlayerKey then self.PlayerAssistTimes[DamagedActor.PlayerKey][InstigatedBy.PlayerKey] = UE.GetServerTime() + MiniGlobalConfig.AssistTime; if self.PlayerDamages[InstigatedBy.PlayerKey] == nil then self.PlayerDamages[InstigatedBy.PlayerKey] = Damage; else self.PlayerDamages[InstigatedBy.PlayerKey] = Damage + self.PlayerDamages[InstigatedBy.PlayerKey]; end UGCEventSystem.SendEvent(EventTypes.UpdatePlayerDamages, self.PlayerDamages); self:SetGameInfo("PlayerDamages", self.PlayerDamages); end end end function MiniGameManager:OnRep_PlayerDamages() UGCLogSystem.LogTree(string.format("[MiniGameManager:OnRep_PlayerDamages] self.PlayerDamages ="), self.PlayerDamages) UGCEventSystem.SendEvent(EventTypes.UpdatePlayerDamages, self.PlayerDamages); end --- 当玩家死亡 ---@param DeadPlayerKey PlayerKey ---@param KillerPlayerKey PlayerKey function MiniGameManager:OnPlayerDead(DeadPlayerKey, KillerPlayerKey) if DeadPlayerKey == nil or DeadPlayerKey <= 0 then return ; end self:DoMiniFunc('OnPlayerDead', DeadPlayerKey, KillerPlayerKey); if IsServer then -- 处理死亡玩家 if self.PlayerKDAs[DeadPlayerKey] == nil then self:ResetPlayerKDA(DeadPlayerKey) end self.PlayerKDAs[DeadPlayerKey].Dead = (self.PlayerKDAs[DeadPlayerKey].Dead or 0) + 1; -- 记录一下当前击杀死亡的 if KillerPlayerKey ~= nil and KillerPlayerKey > 0 then if self.PlayerKDAs[KillerPlayerKey] == nil then self:ResetPlayerKDA(KillerPlayerKey) end self.PlayerKDAs[KillerPlayerKey].Kill = (self.PlayerKDAs[KillerPlayerKey].Kill or 0) + 1; end -- 处理助攻玩家 if self.PlayerAssistTimes[DeadPlayerKey] then local ServerTime = UE.GetServerTime(); for PlayerKey, AssistTime in pairs(self.PlayerAssistTimes[DeadPlayerKey]) do if AssistTime <= ServerTime and PlayerKey ~= DeadPlayerKey and PlayerKey ~= KillerPlayerKey then self.PlayerKDAs[PlayerKey].Assist = (self.PlayerKDAs[PlayerKey].Assist or 0) + 1; end end self.PlayerAssistTimes[DeadPlayerKey] = {}; end self:SetGameInfo("PlayerKDAs", self.PlayerKDAs); UGCLogSystem.LogTree(string.format("[MiniGameManager:OnPlayerDead] self.PlayerKDAs ="), self.PlayerKDAs) -- 发送 RPC self:SendRPC("OnPlayerDead", DeadPlayerKey, KillerPlayerKey); end end function MiniGameManager:OnRep_PlayerKDAs() UGCLogSystem.LogTree(string.format("[MiniGameManager:OnRep_PlayerKDAs] self.PlayerKDAs ="), self.PlayerKDAs) UGCEventSystem.SendEvent(EventTypes.UpdatePlayerKDAs, self.PlayerKDAs); end --- 初始化所有数据,保证客户端可以拿到 function MiniGameManager:InitPlayerData() if IsServer then if table.isEmpty(self.PlayerKDAs) then UE.ForeachAllPCs(function(PC) local PlayerKey = PC.PlayerKey UGCLogSystem.Log("[MiniGameManager:InitPlayerData] PlayerKey = %s", tostring(PlayerKey)); if PlayerKey then self:ResetPlayerDamage(PlayerKey); self:ResetPlayerKDA(PlayerKey); self.PlayerDatas[PlayerKey] = { IsConnect = true, }; end end) self:SetGameInfo("PlayerDamages", self.PlayerDamages); self:SetGameInfo("PlayerKDAs", self.PlayerKDAs); UGCLogSystem.LogTree(string.format("[MiniGameManager:InitPlayerData] self.PlayerKDAs ="), self.PlayerKDAs) UGCLogSystem.LogTree(string.format("[MiniGameManager:InitPlayerData] self.PlayerDamages"), self.PlayerDamages) end end --- 让子模块去初始化 self:DoMiniFunc("InitPlayerData") end function MiniGameManager:ResetPlayerKDA(InPlayerKey) if InPlayerKey == nil then for i, v in pairs(self.PlayerKDAs) do self:ResetPlayerKDA(i); end self:SetGameInfo("PlayerKDAs", self.PlayerKDAs); else self.PlayerKDAs[InPlayerKey] = UE.InitKDA(); end end function MiniGameManager:ResetPlayerDamage(InPlayerKey) if InPlayerKey == nil then for PlayerKey, Damage in pairs(self.PlayerDamages) do self:ResetPlayerDamage(PlayerKey); end self.PlayerAssistTimes = {}; self:SetGameInfo("PlayerDamages", self.PlayerDamages); else self.PlayerDamages[InPlayerKey] = 0; end end function MiniGameManager:ResetPlayers() if IsServer then self:ResetPlayerKDA(); self:ResetPlayerDamage(); -- 重置玩家击杀 UE.ForeachAllPCs(function(PC) PC:ResetGame(); end); UE.RespawnAllPlayer(); end self:DoMiniFunc("ResetMini"); end --- 玩家重生 ---@param InPlayerKey PlayerKey function MiniGameManager:OnPlayerRespawn(InPlayerKey) self:DoMiniFunc("OnPlayerRespawn", InPlayerKey) end --- 当 Pawn 被控制时 ---@param Pawn UGCPlayerPawn_C ---@param NewController UGCPlayerController_C function MiniGameManager:OnPlayerPossessed(Pawn, NewController) -- 检查是否有初始武器,如果有,那么直接添加 if self.CurrSelectMap == nil or self.CurrMiniMode == nil then return ; end if self.GameConfig[self.CurrSelectMap].IsPlayerStunPrepare then NewController:SetCanMove(false); end self:DoMiniFunc("OnPlayerPossessed", Pawn, NewController); end --- 玩家拾取 ---@param PlayerController UGCPlayerController_C ---@param Target APickUpWrapperActor ---@param ItemID int32 ---@param Count int32 function MiniGameManager:OnPlayerPickup(PlayerController, Target, ItemID, Count) self:DoMiniFunc("OnPlayerPickup", PlayerController, Target, ItemID, Count) end --- 玩家拿取武器 ---@param InPawn UGCPlayerPawn_C ---@param Weapon ASTExtraWeapon function MiniGameManager:OnPlayerGetWeapon(InPawn, Weapon) if self.CurrMiniMode then self:DoMiniFunc("OnPlayerGetWeapon", InPawn, Weapon); end end --- 武器攻击到什么东西的时候 ---@param InPawn UGCPlayerPawn_C 是谁射到了 ---@param ShootWeapon ASTExtraShootWeapon 发射用的武器 ---@param Bullet ASTExtraShootWeaponBulletBase 射中的子弹 ---@param HitInfo FHitResult Hit function MiniGameManager:OnWeaponBulletHit(InPawn, ShootWeapon, Bullet, HitInfo) if self.CurrMiniMode then self:DoMiniFunc("OnWeaponBulletHit", InPawn, ShootWeapon, Bullet, HitInfo); end end ---@param InPawn UGCPlayerPawn_C ---@param ShootWeapon ASTExtraShootWeapon ---@param BulletID int32 function MiniGameManager:OnPlayerShoot(InPawn, ShootWeapon, BulletID) if self.CurrMiniMode then self:DoMiniFunc("OnPlayerShoot", InPawn, ShootWeapon, BulletID); end end ------------------------------------ 获取函数 ------------------------------------ --- 获取当前模式 function MiniGameManager:GetCurrentMode() return self.CurrMiniMode; end --- 获取当前地图名 function MiniGameManager:GetCurrLevel() return self.GameConfig[self.CurrSelectMap].Map.MapName; end --- 获取当前显示地图名称 function MiniGameManager:GetShowName() return self.GameConfig[self.CurrSelectMap].Map.ShowName; end --- 获取当前是第几回合 function MiniGameManager:GetCurrRoundTimes() if table.isEmpty(self.GameConfig) or self.CurrSelectMap == nil or table.isEmpty(self.GameConfig[self.CurrSelectMap]) then return 1; end if self.GameConfig[self.CurrSelectMap].RoundTimes == nil then return 1; else return self.GameConfig[self.CurrSelectMap].RoundTimes - self.CurrMiniMode.RoundTimes; end end --- S & C 获取当前 State 的总时间 function MiniGameManager:GetCurrStateTotalTime() if self.State == nil then return 0; end local Times = nil; if self.CurrSelectMap ~= nil and self.GameConfig ~= nil then Times = self.GameConfig[self.CurrSelectMap].MiniGameTimes; end local TimeFunc = function(key) local Time = 0; if Times ~= nil and Times[key] ~= nil then Time = Times[key]; else Time = MiniGameTimes[key]; end assert(Time ~= nil); return Time; end if self.State <= MiniGameState.NON_START then return 0; elseif self.State == MiniGameState.SELECT_GAMES then return MiniGameTimes.Active; elseif self.State == MiniGameState.MINI_PREPARE then return TimeFunc("Prepare"); elseif self.State == MiniGameState.ROUND_PREPARE then return TimeFunc("RoundPrepare"); elseif self.State == MiniGameState.ROUND_GAMING then return TimeFunc("RoundGaming"); elseif self.State == MiniGameState.ROUND_END then return TimeFunc("RoundEnd") elseif self.State == MiniGameState.MINI_END then return TimeFunc("MiniEnd") elseif self.State == MiniGameState.ENDED then return TimeFunc("GameEnd") end end --- 获取总的回合数 function MiniGameManager:GetTotalRoundTimes() if table.isEmpty(self.GameConfig) or self.CurrSelectMap == nil or table.isEmpty(self.GameConfig[self.CurrSelectMap]) then return 1; end if self.GameConfig[self.CurrSelectMap].RoundTimes == nil then return 1; else return self.GameConfig[self.CurrSelectMap].RoundTimes end end ------------------------------------ 给予函数 ------------------------------------ --- 所有玩家死亡 function MiniGameManager:AllPlayerDead() for i, v in pairs(UGCGameSystem.GetAllPlayerPawn()) do if v:IsAlive() then v:K2_DestroyActor(); end end end --- 所有玩家重生 function MiniGameManager:AllPlayerRespawn(InTime) self:AllPlayerDead(); UGCEventSystem.SetTimer(GameState, function() for i, v in pairs(UGCGameSystem.GetAllPlayerController(false)) do UGCGameSystem.RespawnPlayer(v.PlayerKey); end end, InTime); end ---@param InPawn UGCPlayerPawn_C ---@param InState EPawnState function MiniGameManager:SetPawnState(InPawn, InState, IsSet) if IsSet then UGCPawnSystem.EnterPawnState(InPawn, InState) else UGCPawnSystem.LeavePawnState(InPawn, InState) end end function MiniGameManager:SetAllPawnsCanMove(IsCan) if self.CurrSelectMap == nil then return ; end if table.isEmpty(self.GameConfig) then return ; end if table.isEmpty(self.GameConfig[self.CurrSelectMap]) then return ; end if not self.GameConfig[self.CurrSelectMap].IsPlayerStunPrepare then return ; end for i, v in pairs(UGCGameSystem.GetAllPlayerController()) do v:SetCanMove(IsCan); end end --- 这个需要放在 GameState 中进行更改 function MiniGameManager:DOREPONCE(k, v) self.Owner[k] = v; DOREPONCE(self.Owner, k); end MiniGameManager.CachedMiniInfo = {}; --- 缓存一份 function MiniGameManager:MakeCachedMiniInfo() if self.CachedMiniInfo.State == nil then self.CachedMiniInfo.State = MiniGameState.NON_START; end end --- 属性同步,对比是否不同,只有不同的会进行 OnRep_ 否则就不会进行。 function MiniGameManager:OnRep_MiniInfo(InOld) --UGCLogSystem.LogTree(string.format("[MiniGameManager:OnRep_MiniInfo] self.MiniInfo ="), self.MiniInfo) --UGCLogSystem.LogTree(string.format("[MiniGameManager:OnRep_MiniInfo] self.CachedMiniInfo Begin ="), self.CachedMiniInfo) for i, v in pairs(self.MiniInfo) do -- 如果发生改变再执行对应的 OnRep if type(v) == 'table' then -- 比较一下这两者有什么不同 if not table.compare(v, self.CachedMiniInfo[i]) then -- 更新一下 UGCLogSystem.Log("[MiniGameManager:OnRep_MiniInfo] 相同变量 Index = %s", tostring(i)); if self:IsRepProp(tostring(i)) then self[i] = v; table.func(self, "OnRep_" .. tostring(i), self.CachedMiniInfo[i]); end if self.CurrMiniMode ~= nil then self.CurrMiniMode[i] = TableHelper.DeepCopyTable(v); self:DoMiniFunc("OnRep_" .. tostring(i), self.CachedMiniInfo[i]); end end else if self.CachedMiniInfo[i] ~= v then if self:IsRepProp(tostring(i)) then self[i] = v; table.func(self, "OnRep_" .. tostring(i), self.CachedMiniInfo[i]); end if self.CurrMiniMode ~= nil then self.CurrMiniMode[i] = v; self:DoMiniFunc("OnRep_" .. tostring(i), self.CachedMiniInfo[i]); end end end UGCLogSystem.Log("[MiniGameManager:OnRep_MiniInfo] Index = %s", i); self.CachedMiniInfo[i] = nil; --UGCLogSystem.LogTree(string.format("[MiniGameManager:OnRep_MiniInfo] self.CachedMiniInfo ="), self.CachedMiniInfo) end --UGCLogSystem.LogTree(string.format("[MiniGameManager:OnRep_MiniInfo] self.CachedMiniInfo End ="), self.CachedMiniInfo) -- 检查是否有多余的 for i, v in pairs(self.CachedMiniInfo) do if self.MiniInfo[i] == nil then if self:IsRepProp(tostring(i)) then self[tostring(i)] = nil; table.func(self, "OnRep_" .. tostring(i), v); end if self.CurrMiniMode ~= nil then self.CurrMiniMode[tostring(i)] = nil; self:DoMiniFunc("OnRep_" .. tostring(i), v); end UGCLogSystem.Log("[MiniGameManager:OnRep_MiniInfo] 执行冗余的 Index = %s", tostring(i)); end UGCLogSystem.Log("[MiniGameManager:OnRep_MiniInfo] 冗余的 Index = %s", tostring(i)); end -- 设置上去进行缓存 self.CachedMiniInfo = TableHelper.DeepCopyTable(self.MiniInfo); --UGCLogSystem.LogTree(string.format("[MiniGameManager:OnRep_MiniInfo] self.CachedMiniInfo Final ="), self.CachedMiniInfo) end --- 获取内部数据 function MiniGameManager:GetMiniInfo(InName) if self.CurrMiniMode then local Item = self:DoMiniFunc("GetMiniInfo", InName); if Item ~= nil then return Item; end return self.CurrMiniMode[InName]; end return nil; end return MiniGameManager;