Example #1
0
    /// <summary>
    /// Generates a rectabgular room (or hall) and tries to attach it to the specified door (FloorTile)
    /// </summary>
    /// <param name="rooms"></param>
    /// <param name="roomType"></param>
    /// <param name="preferredDoor"></param>
    /// <returns></returns>
    private static Room GenerateGenericRoom( List<Room> rooms, CastleRoomType roomType, FloorTile targetDoor )
    {
      Room room = new Room(roomType);

      int tries = 0;
      int maxTries = 5;

      while( tries++ < maxTries )
      {
        int w = 0;
        int h = 0;
        int numDoors    = 0;
        int doorSpacing = 6;  // used for LongHall only
        int hallWidth   = 5;  // used for LongHall only.  Includes walls on either side : 3 should be the minimum, works best with odd numbers

        #region Specify sizes and number of doors, based on roomType
        switch( roomType )
        {
          case CastleRoomType.Bedroom:
            w = rand.Next(5, 9) + 2;
            h = rand.Next(5, 9) + 2;
            numDoors = 1;
            break;

          case CastleRoomType.BedroomMaster:
            w = rand.Next(6, 10) + 2;
            h = rand.Next(6, 10) + 2;
            numDoors = 2;
            break;

          case CastleRoomType.Closet:
            w = rand.Next(1, 3) + 2;
            h = rand.Next(1, 3) + 2;
            numDoors = 1;
            break;

          case CastleRoomType.GreatRoomRectangle:
            w = rand.Next(10, 25) + 2;
            h = rand.Next(15, 25) + 2;
            numDoors = 6;
            break;

          case CastleRoomType.GreatRoomSquare:
            w = rand.Next(10, 25) + 2;
            h = w;
            numDoors = 6;
            break;
            
          case CastleRoomType.GreatroomCircle:
            w = rand.Next(15,30) + 2;
            if( w%2==0 )
              w++;
            h = w;
            numDoors = 2*(rand.Next(6)+1);
            break;

          case CastleRoomType.Kitchen:
            w = rand.Next(3, 11) + 2;
            h = rand.Next(3, 11) + 2;
            numDoors = 3;
            break;

          case CastleRoomType.Storage:
            w = rand.Next(2, 5) + 2;
            h = rand.Next(2, 5) + 2;
            numDoors = 1;
            break;

          case CastleRoomType.LongHall:
            if( rand.Next(2) == 0 )
            {
              w = doorSpacing * (1+rand.Next(8));
              h = hallWidth;
              numDoors = w/doorSpacing;
            }
            else
            {
              w = hallWidth;
              h = doorSpacing * (1+rand.Next(8));
              numDoors = h/doorSpacing;
            }
            break;
        }
        #endregion

        room.cells = new FloorTile[w, h];
        room.doors.Clear();

        switch( roomType )
        {
          case CastleRoomType.GreatroomCircle:
            #region Circular Greatroom may have pillars (and generation is more complex than rectangular rooms)

            // fill room dimensions with wall
            for( int i = 0; i < w; i++ )
              for( int j = 0; j < h; j++ )
                room.cells[i, j] = new FloorTile(i, j, FloorType.Wall);

            // hollow out center, leaving a border of wall tiles
            for( int i = 1; i<w-1; i++ )
              for( int j = 1; j<h-1; j++ )
                if( (int)Math.Sqrt(Math.Pow(w/2 - i, 2.0) + Math.Pow(h/2 - j, 2.0)) < w/2.0-1 )
                  room.cells[i, j].type = FloorType.Floor;

            // Add pillars around the middle?
            if( rand.Next(2)==0 )
            {
              int pillars = (1+rand.Next(6))*2;
              double dist = rand.Next(3, w/2 - 2);
              for( int i=0; i<pillars; i++ )
              {
                room.cells[(int)Math.Round(w/2 - dist * Math.Cos(i*2.0*Math.PI/pillars), 0),
                            (int)Math.Round(w/2 - dist * Math.Sin(i*2.0*Math.PI/pillars), 0)
                          ].type = FloorType.Wall;
              }
            }

            #endregion
            break;

          case CastleRoomType.GreatRoomRectangle:
          case CastleRoomType.GreatRoomSquare:
            #region Greatrooms may have pillars

            // make solid wall outline, hollow center
            for( int i = 0; i < w; i++ )
              for( int j = 0; j < h; j++ )
              {
                room.cells[i, j] = new FloorTile(i, j);
                if( i == 0 || i == w - 1 || j == 0 || j == h - 1 )
                  room.cells[i, j].type = FloorType.Wall;
                else
                  room.cells[i, j].type = FloorType.Floor;
              }

            bool NSpillars = false;
            bool EWpillars = false;

            // Add pillars around the middle?
            switch( rand.Next(4) )
            {
              case 1:
                // all the way around the room
                NSpillars = EWpillars = true;
                break;

              case 2:
                // N/S walls only
                NSpillars = true;
                break;

              case 3:
                // E/W walls only
                EWpillars = true;
                break;

              default:
                // no pillars.
                break;
            }

            // spacing for pillars should be consistent within a room
            int offset  = rand.Next(2,6);
            int spacing = rand.Next(2,8);

            if( NSpillars )
              for( int i=offset; i<=w/2; i+=spacing )
              {
                room.cells[i, offset].type = FloorType.Wall;
                room.cells[i, h-1-offset].type = FloorType.Wall;
                room.cells[w-1-i, offset].type = FloorType.Wall;
                room.cells[w-1-i, h-1-offset].type = FloorType.Wall;
              }

            if( EWpillars )
              for( int i=offset; i<=h/2; i+=spacing )
              {
                room.cells[offset, i].type = FloorType.Wall;
                room.cells[w-1-offset, i].type = FloorType.Wall;
                room.cells[offset, h-1-i].type = FloorType.Wall;
                room.cells[w-1-offset, h-1-i].type = FloorType.Wall;
              }

            #endregion
            break;

          default:
            #region All other rectangualr rooms, including LongHall

            // make solid wall outline, hollow center
            for( int i = 0; i < w; i++ )
              for( int j = 0; j < h; j++ )
              {
                room.cells[i, j] = new FloorTile(i, j);
                if( i == 0 || i == w - 1 || j == 0 || j == h - 1 )
                  room.cells[i, j].type = FloorType.Wall;
                else
                  room.cells[i, j].type = FloorType.Floor;
              }

            #endregion
            break;
        }

        // So far, only applicable to LongHall and GreatRooms
        // If we haven't connected to a GreatRoom after half the max tried, stop limiting the attemps.
        // This setting has no effect on other room connections (so far).
        bool allowConnectionBias = tries < maxTries/2;

        if( !PlaceRoom( rooms, room, roomType, allowConnectionBias, targetDoor ) )
          continue;

        // placed a room via connection - remove that door from the number we're going to generate.
        // (closets, for example, should have that connection count as their only door)
        if( rooms.Count > 0 )
          numDoors--;

        // set doors
        if( roomType == CastleRoomType.GreatroomCircle )
        {
          #region Circular rooms will only place doors along the "flat" sections near N, E, S, W sides.

          int count = 0;
          int maxDoorTries = 30;
          int doorTries = 0;
          while( count < numDoors && doorTries < maxDoorTries )
          {
            double angle = rand.Next(360) * Math.PI / 180.0;

            int tX = (int)Math.Round((w/2) + (w-1)/2 * Math.Cos(angle), 0);
            int tY = (int)Math.Round((w/2) + (w-1)/2 * Math.Sin(angle), 0);

            bool EW =    tX>0 
                      && tX<w-1
                      && (room.cells[tX-1, tY].type == FloorType.Wall)  // || room.cells[tX-1, tY].type == FloorType.DoorClosed) 
                      && (room.cells[tX+1, tY].type == FloorType.Wall); // || room.cells[tX+1, tY].type == FloorType.DoorClosed);

            bool NS =    tY>0
                      && tY<h-1
                      && (room.cells[tX, tY-1].type == FloorType.Wall)  // || room.cells[tX, tY-1].type == FloorType.DoorClosed) 
                      && (room.cells[tX, tY+1].type == FloorType.Wall); // || room.cells[tX, tY+1].type == FloorType.DoorClosed);

            if( EW ^ NS ) // exclusive OR : must be one or the other, but not neither, and not both.
            {
              room.doors.Add(room.cells[tX, tY]);
              room.cells[tX, tY].type = FloorType.DoorClosed;
              count++;
            }
            else
            {
              // failed to place - up the count
              doorTries++;
            }
          }

          #endregion
        }
        else if( roomType == CastleRoomType.LongHall )
        {
          #region Special case : add doors one each end, and evenly spaced down both sides
          
          if( w > h )
          {
            // E/W hall
            room.doors.Add(room.cells[0, h/2]);
            room.doors.Add(room.cells[w-1, h/2]);
            for( int i = 0; i < w/doorSpacing; i++ )
            {
              room.doors.Add(room.cells[doorSpacing/2 + i*doorSpacing, 0+0]);
              room.doors.Add(room.cells[doorSpacing/2 + i*doorSpacing, h-1]);
            }
          }
          else
          {
            // N/S hall
            room.doors.Add(room.cells[w/2, 0]);
            room.doors.Add(room.cells[w/2, h-1]);
            for( int i = 0; i < h/doorSpacing; i++ )
            {
              room.doors.Add(room.cells[0+0, doorSpacing/2 + i*doorSpacing]);
              room.doors.Add(room.cells[w-1, doorSpacing/2 + i*doorSpacing]);
            }
          }

          foreach( FloorTile door in room.doors )
            door.type = FloorType.DoorClosed;

          #endregion
        }
        else
        {
          #region All other square rooms

          for( int i = 0; i < numDoors; i++ )
          {
            if( rand.Next(2) == 0 )
              // top/bottom... x= whatever, and y=1 (top edge) or y=h-2 (bottom edge)
              room.doors.Add(room.cells[rand.Next(1, w - 2), rand.Next(2) * (h - 1)]);
            else
              // left/right... x=1 (left side) or x=w-2 (right side) and y = whatever
              room.doors.Add(room.cells[rand.Next(2) * (w - 1), rand.Next(1, h - 2)]);

            room.doors[room.doors.Count-1].type = FloorType.DoorClosed;
          }

          #endregion
        }

        return room;
      }

      // could not find a valid connection for this room... gave up
      return null;
    }
