---@class BP_PoisonSystem_C:AActor ---@field DefaultSceneRoot USceneComponent ---@field InnerClass UClass ---@field OuterClass UClass ---@field CenterActorClass UClass --Edit Below-- require("Script.Blueprint.XinHaoQuan.PoisonConfig") local BP_PoisonSystem = {}; PoisonCircleState = PoisonCircleState or { NON_START = 1, -- 未开始 PREPARE = 2, -- 准备开始,此时已经设置显示了 RUNNING = 3, -- 正在运行中 ENDED = 4, -- 结束了 }; --- 设置为未开始 BP_PoisonSystem.CurrState = PoisonCircleState.NON_START; BP_PoisonSystem.CachedState = PoisonCircleState.NON_START; --- 配置选项 BP_PoisonSystem.ConfigIndex = 1; ---@type table 存一份的配置 BP_PoisonSystem.Config = {}; --- 外圈索引 BP_PoisonSystem.OuterIndex = 1; --- 内圈索引 BP_PoisonSystem.InnerIndex = 2; ---@type BP_Poison_Outer_C 外层的 Actor BP_PoisonSystem.OuterActor = nil; ---@type BP_Poison_Inner_C 内层的 Actor BP_PoisonSystem.InnerActor = nil; ---@type table 中心点Actor 列表 BP_PoisonSystem.CenterActors = {}; --- 选择的中心点索引 BP_PoisonSystem.SelectCenterIndex = 0; --- 记录一下开始移动的起始时间 BP_PoisonSystem.StartRunningTime = 0; BP_PoisonSystem.CachedStartRunningTime = 0; function BP_PoisonSystem:ReceiveBeginPlay() self.SuperClass.ReceiveBeginPlay(self); UGCLogSystem.Log("[BP_PoisonSystem:ReceiveBeginPlay] 执行") UGCEventSystem.AddListener(EventTypes.MapLoadFinish, self.OnClientAlready, self); end ---@param InIndex int32 状态索引 ---@return string 状态名称 function BP_PoisonSystem:GetPoisonCircleStateName(InIndex) for i, v in pairs(PoisonCircleState) do if v == InIndex then return i; end end return ''; end function BP_PoisonSystem:ReceiveTick(DeltaTime) self.SuperClass.ReceiveTick(self, DeltaTime); local CurrTime = UGCGameSystem.GameState:GetServerTime(); if CurrTime == nil then return end if self.CachedState == PoisonCircleState.RUNNING or self.CurrState == PoisonCircleState.RUNNING then if self.StartRunningTime > 0 then local MoveTime = self.Config[self.OuterIndex].MoveTime if CurrTime - self.StartRunningTime >= MoveTime then if UGCGameSystem.IsServer() then self.OuterIndex = self.InnerIndex; if self.InnerIndex < table.getCount(self.Config) then self.InnerIndex = self.InnerIndex + 1; self:ChangeState(PoisonCircleState.PREPARE); DOREPONCE(self, "InnerIndex"); self:SetActorCenterAndRadius_Internal(self.InnerActor, self.Config, self.InnerIndex) else self:ChangeState(PoisonCircleState.ENDED); end self:SetActorCenterAndRadius_Internal(self.OuterActor, self.Config, self.OuterIndex) DOREPONCE(self, "OuterIndex"); self:StartPoison(); else self:SetActorCenterAndRadius_Internal(self.OuterActor, self.Config, self.OuterIndex + 1) if self.Config[self.InnerIndex + 1] == nil then self.CachedState = PoisonCircleState.ENDED; else self.CachedState = PoisonCircleState.PREPARE; self:SetActorCenterAndRadius_Internal(self.InnerActor, self.Config, self.InnerIndex + 1) end end else local TimeInterval = CurrTime - self.CachedStartRunningTime; local Rate = TimeInterval / self.Config[self.OuterIndex].MoveTime local TargetVec = VectorHelper.Sub(self.Config[self.InnerIndex].Center, self.Config[self.OuterIndex].Center) local TargetLoc = VectorHelper.Add(self.Config[self.OuterIndex].Center, VectorHelper.MulNumber(TargetVec, Rate)) self:FindOuterInnerActor(); self.OuterActor:K2_SetActorLocation(TargetLoc); local r = ((self.Config[self.InnerIndex].Radius - self.Config[self.OuterIndex].Radius) * Rate) + self.Config[self.OuterIndex].Radius --UGCLogSystem.Log("[BP_PoisonSystem:ReceiveTick] r = %s", tostring(r)) self:SetTheActorSize(self.OuterActor, r); end end elseif self.CurrState == PoisonCircleState.PREPARE then end self:TakePoisonDamage(DeltaTime); end --[[ function BP_PoisonSystem:ReceiveEndPlay() self.SuperClass.ReceiveEndPlay(self); end --]] function BP_PoisonSystem:GetReplicatedProperties() return { "CurrState", "Lazy" } , { "OuterIndex", "Lazy" } , { "InnerIndex", "Lazy" } , { "ConfigIndex", "Lazy" } , { "StartRunningTime", "Lazy" } , { "Config", "Lazy" } end function BP_PoisonSystem:OnRep_StartRunningTime() UGCLogSystem.Log("[BP_PoisonSystem:OnRep_StartRunningTime] StartRunningTime = %s", tostring(self.StartRunningTime)); if self.StartRunningTime > 0 then self.CachedStartRunningTime = self.StartRunningTime; end end --[[ function BP_PoisonSystem:GetAvailableServerRPCs() return end --]] function BP_PoisonSystem:OnClientAlready() UGCLogSystem.Log("[BP_PoisonSystem:OnClientAlready] 执行") if UGCGameSystem.IsServer() then if self.ConfigIndex == 0 then self.ConfigIndex = 1; DOREPONCE(self, "ConfigIndex"); end self:FillConfig(true); self:CheckPoisonConfig(); -- 设置到对应位置 --UE.RespawnAllPlayer(2); else self:ShowCircle(false); end end --- function BP_PoisonSystem:StartPoison(InDelay, InCurrTime) if self.CurrState == PoisonCircleState.ENDED then return end if self.CurrState == PoisonCircleState.NON_START then self:ChangeState(PoisonCircleState.PREPARE); end -- 显示出来 UGCLogSystem.Log("[BP_PoisonSystem:StartPoison] OuterIndex = %d, InnerIndex = %d", self.OuterIndex, self.InnerIndex); local Delay = InDelay; if UGCGameSystem.IsServer() then if self.OuterIndex == 1 then self:FillConfig(false); end Delay = self.Config[self.OuterIndex].Delay; if self.Config[self.InnerIndex] == nil then self.InnerIndex = self.OuterIndex; end local CurrTime = UGCGameSystem.GameState:GetServerTime() self.StartRunningTime = CurrTime + Delay; self.CachedStartRunningTime = self.StartRunningTime; DOREPONCE(self, "StartRunningTime"); if self.OuterIndex == self.InnerIndex then self:ChangeState(PoisonCircleState.ENDED); return ; else UnrealNetwork.CallUnrealRPC_Multicast(self, "StartPoison", Delay, CurrTime); end else if UGCGameSystem.GameState.RoundInfo.RoundState <= RoundState.InRound then UITool.ShowTips("安全区还有 %s 秒开始刷新", tostring(InDelay)); end self.CachedStartRunningTime = InDelay + InCurrTime; end UGCEventSystem.SetTimer(self, function() if UGCGameSystem.IsServer() then self:ChangeState(PoisonCircleState.RUNNING); else if UGCGameSystem.GameState.RoundInfo.RoundState == RoundState.InRound then UITool.ShowTips("安全区开始刷新"); end end end, Delay); end function BP_PoisonSystem:FindCenters() self.CenterActors = {}; UE.FindActorsByClass(self.CenterActorClass, self.CenterActors, function(InIndex, InActor) if InActor:ActorHasTag("Poison") then return InActor.Index; end return false; end) UGCLogSystem.LogTree(string.format("[BP_PoisonSystem:FindCenters] self.CenterActors ="), self.CenterActors) end --- 设置配置索引 function BP_PoisonSystem:SetConfigIndex(InIndex) self.ConfigIndex = InIndex; self:ResetPoison(true); self:CheckPoisonConfig(); DOREPONCE(self, "ConfigIndex"); --self:FindOuterInnerActor(); if self.OuterActor then self.OuterActor:InitOuterPoison(); end end --- 重置毒圈系统 function BP_PoisonSystem:ResetPoison(IsForce) self:ChangeState(PoisonCircleState.NON_START); self.OuterIndex = 1; self.InnerIndex = 2; self.CachedState = PoisonCircleState.NON_START; self.SelectCenterIndex = 0; PoisonConfig.SelectIndex = -1; self:FillConfig(true); DOREPONCE(self, "OuterIndex"); DOREPONCE(self, "InnerIndex"); end function BP_PoisonSystem:OnRep_InnerIndex() if table.isEmpty(self.Config) then return ; end if self.InnerActor == nil then return end self:SetActorCenterAndRadius_Internal(self.InnerActor, self.Config, self.InnerIndex); end function BP_PoisonSystem:OnRep_OuterIndex() if table.isEmpty(self.Config) then return ; end if self.OuterActor == nil then return end self:SetActorCenterAndRadius_Internal(self.OuterActor, self.Config, self.OuterIndex); end function BP_PoisonSystem:ChangeState(InState) self.CurrState = InState; self.CachedState = InState; UGCLogSystem.Log("[BP_PoisonSystem:ChangeState] CurrState = %s", self:GetPoisonCircleStateName(InState)); DOREPONCE(self, "CurrState"); end function BP_PoisonSystem:OnRep_CurrState() UGCLogSystem.Log("[BP_PoisonSystem:OnRep_CurrState] CurrState = %d", self.CurrState); if self.CurrState == PoisonCircleState.ENDED then self:SetActorCenterAndRadius(#self.Config, #self.Config, self.Config); end self.CachedState = self.CurrState; self:ShowCircle(self.CachedState ~= PoisonCircleState.NON_START); end --- 服务器找到即可,到时候在类里面进行设置 function BP_PoisonSystem:FindOuterInnerActor() if self.InnerActor == nil then self.InnerActor = UE.FindActorByClass(self.InnerClass); end if self.OuterActor == nil then self.OuterActor = UE.FindActorByClass(self.OuterClass); end end function BP_PoisonSystem:ShowCircle(IsShow) --if self.OuterActor then self.OuterActor:SetShowCircle(IsShow); end --if self.InnerActor then self.InnerActor:SetShowCircle(IsShow); end end ---@param InActor AActor ---@param InIndex int32 function BP_PoisonSystem:SetTheActorLocation(InActor, InIndex) InActor:K2_SetActorLocation(InIndex); end ---@param InActor AActor ---@param InSize FVector function BP_PoisonSystem:SetTheActorSize(InActor, InSize) InActor:SetActorScale3D({ X = InSize, Y = InSize, Z = 1 }); end --- 随机选择中心点 function BP_PoisonSystem:RandomSelectCenter() if table.isEmpty(self.CenterActors) then self:FindCenters(); end local TotalCount = table.getCount(self.CenterActors) if TotalCount == 0 then return ; end self.SelectCenterIndex = math.random(TotalCount); PoisonConfig.SelectIndex = self.SelectCenterIndex; UGCLogSystem.Log("[BP_PoisonSystem:RandomSelectCenter] self.SelectCenterIndex = %d", self.SelectCenterIndex); -- 通知可以重生玩家了 end ---@param IsForce bool 是否是强制重置 function BP_PoisonSystem:FillConfig(IsForce) if table.isEmpty(self.Config) then UGCLogSystem.Log("[BP_PoisonSystem:FillConfig] self.ConfigIndex = %s", tostring(self.ConfigIndex)) self.Config = TableHelper.DeepCopyTable(PoisonConfig.CircleConfig[self.ConfigIndex]); end if IsForce == false and (self:CheckPoisonCenter() == true) then return ; end if table.isEmpty(self.CenterActors) then self:FindCenters(); end if self.SelectCenterIndex == 0 then self:RandomSelectCenter(); end if self.SelectCenterIndex == 0 then return ; end local Actor = self.CenterActors[self.SelectCenterIndex]; UGCLogSystem.Log("[BP_PoisonSystem:FillConfig] Index = %s, Actor = %s", tostring(self.SelectCenterIndex), UE.GetName(Actor)); if Actor == nil then return ; end local FirstLocation = Actor:K2_GetActorLocation(); local BigRadius = 0; for i = 1, table.getCount(self.Config) do local Item = self.Config[i]; if Item == nil then return ; end if IsForce or table.isEmpty(Item.Center) then if i == 1 then Item.Center = VectorHelper.ToLuaTable(FirstLocation); else Item.Center = math.randomCircleInCircle(FirstLocation, BigRadius * 100, Item.Radius * 100); end end FirstLocation = VectorHelper.ToLuaTable(Item.Center); BigRadius = Item.Radius; end UGCLogSystem.LogTree(string.format("[BP_PoisonSystem:FillConfig] self.Config ="), self.Config) DOREPONCE(self, "Config"); end ---@return bool 是否所有中心点都就绪 function BP_PoisonSystem:CheckPoisonCenter() for i, v in pairs(self.Config) do if table.isEmpty(v.Center) then return false; end end return true; end --- 检查一下配置 function BP_PoisonSystem:CheckPoisonConfig(InConfigIndex, InConfig) if UGCGameSystem.IsServer() then for i, v in pairs(UGCGameSystem.GetAllPlayerController()) do UnrealNetwork.CallUnrealRPC(v, self, "Client_CheckPoisonConfig", self.ConfigIndex, self.Config); end DOREPONCE(self, "Config"); end self:SetActorCenterAndRadius(self.InnerIndex, self.OuterIndex, self.Config); end function BP_PoisonSystem:Client_CheckPoisonConfig(InIndex, InConfig) self:SetActorCenterAndRadius(self.InnerIndex, self.OuterIndex, InConfig); end function BP_PoisonSystem:OnRep_Config() if table.isEmpty(self.Config) then return end PoisonConfig.CircleConfig[self.ConfigIndex] = self.Config; self:Client_CheckPoisonConfig(self.ConfigIndex, self.Config); end function BP_PoisonSystem:SetActorCenterAndRadius(InInner, InOuter, InConfig) if table.isEmpty(InConfig) then return ; end self:FindOuterInnerActor(); self:SetActorCenterAndRadius_Internal(self.InnerActor, InConfig, InInner - (InConfig[InInner] and 0 or 1)); self:SetActorCenterAndRadius_Internal(self.OuterActor, InConfig, InOuter); end ---@private function BP_PoisonSystem:SetActorCenterAndRadius_Internal(InActor, InConfig, InIndex) self:FindOuterInnerActor(); local Item = InConfig[InIndex] if table.isEmpty(Item) or table.isEmpty(Item.Center) then if table.isEmpty(Item.Center) then UGCLogSystem.Log("[BP_PoisonSystem:SetActorCenterAndRadius_Internal] Center 此时为空") end return ; end UGCLogSystem.Log("[BP_PoisonSystem:SetActorCenterAndRadius_Internal] Item = %s", VectorHelper.ToString(Item.Center)); local Center = VectorHelper.ToLuaTable(Item.Center); UGCLogSystem.LogTree(string.format("[BP_PoisonSystem:SetActorCenterAndRadius_Internal] Center ="), Center) self:SetTheActorLocation(InActor, Center); self:SetTheActorSize(InActor, Item.Radius); end ---@return float function BP_PoisonSystem:GetPoisonRadius() return self.OuterActor:GetActorScale3D().X; end BP_PoisonSystem.CurrCountTime = 0; BP_PoisonSystem.ConstCurrCountTime = 0.75; --- 执行伤害 function BP_PoisonSystem:TakePoisonDamage(InDeltaTime) if self.CurrState == PoisonCircleState.NON_START then return ; end if not UGCGameSystem.IsServer() then return end -- 检查是否存在 Outer Actor if self.OuterActor ~= nil and UE.IsValid(self.OuterActor) then self.CurrCountTime = self.CurrCountTime + InDeltaTime; if self.CurrCountTime >= self.ConstCurrCountTime then self.CurrCountTime = self.CurrCountTime - self.ConstCurrCountTime; local Radius = self:GetPoisonRadius(); for i, v in pairs(UGCGameSystem.GetAllPlayerPawn()) do local Dis = VectorHelper.GetActorDis2D(self.OuterActor, v); if UE.IsValid(v) and v:IsAlive() and Dis > Radius * 100 then local Damage = self.Config[self.OuterIndex].Damage * self.ConstCurrCountTime; UGCLogSystem.Log("[BP_PoisonSystem:TakePoisonDamage] Damage = %s", Damage); UGCGameSystem.ApplyDamage(v, Damage, nil, self, EDamageType.PoisonDamage); end end end else -- 加载 Outer self:FindOuterInnerActor(); end end ---@return PoisonCircleState function BP_PoisonSystem:GetCurrState() return self.CurrState; end ---@return float function BP_PoisonSystem:GetPlayedTime() return UGCGameSystem.GameState:GetServerTime() - self.CachedStartRunningTime; end --- 获取刷新的时间,前面的是现在已经刷新的时候,后面的是总刷新时间 ---@return float, float function BP_PoisonSystem:GetRefreshTime() local MoveTime = self.Config[self.OuterIndex].MoveTime; local PlayedTime = self:GetPlayedTime(); return PlayedTime > MoveTime and MoveTime or PlayedTime, MoveTime; end --- 获取在圈内的所有玩家 ---@param InTable table out ---@return table function BP_PoisonSystem:GetPawnsInCircle(InTable) if InTable == nil then InTable = {}; end for i, v in pairs(UGCGameSystem.GetAllPlayerPawn()) do if v:IsAlive() then if VectorHelper.GetActorDis2D(v, self.OuterActor) <= self:GetPoisonRadius() then table.insert(InTable, v); end end end return InTable; end --- 获取在圈外的所有玩家 ---@param InTable table out ---@return table function BP_PoisonSystem:GetPawnsOutCircle(InTable) if InTable == nil then InTable = {}; end for i, v in pairs(UGCGameSystem.GetAllPlayerPawn()) do if v:IsAlive() then if VectorHelper.GetActorDis2D(v, self.OuterActor) > self:GetPoisonRadius() then table.insert(InTable, v); end end end return InTable; end function BP_PoisonSystem:GetOuterActor() if self.OuterActor == nil then self:FindOuterInnerActor(); end return self.OuterActor; end function BP_PoisonSystem:GetInnerActor() if self.InnerActor == nil then self:FindOuterInnerActor(); end return self.InnerActor; end return BP_PoisonSystem;