Refactor dmm_suite for readability

Clean up arguments, comments and paths of
/dmm_suite (now /datum/dmm_suite) to
increase readability should other developers
want to take a look at it
This commit is contained in:
mochi
2020-06-19 12:04:55 +02:00
parent ab957a66ea
commit a640335c29
4 changed files with 236 additions and 241 deletions

View File

@@ -17,7 +17,7 @@
name = rename name = rename
/datum/map_template/proc/preload_size(path) /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) if(bounds)
width = bounds[MAP_MAXX] // Assumes all templates are rectangular, have a single Z level, and begin at 1,1,1 width = bounds[MAP_MAXX] // Assumes all templates are rectangular, have a single Z level, and begin at 1,1,1
height = bounds[MAP_MAXY] height = bounds[MAP_MAXY]
@@ -49,7 +49,7 @@
// if given a multi-z template // if given a multi-z template
// it might need to be adapted for that when that time comes // it might need to be adapted for that when that time comes
GLOB.space_manager.add_dirt(placement.z) 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) if(!bounds)
return 0 return 0
if(bot_left == null || top_right == null) if(bot_left == null || top_right == null)

View File

@@ -1,6 +1,4 @@
GLOBAL_DATUM_INIT(maploader, /dmm_suite, new()) /*
dmm_suite{
/*
dmm_suite version 1.0 dmm_suite version 1.0
Released January 30th, 2011. Released January 30th, 2011.
@@ -50,23 +48,9 @@ dmm_suite{
code, and that the /dmm_reader trusts that files to be loaded are in fact valid 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. .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){ GLOBAL_DATUM_INIT(maploader, /datum/dmm_suite, new())
// 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).
}
// save_map is included as a legacy proc. Use write_map instead. /datum/dmm_suite
verb/save_map(var/turf/t1 as turf, var/turf/t2 as turf, var/map_name as text, var/flags as num){ var/static/quote = "\""
// 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).
}
}

View File

@@ -1,13 +1,13 @@
/////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////
//SS13 Optimized Map loader // SS13 Optimized Map loader
////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////
//As of 3.6.2016 // As of 3.6.2016
//global datum that will preload variables on atoms instanciation // global datum that will preload variables on atoms instanciation
GLOBAL_VAR_INIT(use_preloader, FALSE) GLOBAL_VAR_INIT(use_preloader, FALSE)
GLOBAL_DATUM_INIT(_preloader, /dmm_suite/preloader, new()) GLOBAL_DATUM_INIT(_preloader, /datum/dmm_suite/preloader, new())
/dmm_suite /datum/dmm_suite
// These regexes are global - meaning that starting the maploader again mid-load will // 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 // reset progress - which means we need to track our index per-map, or we'll
// eternally recurse // eternally recurse
@@ -37,13 +37,13 @@ GLOBAL_DATUM_INIT(_preloader, /dmm_suite/preloader, new())
* atmos will attempt to start before it's ready, causing runtimes galore if init is * atmos will attempt to start before it's ready, causing runtimes galore if init is
* allowed to romp unchecked. * 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) /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/tfile = dmm_file// the map file we're creating
var/fname = "Lambda" var/fname = "Lambda"
if(isfile(tfile)) if(isfile(tfile))
fname = "[tfile]" fname = "[tfile]"
tfile = file2text(tfile) tfile = file2text(tfile)
if(length(tfile) == 0) if(!length(tfile))
throw EXCEPTION("Map path '[fname]' does not exist!") throw EXCEPTION("Map path '[fname]' does not exist!")
if(!x_offset) if(!x_offset)
@@ -57,7 +57,7 @@ GLOBAL_DATUM_INIT(_preloader, /dmm_suite/preloader, new())
var/list/grid_models = list() var/list/grid_models = list()
var/key_len = 0 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 // This try-catch is used as a budget "Finally" clause, as the dirt count
// needs to be reset // needs to be reset
var/watch = start_watch() var/watch = start_watch()
@@ -76,27 +76,27 @@ GLOBAL_DATUM_INIT(_preloader, /dmm_suite/preloader, new())
if(!key_len) if(!key_len)
key_len = length(key) key_len = length(key)
else else
throw EXCEPTION("Inconsistant key length in DMM") throw EXCEPTION("Inconsistent key length in DMM")
if(!measureOnly) if(!measureOnly)
grid_models[key] = dmmRegex.group[2] grid_models[key] = dmmRegex.group[2]
// (1,1,1) = {"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"} // (1, 1, 1) = {"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"}
else if(dmmRegex.group[3]) // Coords else if(dmmRegex.group[3]) // Coords
if(!key_len) if(!key_len)
throw EXCEPTION("Coords before model definition in DMM") throw EXCEPTION("Coords before model definition in DMM")
var/xcrdStart = text2num(dmmRegex.group[3]) + x_offset - 1 var/xcrdStart = text2num(dmmRegex.group[3]) + x_offset - 1
//position of the currently processed square // position of the currently processed square
var/xcrd var/xcrd
var/ycrd = text2num(dmmRegex.group[4]) + y_offset - 1 var/ycrd = text2num(dmmRegex.group[4]) + y_offset - 1
var/zcrd = text2num(dmmRegex.group[5]) + z_offset - 1 var/zcrd = text2num(dmmRegex.group[5]) + z_offset - 1
if(!measureOnly) if(!measureOnly)
if(zcrd > world.maxz) if(zcrd > world.maxz)
if(cropMap) if(shouldCropMap)
continue continue
else 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_MINX] = min(bounds[MAP_MINX], xcrdStart)
bounds[MAP_MINZ] = min(bounds[MAP_MINZ], zcrd) bounds[MAP_MINZ] = min(bounds[MAP_MINZ], zcrd)
@@ -118,7 +118,7 @@ GLOBAL_DATUM_INIT(_preloader, /dmm_suite/preloader, new())
bounds[MAP_MINY] = min(bounds[MAP_MINY], ycrd) bounds[MAP_MINY] = min(bounds[MAP_MINY], ycrd)
ycrd += gridLines.len - 1 // Start at the top and work down ycrd += gridLines.len - 1 // Start at the top and work down
if(!cropMap && ycrd > world.maxy) if(!shouldCropMap && ycrd > world.maxy)
if(!measureOnly) if(!measureOnly)
world.maxy = ycrd // Expand Y here. X is expanded in the loop below world.maxy = ycrd // Expand Y here. X is expanded in the loop below
bounds[MAP_MAXY] = max(bounds[MAP_MAXY], ycrd) bounds[MAP_MAXY] = max(bounds[MAP_MAXY], ycrd)
@@ -133,9 +133,9 @@ GLOBAL_DATUM_INIT(_preloader, /dmm_suite/preloader, new())
for(var/line in gridLines) for(var/line in gridLines)
if(ycrd <= world.maxy && ycrd >= 1) if(ycrd <= world.maxy && ycrd >= 1)
xcrd = xcrdStart 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(xcrd > world.maxx)
if(cropMap) if(shouldCropMap)
break break
else else
world.maxx = xcrd world.maxx = xcrd
@@ -152,7 +152,7 @@ GLOBAL_DATUM_INIT(_preloader, /dmm_suite/preloader, new())
maxx = max(maxx, xcrd) maxx = max(maxx, xcrd)
++xcrd ++xcrd
--ycrd --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 CHECK_TICK
catch(var/exception/e) catch(var/exception/e)
@@ -175,8 +175,8 @@ GLOBAL_DATUM_INIT(_preloader, /dmm_suite/preloader, new())
if(!measureOnly) 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]))) 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 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 // 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) T.AfterChange(TRUE, keep_cabling = TRUE)
return bounds return bounds
/** /**
@@ -196,14 +196,14 @@ GLOBAL_DATUM_INIT(_preloader, /dmm_suite/preloader, new())
* 4) Instanciates the atom with its variables * 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() /*Method parse_grid()
- Accepts a text string containing a comma separated list of type paths of the - 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. 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 // 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_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/list/cached = modelCache[model]
var/index var/index
@@ -212,7 +212,7 @@ GLOBAL_DATUM_INIT(_preloader, /dmm_suite/preloader, new())
members_attributes = cached[2] members_attributes = cached[2]
else else
///////////////////////////////////////////////////////// /////////////////////////////////////////////////////////
//Constructing members and corresponding variables lists // Constructing members and corresponding variables lists
//////////////////////////////////////////////////////// ////////////////////////////////////////////////////////
members = list() members = list()
@@ -223,13 +223,13 @@ GLOBAL_DATUM_INIT(_preloader, /dmm_suite/preloader, new())
var/dpos var/dpos
do do
//finding next member (e.g /turf/unsimulated/wall{icon_state = "rock"} or /area/mine/dangerous/explored) // 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 {...} 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/variables_start = findtext(full_def, "{")
var/atom_text = trim_text(copytext(full_def, 1, variables_start)) 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 old_position = dpos + 1
if(!atom_def) // Skip the item if the path does not exist. Fix your crap, mappers! if(!atom_def) // Skip the item if the path does not exist. Fix your crap, mappers!
@@ -237,14 +237,14 @@ GLOBAL_DATUM_INIT(_preloader, /dmm_suite/preloader, new())
continue continue
members.Add(atom_def) 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() var/list/fields = list()
if(variables_start)//if there's any variable if(variables_start) // if there's any variable
full_def = copytext(full_def,variables_start+1,length(full_def))//removing the last '}' full_def = copytext(full_def, variables_start + 1, length(full_def)) // removing the last '}'
fields = readlist(full_def, ";") 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.len++
members_attributes[index++] = fields members_attributes[index++] = fields
@@ -255,24 +255,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 index = members.len
var/turf/crds = locate(xcrd,ycrd,zcrd) var/turf/crds = locate(xcrd, ycrd, zcrd)
if(members[index] != /area/template_noop) if(members[index] != /area/template_noop)
// We assume `members[index]` is an area path, as above, yes? I will operate // We assume `members[index]` is an area path, as above, yes? I will operate
// on that assumption. // on that assumption.
if(!ispath(members[index], /area)) if(!ispath(members[index], /area))
throw EXCEPTION("Oh no, I thought this was an 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
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])
instance = LM.area_path_to_real_area(members[index])
if(crds) if(crds)
instance.contents.Add(crds) instance.contents.Add(crds)
@@ -280,45 +279,45 @@ GLOBAL_DATUM_INIT(_preloader, /dmm_suite/preloader, new())
if(GLOB.use_preloader && instance) if(GLOB.use_preloader && instance)
GLOB._preloader.load(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 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++ first_turf_index++
//instanciate the first /turf // instanciate the first /turf
var/turf/T var/turf/T
if(members[first_turf_index] != /turf/template_noop) 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(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 index = first_turf_index + 1
while(index <= members.len - 1) // Last item is an /area while(index <= members.len - 1) // Last item is an /area
var/underlay var/underlay
if(istype(T, /turf)) // I blame this on the stupid clown who coded the BYOND map editor if(istype(T, /turf)) // I blame this on the stupid clown who coded the BYOND map editor
underlay = T.appearance underlay = T.appearance
T = instance_atom(members[index],members_attributes[index],xcrd,ycrd,zcrd)//instance new turf T = instance_atom(members[index], members_attributes[index], xcrd, ycrd, zcrd) // instance new turf
if(ispath(members[index],/turf)) if(ispath(members[index], /turf))
T.underlays += underlay T.underlays += underlay
index++ index++
//finally instance all remainings objects/mobs // finally instance all remainings objects/mobs
for(index in 1 to first_turf_index-1) for(index in 1 to first_turf_index - 1)
instance_atom(members[index],members_attributes[index],xcrd,ycrd,zcrd) instance_atom(members[index], members_attributes[index], xcrd, ycrd, zcrd)
CHECK_TICK CHECK_TICK
//////////////// ////////////////
//Helpers procs // Helpers procs
//////////////// ////////////////
//Instance an atom at (x,y,z) and gives it the variables in attributes // 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) /datum/dmm_suite/proc/instance_atom(path, list/attributes, x = 0, y = 0, z = 0)
var/atom/instance var/atom/instance
GLOB._preloader.setup(attributes, path) GLOB._preloader.setup(attributes, path)
var/turf/T = locate(x,y,z) var/turf/T = locate(x, y, z)
if(T) if(T)
if(ispath(path, /turf)) if(ispath(path, /turf))
T.ChangeTurf(path, defer_change = TRUE, keep_icon = FALSE) T.ChangeTurf(path, defer_change = TRUE, keep_icon = FALSE)
@@ -326,115 +325,112 @@ GLOBAL_DATUM_INIT(_preloader, /dmm_suite/preloader, new())
else if(ispath(path, /area)) else if(ispath(path, /area))
else 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) GLOB._preloader.load(instance)
return instance return instance
//text trimming (both directions) helper proc // text trimming (both directions) helper proc
//optionally removes quotes before and after the text (for variable name) // optionally removes quotes before and after the text (for variable name)
/dmm_suite/proc/trim_text(what as text,trim_quotes=0) /datum/dmm_suite/proc/trim_text(what = "", trim_quotes = FALSE)
if(trim_quotes) if(trim_quotes)
return trimQuotesRegex.Replace(what, "") return trimQuotesRegex.Replace(what, "")
else else
return trimRegex.Replace(what, "") return trimRegex.Replace(what, "")
// find the position of the next delimiter, skipping whatever is comprised between opening_escape and closing_escape
//find the position of the next delimiter,skipping whatever is comprised between opening_escape and closing_escape // returns 0 if reached the last delimiter
//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)
/dmm_suite/proc/find_next_delimiter_position(text as text,initial_position as num, delimiter=",",opening_escape=quote,closing_escape=quote)
var/position = initial_position var/position = initial_position
var/next_delimiter = findtext(text,delimiter,position,0) var/next_delimiter = findtext(text, delimiter, position, 0)
var/next_opening = findtext(text,opening_escape,position,0) var/next_opening = findtext(text, opening_escape, position, 0)
while((next_opening != 0) && (next_opening < next_delimiter)) while((next_opening != 0) && (next_opening < next_delimiter))
position = findtext(text,closing_escape,next_opening + 1,0)+1 position = findtext(text, closing_escape, next_opening + 1, 0) + 1
next_delimiter = findtext(text,delimiter,position,0) next_delimiter = findtext(text, delimiter, position, 0)
next_opening = findtext(text,opening_escape,position,0) next_opening = findtext(text, opening_escape, position, 0)
return next_delimiter return next_delimiter
// build a list from variables in text form (e.g {var1="derp"; var2; var3=7} => list(var1="derp", var2, var3=7))
//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
//return the filled list /datum/dmm_suite/proc/readlist(text = "", delimiter = ",")
/dmm_suite/proc/readlist(text as text, delimiter=",")
var/list/to_return = list() var/list/to_return = list()
var/position var/position
var/old_position = 1 var/old_position = 1
do do
//find next delimiter that is not within "..." // find next delimiter that is not within "..."
position = find_next_delimiter_position(text,old_position,delimiter) 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)) // 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/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 old_position = position + 1
if(equal_position)//associative var, so do the association 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 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 // Make it read to the next delimiter, instead of the quote
if(findtext(trim_right,quote,1,2)) if(findtext(trim_right, quote, 1, 2))
var/endquote = findtext(trim_right,quote,-1) var/endquote = findtext(trim_right, quote, -1)
if(!endquote) if(!endquote)
log_runtime(EXCEPTION("Terminating quote not found!"), src) log_runtime(EXCEPTION("Terminating quote not found!"), src)
// Our map writer escapes quotes and curly brackets to avoid // Our map writer escapes quotes and curly brackets to avoid
// letting our simple parser choke on meanly-crafted names/etc // letting our simple parser choke on meanly-crafted names/etc
// - so we decode it here so it's back to good ol' legibility // - 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))) else if(isnum(text2num(trim_right)))
trim_right = text2num(trim_right) trim_right = text2num(trim_right)
//Check for null // Check for null
else if(trim_right == "null") else if(trim_right == "null")
trim_right = null trim_right = null
//Check for list // Check for list
else if(copytext(trim_right,1,5) == "list") else if(copytext(trim_right, 1, 5) == "list")
trim_right = readlist(copytext(trim_right,6,length(trim_right))) trim_right = readlist(copytext(trim_right, 6, length(trim_right)))
//Check for file // Check for file
else if(copytext(trim_right,1,2) == "'") else if(copytext(trim_right, 1, 2) == "'")
trim_right = file(copytext(trim_right,2,length(trim_right))) trim_right = file(copytext(trim_right, 2, length(trim_right)))
//Check for path // Check for path
else if(ispath(text2path(trim_right))) else if(ispath(text2path(trim_right)))
trim_right = text2path(trim_right) trim_right = text2path(trim_right)
to_return[trim_left] = trim_right to_return[trim_left] = trim_right
else//simple var else// simple var
to_return[trim_left] = null to_return[trim_left] = null
while(position != 0) while(position != 0)
return to_return return to_return
/dmm_suite/Destroy() /datum/dmm_suite/Destroy()
..() ..()
return QDEL_HINT_HARDDEL_NOW return QDEL_HINT_HARDDEL_NOW
////////////////// //////////////////
//Preloader datum // Preloader datum
////////////////// //////////////////
// This ain't re-entrant, but we had this before the maploader update // This ain't re-entrant, but we had this before the maploader update
/dmm_suite/preloader /datum/dmm_suite/preloader
parent_type = /datum parent_type = /datum
var/list/attributes var/list/attributes
var/target_path var/target_path
var/json_ready = 0 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) if(the_attributes.len)
json_ready = 0 json_ready = 0
if("map_json_data" in the_attributes) if("map_json_data" in the_attributes)
@@ -443,25 +439,24 @@ GLOBAL_DATUM_INIT(_preloader, /dmm_suite/preloader, new())
attributes = the_attributes attributes = the_attributes
target_path = path target_path = path
/dmm_suite/preloader/proc/load(atom/what) /datum/dmm_suite/preloader/proc/load(atom/A)
if(json_ready) if(json_ready)
var/json_data = attributes["map_json_data"] var/json_data = dmm_decode(attributes["map_json_data"])
attributes -= "map_json_data" attributes -= "map_json_data"
json_data = dmm_decode(json_data)
try try
what.deserialize(json_decode(json_data)) A.deserialize(json_decode(json_data))
catch(var/exception/e) catch(var/exception/E)
log_runtime(EXCEPTION("Bad json data: '[json_data]'"), src) log_runtime(EXCEPTION("Bad json data: '[json_data]'"), src)
throw e throw E
for(var/attribute in attributes) for(var/attribute in attributes)
var/value = attributes[attribute] var/value = attributes[attribute]
if(islist(value)) if(islist(value))
value = deepCopyList(value) value = deepCopyList(value)
what.vars[attribute] = value A.vars[attribute] = value
GLOB.use_preloader = FALSE GLOB.use_preloader = FALSE
// If the map loader fails, make this safe // If the map loader fails, make this safe
/dmm_suite/preloader/proc/reset() /datum/dmm_suite/preloader/proc/reset()
GLOB.use_preloader = FALSE GLOB.use_preloader = FALSE
attributes = list() attributes = list()
target_path = null target_path = null
@@ -470,12 +465,12 @@ GLOBAL_DATUM_INIT(_preloader, /dmm_suite/preloader, new())
// so that one can have separate "unpowered" areas for ruins or whatever, // 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 // yet have a single area type for use of mapping, instead of creating
// a new area type for each new ruin // a new area type for each new ruin
/dmm_suite/loaded_map /datum/dmm_suite/loaded_map
parent_type = /datum parent_type = /datum
var/list/area_list = list() var/list/area_list = list()
var/index = 1 // To store the state of the regex 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)) if(!ispath(A, /area))
throw EXCEPTION("Wrong argument to `area_path_to_real_area`") throw EXCEPTION("Wrong argument to `area_path_to_real_area`")

View File

@@ -5,28 +5,27 @@
#define DMM_IGNORE_PLAYERS 16 #define DMM_IGNORE_PLAYERS 16
#define DMM_IGNORE_MOBS 24 #define DMM_IGNORE_MOBS 24
#define DMM_USE_JSON 32 #define DMM_USE_JSON 32
/dmm_suite /datum/dmm_suite
var/quote = "\"" var/static/list/letter_digits = list(
var/list/letter_digits = list( "a", "b", "c", "d", "e",
"a","b","c","d","e", "f", "g", "h", "i", "j",
"f","g","h","i","j", "k", "l", "m", "n", "o",
"k","l","m","n","o", "p", "q", "r", "s", "t",
"p","q","r","s","t", "u", "v", "w", "x", "y",
"u","v","w","x","y",
"z", "z",
"A","B","C","D","E", "A", "B", "C", "D", "E",
"F","G","H","I","J", "F", "G", "H", "I", "J",
"K","L","M","N","O", "K", "L", "M", "N", "O",
"P","Q","R","S","T", "P", "Q", "R", "S", "T",
"U","V","W","X","Y", "U", "V", "W", "X", "Y",
"Z" "Z"
) )
/dmm_suite/save_map(var/turf/t1 as turf, var/turf/t2 as turf, var/map_name as text, var/flags as num) /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. // Check for illegal characters in file name... in a cheap way.
if(!((ckeyEx(map_name)==map_name) && ckeyEx(map_name))) if(!((ckeyEx(map_name) == map_name) && ckeyEx(map_name)))
CRASH("Invalid text supplied to proc save_map, invalid characters or empty string.") 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)) if(!isturf(t1) || !isturf(t2))
CRASH("Invalid arguments supplied to proc save_map, arguments were not turfs.") CRASH("Invalid arguments supplied to proc save_map, arguments were not turfs.")
@@ -35,17 +34,17 @@
if(fexists(map_path)) if(fexists(map_path))
fdel(map_path) fdel(map_path)
var/saved_map = file(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 saved_map << map_text
return saved_map return saved_map
/dmm_suite/write_map(var/turf/t1 as turf, var/turf/t2 as turf, var/flags as num) /datum/dmm_suite/proc/write_map(turf/t1, turf/t2, flags = 0)
//Check for valid turfs. // Check for valid turfs.
if(!isturf(t1) || !isturf(t2)) if(!isturf(t1) || !isturf(t2))
CRASH("Invalid arguments supplied to proc write_map, arguments were not turfs.") 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/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/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/templates[0]
var/list/template_buffer = list() var/list/template_buffer = list()
var/template_buffer_text var/template_buffer_text
@@ -54,11 +53,12 @@
var/total_timer = start_watch() var/total_timer = start_watch()
var/timer = start_watch() var/timer = start_watch()
log_debug("Reading turfs...") log_debug("Reading turfs...")
// Read the contents of all the turfs we were given // Read the contents of all the turfs we were given
for(var/pos_z in sw.z to ne.z) 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_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) 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/test_template = make_template(test_turf, flags)
var/template_number = templates.Find(test_template) var/template_number = templates.Find(test_template)
if(!template_number) if(!template_number)
@@ -66,25 +66,27 @@
template_number = templates.len template_number = templates.len
template_buffer += "[template_number]," template_buffer += "[template_number],"
CHECK_TICK CHECK_TICK
template_buffer += ";" template_buffer += ";"
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.") log_debug("Reading turfs took [stop_watch(timer)]s.")
if(templates.len == 0) if(templates.len == 0)
CRASH("No templates found!") CRASH("No templates found!")
var/key_length = round/*floor*/(log(letter_digits.len,templates.len-1)+1)
var/key_length = round/*floor*/(log(letter_digits.len, templates.len - 1) + 1)
var/list/keys[templates.len] var/list/keys[templates.len]
// Write the list of key/model pairs to the file // Write the list of key/model pairs to the file
timer = start_watch() timer = start_watch()
log_debug("Writing out key/model pairs to file header...") log_debug("Writing out key/model pairs to file header...")
var/list/key_models = list() var/list/key_models = list()
for(var/key_pos in 1 to templates.len) for(var/key_pos in 1 to templates.len)
keys[key_pos] = get_model_key(key_pos,key_length) keys[key_pos] = get_model_key(key_pos, key_length)
key_models += "\"[keys[key_pos]]\" = ([templates[key_pos]])\n" key_models += "\"[keys[key_pos]]\" = ([templates[key_pos]])\n"
CHECK_TICK CHECK_TICK
dmm_text += jointext(key_models,"") dmm_text += jointext(key_models,"")
log_debug("Writing key/model pairs complete, took [stop_watch(timer)]s.") log_debug("Writing key/model pairs complete, took [stop_watch(timer)]s.")
@@ -92,58 +94,69 @@
// Loop over all z in our zone // Loop over all z in our zone
timer = start_watch() timer = start_watch()
log_debug("Writing out key map...") log_debug("Writing out key map...")
var/list/key_map = list() var/list/key_map = list()
for(var/z_pos=1;TRUE;z_pos=findtext(template_buffer_text,".",z_pos)+1) 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_pos >= length(template_buffer_text))
if(z_level) key_map += "\n" break
key_map += "\n(1,1,[++z_level]) = {\"\n"
var/z_block = copytext(template_buffer_text,z_pos,findtext(template_buffer_text,".",z_pos)) if(z_level)
for(var/y_pos=1;TRUE;y_pos=findtext(z_block,";",y_pos)+1) key_map += "\n"
if(y_pos>=length(z_block)) break
var/y_block = copytext(z_block,y_pos,findtext(z_block,";",y_pos)) 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))
// A row of keys // A row of keys
for(var/x_pos=1;TRUE;x_pos=findtext(y_block,",",x_pos)+1) for(var/x_pos = 1; TRUE; x_pos = findtext(y_block, ",", x_pos) + 1)
if(x_pos>=length(y_block)) break if(x_pos >= length(y_block))
var/x_block = copytext(y_block,x_pos,findtext(y_block,",",x_pos)) break
var/x_block = copytext(y_block, x_pos, findtext(y_block, ",", x_pos))
var/key_number = text2num(x_block) var/key_number = text2num(x_block)
var/temp_key = keys[key_number] var/temp_key = keys[key_number]
key_map += temp_key key_map += temp_key
CHECK_TICK CHECK_TICK
key_map += "\n" key_map += "\n"
key_map += "\"}" 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("Writing key map complete, took [stop_watch(timer)]s.")
log_debug("TOTAL TIME: [stop_watch(total_timer)]s.") log_debug("TOTAL TIME: [stop_watch(total_timer)]s.")
return dmm_text return dmm_text
/dmm_suite/proc/make_template(var/turf/model as turf, var/flags as num) /datum/dmm_suite/proc/make_template(turf/model, flags = 0)
var/use_json = 0 var/use_json = flags & DMM_USE_JSON
if(flags & DMM_USE_JSON)
use_json = 1
var/template = "" var/template = ""
var/turf_template = "" var/turf_template = ""
var/list/obj_template = list() var/list/obj_template = list()
var/list/mob_template = list() var/list/mob_template = list()
var/area_template = "" var/area_template = ""
// Turf // Turf
if(!(flags & DMM_IGNORE_TURFS)) if(!(flags & DMM_IGNORE_TURFS))
turf_template = "[model.type][check_attributes(model,use_json=use_json)]," turf_template = "[model.type][check_attributes(model,use_json=use_json)],"
else turf_template = "[world.turf]," else
turf_template = "[world.turf],"
// Objects loop // Objects loop
if(!(flags & DMM_IGNORE_OBJS)) if(!(flags & DMM_IGNORE_OBJS))
for(var/obj/O in model.contents) for(var/obj/O in model.contents)
if(O.dont_save || QDELETED(O)) if(O.dont_save || QDELETED(O))
continue continue
obj_template += "[O.type][check_attributes(O,use_json=use_json)]," obj_template += "[O.type][check_attributes(O,use_json=use_json)],"
// Mobs Loop // Mobs Loop
for(var/mob/M in model.contents) for(var/mob/M in model.contents)
if(M.dont_save || QDELETED(M)) if(M.dont_save || QDELETED(M))
continue continue
if(M.client) if(M.client)
if(!(flags & DMM_IGNORE_PLAYERS)) if(!(flags & DMM_IGNORE_PLAYERS))
mob_template += "[M.type][check_attributes(M,use_json=use_json)]," mob_template += "[M.type][check_attributes(M,use_json=use_json)],"
@@ -155,65 +168,68 @@
if(!(flags & DMM_IGNORE_AREAS)) if(!(flags & DMM_IGNORE_AREAS))
var/area/m_area = model.loc var/area/m_area = model.loc
area_template = "[m_area.type][check_attributes(m_area,use_json=use_json)]" 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]" template = "[jointext(obj_template,"")][jointext(mob_template,"")][turf_template][area_template]"
return 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/attributes_text = "{"
var/list/attributes = list() var/list/attributes = list()
if(!use_json) if(!use_json)
for(var/V in A.vars) for(var/V in A.vars)
CHECK_TICK 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) attributes += var_to_dmm(A.vars[V], V)
else 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 // We'll want to write out vars that are important to the editor
// So that the map is legible as before // 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 // Save vars that are important for the map editor, so that
// json-encoded maps are legible for standard editors // json-encoded maps are legible for standard editors
if(A.vars[thing] != initial(A.vars[thing])) if(A.vars[T] != initial(A.vars[T]))
yeah -= thing to_encode -= T
attributes += var_to_dmm(A.vars[thing],thing) attributes += var_to_dmm(A.vars[T], T)
// Remove useless info // Remove useless info
yeah -= "type" to_encode -= "type"
if(yeah.len) if(to_encode.len)
var/json_stuff = json_encode(yeah) var/json_stuff = json_encode(to_encode)
attributes += var_to_dmm(json_stuff, "map_json_data") attributes += var_to_dmm(json_stuff, "map_json_data")
if(attributes.len == 0) if(attributes.len == 0)
return return
// Trim a trailing semicolon - `var_to_dmm` always appends a semicolon, // Trim a trailing semicolon - `var_to_dmm` always appends a semicolon,
// so the last one will be trailing. // so the last one will be trailing.
if(copytext(attributes_text, length(attributes_text)-1, 0) == "; ") if(copytext(attributes_text, length(attributes_text) - 1, 0) == "; ")
attributes_text = copytext(attributes_text, 1, length(attributes_text)-1) attributes_text = copytext(attributes_text, 1, length(attributes_text) - 1)
attributes_text = "{[jointext(attributes,"; ")]}" attributes_text = "{[jointext(attributes,"; ")]}"
return attributes_text return attributes_text
/datum/dmm_suite/proc/get_model_key(which = 0, key_length = 0)
/dmm_suite/proc/get_model_key(var/which as num, var/key_length as num)
var/list/key = list() 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) for(var/digit_pos in key_length to 1 step -1)
var/place_value = round/*floor*/(working_digit/(letter_digits.len**(digit_pos-1))) var/place_value = round/*floor*/(working_digit / (letter_digits.len ** (digit_pos - 1)))
working_digit-=place_value*(letter_digits.len**(digit_pos-1)) working_digit -= place_value * (letter_digits.len ** (digit_pos - 1))
key += letter_digits[place_value+1] key += letter_digits[place_value + 1]
return jointext(key,"") return jointext(key,"")
/datum/dmm_suite/proc/var_to_dmm(attr, name)
/dmm_suite/proc/var_to_dmm(attr, name)
if(istext(attr)) if(istext(attr))
// dmm_encode will strip out characters that would be capable of disrupting // dmm_encode will strip out characters that would be capable of disrupting
// parsing - namely, quotes and curly braces // parsing - namely, quotes and curly braces
return "[name] = \"[dmm_encode(attr)]\"" return "[name] = \"[dmm_encode(attr)]\""
else if(isnum(attr)||ispath(attr)) else if(isnum(attr) || ispath(attr))
return "[name] = [attr]" return "[name] = [attr]"
else if(isicon(attr)||isfile(attr)) else if(isicon(attr) || isfile(attr))
if(length("[attr]") == 0) if(length("[attr]") == 0)
// The DM map reader is unable to read files that have a '' file/icon entry // The DM map reader is unable to read files that have a '' file/icon entry
return return