示例#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;
    }
示例#2
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;
    }
示例#3
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;
    }
示例#4
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;
    }
示例#5
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 });
 }
示例#6
0
    /// <summary>
    /// For the mazes that are "drawn" rather than generated, this will convert 
    ///   the bitmap to a FloorTile grid, using the colors specified in GenerateMaze()
    /// </summary>
    /// <param name="pic"></param>
    /// <returns></returns>
    private static FloorTile[,] TransposePicToGrid( Bitmap pic )
    {
      //tileColor = new Color[Enum.GetValues(typeof(FloorType)).Length];

      //tileColor[(int)FloorType.DoorClosed] = Color.Brown;
      //tileColor[(int)FloorType.DoorOpen] = Color.Tan;

      //tileColor[(int)FloorType.Wall] = Color.DarkGray;
      //tileColor[(int)FloorType.DoorSecret] = Color.DarkGray;

      //tileColor[(int)FloorType.Floor] = Color.BlanchedAlmond;
      //tileColor[(int)FloorType.Trap] = Color.BlanchedAlmond;

      //tileColor[(int)FloorType.StairsDown] = Color.Green;
      //tileColor[(int)FloorType.StairsUp] = Color.DarkGreen;


      grid = new FloorTile[pic.Width, pic.Height];

      for( int i=0; i<pic.Width; i++ )
      {
        for( int j=0; j<pic.Height; j++ )
        {
          grid[i, j] = new FloorTile(i, j);

          if( i==0 || i==pic.Width-1 || j==0 || j==pic.Height-1 )
            grid[i, j].type = FloorType.Wall;
          else
          {
            Color pixelColor = pic.GetPixel(i, j);

            for( int c=0; c<TileColor.Length; c++ )
              if( pixelColor.ToArgb() == TileColor[c].ToArgb() )
              {
                grid[i, j].type = (FloorType)Enum.Parse(typeof(FloorType), c.ToString());
                break;
              }
          }
        }
      }
      return grid;
    }