Example #2
0
    /// <summary>
    /// True = valid placement
    /// False = invalid (overlap found)
    /// </summary>
    /// <param name="room"></param>
    /// <param name="door"></param>
    /// <returns></returns>
    private static bool CheckRoomTilesAgainstGrid( Room room, FloorTile door )
    {
      FloorType gcType;
      FloorType rcType;

      foreach( FloorTile roomCell in room.cells )
      {
        // bounds checking - id it would fall off the scope of the base grid[,], then it's a no-go.
        if( roomCell.x < 1 || roomCell.x >= grid.GetLength(0)-1 || roomCell.y<1 || roomCell.y>=grid.GetLength(1)-1 )
          return false;

        gcType = grid[roomCell.x, roomCell.y].type;
        rcType = roomCell.type;

        if( (rcType != FloorType.Wall && gcType != FloorType.Wall)
            ||
            (rcType == FloorType.Wall && gcType != FloorType.Wall && !(roomCell.x==door.x && roomCell.y==door.y))
          )
        {
          return false;
        }
      }

      // checked everything and it was all OK.  Valid placement.
      return true;
    }
Example #3
0
 /// <summary>
 /// True = valid placement found, room has been updated to the correct offset.
 /// False = no valid connections, room has been updated to last tried offset (but is probably useless).
 /// </summary>
 /// <param name="room"></param>
 /// <param name="door"></param>
 /// <returns></returns>
 private static bool CheckRoomAgainstGrid( Room room, FloorTile door )
 {
   return CheckRoomAgainstGrid(room, door, new Directions[] { Directions.North, Directions.East, Directions.South, Directions.West });
 }
