378 lines
12 KiB
Lua
Raw Normal View History

2025-01-04 23:00:19 +08:00
local BuffDir = 'Script.Blueprint.SceneObj.Buff.'
---@class FBuffManager
---@field OwnerBuffs table<EBuffType, table<int32, BuffScriptItem>>
---@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<EBuffType, table<PlayerKey, BuffScriptItem>>
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<EBuffType, table<PlayerKey, UParticleSystemComponent>>
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;