示例#7
0
    /// <summary>
    /// Implimentation fo A* pathing algorithm to find a path from one floortile to another.
    /// Used for generation of the Dungeon style mazes to display the room connections.
    /// Modified from mob AI pathfinding to disallow diagonal paths.
    /// 
    /// parms (as selected in the calling method) is an array of weights to apply:
    ///  parms[0] = continueStraight
    ///  parms[1] = turningWeight
    ///  parms[2] = pathNeighborWeight
    /// </summary>
    /// <param name="maze"></param>
    /// <param name="start"></param>
    /// <param name="end"></param>
    /// <param name="dlg"></param>
    /// <param name="parms"></param>
    /// <returns></returns>
    private static ArrayList FindPath( FloorTile[,] maze, FloorTile start, FloorTile end, DM_Uber_Tool.ProgressDialog dlg, object[] parms )
    {
      // 
      // pathing applies the following weights to cells : 
      //

      // default weight to move at all.  Applied to all options.
      int baseWeight          =  10;

      // adjust to affect pathing - long straight paths, or semi-windy ones
      int continueStraight    = (int)parms[0];
      int turningWeight       = (int)parms[1];
      int pathNeighborWeight  = (int)parms[2];



      // Following implimentation for room connections :
      //  N/S/E/W only (no diagonals, unlike mob pathing) and 
      //  must be at leaast 1 cell away from the edge of the map (allow for border)


      // clear prev pathing
      for( int i=0; i<maze.GetLength(0); i++ )
        for( int j=0; j<maze.GetLength(1); j++ )
          maze[i, j].cameFrom = null;

      // Initially, all rooms are in open (unconnected) set.
      // Once connected,the room is moved to the closed (connected) set.
      ArrayList closedSet = new ArrayList();
      ArrayList openSet   = new ArrayList();

      maze[start.x, start.y].G = 0;                                                          // G-Score = distance from self to this cell.  Starting cell distance = 0;
      maze[start.x, start.y].H = 10*(Math.Abs(start.x-end.x) + Math.Abs(start.y-end.y)); // Guess at distance from start cell to hero - calculated with no diagonals, and a tile weight of 10 (arbitrary)
      maze[start.x, start.y].F = 0;                                                          // F-Score = total distance traveled from start to this cell.  Start cell traveled dist = 0

      openSet.Add(maze[start.x, start.y]);                                                 // Open set is to be evaluated.  Add starting cell to this set.

      // evaluate cells in openSet until the hero cell is found
      while( openSet.Count>0 )
      {
        openSet.Sort();                             // To always pick the next cell with the lowest actual traveled distance.

        FloorTile current = openSet[0] as FloorTile;  // grab the next path square with the lowest F-Score value

        // prgress dialog pointer set
        dlg.current = current;

        if( current == maze[end.x, end.y] )
        {
          // Path found : assemble path, return list of cells
          ArrayList path = new ArrayList();
          while( current.cameFrom != null )
          {
            path.Add(new Point(current.x, current.y));
            current = current.cameFrom;
          }
          path.Add(new Point(start.x, start.y));
          return path;
        }

        openSet.Remove(current);    // cell being evaluated is removed from the openSet
        closedSet.Add(current);     //  ... and added to the closedSet

        // add all valid neighbors-of-current cells to the openSet
        for( int i=-1; i<=1; i++ )
        {
          for( int j=-1; j<=1; j++ )
          {
            if( (i==-1||i==1)&&(j==-1||j==1) )
              continue; // no diagonal paths to connect rooms

            if( current.x+i <= 0
              ||current.x+i >= maze.GetLength(0)-1
              ||current.y+j <= 0
              ||current.y+j >= maze.GetLength(1)-1 )
              continue; // no cells on boarder or map

            FloorTile neighbor = maze[current.x+i, current.y+j];

            // if the neighbor has already been looked at, don't revisit it.
            if( closedSet.Contains(neighbor) )
              continue;

            // determine tenative G-Score for neighbor cell
            int tenativeGScore = current.G; // base value = current cell G-Score

            // Add a weight to move to the neighbor cell
            tenativeGScore += baseWeight;

            if( current.cameFrom != null && current.cameFrom.cameFrom != null )
            {
              if( (neighbor.x == current.x && current.x == current.cameFrom.x)
                ||(neighbor.y == current.y && current.y == current.cameFrom.y)
                )
                tenativeGScore += continueStraight;
              else
                tenativeGScore += turningWeight;
            }

            // Add a penalty for each path neighbor cell this cell has 
            //  (highly discourage traveling through rooms, try to avoid crossing paths)
            for( int ni=-1; ni<=1; ni++ )
              for( int nj=-1; nj<=1; nj++ )
                if( !(maze[neighbor.x+ni, neighbor.y+nj].type==FloorType.Wall) )
                  tenativeGScore += pathNeighborWeight;


            bool tenativeGScoreIsBetter = false;  // pessimisitc default

            if( !openSet.Contains(neighbor) )
            {
              openSet.Add(neighbor);
              tenativeGScoreIsBetter = true;
            }
            else if( tenativeGScore < current.G )
            {
              tenativeGScoreIsBetter = true;
            }

            // set values and cameFrom 'pointers'
            if( tenativeGScoreIsBetter )
            {
              neighbor.cameFrom = current;
              neighbor.G = tenativeGScore;
              neighbor.H = 10*(Math.Abs(neighbor.x-end.x) + Math.Abs(neighbor.y-end.y));  // arbitrary heuristic - new distance to goal cell, straight line
              neighbor.F = neighbor.G + neighbor.H;
            }
          }
        }
      }

      // Clean up after myself
      openSet.Clear();
      closedSet.Clear();

      return new ArrayList(); // no path found
    }
