RRGGLIB

From RogueBasin
Revision as of 06:12, 27 August 2011 by Ramiro (talk | contribs) (Created page with "=== Ruby Random Game Library === == Introduction == This library is a set of tools to make easily a Rouguelike game in Ruby. <code>#===========================================...")
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigation Jump to search

Ruby Random Game Library

Introduction

This library is a set of tools to make easily a Rouguelike game in Ruby.

#======================================================================================================================

  1. ** RANDOM GAME GENERATOR (RGG)
  2. ----------------------------------------------------------------------------------------------------------------------
  3. * Library for Rouguelike Generation in Ruby.
  4. ----------------------------------------------------------------------------------------------------------------------
  5. * Author: Ramiro Rojo.
  6. * Version 1.0
  7. ----------------------------------------------------------------------------------------------------------------------
  8. * Licence (Free BSD Licence):
  9. Copyright 2011 Ramiro Rojo. All rights reserved.
  10. Redistribution and use in source and binary forms, with or without modification, are
  11. permitted provided that the following conditions are met:
  12. 1. Redistributions of source code must retain the above copyright notice, this list of
  13. conditions and the following disclaimer.
  14. 2. Redistributions in binary form must reproduce the above copyright notice, this list
  15. of conditions and the following disclaimer in the documentation and/or other materials
  16. provided with the distribution.
  17. THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> AS IS AND ANY EXPRESS OR IMPLIED
  18. WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
  19. FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> OR
  20. CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  21. CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  22. SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
  23. ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
  24. NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
  25. ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  26. The views and conclusions contained in the software and documentation are those of the
  27. authors and should not be interpreted as representing official policies, either expressed
  28. or implied, of Ramiro Rojo.
  29. ----------------------------------------------------------------------------------------------------------------------
  30. * Thanks:
  31. - Jim Babcock for the Cellular Automata code and tehory.
  32. - Rougue Basin (http://roguebasin.roguelikedevelopment.org) for the BSP Generation, and normal rouguelike information.
  33. - Emanuele Feronato(http://www.emanueleferonato.com) for the perfect maze generator.
  34. - Alejandro Marzini (aka vgvgf) for the Table and Color classes.
  35. ======================================================================================================================
  1. ======================================================================================================================
  2. ** Table Class
  3. ----------------------------------------------------------------------------------------------------------------------
  4. * A class for two or three dimensional arrays.
  5. ======================================================================================================================

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

  1. ======================================================================================================================
  2. ** Color Class
  3. ----------------------------------------------------------------------------------------------------------------------
  4. * The class containing color data in RGBA format.
  5. ======================================================================================================================

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

  1. ======================================================================================================================
  2. ** RGG Module (Random Game Generator Module)
  3. ----------------------------------------------------------------------------------------------------------------------
  4. * The module to control all the ganaration of the library.
  5. ======================================================================================================================

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::Dungeon_Base::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 return data end

   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 [Category: Ruby] [Category: Libraries]