/// <summary>
 /// Informs neighbors of this room to unload themselves. Does NOT unload
 /// this room itself! Also, you may specify one neighbor not to despawn.
 /// </summary>
 /// <param name="dontDespawn">Room you would like to not unload</param>
 public virtual void UnloadNeighbors(RogueRoom dontDespawn)
 {
     if (LeftNeighbor != null && LeftNeighbor != dontDespawn)
         LeftNeighbor.UnloadRoom();
     if (RightNeighbor != null && RightNeighbor != dontDespawn)
         RightNeighbor.UnloadRoom();
     if (UpNeighbor != null && UpNeighbor != dontDespawn)
         UpNeighbor.UnloadRoom();
     if (DownNeighbor != null && DownNeighbor != dontDespawn)
         DownNeighbor.UnloadRoom();
 }
        /// <summary>
        /// Generates a new RogueDungeon of the specified width and height.
        /// In this case, "width" and "height" mean the number of potential
        /// rooms horizontally or vertically, respectively.
        /// </summary>
        /// <param name="width">Maximum number of rooms tall the map can be</param>
        /// <param name="height">Maximum number of rooms wide the map can be</param>
        public RogueDungeon(int width, int height)
        {
            // Build a maze, which gives us door values
            bool[,] boolMap = Maze.GenerateMaze(width, height);

            // Use the maze to fill in our map
            int newWidth = boolMap.GetLength(0) - 2;
            int newHeight = boolMap.GetLength(1) - 2;
            Map = new RogueRoom[newWidth, newHeight];

            // Assign initial placements:
            int bossX = 0, bossY = 0;
            int keyX = 0, keyY = 0;
            startRoomX = 0;
            startRoomY = 0;
            for (int x = 1; x < boolMap.GetLength(0); x += 2)
            {
                for (int y = 1; y < boolMap.GetLength(1); y += 2)
                {
                    int dirCode = 0;
                    dirCode |= (boolMap[x-1,y] ? 0x1 : 0x0);
                    dirCode |= (boolMap[x+1,y] ? 0x2 : 0x0);
                    dirCode |= (boolMap[x,y-1] ? 0x4 : 0x0);
                    dirCode |= (boolMap[x,y+1] ? 0x8 : 0x0);

                    if (dirCode == 0x1)
                    {
                        bossX = x;
                        bossY = y;
                        startRoomX = x-2;
                        startRoomY = y;
                    }
                    else if (dirCode == 0x2)
                    {
                        bossX = x;
                        bossY = y;
                        startRoomX = x+2;
                        startRoomY = y;
                    }
                    else if (dirCode == 0x4)
                    {
                        bossX = x;
                        bossY = y;
                        startRoomX = x;
                        startRoomY = y-2;
                    }
                    else if (dirCode == 0x8)
                    {
                        bossX = x;
                        bossY = y;
                        startRoomX = x;
                        startRoomY = y+2;
                    }
                }
            }
            // Find key room:
            Maze.FindFarthest(boolMap, bossX, bossY, out keyX, out keyY);

            // Account for coordinate offset:
            bossX -= 1;
            bossY -= 1;
            startRoomX -= 1;
            startRoomY -= 1;
            keyX -= 1;
            keyY -= 1;

            // Instantiate between rooms:
            for (int x = 0; x < newWidth; x += 2)
            {
                for (int y = 0; y < newHeight; y += 2)
                {
                    // CorridorFork room by default
                    int roomWidth = CORRIDOR_WIDTH;
                    int roomDepth = CORRIDOR_WIDTH;
                    RogueRoom.RoomType type = RogueRoom.RoomType.corridorFork;

                    // If the x,y coordinate was one of our set aside vectors, use that type:
                    if (x == startRoomX && y == startRoomY)
                    {
                        roomWidth = ROOM_WIDTH;//r.Next(MIN_SHOP_ROOM_WIDTH, MAX_SHOP_ROOM_WIDTH-1);
                        roomDepth = ROOM_DEPTH;//r.Next (MIN_SHOP_ROOM_DEPTH, MAX_SHOP_ROOM_DEPTH-1);
                        type = RogueRoom.RoomType.start;
                    }
                    else if (x == bossX && y == bossY)
                    {
                        roomWidth = BOSS_ROOM_WIDTH;//r.Next(MIN_BOSS_ROOM_WIDTH, MAX_BOSS_ROOM_WIDTH-1);
                        roomDepth = BOSS_ROOM_DEPTH;//r.Next (MIN_BOSS_ROOM_DEPTH, MAX_BOSS_ROOM_DEPTH-1);
                        type = RogueRoom.RoomType.boss;
                    }
                    else if (x == keyX && y == keyY)
                    {
                        roomWidth = ROOM_WIDTH;
                        roomDepth = ROOM_DEPTH;
                        type = RogueRoom.RoomType.keyRoom;
                    }
                    // TODO: others?

                    // If we decide to place a room here, adjust the width
                    // and height accordingly
                    else if (Maze.rnd.NextDouble() < ENEMY_ROOM_DENSITY)
                    {
                        roomWidth = ROOM_WIDTH;//r.Next(MIN_ENEMY_ROOM_WIDTH, MAX_ENEMY_ROOM_WIDTH-1);
                        roomDepth = ROOM_DEPTH;//r.Next(MIN_ENEMY_ROOM_DEPTH, MAX_ENEMY_ROOM_DEPTH-1);
                        type = RogueRoom.RoomType.enemy;

                        // TODO: handle enemy score distribution
                    }

                    // We need rooms to be nice values to accomodate corridor widths & cubes.
                    // Short version: we need an equal number of blocks on the left and right
                    // of every door in the maze, so width-corridor_width as well as
                    // height-corridor_width must be even.
                    // Adjust accordingly:
                    /*
                    if ((roomWidth - CORRIDOR_WIDTH) % 2 == 1)
                        roomWidth++;
                    if ((roomDepth - CORRIDOR_WIDTH) % 2 == 1)
                        roomDepth++;
                        */

                    // Determine what the door code should be, by checking the map:
                    int mapCoordX = x + 1;
                    int mapCoordY = y + 1;
                    int doorCode = 0x0;

                    // NOTE: the below operations are safe, because the Maze class guarantees
                    // that the map will have a buffer of false values around where the rooms are.
                    // Left:
                    if (boolMap[mapCoordX - 1, mapCoordY])
                        doorCode |= RogueRoom.LEFT_DOOR_MASK;

                    // Right:
                    if (boolMap[mapCoordX + 1, mapCoordY])
                        doorCode |= RogueRoom.RIGHT_DOOR_MASK;

                    // Up:
                    if (boolMap[mapCoordX, mapCoordY - 1])
                        doorCode |= RogueRoom.UP_DOOR_MASK;

                    // Down:
                    if (boolMap[mapCoordX, mapCoordY + 1])
                        doorCode |= RogueRoom.DOWN_DOOR_MASK;

                    // Create the room, and insert it into the map:
                    RogueRoom newRoom;
                    switch (type)
                    {
                    case RogueRoom.RoomType.start:
                    case RogueRoom.RoomType.shop:
                    case RogueRoom.RoomType.empty:
                    case RogueRoom.RoomType.enemy:
                        newRoom = new GeneralRoom(roomWidth, roomDepth, MAX_ROOM_HEIGHT, x, y, doorCode);
                        break;
                    case RogueRoom.RoomType.boss:
                    case RogueRoom.RoomType.keyRoom:
                        newRoom = new GeneralRoom(roomWidth, roomDepth, MAX_ROOM_HEIGHT, x, y, doorCode);
                        SkeletonKingAI.bossRoom = (GeneralRoom)newRoom;
                        ZombiePrinceAI.bossRoom = (GeneralRoom)newRoom;
                        SpiderQueenAI.bossRoom = (GeneralRoom)newRoom;
                        break;
                    case RogueRoom.RoomType.corridorFork:
                    default:
                        newRoom = new CorridorBranchRoom(roomWidth, MAX_ROOM_HEIGHT, x, y, doorCode);
                        break;
                    // This case can't happen:
                    //case RogueRoom.RoomType.corridor:
                    }
                    newRoom.Type = type;

                    // Initialize enemy list to a GeneralRoom. The GeneralRoom handles when it isn't an
                    // enemy room, so we won't worry about that.
                    // TODO: smarter distribution of enemy points. For now, just give same value to each room.
                    if (newRoom is GeneralRoom)
                        ((GeneralRoom)newRoom).AssignEnemies(LevelHolder.Level * 10);

                    Map[x, y] = newRoom;
                }
            }

            // Instantiate corridors:
            // Left-Right:
            for (int x = 1; x < newWidth; x += 2)
            {
                for (int y = 0; y < newHeight; y += 2)
                {
                    // Determine door codes, and attach relationships:
                    int mapCoordX = x + 1;
                    int mapCoordY = y + 1;
                    int doorCode = 0x0;
                    RogueRoom lftNbr = null,
                              rgtNbr = null;

                    int corridorWidth = CORRIDOR_WIDTH;
                    int corridorDepth = CORRIDOR_WIDTH;
                    CorridorRoom corridor;

                    // Confirm this corridor is actually in the maze:
                    if (boolMap[mapCoordX, mapCoordY])
                    {
                        doorCode |= RogueRoom.LEFT_DOOR_MASK;
                        lftNbr = Map[x-1, y];
                        doorCode |= RogueRoom.RIGHT_DOOR_MASK;
                        rgtNbr = Map[x+1,y];

                        // Determine correct width for this corridor:
                        corridorWidth = MAX_ROOM_WIDTH +
                                        ((MAX_ROOM_WIDTH - lftNbr.Width) / 2) +
                                        ((MAX_ROOM_WIDTH - rgtNbr.Width) / 2);

                        // Instantiate & hookup neighbors:
                        corridor = new CorridorRoom(corridorWidth, corridorDepth, MAX_ROOM_HEIGHT, x, y, doorCode);
                        lftNbr.RightNeighbor = corridor;
                        rgtNbr.LeftNeighbor = corridor;
                        corridor.LeftNeighbor = lftNbr;
                        corridor.RightNeighbor = rgtNbr;

                        // Initialize cubes:
                        corridor.InitializeCubes();

                        // Put it in the map:
                        Map[x,y] = corridor;
                    }
                    else
                    {
                        Map[x,y] = null;
                    }
                }
            }

                // Up-Down:
            for (int x = 0; x < newWidth; x += 2)
            {
                for (int y = 1; y < newHeight; y += 2)
                {
                    // Determine door codes, and attach relationships:
                    int mapCoordX = x + 1;
                    int mapCoordY = y + 1;
                    int doorCode = 0x0;
                    RogueRoom upNbr  = null,
                              dwnNbr = null;

                    int corridorWidth = CORRIDOR_WIDTH;
                    int corridorDepth = CORRIDOR_WIDTH;
                    CorridorRoom corridor;

                    // Confirm this corridor is actually in the maze:
                    if (boolMap[mapCoordX, mapCoordY])
                    {
                        doorCode |= RogueRoom.UP_DOOR_MASK;
                        upNbr = Map[x, y-1];
                        doorCode |= RogueRoom.DOWN_DOOR_MASK;
                        dwnNbr = Map[x,y+1];

                        // Determine correct width for this corridor:
                        corridorDepth = MAX_ROOM_DEPTH +
                                        ((MAX_ROOM_DEPTH - upNbr.Depth) / 2) +
                                        ((MAX_ROOM_DEPTH - dwnNbr.Depth) / 2);

                        // Instantiate & hookup neighbors:
                        corridor = new CorridorRoom(corridorWidth, corridorDepth, MAX_ROOM_HEIGHT, x, y, doorCode);
                        upNbr.DownNeighbor = corridor;
                        dwnNbr.UpNeighbor = corridor;
                        corridor.UpNeighbor = upNbr;
                        corridor.DownNeighbor = dwnNbr;

                        // Initialize cubes:
                        corridor.InitializeCubes();

                        // Put it in the map:
                        Map[x,y] = corridor;
                    }
                    else
                    {
                        Map[x,y] = null;
                    }
                }
            }

            // Initialize main rooms' cubes:
            for (int x = 0; x < newWidth; x += 2)
            {
                for (int y = 0; y < newHeight; y += 2)
                {
                    Map[x,y].InitializeCubes();
                }
            }
        }
 /// <summary>
 /// Informs this room's neighbors that they need to get themselves loaded into the
 /// scene. Does NOT load this room itself! Also, will not load a room if that room
 /// is already loaded.
 /// 
 /// You may pass in a room you would like not to load, as well, in case your room
 /// is adjacent to a room already loaded (for example).
 /// </summary>
 /// <param name="maxWidth">Maximum width a room can be in the maze, in cube units.</param>
 /// <param name="maxDepth">Maximum depth a room can be in the maze, in cube units.</param>
 /// <param name="doorWidth">How large the openings to the room need to be. Must be no more than Width or Depth.</param>
 /// <param name="scalar">1 cube -> scalar units in Unity.</param>
 public virtual void LoadNeighbors(RogueRoom dontLoad)
 {
     if (LeftNeighbor != null && LeftNeighbor != dontLoad)
         LeftNeighbor.DynamicLoad();
     if (RightNeighbor != null && RightNeighbor != dontLoad)
         RightNeighbor.DynamicLoad();
     if (UpNeighbor != null && UpNeighbor != dontLoad)
         UpNeighbor.DynamicLoad();
     if (DownNeighbor != null && DownNeighbor != dontLoad)
         DownNeighbor.DynamicLoad();
 }