示例#8
0
    /// <summary>
    /// Random room placement, then random rall connections
    /// 
    /// Pplace several rooms int the maze
    /// Pathing algorithm to connect wall cells to another room's wall cell
    ///  Unlike mob and player patihing, only horizontal and vertical moves are allowed.
    ///  Pathing weights
    ///    To create hallways, cells that neighbor already pathed cells will have a penalty to their weight.
    ///    If pathing tunels into an existing tunnel, existing tunnel cells will have a weight of 1/2 normal.
    /// 
    /// height*width*poathwidth will be used to create the total area to place rooms in
    /// rooms will be 1x to 2x pathWidth in size, i.e.: random( pathwidth, 2*pathWidth)
    /// </summary>
    /// <param name="width"></param>
    /// <param name="height"></param>
    /// <param name="pathWidth"></param>
    /// <returns></returns>
    private static FloorTile[,] GenerateStyle_Dungeon( int width, int height, int pathWidth )
    {
      DM_Uber_Tool.ProgressDialog dlg = new DM_Uber_Tool.ProgressDialog();
      dlg.status = "Initializing...";
      dlg.ShowProgressDialog();

      // set overall maze size
      Bitmap pic = new Bitmap(width*pathWidth, height*pathWidth);
      //FloorTile[,] grid = new FloorTile[width*pathWidth, height*pathWidth];
      grid = new FloorTile[width * pathWidth, height * pathWidth];

      for( int i=0; i<width*pathWidth; i++ )
        for( int j=0; j<pathWidth*height; j++ )
          grid[i, j] = new FloorTile(i, j);

      Graphics g = Graphics.FromImage(pic);
      g.Clear(TileColor[(int)FloorType.Wall]); // all walls up

      Pen wallsUp   = new Pen(TileColor[(int)FloorType.Wall], 1);
      Pen wallsDown = new Pen(TileColor[(int)FloorType.Floor], 1);

      ArrayList rooms = new ArrayList();

      // place rooms - must have a 'roomSpacing'-cell border to allow pathing along the edges
      int roomSpacing = 3;
      int rx=0, 
          ry=0, 
          rw=0, 
          rh=0;
      int tries = 0;
      int totalRooms = 0;


      while( tries < 50 )
      {
        rx = rand.Next(roomSpacing, width*pathWidth-1-roomSpacing);
        ry = rand.Next(roomSpacing, height*pathWidth-1-roomSpacing);
        rw = rand.Next(pathWidth, pathWidth*3);
        rh = rand.Next(pathWidth, pathWidth*3);

        bool valid = true;

        // check room boundaries - no overlaps, 3 cell gap between them all
        for( int i=rx-roomSpacing; i<rx+rw+roomSpacing && valid; i++ )
          for( int j=ry-roomSpacing; j<ry+rh+roomSpacing && valid; j++ )
          {
            valid = i >= 0 &&
                    i <  width*pathWidth &&
                    j >= 0 &&
                    j <  height*pathWidth &&
                    grid[i, j].type==FloorType.Wall;
          }

        if( valid )
        {
          ArrayList room = new ArrayList();

          // flags for checking new rooms
          for( int i=rx; i<rx+rw; i++ )
            for( int j=ry; j<ry+rh; j++ )
            {
              // flag set to part of the room
              grid[i, j].type = FloorType.Floor;

              // borders of room added to list of potential cells to connect to other rooms
              // elaborate conditions are to attempt to not use the corner cells of the room for connections
              //if(  ((i==rx || i==rx+rw-1) && (j>ry && j<ry+rh-1))
              //  || ((j==ry || j==ry+rh-1) && (i>rx && i<rx+rw-1))
              //  )
              //  room.Add( m[i, j] );  // edge wall - add to room border
              if( i==rx && (j>ry && j<ry+rh-1) )
                room.Add(grid[i-1, j]);

              if( i==rx+rw-1 && (j>ry && j<ry+rh-1) )
                room.Add(grid[i+1, j]);

              if( j==ry && (i>rx && i<rx+rw-1) )
                room.Add(grid[i, j-1]);

              if( j==ry+rh-1 && (i>rx && i<rx+rw-1) )
                room.Add(grid[i, j+1]);
            }

          rooms.Add(room);

          // picture of maze
          g.FillRectangle(wallsDown.Brush, rx, ry, rw, rh);
          totalRooms++;

          tries = 0;
        }
        else
          tries++;
      }

      dlg.flags = grid;
      dlg.status = string.Format("Connected {0}/{1} rooms...", totalRooms-rooms.Count, totalRooms);
      dlg.percentDone = (double)(totalRooms-rooms.Count)/totalRooms;

      // find center-most room
      ArrayList centerRoom = (ArrayList)rooms[0];
      ArrayList connected = new ArrayList();

      int r1x=0, r1y=0, r2x=0, r2y=0, dist = int.MaxValue;

      foreach( ArrayList room in rooms )
      {
        if( room == centerRoom )
          continue;

        r1x = ((FloorTile)room[0]).x;
        r1y = ((FloorTile)room[0]).y;
        r2x = grid.GetLength(0)/2;
        r2y = grid.GetLength(1)/2;

        int tmpDist = (int)(Math.Pow(r1x-r2x, 2)+Math.Pow(r1y-r2y, 2));

        if( tmpDist < dist )
        {
          centerRoom = room;
          dist = tmpDist;
        }
      }

      connected.Add(centerRoom);
      rooms.Remove(centerRoom);

      // rooms ready to connect - define pathing weights/rules
      int turningWeight=0;
      int continueStraight = 0;
      int pathNeighborWeight = 0;

      switch( rand.Next(3) )
      {
        case 0:
          // favor long, straight horiz and vert paths
          turningWeight += 10;
          break;

        case 1:
          // favor long diagonal paths (i.e. : left, up, left, up, left, up instead of left, left, left, left, up, up, up, up)
          continueStraight += 10;
          break;

        default:
          // no change - paths between rooms will be somewhat haphazard and jaggedy.
          break;
      }

      // 2-in-3 chance to bais _against_ tunneling through existing paths, but allows if needed.
      //  Will try to tunnel around existing paths when set, otherwise the most direct route (through paths or rooms alike) will be used.
      pathNeighborWeight  = rand.Next(3)==0 ? 5 : 20;


      /*
       * DEBUG - override random set values for specific pathing behavior.
       */
      //continueStraight = 0;
      //turningWeight = 20;
      //pathNeighborWeight = 50;


      object[] parms = new object[] { continueStraight, turningWeight, pathNeighborWeight };

      // for each room, pick 1 or more cells that will connect to the rest
      //  add these cells to the list of doorsToConnect and Doors
      while( rooms.Count > 0 )
      {
        // pick the closest room to center from rooms (unconnected),
        dist = int.MaxValue;
        ArrayList room1 = null;

        if( rand.Next(100) == 0 )  // 1% chance to grab ANY room to connect (leads to occasional long path to wherever, takes longer to path to)
        {
          room1 = (ArrayList)(rooms[rand.Next(rooms.Count)]);
        }
        else
        {
          foreach( ArrayList tmpRoom in rooms )
          {
            r1x = ((FloorTile)tmpRoom[0]).x;
            r1y = ((FloorTile)tmpRoom[0]).y;
            r2x = grid.GetLength(0)/2;
            r2y = grid.GetLength(1)/2;

            int tmpDist = (int)(Math.Pow(r1x-r2x, 2)+Math.Pow(r1y-r2y, 2));
            if( tmpDist < dist )
            {
              room1 = tmpRoom;
              dist = tmpDist;
            }
          }
        }

        // find closest connected room to room1 - these will be connected
        dist = int.MaxValue;
        ArrayList room2 = null;

        if( rand.Next(100) == 0 )  // 1% chance to grab ANY room to connect (leads to occasional long path to wherever, takes longer to path to)
        {
          room2 = (ArrayList)(connected[rand.Next(connected.Count)]);
        }
        else
        {
          foreach( ArrayList tmpRoom in connected )
          {
            r1x = ((FloorTile)tmpRoom[0]).x;
            r1y = ((FloorTile)tmpRoom[0]).y;
            r2x = ((FloorTile)room1[0]).x;
            r2y = ((FloorTile)room1[0]).y;

            int tmpDist = (int)(Math.Pow(r1x-r2x, 2)+Math.Pow(r1y-r2y, 2));
            if( tmpDist < dist )
            {
              room2 = tmpRoom;
              dist = tmpDist;
            }
          }
        }

        FloorTile start = (FloorTile)room1[rand.Next(room1.Count)];
        FloorTile end   = (FloorTile)room2[rand.Next(room2.Count)];

        connected.Add(room1);
        rooms.Remove(room1);

        dlg.flags  = grid;
        dlg.start  = start;
        dlg.end    = end;
        dlg.status = string.Format("Connected {0}/{1} rooms...", totalRooms-rooms.Count, totalRooms);
        dlg.percentDone = (double)(totalRooms-rooms.Count)/totalRooms;

        ArrayList path = FindPath(grid, start, end, dlg, parms);

        foreach( Point p in path )
        {
          grid[p.X, p.Y].type = FloorType.Floor;
          pic.SetPixel(p.X, p.Y, TileColor[(int)FloorType.Floor]);
        }
      }

      dlg.CloseProgressDialog();

      return TransposePicToGrid(pic);
    }
