---@class SoldierBase ---@field Init fun() 初始化兵种信息 ---@field PlayerKey PlayerKey 拥有者的玩家 Key ---@field Skills table 拥有的技能 ---@field Features table 拥有的特性 ---@field SetPlayerKey fun(InPlayerKey:PlayerKey) 设置玩家 Key ---@field StartPlay fun() 正式开始游戏 ---@field EnableFeature fun(IsEnable:bool) 启用特性 ---@field TriggerSkill fun(InSkillId: EBuffType|nil, ...:vararg) 执行技能 ---@field StopTriggerSkill fun(InSkillId: EBuffType|nil) 停止执行 Skill ---@field Tick fun(DeltaTime: float) 每帧执行 ---@field HasActiveSkill fun() 是否有主动技能 ---@class SoldierBuffInfo ---@field StartTime float 当前使用的开始时间 ---@field EndTime float 当前使用的开始时间 ---@field IsDefault bool 是否是被动技能 ---@field BuffId int32 当前执行的 Buff ID require('Script.Global.Table.SoldierConfig') require('Script.Global.Table.ItemTable') -- 兵种类型基类 local SoldierBase = { ---@type PlayerKey PlayerKey = nil; ---@type table Skills = { }; ---@type table 当前的特性 Features = { [SoldierConfig.ESpecialType.Health ] = 1, -- 生命 [SoldierConfig.ESpecialType.Damage ] = 1, -- 伤害 [SoldierConfig.ESpecialType.Speed ] = 1, -- 移速 [SoldierConfig.ESpecialType.Poison ] = 1, -- 毒杀 [SoldierConfig.ESpecialType.Defence ] = 1, -- 防御值 }; ---@type ESoldierType 当前的兵种类型 SoldierType = 0; ---@type table 下一次可以使用 Buff 的时间,包含可以使用的 Buff BuffInfo = { }; }; --- 初始化,分为客户端和服务器,仅在最开始的时候设置一次;在设置兵种的时候进行初始化 ---@param InPlayerKey PlayerKey function SoldierBase:Init(InPlayerKey, InSoldierType) self.PlayerKey = InPlayerKey; self.SoldierType = InSoldierType; if UGCGameSystem.IsServer() then -- 检查当前数值是否都是 1,如果不是,就改变 local Feature = SoldierConfig.Features[self.SoldierType] for i, v in pairs(SoldierConfig.ESpecialType) do if Feature[v] ~= nil then self.Features[v] = Feature[v]; end end self:CheckSoldierType(); self:SendRPC(false, "Init", self.PlayerKey, self.SoldierType); else -- 执行对应操作 end end --- 检查兵种是否有可执行的技能,如果有,立刻执行;没有就返回 function SoldierBase:CheckSoldierType(InBuffInfo, InSkill) if self:HasAuthority() then self.BuffInfo = {}; self.Skills = {}; -- 检查是否含有被动 local Config = SoldierConfig.Skills[self.SoldierType]; if table.isEmpty(Config) then return; end local Skills = {}; local CurrTime = KismetSystemLibrary.GetGameTimeInSeconds(UGCGameSystem.GameState); for i, v in pairs(Config.Skills) do self.Skills[#self.Skills + 1] = v.BuffType; self.BuffInfo[v.BuffType] = { StartTime = CurrTime; EndTime = CurrTime; IsDefault = v.Cooldown == nil; BuffId = nil; -- 之后填充 }; if v.Cooldown == nil then self:TriggerSkill(); end end UGCLogSystem.LogTree("[SoldierBase:CheckSoldierType] self.BuffInfo = self.SoldierType = " .. tostring(self.SoldierType), self.BuffInfo); -- 发送到客户端 self:SendRPC(false, "CheckSoldierType", self.BuffInfo, self.Skills); else -- 如果没有的话通知没有技能,就不显示技能图标了 self.BuffInfo = InBuffInfo; self.Skills = InSkill; end end -- 正式开始游戏 function SoldierBase:StartPlay() if UGCGameSystem.IsServer() then self:EnableFeature(true); end end --- S & C function SoldierBase:HasSkill() return not table.isEmpty(self.BuffInfo); end --- 执行对应的技能 --- S ---@param InSkillId EBuffType|nil|table ---@vararg any|nil 传入技能是否会被改变的参数 function SoldierBase:TriggerSkill(InSkillId, ...) if table.isEmpty(self.Skills) then return false; end -- 检查是否存在当前技能 local UseSkills = {}; if InSkillId == nil then UseSkills = self.Skills; else if not table.hasValue(InSkillId) then return false; end UseSkills[#UseSkills + 1] = InSkillId; end -- 开始执行 local CanUseBuffs = {}; local CurrTime = KismetSystemLibrary.GetGameTimeInSeconds(UGCGameSystem.GameState); for Index, BuffType in pairs(UseSkills) do local Info = self.BuffInfo[BuffType]; if Info.EndTime <= CurrTime then CanUseBuffs[#CanUseBuffs + 1] = BuffType; end end if table.isEmpty(CanUseBuffs) then UGCLogSystem.Log("[SoldierBase:TriggerSkill] 可以使用的时间还未到。"); return CanUseBuffs; end local ConfigSkills = SoldierConfig.Skills[self.SoldierType]; if table.isEmpty(ConfigSkills) then return CanUseBuffs; end -- 依次执行技能 for Index, BuffType in pairs(CanUseBuffs) do -- 检查是否有参数,先使用默认参数 for i, v in pairs(ConfigSkills.Skills) do if BuffType ~= v.BuffType then return; end local BuffManager = BP_BuffManager.GetBuffManager(); local IsSucceed, BuffId = BuffManager:AddBuff(BuffType, self.PlayerKey, BuffType, table.unpackTable(v.Params)); if IsSucceed == BP_BuffManager.EResult.Succeed then self.BuffInfo[BuffType].BuffId = BuffId; if v.Cooldown == nil then self.BuffInfo[BuffType].IsDefault = true; else self.BuffInfo[BuffType].IsDefault = false; local ContinueTime = 0; if v.ContinueTime ~= nil and v.ContinueTime > 0 then ContinueTime = v.ContinueTime BuffManager:SetBuffDuration(BuffId, ContinueTime); end if v.Cooldown > 0 then self.BuffInfo[BuffType].StartTime = CurrTime; CurrTime = CurrTime + v.Cooldown + ContinueTime self.BuffInfo[BuffType].EndTime = CurrTime; end end self:PostTriggerSkill(BuffType, self.BuffInfo[BuffType]); -- 发送通知 else UGCLogSystem.LogError("[SoldierBase:TriggerSkill] 当前 %d:没有启动 BUFF: %d,报错信息:%s", self.PlayerKey, BuffType, IsSucceed); end end end end --- 触发技能之后执行的,子类进行继承,可以实现不同效果,父类默认发送给自身 function SoldierBase:PostTriggerSkill(InBuffType, InBuffInfo) local CurrTime = KismetSystemLibrary.GetGameTimeInSeconds(UGCGameSystem.GameState); UGCGameSystem.GameState:ClientSendEvent(nil, EventTypes.PlayerUseBuff, self.PlayerKey, InBuffType, InBuffInfo, CurrTime); UGCLogSystem.Log("[SoldierBase:PostTriggerSkill] PlayerKey = %s", tostring(self.PlayerKey)); self:SendRPC(true, "Client_TriggerSkill", self.PlayerKey, InBuffType, InBuffInfo); end ---@param InPlayerKey PlayerKey ---@param InBuffType EBuffType ---@param InBuffInfo table function SoldierBase:Client_TriggerSkill(InPlayerKey, InBuffType, InBuffInfo) -- 通知所有客户端xxx的xxx技能已经执行了 UGCLogSystem.Log("[SoldierBase:Client_TriggerSkill] %s 客户端触发技能:%s", tostring(InPlayerKey), tostring(InBuffType)); end --- 停止执行技能 ---@param InSkillId EBuffType|nil 传入对应 function SoldierBase:StopTriggerSkill(InSkillId) -- 直接移除即可 local BuffManager = BP_BuffManager.GetBuffManager(); local CurrTime = KismetSystemLibrary.GetGameTimeInSeconds(UGCGameSystem.GameState); if self.BuffInfo[InSkillId] == nil then return; end if self.BuffInfo[InSkillId].EndTime < CurrTime then BuffManager:RemoveBuffDuration(self.BuffInfo[InSkillId].BuffId); end end --- 启用特性 --- S ---@param IsEnable bool function SoldierBase:EnableFeature(IsEnable) local Pawn = UGCGameSystem.GetPlayerPawnByPlayerKey(self.PlayerKey); for i, v in pairs(self.Features) do if i == SoldierConfig.ESpecialType.Health then UGCPawnAttrSystem.SetHealthMax(Pawn, UGCPawnAttrSystem.GetHealthMax(Pawn) * v); UGCPawnAttrSystem.SetHealth(Pawn, UGCPawnAttrSystem.GetHealthMax(Pawn)); elseif i == SoldierConfig.ESpecialType.Speed then UGCPawnAttrSystem.SetSpeedScale(Pawn, UGCPawnAttrSystem.GetSpeedScale(self) * v); elseif i == SoldierConfig.ESpecialType.Strong then -- 设置对应数据即可 Pawn:SetParam('Strong', Pawn:GetParam('Strong') * v); elseif i == SoldierConfig.ESpecialType.Defence then Pawn:SetParam('Defence', Pawn:GetParam('Defence') * v); elseif i == SoldierConfig.ESpecialType.Poison then -- 设置毒圈属性 Pawn:SetParam('PoisonDefence', Pawn:GetParam('PoisonDefence') * v); end end end --- 停止特性 --- S function SoldierBase:UnEnableFeature() local Pawn = UGCGameSystem.GetPlayerPawnByPlayerKey(self.PlayerKey); for i, v in pairs(self.Features) do if i == SoldierConfig.ESpecialType.Health then UGCPawnAttrSystem.SetHealthMax(Pawn, UGCPawnAttrSystem.GetHealthMax(Pawn) / v); UGCPawnAttrSystem.SetHealth(Pawn, UGCPawnAttrSystem.GetHealthMax(Pawn)); elseif i == SoldierConfig.ESpecialType.Speed then UGCPawnAttrSystem.SetSpeedScale(Pawn, UGCPawnAttrSystem.GetSpeedScale(self) / v); elseif i == SoldierConfig.ESpecialType.Strong then -- 设置对应数据即可 Pawn:SetParam('Strong', Pawn:GetParam('Strong') / v); elseif i == SoldierConfig.ESpecialType.Defence then Pawn:SetParam('Defence', Pawn:GetParam('Defence') / v); -- 永久改变 elseif i == SoldierConfig.ESpecialType.Poison then -- 设置毒 Pawn:SetParam('PoisonDefence', Pawn:GetParam('PoisonDefence') / v); end end end --- 判断当前是否是服务器 function SoldierBase:HasAuthority() return UGCGameSystem.IsServer(); end ---@param IsMulti bool 是否是 mrpc ---@param FuncName string 函数名称 function SoldierBase:SendRPC(IsMulti, FuncName, ...) UGCGameSystem.GameState:SendSoldierRPC(IsMulti, FuncName, self.PlayerKey, self.SoldierType, ...) end --- tick 函数 ---@param DeltaTime float function SoldierBase:Tick(DeltaTime) end --- 获取兵种类型 ---@return ESoldierType function SoldierBase:GetSoldierType() return self.SoldierType; end --- 重置兵种信息 function SoldierBase:RestSoldier() local Curr = KismetSystemLibrary.GetGameTimeInSeconds(UGCGameSystem.GameState); for i, v in pairs(self.BuffInfo) do if v.BuffId ~= nil then -- 销毁 self:StopTriggerSkill(v.BuffId); end v.EndTime = Curr; v.StartTime = Curr; end end --- 是否有主动技能 function SoldierBase:HasActiveSkill() UGCLogSystem.LogTree("[SoldierBase:HasActiveSkill] self.BuffInfo = ", self.BuffInfo); for i, v in pairs(self.BuffInfo) do if v.IsDefault == false then return true; end end return false; end function SoldierBase:new() local newObj = SoldierBase self.__index = self; return setmetatable(newObj, self); end return SoldierBase;