Difference between revisions of "RRGGLIB"

From RogueBasin
Jump to navigation Jump to search
(Created page with "=== Ruby Random Game Library === == Introduction == This library is a set of tools to make easily a Rouguelike game in Ruby. <code>#===========================================...")
 
Line 4: Line 4:
  This library is a set of tools to make easily a Rouguelike game in Ruby.
  This library is a set of tools to make easily a Rouguelike game in Ruby.


<code>#======================================================================================================================
<pre>#======================================================================================================================
# ** RANDOM GAME GENERATOR (RGG)
# ** RANDOM GAME GENERATOR (RGG)
#----------------------------------------------------------------------------------------------------------------------
#----------------------------------------------------------------------------------------------------------------------
Line 1,289: Line 1,289:
end
end
end</code>
end</pre>
[Category: Ruby]
[Category: Ruby]
[Category: Libraries]
[Category: Libraries]

Revision as of 06:14, 27 August 2011

Ruby Random Game Library

Introduction

This library is a set of tools to make easily a Rouguelike game in Ruby.
#======================================================================================================================
# ** 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::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]