Support stations with multiple z-levels (#35339)

* Remove ZLEVEL_STATION_PRIMARY

* Add Up and Down traits for use by ladders and chasms

* Give map_config creation its own proc

* Combine LoadConfig and ValidateJSON and remove transition_config

* Make space linkage a z-level trait

* Remove ZLEVEL_EMPTY_SPACE

* Update uses of GetFullMapPath

* Handle multi-Z stations and load Lavaland and Reebe at runtime

* Remove unused space maps

* Fix inappropriate z-expansion in map reader, improve logging

* Update comments relating to z-level configuration

* Add Lavaland and Reebe to ALL_MAPS
This commit is contained in:
Tad Hardesty
2018-02-15 01:21:25 -08:00
committed by AnturK
parent 1fbea727bd
commit bbe4d12d13
23 changed files with 272 additions and 196863 deletions

View File

@@ -1,16 +1,11 @@
//#define LOWMEMORYMODE //uncomment this to load centcom and runtime station and thats it.
#include "map_files\generic\CentCom.dmm"
#include "map_files\generic\SpaceStation.dmm"
#ifndef LOWMEMORYMODE
#include "map_files\generic\Space.dmm"
#include "map_files\generic\Space2.dmm"
#include "map_files\Mining\Lavaland.dmm"
#include "map_files\generic\City_of_Cogs.dmm"
#ifdef ALL_MAPS
#include "map_files\Mining\Lavaland.dmm"
#include "map_files\generic\City_of_Cogs.dmm"
#include "map_files\debug\runtimestation.dmm"
#include "map_files\Deltastation\DeltaStation2.dmm"
#include "map_files\MetaStation\MetaStation.dmm"

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,46 +1,32 @@
/*
The /tg/ codebase currently requires you to have 11 z-levels of the same size dimensions.
z-level order is important, the order you put them in inside the map config.dm will determine what z level number they are assigned ingame.
Names of z-level do not matter, but order does greatly, for instances such as checking alive status of revheads on z1
The /tg/ codebase allows mixing of hardcoded and dynamically-loaded z-levels.
Z-levels can be reordered as desired and their properties are set by "traits".
See map_config.dm for how a particular station's traits may be chosen.
The list DEFAULT_MAP_TRAITS at the bottom of this file should correspond to
the maps that are hardcoded, as set in _maps/_basemap.dm. SSmapping is
responsible for loading every non-hardcoded z-level.
current as of september 17, 2017
z1 = station
z2 = centcom
z5 = mining
z6 = city of cogs
Everything else = randomized space
Last space-z level = empty
As of 2018-02-04, the typical z-levels for a single-level station are:
1: CentCom
2: Station
3-4: Randomized space
5: Mining
6: City of Cogs
7-11: Randomized space
12: Empty space
13: Transit space
Multi-Z stations are supported and multi-Z mining and away missions would
require only minor tweaks.
*/
#define CROSSLINKED 2
#define SELFLOOPING 1
#define UNAFFECTED 0
#define MAIN_STATION "Main Station"
#define CENTCOM "CentCom"
#define CITY_OF_COGS "City of Cogs"
#define EMPTY_AREA_1 "Empty Area 1"
#define EMPTY_AREA_2 "Empty Area 2"
#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"
#define EMPTY_AREA_6 "Empty Area 6"
#define EMPTY_AREA_7 "Empty Area 7"
#define EMPTY_AREA_8 "Empty Area 8"
#define AWAY_MISSION "Away Mission"
//for modifying jobs
// helpers for modifying jobs, used in various job_changes.dm files
#define MAP_JOB_CHECK if(SSmapping.config.map_name != JOB_MODIFICATION_MAP_NAME) { return; }
#define MAP_JOB_CHECK_BASE if(SSmapping.config.map_name != JOB_MODIFICATION_MAP_NAME) { return ..(); }
#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_STATION_PRIMARY 2
#define ZLEVEL_EMPTY_SPACE 12
#define SPACERUIN_MAP_EDGE_PAD 15
#define ZLEVEL_SPACE_RUIN_COUNT 5
#define ZLEVEL_SPACE_RUIN_COUNT 7
// traits
// boolean - marks a level as having that property if present
@@ -52,26 +38,40 @@ Last space-z level = empty
#define ZTRAIT_AWAY "Away Mission"
#define ZTRAIT_SPACE_RUINS "Space Ruins"
#define ZTRAIT_LAVA_RUINS "Lava Ruins"
// number - bombcap is multiplied by this before being applied to bombs
#define ZTRAIT_BOMBCAP_MULTIPLIER "Bombcap Multiplier"
// trait definitions
#define DL_NAME "name"
#define DL_LINKAGE "linkage"
#define DL_TRAITS "traits"
// numeric offsets - e.g. {"Down": -1} means that chasms will fall to z - 1 rather than oblivion
#define ZTRAIT_UP "Up"
#define ZTRAIT_DOWN "Down"
#define DECLARE_LEVEL(NAME, LINKAGE, TRAITS) list(DL_NAME = NAME, DL_LINKAGE = LINKAGE, DL_TRAITS = TRAITS)
// corresponds to basemap.dm
// enum - how space transitions should affect this level
#define ZTRAIT_LINKAGE "Linkage"
// UNAFFECTED if absent - no space transitions
#define UNAFFECTED null
// SELFLOOPING - space transitions always self-loop
#define SELFLOOPING "Self"
// CROSSLINKED - mixed in with the cross-linked space pool
#define CROSSLINKED "Cross"
// default trait definitions, used by SSmapping
#define ZTRAITS_CENTCOM list(ZTRAIT_LINKAGE = SELFLOOPING, ZTRAIT_CENTCOM = TRUE)
#define ZTRAITS_STATION list(ZTRAIT_LINKAGE = CROSSLINKED, ZTRAIT_STATION = TRUE)
#define ZTRAITS_SPACE list(ZTRAIT_LINKAGE = CROSSLINKED, ZTRAIT_SPACE_RUINS = TRUE)
#define ZTRAITS_LAVALAND list(ZTRAIT_MINING = TRUE, ZTRAIT_LAVA_RUINS = TRUE, ZTRAIT_BOMBCAP_MULTIPLIER = 2)
#define ZTRAITS_REEBE list(ZTRAIT_REEBE = TRUE, ZTRAIT_BOMBCAP_MULTIPLIER = 0.5)
#define DL_NAME "name"
#define DL_TRAITS "traits"
#define DECLARE_LEVEL(NAME, TRAITS) list(DL_NAME = NAME, DL_TRAITS = TRAITS)
// must correspond to _basemap.dm for things to work correctly
#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 = 2)),\
DECLARE_LEVEL("Reebe", UNAFFECTED, list(ZTRAIT_REEBE = TRUE, ZTRAIT_BOMBCAP_MULTIPLIER = 0.5)),\
DECLARE_LEVEL("CentCom", ZTRAITS_CENTCOM),\
)
//Camera lock flags
// Camera lock flags
#define CAMERA_LOCK_STATION 1
#define CAMERA_LOCK_MINING 2
#define CAMERA_LOCK_CENTCOM 4
@@ -85,4 +85,4 @@ Last space-z level = empty
#define PLACE_DEFAULT "random"
#define PLACE_SAME_Z "same"
#define PLACE_SPACE_RUIN "space"
#define PLACE_LAVA_RUIN "lavaland"
#define PLACE_LAVA_RUIN "lavaland"

