public DoorInstance(OrthogonalLine doorLine, Vector2Int facingDirection, RoomBase connectedRoom, RoomInstance connectedRoomInstance)
 {
     this.doorLine              = doorLine;
     this.facingDirection       = facingDirection;
     this.connectedRoom         = connectedRoom;
     this.connectedRoomInstance = connectedRoomInstance;
     this.isHorizontal          = FacingDirection == Vector2Int.up || FacingDirection == Vector2Int.down;
 }
 private static List <Vector3Int> GetTilesInsideOutline(RoomInstance roomInstance, bool useLocalPositions)
 {
     return(roomInstance
            .OutlinePolygon
            .GetAllPoints()
            .Select(x => new Vector3Int(x.x, x.y, 0))
            .Select(x => useLocalPositions ? x - roomInstance.Position : x)
            .ToList());
 }
        /// <summary>
        /// Copies tiles from a given room template to given destination tilemaps.
        /// </summary>
        /// <remarks>
        /// One important aspect of this method is how to handle already existing tiles in destination tilemaps.
        ///
        /// When deleteNonNullTiles is true, it computes all non-null positions across all layers in the room template.
        /// After that, it deletes all tiles on these positions in destination tilemaps.
        ///
        /// When deleteTilesInsideOutline is true, it computes all tiles inside the outline of the room template and
        /// deletes them from the destination tilemaps.
        /// So even if there is a hole inside the room template, the position is still removed.
        ///
        /// deleteNonNullTiles and deleteTilesInsideOutline can be combined together.
        /// </remarks>
        /// <param name="roomInstance">Room instance to be copied to the destination tilemaps.</param>
        /// <param name="destinationTilemaps">List of destination tilemaps.</param>
        /// <param name="deleteNonNullTiles">Whether to delete non-null tiles from destination tilemaps.</param>
        /// <param name="deleteTilesInsideOutline">Whether to delete all tiles insides the outline from destination tilemaps.</param>
        public static void CopyTiles(RoomInstance roomInstance, List <Tilemap> destinationTilemaps, bool deleteNonNullTiles, bool deleteTilesInsideOutline)
        {
            var sourceTilemaps = RoomTemplateUtils.GetTilemaps(roomInstance.RoomTemplateInstance);

            sourceTilemaps = RoomTemplateUtils.GetTilemapsForCopying(sourceTilemaps);

            var tilesToRemove = new List <Vector3Int>();

            if (deleteNonNullTiles)
            {
                var tiles = GetNonNullTiles(sourceTilemaps);
                tilesToRemove.AddRange(tiles.Select(x => x + roomInstance.Position));
            }

            if (deleteTilesInsideOutline)
            {
                var tiles = GetTilesInsideOutline(roomInstance, false);
                tilesToRemove.AddRange(tiles);
            }

            RemoveTiles(destinationTilemaps, tilesToRemove);

            foreach (var sourceTilemap in sourceTilemaps)
            {
                var destinationTilemap = destinationTilemaps.FirstOrDefault(x => x.name == sourceTilemap.name);

                if (destinationTilemap == null)
                {
                    continue;
                }

                foreach (var tilemapPosition in sourceTilemap.cellBounds.allPositionsWithin)
                {
                    var tile = sourceTilemap.GetTile(tilemapPosition);

                    if (tile != null)
                    {
                        destinationTilemap.SetTile(tilemapPosition + roomInstance.Position, tile);
                        destinationTilemap.SetTransformMatrix(tilemapPosition + roomInstance.Position, sourceTilemap.GetTransformMatrix(tilemapPosition));
                    }
                }
            }
        }
 /// <summary>
 /// Copies tiles from a given room template to given destination tilemaps.
 /// </summary>
 /// <remarks>
 /// One important aspect of this method is how to handle already existing tiles in destination tilemaps.
 ///
 /// When deleteNonNullTiles is true, it computes all non-null positions across all layers in the room template.
 /// After that, it deletes all tiles on these positions in destination tilemaps.
 ///
 /// When deleteTilesInsideOutline is true, it computes all tiles inside the outline of the room template and
 /// deletes them from the destination tilemaps.
 /// So even if there is a hole inside the room template, the position is still removed.
 ///
 /// deleteNonNullTiles and deleteTilesInsideOutline can be combined together.
 /// </remarks>
 /// <param name="roomInstance">Room instance to be copied to the destination tilemaps.</param>
 /// <param name="destinationTilemaps">List of destination tilemaps.</param>
 /// <param name="deleteNonNullTiles">Whether to delete non-null tiles from destination tilemaps.</param>
 /// <param name="deleteTilesInsideOutline">Whether to delete all tiles insides the outline from destination tilemaps.</param>
 public static void CopyTiles(RoomInstance roomInstance, List <Tilemap> destinationTilemaps, bool deleteNonNullTiles, bool deleteTilesInsideOutline)
 {
     PostProcessUtils.CopyTiles(roomInstance, destinationTilemaps, deleteNonNullTiles, deleteTilesInsideOutline);
 }
        private static DoorInstance TransformDoorInfo(LayoutDoorGrid2D <RoomBase> doorInfo, RoomInstance connectedRoomInstance)
        {
            var doorLine = doorInfo.DoorLine;

            switch (doorLine.GetDirection())
            {
            case OrthogonalLineGrid2D.Direction.Right:
                return(new DoorInstance(new OrthogonalLine(doorLine.From.ToUnityIntVector3(), doorLine.To.ToUnityIntVector3()), Vector2Int.up,
                                        connectedRoomInstance.Room, connectedRoomInstance));

            case OrthogonalLineGrid2D.Direction.Left:
                return(new DoorInstance(new OrthogonalLine(doorLine.To.ToUnityIntVector3(), doorLine.From.ToUnityIntVector3()), Vector2Int.down,
                                        connectedRoomInstance.Room, connectedRoomInstance));

            case OrthogonalLineGrid2D.Direction.Top:
                return(new DoorInstance(new OrthogonalLine(doorLine.From.ToUnityIntVector3(), doorLine.To.ToUnityIntVector3()), Vector2Int.left,
                                        connectedRoomInstance.Room, connectedRoomInstance));

            case OrthogonalLineGrid2D.Direction.Bottom:
                return(new DoorInstance(new OrthogonalLine(doorLine.To.ToUnityIntVector3(), doorLine.From.ToUnityIntVector3()), Vector2Int.right,
                                        connectedRoomInstance.Room, connectedRoomInstance));

            default:
                throw new ArgumentOutOfRangeException();
            }
        }
        public static GeneratedLevel TransformLayout(LayoutGrid2D <RoomBase> layout, LevelDescription levelDescription, GameObject rootGameObject)
        {
            // var layoutCenter = GetLayoutCenter(layout);
            var prefabToRoomTemplateMapping = levelDescription.GetPrefabToRoomTemplateMapping();
            var corridorToConnectionMapping = levelDescription.GetCorridorToConnectionMapping();

            // Prepare an object to hold instantiated room templates
            var roomTemplateInstancesRoot = new GameObject(GeneratorConstants.RoomsRootName);

            roomTemplateInstancesRoot.transform.parent = rootGameObject.transform;

            // Initialize rooms
            var layoutData  = new Dictionary <RoomBase, RoomInstance>();
            var layoutRooms = layout.Rooms.ToDictionary(x => x.Room, x => x);

            foreach (var layoutRoom in layoutRooms.Values)
            {
                var roomTemplatePrefab = prefabToRoomTemplateMapping.GetByValue(layoutRoom.RoomTemplate);

                // Instantiate room template
                var roomTemplateInstance = Object.Instantiate(roomTemplatePrefab);
                roomTemplateInstance.transform.SetParent(roomTemplateInstancesRoot.transform);
                roomTemplateInstance.name = $"{layoutRoom.Room.GetDisplayName()} - {roomTemplatePrefab.name}";

                // Compute correct room position
                var position = layoutRoom.Position.ToUnityIntVector3();
                roomTemplateInstance.transform.position = position;

                // Correct the position based on the grid
                // This is important when there is some cell spacing or when the level is isometric
                var tilemapsHolder = roomTemplateInstance.transform.Find(GeneratorConstants.TilemapsRootName).gameObject;
                if (tilemapsHolder != null)
                {
                    var grid = tilemapsHolder.GetComponent <Grid>();
                    roomTemplateInstance.transform.position = grid.CellToLocal(position);
                }

                // Compute outline polygon
                var polygon = new Polygon2D(layoutRoom.Outline + layoutRoom.Position);

                var connection   = layoutRoom.IsCorridor ? corridorToConnectionMapping[layoutRoom.Room] : null;
                var roomInstance = new RoomInstance(layoutRoom.Room, layoutRoom.IsCorridor, connection, roomTemplatePrefab, roomTemplateInstance, position, polygon);

                // Add room info to the GameObject
                var roomInfo = roomTemplateInstance.GetComponent <RoomInfo>();

                if (roomInfo != null)
                {
                    PostProcessUtils.Destroy(roomInfo);
                }

                roomInfo = roomTemplateInstance.AddComponent <RoomInfo>();
                roomInfo.RoomInstance = roomInstance;

                layoutData.Add(layoutRoom.Room, roomInstance);
            }

            foreach (var roomInstance in layoutData.Values)
            {
                roomInstance.SetDoors(TransformDoorInfo(layoutRooms[roomInstance.Room].Doors, layoutData));
            }

            // Add level info
            var levelInfo = rootGameObject.GetComponent <LevelInfo>();

            if (levelInfo != null)
            {
                PostProcessUtils.Destroy(levelInfo);
            }

            levelInfo = rootGameObject.AddComponent <LevelInfo>();
            levelInfo.RoomInstances = layoutData.Values.ToList();

            return(new GeneratedLevel(layoutData, layout, rootGameObject));
        }