---@class BP_PlaceModeManager_C:AActor ---@field PointToWidget UWidgetComponent ---@field DefaultSceneRoot USceneComponent require("Script.Blueprint.PlaceItems.PlacementModeConfig") --Edit Below-- local BP_PlaceModeManager = { -- 放置值 PlaceValue = 0; -- 当前已放置的物体 {{Type = uint, ID = uint, Pos = {XYZ}, Rot = {RPY}}, ...} PlacedItemCount = {}; -- 是否已开启放置模式 PlaceMode = PlacementModeConfig.EPlaceMode.None; -- 当前是否为使用场景摄像机进行放置物体 bIsSceneCamera = false; -- 所有放置物 {Type = {Actor*, ...}, ...} AllPlaceItems = {}; -- 客户端预览放置的Actor AllPreviewActor = {}; AddPreviewQuat = {X=0,Y=0,Z=0,W=1}; -- 所有可放置区间Actor AllPlaceableAreaActors = {}; -- 预导入的地图 [PlayerKey] = Code PreUsePlayerPlaceItemCode = {}; }; function BP_PlaceModeManager:GetReplicatedProperties() return "PlaceValue", "PlacedItemCount" end -- OnRep -------------------------------------------------------------------------------------------- function BP_PlaceModeManager:OnRep_PlaceValue() if self == nil then return end UGCEventSystem.SendEvent(EventEnum.UpdatePlaceValue) end function BP_PlaceModeManager:OnRep_PlacedItemCount() if self == nil then return end UGCEventSystem.SendEvent(EventEnum.UpdatePlacedItemCount, self.PlacedItemCount) end -- OnRep End ---------------------------------------------------------------------------------------- function BP_PlaceModeManager:ReceiveBeginPlay() self.SuperClass.ReceiveBeginPlay(self); UGCLogSystem.Log("[BP_PlaceModeManager_ReceiveBeginPlay]") self:UpdatePlaceableArea() if UGCGameSystem.IsServer() then self:InitAllPlaceItemsList() if PlacementModeConfig.PlacingAttr.IsObjectPoolPlace then self:SpawnPlaceItem() end else self:SetPointToWidgetVis(false) if PlacementModeConfig.IsPlaceMode() then self:SpawnPreviewItem() end end UGCLogSystem.Log("[BP_PlaceModeManager_ReceiveBeginPlay] Finish") end BP_PlaceModeManager.LastTraceTime = 0; BP_PlaceModeManager.LastRemoveTraceTime = 0; function BP_PlaceModeManager:ReceiveTick(DeltaTime) self.SuperClass.ReceiveTick(self, DeltaTime); if UGCGameSystem.IsServer() then if self.PlaceMode ~= PlacementModeConfig.EPlaceMode.None then self:TickUpdatePlaceValue() end else if self:IsPlaceMode() then self.LastTraceTime = self.LastTraceTime + DeltaTime if self.LastTraceTime > PlacementModeConfig.PlacingAttr.CheckPosTimeInterval then self.LastTraceTime = 0 -- 获取检测点位并预览 UGCLogSystem.Log("[BP_PlaceModeManager_ReceiveTick] CheckPlacePos") self:CheckPlacePos() end elseif self:IsRemoveMode() then self.LastRemoveTraceTime = self.LastRemoveTraceTime + DeltaTime if self.LastRemoveTraceTime > PlacementModeConfig.PlacingAttr.CheckRemoveItemTimeInterval then self.LastRemoveTraceTime = 0 -- 获取检测点位并预览 self:CheckRemoveItem() end end end end function BP_PlaceModeManager:ReceiveEndPlay() -- 销毁生成物 for i, v in pairs(self.AllPlaceItems) do if UE.IsValid(v) then v:K2_DestroyActor() end end for i, v in pairs(self.AllPreviewActor) do if UE.IsValid(v) then v:K2_DestroyActor() end end self.SuperClass.ReceiveEndPlay(self); end -- 生成函数 -------------------------------------------------------------------------------------------- --- 对象池生成放置物 function BP_PlaceModeManager:SpawnPlaceItem() for Type, v in pairs(PlacementModeConfig.ItemInfo) do self.AllPlaceItems[Type] = {} local ItemClass = UE.LoadClass(v.ItemPath) for i = 1, v.MaxCount do local PlaceItemInst = UGCGameSystem.SpawnActor(self, ItemClass, PlacementModeConfig.PlacingAttr.SpawnPos, VectorHelper.RotZero(), VectorHelper.ScaleOne()) PlaceItemInst:SetPlaceItemType(Type) self.AllPlaceItems[Type][i] = PlaceItemInst end end end --- 客户端生成预览物 function BP_PlaceModeManager:SpawnPreviewItem() for Type, v in pairs(PlacementModeConfig.ItemInfo) do self.AllPlaceItems[Type] = {} local ItemClass = UE.LoadClass(v.PreviewItemPath) self.AllPreviewActor[Type] = UGCGameSystem.SpawnActor(self, ItemClass, PlacementModeConfig.PlacingAttr.SpawnPos, VectorHelper.RotZero(), VectorHelper.ScaleOne()) end end --- 获取未使用的放置物 仅对象池生成模式有效 function BP_PlaceModeManager:GetUnPlaceItemFromItemType(PlaceItemType) if self.AllPlaceItems[PlaceItemType] then for i, v in pairs(self.AllPlaceItems[PlaceItemType]) do if not v:GetIsPlaced() then return v end end end return nil end -- 生成函数 End ---------------------------------------------------------------------------------------- --- 开启放置模式类型 ---@param InPlaceMode EPlaceMode function BP_PlaceModeManager:SetPlaceMode(InPlaceMode) if self.PlaceMode ~= InPlaceMode then self.PlaceMode = InPlaceMode UGCEventSystem.SendEvent(EventEnum.PlaceModeChange, self.PlaceMode) if self.PlaceMode == PlacementModeConfig.EPlaceMode.None then self:SetPlaceItemType(-1) self:SetPointToWidgetVis(false) elseif self.PlaceMode == PlacementModeConfig.EPlaceMode.PlaceMode then self:SetPlaceItemType(0) self:SetPointToWidgetVis(false) elseif self.PlaceMode == PlacementModeConfig.EPlaceMode.RemoveMode then self:SetPlaceItemType(-1) end end end function BP_PlaceModeManager:IsPlaceMode() return self.PlaceMode == PlacementModeConfig.EPlaceMode.PlaceMode end function BP_PlaceModeManager:IsRemoveMode() return self.PlaceMode == PlacementModeConfig.EPlaceMode.RemoveMode end function BP_PlaceModeManager:GetPlacementModeType() return self.PlaceMode end -- 客户端预览 放置和移除 ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- 检测放置位置 function BP_PlaceModeManager:CheckPlacePos() local PreviewActor = self:GetPreviewPlaceActor() if UE.IsValid(PreviewActor) then UGCLogSystem.Log("[BP_PlaceModeManager_CheckPlacePos]") self.TargetUnitPos, self.TargetUnitRot = self:GetPlacingPos() PreviewActor:PlaceItem(self.TargetUnitPos, self.TargetUnitRot) UGCLogSystem.Log("[BP_PlaceModeManager_CheckPlacePos] TargetUnitPos:%s, TargetUnitRot:%s", VectorHelper.ToString(self.TargetUnitPos), VectorHelper.RotToString(self.TargetUnitRot)) if self:ClientPlaceCondition() then PlacementModeConfig.SetCanPlace(true) else PlacementModeConfig.SetCanPlace(false) end else UGCLogSystem.LogError("[BP_PlaceModeManager_CheckPlacePos] PreviewActor:%s is nil.", tostring(self.PlaceItemType)) end end --- 获取本地摄像机控制器Actor function BP_PlaceModeManager:GetLocalCameraManager() if not UE.IsValid(self.CameraManager) then self.CameraManager = UGCSystemLibrary.GetLocalPlayerController().PlayerCameraManager end return self.CameraManager end --- Client --- 获取预放置的点位 function BP_PlaceModeManager:GetPlacingPos() if not UE.IsValid(self:GetLocalCameraManager()) then return end local StartPos = self.CameraManager:K2_GetActorLocation() local Dir = self.CameraManager:GetActorForwardVector() local EndPos = VectorHelper.Add(StartPos, VectorHelper.MulNumber(Dir, PlacementModeConfig.PlacingAttr.TraceDis)) --- HitResult:FHitResult local bSucceed, HitResult = TraceManager.LineTraceSingleForObjects(self, StartPos, EndPos, PlacementModeConfig.PlacingAttr.TraceObjType, {self:GetPreviewPlaceActor(), UGCSystemLibrary.GetLocalPlayerPawn()}, PlacementModeConfig.PlacingAttr.EnableDrawDebug) --local bSucceed, HitResult = TraceManager.SphereTraceSingleForObjects(self, StartPos, EndPos, 20, PlacementModeConfig.PlacingAttr.TraceObjType, {self:GetPreviewPlaceActor(), UGCSystemLibrary.GetLocalPlayerPawn()}, PlacementModeConfig.PlacingAttr.EnableDrawDebug) local ImpactPoint, ImpactNormal = HitResult.ImpactPoint, HitResult.ImpactNormal if not bSucceed then ImpactPoint = EndPos end local TargetRot local TargetPos = ImpactPoint local ImpactRot = KismetMathLibrary.MakeRotFromZ(ImpactNormal) local PreviewPlaceActor = self:GetPreviewPlaceActor() if not PreviewPlaceActor:GetIsRotateAlignment() then TargetRot = QuatHelper.QuatToRot(self.AddPreviewQuat) ImpactRot = VectorHelper.RotZero() local AllVertexPreviewRelativePos = PreviewPlaceActor:GetAllVertexPreviewRelativePos(TargetRot) -- UGCLogSystem.LogTree("[BP_PlaceModeManager_GetPlacingPos] AllVertexPreviewPos", AllVertexPreviewPos) --if math.abs(ImpactNormal.Z - 1) > 0.01 and VectorHelper.Length2D(ImpactNormal) > 0.01 then -- -- 只计算平面的距离 -- ImpactNormal.Z = 0 -- -- local MaxDis = 0; -- for i, v in pairs(AllVertexPreviewRelativePos) do -- local Dir2D = VectorHelper.MulNumber2D(v, -1) -- local Dis2D = VectorHelper.Length2D(Dir2D) -- --UGCLogSystem.Log("[BP_PlaceModeManager_GetPlacingPos] Dis2D:%s", tostring(Dis2D)) -- local CosVal = VectorHelper.CosineValue(Dir2D, ImpactNormal) -- if CosVal > 0.1 then -- MaxDis = math.max(MaxDis, CosVal * Dis2D) -- end -- end -- --UGCLogSystem.Log("[BP_PlaceModeManager_GetPlacingPos] MaxDis:%s", tostring(MaxDis)) -- local UnitVectorImpactPoint = VectorHelper.MulNumber(ImpactNormal, 1. / VectorHelper.Length2D(ImpactNormal)) -- TargetPos = VectorHelper.Add(ImpactPoint, VectorHelper.MulNumber(UnitVectorImpactPoint, MaxDis)) --end if math.abs(ImpactNormal.Z - 1) > 0.01 and VectorHelper.Length2D(ImpactNormal) > 0.01 then local MaxDis = 0; for i, v in pairs(AllVertexPreviewRelativePos) do local PreDir = VectorHelper.MulNumber(v, -1) local PreDis = VectorHelper.Length(PreDir) local CosVal = VectorHelper.CosineValue(PreDir, ImpactNormal) if CosVal > 0.1 then MaxDis = math.max(MaxDis, CosVal * PreDis) end end local UnitVectorImpactPoint = VectorHelper.MulNumber(ImpactNormal, 1. / VectorHelper.Length2D(ImpactNormal)) TargetPos = VectorHelper.Add(ImpactPoint, VectorHelper.MulNumber(UnitVectorImpactPoint, MaxDis)) end else TargetRot = QuatHelper.QuatToRot(QuatHelper.mul(QuatHelper.RotToQuat(ImpactRot), self.AddPreviewQuat)) end return PlacementModeConfig.VectorToUnitVector(TargetPos), PlacementModeConfig.RotatorToUnitRotator(TargetRot) end --- Client --- 获取指向的放置Actor function BP_PlaceModeManager:CheckRemoveItem() if not UE.IsValid(self:GetLocalCameraManager()) then return end local StartPos = self.CameraManager:K2_GetActorLocation() local Dir = self.CameraManager:GetActorForwardVector() local EndPos = VectorHelper.Add(StartPos, VectorHelper.MulNumber(Dir, PlacementModeConfig.PlacingAttr.TraceRemoveDis)) local bSucceed, HitResult = TraceManager.LineTraceSingleForObjects(self, StartPos, EndPos, PlacementModeConfig.PlacingAttr.RemoveTraceObjType, {UGCSystemLibrary.GetLocalPlayerPawn()}, PlacementModeConfig.PlacingAttr.EnableDrawDebug) if bSucceed then local HitActor = HitResult.Actor:Get() -- 判断标签或者类型 if HitActor:ActorHasTag(PlacementModeConfig.PlaceItemTag) or UE.IsA(HitActor, PlacementModeConfig.GetPlaceItemBaseClass()) then self:SetPointToWidgetVis(true) local PointPos if PlacementModeConfig.ItemInfo[HitActor:GetPlaceItemType()].RemovePointToActor then PointPos = HitActor:K2_GetActorLocation() else local Origin,BoxExtent = HitActor:GetActorBounds() PointPos = Origin end self:SetPointToWidgetPos(PointPos) self.TargetRemoveItem = HitActor return HitActor end end self:SetPointToWidgetVis(false) self.TargetRemoveItem = nil return nil end --- Client --- 设置指向UI的显示 function BP_PlaceModeManager:SetPointToWidgetVis(IsShow) if IsShow then self.PointToWidget:GetUserWidgetObject():SetVisibility(ESlateVisibility.SelfHitTestInvisible) else self.PointToWidget:GetUserWidgetObject():SetVisibility(ESlateVisibility.Collapsed) end end function BP_PlaceModeManager:SetPointToWidgetPos(InPos) self.PointToWidget:K2_SetWorldLocation(InPos) end --- 获取当前预览放置物的Actor function BP_PlaceModeManager:GetPreviewPlaceActor() return self.AllPreviewActor[self.PlaceItemType] end --- 重置预览Actor的位置 function BP_PlaceModeManager:ResetPreviewActorPos() local PreviewActor = self:GetPreviewPlaceActor() if UE.IsValid(PreviewActor) then PreviewActor:K2_SetActorLocation(PlacementModeConfig.PlacingAttr.SpawnPos) end end --- 客户端设置当前预览放置物的类型 function BP_PlaceModeManager:SetPlaceItemType(InPlaceItemType) self:ResetPreviewActorPos() self.PlaceItemType = InPlaceItemType UGCEventSystem.SendEvent(EventEnum.PlaceItemTypeIsChange, self.PlaceItemType) end function BP_PlaceModeManager:GetPlaceItemType() return self.PlaceItemType end --- 设置旋转值 function BP_PlaceModeManager:SetAddPreviewQuat(InYaw) self.AddPreviewQuat = QuatHelper.RotToQuat({Roll = 0, Pitch = 0, Yaw = InYaw}) end --- 重置旋转 function BP_PlaceModeManager:ResetAddPreviewQuat() self.AddPreviewQuat = QuatHelper.RotToQuat({Roll = 0, Pitch = 0, Yaw = 0}) end -- 客户端预览 放置和移除 End ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- 放置 ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- 客户端判断放置条件 ---@return bool function BP_PlaceModeManager:ClientPlaceCondition() if self:GetPreviewPlaceActor():IsOverlappingPlayer() then return false end local Pos = PlacementModeConfig.UnitVectorToVector(self.TargetUnitPos) local LineOfDefense = PlacementModeConfig.GetSimpleLineOfDefense() if UE.IsValid(LineOfDefense) then local LineOfDefensePos = LineOfDefense:K2_GetActorLocation() if math.abs(LineOfDefensePos.Z - Pos.Z) < PlacementModeConfig.PlacingAttr.LineOfDefenseHeight and VectorHelper.Length2D(VectorHelper.Sub2D(LineOfDefensePos, Pos)) < PlacementModeConfig.PlacingAttr.LineOfDefenseRadius then return false end end for i, v in pairs(self.AllPlaceableAreaActors) do if v:InPlaceableArea(Pos) then return true end end return false end --- 获取玩家可放置某物体的数量 --function BP_PlaceModeManager:GetPlayerPlaceItemCount(PlayerKey, InItemType) -- local ItemIncrement = ArchiveDataConfig.GetPlayerArchiveDataFromType(PlayerKey, ArchiveDataConfig.EArchiveType.ItemIncrement) -- local ItemInfo = PlacementModeConfig.ItemInfo[InItemType] -- if ItemIncrement and ItemIncrement[InItemType] then -- return math.clamp(ItemIncrement[InItemType] + ItemInfo.InitialCount, 0, ItemInfo.MaxCount) -- end -- return ItemInfo.InitialCount --end --- 获取单人放置模式时的玩家PC function BP_PlaceModeManager:GetSimplePlayerPC() return UGCGameSystem.GetAllPlayerController()[1] end --- 服务器判断放置条件 ---@return bool function BP_PlaceModeManager:ServerPlaceCondition(InPlayerKey, InPlaceItemType, UnitPos, IgnoreQuantityLimit) -- 舒服进行数量限制判断 if IgnoreQuantityLimit ~= true then -- 放置量满足 if PlacementModeConfig.ItemInfo[InPlaceItemType].Cost + self:GetPlaceValue() > PlacementModeConfig.GetPlaceMaxValue(InPlayerKey) then if PlacementModeConfig.IsPlaceMode() then UGCSendRPCSystem.RPCEvent(InPlayerKey, EventEnum.AddTip, TipConfig.TipType.PlaceFailure, PlacementModeConfig.EPlaceCallback.InsufficientPlaceValue) end UGCLogSystem.LogError("[BP_PlaceModeManager_ServerPlaceCondition] 放置量不满足 目标放置量:%s, 玩家的可放置量:%s", tostring(PlacementModeConfig.ItemInfo[InPlaceItemType].Cost + self:GetPlaceValue()), tostring(PlacementModeConfig.GetPlaceMaxValue(InPlayerKey))) return false end -- 满足放置数量 if #self.AllPlaceItems[InPlaceItemType] >= PlacementModeConfig.GetPlayerPlaceItemCount(InPlayerKey, InPlaceItemType) then if PlacementModeConfig.IsPlaceMode() then UGCSendRPCSystem.RPCEvent(InPlayerKey, EventEnum.AddTip, TipConfig.TipType.PlaceFailure, PlacementModeConfig.EPlaceCallback.InsufficientNumberOfItem) end UGCLogSystem.LogError("[BP_PlaceModeManager_ServerPlaceCondition] 物体可放置数量不满足 当前该问题数量:%s, 玩家的物体最大放置量:%s", tostring(#self.AllPlaceItems[InPlaceItemType]), tostring(#self.AllPlaceItems[InPlaceItemType])) return false end end if not PlacementModeConfig.IsPlaceMode() then return true end -- 下方仅为放置模式才做的判断 ----------------------------------------------------------------------------------------------------- local IsInArea = false local Pos = PlacementModeConfig.UnitVectorToVector(UnitPos) -- 判断是否在防线范围内 local LineOfDefense = PlacementModeConfig.GetSimpleLineOfDefense() if UE.IsValid(LineOfDefense) then local LineOfDefensePos = LineOfDefense:K2_GetActorLocation() if math.abs(LineOfDefensePos.Z - Pos.Z) < PlacementModeConfig.PlacingAttr.LineOfDefenseHeight and VectorHelper.Length2D(VectorHelper.Sub2D(LineOfDefensePos, Pos)) < PlacementModeConfig.PlacingAttr.LineOfDefenseRadius then UGCSendRPCSystem.RPCEvent(InPlayerKey, EventEnum.AddTip, TipConfig.TipType.PlaceFailure, PlacementModeConfig.EPlaceCallback.LocationNotWithinRange) return false end end -- 判断是否在可放置范围内 for i, v in pairs(self.AllPlaceableAreaActors) do if v:InPlaceableArea(Pos) then IsInArea = true break end end if not IsInArea then UGCSendRPCSystem.RPCEvent(InPlayerKey, EventEnum.AddTip, TipConfig.TipType.PlaceFailure, PlacementModeConfig.EPlaceCallback.LocationNotWithinRange) return false end return true end --- 客户端调用放置操作 function BP_PlaceModeManager:ClientPlace() if self:ClientPlaceCondition() then UGCSendRPCSystem.ActorRPCNotify(nil, self, "ServerPlace", UGCSystemLibrary.GetLocalPlayerKey(), self.PlaceItemType, self.TargetUnitPos, self.TargetUnitRot) else -- Tip放置失败的提示 UGCEventSystem.SendEvent(EventEnum.AddTip, TipConfig.TipType.PlaceFailure, PlacementModeConfig.EPlaceCallback.LocationNotWithinRange) end end --- 服务器调用放置操作 ---@param IgnoreQuantityLimit 忽略数量限制 function BP_PlaceModeManager:ServerPlace(InPlayerKey, InPlaceItemType, InTargetUnitPos, InTargetUnitRot, IgnoreQuantityLimit) UGCLogSystem.Log("[BP_PlaceModeManager_ServerPlace] InPlayerKey:%s, InPlaceItemType:%s", tostring(InPlayerKey), tostring(InPlaceItemType)) self.PlaceItemType, self.TargetUnitPos, self.TargetUnitRot = InPlaceItemType, InTargetUnitPos, InTargetUnitRot if self:ServerPlaceCondition(InPlayerKey, self.PlaceItemType, self.TargetUnitPos, IgnoreQuantityLimit) then if PlacementModeConfig.PlacingAttr.IsObjectPoolPlace then local UnPlaceItem = self:GetUnPlaceItemFromItemType(self.PlaceItemType) if UE.IsValid(UnPlaceItem) then UnPlaceItem:PlaceItem(self.TargetUnitPos, self.TargetUnitRot) UGCLogSystem.Log("[BP_PlaceModeManager_ServerPlace] Succeed 1111") end else if self.AllPlaceItems[self.PlaceItemType] == nil then self.AllPlaceItems[self.PlaceItemType] = {} end local ItemPath = PlacementModeConfig.ItemInfo[self.PlaceItemType].ItemPath local ItemClass = UE.LoadClass(ItemPath) local PlaceItemInst = UGCGameSystem.SpawnActor(self, ItemClass, PlacementModeConfig.UnitVectorToVector(self.TargetUnitPos), PlacementModeConfig.UnitRotatorToRotator(self.TargetUnitRot), VectorHelper.ScaleOne()) PlaceItemInst:PlaceItem(self.TargetUnitPos, self.TargetUnitRot) self.AllPlaceItems[self.PlaceItemType][#self.AllPlaceItems[self.PlaceItemType] + 1] = PlaceItemInst UGCLogSystem.Log("[BP_PlaceModeManager_ServerPlace] Succeed 2222") end self:AddPlacedItem(self.PlaceItemType) end end -- 放置End ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- 移除 ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- 客户端判断移除的条件 ---@return bool function BP_PlaceModeManager:ClientRemoveItemCondition() return UE.IsValid(self.TargetRemoveItem) end --- 服务器判断移除条件 ---@return bool function BP_PlaceModeManager:ServerRemoveItemCondition(InRemoveItem) -- 如果是对象池生成模式还需要判断该物体上方是否有玩家 不然服务器会崩溃 -- Warning: [LuaException] OnLogLuaStack: Assertion failed:!bCurrentBaseHadBeenStatic -- LogLinux: Error: [GIsGuarded:1]appError called: Assertion failed: !bCurrentBaseHadBeenStatic [File:D:\CG026\Survive\Source\ShadowTrackerExtra\Character\STCharacterMovementComponent.cpp] [Line: 4383] -- !!!!!!!!!!!!!!!!!!!!!!!!! return UE.IsA(InRemoveItem, PlacementModeConfig.GetPlaceItemBaseClass()) end function BP_PlaceModeManager:ClientRemoveItem() if self:ClientRemoveItemCondition() then UGCSendRPCSystem.ActorRPCNotify(nil, self, "ServerRemoveItem", self.TargetRemoveItem) else UGCLogSystem.LogError("[BP_PlaceModeManager_ClientRemoveItem]") end end function BP_PlaceModeManager:ServerRemoveItem(InRemoveItem) if self:ServerRemoveItemCondition(InRemoveItem) then local ItemType = InRemoveItem:GetPlaceItemType() self:RemovePlacedItem(ItemType) if PlacementModeConfig.PlacingAttr.IsObjectPoolPlace == false then table.removeValue(self.AllPlaceItems[ItemType], InRemoveItem, true) end InRemoveItem:RecoveryItem() else UGCLogSystem.LogError("[BP_PlaceModeManager_ServerRemoveItem]") end end function BP_PlaceModeManager:RemoveItemFromType(InItemType) if self.AllPlaceItems[InItemType] then for i = #self.AllPlaceItems[InItemType], 1, -1 do self:ServerRemoveItem(self.AllPlaceItems[InItemType][i]) end end end function BP_PlaceModeManager:ClearItem() UGCLogSystem.Log("[BP_PlaceModeManager_ClearItem]") for i, v in pairs(self.AllPlaceItems) do for _, ItemInst in pairs(v) do ItemInst:RecoveryItem() end end if PlacementModeConfig.PlacingAttr.IsObjectPoolPlace == false then self:InitAllPlaceItemsList() end self.PlacedItemCount = {} self:UpdatePlaceValue() end function BP_PlaceModeManager:InitAllPlaceItemsList() for i, v in pairs(EPlaceItemType) do self.AllPlaceItems[v] = {} end end -- 移除End ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- 放置量参数设置 ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- 增加放置量 function BP_PlaceModeManager:AddPlacedItem(PlaceItemType) if self.PlacedItemCount[PlaceItemType] == nil then self.PlacedItemCount[PlaceItemType] = 0 end self.PlacedItemCount[PlaceItemType] = self.PlacedItemCount[PlaceItemType] + 1 self:UpdatePlaceValue() end --- 减少放置量 function BP_PlaceModeManager:RemovePlacedItem(PlaceItemType) self.PlacedItemCount[PlaceItemType] = self.PlacedItemCount[PlaceItemType] - 1 if self.PlacedItemCount[PlaceItemType] < 0 then UGCLogSystem.LogError("[BP_PlaceModeManager_RemovePlacedItem] PlaceItemType:%s 数量小于0!!!!!!!!!!", tostring(PlaceItemType)) self.PlacedItemCount[PlaceItemType] = 0 end self:UpdatePlaceValue() end function BP_PlaceModeManager:GetPlacedItemNum(PlaceItemType) if self.PlacedItemCount[PlaceItemType] then return self.PlacedItemCount[PlaceItemType] end return 0 end function BP_PlaceModeManager:UpdatePlaceValue() self.PlaceValue = 0 for i, v in pairs(self.PlacedItemCount) do self.PlaceValue = self.PlaceValue + PlacementModeConfig.ItemInfo[i].Cost * v end self.bPlaceValueIsChange = true end function BP_PlaceModeManager:ClientUpdatePlaceValue(InPlaceValue, InPlacedItemCount) self.PlaceValue = InPlaceValue self.PlacedItemCount = InPlacedItemCount UGCEventSystem.SendEvent(EventEnum.UpdatePlaceValue) UGCEventSystem.SendEvent(EventEnum.UpdatePlacedItemCount, self.PlacedItemCount) end function BP_PlaceModeManager:GetPlaceValue() return self.PlaceValue end BP_PlaceModeManager.LastNotifyPlaceValueTime = 0 BP_PlaceModeManager.NotifyTimeInterval = 1 function BP_PlaceModeManager:TickUpdatePlaceValue(DeltaTime) self.LastNotifyPlaceValueTime = self.LastNotifyPlaceValueTime + DeltaTime if self.bPlaceValueIsChange and self.LastNotifyPlaceValueTime > self.NotifyTimeInterval then self.LastNotifyPlaceValueTime = 0; self.bPlaceValueIsChange = false UGCSendRPCSystem.ActorRPCNotify(nil, self, "ClientUpdatePlaceValue", self.PlaceValue, self.PlacedItemCount) end end -- 放置量参数设置 End ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- 导出/导入放置代码 ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- 导出 function BP_PlaceModeManager:ExportPlacementCode() return PlacementModeConfig.PlaceCodeEncode(self:GetNowMap()) end -- 导入 ---@param IgnoreQuantityLimit 忽略数量限制 为true则忽略 function BP_PlaceModeManager:ImportPlacementCode(InPlayerKey, InCode, IgnoreQuantityLimit) if not UGCGameSystem.IsServer() then return end if PlacementModeConfig.IsPlaceMode() then UGCGameSystem.SendModeCustomEvent("ResetAllPlayers") end local DecodeResType, MapType, PlaceItemInfo = PlacementModeConfig.PlaceCodeDecode(InCode) if PlacementModeConfig.IsPlaceMode() then UGCSendRPCSystem.RPCEvent(nil, EventEnum.ImportPlacementCallBack, DecodeResType) end if DecodeResType == PlacementModeConfig.EPlaceDecodeCallback.Succeed then UGCLogSystem.LogTree("[BP_PlaceModeManager_ImportPlacementCode] PlaceItemInfo:", PlaceItemInfo) self:ClearItem() self:LoadMap(MapType) -- PlaceItemInfo:PlaceItemType, UnitPos, UnitRot for i, v in pairs(PlaceItemInfo) do self:ServerPlace(InPlayerKey, v.PlaceItemType, v.UnitPos, v.UnitRot, IgnoreQuantityLimit) end end end --- 强行加载玩家预放置的信息 function BP_PlaceModeManager:ForcefullyImportMap(InPlayerKey) if self.PreUsePlayerPlaceItemCode[InPlayerKey] then -- 强行导入 self:ImportPlacementCode(InPlayerKey, self.PreUsePlayerPlaceItemCode[InPlayerKey], true) -- 重置 self.PreUsePlayerPlaceItemCode[InPlayerKey] = nil end end --- 预导入地图 function BP_PlaceModeManager:PreImportPlacementCode(InPlayerKey, InCode) local bCanUse, PlaceCount, DecodeResType, PlaceItemInfo = PlacementModeConfig.CheckPlayerCanUsePlaceCode(InCode, InPlayerKey) if DecodeResType == PlacementModeConfig.EPlaceDecodeCallback.Succeed then if bCanUse then self:ImportPlacementCode(InPlayerKey, InCode) else self.PreUsePlayerPlaceItemCode[InPlayerKey] = InCode UGCSendRPCSystem.ActorRPCNotify(InPlayerKey, self, "ShowForcefullyImportSecondaryConfirmation") end else if PlacementModeConfig.IsPlaceMode() then UGCSendRPCSystem.RPCEvent(nil, EventEnum.ImportPlacementCallBack, DecodeResType) end end end --- 给玩家二次确认是否要强行导入不可用的地图,只能在放置模式下使用 function BP_PlaceModeManager:ShowForcefullyImportSecondaryConfirmation() local SecondaryConfirmationWidget = WidgetManager:GetPanel(WidgetConfig.EUIType.SecondaryConfirmation) SecondaryConfirmationWidget:SetTextInfo("该地图已超出您的使用限制,即使保存也无法正常使用。是否强行导入该地图?", "取消", "强行导入") SecondaryConfirmationWidget:BindConfirmCallBack(function() UGCSendRPCSystem.ActorRPCNotify(nil, self, "ForcefullyImportMap", UGCSystemLibrary.GetLocalPlayerKey()) end) WidgetManager:ShowPanel(WidgetConfig.EUIType.SecondaryConfirmation, false) end function BP_PlaceModeManager:ImportPlacementCodeCallBack(InDecodeResType) UGCEventSystem.SendEvent(EventEnum.AddTip, InDecodeResType) end --- 加载已保存的放置地图 function BP_PlaceModeManager:LoadSavedPlaceMap(LoadType, Index) UGCLogSystem.Log("[BP_PlaceModeManager_LoadSavedPlaceMap] LoadType:%s, Index:%s", tostring(LoadType), tostring(Index)) if UGCGameSystem.IsServer() then if LoadType == PlacementModeConfig.LoadPlaceItemsType.SavedMap then local SavedMapList = ArchiveDataConfig.GetPlayerArchiveDataFromType(UGCSystemLibrary.GetLocalPlayerKey(), ArchiveDataConfig.EArchiveType.SavedMap) if SavedMapList and SavedMapList[Index] then self:ImportPlacementCode(self:GetSimplePlayerPC().PlayerKey, SavedMapList[Index].MapCode) end elseif LoadType == PlacementModeConfig.LoadPlaceItemsType.None then self:ClearItem() elseif LoadType == PlacementModeConfig.LoadPlaceItemsType.Default then self:ImportPlacementCode(self:GetSimplePlayerPC().PlayerKey, PlacementModeConfig.MapInfo[self:GetNowMap()].DefaultPlaceCode) end else UGCSendRPCSystem.ActorRPCNotify(nil, self, "LoadSavedPlaceMap", LoadType, Index) end end -- 导出/导入放置代码 End ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- 地图 ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- function BP_PlaceModeManager:GetNowMap() return PlacementModeConfig.PlaceMapType.RelicDefenseLine end function BP_PlaceModeManager:LoadMap(InMapType) end --- 更新可放置位置Actor function BP_PlaceModeManager:UpdatePlaceableArea() if self.PlaceableAreaClass == nil then self.PlaceableAreaClass = UE.LoadClass(PlacementModeConfig.PlaceableAreaPath) end if self.PlaceableAreaClass then self.AllPlaceableAreaActors = GameplayStatics.GetAllActorsOfClass(UGCGameSystem.GameState, self.PlaceableAreaClass) end end -- 地图 End ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- return BP_PlaceModeManager;