2024-05-19 12:29:32 +00:00
|
|
|
|
using System.Threading.Tasks;
|
2024-05-22 14:39:41 +00:00
|
|
|
|
using ColdMint.scripts.debug;
|
2024-05-19 12:29:32 +00:00
|
|
|
|
using ColdMint.scripts.levelGraphEditor;
|
|
|
|
|
using ColdMint.scripts.map.dateBean;
|
|
|
|
|
using ColdMint.scripts.map.interfaces;
|
2024-05-20 14:38:41 +00:00
|
|
|
|
using ColdMint.scripts.map.room;
|
2024-05-22 14:39:41 +00:00
|
|
|
|
using ColdMint.scripts.utils;
|
2024-05-19 12:29:32 +00:00
|
|
|
|
using Godot;
|
|
|
|
|
|
|
|
|
|
namespace ColdMint.scripts.map.RoomPlacer;
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// <para>Patchwork room placement strategy</para>
|
|
|
|
|
/// <para>拼接的房间放置策略</para>
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <remarks>
|
|
|
|
|
///<para>Under this strategy, think of each room template as a puzzle piece, find their "slots", and then connect them together.</para>
|
|
|
|
|
///<para>在此策略下,将每个房间模板看作是一块拼图,找到他们的“槽”,然后将其连接在一起。</para>
|
|
|
|
|
/// </remarks>
|
|
|
|
|
public class PatchworkRoomPlacementStrategy : IRoomPlacementStrategy
|
|
|
|
|
{
|
2024-05-20 14:38:41 +00:00
|
|
|
|
private readonly Vector2 _halfCell = new Vector2(Config.CellSize / 2f, Config.CellSize / 2f);
|
|
|
|
|
|
|
|
|
|
public Task<bool> PlaceRoom(Node mapRoot, RoomPlacementData roomPlacementData)
|
2024-05-19 12:29:32 +00:00
|
|
|
|
{
|
2024-05-20 14:38:41 +00:00
|
|
|
|
if (roomPlacementData.Room == null || roomPlacementData.Position == null)
|
|
|
|
|
{
|
|
|
|
|
return Task.FromResult(false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (roomPlacementData.Room.RootNode == null)
|
|
|
|
|
{
|
|
|
|
|
return Task.FromResult(false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var rootNode = roomPlacementData.Room.RootNode;
|
2024-05-21 14:50:33 +00:00
|
|
|
|
mapRoot.AddChild(rootNode);
|
2024-05-20 14:38:41 +00:00
|
|
|
|
rootNode.Position = roomPlacementData.Position.Value;
|
|
|
|
|
return Task.FromResult(true);
|
2024-05-19 12:29:32 +00:00
|
|
|
|
}
|
|
|
|
|
|
2024-05-21 14:50:33 +00:00
|
|
|
|
public Task<RoomPlacementData?> CalculateNewRoomPlacementData(RandomNumberGenerator randomNumberGenerator,
|
|
|
|
|
Room? parentRoomNode,
|
|
|
|
|
RoomNodeData newRoomNodeData)
|
2024-05-19 12:29:32 +00:00
|
|
|
|
{
|
2024-05-20 14:38:41 +00:00
|
|
|
|
if (newRoomNodeData.RoomTemplateSet == null || newRoomNodeData.RoomTemplateSet.Length == 0)
|
|
|
|
|
{
|
|
|
|
|
return Task.FromResult<RoomPlacementData?>(null);
|
|
|
|
|
}
|
2024-05-19 12:29:32 +00:00
|
|
|
|
|
2024-05-20 14:38:41 +00:00
|
|
|
|
if (parentRoomNode == null)
|
|
|
|
|
{
|
2024-05-21 14:50:33 +00:00
|
|
|
|
return Task.FromResult<RoomPlacementData?>(null);
|
2024-05-20 14:38:41 +00:00
|
|
|
|
}
|
2024-05-22 14:39:41 +00:00
|
|
|
|
|
|
|
|
|
var roomResArray = RoomFactory.RoomTemplateSetToRoomRes(newRoomNodeData.RoomTemplateSet);
|
|
|
|
|
if (roomResArray.Length == 0)
|
|
|
|
|
{
|
|
|
|
|
return Task.FromResult<RoomPlacementData?>(null);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var roomSlots = parentRoomNode.RoomSlots;
|
|
|
|
|
if (roomSlots == null || roomSlots.Length == 0)
|
|
|
|
|
{
|
|
|
|
|
return Task.FromResult<RoomPlacementData?>(null);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//Matches unmatched slots.
|
|
|
|
|
//对未匹配的插槽进行匹配。
|
|
|
|
|
foreach (var roomRes in roomResArray)
|
|
|
|
|
{
|
|
|
|
|
var newRoom = RoomFactory.CreateRoom(roomRes);
|
|
|
|
|
if (newRoom == null)
|
|
|
|
|
{
|
|
|
|
|
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) continue;
|
|
|
|
|
if (mainRoomSlot == null || newRoomSlot == null)
|
|
|
|
|
{
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2024-05-23 12:52:52 +00:00
|
|
|
|
var position = CalculatedPosition(parentRoomNode, newRoom, mainRoomSlot, newRoomSlot, false)
|
2024-05-22 14:39:41 +00:00
|
|
|
|
.Result;
|
|
|
|
|
if (position == null) continue;
|
|
|
|
|
var roomPlacementData = new RoomPlacementData
|
|
|
|
|
{
|
|
|
|
|
Room = newRoom,
|
|
|
|
|
Position = position
|
|
|
|
|
};
|
|
|
|
|
return Task.FromResult<RoomPlacementData?>(roomPlacementData);
|
|
|
|
|
}
|
|
|
|
|
|
2024-05-21 14:50:33 +00:00
|
|
|
|
return Task.FromResult<RoomPlacementData?>(null);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public Task<RoomPlacementData?> CalculatePlacementDataForStartingRoom(RandomNumberGenerator randomNumberGenerator,
|
|
|
|
|
RoomNodeData startRoomNodeData)
|
|
|
|
|
{
|
|
|
|
|
if (startRoomNodeData.RoomTemplateSet == null || startRoomNodeData.RoomTemplateSet.Length == 0)
|
2024-05-20 14:38:41 +00:00
|
|
|
|
{
|
|
|
|
|
return Task.FromResult<RoomPlacementData?>(null);
|
|
|
|
|
}
|
2024-05-21 14:50:33 +00:00
|
|
|
|
|
|
|
|
|
var roomResArray = RoomFactory.RoomTemplateSetToRoomRes(startRoomNodeData.RoomTemplateSet);
|
|
|
|
|
if (roomResArray.Length == 0)
|
|
|
|
|
{
|
|
|
|
|
return Task.FromResult<RoomPlacementData?>(null);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var index = randomNumberGenerator.Randi() % roomResArray.Length;
|
|
|
|
|
var roomPlacementData = new RoomPlacementData
|
|
|
|
|
{
|
|
|
|
|
Room = RoomFactory.CreateRoom(roomResArray[index]),
|
|
|
|
|
Position = Vector2.Zero
|
|
|
|
|
};
|
|
|
|
|
return Task.FromResult<RoomPlacementData?>(roomPlacementData);
|
2024-05-20 14:38:41 +00:00
|
|
|
|
}
|
2024-05-19 12:29:32 +00:00
|
|
|
|
|
2024-05-21 14:50:33 +00:00
|
|
|
|
|
2024-05-22 14:39:41 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// <para>if it matches</para>
|
|
|
|
|
/// <para>是否匹配</para>
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="mainRoom"></param>
|
|
|
|
|
/// <param name="newRoom"></param>
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
public Task<bool> 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)
|
|
|
|
|
{
|
|
|
|
|
//如果已经匹配过了,就不再匹配
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
foreach (var newRoomSlot in newRoomSlots)
|
|
|
|
|
{
|
|
|
|
|
if (newRoomSlot == null)
|
|
|
|
|
{
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (newRoomSlot.Matched)
|
|
|
|
|
{
|
|
|
|
|
//如果已经匹配过了,就不再匹配
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LogCat.Log(distanceToMidpointOfRoom[0] + "-" + distanceToMidpointOfRoom[1] + "和" +
|
|
|
|
|
newDistanceToMidpointOfRoom[0] + "-" + newDistanceToMidpointOfRoom[1] + "匹配成功");
|
|
|
|
|
mainRoomSlot.Matched = true;
|
|
|
|
|
newRoomSlot.Matched = true;
|
|
|
|
|
outMainRoomSlot = mainRoomSlot;
|
|
|
|
|
outNewRoomSlot = newRoomSlot;
|
|
|
|
|
return Task.FromResult(true);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
outNewRoomSlot = null;
|
|
|
|
|
outMainRoomSlot = null;
|
|
|
|
|
return Task.FromResult(false);
|
|
|
|
|
}
|
|
|
|
|
|
2024-05-20 14:38:41 +00:00
|
|
|
|
private Task<Vector2?> CalculatedPosition(Room mainRoom, Room newRoom, RoomSlot? mainRoomSlot,
|
2024-05-21 14:50:33 +00:00
|
|
|
|
RoomSlot? newRoomSlot, bool roomSlotOverlap)
|
2024-05-19 12:29:32 +00:00
|
|
|
|
{
|
2024-05-22 14:39:41 +00:00
|
|
|
|
if (mainRoom.RootNode == null || newRoom.RootNode == null || newRoom.TileMap == null ||
|
|
|
|
|
mainRoom.TileMap == null ||
|
|
|
|
|
newRoom.TileMap == null || mainRoomSlot == null ||
|
2024-05-20 14:38:41 +00:00
|
|
|
|
newRoomSlot == null)
|
|
|
|
|
{
|
|
|
|
|
return Task.FromResult<Vector2?>(null);
|
|
|
|
|
}
|
|
|
|
|
|
2024-05-22 14:39:41 +00:00
|
|
|
|
//Main room slot location description
|
|
|
|
|
//主房间槽位置描述
|
|
|
|
|
var mainOrientationDescribe = mainRoomSlot.DistanceToMidpointOfRoom;
|
|
|
|
|
//New room slot location description
|
|
|
|
|
//新房间槽位置描述
|
|
|
|
|
var newOrientationDescribe = newRoomSlot.DistanceToMidpointOfRoom;
|
|
|
|
|
if (mainOrientationDescribe == null || newOrientationDescribe == null)
|
2024-05-20 14:38:41 +00:00
|
|
|
|
{
|
2024-05-22 14:39:41 +00:00
|
|
|
|
//If the room slot is described as null, null is returned
|
|
|
|
|
//若房间槽描述为null,那么返回null
|
|
|
|
|
return Task.FromResult<Vector2?>(null);
|
|
|
|
|
}
|
|
|
|
|
|
2024-05-23 12:52:52 +00:00
|
|
|
|
var mainRoomSlotPosition = mainRoom.TileMap.MapToLocal(mainRoomSlot.StartPosition);
|
|
|
|
|
var newRoomSlotPosition = newRoom.TileMap.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)
|
2024-05-22 14:39:41 +00:00
|
|
|
|
{
|
2024-05-23 12:52:52 +00:00
|
|
|
|
//如果不允许房间槽位重叠
|
|
|
|
|
//If room slot overlap is not allowed
|
|
|
|
|
if (mainRoomSlot.IsHorizontal)
|
2024-05-22 14:39:41 +00:00
|
|
|
|
{
|
2024-05-23 12:52:52 +00:00
|
|
|
|
//Horizontal slot, offset in the Y direction.
|
|
|
|
|
//水平方向槽,向Y方向偏移。
|
|
|
|
|
if (result.Y < 0)
|
|
|
|
|
{
|
|
|
|
|
result.Y += Config.CellSize;
|
|
|
|
|
}else
|
|
|
|
|
{
|
|
|
|
|
result.Y -= Config.CellSize;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
//Vertical slot, offset in the X direction.
|
|
|
|
|
//垂直方向槽向X方向偏移。
|
|
|
|
|
if (result.X < 0)
|
|
|
|
|
{
|
|
|
|
|
result.X -= Config.CellSize;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
result.X += Config.CellSize;
|
|
|
|
|
}
|
2024-05-22 14:39:41 +00:00
|
|
|
|
}
|
2024-05-20 14:38:41 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return Task.FromResult<Vector2?>(result);
|
2024-05-19 12:29:32 +00:00
|
|
|
|
}
|
|
|
|
|
}
|