---@class PoisonCircleSystem_C:AActor ---@field DefaultSceneRoot USceneComponent ---@field OuterClass UClass ---@field InnerClass UClass ---@field CenterActorClass UClass --Edit Below-- require("Script.Blueprint.XinHaoQuan.PoisonConfig") local PoisonCircleSystem = {}; PoisonCircleState = PoisonCircleState or { NON_START = 1, -- 未开始 PREPARE = 2, -- 准备开始,此时已经设置显示了 RUNNING = 3, -- 正在运行中 ENDED = 4, -- 结束了 }; --- 设置为未开始 PoisonCircleSystem.CurrState = PoisonCircleState.NON_START; PoisonCircleSystem.CachedState = PoisonCircleState.NON_START; --- 配置选项 PoisonCircleSystem.ConfigIndex = 1; ---@type table 存一份的配置 PoisonCircleSystem.Config = {}; --- 外圈索引 PoisonCircleSystem.OuterIndex = 1; --- 内圈索引 PoisonCircleSystem.InnerIndex = 2; ---@type BP_Poison_Outer_C 外层的 Actor PoisonCircleSystem.OuterActor = nil; ---@type BP_Poison_Inner_C 内层的 Actor PoisonCircleSystem.InnerActor = nil; ---@type table 中心点Actor 列表 PoisonCircleSystem.CenterActors = {}; --- 选择的中心点索引 PoisonCircleSystem.SelectCenterIndex = 0; --- 记录一下开始移动的起始时间 PoisonCircleSystem.StartRunningTime = 0; PoisonCircleSystem.CachedStartRunningTime = 0; --PoisonCircleSystem.ConfigTime = 0; function PoisonCircleSystem:ReceiveBeginPlay() self.SuperClass.ReceiveBeginPlay(self); --self:ShowCircle(false); UGCEventSystem.AddListener(EventTypes.ClientAlready, self.OnClientAlready, self); end ---@param InIndex int32 状态索引 ---@return string 状态名称 function PoisonCircleSystem:GetPoisonCircleStateName(InIndex) for i, v in pairs(PoisonCircleState) do if v == InIndex then return i; end end return ''; end function PoisonCircleSystem:ReceiveTick(DeltaTime) self.SuperClass.ReceiveTick(self, DeltaTime); local CurrTime = UGCGameSystem.GameState:GetServerTime(); if self.CachedState == PoisonCircleState.RUNNING then if CurrTime - self.CachedStartRunningTime >= self.Config[self.OuterIndex].MoveTime then --self:SetActorCenterAndRadius(self.InnerIndex + 1, self.InnerIndex, self.Config) if UGCGameSystem.IsServer() then self.OuterIndex = self.InnerIndex; self.InnerIndex = self.InnerIndex + 1; self:ChangeState(PoisonCircleState.PREPARE); self:StartPoison(); self:SetActorCenterAndRadius_Internal(self.OuterActor, self.Config, self.OuterIndex) self:SetActorCenterAndRadius_Internal(self.InnerActor, self.Config, self.InnerIndex) DOREPONCE(self, "OuterIndex"); DOREPONCE(self, "InnerIndex"); if self.Config[self.InnerIndex] == nil then self:ChangeState(PoisonCircleState.ENDED); end else self:SetActorCenterAndRadius_Internal(self.OuterActor, self.Config, self.OuterIndex + 1) self.CachedState = PoisonCircleState.PREPARE; if self.Config[self.InnerIndex + 1] == nil then self.CachedState = PoisonCircleState.ENDED; else 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("[PoisonCircleSystem:ReceiveTick] r = %s", tostring(r)) self:SetTheActorSize(self.OuterActor, r); end elseif self.CurrState == PoisonCircleState.PREPARE then end self:TakePoisonDamage(DeltaTime); end --[[ function PoisonCircleSystem:ReceiveEndPlay() self.SuperClass.ReceiveEndPlay(self); end --]] function PoisonCircleSystem:GetReplicatedProperties() return { "CurrState", "Lazy" } , { "OuterIndex", "Lazy" } , { "InnerIndex", "Lazy" } , { "ConfigIndex", "Lazy" } , { "StartRunningTime", "Lazy" } , { "Config", "Lazy" } --, { "ConfigTime", "Lazy" } end function PoisonCircleSystem:OnRep_StartRunningTime() UGCLogSystem.Log("[PoisonCircleSystem:OnRep_StartRunningTime] StartRunningTime = %s", tostring(self.StartRunningTime)); if self.StartRunningTime > 0 then self.CachedStartRunningTime = self.StartRunningTime; end end --[[ function PoisonCircleSystem:GetAvailableServerRPCs() return end --]] function PoisonCircleSystem:OnClientAlready() UGCLogSystem.Log("[PoisonCircleSystem:OnClientAlready] 111") if UGCGameSystem.IsServer() then if self.ConfigIndex == 0 then self.ConfigIndex = 1; DOREPONCE(self, "ConfigIndex"); end UGCLogSystem.Log("[PoisonCircleSystem:OnClientAlready] 执行") self:FillConfig(true); self:CheckPoisonConfig(); -- 设置到对应位置 else self:ShowCircle(false); end end --- function PoisonCircleSystem: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("[PoisonCircleSystem:StartPoison] OuterIndex = %d, InnerIndex = %d", self.OuterIndex, self.InnerIndex); local Delay = InDelay; if UGCGameSystem.IsServer() then self:FillConfig(self.OuterIndex == 1); 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; UGCLogSystem.Log("[PoisonCircleSystem:StartPoison] self.StartRunningTime = %s", tostring(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 UITool.ShowTips("安全区还有 %s 秒开始刷新", tostring(InDelay)); self.CachedStartRunningTime = Delay + InCurrTime; end UGCEventSystem.SetTimer(self, function() if UGCGameSystem.IsServer() then self:ChangeState(PoisonCircleState.RUNNING); else UITool.ShowTips("安全区开始刷新"); end end, Delay); end function PoisonCircleSystem:FindCenters() self.CenterActors = {}; local AllActors = {}; UE.FindActorsByClass(self.CenterActorClass, AllActors) UGCLogSystem.LogTree(string.format("[PoisonCircleSystem:FindCenters] AllActors ="), AllActors) 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("[PoisonCircleSystem:FindCenters] self.CenterActors ="), self.CenterActors) end --- 设置配置索引 function PoisonCircleSystem:SetConfigIndex(InIndex) self.ConfigIndex = InIndex; self:ResetPoison(true); self:CheckPoisonConfig(); DOREPONCE(self, "ConfigIndex"); if self.OuterActor then self.OuterActor:InitOuterPoison(); end end --- 重置毒圈系统 function PoisonCircleSystem:ResetPoison(IsForce) self:ChangeState(PoisonCircleState.NON_START); self.OuterIndex = 1; self.InnerIndex = 2; self:FillConfig(true); DOREPONCE(self, "OuterIndex"); DOREPONCE(self, "InnerIndex"); end function PoisonCircleSystem: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 PoisonCircleSystem:ChangeState(InState) self.CurrState = InState; self.CachedState = InState; UGCLogSystem.Log("[PoisonCircleSystem:ChangeState] CurrState = %s", self:GetPoisonCircleStateName(InState)); DOREPONCE(self, "CurrState"); end function PoisonCircleSystem:OnRep_CurrState() UGCLogSystem.Log("[PoisonCircleSystem: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 PoisonCircleSystem: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 PoisonCircleSystem: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 PoisonCircleSystem:SetTheActorLocation(InActor, InIndex) InActor:K2_SetActorLocation(InIndex); end ---@param InActor AActor ---@param InSize FVector function PoisonCircleSystem:SetTheActorSize(InActor, InSize) InActor:SetActorScale3D({ X = InSize, Y = InSize, Z = 1 }); end function PoisonCircleSystem:FillConfig(IsForce) if table.isEmpty(self.Config) then UGCLogSystem.Log("[PoisonCircleSystem:FillConfig] self.ConfigIndex = %s", tostring(self.ConfigIndex)) self.Config = TableHelper.DeepCopyTable(PoisonConfig.CircleConfig[self.ConfigIndex]); end if not IsForce or self:CheckPoisonCenter() then return; end if table.isEmpty(self.CenterActors) then self:FindCenters(); end local actor, index = table.random(self.CenterActors); UGCLogSystem.Log("[PoisonCircleSystem:FillConfig] Index = %s, actor = %s", tostring(index), tostring(actor)); self.SelectCenterIndex = index; PoisonConfig.SelectIndex = index; local FirstLocation = actor == nil and DefaultSettings.MapCenterLocation or 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 Item.Center == nil 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("[PoisonCircleSystem:FillConfig] self.Config ="), self.Config) DOREPONCE(self, "Config"); end ---@return bool 是否所有中心点都就绪 function PoisonCircleSystem:CheckPoisonCenter() for i, v in pairs(self.Config) do if table.isEmpty(v.Center) then return false; end end return true; end --- 检查一下配置 function PoisonCircleSystem: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 PoisonCircleSystem:Client_CheckPoisonConfig(InIndex, InConfig) self:SetActorCenterAndRadius(self.InnerIndex, self.OuterIndex, InConfig); end function PoisonCircleSystem: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 PoisonCircleSystem: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 PoisonCircleSystem:SetActorCenterAndRadius_Internal(InActor, InConfig, InIndex) self:SetTheActorLocation(InActor, VectorHelper.ToLuaTable(InConfig[InIndex].Center)); self:SetTheActorSize(InActor, InConfig[InIndex].Radius); end ---@return float function PoisonCircleSystem:GetPoisonRadius() return self.OuterActor:GetActorScale3D().X; end PoisonCircleSystem.CurrCountTime = 0; PoisonCircleSystem.ConstCurrCountTime = 0.75; --- 执行伤害 function PoisonCircleSystem:TakePoisonDamage(InDeltaTime) if self.CurrState == PoisonCircleState.NON_START then return; end if not UGCGameSystem.IsServer() then return end 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("[PoisonCircleSystem:TakePoisonDamage] Damage = %s", Damage); UGCGameSystem.ApplyDamage(v, Damage, nil, self, EDamageType.PoisonDamage); end end end end ---@return PoisonCircleState function PoisonCircleSystem:GetCurrState() return self.CurrState; end ---@return float function PoisonCircleSystem:GetPlayedTime() return UGCGameSystem.GameState:GetServerTime() - self.CachedStartRunningTime; end --- 获取刷新的时间,前面的是现在已经刷新的时候,后面的是总刷新时间 ---@return float, float function PoisonCircleSystem: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 PoisonCircleSystem: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 PoisonCircleSystem: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 PoisonCircleSystem:GetOuterActor() if self.OuterActor == nil then self:FindOuterInnerActor(); end return self.OuterActor; end function PoisonCircleSystem:GetInnerActor() if self.InnerActor == nil then self:FindOuterInnerActor(); end return self.InnerActor; end return PoisonCircleSystem;