RRGGLIB
Jump to navigation
Jump to search
The printable version is no longer supported and may have rendering errors. Please update your browser bookmarks and please use the default browser print function instead.
Introduction
This library is a set of tools to make easily a Rouguelike game in Ruby.
Usage
It will make a data called Table wich is a two dimensional array with the data of each kind of dungeon. I suggest you should read each kind of dungeon to make sure you are trying to make the one you want.
These are the kind of dungeons:
#============================================================================================================== # ** RGG::Dungeons::grid_based(width, height[,wall_height, cell_width, cell_height]) #-------------------------------------------------------------------------------------------------------------- # * Creates a dungeon based on grids #-------------------------------------------------------------------------------------------------------------- # * Parameters: # - width : The width (in tiles) for the dungeon # - height : The height (in tiles) for the dungeon # - wall_height : The wall height (in tiles) of the dungeon, set as 0, if you dont want any. # (default = DEFAUL_WALL_HEIGHT) # - cell_width : The width of each cell (default 9) # - cell_height : the height of each cell (default 9) # * Returns: # - A Table Object(see Table) with sizes width x height containing the generated dungeon. #-------------------------------------------------------------------------------------------------------------- # * Generator Notes: # Advantages: # - This system is easy to use, and easy to maintain, since it can create big rooms easier than # other methods. # - This system ensures the connections of all rooms. # Disadvantages: # - Maps doesn't have to be completelly filled. # - Not too realisitc for cave layouts. # - Cells are predecible because they are the same size. #============================================================================================================== #============================================================================================================== # ** RGG::Dungeons::maze(width, height[,wall_height]) #-------------------------------------------------------------------------------------------------------------- # * Creates a perfect maze, trying to take all the possible space. #-------------------------------------------------------------------------------------------------------------- # * Parameters: # - width : The width (in tiles) for the dungeon # - height : The height (in tiles) for the dungeon # - wall_height : The wall height (in tiles) of the dungeon, set as 0, if you dont want any. # (default = DEFAUL_WALL_HEIGHT) # * Returns: # - A Table Object(see Table) with sizes width x height containing the generated dungeon. #-------------------------------------------------------------------------------------------------------------- # * Generator Notes: # Advantages: # - This system ensures that every space is rechable. # Disadvantages: # - Small size to put enemies, obejects, chests. # - Boring arquitecture. # - Quite slow. #============================================================================================================== #============================================================================================================== # ** RGG::Dungeons::bsp(width, height[,wall_height last_mov]) #-------------------------------------------------------------------------------------------------------------- # * Creates a Maze with the BSP method. #-------------------------------------------------------------------------------------------------------------- # * Parameters: # - width : The width (in tiles) for the dungeon # - height : The height (in tiles) for the dungeon # - wall_height : The wall height (in tiles) of the dungeon, set as 0, if you dont want any. # (default = DEFAULT_WALL_HEIGHT) # - last_mov : The starting maze direction (random by default, 0 = horizontal, else vertical) # * Returns: # - A Table Object(see Table) with sizes width x height containing the generated dungeon. #-------------------------------------------------------------------------------------------------------------- # * Generator Notes: # Advantages: # - This system ensures that every space is rechable. # - Easy maintained rooms. # - Better method than a normal cell based. # Disadvantages: # - This method would make easier dungeons than other ones. #============================================================================================================== #============================================================================================================== # ** RGG::Dungeons::cellular_automata(width, height[,wall_height last_mov]) #-------------------------------------------------------------------------------------------------------------- # * Creates a Dungeon with the BSP method. #-------------------------------------------------------------------------------------------------------------- # * Parameters: # - width : The width (in tiles) for the dungeon # - height : The height (in tiles) for the dungeon # - wall_height : The wall height (in tiles) of the dungeon, set as 0, if you dont want any. # (default = DEFAULT_WALL_HEIGHT) # * Returns: # - A Table Object(see Table) with sizes width x height containing the generated dungeon. #-------------------------------------------------------------------------------------------------------------- # * Generator Notes: # Advantages: # - This system ensures that every space is rechable. # - Ganarates a great irregular effect. (Specially good for caves) # Disadvantages: # - This method would make easier dungeons than other ones. # - Extremly slow method. # - May create "Big areas" filled with nothing. #============================================================================================================== #============================================================================================================== # ** RGG::Dungeons::mirrored(dungeon[, mode]) #-------------------------------------------------------------------------------------------------------------- # * Creates a Dungeon using a mirror method. # The width and height of the newly created dungeon is automatically recognized. #-------------------------------------------------------------------------------------------------------------- # * Parameters: # - dungeon : The dungeon data to mirror # - mode : The mirror mode (default is X and Y) # - wall_height : The height of the wall. (default = DEFAULT_WALL_HEIGHT) # * Returns: # - A Table Object(see Table) with sizes width x height containing the generated dungeon. #-------------------------------------------------------------------------------------------------------------- # * Generator Notes: # Advantages: # - Great effect for some places. # Disadvantages: # - Needs previous data to work, so it is just #==============================================================================================================
Code
#====================================================================================================================== # ** RANDOM GAME GENERATOR (RGG) #---------------------------------------------------------------------------------------------------------------------- # * Library for Rouguelike Generation in Ruby. #---------------------------------------------------------------------------------------------------------------------- # * Author: Ramiro Rojo. # * Version 1.0 #---------------------------------------------------------------------------------------------------------------------- # * Licence (Free BSD Licence): # Copyright 2011 Ramiro Rojo. All rights reserved. # # Redistribution and use in source and binary forms, with or without modification, are # permitted provided that the following conditions are met: # # 1. Redistributions of source code must retain the above copyright notice, this list of # conditions and the following disclaimer. # # 2. Redistributions in binary form must reproduce the above copyright notice, this list # of conditions and the following disclaimer in the documentation and/or other materials # provided with the distribution. # # THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ''AS IS'' AND ANY EXPRESS OR IMPLIED # WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND # FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> OR # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON # ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF # ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # # The views and conclusions contained in the software and documentation are those of the # authors and should not be interpreted as representing official policies, either expressed # or implied, of Ramiro Rojo. #---------------------------------------------------------------------------------------------------------------------- # * Thanks: # - Jim Babcock for the Cellular Automata code and tehory. # - Rougue Basin (http://roguebasin.roguelikedevelopment.org) for the BSP Generation, and normal rouguelike information. # - Emanuele Feronato(http://www.emanueleferonato.com) for the perfect maze generator. # - Alejandro Marzini (aka vgvgf) for the Table and Color classes. #====================================================================================================================== #====================================================================================================================== # ** Table Class #---------------------------------------------------------------------------------------------------------------------- # * A class for two or three dimensional arrays. #====================================================================================================================== class Table #------------------------------------------------------------------------------------------------------------------ # * Public Instance Variables #------------------------------------------------------------------------------------------------------------------ attr_reader :xsize # the x dimension of the table. attr_reader :ysize # the y dimension of the table. attr_reader :zsize # the z imension of the table. def initialize(x, y = 1, z = 1) @xsize, @ysize, @zsize = x, y, z @data = Array.new(x * y * z, 0) end def [](x, y = 0, z = 0) @data[x + y * @xsize + z * @xsize * @ysize] end def []=(*args) x = args[0] y = args.size > 2 ? args[1] :0 z = args.size > 3 ? args[2] :0 v = args.pop @data[x + y * @xsize + z * @xsize * @ysize] = v end def _dump(d = 0) s = [3].pack('L') s += [@xsize].pack('L') + [@ysize].pack('L') + [@zsize].pack('L') s += [@xsize * @ysize * @zsize].pack('L') for z in 0...@zsize for y in 0...@ysize for x in 0...@xsize s += [@data[x + y * @xsize + z * @xsize * @ysize]].pack('S') end end end s end def self._load(s) size = s[0, 4].unpack('L')[0] nx = s[4, 4].unpack('L')[0] ny = s[8, 4].unpack('L')[0] nz = s[12, 4].unpack('L')[0] data = [] pointer = 20 loop do data.push(*s[pointer, 2].unpack('S')) pointer += 2 break if pointer > s.size - 1 end t = Table.new(nx, ny, nz) n = 0 for z in 0...nz for y in 0...ny for x in 0...nx t[x, y, z] = data[n] n += 1 end end end t end end #====================================================================================================================== # ** Color Class #---------------------------------------------------------------------------------------------------------------------- # * The class containing color data in RGBA format. #====================================================================================================================== class Color #------------------------------------------------------------------------------------------------------------------ # * Public Instance Variables #------------------------------------------------------------------------------------------------------------------ attr_reader :red # the red component. attr_reader :green # the green component. attr_reader :blue # the blue component. attr_reader :alpha # the alpha (transparency) component. #================================================================================================================== # * Color#initialize(r, g, b[, a]) #------------------------------------------------------------------------------------------------------------------ # * Instantiates a new color #------------------------------------------------------------------------------------------------------------------ # * Parameters: # - r : The red component of the color # - g : The green component of the color # - b : The blue component of the color # - a : the alpha component of the color (default = 255) #================================================================================================================== def initialize(r, g, b, a = 255.0) self.red = r.to_f self.green = g.to_f self.blue = b.to_f self.alpha = a.to_f end #================================================================================================================== # * Color#set(r, g, b[, a]) #------------------------------------------------------------------------------------------------------------------ # * Sets all the data of the color directly #------------------------------------------------------------------------------------------------------------------ # * Parameters: # - r : The red component of the color # - g : The green component of the color # - b : The blue component of the color # - a : the alpha component of the color ( default = 255) #================================================================================================================== def set(r, g, b, a = 255.0) self.red = r.to_f self.green = g.to_f self.blue = b.to_f self.alpha = a.to_f end #================================================================================================================== # * Color#red =(val) #------------------------------------------------------------------------------------------------------------------ # * Sets the red component of the color #------------------------------------------------------------------------------------------------------------------ # * Parameters: # - val : The red component of the color #================================================================================================================== def red=(val) @red = [[val.to_f, 0.0].max, 255.0].min end #================================================================================================================== # * Color#green =(val) #------------------------------------------------------------------------------------------------------------------ # * Sets the green component of the color #------------------------------------------------------------------------------------------------------------------ # * Parameters: # - val : The green component of the color #================================================================================================================== def green=(val) @green = [[val.to_f, 0.0].max, 255.0].min end #================================================================================================================== # * Color#blue =(val) #------------------------------------------------------------------------------------------------------------------ # * Sets the blue component of the color #------------------------------------------------------------------------------------------------------------------ # * Parameters: # - val : The blue component of the color #================================================================================================================== def blue=(val) @blue = [[val.to_f, 0.0].max, 255.0].min end #================================================================================================================== # * Color#alpha =(val) #------------------------------------------------------------------------------------------------------------------ # * Sets the alpha component of the color #------------------------------------------------------------------------------------------------------------------ # * Parameters: # - val : The alpha component of the color #================================================================================================================== def alpha=(val) @alpha = [[val.to_f, 0.0].max, 255.0].min end #================================================================================================================== # * Color#color #------------------------------------------------------------------------------------------------------------------ # * Creates a duplication of the current color #------------------------------------------------------------------------------------------------------------------ # * Parameters: # - none. #================================================================================================================== def color Color.new(@red, @green, @blue, @alpha) end #================================================================================================================== # * Color::_dump #------------------------------------------------------------------------------------------------------------------ # * Stores the color in a binary file #------------------------------------------------------------------------------------------------------------------ # * Parameters: # - none #================================================================================================================== def _dump(d = 0) [@red, @green, @blue, @alpha].pack('d4') end #================================================================================================================== # * Color::_load #------------------------------------------------------------------------------------------------------------------ # * Loads a color from a binary file #------------------------------------------------------------------------------------------------------------------ # * Parameters: # - none. #================================================================================================================== def self._load(s) Color.new(*s.unpack('d4')) end #================================================================================================================== # * Color#==(other) #------------------------------------------------------------------------------------------------------------------ # * Cheks if the color is the same as other. #------------------------------------------------------------------------------------------------------------------ # * Parameters: # - other: other Color to compare. #================================================================================================================== def ==(other) (@red == other.red && @green == other.green && @blue == other.blue && @alpha == other.alpha) end #================================================================================================================== # * Color#===(other) #------------------------------------------------------------------------------------------------------------------ # * Cheks if the color is the same as other. #------------------------------------------------------------------------------------------------------------------ # * Parameters: # - other: other Color to compare. #================================================================================================================== def ===(other) (@red == other.red && @green == other.green && @blue == other.blue && @alpha == other.alpha) end end class Point attr_accessor :x attr_accessor :y MANHATTAN = 0 HYPOT = 1 def initialize(x, y) @x = x @y = y end def effort(x, y, mode = MANHATTAN) case mode when MANHATTAN; return ((@x - x) + (@y - y)).abs when HYPOT; return Math.hypot((@x - x), (@y - y)) else; return ((@x - x) + (@y - y)).abs end end def ==(other) other.is_a?(Point) ? (self.x == other.x && self.y == other.y) : false end def ===(other) other.is_a?(Point) ? (self.x == other.x && self.y == other.y) : false end end #====================================================================================================================== # ** RGG Module (Random Game Generator Module) #---------------------------------------------------------------------------------------------------------------------- # * The module to control all the ganaration of the library. #====================================================================================================================== module RGG #================================================================================================================== # ** RGG::Dungeons Module #------------------------------------------------------------------------------------------------------------------ # * The sub module that controls the dungeon generation. #================================================================================================================== module Dungeons #-------------------------------------------------------------------------------------------------------------- # * Module Constants #-------------------------------------------------------------------------------------------------------------- # the tile id for the "floor of the dungeon" FLOOR = 0 # the tile id for the wall of the dungeon WALL = 1 # the tile id for roofs of the dungeons ROOF = 2 # mirror x and y axis BOTH_MIRROR = 1 # mirror x axis X_MIRROR = 2 # mirror y axis Y_MIRROR = 3 # The default size of the floors of a dungeon DEFAULT_WALL_HEIGHT = 1 # Color codes used for tiled data (for testing orpouses ) COLORS = { ROOF => Color.new(255, 255, 255), WALL => Color.new(180, 180, 180), FLOOR => Color.new(60, 60, 60), } #============================================================================================================== # ** RGG::Dungeons::grid_based(width, height[,wall_height, cell_width, cell_height]) #-------------------------------------------------------------------------------------------------------------- # * Creates a dungeon based on grids #-------------------------------------------------------------------------------------------------------------- # * Parameters: # - width : The width (in tiles) for the dungeon # - height : The height (in tiles) for the dungeon # - wall_height : The wall height (in tiles) of the dungeon, set as 0, if you dont want any. # (default = DEFAUL_WALL_HEIGHT) # - cell_width : The width of each cell (default 9) # - cell_height : the height of each cell (default 9) # * Returns: # - A Table Object(see Table) with sizes width x height containing the generated dungeon. #-------------------------------------------------------------------------------------------------------------- # * Generator Notes: # Advantages: # - This system is easy to use, and easy to maintain, since it can create big rooms easier than # other methods. # - This system ensures the connections of all rooms. # Disadvantages: # - Maps doesn't have to be completelly filled. # - Not too realisitc for cave layouts. # - Cells are predecible because they are the same size. #============================================================================================================== def self.grid_based(width, height,wall_height = DEFAULT_WALL_HEIGHT, cell_width=9, cell_height=9) # sets up the data data = Table.new(width, height) for x in 0...width for y in 0...height data[x, y] = ROOF end end # define the numbers of horizontal and vertical rooms h = data.xsize / cell_width v = data.ysize / cell_height # define an arbitary number of total rooms total_rooms = [h * v / 2, rand(h * v) + 1].max rooms = 0 # sets the current room as the midde one. current_room = Point.new(h / 2, v / 2) first_room = Point.new(current_room.x, current_room.y) connections = Table.new(h, v) # sets up the connections for i in 0...h for j in 0...v connections[i, j] = 0 end end now_x = h / 2 now_y = v / 2 connections[now_x, now_y] = 1 for x in (now_x * cell_width + 1)...((now_x + 1) * cell_width - 1) for y in (now_y * cell_height + 1 + wall_height)...((now_y + 1) * cell_height - 1) data[x, y] = FLOOR end end last_x = now_x last_y = now_y can_create_room = true next_pos = [] gone_room_count = 4 while can_create_room d = [] d.push("N") if now_y > 0 && connections[now_x, now_y-1] == 0 d.push("S") if now_y < (v - 1) && connections[now_x, now_y+1] == 0 d.push("W") if now_x > 0 && connections[now_x-1, now_y] == 0 d.push("E") if now_x < (h - 1) && connections[now_x+1, now_y] == 0 if d.size == 0 can_create_room = false else c1 = [now_x * cell_width + cell_width / 2, now_y * cell_height + (cell_height - 1 - wall_height) / 2 + wall_height ] t = d.size - 1 + rand(2) (t).times do |i| case d[rand(d.size)] when "N" temp_y = now_y - 1 temp_x = now_x d.delete("N") when "S" temp_y = now_y + 1 temp_x = now_x d.delete("S") when "E" temp_x = now_x + 1 temp_y = now_y d.delete("E") when "W" temp_x = now_x - 1 temp_y = now_y d.delete("W") end connections[temp_x, temp_y] = 1 c2 = [temp_x * cell_width + cell_width / 2, temp_y * cell_height + (cell_height - 1 - wall_height) / 2 + wall_height ] if rand(gone_room_count) != 0 for x in (temp_x * cell_width + 1)...((temp_x + 1) * 9 - 1) for y in (temp_y * cell_height + 1 + wall_height)...((temp_y + 1) * cell_height - 1) data[x, y] = FLOOR end end gone_room_count -= 1 else gone_room_count += 1 end next_pos.push(Point.new(temp_x, temp_y)) x1 = [c1[0], c2[0]].min x2 = [c1[0], c2[0]].max y1 = [c1[1], c2[1]].min y2 = [c1[1], c2[1]].max for x in x1..x2 for y in y1..y2 data[x, y] = FLOOR end end end point = next_pos[rand(next_pos.size)] next_pos.delete(point) now_x = point.x now_y = point.y end end # returns the current data created return generate_walls(data, wall_height) end #============================================================================================================== # ** RGG::Dungeons::maze(width, height[,wall_height]) #-------------------------------------------------------------------------------------------------------------- # * Creates a perfect maze, trying to take all the possible space. #-------------------------------------------------------------------------------------------------------------- # * Parameters: # - width : The width (in tiles) for the dungeon # - height : The height (in tiles) for the dungeon # - wall_height : The wall height (in tiles) of the dungeon, set as 0, if you dont want any. # (default = DEFAUL_WALL_HEIGHT) # * Returns: # - A Table Object(see Table) with sizes width x height containing the generated dungeon. #-------------------------------------------------------------------------------------------------------------- # * Generator Notes: # Advantages: # - This system ensures that every space is rechable. # Disadvantages: # - Small size to put enemies, obejects, chests. # - Boring arquitecture. # - Quite slow. #============================================================================================================== def self.maze(width, height,wall_height = DEFAULT_WALL_HEIGHT) # sets up the data data = Table.new(width, height) for x in 0...width for y in 0...height data[x, y] = ROOF end end sparcing = (wall_height+2) for x in 0...width for y in 0...height data[x, y] = ROOF end end moves = [] moves.push(Point.new(rand(width - 1) + 1,rand(height - 1) + 1)) last_dir = nil while moves.size > 0 d = [] case rand(5) when 0; moves.sort! {|a, b| a.x<=>b.y} when 1; moves.sort! {|a, b| a.y<=>b.x} when 2; moves.sort! {|a, b| b.x<=>a.y} when 3; moves.sort! {|a, b| b.y<=>a.x} end move = moves.pop d.push("E") if data[move.x+sparcing, move.y] == ROOF && valid?(move.x+sparcing, move.y, data, wall_height) d.push("W") if data[move.x-sparcing, move.y] == ROOF && valid?(move.x-sparcing, move.y, data, wall_height) d.push("S") if data[move.x, move.y+sparcing] == ROOF && valid?(move.x, move.y+sparcing, data, wall_height) d.push("N") if data[move.x, move.y-sparcing] == ROOF && valid?(move.x, move.y-sparcing, data, wall_height) if d.size > 0 case d[rand(d.size)] when "N" for i in 0..sparcing data[move.x, move.y-+i] = FLOOR if valid?(move.x, move.y-i, data, wall_height) end moves.push(Point.new(move.x, move.y-sparcing)) when "S" for i in 0..sparcing data[move.x, move.y+i] = FLOOR if valid?(move.x, move.y+i, data, wall_height) end moves.push(Point.new(move.x, move.y+sparcing)) when "E" for i in 0..sparcing data[move.x+i, move.y] = FLOOR if valid?(move.x+i, move.y, data, wall_height) end moves.push(Point.new(move.x+sparcing, move.y)) when "W" for i in 0..sparcing data[move.x-i, move.y] = FLOOR if valid?(move.x-i, move.y, data, wall_height) end moves.push(Point.new(move.x-sparcing, move.y)) end end moves.push(move) if (d.size > 1 && !moves.include?(move)) moves.uniq! end return generate_walls(data, wall_height) end #============================================================================================================== # ** RGG::Dungeons::bsp(width, height[,wall_height last_mov]) #-------------------------------------------------------------------------------------------------------------- # * Creates a Maze with the BSP method. #-------------------------------------------------------------------------------------------------------------- # * Parameters: # - width : The width (in tiles) for the dungeon # - height : The height (in tiles) for the dungeon # - wall_height : The wall height (in tiles) of the dungeon, set as 0, if you dont want any. # (default = DEFAULT_WALL_HEIGHT) # - last_mov : The starting maze direction (random by default, 0 = horizontal, else vertical) # * Returns: # - A Table Object(see Table) with sizes width x height containing the generated dungeon. #-------------------------------------------------------------------------------------------------------------- # * Generator Notes: # Advantages: # - This system ensures that every space is rechable. # - Easy maintained rooms. # - Better method than a normal cell based. # Disadvantages: # - This method would make easier dungeons than other ones. #============================================================================================================== def self.bsp(width, height,wall_height = DEFAULT_WALL_HEIGHT, last_mov = rand(2)) return generate_walls(DungeonDivision.new(0,0, width, height, wall_height, last_mov).data, wall_height) end #============================================================================================================== # ** RGG:: DungeonDivision class #-------------------------------------------------------------------------------------------------------------- # * A class to help making a BSP dungeon. #============================================================================================================== class DungeonDivision #---------------------------------------------------------------------------------------------------------- # * Public instance variables #---------------------------------------------------------------------------------------------------------- attr_accessor :x # the x coordinate of this division. attr_accessor :y # the y coordinate of this division. attr_accessor :width # the width of this division. attr_accessor :height # the height of this division. attr_accessor :divisions # the divisions of this division. attr_accessor :data # the data of this division #============================================================================================================== # ** RGG:: DungeonDivision#initialize(x, y, width, height[,wall_height last_mov]) #-------------------------------------------------------------------------------------------------------------- # * Creates a Maze with the BSP method. #-------------------------------------------------------------------------------------------------------------- # * Parameters: # - x : the x coordinate of the dungeon. # - y : the y coordinate of the dungeon. # - width : The width (in tiles) for the dungeon # - height : The height (in tiles) for the dungeon # - wall_height : The wall height (in tiles) of the dungeon, set as 0, if you dont want any. # (default = DEFAULT_WALL_HEIGHT) # - last_mov : The starting maze direction (random by default, 0 = horizontal, else vertical) #============================================================================================================== def initialize(x, y, width, height,wall_height = 1, last_mov = 0) @x = x @y = y @width = width @height = height @divisions = [] @data = Table.new(width, height) for x in 0...width for y in 0...height @data[x, y] = ::RGG::Dungeons::ROOF end end iterations = (width > 15) && (height > 14 + wall_height) ? 1 : 0 if (iterations > 0) r = rand(3) r = 1 - last_mov if r > 1 case rand(2) when 0 w = width * (rand(21) + 40) / 100 @divisions.push(DungeonDivision.new(0, 0, w, height, wall_height, 0)) w = width - w @divisions.push(DungeonDivision.new(w, 0, w, height, wall_height, 0)) for x in 0...@divisions[0].width for y in 0...@divisions[0].height @data[x, y] = @divisions[0].data[x, y] end end for x in 0...@divisions[1].width for y in 0...@divisions[1].height @data[x + @divisions[0].width, y] = @divisions[1].data[x, y] end end y = height / 2 for x in (@divisions[0].width / 2)..(@divisions[0].width + @divisions[1].width / 2) @data[x, y] = ::RGG::Dungeons::FLOOR end when 1 h = height * (rand(21) + 40) / 100 @divisions.push(DungeonDivision.new(0, 0, width, h, wall_height, 1)) h = height - h @divisions.push(DungeonDivision.new(0, h, width, h, wall_height, 1)) for x in 0...@divisions[0].width for y in 0...@divisions[0].height @data[x, y] = @divisions[0].data[x, y] end end for x in 0...@divisions[1].width for y in 0...@divisions[1].height @data[x, y + @divisions[0].height] = @divisions[1].data[x, y] end end x = width / 2 for y in (@divisions[0].height / 2)..(@divisions[0].height + @divisions[1].height / 2) @data[x, y] = ::RGG::Dungeons::FLOOR end end else w = [rand(width) + rand(width / 2) - rand(width / 4), width - 4].min h = [rand(height) + rand(height / 2) - rand(height / 4), height - 6].min mx = (width - w) / 2 my = (height - h) / 2 for x in mx...mx+w for y in my...my+h @data[x, y] = ::RGG::Dungeons::FLOOR end end end end end #============================================================================================================== # ** RGG::Dungeons::cellular_automata(width, height[,wall_height last_mov]) #-------------------------------------------------------------------------------------------------------------- # * Creates a Dungeon with the BSP method. #-------------------------------------------------------------------------------------------------------------- # * Parameters: # - width : The width (in tiles) for the dungeon # - height : The height (in tiles) for the dungeon # - wall_height : The wall height (in tiles) of the dungeon, set as 0, if you dont want any. # (default = DEFAULT_WALL_HEIGHT) # * Returns: # - A Table Object(see Table) with sizes width x height containing the generated dungeon. #-------------------------------------------------------------------------------------------------------------- # * Generator Notes: # Advantages: # - This system ensures that every space is rechable. # - Ganarates a great irregular effect. (Specially good for caves) # Disadvantages: # - This method would make easier dungeons than other ones. # - Extremly slow method. # - May create "Big areas" filled with nothing. #============================================================================================================== def self.cellular_automata(width, height,wall_height = DEFAULT_WALL_HEIGHT) # sets up the data data = Table.new(width, height) for x in 0...data.xsize for y in 0...data.ysize data[x, y] = ROOF end end points = data.xsize * data.ysize * 10 / 100 while points > 0 rx = [rand(width ) / 2, 5].min ry = [rand(height) / 2, 5].min x = rand(width - 6 - rx * 2) + rx / 2 + 2 y = rand(height - 6 - ry * 2) + ry / 2 + 2 if rand(10) == 0 add_inverted_circle(x, y, rx, ry, data) else add_circle(x, y, rx, ry, data) end points -= rx * ry end points = width * height * 30 / 100 tries = 0 while points > 0 x = rand(data.xsize) y = rand(data.ysize) if check_walls(x, y,data, 1, wall_height+1) == 0 for i in y..(y + wall_height+1) data[x, i] = ROOF points -= wall_height + 2 tries = 0 end end tries += 1 points = 0 if tries > 10000 end leap = 1 for x in 1...((data.xsize - 1) / leap) for y in 1...((data.ysize-1) / leap) if data[x * leap, y * leap] == FLOOR join_points(Point.new(x * leap, y * leap), data, wall_height) end end end (2).times do |i| for x in 1...(data.xsize - 1) for y in 1...(data.ysize - 1) wall_count = check_walls(x, y, data) if wall_count > 5 data[x, y] = ROOF if data[x, y] elsif wall_count < 4 data[x, y] = FLOOR if data[x, y] end end end end # returns the current data created return generate_walls(data, wall_height) end #============================================================================================================== # ** RGG::Dungeons::mirrored(dungeon[, mode]) #-------------------------------------------------------------------------------------------------------------- # * Creates a Dungeon using a mirror method. # The width and height of the newly created dungeon is automatically recognized. #-------------------------------------------------------------------------------------------------------------- # * Parameters: # - dungeon : The dungeon data to mirror # - mode : The mirror mode (default is X and Y) # - wall_height : The height of the wall. (default = DEFAULT_WALL_HEIGHT) # * Returns: # - A Table Object(see Table) with sizes width x height containing the generated dungeon. #-------------------------------------------------------------------------------------------------------------- # * Generator Notes: # Advantages: # - Great effect for some places. # Disadvantages: # - Needs previous data to work, so it is just #============================================================================================================== def self.mirrored(dungeon, mode= Y_MIRROR, wall_height = DEFAULT_WALL_HEIGHT) # sets up the data width = (mode != Y_MIRROR) ? dungeon.xsize * 2 : dungeon.xsize height = (mode != X_MIRROR) ? dungeon.ysize * 2 : dungeon.ysize data = Table.new(width, height) for x in 0...width for y in 0...height data[x, y] = ROOF end end case mode when X_MIRROR for x in 0...(width / 2) for y in 0...(height) data[x, y] = dungeon[x, y] if valid_draw?(x, y, dungeon) end end case rand(2) when 0 x = width / 2 - 1 while x > 0 y = height - 1 while y > 0 if data[x, y] == FLOOR for i in x..(width / 2) data[i, y] = FLOOR end y = x = 0 end y -= 1 end x -= 1 end else x = width / 2 - 1 while x > 0 y = 0 while y < height - 1 if data[x, y] == FLOOR for i in x..(width / 2) data[i, y] = FLOOR end y = height x = 0 end y += 1 end x -= 1 end end for x in 0...(width / 2) for y in 0...(height) data[width - x - 1, y] = data[x, y] if valid_draw?(x, y, data) end end when Y_MIRROR for x in 0...(width) for y in 0...(height / 2) data[x, y] = dungeon[x, y] if valid_draw?(x, y, dungeon) end end case rand(2) when 0 x = width - 1 while x > 0 y = height / 2 - 1 while y > 0 if data[x, y] == FLOOR for i in y..(height / 2) data[x, i] = FLOOR end y = x = 0 end y -= 1 end x -= 1 end else x = 0 while x < width - 1 y = height / 2 - 1 while y > 0 if data[x, y] == FLOOR for i in y..(height / 2) data[x, i] = FLOOR end y = 0 x = width end y -= 1 end x += 1 end end for x in 0...(width) for y in 0...(height / 2) data[x,height - y - 2] = data[x, y] if valid_draw?(x, y, data) end end else for x in 0...(width / 2) for y in 0...(height / 2) data[x, y] = dungeon[x, y] = dungeon[x, y] if valid_draw?(x, y, dungeon) end end case rand(2) when 0 x = width / 2 - 1 while x > 0 y = height / 2 - 1 while y > 0 if data[x, y] == FLOOR for i in x..(width / 2) data[i, y] = FLOOR end y = x = 0 end y -= 1 end x -= 1 end else x = width / 2 - 1 while x > 0 y = 0 while y < height / 2 - 1 if data[x, y] == FLOOR for i in x..(width / 2) data[i, y] = FLOOR end y = height x = 0 end y += 1 end x -= 1 end end case rand(2) when 0 x = width / 2 - 1 while x > 0 y = height / 2 - 1 while y > 0 if data[x, y] == FLOOR for i in y..(height / 2) data[x, i] = FLOOR end y = x = 0 end y -= 1 end x -= 1 end else x = 0 while x < width / 2 - 1 y = height / 2 - 1 while y > 0 if data[x, y] == FLOOR for i in y..(height / 2) data[x, i] = FLOOR end y = 0 x = width end y -= 1 end x += 1 end end for x in 0...(width / 2) for y in 0...(height / 2) data[width - x - 1, y] = data[x, y] if valid_draw?(x, y, data) end end for x in 0...(width) for y in 0...(height / 2) data[x,height - y - 1] = data[x, y] if valid_draw?(x, y, data) end end end # returns the current data created return generate_walls(data, wall_height) end #============================================================================================================== # ** RGG::Dungeons::valid_draw?(x, y, data) #-------------------------------------------------------------------------------------------------------------- # * Checks if a point of the map is valid. #-------------------------------------------------------------------------------------------------------------- # * Parameters: # - x : the x coordinate to check. # - y : the y coordinate to check. # - data : the data to check. # * Returns: # - true if the position is valid, false elsewhere. #============================================================================================================== def self.valid_draw?(x, y, data) return false if x > data.xsize - 1 || x < 1 || y > data.ysize || y < 1 return false if !data[x, y] return false if data[x, y] != FLOOR return true end #============================================================================================================== # ** RGG::Dungeons::join_points(point, data[, wall_height]) #-------------------------------------------------------------------------------------------------------------- # * Join a point to the center of a dungeon. #-------------------------------------------------------------------------------------------------------------- # * Parameters: # - point : The point to join. # - data : The data where the point is. # - wall_height : The height of the wall. (default = DEFAULT_WALL_HEIGHT) #============================================================================================================== def self.join_points(point, data, wall_height= DEFAULT_WALL_HEIGHT) center_point = center(data) tries = 0 loop do dir = get_tunnel_dir(point, center_point) case rand(2) when 0 next_point = Point.new(dir.x + point.x, point.y) else next_point = Point.new(point.x, point.y + dir.y) end break if stop_drawing(point,next_point, data) || tries > 10000 || next_point.x < 1 || next_point.y < wall_height || next_point.x > (data.xsize - 1) || next_point.y > (data.ysize - 1) data[next_point.x, next_point.y] = FLOOR if valid?(next_point.x, next_point.y, data) point = next_point tries += 1 end end #============================================================================================================== # ** RGG::Dungeons::get_tunnel_dir(p1, p2) #-------------------------------------------------------------------------------------------------------------- # * Checks the direction between a "current point" and a "goal point". #-------------------------------------------------------------------------------------------------------------- # * Parameters: # - p1 : the current point. # - p2 : the goal point. # * Returns: # - a Point with the x and y coordinates of direction. #============================================================================================================== def self.get_tunnel_dir(p1, p2) dir = Point.new(0, 0) if p1.x < p2.x dir.x += 1 elsif p1.x > p2.x dir.x -= 1 end if p1.y < p2.y dir.y += 1 elsif p1.y > p2.y dir.y -= 1 end return dir end #============================================================================================================== # ** RGG::Dungeons::stop_drawing(p1, p2, data) #-------------------------------------------------------------------------------------------------------------- # * Determines if a "current point" has not reached a "goal point". #-------------------------------------------------------------------------------------------------------------- # * Parameters: # - p1 : the current point. # - p2 : the goal point. # - data : the data to check. # * Returns: # - true if it's needed to stop drawing, false elsewhere. #============================================================================================================== def self.stop_drawing(p1,p2, data) return true if p2 == center(data) return true if p1 != p2 && data[p2.x, p2.y] == FLOOR return false end #============================================================================================================== # ** RGG::Dungeons::check_walls(x, y, data[,distance, wall_height]) #-------------------------------------------------------------------------------------------------------------- # * hecks how many walls are arround a determined tile. #-------------------------------------------------------------------------------------------------------------- # * Parameters: # - x : the x coordinate to check. # - y : the y coordinate to check. # - data : the data to check. # - distance : the distance of checking (default = 1) # - wall_height : the height of the wall (default = DEFAULT_WALL_HEIGHT) # * Returns: # - a number representing the number of tiles in area # [x-distance ... x+distance; y-distance ... y+distance+wall_height] whow many tiles are walls. # including the centered tile as well. #============================================================================================================== def self.check_walls(x, y, data, distance = 1, wall_count = 0) sum = 0 for i in (x-distance)..(x+distance) for j in (y-distance)..(y+distance+wall_count) if (!valid?(x, y, data) || data[x, y] != FLOOR) sum += 1 end end end return sum end #============================================================================================================== # ** RGG::Dungeons::valid?(x, y, data[, wall_height]) #-------------------------------------------------------------------------------------------------------------- # * Checks if a determined point of the dungeon is able tobe drawn. #-------------------------------------------------------------------------------------------------------------- # * Parameters: # - x : the x coordinate to check. # - y : the y coordinate to check. # - data : the data to check. # - wall_height : the height of the wall (default = DEFAULT_WALL_HEIGHT) # * Returns: # - true if the position is valid, false elsewhere. #============================================================================================================== def self.valid?(x, y, data, wall_height = DEFAULT_WALL_HEIGHT) return (x > 0 && y > wall_height + 1 && x < data.xsize - 1 && y < data.ysize - 1) end #============================================================================================================== # ** RGG::Dungeons::center(data) #-------------------------------------------------------------------------------------------------------------- # * Gets the center position of the data. #-------------------------------------------------------------------------------------------------------------- # * Parameters: # - data : The data to check. # * Returns: # - a Point with the positions of the center of the data. #============================================================================================================== def self.center(data) return Point.new(data.xsize / 2, data.ysize / 2) end #============================================================================================================== # ** RGG::Dungeons::generate_walls(data[, wall_height]) #-------------------------------------------------------------------------------------------------------------- # * Generates the walls of a dungeon. #-------------------------------------------------------------------------------------------------------------- # * Parameters: # - x : the x coordinate to check. # - y : the y coordinate to check. # - data : the data to check. # - wall_height : the height of the wall (default = DEFAULT_WALL_HEIGHT) # * Returns: # - true if the position is valid, false elsewhere. #============================================================================================================== def self.generate_walls(data, wall_height = DEFAULT_WALL_HEIGHT) for x in 0...data.xsize for y in 0...data.ysize if data[x, y] == FLOOR need_wall = true for i in 1..(wall_height + 1) if !data[x, y-i] || data[x, y-i] != ROOF need_wall = false break end end if need_wall for i in 1..wall_height data[x, y-i] = WALL if data[x, y-i] end end end end end return revise_walls(data) end #============================================================================================================== # ** RGG::Dungeons::wall_complete?(x, y, data) #-------------------------------------------------------------------------------------------------------------- # * Checks if a wall can exist in that position. #-------------------------------------------------------------------------------------------------------------- # * Parameters: # - x : the x coordinate to check. # - y : the y coordinate to check. # - data : the data to check. # * Returns: # - true if the wall has to be there, false elsewhere. #============================================================================================================== def self.wall_complete?(x, y, data) for i in y...data.ysize return true if data[x, i] == WALL return false if data[x, i] != ROOF end return true end #============================================================================================================== # ** RGG::Dungeons::revise_walls(data) #-------------------------------------------------------------------------------------------------------------- # * Correct impossible walls. #-------------------------------------------------------------------------------------------------------------- # * Parameters: # - data : The data to check. # * Returns: # - the data with the corrections. #============================================================================================================== def self.revise_walls(data) for x in 0...data.xsize for y in 0...data.ysize if data[x, y] == ROOF data[x, y] = FLOOR if !wall_complete?(x, y, data) end end end return data end #============================================================================================================== # ** RGG::Dungeons::revise_walls(data) #-------------------------------------------------------------------------------------------------------------- # * Creates an inverted circular randomized space in a chunk of data. #-------------------------------------------------------------------------------------------------------------- # * Parameters: # - x : the x coordinate. # - y : the y coordinate. # - rx : the x radious of the circle. # - ry : the y radious of the circle. # - data : The data to add the circle. #============================================================================================================== def self.add_inverted_circle(x, y, rx, ry, data) cx = x + rx cy = y + ry x1 = (x - rx) x2 = (x + 2 * rx) y1 = (y - ry) y2 = (y + 2 * ry) r = Math.sqrt((rx * ry).abs) for i in x1..x2 for j in y1..y2 data[i, j] = FLOOR if valid?(i, j, data) && Math.hypot(i - cx, j - cy) >= r end end end #============================================================================================================== # ** RGG::Dungeons::revise_walls(data) #-------------------------------------------------------------------------------------------------------------- # * Creates a circular randomized space in a chunk of data. #-------------------------------------------------------------------------------------------------------------- # * Parameters: # - x : the x coordinate. # - y : the y coordinate. # - rx : the x radious of the circle. # - ry : the y radious of the circle. # - data : The data to add the circle. #============================================================================================================== def self.add_circle(x, y, rx, ry, data) cx = x + rx cy = y + ry x1 = (x - rx) x2 = (x + 2 * rx) y1 = (y - ry) y2 = (y + 2 * ry) r = Math.sqrt(rx * ry) for i in x1..x2 for j in y1..y2 data[i, j] = FLOOR if valid?(i, j, data) && Math.hypot(i - cx, j - cy) < r end end end end end
If you want to use it with the RPG Maker XP/VX program, rmemeber to delete the Color and Table classes.
You can take a view with that maker with this:
class Sprite_MapTest < Sprite TILE_SIZE = 4 def initialize(data) super() @data = data draw_bitmap end def draw_bitmap self.bitmap.dispose if self.bitmap self.bitmap = Bitmap.new(@data.xsize * TILE_SIZE, @data.ysize * TILE_SIZE) for i in 0...@data.xsize for j in 0...@data.ysize x = i * TILE_SIZE y = j * TILE_SIZE self.bitmap.fill_rect(x,y, TILE_SIZE, TILE_SIZE, RGG::Dungeons::COLORS[@data[i, j]]) end end end def dispose self.bitmap.dispose if self.bitmap super end end Sprite_MapTest.new(RGG::Dungeons.cellular_automata(80,80)) loop do Graphics.update end