diff --git a/code/__DEFINES/maps.dm b/code/__DEFINES/maps.dm
index cdd2739b3cf..b7043d01ff0 100644
--- a/code/__DEFINES/maps.dm
+++ b/code/__DEFINES/maps.dm
@@ -21,7 +21,7 @@ Last space-z level = empty
#define CITY_OF_COGS "City of Cogs"
#define EMPTY_AREA_1 "Empty Area 1"
#define EMPTY_AREA_2 "Empty Area 2"
-#define MINING "Mining Asteroid"
+#define MINING_ASTEROID "Mining Asteroid"
#define EMPTY_AREA_3 "Empty Area 3"
#define EMPTY_AREA_4 "Empty Area 4"
#define EMPTY_AREA_5 "Empty Area 5"
@@ -36,11 +36,8 @@ Last space-z level = empty
#define MAP_REMOVE_JOB(jobpath) /datum/job/##jobpath/map_check() { return (SSmapping.config.map_name != JOB_MODIFICATION_MAP_NAME) && ..() }
//zlevel defines, can be overridden for different maps in the appropriate _maps file.
-#define ZLEVEL_CENTCOM 1
#define ZLEVEL_STATION_PRIMARY 2
-#define ZLEVEL_MINING 5
#define ZLEVEL_LAVALAND 5
-#define ZLEVEL_CITYOFCOGS 6
#define ZLEVEL_EMPTY_SPACE 12
//Unless you modify it in map config should be equal to ZLEVEL_SPACEMAX
#define ZLEVEL_TRANSIT 13
@@ -49,3 +46,31 @@ Last space-z level = empty
#define ZLEVEL_SPACEMAX 13
#define SPACERUIN_MAP_EDGE_PAD 15
+#define ZLEVEL_SPACE_RUIN_COUNT 5
+
+// traits
+#define ZTRAIT_CENTCOM "CentCom"
+#define ZTRAIT_STATION "Station"
+#define ZTRAIT_MINING "Mining"
+#define ZTRAIT_REEBE "Reebe"
+#define ZTRAIT_TRANSIT "Transit"
+#define ZTRAIT_AWAY "Away Mission"
+#define ZTRAIT_SPACE_RUINS "Space Ruins"
+#define ZTRAIT_LAVA_RUINS "Lava Ruins"
+#define ZTRAIT_BOMBCAP_MULTIPLIER "Bombcap Multiplier"
+
+// trait definitions
+#define DL_NAME "name"
+#define DL_LINKAGE "linkage"
+#define DL_TRAITS "traits"
+
+#define DECLARE_LEVEL(NAME, LINKAGE, TRAITS) list(DL_NAME = NAME, DL_LINKAGE = LINKAGE, DL_TRAITS = TRAITS)
+// corresponds to basemap.dm
+#define DEFAULT_MAP_TRAITS list(\
+ DECLARE_LEVEL("CentCom", SELFLOOPING, list(ZTRAIT_CENTCOM = TRUE)),\
+ DECLARE_LEVEL("Main Station", CROSSLINKED, list(ZTRAIT_STATION = TRUE)),\
+ DECLARE_LEVEL("Empty Area 1", CROSSLINKED, list(ZTRAIT_SPACE_RUINS = TRUE)),\
+ DECLARE_LEVEL("Empty Area 2", CROSSLINKED, list(ZTRAIT_SPACE_RUINS = TRUE)),\
+ DECLARE_LEVEL("Lavaland", UNAFFECTED, list(ZTRAIT_MINING = TRUE, ZTRAIT_LAVA_RUINS = TRUE, ZTRAIT_BOMBCAP_MULTIPLIER = 3)),\
+ DECLARE_LEVEL("Reebe", UNAFFECTED, list(ZTRAIT_REEBE = TRUE, ZTRAIT_BOMBCAP_MULTIPLIER = 0.5)),\
+)
diff --git a/code/__HELPERS/level_traits.dm b/code/__HELPERS/level_traits.dm
index e2a74cf8e01..4d20c9b513b 100644
--- a/code/__HELPERS/level_traits.dm
+++ b/code/__HELPERS/level_traits.dm
@@ -1,17 +1,17 @@
// Helpers for checking whether a z-level conforms to a specific requirement
// Basic levels
-#define is_centcom_level(z) ((z) == ZLEVEL_CENTCOM)
+#define is_centcom_level(z) SSmapping.level_trait(z, ZTRAIT_CENTCOM)
-#define is_station_level(z) ((z) in GLOB.station_z_levels)
+#define is_station_level(z) SSmapping.level_trait(z, ZTRAIT_STATION)
-#define is_mining_level(z) ((z) == ZLEVEL_MINING)
+#define is_mining_level(z) SSmapping.level_trait(z, ZTRAIT_MINING)
-#define is_reebe(z) ((z) == ZLEVEL_CITYOFCOGS)
+#define is_reebe(z) SSmapping.level_trait(z, ZTRAIT_REEBE)
-#define is_transit_level(z) ((z) == ZLEVEL_TRANSIT)
+#define is_transit_level(z) SSmapping.level_trait(z, ZTRAIT_TRANSIT)
-#define is_away_level(z) ((z) > ZLEVEL_SPACEMAX)
+#define is_away_level(z) SSmapping.level_trait(z, ZTRAIT_AWAY)
// If true, the singularity cannot strip away asteroid turf on this Z
#define is_planet_level(z) (GLOB.z_is_planet["z"])
diff --git a/code/_globalvars/lists/mapping.dm b/code/_globalvars/lists/mapping.dm
index 51af2b3525a..0d8965d10a7 100644
--- a/code/_globalvars/lists/mapping.dm
+++ b/code/_globalvars/lists/mapping.dm
@@ -1,14 +1,7 @@
-#define Z_NORTH 1
-#define Z_EAST 2
-#define Z_SOUTH 3
-#define Z_WEST 4
-
GLOBAL_LIST_INIT(cardinals, list(NORTH, SOUTH, EAST, WEST))
GLOBAL_LIST_INIT(alldirs, list(NORTH, SOUTH, EAST, WEST, NORTHEAST, NORTHWEST, SOUTHEAST, SOUTHWEST))
GLOBAL_LIST_INIT(diagonals, list(NORTHEAST, NORTHWEST, SOUTHEAST, SOUTHWEST))
-GLOBAL_LIST_INIT(station_z_levels, list(ZLEVEL_STATION_PRIMARY))
-
GLOBAL_LIST(global_map)
//list/global_map = list(list(1,5),list(4,3))//an array of map Z levels.
//Resulting sector map looks like
diff --git a/code/controllers/subsystem/mapping.dm b/code/controllers/subsystem/mapping.dm
index d13d340b036..8046dab41e3 100644
--- a/code/controllers/subsystem/mapping.dm
+++ b/code/controllers/subsystem/mapping.dm
@@ -22,6 +22,9 @@ SUBSYSTEM_DEF(mapping)
var/loading_ruins = FALSE
+ // Z-manager stuff
+ var/list/z_list
+
/datum/controller/subsystem/mapping/PreInit()
if(!config)
#ifdef FORCE_MAP
@@ -39,28 +42,34 @@ SUBSYSTEM_DEF(mapping)
repopulate_sorted_areas()
process_teleport_locs() //Sets up the wizard teleport locations
preloadTemplates()
+
+ // Create space levels
+ for(var/I in 1 to ZLEVEL_SPACE_RUIN_COUNT)
+ add_new_zlevel("Empty Area [2 + I]", CROSSLINKED, list(ZTRAIT_SPACE_RUINS = TRUE))
+ add_new_zlevel("Empty Area [3 + ZLEVEL_SPACE_RUIN_COUNT]", CROSSLINKED, list()) // no ruins
+ add_new_zlevel("Transit", UNAFFECTED, list(ZTRAIT_TRANSIT = TRUE))
+
// Pick a random away mission.
createRandomZlevel()
- // Generate mining.
+ if (z_list.len < world.maxz)
+ add_new_zlevel("Away Mission", UNAFFECTED, list(ZTRAIT_AWAY = TRUE))
+
+ // Generate mining ruins
loading_ruins = TRUE
- var/mining_type = config.minetype
- if (mining_type == "lavaland")
- seedRuins(list(ZLEVEL_LAVALAND), CONFIG_GET(number/lavaland_budget), /area/lavaland/surface/outdoors/unexplored, lava_ruins_templates)
- spawn_rivers()
+ var/list/lava_ruins = levels_by_trait(ZTRAIT_LAVA_RUINS)
+ if (lava_ruins.len)
+ seedRuins(lava_ruins, CONFIG_GET(number/lavaland_budget), /area/lavaland/surface/outdoors/unexplored, lava_ruins_templates)
+ for (var/lava_z in lava_ruins)
+ spawn_rivers(lava_z)
- // deep space ruins
- var/space_zlevels = list()
- for(var/i in ZLEVEL_SPACEMIN to ZLEVEL_SPACEMAX)
- switch(i)
- if(ZLEVEL_MINING, ZLEVEL_LAVALAND, ZLEVEL_EMPTY_SPACE, ZLEVEL_TRANSIT, ZLEVEL_CITYOFCOGS)
- continue
- else
- space_zlevels += i
-
- seedRuins(space_zlevels, CONFIG_GET(number/space_budget), /area/space, space_ruins_templates)
+ // Generate deep space ruins
+ var/list/space_ruins = levels_by_trait(ZTRAIT_SPACE_RUINS)
+ if (space_ruins.len)
+ seedRuins(space_ruins, CONFIG_GET(number/space_budget), /area/space, space_ruins_templates)
loading_ruins = FALSE
+
repopulate_sorted_areas()
- // Set up Z-level transistions.
+ // Set up Z-level transitions.
setup_map_transitions()
generate_station_area_list()
..()
@@ -98,6 +107,8 @@ SUBSYSTEM_DEF(mapping)
config = SSmapping.config
next_map_config = SSmapping.next_map_config
+ z_list = SSmapping.z_list
+
/datum/controller/subsystem/mapping/proc/TryLoadZ(filename, errorList, forceLevel, last)
var/static/dmm_suite/loader
if(!loader)
@@ -107,11 +118,6 @@ SUBSYSTEM_DEF(mapping)
if(last)
QDEL_NULL(loader)
-/datum/controller/subsystem/mapping/proc/CreateSpace(MaxZLevel)
- while(world.maxz < MaxZLevel)
- ++world.maxz
- CHECK_TICK
-
#define INIT_ANNOUNCE(X) to_chat(world, "[X]"); log_world(X)
/datum/controller/subsystem/mapping/proc/loadWorld()
//if any of these fail, something has gone horribly, HORRIBLY, wrong
@@ -121,6 +127,7 @@ SUBSYSTEM_DEF(mapping)
INIT_ANNOUNCE("Loading [config.map_name]...")
TryLoadZ(config.GetFullMapPath(), FailedZs, ZLEVEL_STATION_PRIMARY)
+ InitializeDefaultZLevels()
INIT_ANNOUNCE("Loaded station in [(REALTIMEOFDAY - start_time)/10]s!")
if(SSdbcore.Connect())
var/datum/DBQuery/query_round_map_name = SSdbcore.NewQuery("UPDATE [format_table_name("round")] SET map_name = '[config.map_name]' WHERE id = [GLOB.round_id]")
@@ -129,8 +136,6 @@ SUBSYSTEM_DEF(mapping)
if(config.minetype != "lavaland")
INIT_ANNOUNCE("WARNING: A map without lavaland set as its minetype was loaded! This is being ignored! Update the maploader code!")
- CreateSpace(ZLEVEL_SPACEMAX)
-
if(LAZYLEN(FailedZs)) //but seriously, unless the server's filesystem is messed up this will never happen
var/msg = "RED ALERT! The following map files failed to load: [FailedZs[1]]"
if(FailedZs.len > 1)
diff --git a/code/controllers/subsystem/minimap.dm b/code/controllers/subsystem/minimap.dm
index 4bead36bb12..fa9821b85b1 100644
--- a/code/controllers/subsystem/minimap.dm
+++ b/code/controllers/subsystem/minimap.dm
@@ -5,9 +5,10 @@ SUBSYSTEM_DEF(minimap)
var/const/MINIMAP_SIZE = 2048
var/const/TILE_SIZE = 8
- var/list/z_levels = list(ZLEVEL_STATION_PRIMARY)
+ var/list/z_levels
/datum/controller/subsystem/minimap/Initialize(timeofday)
+ z_levels = SSmapping.levels_by_trait(ZTRAIT_STATION)
var/hash = md5(SSmapping.config.GetFullMapPath())
if(CONFIG_GET(flag/generate_minimaps))
if(hash == trim(file2text(hash_path())))
diff --git a/code/controllers/subsystem/squeak.dm b/code/controllers/subsystem/squeak.dm
index 022b659d8a2..861bb0d8d5a 100644
--- a/code/controllers/subsystem/squeak.dm
+++ b/code/controllers/subsystem/squeak.dm
@@ -34,7 +34,7 @@ SUBSYSTEM_DEF(squeak)
/datum/controller/subsystem/squeak/proc/find_exposed_wires()
exposed_wires.Cut()
var/list/all_turfs
- for (var/z in GLOB.station_z_levels)
+ for (var/z in SSmapping.levels_by_trait(ZTRAIT_STATION))
all_turfs += block(locate(1,1,z), locate(world.maxx,world.maxy,z))
for(var/turf/open/floor/plating/T in all_turfs)
if(is_blocked_turf(T))
diff --git a/code/datums/explosion.dm b/code/datums/explosion.dm
index 9b5cd18774f..0616d0e4c67 100644
--- a/code/datums/explosion.dm
+++ b/code/datums/explosion.dm
@@ -1,6 +1,4 @@
#define EXPLOSION_THROW_SPEED 4
-#define CITYOFCOGS_CAP_MULTIPLIER 0.5
-#define MINING_CAP_MULTIPLIER 3
GLOBAL_LIST_EMPTY(explosions)
//Against my better judgement, I will return the explosion datum
@@ -54,17 +52,14 @@ GLOBAL_LIST_EMPTY(explosions)
var/orig_dev_range = devastation_range
var/orig_heavy_range = heavy_impact_range
var/orig_light_range = light_impact_range
-
+
var/orig_max_distance = max(devastation_range, heavy_impact_range, light_impact_range, flash_range, flame_range)
-
+
//Zlevel specific bomb cap multiplier
- var/cap_multiplier = 1
- switch(epicenter.z)
- if(ZLEVEL_CITYOFCOGS)
- cap_multiplier = CITYOFCOGS_CAP_MULTIPLIER
- if(ZLEVEL_MINING)
- cap_multiplier = MINING_CAP_MULTIPLIER
-
+ var/cap_multiplier = SSmapping.level_trait(epicenter.z, ZTRAIT_BOMBCAP_MULTIPLIER)
+ if (isnull(cap_multiplier))
+ cap_multiplier = 1
+
if(!ignorecap)
devastation_range = min(GLOB.MAX_EX_DEVESTATION_RANGE * cap_multiplier, devastation_range)
heavy_impact_range = min(GLOB.MAX_EX_HEAVY_RANGE * cap_multiplier, heavy_impact_range)
@@ -91,11 +86,11 @@ GLOBAL_LIST_EMPTY(explosions)
if(adminlog)
message_admins("Explosion with size ([devastation_range], [heavy_impact_range], [light_impact_range], [flame_range]) in area: [epi_area] [ADMIN_COORDJMP(epicenter)]")
log_game("Explosion with size ([devastation_range], [heavy_impact_range], [light_impact_range], [flame_range]) in area [epicenter.loc.name] ([epicenter.x],[epicenter.y],[epicenter.z])")
-
+
var/x0 = epicenter.x
var/y0 = epicenter.y
var/z0 = epicenter.z
-
+
SSblackbox.record_feedback("associative", "explosion", 1, list("dev" = devastation_range, "heavy" = heavy_impact_range, "light" = light_impact_range, "flash" = flash_range, "flame" = flame_range, "orig_dev" = orig_dev_range, "orig_heavy" = orig_heavy_range, "orig_light" = orig_light_range, "x" = x0, "y" = y0, "z" = z0, "area" = epi_area.type))
// Play sounds; we want sounds to be different depending on distance so we will manually do it ourselves.
@@ -199,7 +194,7 @@ GLOBAL_LIST_EMPTY(explosions)
//------- EX_ACT AND TURF FIRES -------
if(T == epicenter) // Ensures explosives detonating from bags trigger other explosives in that bag
- var/list/items = list()
+ var/list/items = list()
for(var/I in T)
var/atom/A = I
items += A.GetAllContents()
@@ -418,6 +413,3 @@ GLOBAL_LIST_EMPTY(explosions)
// 10 explosion power is a (1, 3, 6) explosion.
// 5 explosion power is a (0, 1, 3) explosion.
// 1 explosion power is a (0, 0, 1) explosion.
-
-#undef CITYOFCOGS_CAP_MULTIPLIER
-#undef MINING_CAP_MULTIPLIER
diff --git a/code/datums/helper_datums/teleport.dm b/code/datums/helper_datums/teleport.dm
index c030bd4c2a3..f90d6eed321 100644
--- a/code/datums/helper_datums/teleport.dm
+++ b/code/datums/helper_datums/teleport.dm
@@ -166,9 +166,12 @@
// Safe location finder
-/proc/find_safe_turf(zlevel = ZLEVEL_STATION_PRIMARY, list/zlevels, extended_safety_checks = FALSE)
+/proc/find_safe_turf(zlevel, list/zlevels, extended_safety_checks = FALSE)
if(!zlevels)
- zlevels = list(zlevel)
+ if (zlevel)
+ zlevels = list(zlevel)
+ else
+ zlevels = SSmapping.levels_by_trait(ZTRAIT_STATION)
var/cycles = 1000
for(var/cycle in 1 to cycles)
// DRUNK DIALLING WOOOOOOOOO
diff --git a/code/datums/position_point_vector.dm b/code/datums/position_point_vector.dm
index 635ae0bbec5..6fae092c9dd 100644
--- a/code/datums/position_point_vector.dm
+++ b/code/datums/position_point_vector.dm
@@ -133,7 +133,7 @@
if(y > maxy)
y -= maxy
move_zy += 1
- var/datum/space_level/S = GLOB.z_levels_list["[z]"]
+ var/datum/space_level/S = SSmapping.get_level(z)
if(move_zx != 0)
var/datum/space_level/L = S.neigbours["[move_zx < 0? WEST : EAST]"]
z = L.z_value
diff --git a/code/game/gamemodes/blob/blob_report.dm b/code/game/gamemodes/blob/blob_report.dm
index 6585da0ba43..4385b732c3d 100644
--- a/code/game/gamemodes/blob/blob_report.dm
+++ b/code/game/gamemodes/blob/blob_report.dm
@@ -8,7 +8,7 @@
var/mach = 0
/datum/station_state/proc/count()
- for(var/Z in GLOB.station_z_levels)
+ for(var/Z in SSmapping.levels_by_trait(ZTRAIT_STATION))
for(var/turf/T in block(locate(1,1,Z), locate(world.maxx,world.maxy,Z)))
// don't count shuttles since they may have just left
if(istype(T.loc, /area/shuttle))
diff --git a/code/game/gamemodes/meteor/meteors.dm b/code/game/gamemodes/meteor/meteors.dm
index 9e0c9652715..13ab7a8bb12 100644
--- a/code/game/gamemodes/meteor/meteors.dm
+++ b/code/game/gamemodes/meteor/meteors.dm
@@ -31,7 +31,7 @@ GLOBAL_LIST_INIT(meteorsC, list(/obj/effect/meteor/dust)) //for space dust event
var/max_i = 10//number of tries to spawn meteor.
while(!isspaceturf(pickedstart))
var/startSide = pick(GLOB.cardinals)
- var/startZ = pick(GLOB.station_z_levels)
+ var/startZ = pick(SSmapping.levels_by_trait(ZTRAIT_STATION))
pickedstart = spaceDebrisStartLoc(startSide, startZ)
pickedgoal = spaceDebrisFinishLoc(startSide, startZ)
max_i--
@@ -40,7 +40,6 @@ GLOBAL_LIST_INIT(meteorsC, list(/obj/effect/meteor/dust)) //for space dust event
var/Me = pickweight(meteortypes)
var/obj/effect/meteor/M = new Me(pickedstart, pickedgoal)
M.dest = pickedgoal
- M.z_original = M.z
/proc/spaceDebrisStartLoc(startSide, Z)
var/starty
@@ -95,7 +94,7 @@ GLOBAL_LIST_INIT(meteorsC, list(/obj/effect/meteor/dust)) //for space dust event
pass_flags = PASSTABLE
var/heavy = 0
var/meteorsound = 'sound/effects/meteorimpact.ogg'
- var/z_original = ZLEVEL_STATION_PRIMARY
+ var/z_original
var/threat = 0 // used for determining which meteors are most interesting
var/lifetime = DEFAULT_METEOR_LIFETIME
var/timerid = null
@@ -126,6 +125,7 @@ GLOBAL_LIST_INIT(meteorsC, list(/obj/effect/meteor/dust)) //for space dust event
/obj/effect/meteor/Initialize(mapload, target)
. = ..()
+ z_original = z
GLOB.meteor_list += src
SSaugury.register_doom(src, threat)
SpinAnimation()
diff --git a/code/game/machinery/computer/camera_advanced.dm b/code/game/machinery/computer/camera_advanced.dm
index 7c790f63de7..f007050560c 100644
--- a/code/game/machinery/computer/camera_advanced.dm
+++ b/code/game/machinery/computer/camera_advanced.dm
@@ -17,7 +17,7 @@
/obj/machinery/computer/camera_advanced/Initialize()
. = ..()
if(station_lock_override)
- z_lock = GLOB.station_z_levels.Copy()
+ z_lock = SSmapping.levels_by_trait(ZTRAIT_STATION)
/obj/machinery/computer/camera_advanced/syndie
icon_keyboard = "syndie_key"
diff --git a/code/game/turfs/simulated/river.dm b/code/game/turfs/simulated/river.dm
index a5753edcd13..79846791d3a 100644
--- a/code/game/turfs/simulated/river.dm
+++ b/code/game/turfs/simulated/river.dm
@@ -4,7 +4,7 @@
#define RANDOM_LOWER_X 50
#define RANDOM_LOWER_Y 50
-/proc/spawn_rivers(target_z = ZLEVEL_LAVALAND, nodes = 4, turf_type = /turf/open/lava/smooth/lava_land_surface, whitelist_area = /area/lavaland/surface/outdoors, min_x = RANDOM_LOWER_X, min_y = RANDOM_LOWER_Y, max_x = RANDOM_UPPER_X, max_y = RANDOM_UPPER_Y)
+/proc/spawn_rivers(target_z, nodes = 4, turf_type = /turf/open/lava/smooth/lava_land_surface, whitelist_area = /area/lavaland/surface/outdoors, min_x = RANDOM_LOWER_X, min_y = RANDOM_LOWER_Y, max_x = RANDOM_UPPER_X, max_y = RANDOM_UPPER_Y)
var/list/river_nodes = list()
var/num_spawned = 0
while(num_spawned < nodes)
diff --git a/code/modules/admin/verbs/mapping.dm b/code/modules/admin/verbs/mapping.dm
index ce50378f4d9..fac7d93f9a3 100644
--- a/code/modules/admin/verbs/mapping.dm
+++ b/code/modules/admin/verbs/mapping.dm
@@ -45,7 +45,8 @@ GLOBAL_LIST_INIT(admin_verbs_debug_mapping, list(
/client/proc/start_line_profiling,
/client/proc/stop_line_profiling,
/client/proc/show_line_profiling,
- /client/proc/create_mapping_job_icons
+ /client/proc/create_mapping_job_icons,
+ /client/proc/debug_z_levels,
))
/obj/effect/debugging/mapfix_marker
@@ -292,4 +293,64 @@ GLOBAL_VAR_INIT(say_disabled, FALSE)
//Also add the x
for(var/x_number in 1 to 4)
final.Insert(icon('icons/mob/screen_gen.dmi', "x[x_number == 1 ? "" : x_number]"), "x[x_number == 1 ? "" : x_number]")
- fcopy(final, "icons/mob/landmarks.dmi")
\ No newline at end of file
+ fcopy(final, "icons/mob/landmarks.dmi")
+
+/client/proc/debug_z_levels()
+ set name = "Debug Z-Levels"
+ set category = "Mapping"
+
+ var/list/z_list = SSmapping.z_list
+ var/list/messages = list()
+ messages += "World: [world.maxx] x [world.maxy] x [world.maxz]
"
+
+ var/list/linked_levels = list()
+ var/min_x = INFINITY
+ var/min_y = INFINITY
+ var/max_x = -INFINITY
+ var/max_y = -INFINITY
+
+ for(var/z in 1 to max(world.maxz, z_list.len))
+ if (z > z_list.len)
+ messages += "[z]: Unmanaged (out of bounds)
"
+ continue
+ var/datum/space_level/S = z_list[z]
+ if (!S)
+ messages += "[z]: Unmanaged (null)
"
+ continue
+ var/linkage
+ switch (S.linkage)
+ if (UNAFFECTED)
+ linkage = "no linkage"
+ if (SELFLOOPING)
+ linkage = "self-looping"
+ if (CROSSLINKED)
+ linkage = "linked at ([S.xi], [S.yi])"
+ linked_levels += S
+ min_x = min(min_x, S.xi)
+ min_y = min(min_y, S.yi)
+ max_x = max(max_x, S.xi)
+ max_y = max(max_y, S.yi)
+ else
+ linkage = "unknown linkage '[S.linkage]'"
+
+ messages += "[z]: [S.name], [linkage], traits: [json_encode(S.traits)]
"
+ if (S.z_value != z)
+ messages += "-- z_value is [S.z_value], should be [z]
"
+ if (S.name == initial(S.name))
+ messages += "-- name not set
"
+ if (z > world.maxz)
+ messages += "-- exceeds max z"
+
+ var/grid[max_x - min_x + 1][max_y - min_y + 1]
+ for(var/datum/space_level/S in linked_levels)
+ grid[S.xi - min_x + 1][S.yi - min_y + 1] = S.z_value
+
+ messages += "
| [part.Join(" | ")] |