SephirothSpawn
Sponsor
Random Map Maker
Version 0.2.2 - December 12th, 2008
Introduction
This script makes a random map given certain specifications (much like in rmvx, the difference being the type of map produced here). Please do not be expecting magic from it right now. I only started it last night and spent most of the time recording possible autotile ids (and coming up with a algorithm for passability that i scrapped because it took too long and was ineffective).
Please post suggestions or additions I can make. I hope by now everyone knows I love customization so if you have any ideas on improvement, please do share. I already have tons of plans to develop this further, but within my entire experience with RPG Maker in over 3 years, I have made maybe 20 maps, of which were horrible quality. So you mappers, help me out.
As of now, just copy and paste the script in a project. Do not put this in your project as this over-writes Map001.rxdata once the game is run. When you press new game you will be standing on a random map. The tileset is choosen at random of the 50 in the game. Floors and walls can be further modified under module Map_Maze_Maker, where you create a list of tile ids for the floor or wall.
I know some maps generated are rather horrible so just press F12. You'll get a new map in under a second.
Let me know what you guys think.
Heres a few screens of a few maps it produced:
http://owainc.net/screenshots/rand_map_maker/screen1.PNG
http://owainc.net/screenshots/rand_map_maker/screen2.PNG
http://owainc.net/screenshots/rand_map_maker/screen3.PNG
Version 0.2.2 - December 12th, 2008
Introduction
This script makes a random map given certain specifications (much like in rmvx, the difference being the type of map produced here). Please do not be expecting magic from it right now. I only started it last night and spent most of the time recording possible autotile ids (and coming up with a algorithm for passability that i scrapped because it took too long and was ineffective).
Please post suggestions or additions I can make. I hope by now everyone knows I love customization so if you have any ideas on improvement, please do share. I already have tons of plans to develop this further, but within my entire experience with RPG Maker in over 3 years, I have made maybe 20 maps, of which were horrible quality. So you mappers, help me out.
As of now, just copy and paste the script in a project. Do not put this in your project as this over-writes Map001.rxdata once the game is run. When you press new game you will be standing on a random map. The tileset is choosen at random of the 50 in the game. Floors and walls can be further modified under module Map_Maze_Maker, where you create a list of tile ids for the floor or wall.
I know some maps generated are rather horrible so just press F12. You'll get a new map in under a second.
Code:
#==============================================================================
# ** Map Maze Maker
#------------------------------------------------------------------------------
# SephirothSpawn
# Version 0.2.2 (BETA)
# 2008-12-13
#------------------------------------------------------------------------------
# * As of now this script is just in it's beta form. It produces random
# "maze like" maps given map size, enterance points, etc.
#
# * Our first step is to setup our map
# - Map_Maze_Maker.generate_map(tileset_id, width, height, enc, floor)
#
# tileset_id = tileset_id from database
# width = map width (defaults to 20)
# height = map height (defaults to 15)
# enc = encounter list [troop_id, ...] (defaults to [])
# fllor = floor tile_id (defaults to random tile for tileset)
#
# * Our next step is to create random pathways to our target point
# - Map_Maze_Maker.generate_passability_table(points, target)
#
# points = enterance points to map [[x, y]... ] (defaults to random)
# target = target path end point [x, y] (defaults to random)
#
# * Our next step is to setup our walls
# - Map_Maze_Maker.generate_data(wall)
#
# wall = impassable autotile id (defaults to random for tileset)
#
# * Our final step is to finalize our data
# - Map_Maze_Maker.finalize_map
#
#
# ** Optional steps (after generate_passability_table):
#
# * Creating room around target room
# - Map_Maze_Maker.generate_target_room(width, height)
#
# width = width of room (defaults to random)
# height = height of room (defaults to random)
#
# * Creating a specified room
# - Map_Maze_Maker.generate_room(x, y, width, height)
#
# x, y = top left location of room
# width = width of room
# height = height of room
#------------------------------------------------------------------------------
# * Version History
#
# Version 0.1 ---------------------------------------------------- (2008-12-12)
# Version 0.1.1 -------------------------------------------------- (2008-12-12)
# - Fix: Path going off map
# Version 0.1.2 -------------------------------------------------- (2008-12-13)
# - Update: Configured floors and walls default tilesets
# Changed so "walls" may be drawn on different layers
# Version 0.2 ---------------------------------------------------- (2008-12-13)
# - Update: Updated format layout (seperated methods)
# Added room creation
# Version 0.2.1 -------------------------------------------------- (2008-12-13)
# - Fix: Fixed ID & Cant_Include constants
# Version 0.2.2 -------------------------------------------------- (2008-12-13)
# - Update: Added fllor decorations
#==============================================================================
#==============================================================================
# ** Map Maze Maker - Configuration
#==============================================================================
module Map_Maze_Maker
#--------------------------------------------------------------------------
# * Map Floors = {tileset_id => [floor_id, ...], ...}
#--------------------------------------------------------------------------
Floors = {}
Floors[3] = (384..388).to_a
Floors[5] = [385]
Floors[6] = [385,386]
Floors[9] = (384..385).to_a
Floors[18] = (384..389).to_a
Floors[20] = (384..388).to_a
Floors[22] = (384..389).to_a
Floors[24] = (384..386).to_a
Floors[26] = (384..387).to_a
Floors[27] = (384..387).to_a
Floors[27] = (384..385).to_a
Floors[29] = (384..388).to_a
Floors[30] = (384..387).to_a
Floors[31] = (384..386).to_a
Floors[32] = (384..385).to_a
Floors[33] = (384..386).to_a
Floors[36] = (384..389).to_a
Floors[38] = (384..389).to_a
Floors[40] = (384..388).to_a
Floors[41] = (384..385).to_a
Floors[42] = (384..386).to_a
Floors[47] = (384..385).to_a
Floors[48] = (384..387).to_a
Floors[49] = (384..385).to_a
Floors[50] = (384..385).to_a
Floors.default = [384]
#--------------------------------------------------------------------------
# * Floor Decorations = {tileset_id => [tile_id, ...], ...}
#--------------------------------------------------------------------------
Floor_Decoration_Probability = 10
Floor_Decorations = {}
Floor_Decorations[1] = [385, 386, 387, 389, 393, 394, 395, 397, 398, 403,
404]
Floor_Decorations[2] = [385, 386, 387, 389, 393, 394, 395, 397, 398, 402,
403, 404]
Floor_Decorations.default = []
#--------------------------------------------------------------------------
# * Map Walls = {tileset_id => [wall_id, ...], ...}
#--------------------------------------------------------------------------
Walls = {}
Walls[1] = [[48,0],[336,0]]
Walls[5] = [[96,0]]
Walls[7] = [[240,0]]
Walls[11] = [[336,0]]
for i in [2,3,4,6,8,13,15,19,21,23,34,35,37,39,43,44,45,46]
Walls[i] = [[48,0]]
end
Walls.default = [[48,1]]
#--------------------------------------------------------------------------
# * Inspection Tools
#
# Create passability table file: Inspect_Passability
# Print tile to complete: Inspect_Time
#--------------------------------------------------------------------------
Inspect_Passability = false
Inspect_Time = true
end
#==============================================================================
# ** Map Maze Maker - Developer Methods
#==============================================================================
module Map_Maze_Maker
#--------------------------------------------------------------------------
# * Generate Map
#--------------------------------------------------------------------------
def self.generate_map(tileset_id, width = 20, height = 15, enc = [],
floor = nil)
# Save start time
@start_time = Time.new
# Floor and Wall ID
if floor == nil
floors = Floors[tileset_id]
floor = floors[rand(floors.size)]
end
# Error fixer
width = 20 if width < 20; height = 15 if height < 15
# Create RPG::Map
@map = RPG::Map.new(width, height)
@map.tileset_id = tileset_id
@map.encounter_list = enc
@map.data = Table.new(width, height, 3)
# Set floor
for x in 0...width
for y in 0...height
@map.data[x, y, 0] = floor
end
end
end
#--------------------------------------------------------------------------
# * Generate Passability Table
#--------------------------------------------------------------------------
def self.generate_passability_table(enterance_points = [], target = nil)
# Create passability table
@passability_table = Table.new(@map.width, @map.height)
# If enterance points empty
if enterance_points.empty?
# Creates sides list
sides = [1, 2, 3, 4]
# Pick number of enterance points 2-4
points = [2, 3, 4][rand(3)]
# For each enterance point
points.times do
# Pick random side
side = sides[rand(sides.size)]
# Delete side from list
sides.delete(side)
# Gets width & height
w, h = @map.width, @map.height
# Branch by side
case side
when 1 # Left
x = 0; y = rand(h - 1)
when 2 # Right
x = w - 1; y = rand(h - 1)
when 3 # Bottom
x = rand(w - 1); y = h - 1
when 4 # Top
x = rand(w - 1); y = 0
end
# Adds random enterance point
enterance_points << [x, y]
end
end
# Modify enterance points
@points = enterance_points
# If random target
if target == nil
# Gets half
w, h = @map.width / 2, @map.height / 2
# Gets fouth
hw, hh = w / 2, h / 2
# Gets random x & y
@pt_tx, @pt_ty = rand(w) + hw, rand(h) + hh
# If set target point
else
@pt_tx, @pt_ty = *target
end
# Set target point
@passability_table[@pt_tx, @pt_ty] = 1
# Until @points is empty
until @points.empty?
# Pass through each point
@points.each do |xy|
# Set point
self.gpt_set_point(xy)
end
end
end
#--------------------------------------------------------------------------
# * Generate Room around target
#--------------------------------------------------------------------------
def self.generate_target_room(w = nil, h = nil)
# Random width/height
if w == nil
h = @map.width / (@map.width / 4)
w = h + rand(h)
end
if h == nil
h = @map.height / (@map.width / 4)
h = h + rand(h)
end
# Random offset off target center
rx = @pt_tx - rand(w / 2)
ry = @pt_ty - rand(h / 2)
# Set random room
for x in rx..(rx + w)
for y in ry..(ry + h)
@passability_table[x, y] = 1
end
end
end
#--------------------------------------------------------------------------
# * Generate Room
#--------------------------------------------------------------------------
def self.generate_room(x, y, w, h)
for i in x..(x + w)
for j in y..(y + h)
@passability_table[i, j] = 1
end
end
end
#--------------------------------------------------------------------------
# * Generate Data
#--------------------------------------------------------------------------
def self.generate_data(wall = nil)
# Random wall
if wall == nil
walls = Walls[@map.tileset_id]
wall = walls[rand(walls.size)]
end
# Gets tile_id & layer
tile_id, layer = *wall
# Get floor decorations
decorations = Floor_Decorations[@map.tileset_id]
# Pass through passability table
for x in 0...@map.width
for y in 0...@map.height
# If path tile
if @passability_table[x, y] != 0
# If decorations exist
unless decorations.empty?
# If randomly place floor decoration
if rand(100) < Floor_Decoration_Probability
# Set floor decoration
@map.data[x, y, 1] = decorations[rand(decorations.size)]
end
end
next
end
# Start list
list = []
n = @passability_table[x - 1, y + 1]
list << 1 if n != 0 #|| n == nil
n = @passability_table[x, y + 1]
list << 2 if n != 0 #|| n == nil
n = @passability_table[x + 1, y + 1]
list << 3 if n != 0 #|| n == nil
n = @passability_table[x - 1, y]
list << 4 if n != 0 #|| n == nil
n = @passability_table[x + 1, y]
list << 6 if n != 0 #|| n == nil
n = @passability_table[x - 1, y - 1]
list << 7 if n != 0 #|| n == nil
n = @passability_table[x, y - 1]
list << 8 if n != 0 #|| n == nil
n = @passability_table[x + 1, y - 1]
list << 9 if n != 0 #|| n == nil
# Set Data ID
@map.data[x, y, layer] = tile_id + Autotile_ID_Generator.get_id(list)
end
end
end
#--------------------------------------------------------------------------
# * Finalize Map
#--------------------------------------------------------------------------
def self.finalize_map(map_id = 1, name = '')
# Saves Map Data - Beta Feature
save_data(@map, "Data/Map001.rxdata")
# Make Passability Table File
self.inspect_passability_table if Inspect_Passability
# Print Time to Make
p 'Complete', Time.new - @start_time if Inspect_Time
end
#--------------------------------------------------------------------------
# * Inspect Passability Table
#--------------------------------------------------------------------------
def self.inspect_passability_table
# Open 'Maze Maker Inspection.txt' File and write table
File.open('Maze Maker Inspection.txt',"a+") do |f|
for y in 0...@map.height
for x in 0...@map.width
f.write("#{@passability_table[x, y]}")
end
f.write("\n")
end
f.write("\n")
end
end
end
#==============================================================================
# ** Map Maze Maker - Private Methods
#==============================================================================
module Map_Maze_Maker
#--------------------------------------------------------------------------
# * Generate Passability Table: Set Point
#--------------------------------------------------------------------------
private
def self.gpt_set_point(xy)
# Delete from points
@points.delete(xy)
# Get x, y
x, y = *xy
# Set point
@passability_table[x, y] = 1
# Return if at target
return if x == @pt_tx && @pt_ty == y
# Directions
directions = []
directions << 2 if @pt_ty > y
directions << 4 if @pt_tx < x
directions << 6 if @pt_tx > x
directions << 8 if @pt_ty < y
# Gets distances
dx = (@pt_tx - x).abs
dy = (@pt_ty - @pt_ty).abs
# Random direction addon
if dx > dy && dx > rand(5) + 5
directions << (@pt_ty < y ? 2 : 8)
elsif dy > dx && dy > rand(5) + 5
directions << (@pt_tx > x ? 4 : 6)
end
# Remove movements that go off map
directions.delete(4) if x == 0
directions.delete(6) if x == @map.width - 1
directions.delete(2) if y == @map.height - 1
directions.delete(8) if y == 0
# Pick random directions
d = directions[rand(directions.size)]
# Get next point
nx = x + (d == 4 ? -1 : d == 6 ? 1 : 0)
ny = y + (d == 8 ? -1 : d == 2 ? 1 : 0)
# Add to points
@points << [nx, ny]
end
end
#==============================================================================
# ** Autotile ID Generator
#------------------------------------------------------------------------------
# Checking for tile z at the position x, y, with that position being an
# autotile, pass a list of the 8 surround tiles that are also autotiles.
#
# Autotile_ID_Generator.get_id([pos, ...])
#
# where pos = 1:lower-left, 2:down, 3:lower-right, 4:left, 6:right,
# 7:upper-left, 8:up, 9:upper-right
#
# Examples: O = Autotile, X = Non-Tile, S = Autotile finding ID for
#
# 00X
# 0SX Autotile_ID_Generator.get_id([3,4,7,8])
# XX0
#
# X00
# 0S0 Autotile_ID_Generator.get_id([2,4,5,8,9])
# X0X
#==============================================================================
class Autotile_ID_Generator
#--------------------------------------------------------------------------
# * Constant Setup - DO NOT MODIFY
#--------------------------------------------------------------------------
ID = {}
Cant_Include = {}
ID[[7]] = 1
Cant_Include[1] = [1,2,3,4,6,8,9]
ID[[9]] = 2
Cant_Include[2] = [1,2,3,4,6,7,8]
ID[[7,9]] = 3
Cant_Include[3] = [1,2,3,4,6,8]
ID[[3]] = 4
Cant_Include[4] = [1,2,4,6,7,8,9]
ID[[3,7]] = 5
Cant_Include[5] = [1,2,4,6,8,9]
ID[[3,9]] = 6
Cant_Include[6] = [1,2,4,6,7,8]
ID[[3,7,9]] = 7
Cant_Include[7] = [1,2,4,6,8]
ID[[1]] = 8
Cant_Include[8] = [2,3,4,6,7,8,9]
ID[[1,7]] = 9
Cant_Include[9] = [2,3,4,6,8,9]
ID[[1,9]] = 10
Cant_Include[10] = [2,3,4,6,7,8]
ID[[1,7,9]] = 11
Cant_Include[11] = [2,3,4,6,8]
ID[[1,3]] = 12
Cant_Include[12] = [2,4,6,7,8,9]
ID[[1,3,7]] = 13
Cant_Include[13] = [2,4,6,8,9]
ID[[1,3,9]] = 14
Cant_Include[14] = [2,4,6,7,8]
ID[[1,3,7,9]] = 15
Cant_Include[15] = [2,4,6,8]
ID[[4]] = 16
Cant_Include[16] = [2,3,6,8,9]
ID[[4,9]] = 17
Cant_Include[17] = [2,3,6,8]
ID[[3,4]] = 18
Cant_Include[18] = [2,6,8,9]
ID[[3,4,9]] = 19
Cant_Include[19] = [2,6,8]
ID[[8]] = 20
Cant_Include[20] = [1,2,3,4,6]
ID[[3,8]] = 21
Cant_Include[21] = [1,2,4,6]
ID[[1,8]] = 22
Cant_Include[22] = [2,3,4,6]
ID[[1,3,8]] = 23
Cant_Include[23] = [2,4,6]
ID[[6]] = 24
Cant_Include[24] = [1,2,4,7,8]
ID[[1,6]] = 25
Cant_Include[25] = [2,4,7,8]
ID[[6,7]] = 26
Cant_Include[26] = [1,2,4,8]
ID[[1,3,6,7,9]] = 27
Cant_Include[27] = [2,4,8]
ID[[2]] = 28
Cant_Include[28] = [4,6,7,8,9]
ID[[2,7]] = 29
Cant_Include[29] = [4,6,8,9]
ID[[2,9]] = 30
Cant_Include[30] = [4,6,7,8]
ID[[2,7,9]] = 31
Cant_Include[31] = [4,6,8]
ID[[4,6]] = 32
Cant_Include[32] = [2,8]
ID[[2,8]] = 33
Cant_Include[33] = [4,6]
ID[[4,8]] = 34
Cant_Include[34] = [2,3,6]
ID[[3,4,8]] = 35
Cant_Include[35] = [2,6]
ID[[6,8]] = 36
Cant_Include[36] = [1,2,4]
ID[[1,6,8]] = 37
Cant_Include[37] = [2,4]
ID[[2,6]] = 38
Cant_Include[38] = [4,7,8]
ID[[2,6,7]] = 39
Cant_Include[39] = [4,8]
ID[[2,4]] = 40
Cant_Include[40] = [6,8,9]
ID[[2,4,9]] = 41
Cant_Include[41] = [6,8]
ID[[4,6,8]] = 42
Cant_Include[42] = [2]
ID[[2,4,8]] = 43
Cant_Include[43] = [6]
ID[[2,4,6]] = 44
Cant_Include[44] = [8]
ID[[2,6,8]] = 45
Cant_Include[45] = [4]
ID[[2,4,6,8]] = 46
#--------------------------------------------------------------------------
# * Get ID
#--------------------------------------------------------------------------
def self.get_id(list)
# If empty list
return 0 if list.empty?
# Pass through all list
ID.keys.each do |l|
# If list includes all of searching list
if list.includes_all?(*l)
# If can't include certain numbers
if Cant_Include.has_key?(ID[l])
# Skip if searching list includes a non-includable number
next if Cant_Include[ID[l]].includes_any?(*list)
end
return ID[l]
end
end
# Error catcher
p 'Please report this list so that I may fix it. Thank you.', list
return 0
end
end
#==============================================================================
# ** Array
#==============================================================================
class Array
#--------------------------------------------------------------------------
# * Includes All
#--------------------------------------------------------------------------
def includes_all?(*args)
args.each {|i| return false unless include?(i)}
return true
end
#--------------------------------------------------------------------------
# * Includes Any
#--------------------------------------------------------------------------
def includes_any?(*args)
return true if args.empty?
args.each {|object| return true if include?(object)}
return false
end
end
#==============================================================================
# ** BETA Testing Configuration
#------------------------------------------------------------------------------
# Creates Random Map - 40x40, with 8 random enterances, 2 on each side of map
#==============================================================================
enterance_points = []
enterance_points << [0, rand(20)]
enterance_points << [0, rand(20) + 20]
enterance_points << [rand(20), 0]
enterance_points << [rand(20) + 20, 0]
enterance_points << [39, rand(20)]
enterance_points << [39, rand(20) + 20]
enterance_points << [rand(20), 39]
enterance_points << [rand(20) + 20, 39]
Map_Maze_Maker.generate_map(rand(2) + 1, 40, 40)
Map_Maze_Maker.generate_passability_table(enterance_points)
Map_Maze_Maker.generate_target_room
Map_Maze_Maker.generate_data
Map_Maze_Maker.finalize_map
Let me know what you guys think.
Heres a few screens of a few maps it produced:
http://owainc.net/screenshots/rand_map_maker/screen1.PNG
http://owainc.net/screenshots/rand_map_maker/screen2.PNG
http://owainc.net/screenshots/rand_map_maker/screen3.PNG