using System; using System.Collections.Generic; using System.Threading.Tasks; using ColdMint.scripts.debug; using ColdMint.scripts.levelGraphEditor; using ColdMint.scripts.map.dateBean; using ColdMint.scripts.map.interfaces; using ColdMint.scripts.map.room; using ColdMint.scripts.utils; using Godot; namespace ColdMint.scripts.map.RoomPlacer; /// /// Patchwork room placement strategy /// 拼接的房间放置策略 /// /// ///Under this strategy, think of each room template as a puzzle piece, find their "slots", and then connect them together. ///在此策略下,将每个房间模板看作是一块拼图,找到他们的“槽”,然后将其连接在一起。 /// public class PatchworkRoomPlacementStrategy : IRoomPlacementStrategy { /// /// We use a temporary area to measure whether the rooms overlap /// 我们使用一个临时区域进行测量房间是否重叠 /// private Area2D? _measuringArea2D; private CollisionShape2D? _measuringCollisionShape2D; private Area2D? _selfArea2D; /// /// How many rooms overlap with the new rooms that will be placed /// 有多少个房间与将要放置的新房间重叠 /// private int _overlapQuantity; public Task StartGeneration(Node mapRoot) { if (_measuringArea2D == null) { _measuringArea2D = new Area2D(); _measuringArea2D.Monitoring = true; _measuringArea2D.AreaEntered += body => { if (_selfArea2D != null && body == _selfArea2D) { return; } //Room overlap detected //检测到房间重叠 _overlapQuantity++; }; _measuringArea2D.AreaExited += body => { if (_selfArea2D != null && body == _selfArea2D) { return; } //Rooms no longer overlap //房间不再重叠 _overlapQuantity--; }; mapRoot.AddChild(_measuringArea2D); } if (_measuringCollisionShape2D == null) { _measuringCollisionShape2D = new CollisionShape2D(); _measuringArea2D.AddChild(_measuringCollisionShape2D); } return Task.FromResult(true); } public Task GeneratedComplete(Node mapRoot) { if (_measuringCollisionShape2D != null) { _measuringCollisionShape2D?.QueueFree(); _measuringArea2D?.RemoveChild(_measuringCollisionShape2D); _measuringCollisionShape2D = null; } if (_measuringArea2D != null) { _measuringArea2D?.QueueFree(); mapRoot.RemoveChild(_measuringArea2D); _measuringArea2D = null; } return Task.CompletedTask; } public Task PlaceRoom(Node mapRoot, RoomPlacementData roomPlacementData) { if (roomPlacementData.NewRoom == null || roomPlacementData.Position == null) { return Task.FromResult(false); } if (roomPlacementData.NewRoom.RootNode == null) { return Task.FromResult(false); } var newRootRootNode = roomPlacementData.NewRoom.RootNode; mapRoot.AddChild(newRootRootNode); newRootRootNode.Position = roomPlacementData.Position.Value; //Place navigation Link //放置导航Link Vector2? navigationLink2DStartPosition = null; if (roomPlacementData is { ParentRoom: not null, ParentRoomSlot: not null }) { var parentRoomTileMap = roomPlacementData.ParentRoom.GetTileMapLayer(Config.TileMapLayerName.Ground); var parentRoomRootNode = roomPlacementData.ParentRoom.RootNode; if (parentRoomTileMap != null && parentRoomRootNode != null) { navigationLink2DStartPosition = parentRoomRootNode.Position + parentRoomTileMap.MapToLocal(roomPlacementData.ParentRoomSlot .EndPosition); } } Vector2? navigationLink2DEndPosition = null; if (roomPlacementData.NewRoomSlot != null) { var newRoomTileMap = roomPlacementData.NewRoom.GetTileMapLayer(Config.TileMapLayerName.Ground); if (newRoomTileMap != null) { navigationLink2DEndPosition = newRootRootNode.Position + newRoomTileMap.MapToLocal(roomPlacementData.NewRoomSlot.EndPosition); } } if (navigationLink2DStartPosition != null && navigationLink2DEndPosition != null) { var navigationLink2D = new NavigationLink2D(); navigationLink2D.StartPosition = navigationLink2DStartPosition.Value; navigationLink2D.EndPosition = navigationLink2DEndPosition.Value; mapRoot.AddChild(navigationLink2D); } return Task.FromResult(true); } public async Task CalculateNewRoomPlacementData(RandomNumberGenerator randomNumberGenerator, Room? parentRoomNode, RoomNodeData newRoomNodeData) { if (newRoomNodeData.RoomTemplateSet == null || newRoomNodeData.RoomTemplateSet.Length == 0) { LogCat.LogWithFormat("new_room_node_data_is_incomplete", label: LogCat.LogLabel.PatchworkRoomPlacementStrategy, newRoomNodeData.Id); return null; } if (parentRoomNode == null) { LogCat.LogWithFormat("parent_room_does_not_exist", label: LogCat.LogLabel.PatchworkRoomPlacementStrategy, newRoomNodeData.Id); return null; } var roomResArray = RoomFactory.RoomTemplateSetToRoomRes(newRoomNodeData.RoomTemplateSet); if (roomResArray.Length == 0) { LogCat.LogWithFormat("room_template_does_not_exist", label: LogCat.LogLabel.PatchworkRoomPlacementStrategy, newRoomNodeData.Id); return null; } var roomSlots = parentRoomNode.RoomSlots; if (roomSlots == null || roomSlots.Length == 0) { LogCat.LogWithFormat("room_slot_does_not_exist", label: LogCat.LogLabel.PatchworkRoomPlacementStrategy, newRoomNodeData.Id); return null; } //Saves all data in the room template that matches the parent room. //保存房间模板内所有与父房间匹配的数据。 var usableRoomPlacementData = new List(); foreach (var roomRes in roomResArray) { var newRoom = RoomFactory.CreateRoom(roomRes, newRoomNodeData.EnterRoomEventHandlerId, newRoomNodeData.ExitRoomEventHandlerId); if (newRoom == null) { LogCat.LogWithFormat("room_corresponding_to_resource_is_null", label: LogCat.LogLabel.PatchworkRoomPlacementStrategy, newRoomNodeData.Id, roomRes); continue; } //Create a room, try to use the room slot to match the existing room slot. //创建了一个房间,尝试使用房间的槽与现有的房间槽匹配。 if (!IsMatch(parentRoomNode, newRoom, out var mainRoomSlot, out var newRoomSlot).Result) { LogCat.LogWithFormat("slots_do_not_match", label: LogCat.LogLabel.PatchworkRoomPlacementStrategy, newRoomNodeData.Id); continue; } if (mainRoomSlot == null || newRoomSlot == null) { LogCat.LogWithFormat("slot_incomplete", label: LogCat.LogLabel.PatchworkRoomPlacementStrategy, newRoomNodeData.Id); continue; } var position = await CalculatedPosition(parentRoomNode, newRoom, mainRoomSlot, newRoomSlot, false); if (position == null) { LogCat.LogWithFormat("placement_position_is_empty", label: LogCat.LogLabel.PatchworkRoomPlacementStrategy, newRoomNodeData.Id); continue; } var roomPlacementData = new RoomPlacementData { NewRoom = newRoom, ParentRoom = parentRoomNode, Position = position, ParentRoomSlot = mainRoomSlot, NewRoomSlot = newRoomSlot }; usableRoomPlacementData.Add(roomPlacementData); } if (usableRoomPlacementData.Count == 0) { LogCat.LogWithFormat("available_rooms_are_not_available", label: LogCat.LogLabel.PatchworkRoomPlacementStrategy, newRoomNodeData.Id); return null; } else { var index = randomNumberGenerator.Randi() % usableRoomPlacementData.Count; var roomPlacementData = usableRoomPlacementData[(int)index]; return roomPlacementData; } } public Task CalculatePlacementDataForStartingRoom(RandomNumberGenerator randomNumberGenerator, RoomNodeData startRoomNodeData) { if (startRoomNodeData.RoomTemplateSet == null || startRoomNodeData.RoomTemplateSet.Length == 0) { return Task.FromResult(null); } var roomResArray = RoomFactory.RoomTemplateSetToRoomRes(startRoomNodeData.RoomTemplateSet); if (roomResArray.Length == 0) { return Task.FromResult(null); } var index = randomNumberGenerator.Randi() % roomResArray.Length; var roomPlacementData = new RoomPlacementData { NewRoom = RoomFactory.CreateRoom(roomResArray[index], startRoomNodeData.EnterRoomEventHandlerId, startRoomNodeData.ExitRoomEventHandlerId), Position = Vector2.Zero }; return Task.FromResult(roomPlacementData); } /// /// if it matches /// 是否匹配 /// /// /// /// /// /// public Task IsMatch(Room? mainRoom, Room newRoom, out RoomSlot? outMainRoomSlot, out RoomSlot? outNewRoomSlot) { if (mainRoom == null) { outNewRoomSlot = null; outMainRoomSlot = null; return Task.FromResult(false); } var roomSlots = mainRoom.RoomSlots; if (roomSlots == null) { outNewRoomSlot = null; outMainRoomSlot = null; return Task.FromResult(false); } var newRoomSlots = newRoom.RoomSlots; if (newRoomSlots == null) { outNewRoomSlot = null; outMainRoomSlot = null; return Task.FromResult(false); } foreach (var mainRoomSlot in roomSlots) { if (mainRoomSlot == null || mainRoomSlot.Matched) { //If it's already a match, it's no longer a match //如果已经匹配过了,就不再匹配 continue; } foreach (var newRoomSlot in newRoomSlots) { if (newRoomSlot == null) { continue; } if (newRoomSlot.Matched) { //If it's already a match, it's no longer a match //如果已经匹配过了,就不再匹配 continue; } if (mainRoomSlot.IsHorizontal != newRoomSlot.IsHorizontal) { continue; } if (mainRoomSlot.Length != newRoomSlot.Length) { continue; } var distanceToMidpointOfRoom = mainRoomSlot.DistanceToMidpointOfRoom; var newDistanceToMidpointOfRoom = newRoomSlot.DistanceToMidpointOfRoom; if (distanceToMidpointOfRoom == null || newDistanceToMidpointOfRoom == null) { continue; } if (distanceToMidpointOfRoom[0] == newDistanceToMidpointOfRoom[0] && distanceToMidpointOfRoom[1] == newDistanceToMidpointOfRoom[1]) { continue; } outMainRoomSlot = mainRoomSlot; outNewRoomSlot = newRoomSlot; return Task.FromResult(true); } } outNewRoomSlot = null; outMainRoomSlot = null; return Task.FromResult(false); } /// /// Calculate room position /// 计算房间位置 /// /// ///Main room ///主房间 /// /// ///New room ///新房间 /// /// ///Main room slot ///主房间插槽 /// /// ///New room slot ///新房间插槽 /// /// ///Whether room slots allow overlays ///房间插槽是否允许覆盖 /// /// private async Task CalculatedPosition(Room mainRoom, Room newRoom, RoomSlot? mainRoomSlot, RoomSlot? newRoomSlot, bool roomSlotOverlap) { var mainRoomTileMapLayer = mainRoom.GetTileMapLayer(Config.TileMapLayerName.Ground); var newRoomTileMapLayer = newRoom.GetTileMapLayer(Config.TileMapLayerName.Ground); if (mainRoom.RootNode == null || newRoom.RootNode == null || mainRoomTileMapLayer == null || newRoomTileMapLayer == null || mainRoomSlot == null || newRoomSlot == null) { LogCat.Log("parameter_inconsistency", label: LogCat.LogLabel.PatchworkRoomPlacementStrategy); return null; } //Main room slot location description //主房间槽位置描述 var mainOrientationDescribe = mainRoomSlot.DistanceToMidpointOfRoom; //New room slot location description //新房间槽位置描述 var newOrientationDescribe = newRoomSlot.DistanceToMidpointOfRoom; if (mainOrientationDescribe == null || newOrientationDescribe == null) { //If the room slot is described as null, null is returned //若房间槽描述为null,那么返回null LogCat.Log("room_slot_position_is_empty", label: LogCat.LogLabel.PatchworkRoomPlacementStrategy); return null; } var mainRoomSlotPosition = mainRoomTileMapLayer .MapToLocal(mainRoomSlot.StartPosition); var newRoomSlotPosition = newRoomTileMapLayer .MapToLocal(newRoomSlot.StartPosition); //Get the vector from the new room slot to the main room slot //得到从新房间槽位到主房间槽位的向量 var newToMain = mainRoomSlotPosition - newRoomSlotPosition; var result = newToMain + mainRoom.RootNode.Position; if (!roomSlotOverlap) { //如果不允许房间槽位重叠 //If room slot overlap is not allowed if (mainRoomSlot.IsHorizontal) { //Horizontal slot, offset in the Y direction. //水平方向槽,向Y方向偏移。 if (newOrientationDescribe[1] == CoordinateUtils.OrientationDescribe.Up) { result.Y += Config.CellSize; } else { result.Y -= Config.CellSize; } } else { //Vertical slot, offset in the X direction. //垂直方向槽向X方向偏移。 if (newOrientationDescribe[0] == CoordinateUtils.OrientationDescribe.Right) { result.X -= Config.CellSize; } else { result.X += Config.CellSize; } } } //Do calculations overlap with other rooms? //计算结果是否与其他房间重叠? if (newRoom.RoomCollisionShape2D != null && _measuringArea2D != null && _measuringCollisionShape2D != null) { //Ignore yourself when detecting room overlap //检测房间重叠时应忽略自身 _selfArea2D = newRoom.RoomArea2D; _measuringArea2D.Position = result; _measuringCollisionShape2D.Shape = newRoom.RoomCollisionShape2D.Shape; //Calculate the offset of the shape. //计算形状的偏移量。 _measuringCollisionShape2D.Position = newRoom.RoomCollisionShape2D.Shape.GetRect().Size / 2; await Task.Delay(TimeSpan.FromMilliseconds(50)); if (_overlapQuantity > 0) { LogCat.Log("room_overlap", label: LogCat.LogLabel.PatchworkRoomPlacementStrategy); return null; } } return result; } }