View File

@@ -236,7 +236,7 @@
switch (command)
if ("map")
currentmap = new ("_maps/[data].json")
currentmap = load_map_config("_maps/[data].json")
if(currentmap.defaulted)
log_config("Failed to load map config for [data]!")
currentmap = null

View File

@@ -23,15 +23,19 @@ SUBSYSTEM_DEF(mapping)
var/loading_ruins = FALSE
// Z-manager stuff
var/station_start // should only be used for maploading-related tasks
var/space_levels_so_far = 0
var/list/z_list
var/datum/space_level/transit
var/datum/space_level/empty_space
var/dmm_suite/loader
/datum/controller/subsystem/mapping/PreInit()
if(!config)
#ifdef FORCE_MAP
config = new(FORCE_MAP)
config = load_map_config(FORCE_MAP)
#else
config = new(error_if_missing = FALSE)
config = load_map_config(error_if_missing = FALSE)
#endif
return ..()
@@ -39,16 +43,20 @@ SUBSYSTEM_DEF(mapping)
/datum/controller/subsystem/mapping/Initialize(timeofday)
if(config.defaulted)
to_chat(world, "<span class='boldannounce'>Unable to load next map config, defaulting to Box Station</span>")
loader = new
loadWorld()
repopulate_sorted_areas()
process_teleport_locs() //Sets up the wizard teleport locations
preloadTemplates()
#ifndef LOWMEMORYMODE
// 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
transit = add_new_zlevel("Transit", UNAFFECTED, list(ZTRAIT_TRANSIT = TRUE))
// Create space ruin levels
while (space_levels_so_far < ZLEVEL_SPACE_RUIN_COUNT)
++space_levels_so_far
add_new_zlevel("Empty Area [space_levels_so_far]", ZTRAITS_SPACE)
// and one level with no ruins
empty_space = add_new_zlevel("Empty Area [1 + space_levels_so_far]", list(ZTRAIT_LINKAGE = CROSSLINKED))
// and the transit level
transit = add_new_zlevel("Transit", list(ZTRAIT_TRANSIT = TRUE))
// Pick a random away mission.
createRandomZlevel()
@@ -71,6 +79,7 @@ SUBSYSTEM_DEF(mapping)
// Set up Z-level transitions.
setup_map_transitions()
generate_station_area_list()
QDEL_NULL(loader)
..()
/* Nuke threats, for making the blue tiles on the station go RED
@@ -108,32 +117,77 @@ SUBSYSTEM_DEF(mapping)
z_list = SSmapping.z_list
/datum/controller/subsystem/mapping/proc/TryLoadZ(filename, errorList, forceLevel, last)
var/static/dmm_suite/loader
if(!loader)
loader = new
if(!loader.load_map(file(filename), 0, 0, forceLevel, no_changeturf = TRUE))
errorList |= filename
if(last)
QDEL_NULL(loader)
#define INIT_ANNOUNCE(X) to_chat(world, "<span class='boldannounce'>[X]</span>"); log_world(X)
/datum/controller/subsystem/mapping/proc/LoadGroup(list/errorList, name, path, files, list/traits, list/default_traits)
var/start_time = REALTIMEOFDAY
if (!islist(files)) // handle single-level maps
files = list(files)
// check that the total z count of all maps matches the list of traits
var/total_z = 0
for (var/file in files)
var/full_path = "_maps/[path]/[file]"
var/bounds = loader.load_map(file(full_path), 1, 1, 1, cropMap=FALSE, measureOnly=TRUE)
files[file] = 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
for (var/i in 1 to total_z)
traits += list(default_traits)
else if (total_z != traits.len) // mismatch
INIT_ANNOUNCE("WARNING: [traits.len] trait sets specified for [total_z] z-levels in [path]!")
if (total_z < traits.len) // ignore extra traits
traits.Cut(total_z + 1)
while (total_z > traits.len) // fall back to defaults on extra levels
traits += list(default_traits)
// preload the relevant space_level datums
var/start_z = world.maxz + 1
var/i = 0
for (var/level in traits)
add_new_zlevel("[name][i ? " [i + 1]" : ""]", level)
++i
// load the maps
for (var/file in files)
var/full_path = "_maps/[path]/[file]"
if(!loader.load_map(file(full_path), 0, 0, start_z + files[file], no_changeturf = TRUE))
errorList |= full_path
INIT_ANNOUNCE("Loaded [name] in [(REALTIMEOFDAY - start_time)/10]s!")
/datum/controller/subsystem/mapping/proc/loadWorld()
//if any of these fail, something has gone horribly, HORRIBLY, wrong
var/list/FailedZs = list()
var/start_time = REALTIMEOFDAY
INIT_ANNOUNCE("Loading [config.map_name]...")
TryLoadZ(config.GetFullMapPath(), FailedZs, ZLEVEL_STATION_PRIMARY)
// ensure we have space_level datums for compiled-in maps
InitializeDefaultZLevels()
INIT_ANNOUNCE("Loaded station in [(REALTIMEOFDAY - start_time)/10]s!")
// load the station
station_start = world.maxz + 1
INIT_ANNOUNCE("Loading [config.map_name]...")
LoadGroup(FailedZs, "Station", config.map_path, config.map_file, config.traits, ZTRAITS_STATION)
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]")
query_round_map_name.Execute()
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!")
#ifndef LOWMEMORYMODE
// TODO: remove this when the DB is prepared for the z-levels getting reordered
while (world.maxz < (5 - 1) && space_levels_so_far < ZLEVEL_SPACE_RUIN_COUNT)
++space_levels_so_far
add_new_zlevel("Empty Area [space_levels_so_far]", ZTRAITS_SPACE)
// load mining
if(config.minetype == "lavaland")
LoadGroup(FailedZs, "Lavaland", "map_files/Mining", "Lavaland.dmm", default_traits = ZTRAITS_LAVALAND)
else if (!isnull(config.minetype))
INIT_ANNOUNCE("WARNING: An unknown minetype '[config.minetype]' was set! This is being ignored! Update the maploader code!")
// load Reebe
LoadGroup(FailedZs, "Reebe", "map_files/generic", "City_of_Cogs.dmm", default_traits = ZTRAITS_REEBE)
#endif
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]]"
@@ -209,7 +263,7 @@ GLOBAL_LIST_EMPTY(the_station_areas)
/datum/controller/subsystem/mapping/proc/changemap(var/datum/map_config/VM)
if(!VM.MakeNextMap())
next_map_config = new(default_to_box = TRUE)
next_map_config = load_map_config(default_to_box = TRUE)
message_admins("Failed to set new map with next_map.json for [VM.map_name]! Using default as backup!")
return

View File

@@ -9,7 +9,10 @@ SUBSYSTEM_DEF(minimap)
/datum/controller/subsystem/minimap/Initialize(timeofday)
z_levels = SSmapping.levels_by_trait(ZTRAIT_STATION)
var/hash = md5(SSmapping.config.GetFullMapPath())
var/list/hashlist = list()
for (var/file in SSmapping.config.GetFullMapPaths())
hashlist += md5(file2text(file))
var/hash = hashlist.Join("\n")
if(CONFIG_GET(flag/generate_minimaps))
if(hash == trim(file2text(hash_path())))
for(var/z in z_levels) //We have these files cached, let's register them

View File

@@ -56,7 +56,7 @@ SUBSYSTEM_DEF(persistence)
old_secret_satchels.Cut(pos, pos+1 % old_secret_satchels.len)
F.x = old_secret_satchels[pos]["x"]
F.y = old_secret_satchels[pos]["y"]
F.z = ZLEVEL_STATION_PRIMARY
F.z = SSmapping.station_start
path = text2path(old_secret_satchels[pos]["saved_obj"])
if(F)
@@ -67,7 +67,7 @@ SUBSYSTEM_DEF(persistence)
spawned_objects[spawned_item] = TRUE
placed_satchel++
var/free_satchels = 0
for(var/turf/T in shuffle(block(locate(TRANSITIONEDGE,TRANSITIONEDGE,ZLEVEL_STATION_PRIMARY), locate(world.maxx-TRANSITIONEDGE,world.maxy-TRANSITIONEDGE,ZLEVEL_STATION_PRIMARY)))) //Nontrivially expensive but it's roundstart only
for(var/turf/T in shuffle(block(locate(TRANSITIONEDGE,TRANSITIONEDGE,SSmapping.station_start), locate(world.maxx-TRANSITIONEDGE,world.maxy-TRANSITIONEDGE,SSmapping.station_start)))) //Nontrivially expensive but it's roundstart only
if(isfloorturf(T) && !isplatingturf(T))
new /obj/item/storage/backpack/satchel/flat/secret(T)
free_satchels++
@@ -189,7 +189,7 @@ SUBSYSTEM_DEF(persistence)
var/list/satchels_to_add = list()
for(var/A in new_secret_satchels)
var/obj/item/storage/backpack/satchel/flat/F = A
if(QDELETED(F) || F.z != ZLEVEL_STATION_PRIMARY || F.invisibility != INVISIBILITY_MAXIMUM)
if(QDELETED(F) || F.z != SSmapping.station_start || F.invisibility != INVISIBILITY_MAXIMUM)
continue
var/list/savable_obj = list()
for(var/obj/O in F)

View File

@@ -4,46 +4,42 @@
// -Cyberboss
/datum/map_config
// Metadata
var/config_filename = "_maps/boxstation.json"
var/defaulted = TRUE // set to FALSE by LoadConfig() succeeding
// Config from maps.txt
var/config_max_users = 0
var/config_min_users = 0
var/voteweight = 1
// Config actually from the JSON - should default to Box
var/map_name = "Box Station"
var/map_path = "map_files/BoxStation"
var/map_file = "BoxStation.dmm"
var/traits = null
var/minetype = "lavaland"
var/allow_custom_shuttles = TRUE
var/shuttles = list(
"cargo" = "cargo_box",
"ferry" = "ferry_fancy",
"whiteship" = "whiteship_box",
"emergency" = "emergency_box")
//Order matters here.
var/list/transition_config = list(CENTCOM = SELFLOOPING,
MAIN_STATION = CROSSLINKED,
EMPTY_AREA_1 = CROSSLINKED,
EMPTY_AREA_2 = CROSSLINKED,
MINING = SELFLOOPING,
CITY_OF_COGS = SELFLOOPING,
EMPTY_AREA_3 = CROSSLINKED,
EMPTY_AREA_4 = CROSSLINKED,
EMPTY_AREA_5 = CROSSLINKED,
EMPTY_AREA_6 = CROSSLINKED,
EMPTY_AREA_7 = CROSSLINKED,
EMPTY_AREA_8 = CROSSLINKED)
var/defaulted = TRUE //if New failed
var/config_max_users = 0
var/config_min_users = 0
var/voteweight = 1
var/allow_custom_shuttles = TRUE
/datum/map_config/New(filename = "data/next_map.json", default_to_box, delete_after, error_if_missing = TRUE)
if(default_to_box)
return
LoadConfig(filename, error_if_missing)
if(delete_after)
/proc/load_map_config(filename = "data/next_map.json", default_to_box, delete_after, error_if_missing = TRUE)
var/datum/map_config/config = new
if (default_to_box)
return config
if (!config.LoadConfig(filename, error_if_missing))
qdel(config)
config = new /datum/map_config // Fall back to Box
if (delete_after)
fdel(filename)
return config
#define CHECK_EXISTS(X) if(!istext(json[X])) { log_world("[##X] missing from json!"); return; }
/datum/map_config/proc/LoadConfig(filename, error_if_missing)
if(!fexists(filename))
if(error_if_missing)
@@ -65,99 +61,66 @@
log_world("map_config is not json: [filename]")
return
if(!ValidateJSON(json))
log_world("map_config failed to validate for above reason: [filename]")
return
config_filename = filename
CHECK_EXISTS("map_name")
map_name = json["map_name"]
CHECK_EXISTS("map_path")
map_path = json["map_path"]
map_file = json["map_file"]
if(islist(json["shuttles"]))
map_file = json["map_file"]
// "map_file": "BoxStation.dmm"
if (istext(map_file))
if (!fexists("_maps/[map_path]/[map_file]"))
log_world("Map file ([map_path]/[map_file]) does not exist!")
return
// "map_file": ["Lower.dmm", "Upper.dmm"]
else if (islist(map_file))
for (var/file in map_file)
if (!fexists("_maps/[map_path]/[file]"))
log_world("Map file ([map_path]/[file]) does not exist!")
return
else
log_world("map_file missing from json!")
return
if (islist(json["shuttles"]))
var/list/L = json["shuttles"]
for(var/key in L)
var/value = L[key]
shuttles[key] = value
minetype = json["minetype"] || minetype
allow_custom_shuttles = json["allow_custom_shuttles"] != FALSE
var/jtcl = json["transition_config"]
if(jtcl && jtcl != "default")
transition_config.Cut()
for(var/I in jtcl)
transition_config[TransitionStringToEnum(I)] = TransitionStringToEnum(jtcl[I])
defaulted = FALSE
#define CHECK_EXISTS(X) if(!istext(json[X])) { log_world("[##X] missing from json!"); return; }
/datum/map_config/proc/ValidateJSON(list/json)
CHECK_EXISTS("map_name")
CHECK_EXISTS("map_path")
CHECK_EXISTS("map_file")
var/shuttles = json["shuttles"]
if(shuttles && !islist(shuttles))
log_world("json\[shuttles\] is not a list!")
var/path = GetFullMapPath(json["map_path"], json["map_file"])
if(!fexists(path))
log_world("Map file ([path]) does not exist!")
else if ("shuttles" in json)
log_world("map_config shuttles is not a list!")
return
var/tc = json["transition_config"]
if(tc != null && tc != "default")
if(!islist(tc))
log_world("transition_config is not a list!")
return
traits = json["traits"]
// "traits": [{"Linkage": "Cross"}, {"Space Ruins": true}]
if (islist(traits))
// "Station" is set by default, but it's assumed if you're setting
// traits you want to customize which level is cross-linked
for (var/level in traits)
if (!(ZTRAIT_STATION in level))
level[ZTRAIT_STATION] = TRUE
// "traits": null or absent -> default
else if (!isnull(traits))
log_world("map_config traits is not a list!")
return
for(var/I in tc)
if(isnull(TransitionStringToEnum(I)))
log_world("Invalid transition_config option: [I]!")
if(isnull(TransitionStringToEnum(tc[I])))
log_world("Invalid transition_config option: [I]!")
if ("minetype" in json)
minetype = json["minetype"]
allow_custom_shuttles = json["allow_custom_shuttles"] != FALSE
defaulted = FALSE
return TRUE
#undef CHECK_EXISTS
/datum/map_config/proc/TransitionStringToEnum(string)
switch(string)
if("CROSSLINKED")
return CROSSLINKED
if("SELFLOOPING")
return SELFLOOPING
if("UNAFFECTED")
return UNAFFECTED
if("MAIN_STATION")
return MAIN_STATION
if("CENTCOM")
return CENTCOM
if("CITY_OF_COGS")
return CITY_OF_COGS
if("MINING")
return MINING
if("EMPTY_AREA_1")
return EMPTY_AREA_1
if("EMPTY_AREA_2")
return EMPTY_AREA_2
if("EMPTY_AREA_3")
return EMPTY_AREA_3
if("EMPTY_AREA_4")
return EMPTY_AREA_4
if("EMPTY_AREA_5")
return EMPTY_AREA_5
if("EMPTY_AREA_6")
return EMPTY_AREA_6
if("EMPTY_AREA_7")
return EMPTY_AREA_7
if("EMPTY_AREA_8")
return EMPTY_AREA_8
/datum/map_config/proc/GetFullMapPath(mp = map_path, mf = map_file)
return "_maps/[mp]/[mf]"
/datum/map_config/proc/GetFullMapPaths()
if (istext(map_file))
return list("_maps/[map_path]/[map_file]")
. = list()
for (var/file in map_file)
. += "_maps/[map_path]/[file]"
/datum/map_config/proc/MakeNextMap()
return config_filename == "data/next_map.json" || fcopy(config_filename, "data/next_map.json")

View File

@@ -35,19 +35,20 @@
/obj/structure/ladder/LateInitialize()
// By default, discover ladders above and below us vertically
var/turf/T = get_turf(src)
var/obj/structure/ladder/L
if (!down)
for (var/obj/structure/ladder/L in locate(T.x, T.y, T.z - 1))
L = locate() in SSmapping.get_turf_below(T)
if (L)
down = L
L.up = src // Don't waste effort looping the other way
L.update_icon()
break
if (!up)
for (var/obj/structure/ladder/L in locate(T.x, T.y, T.z + 1))
L = locate() in SSmapping.get_turf_above(T)
if (L)
up = L
L.down = src // Don't waste effort looping the other way
L.update_icon()
break
update_icon()

View File

@@ -11,7 +11,7 @@
/turf/open/chasm/Initialize()
. = ..()
AddComponent(/datum/component/chasm, null)
AddComponent(/datum/component/chasm, SSmapping.get_turf_below(src))
/turf/open/chasm/proc/set_target(turf/target)
GET_COMPONENT(chasm_component, /datum/component/chasm)
@@ -64,16 +64,6 @@
/turf/open/chasm/CanPass(atom/movable/mover, turf/target)
return 1
// Naive "down" which just subtracts a z-level
/turf/open/chasm/straight_down
baseturfs = /turf/open/chasm/straight_down
/turf/open/chasm/straight_down/Initialize()
. = ..()
set_target(locate(x, y, z - 1))
// Chasms for Lavaland, with planetary atmos and lava glow
/turf/open/chasm/lavaland
initial_gas_mix = LAVALAND_DEFAULT_ATMOS
@@ -95,10 +85,3 @@
underlay_appearance.icon = 'icons/turf/floors.dmi'
underlay_appearance.icon_state = "dirt"
return TRUE
/turf/open/chasm/jungle/straight_down
baseturfs = /turf/open/chasm/jungle/straight_down
/turf/open/chasm/jungle/straight_down/Initialize(mapload)
. = ..()
set_target(locate(x, y, z - 1))

View File

@@ -3,7 +3,7 @@
desc = "Upon closer examination, it's still dirt."
icon = 'icons/turf/floors.dmi'
icon_state = "dirt"
baseturfs = /turf/open/chasm/jungle/straight_down
baseturfs = /turf/open/chasm/jungle
initial_gas_mix = LAVALAND_DEFAULT_ATMOS
planetary_atmos = TRUE
attachment_holes = FALSE

View File

@@ -40,12 +40,10 @@
var/min = 1+TRANSITIONEDGE
var/list/possible_transtitons = list()
var/k = 1
var/list/config_list = SSmapping.config.transition_config
for(var/a in config_list)
if(config_list[a] == CROSSLINKED) // Only pick z-levels connected to station space
possible_transtitons += k
k++
for(var/A in SSmapping.z_list)
var/datum/space_level/D = A
if (D.linkage == CROSSLINKED)
possible_transtitons += D.z_value
var/_z = pick(possible_transtitons)
//now select coordinates for a border turf

View File

@@ -60,7 +60,7 @@
var/datum/map_template/pirate_event_ship/ship = new
var/x = rand(TRANSITIONEDGE,world.maxx - TRANSITIONEDGE - ship.width)
var/y = rand(TRANSITIONEDGE,world.maxy - TRANSITIONEDGE - ship.height)
var/z = ZLEVEL_EMPTY_SPACE
var/z = SSmapping.empty_space.z_value
var/turf/T = locate(x,y,z)
if(!T)
CRASH("Pirate event found no turf to load in")

View File

@@ -52,7 +52,7 @@
var/x = round((world.maxx - width)/2)
var/y = round((world.maxy - height)/2)
var/datum/space_level/level = SSmapping.add_new_zlevel(name, UNAFFECTED, list(ZTRAIT_AWAY = TRUE))
var/datum/space_level/level = SSmapping.add_new_zlevel(name, list(ZTRAIT_AWAY = TRUE))
var/list/bounds = maploader.load_map(file(mappath), x, y, level.z_value, no_changeturf=(SSatoms.initialized == INITIALIZATION_INSSATOMS), placeOnTop=TRUE)
if(!bounds)
return FALSE

View File

@@ -94,7 +94,7 @@ GLOBAL_DATUM_INIT(_preloader, /dmm_suite/preloader, new)
var/zcrd = text2num(dmmRegex.group[5]) + z_offset - 1
var/zexpansion = zcrd > world.maxz
if(zexpansion)
if(zexpansion && !measureOnly)
if(cropMap)
continue
else

View File

@@ -1,5 +1,5 @@
/datum/space_level
var/name = "Your config settings failed, you need to fix this for the datum space levels to work"
var/name = "NAME MISSING"
var/list/neigbours = list()
var/list/traits
var/z_value = 1 //actual z placement
@@ -7,8 +7,8 @@
var/xi
var/yi //imaginary placements on the grid
/datum/space_level/New(new_z, new_name, new_linkage = SELFLOOPING, list/new_traits = list())
/datum/space_level/New(new_z, new_name, list/new_traits = list())
z_value = new_z
name = new_name
traits = new_traits
set_linkage(new_linkage)
set_linkage(new_traits[ZTRAIT_LINKAGE])

View File

@@ -1,9 +1,8 @@
/datum/space_level/proc/set_linkage(new_linkage)
linkage = new_linkage
if(linkage == SELFLOOPING)
neigbours = list()
var/list/L = list(TEXT_NORTH,TEXT_SOUTH,TEXT_EAST,TEXT_WEST)
for(var/A in L)
neigbours = list(TEXT_NORTH,TEXT_SOUTH,TEXT_EAST,TEXT_WEST)
for(var/A in neigbours)
neigbours[A] = src
/datum/space_level/proc/set_neigbours(list/L)

View File

@@ -1,15 +1,19 @@
// Look up levels[z].traits[trait]
/datum/controller/subsystem/mapping/proc/level_trait(z, trait)
if (!z)
if (!isnum(z) || z < 1)
return null
var/list/trait_list
if (z_list)
if (z > z_list.len)
stack_trace("Unmanaged z-level [z]! maxz = [world.maxz], z_list.len = [z_list.len]")
return list()
var/datum/space_level/S = get_level(z)
trait_list = S.traits
return S.traits[trait]
else
var/list/default_map_traits = DEFAULT_MAP_TRAITS
trait_list = default_map_traits[z][DL_TRAITS]
return trait_list[trait]
var/list/default = DEFAULT_MAP_TRAITS
if (z > default.len)
stack_trace("Unmanaged z-level [z]! maxz = [world.maxz], default.len = [default.len]")
return list()
return default[z][DL_TRAITS][trait]
// Check if levels[z] has any of the specified traits
/datum/controller/subsystem/mapping/proc/level_has_any_trait(z, list/traits)
@@ -45,6 +49,24 @@
. += S.z_value
break
// Attempt to get the turf below the provided one according to Z traits
/datum/controller/subsystem/mapping/proc/get_turf_below(turf/T)
if (!T)
return
var/offset = level_trait(T.z, ZTRAIT_DOWN)
if (!offset)
return
return locate(T.x, T.y, T.z + offset)
// Attempt to get the turf above the provided one according to Z traits
/datum/controller/subsystem/mapping/proc/get_turf_above(turf/T)
if (!T)
return
var/offset = level_trait(T.z, ZTRAIT_UP)
if (!offset)
return
return locate(T.x, T.y, T.z + offset)
// Prefer not to use this one too often
/datum/controller/subsystem/mapping/proc/get_station_center()
var/station_z = levels_by_trait(ZTRAIT_STATION)[1]

View File

@@ -13,20 +13,20 @@
for (var/I in 1 to default_map_traits.len)
var/list/features = default_map_traits[I]
var/datum/space_level/S = new(I, features[DL_NAME], features[DL_LINKAGE], features[DL_TRAITS])
var/datum/space_level/S = new(I, features[DL_NAME], features[DL_TRAITS])
z_list += S
/datum/controller/subsystem/mapping/proc/add_new_zlevel(name, linkage = SELFLOOPING, traits = list(), z_type = /datum/space_level)
/datum/controller/subsystem/mapping/proc/add_new_zlevel(name, traits = list(), z_type = /datum/space_level)
var/new_z = z_list.len + 1
if (world.maxz < new_z)
world.incrementMaxZ()
CHECK_TICK
// TODO: sleep here if the Z level needs to be cleared
var/datum/space_level/S = new z_type(new_z, name, linkage, traits)
var/datum/space_level/S = new z_type(new_z, name, traits)
z_list += S
return S
/datum/controller/subsystem/mapping/proc/get_level(z)
. = z_list[z]
if (!.)
CRASH("Unmanaged z-level: '[z]'")
if (z_list && z >= 1 && z <= z_list.len)
return z_list[z]
CRASH("Unmanaged z-level [z]! maxz = [world.maxz], z_list.len = [z_list ? z_list.len : "null"]")

View File

@@ -20,11 +20,15 @@
if(!istype(mother, /datum/mapGenerator/repair/reload_station_map))
return
var/datum/mapGenerator/repair/reload_station_map/mother1 = mother
if(!is_station_level(mother1.z))
return //This is only for reloading station blocks!
GLOB.reloading_map = TRUE
var/static/dmm_suite/reloader = new
var/list/bounds = reloader.load_map(file(SSmapping.config.GetFullMapPath()),measureOnly = FALSE, no_changeturf = FALSE,x_offset = 0, y_offset = 0, z_offset = ZLEVEL_STATION_PRIMARY, 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)
// 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())
bounds = 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)
z_offset += bounds[MAP_MAXZ] - bounds[MAP_MINZ] + 1
var/list/obj/machinery/atmospherics/atmos_machines = list()
var/list/obj/structure/cable/cables = list()
@@ -32,8 +36,8 @@
repopulate_sorted_areas()
for(var/L in block(locate(bounds[MAP_MINX], bounds[MAP_MINY], bounds[MAP_MINZ]),
locate(bounds[MAP_MAXX], bounds[MAP_MAXY], bounds[MAP_MAXZ])))
for(var/L in block(locate(bounds[MAP_MINX], bounds[MAP_MINY], SSmapping.station_start),
locate(bounds[MAP_MAXX], bounds[MAP_MAXY], z_offset - 1)))
set waitfor = FALSE
var/turf/B = L
atoms += B
@@ -93,7 +97,7 @@
y_low = min(start.y, end.y)
x_high = max(start.x, end.x)
y_high = max(start.y, end.y)
z = ZLEVEL_STATION_PRIMARY
z = SSmapping.station_start
GLOBAL_VAR_INIT(reloading_map, FALSE)

View File

@@ -100,9 +100,16 @@
/obj/structure/mirror/magic/pride/curse(mob/user)
user.visible_message("<span class='danger'><B>The ground splits beneath [user] as [user.p_their()] hand leaves the mirror!</B></span>", \
"<span class='notice'>Perfect. Much better! Now <i>nobody</i> will be able to resist yo-</span>")
var/turf/T = get_turf(user)
T.ChangeTurf(/turf/open/chasm/straight_down)
var/list/levels = SSmapping.levels_by_trait(ZTRAIT_SPACE_RUINS)
var/turf/dest
if (levels.len)
dest = locate(T.x, T.y, pick(levels))
T.ChangeTurf(/turf/open/chasm)
var/turf/open/chasm/C = T
C.set_target(dest)
C.drop(user)
//can't be bothered to do sloth right now, will make later