1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218
|
<?xml version="1.0" encoding="UTF-8" standalone="no" ?>
<el:level xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://enigma-game.org/schema/level/1 level.xsd" xmlns:el="http://enigma-game.org/schema/level/1">
<el:protected>
<el:info el:type="library">
<el:identity el:title="" el:id="lib/libimport"/>
<el:version el:score="1" el:release="1" el:revision="2" el:status="released"/>
<el:author el:name="Enigma Team" el:email="" el:homepage=""/>
<el:copyright>Copyright © 2009 Enigma Team</el:copyright>
<el:license el:type="GPL v2.0 or above" el:open="true"/>
<el:compatibility el:enigma="1.10">
<el:dependency el:path="lib/libmap" el:id="lib/libmap" el:release="1" el:preload="true"/>
</el:compatibility>
<el:modes el:easy="false" el:single="false" el:network="false"/>
<el:comments>
</el:comments>
<el:score el:easy="-" el:difficult="-"/>
</el:info>
<el:luamain><![CDATA[
-- libimport.xml is dedicated to parse level files of other
-- games or formats and typically returns libmap-maps.
-- In this context, the word "level" refers to a string,
-- representing a level of the other game. "multilevel"
-- is a string composed of one or more levels.
-- A "monolevel" is a string which holds a single level,
-- but also additional information, so it has to be handled
-- as multilevel to extract the level.
-- A map is a map in the libmap-sense, i.e. an Enigma level.
lib.import = {}
setmetatable(lib.import, getmetatable(lib))
lib.import.line_regexp = {}
-------------------
-- General tools --
-------------------
function lib.import.unpack_multilevel(multilevel, sublevel_number, format)
assert_type(multilevel, "lib.import.unpack_multilevel first argument", 2, "string")
assert_type(sublevel_number, "lib.import.unpack_multilevel second argument", 2, "nil", "positive integer")
local current_level_number = 0
local level = ""
-- search the correct sublevel by pattern matching, line for line
local in_level = false
for line in string.gmatch(multilevel, "([^\r\n\|]*)[\r\n\|]+") do
local start_match, end_match = string.find(line, lib.import.line_regexp[format])
if (start_match == 1) and (end_match == string.len(line)) then
if not in_level then
-- we just entered a new level
current_level_number = current_level_number + 1
in_level = true
end
if current_level_number == (sublevel_number or 1) then
level = level..line.."\n"
end
else
in_level = false
end
end
assert_bool(level ~= "", "lib.import.unpack_multilevel: Multilevel holds less than "..sublevel_number.." levels!", 2)
return level
end
function lib.import.uncompress_rle(level, attribs)
assert_type(level, "lib.import.uncompress_rle first argument", 2, "string")
assert_type(attribs, "lib.import.uncompress_rle second argument", 2, "nil", "table")
local uncompressed = ""
local rle_counter = nil
local error_on_multiple = (attribs or {}).error_on_multiple
for j = 1, string.len(level) do
local ch = string.sub(level, j, j)
if tonumber(ch) then
-- add number for runlength encoding
rle_counter = 10 * (rle_counter or 0) + tonumber(ch)
else
if error_on_multiple and ((rle_counter or 1) > 1) then
-- check whether character is allowed as multiple
if string.find(ch, error_on_multiple) then
error("lib.import.uncompress_rle: Level uses rle-compression on an invalid character.", 2)
end
end
uncompressed = uncompressed .. string.rep(ch, rle_counter or 1)
rle_counter = nil
end
end
return uncompressed
end
function lib.import.string_replace(level, old_string, new_string)
return string.gsub(level, old_string, function(s) return new_string end)
end
function lib.import.string_remove_multiples(level, ch)
local result = level
while string.find(result, ch..ch) do
result = lib.import.string_replace(result, ch..ch, ch)
end
return result
end
-- When using level_to_map, make sure in advance that the level ends
-- with a proper line ending, and only one line ending.
function lib.import.level_to_map(level, line_ending, default_key)
assert_type(level, "lib.import.level_to_map first argument", 2, "string")
assert_type(line_ending, "lib.import.level_to_map second argument", 2, "nil", "non-empty string")
assert_type(default_key, "lib.import.level_to_map third argument", 2, "nil", "non-empty string")
local premap = {}
local reg_exp_line = "(.-)" .. (line_ending or "\n")
string.gsub(level, reg_exp_line, function (s)
table.insert(premap, s)
end)
return wo:newMap(default_key or " ", premap)
end
----------------------------------------
-- Specific import data and functions --
----------------------------------------
--------------------------
-- Sokoban level format --
--------------------------
-- Sokoban level files can be very different - they can use
-- runlength encoding and multilevel files. Sometimes, "|"
-- is used as line ending instead of or together with "\n".
-- " ", "_" and "-" can all represent space. Sometimes, the
-- outside is filled with " ", then again with "#" (what we
-- call "redundant walls"). Finally, line width can vary.
-- We use a slightly generalized version, to include
-- chessoban levels. Allowed characters are:
-- - _ space space
-- # wall
-- . goal
-- $ * box, box with goal
-- n N st_chess, st_chess with goal
-- @ + player, player with goal
-- The returned map will use " " for inside space, "-" for
-- outside space, default key is " ".
lib.import.line_regexp["sokoban"] =
"[ \-\#\_\$\.@\*0-9Nn\+]*" -- all allowed characters except line ending
.. "[\#\$\.@\*Nn\+]+" -- at least one non-space character
.. "[ \-\#\_\$\.@\*0-9Nn\+]*" -- again all allowed characters except line ending
.. "[\r]?" -- maybe a carriage return from line ending
function lib.import.map_sokoban(multilevel, sublevel_number, keylength_one)
assert_type(multilevel, "lib.import.map_sokoban first argument", 2, "non-empty string")
assert_type(sublevel_number, "lib.import.map_sokoban second argument", 2, "nil", "positive integer")
local level
level = lib.import.unpack_multilevel(multilevel .. "\n", sublevel_number or 1, "sokoban")
level = lib.import.uncompress_rle(level, {error_on_multiple = "|\n"})
level = lib.import.string_replace(level, "\r\n", "\n")
level = lib.import.string_replace(level, "|", "\n")
level = lib.import.string_remove_multiples(level .. "\n", "\n")
local map = lib.import.level_to_map(level, "\n", " ")
map:replace("_")
map:replace("-")
map:replace_outside(" ", "-")
-- Remove redundant walls by outside:
-- A wall is redundant, if it is surrounded by "-" or "#".
-- In this case, it can be replaced by "-".
map.defaultkey = "-"
map[map:match({{po(0,0), "#"}, {NEIGHBORS_8, "-", "#"}})] = "-"
map = map:trim()
map.defaultkey = " "
-- MAP now is a ready-to-use keylength-one-map.
-- If KEYLENGTH_ONE is true, we may return the map;
-- otherwise, we resolve the characters "*", "N" and
-- "+", which include triggers.
if keylength_one then
return map
end
twocharmap = map * wo:newMap(" ")
twocharmap:replace(". ", " .")
twocharmap:replace("* ", "$.")
twocharmap:replace("N ", "n.")
twocharmap:replace("+ ", "@.")
return twocharmap
end
---------------------------
-- Wanderer level format --
---------------------------
-- Wanderer is a game originally created by Steven Shipway in 1988.
-- Wanderer level files are uncompressed monolevels with fixed
-- width. The level itself is followed by a short description
-- (title/author/email) in a single line or a line of "#"s. Last
-- line optionally is a number. A Wanderer level knows the
-- following characters (taken from Wanderer's editor):
-- : _ # earth, rock, indestructible rock
-- * - space treasure, alternative space, space
-- O < > ^ falling boulder, arrow from right, arrow from left, balloon
-- ! + B landmine, cage, bomb
-- / \\ deflectors
-- T A X @ teleport, arrival, exit, start
-- M S C ~ big monster, baby monster, time capsule, thingy
lib.import.line_regexp["wanderer"] =
"[ \:\_\#\*\-O\<\>\^\!\+B\/\\TAX@MSC\~]+" -- at least one character
function lib.import.map_wanderer(monolevel)
assert_type(monolevel, "lib.import.map_wanderer first argument", 2, "non-empty string")
-- strip level from text lines
local level = lib.import.unpack_multilevel(monolevel, 1, "wanderer")
local map = lib.import.level_to_map(level, "\n", " ")
-- replace alternative space by space (i.e. default key)
map:replace("-")
return map
end
]]></el:luamain>
<el:i18n>
</el:i18n>
</el:protected>
</el:level>
|