Example #4
0
    /// <summary>
    /// True = valid placement found, room has been updated to the correct offset.
    /// False = no valid connections, room has been updated to last tried offset (but is probably useless).
    /// </summary>
    /// <param name="room"></param>
    /// <param name="door"></param>
    /// <param name="directions"></param>
    /// <returns></returns>
    private static bool CheckRoomAgainstGrid( Room room, FloorTile door, Directions[] directions )
    {
      // kind of cumbersome, but allows random selection and deletion by reference.
      //   Arrays are more difficult to remove things from.
      List<Directions> dirs = new List<Directions>();
      for( int i=0; i<directions.Length; i++ )
        dirs.Add(directions[i]);

      // try attaching to the north
      int w = room.cells.GetLength(0);
      int h = room.cells.GetLength(1);

      Directions d;

      // Pick a direction specified, and test it.  
      // Avoids obvious bias against North, and allows specific tests for certain room types (halls, for example)
      while( dirs.Count > 0 )
      {
        d = dirs[rand.Next(dirs.Count)];
        dirs.Remove(d);

        switch( d )
        {
          case Directions.North:  // try attaching to the north
            room.SetRoomPosition(door.x - w/2, door.y-h+1);
            if( CheckRoomTilesAgainstGrid(room, door) )
              return true;
            break;

          case Directions.South: // try attaching to the south
            room.SetRoomPosition(door.x - w/2, door.y);
            if( CheckRoomTilesAgainstGrid(room, door) )
              return true;
            break;

          case Directions.East: // try attaching to the east
            room.SetRoomPosition(door.x-w+1, door.y-h/2);
            if( CheckRoomTilesAgainstGrid(room, door) )
              return true;
            break;

          case Directions.West: // try attaching to the west
            room.SetRoomPosition(door.x, door.y-h/2);
            if( CheckRoomTilesAgainstGrid(room, door) )
              return true;
            break;
        }
      }

      // no valid connection found.
      return false;
    }
