diff --git a/code/datums/helper_datums/map_template.dm b/code/datums/helper_datums/map_template.dm index a54707842db..ee5a4df4f5b 100644 --- a/code/datums/helper_datums/map_template.dm +++ b/code/datums/helper_datums/map_template.dm @@ -17,7 +17,7 @@ name = rename /datum/map_template/proc/preload_size(path) - var/bounds = GLOB.maploader.load_map(file(path), 1, 1, 1, cropMap = 0, measureOnly = 1) + var/bounds = GLOB.maploader.load_map(file(path), 1, 1, 1, shouldCropMap = FALSE, measureOnly = TRUE) if(bounds) width = bounds[MAP_MAXX] // Assumes all templates are rectangular, have a single Z level, and begin at 1,1,1 height = bounds[MAP_MAXY] @@ -49,7 +49,7 @@ // if given a multi-z template // it might need to be adapted for that when that time comes GLOB.space_manager.add_dirt(placement.z) - var/list/bounds = GLOB.maploader.load_map(get_file(), min_x, min_y, placement.z, cropMap = 1) + var/list/bounds = GLOB.maploader.load_map(get_file(), min_x, min_y, placement.z, shouldCropMap = TRUE) if(!bounds) return 0 if(bot_left == null || top_right == null) diff --git a/code/modules/awaymissions/maploader/dmm_suite.dm b/code/modules/awaymissions/maploader/dmm_suite.dm index 1eee3d8fc34..5e958f28ee6 100644 --- a/code/modules/awaymissions/maploader/dmm_suite.dm +++ b/code/modules/awaymissions/maploader/dmm_suite.dm @@ -1,72 +1,82 @@ -GLOBAL_DATUM_INIT(maploader, /dmm_suite, new()) -dmm_suite{ - /* +/* - dmm_suite version 1.0 - Released January 30th, 2011. + dmm_suite version 1.0 + Released January 30th, 2011. - defines the object /dmm_suite - - Provides the proc load_map() - - Loads the specified map file onto the specified z-level. - - provides the proc write_map() - - Returns a text string of the map in dmm format - ready for output to a file. - - provides the proc save_map() - - Returns a .dmm file if map is saved - - Returns FALSE if map fails to save + defines the object /dmm_suite + - Provides the proc load_map() + - Loads the specified map file onto the specified z-level. + - provides the proc write_map() + - Returns a text string of the map in dmm format + ready for output to a file. + - provides the proc save_map() + - Returns a .dmm file if map is saved + - Returns FALSE if map fails to save - The dmm_suite provides saving and loading of map files in BYOND's native DMM map - format. It approximates the map saving and loading processes of the Dream Maker - and Dream Seeker programs so as to allow editing, saving, and loading of maps at - runtime. + The dmm_suite provides saving and loading of map files in BYOND's native DMM map + format. It approximates the map saving and loading processes of the Dream Maker + and Dream Seeker programs so as to allow editing, saving, and loading of maps at + runtime. - ------------------------ + ------------------------ - To save a map at runtime, create an instance of /dmm_suite, and then call - write_map(), which accepts three arguments: - - A turf representing one corner of a three dimensional grid (Required). - - Another turf representing the other corner of the same grid (Required). - - Any, or a combination, of several bit flags (Optional, see documentation). + To save a map at runtime, create an instance of /dmm_suite, and then call + write_map(), which accepts three arguments: + - A turf representing one corner of a three dimensional grid (Required). + - Another turf representing the other corner of the same grid (Required). + - Any, or a combination, of several bit flags (Optional, see documentation). - The order in which the turfs are supplied does not matter, the /dmm_writer will - determine the grid containing both, in much the same way as DM's block() function. - write_map() will then return a string representing the saved map in dmm format; - this string can then be saved to a file, or used for any other purose. + The order in which the turfs are supplied does not matter, the /dmm_writer will + determine the grid containing both, in much the same way as DM's block() function. + write_map() will then return a string representing the saved map in dmm format; + this string can then be saved to a file, or used for any other purose. - ------------------------ + ------------------------ - To load a map at runtime, create an instance of /dmm_suite, and then call load_map(), - which accepts two arguments: - - A .dmm file to load (Required). - - A number representing the z-level on which to start loading the map (Optional). + To load a map at runtime, create an instance of /dmm_suite, and then call load_map(), + which accepts two arguments: + - A .dmm file to load (Required). + - A number representing the z-level on which to start loading the map (Optional). - The /dmm_suite will load the map file starting on the specified z-level. If no - z-level was specified, world.maxz will be increased so as to fit the map. Note - that if you wish to load a map onto a z-level that already has objects on it, - you will have to handle the removal of those objects. Otherwise the new map will - simply load the new objects on top of the old ones. + The /dmm_suite will load the map file starting on the specified z-level. If no + z-level was specified, world.maxz will be increased so as to fit the map. Note + that if you wish to load a map onto a z-level that already has objects on it, + you will have to handle the removal of those objects. Otherwise the new map will + simply load the new objects on top of the old ones. - Also note that all type paths specified in the .dmm file must exist in the world's - code, and that the /dmm_reader trusts that files to be loaded are in fact valid - .dmm files. Errors in the .dmm format will cause runtime errors. + Also note that all type paths specified in the .dmm file must exist in the world's + code, and that the /dmm_reader trusts that files to be loaded are in fact valid + .dmm files. Errors in the .dmm format will cause runtime errors. - */ +*/ - verb/load_map(var/dmm_file as file, var/x_offset as num, var/y_offset as num, var/z_offset as num, do_sleep as num){ - // dmm_file: A .dmm file to load (Required). - // z_offset: A number representing the z-level on which to start loading the map (Optional). - } - verb/write_map(var/turf/t1 as turf, var/turf/t2 as turf, var/flags as num){ - // t1: A turf representing one corner of a three dimensional grid (Required). - // t2: Another turf representing the other corner of the same grid (Required). - // flags: Any, or a combination, of several bit flags (Optional, see documentation). - } +GLOBAL_DATUM_INIT(maploader, /datum/dmm_suite, new()) - // save_map is included as a legacy proc. Use write_map instead. - verb/save_map(var/turf/t1 as turf, var/turf/t2 as turf, var/map_name as text, var/flags as num){ - // t1: A turf representing one corner of a three dimensional grid (Required). - // t2: Another turf representing the other corner of the same grid (Required). - // map_name: A valid name for the map to be saved, such as "castle" (Required). - // flags: Any, or a combination, of several bit flags (Optional, see documentation). - } - } +/datum/dmm_suite + var/static/quote = "\"" + + // These regexes are global - meaning that starting the maploader again mid-load will + // reset progress - which means we need to track our index per-map, or we'll + // eternally recurse + // /"([a-zA-Z]+)" = \(((?:.|\n)*?)\)\n(?!\t)|\((\d+),(\d+),(\d+)\) = \{"([a-zA-Z\n]*)"\}/g + var/static/regex/dmmRegex = new/regex({""(\[a-zA-Z]+)" = \\(((?:.|\n)*?)\\)\n(?!\t)|\\((\\d+),(\\d+),(\\d+)\\) = \\{"(\[a-zA-Z\n]*)"\\}"}, "g") + // /^[\s\n]+"?|"?[\s\n]+$|^"|"$/g + var/static/regex/trimQuotesRegex = new/regex({"^\[\\s\n]+"?|"?\[\\s\n]+$|^"|"$"}, "g") + // /^[\s\n]+|[\s\n]+$/ + var/static/regex/trimRegex = new/regex("^\[\\s\n]+|\[\\s\n]+$", "g") + var/static/list/modelCache = list() + + var/static/list/letter_digits = list( + "a", "b", "c", "d", "e", + "f", "g", "h", "i", "j", + "k", "l", "m", "n", "o", + "p", "q", "r", "s", "t", + "u", "v", "w", "x", "y", + "z", + "A", "B", "C", "D", "E", + "F", "G", "H", "I", "J", + "K", "L", "M", "N", "O", + "P", "Q", "R", "S", "T", + "U", "V", "W", "X", "Y", + "Z" + ) diff --git a/code/modules/awaymissions/maploader/reader.dm b/code/modules/awaymissions/maploader/reader.dm index 9a7cf6b6405..7efff7cc27b 100644 --- a/code/modules/awaymissions/maploader/reader.dm +++ b/code/modules/awaymissions/maploader/reader.dm @@ -1,23 +1,11 @@ /////////////////////////////////////////////////////////////// -//SS13 Optimized Map loader +// SS13 Optimized Map loader ////////////////////////////////////////////////////////////// -//As of 3.6.2016 -//global datum that will preload variables on atoms instanciation +// As of 3.6.2016 +// global datum that will preload variables on atoms instanciation GLOBAL_VAR_INIT(use_preloader, FALSE) -GLOBAL_DATUM_INIT(_preloader, /dmm_suite/preloader, new()) - -/dmm_suite - // These regexes are global - meaning that starting the maploader again mid-load will - // reset progress - which means we need to track our index per-map, or we'll - // eternally recurse - // /"([a-zA-Z]+)" = \(((?:.|\n)*?)\)\n(?!\t)|\((\d+),(\d+),(\d+)\) = \{"([a-zA-Z\n]*)"\}/g - var/static/regex/dmmRegex = new/regex({""(\[a-zA-Z]+)" = \\(((?:.|\n)*?)\\)\n(?!\t)|\\((\\d+),(\\d+),(\\d+)\\) = \\{"(\[a-zA-Z\n]*)"\\}"}, "g") - // /^[\s\n]+"?|"?[\s\n]+$|^"|"$/g - var/static/regex/trimQuotesRegex = new/regex({"^\[\\s\n]+"?|"?\[\\s\n]+$|^"|"$"}, "g") - // /^[\s\n]+|[\s\n]+$/ - var/static/regex/trimRegex = new/regex("^\[\\s\n]+|\[\\s\n]+$", "g") - var/static/list/modelCache = list() +GLOBAL_DATUM_INIT(_preloader, /datum/dmm_suite/preloader, new()) /** * Construct the model map and control the loading process @@ -37,13 +25,13 @@ GLOBAL_DATUM_INIT(_preloader, /dmm_suite/preloader, new()) * atmos will attempt to start before it's ready, causing runtimes galore if init is * allowed to romp unchecked. */ -/dmm_suite/load_map(dmm_file as file, x_offset as num, y_offset as num, z_offset as num, cropMap as num, measureOnly as num) - var/tfile = dmm_file//the map file we're creating +/datum/dmm_suite/proc/load_map(dmm_file, x_offset = 0, y_offset = 0, z_offset = 0, shouldCropMap = FALSE, measureOnly = FALSE) + var/tfile = dmm_file// the map file we're creating var/fname = "Lambda" if(isfile(tfile)) fname = "[tfile]" tfile = file2text(tfile) - if(length(tfile) == 0) + if(!length(tfile)) throw EXCEPTION("Map path '[fname]' does not exist!") if(!x_offset) @@ -57,7 +45,7 @@ GLOBAL_DATUM_INIT(_preloader, /dmm_suite/preloader, new()) var/list/grid_models = list() var/key_len = 0 - var/dmm_suite/loaded_map/LM = new + var/datum/dmm_suite/loaded_map/LM = new // This try-catch is used as a budget "Finally" clause, as the dirt count // needs to be reset var/watch = start_watch() @@ -76,7 +64,7 @@ GLOBAL_DATUM_INIT(_preloader, /dmm_suite/preloader, new()) if(!key_len) key_len = length(key) else - throw EXCEPTION("Inconsistant key length in DMM") + throw EXCEPTION("Inconsistent key length in DMM") if(!measureOnly) grid_models[key] = dmmRegex.group[2] @@ -86,17 +74,17 @@ GLOBAL_DATUM_INIT(_preloader, /dmm_suite/preloader, new()) throw EXCEPTION("Coords before model definition in DMM") var/xcrdStart = text2num(dmmRegex.group[3]) + x_offset - 1 - //position of the currently processed square + // position of the currently processed square var/xcrd var/ycrd = text2num(dmmRegex.group[4]) + y_offset - 1 var/zcrd = text2num(dmmRegex.group[5]) + z_offset - 1 if(!measureOnly) if(zcrd > world.maxz) - if(cropMap) + if(shouldCropMap) continue else - GLOB.space_manager.increase_max_zlevel_to(zcrd) //create a new z_level if needed + GLOB.space_manager.increase_max_zlevel_to(zcrd) // create a new z_level if needed bounds[MAP_MINX] = min(bounds[MAP_MINX], xcrdStart) bounds[MAP_MINZ] = min(bounds[MAP_MINZ], zcrd) @@ -118,7 +106,7 @@ GLOBAL_DATUM_INIT(_preloader, /dmm_suite/preloader, new()) bounds[MAP_MINY] = min(bounds[MAP_MINY], ycrd) ycrd += gridLines.len - 1 // Start at the top and work down - if(!cropMap && ycrd > world.maxy) + if(!shouldCropMap && ycrd > world.maxy) if(!measureOnly) world.maxy = ycrd // Expand Y here. X is expanded in the loop below bounds[MAP_MAXY] = max(bounds[MAP_MAXY], ycrd) @@ -133,9 +121,9 @@ GLOBAL_DATUM_INIT(_preloader, /dmm_suite/preloader, new()) for(var/line in gridLines) if(ycrd <= world.maxy && ycrd >= 1) xcrd = xcrdStart - for(var/tpos = 1 to length(line) - key_len + 1 step key_len) + for(var/tpos = 1 to (length(line) - key_len + 1) step key_len) if(xcrd > world.maxx) - if(cropMap) + if(shouldCropMap) break else world.maxx = xcrd @@ -152,7 +140,7 @@ GLOBAL_DATUM_INIT(_preloader, /dmm_suite/preloader, new()) maxx = max(maxx, xcrd) ++xcrd --ycrd - bounds[MAP_MAXX] = max(bounds[MAP_MAXX], cropMap ? min(maxx, world.maxx) : maxx) + bounds[MAP_MAXX] = max(bounds[MAP_MAXX], shouldCropMap ? min(maxx, world.maxx) : maxx) CHECK_TICK catch(var/exception/e) @@ -175,8 +163,8 @@ GLOBAL_DATUM_INIT(_preloader, /dmm_suite/preloader, new()) if(!measureOnly) for(var/t in block(locate(bounds[MAP_MINX], bounds[MAP_MINY], bounds[MAP_MINZ]), locate(bounds[MAP_MAXX], bounds[MAP_MAXY], bounds[MAP_MAXZ]))) var/turf/T = t - //we do this after we load everything in. if we don't; we'll have weird atmos bugs regarding atmos adjacent turfs - T.AfterChange(1, keep_cabling = TRUE) + // we do this after we load everything in. if we don't; we'll have weird atmos bugs regarding atmos adjacent turfs + T.AfterChange(TRUE, keep_cabling = TRUE) return bounds /** @@ -196,14 +184,14 @@ GLOBAL_DATUM_INIT(_preloader, /dmm_suite/preloader, new()) * 4) Instanciates the atom with its variables * */ -/dmm_suite/proc/parse_grid(model as text,xcrd as num,ycrd as num,zcrd as num, dmm_suite/loaded_map/LM) +/datum/dmm_suite/proc/parse_grid(model = "", xcrd = 0, ycrd = 0, zcrd = 0, datum/dmm_suite/loaded_map/LM) /*Method parse_grid() - Accepts a text string containing a comma separated list of type paths of the same construction as those contained in a .dmm file, and instantiates them. */ - var/list/members //will contain all members (paths) in model (in our example : /turf/unsimulated/wall and /area/mine/dangerous/explored) - var/list/members_attributes //will contain lists filled with corresponding variables, if any (in our example : list(icon_state = "rock") and list()) + var/list/members // will contain all members (paths) in model (in our example : /turf/unsimulated/wall and /area/mine/dangerous/explored) + var/list/members_attributes // will contain lists filled with corresponding variables, if any (in our example : list(icon_state = "rock") and list()) var/list/cached = modelCache[model] var/index @@ -212,7 +200,7 @@ GLOBAL_DATUM_INIT(_preloader, /dmm_suite/preloader, new()) members_attributes = cached[2] else ///////////////////////////////////////////////////////// - //Constructing members and corresponding variables lists + // Constructing members and corresponding variables lists //////////////////////////////////////////////////////// members = list() @@ -223,13 +211,13 @@ GLOBAL_DATUM_INIT(_preloader, /dmm_suite/preloader, new()) var/dpos do - //finding next member (e.g /turf/unsimulated/wall{icon_state = "rock"} or /area/mine/dangerous/explored) - dpos = find_next_delimiter_position(model, old_position, ",", "{", "}") //find next delimiter (comma here) that's not within {...} + // finding next member (e.g /turf/unsimulated/wall{icon_state = "rock"} or /area/mine/dangerous/explored) + dpos = find_next_delimiter_position(model, old_position, ",", "{", "}") // find next delimiter (comma here) that's not within {...} - var/full_def = trim_text(copytext(model, old_position, dpos)) //full definition, e.g : /obj/foo/bar{variables=derp} + var/full_def = trim_text(copytext(model, old_position, dpos)) // full definition, e.g : /obj/foo/bar{variables=derp} var/variables_start = findtext(full_def, "{") var/atom_text = trim_text(copytext(full_def, 1, variables_start)) - var/atom_def = text2path(atom_text) //path definition, e.g /obj/foo/bar + var/atom_def = text2path(atom_text) // path definition, e.g /obj/foo/bar old_position = dpos + 1 if(!atom_def) // Skip the item if the path does not exist. Fix your crap, mappers! @@ -237,14 +225,14 @@ GLOBAL_DATUM_INIT(_preloader, /dmm_suite/preloader, new()) continue members.Add(atom_def) - //transform the variables in text format into a list (e.g {var1="derp"; var2; var3=7} => list(var1="derp", var2, var3=7)) + // transform the variables in text format into a list (e.g {var1="derp"; var2; var3=7} => list(var1="derp", var2, var3=7)) var/list/fields = list() - if(variables_start)//if there's any variable - full_def = copytext(full_def,variables_start+1,length(full_def))//removing the last '}' + if(variables_start) // if there's any variable + full_def = copytext(full_def, variables_start + 1, length(full_def)) // removing the last '}' fields = readlist(full_def, ";") - //then fill the members_attributes list with the corresponding variables + // then fill the members_attributes list with the corresponding variables members_attributes.len++ members_attributes[index++] = fields @@ -255,24 +243,23 @@ GLOBAL_DATUM_INIT(_preloader, /dmm_suite/preloader, new()) //////////////// - //Instanciation + // Instanciation //////////////// - //The next part of the code assumes there's ALWAYS an /area AND a /turf on a given tile + // The next part of the code assumes there's ALWAYS an /area AND a /turf on a given tile - //first instance the /area and remove it from the members list + // first instance the /area and remove it from the members list index = members.len - var/turf/crds = locate(xcrd,ycrd,zcrd) + var/turf/crds = locate(xcrd, ycrd, zcrd) if(members[index] != /area/template_noop) // We assume `members[index]` is an area path, as above, yes? I will operate // on that assumption. if(!ispath(members[index], /area)) throw EXCEPTION("Oh no, I thought this was an area!") - var/atom/instance - GLOB._preloader.setup(members_attributes[index])//preloader for assigning set variables on atom creation - instance = LM.area_path_to_real_area(members[index]) + GLOB._preloader.setup(members_attributes[index]) // preloader for assigning set variables on atom creation + var/atom/instance = LM.area_path_to_real_area(members[index]) if(crds) instance.contents.Add(crds) @@ -280,45 +267,46 @@ GLOBAL_DATUM_INIT(_preloader, /dmm_suite/preloader, new()) if(GLOB.use_preloader && instance) GLOB._preloader.load(instance) - //then instance the /turf and, if multiple tiles are presents, simulates the DMM underlays piling effect + // then instance the /turf and, if multiple tiles are presents, simulates the DMM underlays piling effect var/first_turf_index = 1 - while(!ispath(members[first_turf_index],/turf)) //find first /turf object in members + while(!ispath(members[first_turf_index], /turf)) // find first /turf object in members first_turf_index++ - //instanciate the first /turf + // instanciate the first /turf var/turf/T if(members[first_turf_index] != /turf/template_noop) - T = instance_atom(members[first_turf_index],members_attributes[first_turf_index],xcrd,ycrd,zcrd) + T = instance_atom(members[first_turf_index], members_attributes[first_turf_index], xcrd, ycrd, zcrd) if(T) - //if others /turf are presents, simulates the underlays piling effect + // if others /turf are presents, simulates the underlays piling effect index = first_turf_index + 1 - while(index <= members.len - 1) // Last item is an /area + var/mlen = members.len - 1 + while(index <= mlen) // Last item is an /area var/underlay if(istype(T, /turf)) // I blame this on the stupid clown who coded the BYOND map editor underlay = T.appearance - T = instance_atom(members[index],members_attributes[index],xcrd,ycrd,zcrd)//instance new turf - if(ispath(members[index],/turf)) + T = instance_atom(members[index], members_attributes[index], xcrd, ycrd, zcrd) // instance new turf + if(ispath(members[index], /turf)) T.underlays += underlay index++ - //finally instance all remainings objects/mobs - for(index in 1 to first_turf_index-1) - instance_atom(members[index],members_attributes[index],xcrd,ycrd,zcrd) + // finally instance all remainings objects/mobs + for(index in 1 to first_turf_index - 1) + instance_atom(members[index], members_attributes[index], xcrd, ycrd, zcrd) CHECK_TICK //////////////// -//Helpers procs +// Helpers procs //////////////// -//Instance an atom at (x,y,z) and gives it the variables in attributes -/dmm_suite/proc/instance_atom(path,list/attributes, x, y, z) +// Instance an atom at (x, y, z) and gives it the variables in attributes +/datum/dmm_suite/proc/instance_atom(path, list/attributes, x, y, z) var/atom/instance GLOB._preloader.setup(attributes, path) - var/turf/T = locate(x,y,z) + var/turf/T = locate(x, y, z) if(T) if(ispath(path, /turf)) T.ChangeTurf(path, defer_change = TRUE, keep_icon = FALSE) @@ -326,115 +314,111 @@ GLOBAL_DATUM_INIT(_preloader, /dmm_suite/preloader, new()) else if(ispath(path, /area)) else - instance = new path (T)//first preloader pass + instance = new path(T) // first preloader pass - if(GLOB.use_preloader && instance)//second preloader pass, for those atoms that don't ..() in New() + if(GLOB.use_preloader && instance) // second preloader pass, for those atoms that don't ..() in New() GLOB._preloader.load(instance) return instance -//text trimming (both directions) helper proc -//optionally removes quotes before and after the text (for variable name) -/dmm_suite/proc/trim_text(what as text,trim_quotes=0) +// text trimming (both directions) helper proc +// optionally removes quotes before and after the text (for variable name) +/datum/dmm_suite/proc/trim_text(what, trim_quotes = FALSE) if(trim_quotes) return trimQuotesRegex.Replace(what, "") else return trimRegex.Replace(what, "") - -//find the position of the next delimiter,skipping whatever is comprised between opening_escape and closing_escape -//returns 0 if reached the last delimiter -/dmm_suite/proc/find_next_delimiter_position(text as text,initial_position as num, delimiter=",",opening_escape=quote,closing_escape=quote) +// find the position of the next delimiter, skipping whatever is comprised between opening_escape and closing_escape +// returns 0 if reached the last delimiter +/datum/dmm_suite/proc/find_next_delimiter_position(text, initial_position = 0, delimiter = ",", opening_escape = quote, closing_escape = quote) var/position = initial_position - var/next_delimiter = findtext(text,delimiter,position,0) - var/next_opening = findtext(text,opening_escape,position,0) + var/next_delimiter = findtext(text, delimiter, position, 0) + var/next_opening = findtext(text, opening_escape, position, 0) while((next_opening != 0) && (next_opening < next_delimiter)) - position = findtext(text,closing_escape,next_opening + 1,0)+1 - next_delimiter = findtext(text,delimiter,position,0) - next_opening = findtext(text,opening_escape,position,0) + position = findtext(text, closing_escape, next_opening + 1, 0) + 1 + next_delimiter = findtext(text, delimiter, position, 0) + next_opening = findtext(text, opening_escape, position, 0) return next_delimiter - -//build a list from variables in text form (e.g {var1="derp"; var2; var3=7} => list(var1="derp", var2, var3=7)) -//return the filled list -/dmm_suite/proc/readlist(text as text, delimiter=",") - +// build a list from variables in text form (e.g {var1="derp"; var2; var3=7} => list(var1="derp", var2, var3=7)) +// return the filled list +/datum/dmm_suite/proc/readlist(text, delimiter = ",") var/list/to_return = list() var/position var/old_position = 1 do - //find next delimiter that is not within "..." - position = find_next_delimiter_position(text,old_position,delimiter) + // find next delimiter that is not within "..." + position = find_next_delimiter_position(text, old_position, delimiter) - //check if this is a simple variable (as in list(var1, var2)) or an associative one (as in list(var1="foo",var2=7)) - var/equal_position = findtext(text,"=",old_position, position) + // check if this is a simple variable (as in list(var1, var2)) or an associative one (as in list(var1="foo", var2=7)) + var/equal_position = findtext(text, "=", old_position, position) - var/trim_left = trim_text(copytext(text,old_position,(equal_position ? equal_position : position)),1)//the name of the variable, must trim quotes to build a BYOND compliant associatives list + var/trim_left = trim_text(copytext(text, old_position, (equal_position ? equal_position : position)), 1) // the name of the variable, must trim quotes to build a BYOND compliant associatives list old_position = position + 1 - if(equal_position)//associative var, so do the association - var/trim_right = trim_text(copytext(text,equal_position+1,position))//the content of the variable + if(equal_position) // associative var, so do the association + var/trim_right = trim_text(copytext(text, equal_position + 1, position)) // the content of the variable - //Check for string + // Check for string // Make it read to the next delimiter, instead of the quote - if(findtext(trim_right,quote,1,2)) - var/endquote = findtext(trim_right,quote,-1) + if(findtext(trim_right, quote, 1, 2)) + var/endquote = findtext(trim_right, quote, -1) if(!endquote) log_runtime(EXCEPTION("Terminating quote not found!"), src) // Our map writer escapes quotes and curly brackets to avoid // letting our simple parser choke on meanly-crafted names/etc // - so we decode it here so it's back to good ol' legibility - trim_right = dmm_decode(copytext(trim_right,2,endquote)) + trim_right = dmm_decode(copytext(trim_right, 2, endquote)) - //Check for number + // Check for number else if(isnum(text2num(trim_right))) trim_right = text2num(trim_right) - //Check for null + // Check for null else if(trim_right == "null") trim_right = null - //Check for list - else if(copytext(trim_right,1,5) == "list") - trim_right = readlist(copytext(trim_right,6,length(trim_right))) + // Check for list + else if(copytext(trim_right, 1, 5) == "list") + trim_right = readlist(copytext(trim_right, 6, length(trim_right))) - //Check for file - else if(copytext(trim_right,1,2) == "'") - trim_right = file(copytext(trim_right,2,length(trim_right))) + // Check for file + else if(copytext(trim_right, 1, 2) == "'") + trim_right = file(copytext(trim_right, 2, length(trim_right))) - //Check for path + // Check for path else if(ispath(text2path(trim_right))) trim_right = text2path(trim_right) to_return[trim_left] = trim_right - else//simple var + else// simple var to_return[trim_left] = null while(position != 0) return to_return -/dmm_suite/Destroy() +/datum/dmm_suite/Destroy() ..() return QDEL_HINT_HARDDEL_NOW ////////////////// -//Preloader datum +// Preloader datum ////////////////// // This ain't re-entrant, but we had this before the maploader update -/dmm_suite/preloader - parent_type = /datum +/datum/dmm_suite/preloader var/list/attributes var/target_path var/json_ready = 0 -/dmm_suite/preloader/proc/setup(list/the_attributes, path) +/datum/dmm_suite/preloader/proc/setup(list/the_attributes, path) if(the_attributes.len) json_ready = 0 if("map_json_data" in the_attributes) @@ -443,25 +427,24 @@ GLOBAL_DATUM_INIT(_preloader, /dmm_suite/preloader, new()) attributes = the_attributes target_path = path -/dmm_suite/preloader/proc/load(atom/what) +/datum/dmm_suite/preloader/proc/load(atom/A) if(json_ready) - var/json_data = attributes["map_json_data"] + var/json_data = dmm_decode(attributes["map_json_data"]) attributes -= "map_json_data" - json_data = dmm_decode(json_data) try - what.deserialize(json_decode(json_data)) - catch(var/exception/e) + A.deserialize(json_decode(json_data)) + catch(var/exception/E) log_runtime(EXCEPTION("Bad json data: '[json_data]'"), src) - throw e + throw E for(var/attribute in attributes) var/value = attributes[attribute] if(islist(value)) value = deepCopyList(value) - what.vars[attribute] = value + A.vars[attribute] = value GLOB.use_preloader = FALSE // If the map loader fails, make this safe -/dmm_suite/preloader/proc/reset() +/datum/dmm_suite/preloader/proc/reset() GLOB.use_preloader = FALSE attributes = list() target_path = null @@ -470,12 +453,11 @@ GLOBAL_DATUM_INIT(_preloader, /dmm_suite/preloader, new()) // so that one can have separate "unpowered" areas for ruins or whatever, // yet have a single area type for use of mapping, instead of creating // a new area type for each new ruin -/dmm_suite/loaded_map - parent_type = /datum +/datum/dmm_suite/loaded_map var/list/area_list = list() var/index = 1 // To store the state of the regex -/dmm_suite/loaded_map/proc/area_path_to_real_area(area/A) +/datum/dmm_suite/loaded_map/proc/area_path_to_real_area(area/A) if(!ispath(A, /area)) throw EXCEPTION("Wrong argument to `area_path_to_real_area`") diff --git a/code/modules/awaymissions/maploader/writer.dm b/code/modules/awaymissions/maploader/writer.dm index c6d1fd589cf..b609f26c69d 100644 --- a/code/modules/awaymissions/maploader/writer.dm +++ b/code/modules/awaymissions/maploader/writer.dm @@ -5,28 +5,12 @@ #define DMM_IGNORE_PLAYERS 16 #define DMM_IGNORE_MOBS 24 #define DMM_USE_JSON 32 -/dmm_suite - var/quote = "\"" - var/list/letter_digits = list( - "a","b","c","d","e", - "f","g","h","i","j", - "k","l","m","n","o", - "p","q","r","s","t", - "u","v","w","x","y", - "z", - "A","B","C","D","E", - "F","G","H","I","J", - "K","L","M","N","O", - "P","Q","R","S","T", - "U","V","W","X","Y", - "Z" - ) -/dmm_suite/save_map(var/turf/t1 as turf, var/turf/t2 as turf, var/map_name as text, var/flags as num) - //Check for illegal characters in file name... in a cheap way. - if(!((ckeyEx(map_name)==map_name) && ckeyEx(map_name))) +/datum/dmm_suite/proc/save_map(turf/t1, turf/t2, map_name = "", flags = 0) + // Check for illegal characters in file name... in a cheap way. + if(!((ckeyEx(map_name) == map_name) && ckeyEx(map_name))) CRASH("Invalid text supplied to proc save_map, invalid characters or empty string.") - //Check for valid turfs. + // Check for valid turfs. if(!isturf(t1) || !isturf(t2)) CRASH("Invalid arguments supplied to proc save_map, arguments were not turfs.") @@ -35,17 +19,17 @@ if(fexists(map_path)) fdel(map_path) var/saved_map = file(map_path) - var/map_text = write_map(t1,t2,flags,saved_map) + var/map_text = write_map(t1, t2, flags, saved_map) saved_map << map_text return saved_map -/dmm_suite/write_map(var/turf/t1 as turf, var/turf/t2 as turf, var/flags as num) - //Check for valid turfs. +/datum/dmm_suite/proc/write_map(turf/t1, turf/t2, flags = 0) + // Check for valid turfs. if(!isturf(t1) || !isturf(t2)) CRASH("Invalid arguments supplied to proc write_map, arguments were not turfs.") - var/turf/ne = locate(max(t1.x,t2.x),max(t1.y,t2.y),max(t1.z,t2.z)) // Outer corner - var/turf/sw = locate(min(t1.x,t2.x),min(t1.y,t2.y),min(t1.z,t2.z)) // Inner corner + var/turf/ne = locate(max(t1.x, t2.x), max(t1.y, t2.y), max(t1.z, t2.z)) // Outer corner + var/turf/sw = locate(min(t1.x, t2.x), min(t1.y, t2.y), min(t1.z, t2.z)) // Inner corner var/list/templates[0] var/list/template_buffer = list() var/template_buffer_text @@ -54,37 +38,40 @@ var/total_timer = start_watch() var/timer = start_watch() log_debug("Reading turfs...") + // Read the contents of all the turfs we were given for(var/pos_z in sw.z to ne.z) for(var/pos_y in ne.y to sw.y step -1) // We're reversing this because the map format is silly for(var/pos_x in sw.x to ne.x) - var/turf/test_turf = locate(pos_x,pos_y,pos_z) + var/turf/test_turf = locate(pos_x, pos_y, pos_z) var/test_template = make_template(test_turf, flags) var/template_number = templates.Find(test_template) if(!template_number) templates.Add(test_template) - template_number = templates.len + template_number = length(templates) template_buffer += "[template_number]," CHECK_TICK - template_buffer += ";" - template_buffer += "." - template_buffer_text = jointext(template_buffer,"") + + template_buffer_text = jointext(template_buffer, "") log_debug("Reading turfs took [stop_watch(timer)]s.") - if(templates.len == 0) + if(length(templates) == 0) CRASH("No templates found!") - var/key_length = round/*floor*/(log(letter_digits.len,templates.len-1)+1) - var/list/keys[templates.len] + + var/key_length = round(log(length(letter_digits), length(templates) - 1) + 1) // or floor + var/list/keys[length(templates)] + // Write the list of key/model pairs to the file timer = start_watch() log_debug("Writing out key/model pairs to file header...") var/list/key_models = list() - for(var/key_pos in 1 to templates.len) - keys[key_pos] = get_model_key(key_pos,key_length) + for(var/key_pos in 1 to length(templates)) + keys[key_pos] = get_model_key(key_pos, key_length) key_models += "\"[keys[key_pos]]\" = ([templates[key_pos]])\n" CHECK_TICK + dmm_text += jointext(key_models,"") log_debug("Writing key/model pairs complete, took [stop_watch(timer)]s.") @@ -92,58 +79,69 @@ // Loop over all z in our zone timer = start_watch() log_debug("Writing out key map...") + var/list/key_map = list() - for(var/z_pos=1;TRUE;z_pos=findtext(template_buffer_text,".",z_pos)+1) - if(z_pos>=length(template_buffer_text)) break - if(z_level) key_map += "\n" + for(var/z_pos = 1; TRUE; z_pos = findtext(template_buffer_text, ".", z_pos) + 1) + if(z_pos >= length(template_buffer_text)) + break + + if(z_level) + key_map += "\n" + key_map += "\n(1,1,[++z_level]) = {\"\n" - var/z_block = copytext(template_buffer_text,z_pos,findtext(template_buffer_text,".",z_pos)) - for(var/y_pos=1;TRUE;y_pos=findtext(z_block,";",y_pos)+1) - if(y_pos>=length(z_block)) break - var/y_block = copytext(z_block,y_pos,findtext(z_block,";",y_pos)) + var/z_block = copytext(template_buffer_text, z_pos, findtext(template_buffer_text, ".", z_pos)) + for(var/y_pos = 1; TRUE; y_pos = findtext(z_block, ";", y_pos) + 1) + if(y_pos >= length(z_block)) + break + + var/y_block = copytext(z_block, y_pos, findtext(z_block, ";", y_pos)) // A row of keys - for(var/x_pos=1;TRUE;x_pos=findtext(y_block,",",x_pos)+1) - if(x_pos>=length(y_block)) break - var/x_block = copytext(y_block,x_pos,findtext(y_block,",",x_pos)) + for(var/x_pos = 1; TRUE; x_pos = findtext(y_block, ",", x_pos) + 1) + if(x_pos >= length(y_block)) + break + + var/x_block = copytext(y_block, x_pos, findtext(y_block, ",", x_pos)) var/key_number = text2num(x_block) var/temp_key = keys[key_number] key_map += temp_key CHECK_TICK key_map += "\n" key_map += "\"}" - dmm_text += jointext(key_map,"") + + dmm_text += jointext(key_map, "") log_debug("Writing key map complete, took [stop_watch(timer)]s.") log_debug("TOTAL TIME: [stop_watch(total_timer)]s.") + return dmm_text -/dmm_suite/proc/make_template(var/turf/model as turf, var/flags as num) - var/use_json = 0 - if(flags & DMM_USE_JSON) - use_json = 1 +/datum/dmm_suite/proc/make_template(turf/model, flags = 0) + var/use_json = (flags & DMM_USE_JSON) ? TRUE : FALSE + var/template = "" var/turf_template = "" var/list/obj_template = list() var/list/mob_template = list() var/area_template = "" - - // Turf if(!(flags & DMM_IGNORE_TURFS)) turf_template = "[model.type][check_attributes(model,use_json=use_json)]," - else turf_template = "[world.turf]," + else + turf_template = "[world.turf]," // Objects loop if(!(flags & DMM_IGNORE_OBJS)) for(var/obj/O in model.contents) if(O.dont_save || QDELETED(O)) continue + obj_template += "[O.type][check_attributes(O,use_json=use_json)]," // Mobs Loop for(var/mob/M in model.contents) if(M.dont_save || QDELETED(M)) continue + if(M.client) if(!(flags & DMM_IGNORE_PLAYERS)) mob_template += "[M.type][check_attributes(M,use_json=use_json)]," @@ -155,65 +153,68 @@ if(!(flags & DMM_IGNORE_AREAS)) var/area/m_area = model.loc area_template = "[m_area.type][check_attributes(m_area,use_json=use_json)]" - else area_template = "[world.area]" + else + area_template = "[world.area]" template = "[jointext(obj_template,"")][jointext(mob_template,"")][turf_template][area_template]" return template -/dmm_suite/proc/check_attributes(var/atom/A,use_json=0) +/datum/dmm_suite/proc/check_attributes(atom/A, use_json = FALSE) var/attributes_text = "{" var/list/attributes = list() if(!use_json) for(var/V in A.vars) CHECK_TICK - if((!issaved(A.vars[V])) || (A.vars[V]==initial(A.vars[V]))) continue + if((!issaved(A.vars[V])) || (A.vars[V] == initial(A.vars[V]))) + continue attributes += var_to_dmm(A.vars[V], V) else - var/list/yeah = A.serialize() + var/list/to_encode = A.serialize() // We'll want to write out vars that are important to the editor // So that the map is legible as before - for(var/thing in A.map_important_vars()) + for(var/T in A.map_important_vars()) // Save vars that are important for the map editor, so that // json-encoded maps are legible for standard editors - if(A.vars[thing] != initial(A.vars[thing])) - yeah -= thing - attributes += var_to_dmm(A.vars[thing],thing) + if(A.vars[T] != initial(A.vars[T])) + to_encode -= T + attributes += var_to_dmm(A.vars[T], T) // Remove useless info - yeah -= "type" - if(yeah.len) - var/json_stuff = json_encode(yeah) + to_encode -= "type" + if(length(to_encode)) + var/json_stuff = json_encode(to_encode) attributes += var_to_dmm(json_stuff, "map_json_data") - if(attributes.len == 0) + + if(length(attributes) == 0) return // Trim a trailing semicolon - `var_to_dmm` always appends a semicolon, // so the last one will be trailing. - if(copytext(attributes_text, length(attributes_text)-1, 0) == "; ") - attributes_text = copytext(attributes_text, 1, length(attributes_text)-1) + if(copytext(attributes_text, length(attributes_text) - 1, 0) == "; ") + attributes_text = copytext(attributes_text, 1, length(attributes_text) - 1) + attributes_text = "{[jointext(attributes,"; ")]}" return attributes_text - -/dmm_suite/proc/get_model_key(var/which as num, var/key_length as num) +/datum/dmm_suite/proc/get_model_key(which, key_length) var/list/key = list() - var/working_digit = which-1 + var/working_digit = which - 1 for(var/digit_pos in key_length to 1 step -1) - var/place_value = round/*floor*/(working_digit/(letter_digits.len**(digit_pos-1))) - working_digit-=place_value*(letter_digits.len**(digit_pos-1)) - key += letter_digits[place_value+1] + var/place_value = round/*floor*/(working_digit / (length(letter_digits) ** (digit_pos - 1))) + working_digit -= place_value * (length(letter_digits) ** (digit_pos - 1)) + key += letter_digits[place_value + 1] + return jointext(key,"") - -/dmm_suite/proc/var_to_dmm(attr, name) +/datum/dmm_suite/proc/var_to_dmm(attr, name) if(istext(attr)) // dmm_encode will strip out characters that would be capable of disrupting // parsing - namely, quotes and curly braces return "[name] = \"[dmm_encode(attr)]\"" - else if(isnum(attr)||ispath(attr)) + else if(isnum(attr) || ispath(attr)) return "[name] = [attr]" - else if(isicon(attr)||isfile(attr)) + else if(isicon(attr) || isfile(attr)) if(length("[attr]") == 0) // The DM map reader is unable to read files that have a '' file/icon entry return