Original Source
- Original title: Creating Maps from Text files (incl. Demo: Sokoban)
- Original author: Iavra
- Original date: May 15, 2015
- Source thread: https://forums.rpgmakerweb.com/threads/creating-maps-from-text-files-incl-demo-sokoban.40043/
- Source forum path: Game Development Engines > Ruby Game System (RGSS) Scripts > RGSSx Scripts In Development
Summary
as a template, so we can copy tiles and events. MAP_TEMPLATE = load_data("Data/Map001.rvdata2") # initialize tile hash. @@tiles = Hash[TILES.map{|k, v| [k, (0..3).map{|z| MAP_TEMPLATE.data[*v, z]}] }] # initialize event hash. @@events = Hash[EVENTS.map{|k, v| [k, MAP_TEMPLATE.events[v]]}] #-------------------------------------------------------------------------- # creates a map from a text file and teleports the player onto it. #-------------------------------------------------------------------------- def self.load_level_by_file_name(file_name) load_level_by_file_data(parse_file(file_name)) end #-------------------------------------------------------------------------- # similar from the method above, but directly takes the map data. #-------------------------------------------------------------------------- def self.load_level_by_file_data(file_data) data = pad_file(file_data) pos = get_player_pos(data) map = create_map(data) $game_map.setup_directly(map) $game_player.set_direction(PLAYER_DIR) if [2, 4, 6, 8].include?(PLAYER_DIR) $game_player.moveto(*pos) @@last_level_data...
Archived First Post
i've been working on this script for the past few day and i think it's in a good enough state to post it here.
The script creates in-game maps from text files, during runtime, and is aimed at puzzle games where levels are simple enough to be represented by a few different chars. It allows for level creation on the fly and without rebuilding the game. I commented everything in the script, but if there are any questions i'd be happy to answer them.
The script is in a working state and i'd like to hear some suggestions about further script features.
For a demo, i included a simple Sokoban game. You can grab any level from here (http://sneezingtiger.com/sokoban/levels.html, click "View as text") and drop it into the "Levels" folder of the game. Highscores are saved by creating a CRC32 hash of the game data and using that as a key (not part of the script).
Demo:
https://www.dropbox.com/s/b46kn0pfxcanlz5/Sokoban.exe?dl=0
Demo Credits:
(Music) DeceasedSuperiorTechnician (http://www.nosoapradio.us/)
(Graphics) Kenney (http://www.kenney.nl/)
(Scripts) Glitchfinder, Source
Script:
module IAVRA module GENERATOR # minimum size of a map, smaller maps will be padded and centered. MIN_SIZE = [17, 13] # the character to represent the player position. PLAYER_CHAR = "@" # direction the player is facing. Set to something else than 2, 4, 6, 8 # to retain current facing. PLAYER_DIR = 2 # map chars to tiles (x/y position in the template map). # nil can be used to set a tile for the padding (see MIN_SIZE). TILES = { nil => [0, 0], # floor for padded tiles " " => [0, 0], # floor "@" => [0, 0], # floor under player "$" => [0, 0], # floor under crate "#" => [1, 0], # wall "." => [2, 0], # winning zone "*" => [2, 0] # winning zone under crate } # map chars to events (event id in the template map). EVENTS = { "@" => 1, # player "$" => 3, # crate "*" => 3 # crate on winning zone } # load map #1 as a template, so we can copy tiles and events. MAP_TEMPLATE = load_data("Data/Map001.rvdata2") # initialize tile hash. @@tiles = Hash[TILES.map{|k, v| [k, (0..3).map{|z| MAP_TEMPLATE.data[*v, z]}] }] # initialize event hash. @@events = Hash[EVENTS.map{|k, v| [k, MAP_TEMPLATE.events[v]]}] #-------------------------------------------------------------------------- # creates a map from a text file and teleports the player onto it. #-------------------------------------------------------------------------- def self.load_level_by_file_name(file_name) load_level_by_file_data(parse_file(file_name)) end #-------------------------------------------------------------------------- # similar from the method above, but directly takes the map data. #-------------------------------------------------------------------------- def self.load_level_by_file_data(file_data) data = pad_file(file_data) pos = get_player_pos(data) map = create_map(data) $game_map.setup_directly(map) $game_player.set_direction(PLAYER_DIR) if [2, 4, 6, 8].include?(PLAYER_DIR) $game_player.moveto(*pos) @@last_level_data = file_data end #-------------------------------------------------------------------------- # reloads the last created level. #-------------------------------------------------------------------------- def self.reload_level load_level_by_file_data(@@last_level_data) if @@last_level_data end #-------------------------------------------------------------------------- # loads a file, splits each line into characters and returns the result # as a 2-dimensional array. #-------------------------------------------------------------------------- def self.parse_file(file_name) File.open(file_name) {|f| f.lines.map{|l| l.scan(/./)}} end #-------------------------------------------------------------------------- # Pads an array to center a map that is smaller than the minimal size # defined by MIN_SIZE. Also, maps that are smaller than the default get # displayed pretty strange in RM. #-------------------------------------------------------------------------- def self.pad_file(data) padd_x = [MIN_SIZE[0] - (data.map{|m| m.length}.max || 0), 0].max / 2.0 padd_y = [MIN_SIZE[1] - data.length, 0].max / 2.0 data = data.map{|row| [[nil] * padd_x.floor, row, [nil] * padd_x.ceil].flatten} [[[]] * padd_y.floor, data, [[]] * padd_y.ceil].flatten(1) end #-------------------------------------------------------------------------- # returns the player position as indicated by a defined character. If the # character exists multiple times in the data provided, the first occurence # (from top/left) is returned. #-------------------------------------------------------------------------- def self.get_player_pos(data) data.each.with_index{|row, y| row.each.with_index{|char, x| return [x, y] if char == PLAYER_CHAR }} fail "no player position \"" + PLAYER_CHAR + "\" given." end #-------------------------------------------------------------------------- # creates an instance of RPG::Map from a given data array. calls the methods # create_tiles and create_events to fill the map. #-------------------------------------------------------------------------- def self.create_map(data) width = data.map{|m| m.length}.max map = RPG::Map.new(width, data.length) create_tiles(map, data, width) create_events(map, data, width) map end #-------------------------------------------------------------------------- # get the tile mapped to each character in the given data array from the # template map and store it in map data. characters not included in the # hash are mapped to empty tiles [0, 0, 0, 0]. #-------------------------------------------------------------------------- def self.create_tiles(map, data, width) data.each.with_index{|row, y| (0..width - 1).each{|x| (@@tiles[row[x]] || [0]*4).each.with_index{|tile, z| map.data[x, y, z] = tile} }} end #-------------------------------------------------------------------------- # clones the mapped events and stores them into the map. generates ongoing # ids starting from 1 and sets the event positions accordingly. We also # reset self switches, because all maps are generated with the same id. And # even if we were to generate ids, maps can change between game starts, so # the saved self switches aren't always correct. #-------------------------------------------------------------------------- def self.create_events(map, data, width) index = 0 data.each.with_index{|row, y| (0..width - 1).each{|x| next if @@events[row[x]].nil? attr = {:id => index += 1, :x => x, :y => y} map.events[index] = @@events[row[x]].clone_with_attrs(attr) ("A".."D").each {|s| $game_self_switches[[-1, index, s]] = false} }} end endend#--------------------------------------------------------------------------# ▼ Object#--------------------------------------------------------------------------class Object #-------------------------------------------------------------------------- # clone method, which also sets attributes in the clone. The attributes are # supplied as a hash with the attribute names as symbols for keys. #-------------------------------------------------------------------------- def clone_with_attrs(attr) copy = clone attr.each{|k, v| eval("copy.#{k} = #{v}")} copy end end#--------------------------------------------------------------------------# ▼ Game_Map#--------------------------------------------------------------------------class Game_Map #-------------------------------------------------------------------------- # directly setups a given map instead of loading it from a .rvdata2 file. # Note: Since all maps are generated with the same id, they would all share # the same self switches. To overcome this, self switches for id -1 are reset # everytime a map is generated. #-------------------------------------------------------------------------- def setup_directly(map) @map_id = -1 # default map id @map = map # instead of loading a file @tileset_id = @map.tileset_id @display_x = 0 @display_y = 0 referesh_vehicles setup_events setup_scroll setup_parallax setup_battleback @need_refresh = false end end
Downloads / Referenced Files
Log in, then follow the RPG Maker Developers Group to see these download links.
Log in to downloadLicense / Terms Note
Demo Credits: (Music) DeceasedSuperiorTechnician (http://www.nosoapradio.us/) (Graphics) Kenney (http://www.kenney.nl/) (Scripts) Glitchfinder, Source
Creator Claims / Removal
If you are the original creator and want this listing reassigned, edited, or removed, join BMMPlay and contact the moderators with proof that matches the original RPG Maker Web profile, linked GitHub, itch.io page, or another public creator identity.
Replies (0)
No replies yet.
Topic Summary
Loading summary...