Example #5
0
    /// <summary>
    /// Tries to place the specified room.
    /// True = successful, and the connecting door has been flagged.
    /// False = unable to find a connection.
    /// </summary>
    /// <param name="rooms"></param>
    /// <param name="room"></param>
    /// <param name="roomType"></param>
    /// <param name="allowConnectionBias"></param>
    /// <param name="targetDoor"></param>
    /// <returns></returns>
    private static bool PlaceRoom(List<Room> rooms, Room room, CastleRoomType roomType, bool allowConnectionBias, FloorTile targetDoor )
    {
      int w = room.cells.GetLength(0);
      int h = room.cells.GetLength(1);

      if( rooms.Count == 0 )
      {
        // place in middle
        room.SetRoomPosition(grid.GetLength(0)/2 - w/2, grid.GetLength(1)/2 - h/2);
      }
      else
      {
        // pick a door to attach to, build from there.
        List<FloorTile> doors = new List<FloorTile>();
        FloorTile connectionDoor = null;

        if( targetDoor != null )
        {
          doors.Add(targetDoor);
        }
        else if( allowConnectionBias )
        {
          switch( roomType )
          {
            case CastleRoomType.LongHall:
              // custom bias to connect long Halls to Great Rooms if at all possible.
              foreach( Room r in rooms )
                if( r.type == CastleRoomType.GreatroomCircle
                  ||r.type == CastleRoomType.GreatRoomSquare
                  ||r.type == CastleRoomType.GreatRoomRectangle
                  )
                  foreach( FloorTile door in r.doors )
                    if( door.type == FloorType.DoorClosed)
                      doors.Add(door);
              break;

            case CastleRoomType.GreatroomCircle:
            case CastleRoomType.GreatRoomRectangle:
            case CastleRoomType.GreatRoomSquare:
              // Greatrooms should try to attach to the end of hallways.
              // Since the LongHall are generated with the end doors first, we can 
              //   reference those tow doors directly without searching though the room's doors list
              foreach( Room r in rooms )
                if( r.type == CastleRoomType.LongHall )
                {
                  if( r.doors[0].type == FloorType.DoorClosed)
                    doors.Add(r.doors[0]);

                  if( r.doors[1].type == FloorType.DoorClosed)
                    doors.Add(r.doors[1]);
                }
              break;

            default:
              // no custom doors list - the default of "All of them" will be used below
              break;
          }
        }

        // Check for custom doors list - if there are none, default to use all doors in all rooms for tries.
        if( doors.Count == 0 )
        {
          foreach( Room r in rooms )
            foreach( FloorTile door in r.doors )
              if( door.type == FloorType.DoorClosed)
                doors.Add(door);
        }


        // In the event of a LongHall, we'd like to attach it so that it 
        //  traveles away from the room being attached to, rather than attaching along the side of the hall.
        Directions[] directions;
        if( roomType == CastleRoomType.LongHall )
        {
          if( w>h )
          {
            // E/W hall - try to attach on East or West side only
            directions = new Directions[] { Directions.East, Directions.West };
          }
          else
          {
            // N/S hall - attach north or south only.
            directions = new Directions[] { Directions.North, Directions.South };
          }
        }
        else
        {
          // all other rooms may try to attach from any direction
          directions = new Directions[] { Directions.North, Directions.East, Directions.South, Directions.West };
        }


        while( doors.Count>0 && connectionDoor==null )
        {
          // pick a door
          // try to attach to it to the N/E/W/S
          //   update room position accordingly
          //   check room.cells against all other room.cells
          // break loop when successful

          FloorTile door = doors[rand.Next(doors.Count)];
          doors.Remove(door);

          if( CheckRoomAgainstGrid(room, door, directions) )
          {
            connectionDoor = door;
            break;
          }
        }


        // failed to place room
        if( connectionDoor == null )
          return false;

        //set the connection door
        foreach( FloorTile cell in room.cells )
          if( cell.x==connectionDoor.x && cell.y==connectionDoor.y )
          {
            cell.type = FloorType.DoorOpen;
            connectionDoor.type = FloorType.DoorOpen;
            break;
          }
      }
      return true;
    }