diff --git a/code/controllers/subsystem/mapping.dm b/code/controllers/subsystem/mapping.dm index 23aa9cfe843a..6c4f3024f293 100644 --- a/code/controllers/subsystem/mapping.dm +++ b/code/controllers/subsystem/mapping.dm @@ -33,7 +33,6 @@ SUBSYSTEM_DEF(mapping) var/list/z_list var/datum/space_level/transit var/datum/space_level/empty_space - var/datum/maploader/loader /datum/controller/subsystem/mapping/PreInit() if(!config) @@ -54,7 +53,6 @@ SUBSYSTEM_DEF(mapping) if(!config || config.defaulted) to_chat(world, "Unable to load next or default map config, defaulting to Box Station") config = old_config - loader = new loadWorld() repopulate_sorted_areas() process_teleport_locs() //Sets up the wizard teleport locations @@ -178,11 +176,15 @@ SUBSYSTEM_DEF(mapping) // check that the total z count of all maps matches the list of traits var/total_z = 0 + var/list/parsed_maps = list() for (var/file in files) var/full_path = "_maps/[path]/[file]" - var/datum/parsed_map/pm = loader.load_map(file(full_path), 1, 1, 1, cropMap=FALSE, measureOnly=TRUE) + var/datum/parsed_map/pm = new(file(full_path)) var/bounds = pm?.bounds - files[file] = total_z // save the start Z of this file + if (!bounds) + errorList |= full_path + continue + parsed_maps[pm] = total_z // save the start Z of this file total_z += bounds[MAP_MAXZ] - bounds[MAP_MINZ] + 1 if (!length(traits)) // null or empty - default @@ -203,15 +205,13 @@ SUBSYSTEM_DEF(mapping) ++i // load the maps - for (var/file in files) - var/full_path = "_maps/[path]/[file]" - var/datum/parsed_map/parsed = loader.load_map(file(full_path), 0, 0, start_z + files[file], no_changeturf = TRUE) - if(!parsed?.bounds) - errorList |= full_path - else - . += parsed + for (var/P in parsed_maps) + var/datum/parsed_map/pm = P + if (!pm.load(1, 1, start_z + parsed_maps[P], no_changeturf = TRUE)) + errorList |= pm.original_path if(!silent) INIT_ANNOUNCE("Loaded [name] in [(REALTIMEOFDAY - start_time)/10]s!") + return parsed_maps /datum/controller/subsystem/mapping/proc/loadWorld() //if any of these fail, something has gone horribly, HORRIBLY, wrong diff --git a/code/modules/mapping/README.txt b/code/modules/mapping/README.txt new file mode 100644 index 000000000000..75e57efa8bbd --- /dev/null +++ b/code/modules/mapping/README.txt @@ -0,0 +1,52 @@ +The code in this module originally evolved from dmm_suite and has since been +specialized for SS13 and otherwise tweaked to fit /tg/station's needs. + +dmm_suite version 1.0 + Released January 30th, 2011. + +NOTE: Map saving functionality removed + +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. + +------------------------ + +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. + +------------------------ + +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. + +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. diff --git a/code/modules/mapping/dmm_suite.dm b/code/modules/mapping/dmm_suite.dm deleted file mode 100644 index 625350b3ad96..000000000000 --- a/code/modules/mapping/dmm_suite.dm +++ /dev/null @@ -1,63 +0,0 @@ -/datum/maploader{ - /* - - dmm_suite version 1.0 - Released January 30th, 2011. - - NOTE: Map saving functionality removed - - 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. - - ------------------------ - - 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. - - ------------------------ - - 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. - - 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, var/cropMap as num, var/measureOnly as num, no_changeturf 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). - // cropMap: When true, the map will be cropped to fit the existing world dimensions (Optional). - // measureOnly: When true, no changes will be made to the world (Optional). - // no_changeturf: When true, turf/AfterChange won't be called on loaded turfs - } -} \ No newline at end of file diff --git a/code/modules/mapping/map_template.dm b/code/modules/mapping/map_template.dm index 295efe339f44..df8423ad66d9 100644 --- a/code/modules/mapping/map_template.dm +++ b/code/modules/mapping/map_template.dm @@ -4,7 +4,6 @@ var/height = 0 var/mappath = null var/loaded = 0 // Times loaded this round - var/static/datum/maploader/maploader = new /datum/map_template/New(path = null, rename = null) if(path) @@ -15,14 +14,14 @@ name = rename /datum/map_template/proc/preload_size(path) - var/datum/parsed_map/parsed = maploader.load_map(file(path), 1, 1, 1, cropMap=FALSE, measureOnly=TRUE) + var/datum/parsed_map/parsed = load_map(file(path), 1, 1, 1, cropMap=FALSE, measureOnly=TRUE) var/bounds = parsed?.bounds 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] return bounds -/datum/map_template/proc/initTemplateBounds(var/list/bounds) +/datum/parsed_map/proc/initTemplateBounds() var/list/obj/machinery/atmospherics/atmos_machines = list() var/list/obj/structure/cable/cables = list() var/list/atom/atoms = list() @@ -54,7 +53,7 @@ var/y = round((world.maxy - height)/2) var/datum/space_level/level = SSmapping.add_new_zlevel(name, list(ZTRAIT_AWAY = TRUE)) - var/datum/parsed_map/parsed = maploader.load_map(file(mappath), x, y, level.z_value, no_changeturf=(SSatoms.initialized == INITIALIZATION_INSSATOMS), placeOnTop=TRUE) + var/datum/parsed_map/parsed = load_map(file(mappath), x, y, level.z_value, no_changeturf=(SSatoms.initialized == INITIALIZATION_INSSATOMS), placeOnTop=TRUE) var/list/bounds = parsed.bounds if(!bounds) return FALSE @@ -62,7 +61,7 @@ repopulate_sorted_areas() //initialize things that are normally initialized after map load - initTemplateBounds(bounds) + parsed.initTemplateBounds() smooth_zlevel(world.maxz) log_game("Z-level [name] loaded at at [x],[y],[world.maxz]") @@ -78,7 +77,7 @@ if(T.y+height > world.maxy) return - var/datum/parsed_map/parsed = maploader.load_map(file(mappath), T.x, T.y, T.z, cropMap=TRUE, no_changeturf=(SSatoms.initialized == INITIALIZATION_INSSATOMS), placeOnTop=TRUE) + var/datum/parsed_map/parsed = load_map(file(mappath), T.x, T.y, T.z, cropMap=TRUE, no_changeturf=(SSatoms.initialized == INITIALIZATION_INSSATOMS), placeOnTop=TRUE) var/list/bounds = parsed?.bounds if(!bounds) return @@ -87,7 +86,7 @@ repopulate_sorted_areas() //initialize things that are normally initialized after map load - initTemplateBounds(bounds) + parsed.initTemplateBounds() log_game("[name] loaded at at [T.x],[T.y],[T.z]") return bounds diff --git a/code/modules/mapping/reader.dm b/code/modules/mapping/reader.dm index 40485d4b3533..59cf33357e50 100644 --- a/code/modules/mapping/reader.dm +++ b/code/modules/mapping/reader.dm @@ -10,77 +10,57 @@ GLOBAL_DATUM_INIT(_preloader, /datum/map_preloader, new) var/xcrd var/ycrd var/zcrd - var/xcrdStart var/gridLines /datum/parsed_map + var/original_path + var/key_len = 0 var/list/grid_models = list() var/list/gridSets = list() - var/list/bounds = list(1.#INF, 1.#INF, 1.#INF, -1.#INF, -1.#INF, -1.#INF) - var/key_len = 0 -/datum/parsed_map/proc/initTemplateBounds() - var/list/obj/machinery/atmospherics/atmos_machines = list() - var/list/obj/structure/cable/cables = list() - var/list/atom/atoms = list() + var/list/modelCache + var/list/bad_paths - var/list/turfs = block( locate(bounds[MAP_MINX], bounds[MAP_MINY], bounds[MAP_MINZ]), - locate(bounds[MAP_MAXX], bounds[MAP_MAXY], bounds[MAP_MAXZ])) - var/list/border = block(locate(max(bounds[MAP_MINX]-1, 1), max(bounds[MAP_MINY]-1, 1), bounds[MAP_MINZ]), - locate(min(bounds[MAP_MAXX]+1, world.maxx), min(bounds[MAP_MAXY]+1, world.maxy), bounds[MAP_MAXZ])) - turfs - for(var/L in turfs) - var/turf/B = L - atoms += B - for(var/A in B) - atoms += A - if(istype(A, /obj/structure/cable)) - cables += A - continue - if(istype(A, /obj/machinery/atmospherics)) - atmos_machines += A - for(var/L in border) - var/turf/T = L - T.air_update_turf(TRUE) //calculate adjacent turfs along the border to prevent runtimes + /// Unoffset bounds. Null on parse failure. + var/list/parsed_bounds + /// Offset bounds. Same as parsed_bounds until load(). + var/list/bounds - SSatoms.InitializeAtoms(atoms) - SSmachines.setup_template_powernets(cables) - SSair.setup_template_machinery(atmos_machines) + // raw strings used to represent regexes more accurately + // '' used to avoid confusing syntax highlighting + var/static/regex/dmmRegex = new(@'"([a-zA-Z]+)" = \(((?:.|\n)*?)\)\n(?!\t)|\((\d+),(\d+),(\d+)\) = \{"([a-zA-Z\n]*)"\}', "g") + var/static/regex/trimQuotesRegex = new(@'^[\s\n]+"?|"?[\s\n]+$|^"|"$', "g") + var/static/regex/trimRegex = new(@'^[\s\n]+|[\s\n]+$', "g") -/datum/maploader - // /"([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") #ifdef TESTING - var/turfsSkipped + var/turfsSkipped = 0 #endif -/** - * Construct the model map and control the loading process - * - * WORKING : - * - * 1) Makes an associative mapping of model_keys with model - * e.g aa = /turf/unsimulated/wall{icon_state = "rock"} - * 2) Read the map line by line, parsing the result (using parse_grid) - * - */ -/datum/maploader/load_map(dmm_file as file, x_offset as num, y_offset as num, z_offset as num, cropMap as num, measureOnly as num, no_changeturf as num, lower_crop_x as num, lower_crop_y as num, upper_crop_x as num, upper_crop_y as num, placeOnTop as num) - //How I wish for RAII - Master.StartLoadingMap() - #ifdef TESTING - turfsSkipped = 0 - #endif - . = load_map_impl(dmm_file, x_offset, y_offset, z_offset, cropMap, measureOnly, no_changeturf, lower_crop_x, upper_crop_x, lower_crop_y, upper_crop_y, placeOnTop) - #ifdef TESTING - if(turfsSkipped) - testing("Skipped loading [turfsSkipped] default turfs") - #endif - Master.StopLoadingMap() +/// Shortcut function to parse a map and apply it to the world. +/// +/// - dmm_file: A .dmm file to load (Required). +/// - x_offset, y_offset, z_offset: Positions representign where to load the map (Optional). +/// - cropMap: When true, the map will be cropped to fit the existing world dimensions (Optional). +/// - measureOnly: When true, no changes will be made to the world (Optional). +/// - no_changeturf: When true, turf/AfterChange won't be called on loaded turfs +/// - x_lower, x_upper, y_lower, y_upper: Coordinates (relative to the map) to crop to (Optional). +/// - placeOnTop: Whether to use turf/PlaceOnTop rather than turf/ChangeTurf (Optional). +/proc/load_map(dmm_file as file, x_offset as num, y_offset as num, z_offset as num, cropMap as num, measureOnly as num, no_changeturf as num, x_lower = -INFINITY as num, x_upper = INFINITY as num, y_lower = -INFINITY as num, y_upper = INFINITY as num, placeOnTop = FALSE as num) + var/datum/parsed_map/parsed = new(dmm_file, x_lower, x_upper, y_lower, y_upper, measureOnly) + if(parsed.bounds && !measureOnly) + parsed.load(x_offset, y_offset, z_offset, cropMap, no_changeturf, x_lower, x_upper, y_lower, y_upper, placeOnTop) + return parsed -/datum/parsed_map/New(tfile, x_offset, y_offset, z_offset, x_lower, x_upper, y_lower, y_upper, measureOnly, regex/dmmRegex, cropMap) +/// Parse a map, possibly cropping it. +/datum/parsed_map/New(tfile, x_lower = -INFINITY, x_upper = INFINITY, y_lower = -INFINITY, y_upper=INFINITY, measureOnly=FALSE) + if(isfile(tfile)) + original_path = "[tfile]" + tfile = file2text(tfile) + else if(isnull(tfile)) + // create a new datum without loading a map + return + + bounds = parsed_bounds = list(1.#INF, 1.#INF, 1.#INF, -1.#INF, -1.#INF, -1.#INF) var/stored_index = 1 //multiz lool @@ -112,12 +92,12 @@ GLOBAL_DATUM_INIT(_preloader, /datum/map_preloader, new) var/datum/grid_set/gridSet = new - gridSet.xcrdStart = curr_x + x_offset - 1 + gridSet.xcrd = curr_x //position of the currently processed square - gridSet.ycrd = text2num(dmmRegex.group[4]) + y_offset - 1 - gridSet.zcrd = text2num(dmmRegex.group[5]) + z_offset - 1 + gridSet.ycrd = text2num(dmmRegex.group[4]) + gridSet.zcrd = text2num(dmmRegex.group[5]) - bounds[MAP_MINX] = min(bounds[MAP_MINX], CLAMP(gridSet.xcrdStart, x_lower, x_upper)) + bounds[MAP_MINX] = min(bounds[MAP_MINX], CLAMP(gridSet.xcrd, x_lower, x_upper)) bounds[MAP_MINZ] = min(bounds[MAP_MINZ], gridSet.zcrd) bounds[MAP_MAXZ] = max(bounds[MAP_MAXZ], gridSet.zcrd) @@ -139,103 +119,110 @@ GLOBAL_DATUM_INIT(_preloader, /datum/map_preloader, new) bounds[MAP_MINY] = min(bounds[MAP_MINY], CLAMP(gridSet.ycrd, y_lower, y_upper)) gridSet.ycrd += gridLines.len - 1 // Start at the top and work down + bounds[MAP_MAXY] = max(bounds[MAP_MAXY], CLAMP(gridSet.ycrd, y_lower, y_upper)) - if(!cropMap && gridSet.ycrd > world.maxy) - bounds[MAP_MAXY] = max(bounds[MAP_MAXY], CLAMP(gridSet.ycrd, y_lower, y_upper)) - else - bounds[MAP_MAXY] = max(bounds[MAP_MAXY], CLAMP(min(gridSet.ycrd, world.maxy), y_lower, y_upper)) - - var/maxx = gridSet.xcrdStart + var/maxx = gridSet.xcrd if(gridLines.len) //Not an empty map - maxx = max(maxx, gridSet.xcrdStart + length(gridLines[1]) / key_len - 1) + maxx = max(maxx, gridSet.xcrd + length(gridLines[1]) / key_len - 1) - bounds[MAP_MAXX] = CLAMP(max(bounds[MAP_MAXX], cropMap ? min(maxx, world.maxx) : maxx), x_lower, x_upper) + bounds[MAP_MAXX] = CLAMP(max(bounds[MAP_MAXX], maxx), x_lower, x_upper) CHECK_TICK -/datum/maploader/proc/load_map_impl(dmm_file, x_offset, y_offset, z_offset, cropMap, measureOnly, no_changeturf, x_lower = -INFINITY, x_upper = INFINITY, y_lower = -INFINITY, y_upper = INFINITY, placeOnTop = FALSE) - var/tfile = dmm_file//the map file we're creating - if(isfile(tfile)) - tfile = file2text(tfile) + // Indicate failure to parse any coordinates by nulling bounds + if(bounds[1] == 1.#INF) + bounds = null + parsed_bounds = bounds - if(!x_offset) - x_offset = 1 - if(!y_offset) - y_offset = 1 - if(!z_offset) - z_offset = world.maxz + 1 +/// Load the parsed map into the world. See /proc/load_map for arguments. +/datum/parsed_map/proc/load(x_offset, y_offset, z_offset, cropMap, no_changeturf, x_lower, x_upper, y_lower, y_upper, placeOnTop) + //How I wish for RAII + Master.StartLoadingMap() + . = _load_impl(x_offset, y_offset, z_offset, cropMap, no_changeturf, x_lower, x_upper, y_lower, y_upper, placeOnTop) + Master.StopLoadingMap() - var/datum/parsed_map/parsed = new(tfile, x_offset, y_offset, z_offset, x_lower, x_upper, y_lower, y_upper, measureOnly, dmmRegex, cropMap) +// Do not call except via load() above. +/datum/parsed_map/proc/_load_impl(x_offset = 1, y_offset = 1, z_offset = world.maxz + 1, cropMap = FALSE, no_changeturf = FALSE, x_lower = -INFINITY, x_upper = INFINITY, y_lower = -INFINITY, y_upper = INFINITY, placeOnTop = FALSE) + var/list/modelCache = build_cache(no_changeturf) + var/space_key = modelCache[SPACE_KEY] + var/list/bounds + src.bounds = bounds = list(1.#INF, 1.#INF, 1.#INF, -1.#INF, -1.#INF, -1.#INF) - var/list/modelCache - var/space_key - if(!measureOnly) - modelCache = build_cache(parsed, no_changeturf) - space_key = modelCache[SPACE_KEY] - - for(var/I in parsed.gridSets) + for(var/I in gridSets) var/datum/grid_set/gset = I - if(!cropMap && !measureOnly && gset.ycrd > world.maxy) - world.maxy = gset.ycrd // Expand Y here. X is expanded in the loop below - var/zexpansion = gset.zcrd > world.maxz - if(zexpansion && !measureOnly) + var/ycrd = gset.ycrd + y_offset - 1 + var/zcrd = gset.zcrd + z_offset - 1 + if(!cropMap && ycrd > world.maxy) + world.maxy = ycrd // Expand Y here. X is expanded in the loop below + var/zexpansion = zcrd > world.maxz + if(zexpansion) if(cropMap) continue else - while (gset.zcrd > world.maxz) //create a new z_level if needed + while (zcrd > world.maxz) //create a new z_level if needed world.incrementMaxZ() if(!no_changeturf) WARNING("Z-level expansion occurred without no_changeturf set, this may cause problems when /turf/AfterChange is called") - var/maxx = gset.xcrdStart - if(!measureOnly) - for(var/line in gset.gridLines) - if((gset.ycrd - y_offset + 1) < y_lower || (gset.ycrd - y_offset + 1) > y_upper) //Reverse operation and check if it is out of bounds of cropping. - --gset.ycrd - continue - if(gset.ycrd <= world.maxy && gset.ycrd >= 1) - gset.xcrd = gset.xcrdStart - for(var/tpos = 1 to length(line) - parsed.key_len + 1 step parsed.key_len) - if((gset.xcrd - x_offset + 1) < x_lower || (gset.xcrd - x_offset + 1) > x_upper) //Same as above. - ++gset.xcrd - continue //X cropping. - if(gset.xcrd > world.maxx) - if(cropMap) - break - else - world.maxx = gset.xcrd + for(var/line in gset.gridLines) + if((ycrd - y_offset + 1) < y_lower || (ycrd - y_offset + 1) > y_upper) //Reverse operation and check if it is out of bounds of cropping. + --ycrd + continue + if(ycrd <= world.maxy && ycrd >= 1) + var/xcrd = gset.xcrd + x_offset - 1 + for(var/tpos = 1 to length(line) - key_len + 1 step key_len) + if((xcrd - x_offset + 1) < x_lower || (xcrd - x_offset + 1) > x_upper) //Same as above. + ++xcrd + continue //X cropping. + if(xcrd > world.maxx) + if(cropMap) + break + else + world.maxx = xcrd - if(gset.xcrd >= 1) - var/model_key = copytext(line, tpos, tpos + parsed.key_len) - var/no_afterchange = no_changeturf || zexpansion - if(!no_afterchange || (model_key != space_key)) - var/list/cache = modelCache[model_key] - if(!cache) - CRASH("Undefined model key in DMM: [model_key]") - build_coordinate(cache, gset.xcrd, gset.ycrd, gset.zcrd, no_afterchange, placeOnTop) - #ifdef TESTING - else - ++turfsSkipped - #endif - CHECK_TICK - maxx = max(maxx, gset.xcrd) - ++gset.xcrd - --gset.ycrd + if(xcrd >= 1) + var/model_key = copytext(line, tpos, tpos + key_len) + var/no_afterchange = no_changeturf || zexpansion + if(!no_afterchange || (model_key != space_key)) + var/list/cache = modelCache[model_key] + if(!cache) + CRASH("Undefined model key in DMM: [model_key]") + build_coordinate(cache, xcrd, ycrd, zcrd, no_afterchange, placeOnTop) + + // only bother with bounds that actually exist + bounds[MAP_MINX] = min(bounds[MAP_MINX], xcrd) + bounds[MAP_MINY] = min(bounds[MAP_MINY], ycrd) + bounds[MAP_MINZ] = min(bounds[MAP_MINZ], zcrd) + bounds[MAP_MAXX] = max(bounds[MAP_MAXX], xcrd) + bounds[MAP_MAXY] = max(bounds[MAP_MAXY], ycrd) + bounds[MAP_MAXZ] = max(bounds[MAP_MAXZ], zcrd) + #ifdef TESTING + else + ++turfsSkipped + #endif + CHECK_TICK + ++xcrd + --ycrd CHECK_TICK - var/list/bounds = parsed.bounds - if(bounds[1] == 1.#INF) // Shouldn't need to check every item - parsed.bounds = null - else if(!measureOnly && !no_changeturf) + if(!no_changeturf) 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(CHANGETURF_IGNORE_AIR) - return parsed -/datum/maploader/proc/build_cache(datum/parsed_map/parsed, no_changeturf) - . = list() - var/list/grid_models = parsed.grid_models + #ifdef TESTING + if(turfsSkipped) + testing("Skipped loading [turfsSkipped] default turfs") + #endif + + return TRUE + +/datum/parsed_map/proc/build_cache(no_changeturf) + if(modelCache) + return modelCache + . = modelCache = list() + var/list/grid_models = src.grid_models for(var/model_key in grid_models) var/model = grid_models[model_key] var/list/members = list() //will contain all members (paths) in model (in our example : /turf/unsimulated/wall and /area/mine/explored) @@ -249,16 +236,18 @@ GLOBAL_DATUM_INIT(_preloader, /datum/map_preloader, new) var/old_position = 1 var/dpos - do + while(dpos != 0) //finding next member (e.g /turf/unsimulated/wall{icon_state = "rock"} or /area/mine/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/variables_start = findtext(full_def, "{") - var/atom_def = text2path(trim_text(copytext(full_def, 1, variables_start))) //path definition, e.g /obj/foo/bar + var/path_text = trim_text(copytext(full_def, 1, variables_start)) + var/atom_def = text2path(path_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! + LAZYADD(bad_paths, path_text) continue members.Add(atom_def) @@ -281,7 +270,6 @@ GLOBAL_DATUM_INIT(_preloader, /datum/map_preloader, new) members_attributes[index++] = fields CHECK_TICK - while(dpos != 0) //check and see if we can just skip this turf //So you don't have to understand this horrid statement, we can do this if @@ -307,7 +295,11 @@ GLOBAL_DATUM_INIT(_preloader, /datum/map_preloader, new) .[model_key] = list(members, members_attributes) +<<<<<<< HEAD /datum/maploader/proc/build_coordinate(list/model, xcrd as num, ycrd as num, zcrd as num, no_changeturf as num, placeOnTop as num, stationroom = FALSE) //yogs +======= +/datum/parsed_map/proc/build_coordinate(list/model, xcrd as num, ycrd as num, zcrd as num, no_changeturf as num, placeOnTop as num) +>>>>>>> b83424bd87... Refactor the map loader (#39567) var/index var/list/members = model[1] var/list/members_attributes = model[2] @@ -370,7 +362,7 @@ GLOBAL_DATUM_INIT(_preloader, /datum/map_preloader, new) //////////////// //Instance an atom at (x,y,z) and gives it the variables in attributes -/datum/maploader/proc/instance_atom(path,list/attributes, turf/crds, no_changeturf, placeOnTop) +/datum/parsed_map/proc/instance_atom(path,list/attributes, turf/crds, no_changeturf, placeOnTop) GLOB._preloader.setup(attributes, path) if(crds) @@ -393,13 +385,13 @@ GLOBAL_DATUM_INIT(_preloader, /datum/map_preloader, new) stoplag() SSatoms.map_loader_begin() -/datum/maploader/proc/create_atom(path, crds) +/datum/parsed_map/proc/create_atom(path, crds) set waitfor = FALSE . = new path (crds) //text trimming (both directions) helper proc //optionally removes quotes before and after the text (for variable name) -/datum/maploader/proc/trim_text(what as text,trim_quotes=0) +/datum/parsed_map/proc/trim_text(what as text,trim_quotes=0) if(trim_quotes) return trimQuotesRegex.Replace(what, "") else @@ -408,7 +400,7 @@ GLOBAL_DATUM_INIT(_preloader, /datum/map_preloader, new) //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/maploader/proc/find_next_delimiter_position(text as text,initial_position as num, delimiter=",",opening_escape="\"",closing_escape="\"") +/datum/parsed_map/proc/find_next_delimiter_position(text as text,initial_position as num, delimiter=",",opening_escape="\"",closing_escape="\"") var/position = initial_position var/next_delimiter = findtext(text,delimiter,position,0) var/next_opening = findtext(text,opening_escape,position,0) @@ -423,7 +415,7 @@ GLOBAL_DATUM_INIT(_preloader, /datum/map_preloader, new) //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/maploader/proc/readlist(text as text, delimiter=",") +/datum/parsed_map/proc/readlist(text as text, delimiter=",") var/list/to_return = list() @@ -476,7 +468,7 @@ GLOBAL_DATUM_INIT(_preloader, /datum/map_preloader, new) return to_return -/datum/maploader/Destroy() +/datum/parsed_map/Destroy() ..() return QDEL_HINT_HARDDEL_NOW diff --git a/code/modules/procedural_mapping/mapGenerators/repair.dm b/code/modules/procedural_mapping/mapGenerators/repair.dm index 99750ed9d090..7516f29266d2 100644 --- a/code/modules/procedural_mapping/mapGenerators/repair.dm +++ b/code/modules/procedural_mapping/mapGenerators/repair.dm @@ -21,13 +21,12 @@ return var/datum/mapGenerator/repair/reload_station_map/mother1 = mother GLOB.reloading_map = TRUE - var/static/datum/maploader/reloader = new // This is kind of finicky on multi-Z maps but the reader would need to be // changed to allow Z cropping and that's a mess var/z_offset = SSmapping.station_start var/list/bounds for (var/path in SSmapping.config.GetFullMapPaths()) - var/datum/parsed_map/parsed = reloader.load_map(file(path), measureOnly = FALSE, no_changeturf = FALSE,x_offset = 0, y_offset = 0, z_offset = z_offset, cropMap=TRUE, lower_crop_x = mother1.x_low, lower_crop_y = mother1.y_low, upper_crop_x = mother1.x_high, upper_crop_y = mother1.y_high) + var/datum/parsed_map/parsed = load_map(file(path), 1, 1, z_offset, measureOnly = FALSE, no_changeturf = FALSE, cropMap=TRUE, x_lower = mother1.x_low, y_lower = mother1.y_low, x_upper = mother1.x_high, y_upper = mother1.y_high) bounds = parsed?.bounds z_offset += bounds[MAP_MAXZ] - bounds[MAP_MINZ] + 1 diff --git a/tgstation.dme b/tgstation.dme index 9c98d54cbb03..119046216b15 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -1738,7 +1738,6 @@ #include "code\modules\lighting\lighting_setup.dm" #include "code\modules\lighting\lighting_source.dm" #include "code\modules\lighting\lighting_turf.dm" -#include "code\modules\mapping\dmm_suite.dm" #include "code\modules\mapping\map_template.dm" #include "code\modules\mapping\mapping_helpers.dm" #include "code\modules\mapping\reader.dm"