Exemple #1
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);
    }
Exemple #2
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;
    }