local BuffDir = 'Script.Blueprint.SceneObj.Buff.' ---@class FBuffManager ---@field OwnerBuffs table> ---@field Owner AActor ---@field PreScript string ---@type FBuffManager local BuffManager = {}; ---@class FBuffItem ---@field BuffType EBuffType ---@field OwnerPlayerKey PlayerKey ---@field Owner FBuffManager ---@field BuffName string ---@field new fun(Params:table) 初始化函数 ---@field Init fun(Params:table) 初始化函数 ---@field Reset fun() 初始化函数 ---@field AddBuff fun() 初始化函数 ---@field RemoveBuff fun() 初始化函数 ---@field UpdateBuff fun() 初始化函数 ---@class BuffScriptItem Buff实例 ---@field Buff FBuffItem ---@field Continue float ---@field Cooldown float ---@field StartTime float ---@field EndTime float ---@field State EBuffState ---@type table> BuffManager.OwnerBuffs = {}; -- Buff 的所有者 BuffManager.Owner = nil; BuffManager.Config = nil; BuffManager.PreScript = BuffDir .. 'Script.Buff_'; --- 创建一个实例 function BuffManager:new() return setmetatable( { OwnerBuffs = {}; }, { __index = self, __metatable = self, }); end ---S & C 初始化 function BuffManager:Init(InOwner) self.Owner = InOwner; -- 将永久的 Buff 加上,遍历所有的 Buff 检查哪些是固定添加的 self.Config = require(BuffDir .. 'BuffConfig'); UGCLogSystem.Log("[BuffManager:Init] 执行") GlobalTickTool:AddTick(self, self.OnTick); end BuffManager.DeadRemoveBuffs = {}; function BuffManager:OnPlayerDead(DeadPlayerKey, KillerPlayerKey) for BuffType, PlayersBuff in pairs(self.OwnerBuffs) do if self.Config[BuffType].bRemoveDead then if PlayersBuff[DeadPlayerKey] then if self:HasBuff(BuffType, DeadPlayerKey) then self:RemoveBuff(BuffType, DeadPlayerKey); end end end end end function BuffManager:HasBuff(BuffType, InPlayerKey) local BuffItem = self:FindBuffItem(BuffType, InPlayerKey); if BuffItem then return BuffItem.State == EBuffState.Using; end return false; end ---@param BuffType EBuffType ---@param InPlayerKey PlayerKey ---@vararg any 外部传参会传到实例里面 function BuffManager:AddBuff(BuffType, InPlayerKey, ...) assert(BuffType ~= nil, "BuffType 不能为空") assert(self.Config[BuffType] ~= nil, "BuffConfig 中必须有值") local p = {...}; UGCLogSystem.LogTree(string.format("[BuffManager:AddBuff] p ="), p) local AddSuccess = false; -- 默认一个人同种 BUFF 只能有一个,如果有多个,那么使用传参进行操作,可以传入函数,然后在Buff 内部进行操作 if IsServer then if not self:CanAddBuff(BuffType, InPlayerKey) then UGCLogSystem.Log("[BuffManager:AddBuff] 无法添加") GameState:ShowTipsUI("玩家 %s 技能 %s 还在冷却中", UE.GetPlayerName(InPlayerKey), self:GetBuffName(BuffType)); return false; end end UGCLogSystem.Log("[BuffManager:AddBuff] 开始添加 Buff: %s", TableHelper.printEnum(EBuffType, BuffType)); local BuffItem = self:FindBuffItem(BuffType, InPlayerKey) if BuffItem ~= nil then UGCLogSystem.Log("[BuffManager:AddBuff] 往存在的 BuffItem 上添加:%s", tostring(InPlayerKey)); AddSuccess = self:AddBuff_Exist(BuffItem, InPlayerKey, ...); else UGCLogSystem.Log("[BuffManager:AddBuff] 新添加 BuffItem:%s", tostring(InPlayerKey)); AddSuccess = self:AddBuff_Raw(BuffType, InPlayerKey, ...); end if AddSuccess then self:OnAddBuff(BuffType, InPlayerKey); if IsServer then if self:IsBuffForever(BuffType) then self:SendRPC("Client_AddBuff", InPlayerKey, BuffType, EBuffState.Using, ...); else self:SendRPC("Client_AddBuff", InPlayerKey, BuffType, EBuffState.Using, { StartTime = self.OwnerBuffs[BuffType][InPlayerKey].StartTime, EndTime = self.OwnerBuffs[BuffType][InPlayerKey].EndTime, }, ...); end end else UGCLogSystem.Log("[BuffManager:AddBuff] 玩家 %s 无法添加成功", tostring(InPlayerKey)); end return AddSuccess; end function BuffManager:Register(BuffItem, InFuncName, DeadPlayerKey, KillPlayerKey) end function BuffManager:OnAddBuff(BuffType, InPlayerKey) local Pawn = UGCGameSystem.GetPlayerPawnByPlayerKey(InPlayerKey); if UE.IsValidPawn(Pawn) then table.func(Pawn, "OnAddBuff", BuffType); end local PC = UGCGameSystem.GetPlayerControllerByPlayerKey(InPlayerKey); if PC ~= nil and UE.IsValid(PC) then table.func(PC, "OnAddBuff", BuffType); end end function BuffManager:IsBuffForever(BuffType) local Config = self.Config[BuffType]; return (Config.Cooldown == -1 and Config.Continue == -1) or (Config.Cooldown == nil and Config.Continue == nil); end ---@param BuffItem BuffScriptItem ---@return bool function BuffManager:AddBuff_Exist(BuffItem, InPlayerKey, ...) BuffItem.StartTime = UE.GetServerTime(); BuffItem.EndTime = BuffItem.StartTime + BuffItem.Continue; BuffItem.State = EBuffState.Using; return table.func(BuffItem.Buff, "AddBuff", InPlayerKey, ...); end ---@param BuffType EBuffType ---@param InPlayerKey PlayerKey ---@return bool function BuffManager:AddBuff_Raw(BuffType, InPlayerKey, ...) local BuffBase = require(self.PreScript .. self.Config[BuffType].Script); local Buff = nil; if table.hasFunc(BuffBase, "new") then Buff = table.func(BuffBase, "new", ...); else Buff = setmetatable({}, { __metatable = BuffBase; __index = BuffBase; }); end self:InitBuff(Buff, BuffType, InPlayerKey); local Continue = self.Config[BuffType].Continue; if Continue == nil then Continue = 0; end local Cooldown = self.Config[BuffType].Cooldown; if Cooldown == nil then Cooldown = 0; end local StartTime = UE.GetServerTime(); --UGCLogSystem.Log("[BuffManager:AddBuff_Raw] StartTime = %s", tostring(StartTime)); local Params = {...}; UGCLogSystem.LogTree(string.format("[BuffManager:AddBuff_Raw] Params ="), Params) local AddSuccess = table.func(Buff, "AddBuff", InPlayerKey, ...); if not AddSuccess then return false; end -- 设置定时器然后增加 local Item = { Buff = Buff, Continue = Continue, -- 如果 Continue < 0 表示当前是无限执行的 Cooldown = Cooldown, StartTime = StartTime, EndTime = StartTime + Continue, State = EBuffState.Using, }; UGCLogSystem.Log("[BuffManager:AddBuff_Raw] BuffType = %s", TableHelper.printEnum(EBuffType, BuffType)); if self:IsBuffForever(BuffType) then self.OwnerBuffs[BuffType] = Item; else if InPlayerKey then if self.OwnerBuffs[BuffType] == nil then self.OwnerBuffs[BuffType] = {}; end self.OwnerBuffs[BuffType][InPlayerKey] = Item; end end UGCLogSystem.LogTree(string.format("[BuffManager:AddBuff_Raw] self.OwnerBuffs ="), self.OwnerBuffs) return true; end ---@param BuffType EBuffType ---@param InPlayerKey PlayerKey ---@param Buff BuffScriptItem function BuffManager:InitBuff(Buff, BuffType, InPlayerKey) if not self:IsBuffForever(BuffType) then Buff.OwnerPlayerKey = InPlayerKey; end Buff.BuffType = BuffType; --Buff.BuffName = self.Config[BuffType].Name; if not table.isEmpty(self.Config[BuffType].Params) then for i, v in pairs(self.Config[BuffType].Params) do Buff[i] = v; end end table.func(Buff, "Init", self.Config[BuffType].Params); end ---@param BuffItem BuffScriptItem function BuffManager:RemoveBuff_Internal(PlayerKey, BuffItem) local Buff = BuffItem.Buff; table.func(Buff, "RemoveBuff", PlayerKey); -- 检查是否有冷却 if BuffItem.Cooldown > 0 then BuffItem.State = EBuffState.Cooling; BuffItem.StartTime = UE.GetServerTime(); BuffItem.EndTime = BuffItem.StartTime + BuffItem.Cooldown; -- 发送 RPC self:SendRPC("Client_AddBuff", BuffItem.Buff.OwnerPlayerKey, BuffItem.Buff.BuffType, EBuffState.Cooling, { StartTime = self.OwnerBuffs[BuffItem.Buff.BuffType][BuffItem.Buff.OwnerPlayerKey].StartTime, EndTime = self.OwnerBuffs[BuffItem.Buff.BuffType][BuffItem.Buff.OwnerPlayerKey].EndTime, }); else self:ResetBuff(BuffItem); end end ---@param BuffType EBuffType ---@param InPlayerKey PlayerKey ---@return bool function BuffManager:RemoveBuff(BuffType, InPlayerKey) local BuffItem = self:FindBuffItem(BuffType, InPlayerKey); if BuffItem then self:RemoveBuff_Internal(InPlayerKey, BuffItem); return true; end return false; end --- 重置 Buff ---@param BuffItem BuffScriptItem function BuffManager:ResetBuff(BuffItem) BuffItem.State = EBuffState.None; -- 执行初始化 if table.hasFunc(BuffItem.Buff, "Reset") then table.func(BuffItem.Buff, "Reset", self.Config[BuffItem.Buff.BuffType].Params); else table.func(BuffItem.Buff, "Init", self.Config[BuffItem.Buff.BuffType].Params); end if IsServer then self:SendRPC("Client_AddBuff", BuffItem.Buff.OwnerPlayerKey, BuffItem.Buff.BuffType, EBuffState.None); end end ---@param BuffType EBuffType ---@param InPlayerKey PlayerKey ---@return bool function BuffManager:UpdateBuff(BuffType, InPlayerKey, ...) local BuffItem = self:FindBuffItem(BuffType, InPlayerKey) return table.func(BuffItem.Buff, "UpdateBuff", ...) end --- 发送 RPC function BuffManager:SendRPC(InFuncName, InPlayerKey, BuffType, ...) if IsServer then UnrealNetwork.CallUnrealRPC_Multicast_Unreliable(self.Owner, "SendBuffRPC", InFuncName, InPlayerKey, BuffType, ...); end end ---@param BuffType EBuffType ---@param InPlayerKey PlayerKey function BuffManager:FindBuffItem(BuffType, InPlayerKey) local Table = self.OwnerBuffs; if Table[BuffType] then if self:IsBuffForever(BuffType) then return Table[BuffType]; end if InPlayerKey == nil then return Table[BuffType]; end return Table[BuffType][InPlayerKey]; end return nil; end ---@param BuffType EBuffType ---@param InPlayerKey PlayerKey ---@return bool function BuffManager:CanAddBuff(BuffType, InPlayerKey) if self:IsBuffForever(BuffType) then return true; end local Buff = self:FindBuffItem(BuffType, InPlayerKey); if Buff ~= nil then -- 检查一下是否是 return Buff.State == EBuffState.None; end return true; end function BuffManager:OnTick(dt, st) if IsServer then for BuffType, PlayerBuffs in pairs(self.OwnerBuffs) do if not self:IsBuffForever(BuffType) then for PlayerKey, BuffItem in pairs(PlayerBuffs) do if BuffItem.EndTime <= st then if BuffItem.State == EBuffState.Using then self:RemoveBuff_Internal(PlayerKey, BuffItem); UGCLogSystem.LogTree(string.format("[BuffManager:OnTick] BuffItem Using"), BuffItem) elseif BuffItem.State == EBuffState.Cooling then UGCLogSystem.LogTree(string.format("[BuffManager:OnTick] BuffItem Cooling"), BuffItem) self:ResetBuff(BuffItem); end end end end end end end if not UE_SERVER then ---@type table> BuffManager.PlayerParticles = {}; function BuffManager:Client_AddBuff(InPlayerKey, BuffType, State, ...) if InPlayerKey == nil or BuffType == nil then return; end --UGCEventSystem.SendEvent(EventTypes.PlayerUseBuff, InPlayerKey, BuffType, State, ...); if State == EBuffState.Using then self:AddBuff(BuffType, InPlayerKey, ...); if self.Config[BuffType].Particle ~= nil and string.len(self.Config[BuffType].Particle) > 0 then UE.AsyncLoadObject_Cached(self.Config[BuffType].Particle, function(TargetObject) local Pawn = UGCGameSystem.GetPlayerPawnByPlayerKey(InPlayerKey); if UE.IsValidPawn(Pawn) then if self.PlayerParticles[BuffType] == nil then self.PlayerParticles[BuffType] = {}; end self.PlayerParticles[BuffType][InPlayerKey] = GameplayStatics.SpawnEmitterAttached(TargetObject, Pawn.Mesh, "", VectorHelper.VectorZero(), Pawn:K2_GetActorRotation(), Pawn:GetActorScale3D(), EAttachLocation.SnapToTargetIncludingScale, true); end end); end else if State == EBuffState.Cooling then UGCLogSystem.Log("[BuffManager:Client_AddBuff] 移除") self:RemoveBuff(BuffType, InPlayerKey); elseif State == EBuffState.None then UGCLogSystem.Log("[BuffManager:Client_AddBuff] 重置"); local BuffItem = self:FindBuffItem(BuffType, InPlayerKey); self:ResetBuff(BuffItem); end if self.PlayerParticles[BuffType] then local Item = self.PlayerParticles[BuffType][InPlayerKey] if Item ~= nil and UE.IsValid(Item) then Item:SetHiddenInGame(true, true); end end end end ---@param Image UImage function BuffManager:ChangeBuffIcon(InBuffType, Image) if Image == nil then return ; end local Icon = self.Config[InBuffType].Icon; if Icon ~= nil and string.len(Icon) > 0 then UE.AsyncLoadObject_Cached(Icon, function(TargetObject) UGCLogSystem.Log("[BuffManager:ChangeBuffIcon] 加载成功") Image:SetBrushFromTexture(TargetObject); end); end end end function BuffManager:GetBuffName(BuffType) return self.Config[BuffType].Name; end return BuffManager;