示例#9
0
    /// <summary>
    /// Proposed implimentation : assume a tile is about 2ft square 
    ///  Great Rooms/Halls (30-40ft by 60-100 ft, rectangular )
    ///  Living rooms (15-20 ft square)
    ///  Bedrooms (small - 10ft to 15ft square or rectangular)
    ///  Closet/Storage rooms (tiny - 4ft to 6ft square or rectangular)
    /// 
    /// Connections : 
    ///  Place largest room first
    ///  Spawn hallways from that room (based on room size - great halls should get several, living areas get 2, or soemthing similar to that logic)
    ///  Chance to spawn halls of arbitrary length from existing halls (make a more detailed castle layout?)
    ///  
    ///  Place remaining rooms/closets along halls/rooms with 1 cell wall between and open a path to the nearest room or rooms
    ///    Halls get as many as they have rooms neighboring,
    ///    Living areas get several, but not necessarily one for every room
    ///    Bedrooms get one
    ///    Closets get one
    ///    
    ///  After all rooms get placed, scan hallways for open wall connections, and attach closets sporatically to fill them out some
    ///  
    /// Possibilities : 
    ///  Percent total area covered by each room type
    ///    Generate Great Halls until that percent area is covered,
    ///    Generate Living Areas until that percent area is covered,
    ///    Generate Bedrooms until that percent area is covered,
    ///    Spawn closets and storage like crazy
    ///    Stop generation loop once closet count is reached, all bedrooms have at least one closet OR no valid spots left to add.
    ///    
    ///  Specific count of each room type (possibly easier) :
    ///    If Greatroom count > 0
    ///      Spawn greatroom(s)
    ///      Spawn hallway(s)
    ///        Connect Greatrooms if multiple
    ///      
    ///    If Living Areas count > 0
    ///      Spawn living areas
    ///      Spawn hallways
    ///        Connect to all rooms within a certain radius?  Well connected floorplan...?
    ///      Spawn secret passages (muahaha...)
    ///      
    ///    If Bedroom count > 0
    ///      Spawn bedrooms
    ///      Spawn closets
    ///      Spawn secret passages (even more muahaha...)
    ///      
    ///    Spawn closets along remaining hallways, minimum distance from existing rooms/doorways (avoid over populating with closets, or stop at set count?)
    ///      Spawn secret passages from closets (rediculous quantities of muahaha...)
    ///      
    ///    For each room type, auto spawn specific loot - i.e.: pre-populated (pseudo random) chests.
    /// </summary>
    /// <param name="width"></param>
    /// <param name="height"></param>
    /// <param name="pathWidth"></param>
    /// <param name="percentFull"></param>
    /// <returns></returns>
    private static FloorTile[,] GenerateStyle_Castle( int width, int height, int pathWidth, float percentFull )
    {
      // Progress dialog usually doesn't get to display for too long... might not be needed.
      DM_Uber_Tool.ProgressDialog dlg = new DM_Uber_Tool.ProgressDialog();

      // set overall maze size and initialize each cell
      int castleScale = 1;
      grid = new FloorTile[width * pathWidth * castleScale, height * pathWidth * castleScale];
      for( int i = 0; i < width * pathWidth * castleScale; i++ )
        for( int j = 0; j < pathWidth * height * castleScale; j++ )
          grid[i, j] = new FloorTile(i, j); // defaults to Wall

      // set progress dialog references
      dlg.ShowProgressDialog();
      dlg.flags = grid;
      dlg.status = "Placing rooms...";
      dlg.percentDone = 0;

      List<Room> rooms = new List<Room>();
      Room room = null;
      List<CastleRoomType> roomsToPlace = CreateCastleLayout();

      if( roomsToPlace == null )
      {
        #region If the layout is null, this will simply jam room after room into the availabel area until it can't fit any more
        int tries = 0;
        int maxTries = 5;

        while( tries++ < maxTries )
        {
          int doorCount = 0;
          foreach( Room r in rooms )
            foreach( FloorTile d in r.doors )
              if( d.type == FloorType.DoorClosed )
                doorCount++;

          dlg.status = string.Format("Placing Rooms (fails={0}, rooms={1}, doors={2})", tries, rooms.Count, doorCount);

          CastleRoomType roomType = (CastleRoomType)rand.Next(Enum.GetValues(typeof(CastleRoomType)).Length);

          if( (room = GenerateGenericRoom(rooms, roomType)) != null )
          {
            // to list of existing rooms for next room to refernce doors from
            rooms.Add(room);

            // update the grid[,] of placed tiles
            foreach( FloorTile cell in room.cells )
              grid[cell.x, cell.y].type = cell.type;

            tries = 0;  // reset once a room is placed
          }
        }
        #endregion
      }
      else
      {
        #region Specified layout provided - will try to use only those rooms, in the specified order when placing
        // For each room in the list of rooms to place, try to place that room
        for( int r = 0; r < roomsToPlace.Count; r++ )
        {
          if( (room = GenerateGenericRoom(rooms, roomsToPlace[r])) != null )
          {
            // to list of existing rooms for next room to refernce doors from
            rooms.Add(room);

            // update the grid[,] of placed tiles
            foreach( FloorTile cell in room.cells )
              grid[cell.x, cell.y].type = cell.type;
          }
        }
        #endregion
      }

      // Once all rooms are placed, find all doors and try to connect a closet to each one.  Fill it out a little.
      // If a door cannot have a closet attached, remove the door (set back to wall)
      List<FloorTile> doors = new List<FloorTile>();
      foreach( Room r in rooms )
        foreach( FloorTile d in r.doors )
          if( d.type == FloorType.DoorClosed )
            doors.Add( d );

      foreach( FloorTile targetDoor in doors )
      {
        if( (room = GenerateGenericRoom(rooms, CastleRoomType.Closet, targetDoor)) != null )
        {
          // to list of existing rooms for next room to refernce doors from
          rooms.Add(room);

          // update tje grid[,] of placed tiles
          foreach( FloorTile cell in room.cells )
          {
            if( cell.x==targetDoor.x && cell.y==targetDoor.y )
              grid[cell.x, cell.y].type = FloorType.DoorOpen;
            else
              grid[cell.x, cell.y].type = cell.type;
          }
        }
        else
        {
          // couldn't attach a closet - change from door to wall (cosmetic, really)

          //
          // TODO - need to remove doors from rooms when being attached, both the new placed room and the room that was attached to.
          //        Otherwise this removes all the currently connected doors... not sure how to tackle this yet.
          grid[ targetDoor.x, targetDoor.y].type = FloorType.Floor;
        }
      }

      //finally pass - close all doors that were opened as flags
      for( int i = 0; i < width * pathWidth * castleScale; i++ )
        for( int j = 0; j < pathWidth * height * castleScale; j++ )
          if( grid[i,j].type == FloorType.DoorOpen )
            grid[i,j].type = FloorType.DoorClosed;

      //dlg.CanClose = true; // generation done - let the user click to close after admiring the nifty black&white for a bit.
      dlg.CloseProgressDialog();

      return grid;
    }
示例#10
0
        private void OnPreviewPaint( object sender, PaintEventArgs e )
        {
            if( flags == null )
            return;

              if( pic == null )
              {
            pic = new Bitmap( flags.GetLength( 0 ), flags.GetLength( 1 ) );
            ClientSize = new Size( ClientRectangle.Width - pictureBox2.Width + pic.Width,
                               ClientRectangle.Height- pictureBox2.Height + pic.Height );
              }

              if( pic == null )
            return;

              for( int i=0; i<pic.Width; i++ )
            for( int j=0; j<pic.Height; j++ )
            {
              if(flags[i, j].cameFrom==null)
            pic.SetPixel(i, j, flags[i, j].type==FloorType.Wall ? Color.Black : Color.White);
              else
            pic.SetPixel(i, j, flags[i, j].type==FloorType.Wall ? Color.Gray  : Color.LightGray);
            }

              while( current != null )
              {
            pic.SetPixel( current.x, current.y, Color.Blue );
            current = current.cameFrom;
              }

              e.Graphics.DrawImageUnscaled( pic, 0, 0 );

              if( start != null && end != null )
            e.Graphics.DrawLine( Pens.Red, start.x, start.y, end.x, end.y );
        }