mirror of
https://github.com/Aurorastation/Aurora.3.git
synced 2026-01-14 03:12:30 +00:00
Runtime map now has a bunch of new areas / items with often-tested stuffs, and some hard-to-put-at-runtime stuffs. Runtime map jobs now are positioned to make it faster to reach the aforementioned often-tested stuffs. Runtime map doesn't generate an overmap anymore by default, which speeds up the process. Runtime map now loads in ~11 seconds instead of ~40 seconds as it was before. Updated the maploader to be faster in parsing maps. Bapi is not engaged anymore if we're only measuring the map size, which speeds up the process. In fastboot we do not generate the codexes anymore, which speeds up the process. In fastboot and if exoplanets and away sites are not enabled, we do not parse the map templates anymore, which speeds up the process. Updated the icon smoothing to be faster. Optimized cargo area code. Other optimizations.
542 lines
19 KiB
Plaintext
542 lines
19 KiB
Plaintext
///////////////////////////////////////////////////////////////
|
|
//SS13 Optimized Map loader
|
|
//////////////////////////////////////////////////////////////
|
|
|
|
//global datum that will preload variables on atoms instanciation
|
|
GLOBAL_VAR_INIT(use_preloader, FALSE)
|
|
GLOBAL_DATUM_INIT(_preloader, /dmm_suite/preloader, new)
|
|
|
|
/datum/map_load_metadata
|
|
var/bounds
|
|
var/list/atoms_to_initialise
|
|
|
|
/dmm_suite
|
|
// /"([a-zA-Z]+)" = \(((?:.|\n)*?)\)\n(?!\t)|\((\d+),(\d+),(\d+)\) = \{"([a-zA-Z\n]*)"\}/g
|
|
var/static/regex/dmmRegex = new(@'"([a-zA-Z]+)" = (?:\(\n|\()((?:.|\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/space_key
|
|
|
|
//text trimming (both directions) helper macro
|
|
#define TRIM_TEXT(text) (replacetext_char(text, trimRegex, ""))
|
|
|
|
/**
|
|
* 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)
|
|
*
|
|
*/
|
|
// dmm_files: A list of .dmm files 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
|
|
/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, no_changeturf as num, lower_crop_x as num, lower_crop_y as num, upper_crop_x as num, upper_crop_y as num)
|
|
//How I wish for RAII
|
|
Master.StartLoadingMap()
|
|
space_key = null
|
|
. = 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)
|
|
Master.StopLoadingMap()
|
|
|
|
/dmm_suite/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)
|
|
var/tfile = dmm_file//the map file we're creating
|
|
|
|
/*#### WARNING AURORA SNOWFLAKE SECTION ####*/
|
|
|
|
if(isfile(tfile))
|
|
// name/path of dmm file, new var so as to not rename the `tfile` var
|
|
// to maybe maintain compatibility with other codebases
|
|
var/tfilepath = "[tfile]"
|
|
tfile = null
|
|
// use bapi to read, parse, process, mapmanip etc
|
|
// this will "crash"/stacktrace on fail
|
|
// Except when measuring; i don't give a shit about bapi when measuring the map size,
|
|
// if you're changing the map size from bapi you're stupid and deserve it not to work,
|
|
// and i'm not slowing down the whole initialization by a third just for this possibility
|
|
if(!measureOnly)
|
|
tfile = bapi_read_dmm_file(tfilepath)
|
|
// if bapi for whatever reason fails and returns null, or we're measuring
|
|
// try to load it the old dm way instead
|
|
if(!tfile)
|
|
tfile = file2text(tfilepath)
|
|
|
|
/*#### END AURORA SNOWFLAKE SECTION ####*/
|
|
|
|
if(!x_offset)
|
|
x_offset = 1
|
|
if(!y_offset)
|
|
y_offset = 1
|
|
if(!z_offset)
|
|
z_offset = world.maxz + 1
|
|
|
|
var/list/bounds = list(1.#INF, 1.#INF, 1.#INF, -1.#INF, -1.#INF, -1.#INF)
|
|
var/list/grid_models = list()
|
|
var/key_len = 0
|
|
|
|
var/stored_index = 1
|
|
|
|
var/list/atoms_to_initialise = list()
|
|
var/has_expanded_world_maxx = FALSE
|
|
var/has_expanded_world_maxy = FALSE
|
|
|
|
var/list/regexOutput
|
|
while(findtext(tfile, dmmRegex, stored_index))
|
|
stored_index = dmmRegex.next
|
|
// Datum var lookup is expensive, this isn't
|
|
regexOutput = dmmRegex.group
|
|
|
|
// "aa" = (/type{vars=blah})
|
|
if(regexOutput[1]) // Model
|
|
var/key = regexOutput[1]
|
|
if(grid_models[key]) // Duplicate model keys are ignored in DMMs
|
|
continue
|
|
if(key_len != length(key))
|
|
if(!key_len)
|
|
key_len = length(key)
|
|
else
|
|
CRASH("Inconsistent key length in DMM")
|
|
if(!measureOnly)
|
|
grid_models[key] = regexOutput[2]
|
|
|
|
// (1,1,1) = {"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"}
|
|
else if(regexOutput[3]) // Coords
|
|
if(!key_len)
|
|
CRASH("Coords before model definition in DMM")
|
|
|
|
var/curr_x = text2num(regexOutput[3])
|
|
|
|
if(curr_x < x_lower || curr_x > x_upper)
|
|
continue
|
|
|
|
var/xcrdStart = curr_x + x_offset - 1
|
|
//position of the currently processed square
|
|
var/xcrd
|
|
var/ycrd = text2num(regexOutput[4]) + y_offset - 1
|
|
var/zcrd = text2num(regexOutput[5]) + z_offset - 1
|
|
|
|
var/zexpansion = zcrd > world.maxz
|
|
if(zexpansion && !measureOnly) // don't actually expand the world if we're only measuring bounds
|
|
if(cropMap)
|
|
continue
|
|
else
|
|
world.maxz = zcrd //create a new z_level if needed
|
|
SEND_GLOBAL_SIGNAL(COMSIG_GLOB_NEW_Z, world.maxz)
|
|
if(!no_changeturf)
|
|
WARNING("Z-level expansion occurred without no_changeturf set, this may cause problems when /turf/post_change is called.")
|
|
|
|
bounds[MAP_MINX] = min(bounds[MAP_MINX], clamp(xcrdStart, x_lower, x_upper))
|
|
bounds[MAP_MINZ] = min(bounds[MAP_MINZ], zcrd)
|
|
bounds[MAP_MAXZ] = max(bounds[MAP_MAXZ], zcrd)
|
|
|
|
var/list/gridLines = splittext(regexOutput[6], "\n")
|
|
|
|
var/leadingBlanks = 0
|
|
while(leadingBlanks < length(gridLines) && gridLines[++leadingBlanks] == "")
|
|
if(leadingBlanks > 1)
|
|
gridLines.Cut(1, leadingBlanks) // Remove all leading blank lines.
|
|
|
|
if(!length(gridLines)) // Skip it if only blank lines exist.
|
|
continue
|
|
|
|
if(gridLines[length(gridLines)] == "")
|
|
gridLines.Cut(length(gridLines)) // Remove only one blank line at the end.
|
|
|
|
bounds[MAP_MINY] = min(bounds[MAP_MINY], clamp(ycrd, y_lower, y_upper))
|
|
ycrd += length(gridLines) - 1 // Start at the top and work down
|
|
|
|
if(!cropMap && ycrd > world.maxy)
|
|
if(!measureOnly)
|
|
world.maxy = ycrd // Expand Y here. X is expanded in the loop below
|
|
has_expanded_world_maxy = TRUE
|
|
bounds[MAP_MAXY] = max(bounds[MAP_MAXY], clamp(ycrd, y_lower, y_upper))
|
|
else
|
|
bounds[MAP_MAXY] = max(bounds[MAP_MAXY], clamp(min(ycrd, world.maxy), y_lower, y_upper))
|
|
|
|
var/maxx = xcrdStart
|
|
if(measureOnly)
|
|
for(var/line in gridLines)
|
|
maxx = max(maxx, xcrdStart + length(line) / key_len - 1)
|
|
else
|
|
//turn off base new Initialization until the whole thing is loaded
|
|
SSatoms.map_loader_begin(REF(src))
|
|
|
|
for(var/line in 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)
|
|
xcrd = xcrdStart
|
|
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
|
|
has_expanded_world_maxx = TRUE
|
|
|
|
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))
|
|
if(!grid_models[model_key])
|
|
CRASH("Undefined model key in DMM.")
|
|
var/datum/grid_load_metadata/M = parse_grid(grid_models[model_key], model_key, xcrd, ycrd, zcrd, no_changeturf || zexpansion)
|
|
if (M)
|
|
atoms_to_initialise += M.atoms_to_initialise
|
|
|
|
// CHECK_TICK
|
|
maxx = max(maxx, xcrd)
|
|
++xcrd
|
|
--ycrd
|
|
|
|
//Restore initialization to the previous value
|
|
SSatoms.map_loader_stop(REF(src))
|
|
|
|
bounds[MAP_MAXX] = clamp(max(bounds[MAP_MAXX], cropMap ? min(maxx, world.maxx) : maxx), x_lower, x_upper)
|
|
|
|
CHECK_TICK
|
|
|
|
if(bounds[1] == 1.#INF) // Shouldn't need to check every item
|
|
return null
|
|
else
|
|
if(!measureOnly)
|
|
if(!no_changeturf)
|
|
for(var/turf/T as anything in block(locate(bounds[MAP_MINX], bounds[MAP_MINY], bounds[MAP_MINZ]), locate(bounds[MAP_MAXX], bounds[MAP_MAXY], bounds[MAP_MAXZ])))
|
|
//we do this after we load everything in. if we don't; we'll have weird atmos bugs regarding atmos adjacent turfs
|
|
T.post_change(FALSE)
|
|
|
|
if(has_expanded_world_maxx || has_expanded_world_maxy)
|
|
SEND_GLOBAL_SIGNAL(COMSIG_GLOB_EXPANDED_WORLD_BOUNDS, has_expanded_world_maxx, has_expanded_world_maxy)
|
|
|
|
var/datum/map_load_metadata/M = new
|
|
M.bounds = bounds
|
|
M.atoms_to_initialise = atoms_to_initialise
|
|
return M
|
|
|
|
/**
|
|
* Fill a given tile with its area/turf/objects/mobs
|
|
* Variable model is one full map line (e.g /turf/unsimulated/wall{icon_state = "rock"}, /area/mine/explored)
|
|
*
|
|
* WORKING :
|
|
*
|
|
* 1) Read the model string, member by member (delimiter is ',')
|
|
*
|
|
* 2) Get the path of the atom and store it into a list
|
|
*
|
|
* 3) a) Check if the member has variables (text within '{' and '}')
|
|
*
|
|
* 3) b) Construct an associative list with found variables, if any (the atom index in members is the same as its variables in members_attributes)
|
|
*
|
|
* 4) Instanciates the atom with its variables
|
|
*
|
|
*/
|
|
|
|
/datum/grid_load_metadata
|
|
var/list/atoms_to_initialise
|
|
var/list/atoms_to_delete
|
|
|
|
/dmm_suite/proc/parse_grid(model as text, model_key as text, xcrd as num,ycrd as num,zcrd as num, no_changeturf as num)
|
|
//This should only ever be called by load_map_impl() after announcing the map is being loaded to SSatoms
|
|
PRIVATE_PROC(TRUE)
|
|
|
|
/*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/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
|
|
|
|
if(cached)
|
|
members = cached[1]
|
|
members_attributes = cached[2]
|
|
else
|
|
/////////////////////////////////////////////////////////
|
|
//Constructing members and corresponding variables lists
|
|
////////////////////////////////////////////////////////
|
|
|
|
members = list()
|
|
members_attributes = list()
|
|
index = 1
|
|
|
|
var/old_position = 1
|
|
var/dpos
|
|
|
|
do
|
|
//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
|
|
old_position = dpos + 1
|
|
|
|
if(!atom_def) // Skip the item if the path does not exist. Fix your crap, mappers!
|
|
#ifdef UNIT_TEST
|
|
log_error("Couldn't find atom path specified in map: [full_def]")
|
|
#endif
|
|
if (dpos == 0)
|
|
break
|
|
else
|
|
continue
|
|
|
|
members += atom_def
|
|
|
|
//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
|
|
|
|
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, ";")
|
|
if(length(fields))
|
|
if(!trimtext(fields[length(fields)]))
|
|
--fields.len
|
|
for(var/I in fields)
|
|
var/value = fields[I]
|
|
if(istext(value))
|
|
fields[I] = apply_text_macros(value)
|
|
|
|
//then fill the members_attributes list with the corresponding variables
|
|
members_attributes.len++
|
|
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
|
|
// 1. no_changeturf is set
|
|
// 2. the space_key isn't set yet
|
|
// 3. there are exactly 2 members
|
|
// 4. with no attributes
|
|
// 5. and the members are world.turf and world.area
|
|
// Basically, if we find an entry like this: "XXX" = (/turf/default, /area/default)
|
|
// We can skip calling this proc every time we see XXX
|
|
if(no_changeturf && !space_key && length(members) == 2 && length(members_attributes) == 2 && length(members_attributes[1]) == 0 && length(members_attributes[2]) == 0 && (world.area in members) && (world.turf in members))
|
|
space_key = model_key
|
|
return
|
|
|
|
modelCache[model] = list(members, members_attributes)
|
|
|
|
////////////////
|
|
//Instanciation
|
|
////////////////
|
|
|
|
//since we've switched off autoinitialisation, record atoms to initialise later
|
|
var/list/atoms_to_initialise = list()
|
|
|
|
//The next part of the code assumes there's ALWAYS an /area AND a /turf on a given tile
|
|
var/turf/crds = locate(xcrd,ycrd,zcrd)
|
|
|
|
//first instance the /area and remove it from the members list
|
|
index = length(members)
|
|
if(members[index] != /area/template_noop)
|
|
var/atype = members[index]
|
|
var/atom/instance = GLOB.areas_by_type[atype]
|
|
var/list/attr = members_attributes[index]
|
|
if (LAZYLEN(attr))
|
|
GLOB._preloader.setup(attr)//preloader for assigning set variables on atom creation
|
|
if(!instance)
|
|
instance = new atype(null)
|
|
atoms_to_initialise += instance
|
|
if(crds)
|
|
instance.contents += crds
|
|
|
|
if(GLOB.use_preloader && instance)
|
|
GLOB._preloader.load(instance)
|
|
|
|
//then instance the /turf
|
|
|
|
var/first_turf_index = 1
|
|
while(!ispath(members[first_turf_index], /turf)) //find first /turf object in members
|
|
first_turf_index++
|
|
|
|
//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],crds,no_changeturf)
|
|
atoms_to_initialise += T
|
|
|
|
if(T)
|
|
//if others /turf are presents, simulates the underlays piling effect
|
|
index = first_turf_index + 1
|
|
while(index <= length(members) - 1) // Last item is an /area
|
|
T = instance_atom(members[index],members_attributes[index],crds,no_changeturf)//instance new turf
|
|
index++
|
|
atoms_to_initialise += T
|
|
|
|
//finally instance all remainings objects/mobs
|
|
for(index in 1 to first_turf_index-1)
|
|
atoms_to_initialise += instance_atom(members[index],members_attributes[index],crds,no_changeturf)
|
|
|
|
var/datum/grid_load_metadata/M = new
|
|
M.atoms_to_initialise = atoms_to_initialise
|
|
return M
|
|
|
|
////////////////
|
|
//Helpers procs
|
|
////////////////
|
|
|
|
//Instance an atom at (x,y,z) and gives it the variables in attributes
|
|
/dmm_suite/proc/instance_atom(path,list/attributes, turf/crds, no_changeturf)
|
|
if (LAZYLEN(attributes))
|
|
GLOB._preloader.setup(attributes, path)
|
|
|
|
if(crds)
|
|
if(!no_changeturf && ispath(path, /turf))
|
|
. = crds.ChangeTurf(path, FALSE, TRUE, TRUE)
|
|
else
|
|
. = create_atom(path, crds)//first preloader pass
|
|
|
|
if(GLOB.use_preloader && .)//second preloader pass, for those atoms that don't ..() in New()
|
|
GLOB._preloader.load(.)
|
|
|
|
//custom CHECK_TICK here because we don't want things created while we're sleeping to not initialize
|
|
if(TICK_CHECK)
|
|
SSatoms.map_loader_stop(REF(src))
|
|
stoplag()
|
|
SSatoms.map_loader_begin(REF(src))
|
|
|
|
/dmm_suite/proc/create_atom(path, crds)
|
|
set waitfor = FALSE
|
|
. = new path (crds)
|
|
|
|
//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="\"",closing_escape="\"")
|
|
var/position = initial_position
|
|
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)
|
|
|
|
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=",")
|
|
. = list()
|
|
if (!text)
|
|
return
|
|
|
|
var/position
|
|
var/old_position = 1
|
|
while(position != 0)
|
|
// 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)
|
|
var/trim_left = TRIM_TEXT(copytext(text,old_position,(equal_position ? equal_position : position)))
|
|
var/left_constant = parse_constant(trim_left)
|
|
if(position)
|
|
old_position = position + length(text[position])
|
|
if(!left_constant) // damn newlines man. Exists to provide behavior consistency with the above loop. not a major cost becuase this path is cold
|
|
continue
|
|
|
|
if(equal_position && !isnum(left_constant))
|
|
// Associative var, so do the association.
|
|
// Note that numbers cannot be keys - the RHS is dropped if so.
|
|
var/trim_right = TRIM_TEXT(copytext(text, equal_position + length(text[equal_position]), position))
|
|
var/right_constant = parse_constant(trim_right)
|
|
.[left_constant] = right_constant
|
|
else // simple var
|
|
. += list(left_constant)
|
|
|
|
/dmm_suite/proc/parse_constant(text)
|
|
// number
|
|
var/num = text2num(text)
|
|
if(isnum(num))
|
|
return num
|
|
|
|
// string
|
|
if(text[1] == "\"")
|
|
// insert implied locate \" and length("\"") here
|
|
// It's a minimal timesave but it is a timesave
|
|
// Safe becuase we're guarenteed trimmed constants
|
|
return copytext(text, 2, -1)
|
|
|
|
// list
|
|
if(copytext(text, 1, 6) == "list(")//6 == length("list(") + 1
|
|
return readlist(copytext(text, 6, -1))
|
|
|
|
// typepath
|
|
var/path = text2path(text)
|
|
if(ispath(path))
|
|
return path
|
|
|
|
// file
|
|
if(text[1] == "'")
|
|
return file(copytext_char(text, 2, -1))
|
|
|
|
// null
|
|
if(text == "null")
|
|
return null
|
|
|
|
// not parsed:
|
|
// - pops: /obj{name="foo"}
|
|
// - new(), newlist(), icon(), matrix(), sound()
|
|
|
|
// fallback: string
|
|
return text
|
|
|
|
/dmm_suite/Destroy()
|
|
..()
|
|
return QDEL_HINT_HARDDEL_NOW
|
|
|
|
//////////////////
|
|
//Preloader datum
|
|
//////////////////
|
|
|
|
GLOBAL_LIST_INIT(_preloader_path, null)
|
|
|
|
/dmm_suite/preloader
|
|
parent_type = /datum
|
|
var/list/attributes
|
|
|
|
/dmm_suite/preloader/proc/setup(list/the_attributes, path)
|
|
if(LAZYLEN(the_attributes))
|
|
GLOB.use_preloader = TRUE
|
|
attributes = the_attributes
|
|
GLOB._preloader_path = path
|
|
|
|
/dmm_suite/preloader/proc/load(atom/what)
|
|
GLOB.use_preloader = FALSE
|
|
var/list/attributes = src.attributes
|
|
for(var/attribute in attributes)
|
|
var/value = attributes[attribute]
|
|
if(islist(value))
|
|
value = deep_copy_list(value)
|
|
#ifdef TESTING
|
|
if(what.vars[attribute] == value)
|
|
var/message = "<font color=green>[what.type]</font> at [AREACOORD(what)] - <b>VAR:</b> <font color=red>[attribute] = [isnull(value) ? "null" : (isnum(value) ? value : "\"[value]\"")]</font>"
|
|
log_mapping("DIRTY VAR: [message]")
|
|
GLOB.dirty_vars += message
|
|
#endif
|
|
what.vars[attribute] = value
|
|
|
|
/area/template_noop
|
|
name = "Area Passthrough"
|
|
icon_state = "noop"
|
|
|
|
/turf/template_noop
|
|
name = "Turf Passthrough"
|
|
icon_state = "noop"
|
|
|
|
#undef TRIM_TEXT
|