Files
Bubberstation/code/controllers/subsystem/mapping.dm
Useroth cca7f8ee4c Some missed mirrors (#13415)
* Refactors firestacks into status effects (#66573)

This PR refactors firestacks into two status effects: fire_stacks, which behave like normal firestacks you have right now, and wet_stacks, which are your negative fire stacks right now. This allows for custom fires with custom behaviors and icons to be made.

Some fire related is moved away from species(what the fuck was it even doing there) into these as well.
Oh and I fixed the bug where monkeys on fire had a human fire overlay, why wasn't this fixed already, it's like ancient.

Also changed some related proc names to be snake_case like everything should be.

This allows for custom fire types with custom behaviours, like freezing freon fire or radioactive tritium fire. Removing vars from living and moving them to status effects for modularity is also good.
Nothing to argue about since there's nothing player-facing

* Hud Image Culling By Z Level: Theft edition (#65189)

* makes hud images only apply by z level

* makes some of the atom_hud procs have better names

* fixes warning with the hud_user list and adds better documentation

* better docs for hud_images

* removes TODOs

* docs for hud_list

* adds support for linked z levels so mobs can see lower ones

* fixes merge conflict and shittily makes only shocked airlocks get added

* adds support for setting images in the hud as active and inactive

* gets rid of unatomic spatial grid change

* maybe i should actually try COMPILING my changes

* fixes merge skew and makes it compile again

* fixes huds refusing to remove from users who changed z level

* improves z level and registration logic

* fixes antag huds not appearing

* Fixes antag huds not properly setting. We now use hud_list in init, so it needs to be set before the new call, not after. Not sure why the use of appearance key was split like this, but none else knows either so none can stop me

* Ensures that hiding a basic appearance also hides the atom's active list too

* Fixes antag huds going poof

Ensures that remove_atom_from_hud will return false if the passed atom
isn't managed by it

This fixes antag huds disappearing randomly, since they assumed that if
the parent call of remove_atom_from_hud returned true, we should delete
ourselves. This is a safe assumption for them to make, since they should
only ever have one atom.

Does kinda bork if we call remove_atom_from_hud in a way that is unsure
if the passed atom is actually in that list. We were forced into doing
this by how atom huds use the qdeleting signal.

* makes basic alternate_appearance's only update themselves when setting their hud image to active and makes them not add themselves to the global huds_by_category list

* fixes mistake with hud_users list being set non associatively (bad)

* as anything in bot path loops

* Fixes merge skew problems

* Makes bot paths non global

This way they can show themselves to only the bot that "owns" them, ya
feel me?

* Fixes huds not showing up sometimes, cleans up some code

Post Kapu's limb refactor, we were calling prepare_huds twice in a human
init call chain. What was happening was this:

call prepare_huds() // Human
I gained a new hud image
I set active hud icons to mirror it
call prepare_huds() // Living
I overwrote the new hud image
I attempted to set active hud icons, which failed because it assumes
this can never happen

*cries*

* Renames add_hud_to_atom to show_to

My hope is this will make understanding hud code a bit easier, by tying
the behavior to a "verb" more closely. Also renamed a few vars

* remove_hud_from_mob -> hide_from

* Nitpicks a few comments

* Whoops/fuck/shit/damn it all/hhhhhhhhhhhh

* Moves check down, improves stack trace a bit

Co-authored-by: KylerAce <kylerlumpkin1@gmail.com>

* small touch-up

* this should do it

Co-authored-by: SmArtKar <44720187+SmArtKar@users.noreply.github.com>
Co-authored-by: LemonInTheDark <58055496+LemonInTheDark@users.noreply.github.com>
Co-authored-by: KylerAce <kylerlumpkin1@gmail.com>
2022-05-08 03:59:40 +01:00

624 lines
23 KiB
Plaintext

SUBSYSTEM_DEF(mapping)
name = "Mapping"
init_order = INIT_ORDER_MAPPING
flags = SS_NO_FIRE
var/list/nuke_tiles = list()
var/list/nuke_threats = list()
var/datum/map_config/config
var/datum/map_config/next_map_config
var/map_voted = FALSE
var/list/map_templates = list()
var/list/ruins_templates = list()
///List of ruins, separated by their theme
var/list/themed_ruins = list()
var/datum/space_level/isolated_ruins_z //Created on demand during ruin loading.
var/list/shuttle_templates = list()
var/list/shelter_templates = list()
var/list/holodeck_templates = list()
var/list/areas_in_z = list()
var/loading_ruins = FALSE
var/list/turf/unused_turfs = list() //Not actually unused turfs they're unused but reserved for use for whatever requests them. "[zlevel_of_turf]" = list(turfs)
var/list/datum/turf_reservations //list of turf reservations
var/list/used_turfs = list() //list of turf = datum/turf_reservation
var/list/reservation_ready = list()
var/clearing_reserved_turfs = FALSE
///All possible biomes in assoc list as type || instance
var/list/biomes = list()
// Z-manager stuff
var/station_start // should only be used for maploading-related tasks
var/space_levels_so_far = 0
///list of all the z level datums created representing the z levels in the world
var/list/z_list
var/datum/space_level/transit
var/datum/space_level/empty_space
var/num_of_res_levels = 1
/// True when in the process of adding a new Z-level, global locking
var/adding_new_zlevel = FALSE
/datum/controller/subsystem/mapping/New()
..()
#ifdef FORCE_MAP
config = load_map_config(FORCE_MAP, FORCE_MAP_DIRECTORY)
#else
config = load_map_config(error_if_missing = FALSE)
#endif
/datum/controller/subsystem/mapping/Initialize(timeofday)
if(initialized)
return
if(config.defaulted)
var/old_config = config
config = global.config.defaultmap
if(!config || config.defaulted)
to_chat(world, span_boldannounce("Unable to load next or default map config, defaulting to Meta Station."))
config = old_config
initialize_biomes()
loadWorld()
determine_fake_sale()
repopulate_sorted_areas()
process_teleport_locs() //Sets up the wizard teleport locations
preloadTemplates()
#ifndef LOWMEMORYMODE
// Create space ruin levels
while (space_levels_so_far < config.space_ruin_levels)
++space_levels_so_far
add_new_zlevel("Empty Area [space_levels_so_far]", ZTRAITS_SPACE)
// and one level with no ruins
for (var/i in 1 to config.space_empty_levels)
++space_levels_so_far
empty_space = add_new_zlevel("Empty Area [space_levels_so_far]", list(ZTRAIT_LINKAGE = CROSSLINKED))
// Pick a random away mission.
if(CONFIG_GET(flag/roundstart_away))
createRandomZlevel(prob(CONFIG_GET(number/config_gateway_chance)))
// Load the virtual reality hub
if(CONFIG_GET(flag/virtual_reality))
to_chat(world, span_boldannounce("Loading virtual reality..."))
load_new_z_level("_maps/RandomZLevels/VR/vrhub.dmm", "Virtual Reality Hub")
to_chat(world, span_boldannounce("Virtual reality loaded."))
loading_ruins = TRUE
setup_ruins()
loading_ruins = FALSE
#endif
// Run map generation after ruin generation to prevent issues
run_map_generation()
// Add the transit level
transit = add_new_zlevel("Transit/Reserved", list(ZTRAIT_RESERVED = TRUE))
repopulate_sorted_areas()
// Set up Z-level transitions.
setup_map_transitions()
generate_station_area_list()
initialize_reserved_level(transit.z_value)
SSticker.OnRoundstart(CALLBACK(src, .proc/spawn_maintenance_loot))
return ..()
/**
* ##setup_ruins
*
* Sets up all of the ruins to be spawned
*/
/datum/controller/subsystem/mapping/proc/setup_ruins()
// Generate mining ruins
var/list/lava_ruins = levels_by_trait(ZTRAIT_LAVA_RUINS)
if (lava_ruins.len)
seedRuins(lava_ruins, CONFIG_GET(number/lavaland_budget), list(/area/lavaland/surface/outdoors/unexplored), themed_ruins[ZTRAIT_LAVA_RUINS])
for (var/lava_z in lava_ruins)
spawn_rivers(lava_z)
var/list/ice_ruins = levels_by_trait(ZTRAIT_ICE_RUINS)
if (ice_ruins.len)
// needs to be whitelisted for underground too so place_below ruins work
seedRuins(ice_ruins, CONFIG_GET(number/icemoon_budget), list(/area/icemoon/surface/outdoors/unexplored, /area/icemoon/underground/unexplored), themed_ruins[ZTRAIT_ICE_RUINS])
for (var/ice_z in ice_ruins)
spawn_rivers(ice_z, 4, /turf/open/openspace/icemoon, /area/icemoon/surface/outdoors/unexplored/rivers)
var/list/ice_ruins_underground = levels_by_trait(ZTRAIT_ICE_RUINS_UNDERGROUND)
if (ice_ruins_underground.len)
seedRuins(ice_ruins_underground, CONFIG_GET(number/icemoon_budget), list(/area/icemoon/underground/unexplored), themed_ruins[ZTRAIT_ICE_RUINS_UNDERGROUND])
for (var/ice_z in ice_ruins_underground)
spawn_rivers(ice_z, 4, level_trait(ice_z, ZTRAIT_BASETURF), /area/icemoon/underground/unexplored/rivers)
// 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), list(/area/space), themed_ruins[ZTRAIT_SPACE_RUINS])
/datum/controller/subsystem/mapping/proc/wipe_reservations(wipe_safety_delay = 100)
if(clearing_reserved_turfs || !initialized) //in either case this is just not needed.
return
clearing_reserved_turfs = TRUE
SSshuttle.transit_requesters.Cut()
message_admins("Clearing dynamic reservation space.")
var/list/obj/docking_port/mobile/in_transit = list()
for(var/i in SSshuttle.transit_docking_ports)
var/obj/docking_port/stationary/transit/T = i
if(!istype(T))
continue
in_transit[T] = T.get_docked()
var/go_ahead = world.time + wipe_safety_delay
if(in_transit.len)
message_admins("Shuttles in transit detected. Attempting to fast travel. Timeout is [wipe_safety_delay/10] seconds.")
var/list/cleared = list()
for(var/i in in_transit)
INVOKE_ASYNC(src, .proc/safety_clear_transit_dock, i, in_transit[i], cleared)
UNTIL((go_ahead < world.time) || (cleared.len == in_transit.len))
do_wipe_turf_reservations()
clearing_reserved_turfs = FALSE
/datum/controller/subsystem/mapping/proc/safety_clear_transit_dock(obj/docking_port/stationary/transit/T, obj/docking_port/mobile/M, list/returning)
M.setTimer(0)
var/error = M.initiate_docking(M.destination, M.preferred_direction)
if(!error)
returning += M
qdel(T, TRUE)
/* Nuke threats, for making the blue tiles on the station go RED
Used by the AI doomsday and the self-destruct nuke.
*/
/datum/controller/subsystem/mapping/proc/add_nuke_threat(datum/nuke)
nuke_threats[nuke] = TRUE
check_nuke_threats()
/datum/controller/subsystem/mapping/proc/remove_nuke_threat(datum/nuke)
nuke_threats -= nuke
check_nuke_threats()
/datum/controller/subsystem/mapping/proc/check_nuke_threats()
for(var/datum/d in nuke_threats)
if(!istype(d) || QDELETED(d))
nuke_threats -= d
for(var/N in nuke_tiles)
var/turf/open/floor/circuit/C = N
C.update_appearance()
/datum/controller/subsystem/mapping/proc/determine_fake_sale()
if(length(SSmapping.levels_by_all_traits(list(ZTRAIT_STATION, ZTRAIT_NOPARALLAX))))
GLOB.arcade_prize_pool += /obj/item/stack/tile/fakeice/loaded
else
GLOB.arcade_prize_pool += /obj/item/stack/tile/fakespace/loaded
/datum/controller/subsystem/mapping/Recover()
flags |= SS_NO_INIT
initialized = SSmapping.initialized
map_templates = SSmapping.map_templates
ruins_templates = SSmapping.ruins_templates
for (var/theme in SSmapping.themed_ruins)
themed_ruins[theme] = SSmapping.themed_ruins[theme]
shuttle_templates = SSmapping.shuttle_templates
shelter_templates = SSmapping.shelter_templates
unused_turfs = SSmapping.unused_turfs
turf_reservations = SSmapping.turf_reservations
used_turfs = SSmapping.used_turfs
holodeck_templates = SSmapping.holodeck_templates
transit = SSmapping.transit
areas_in_z = SSmapping.areas_in_z
config = SSmapping.config
next_map_config = SSmapping.next_map_config
clearing_reserved_turfs = SSmapping.clearing_reserved_turfs
z_list = SSmapping.z_list
#define INIT_ANNOUNCE(X) to_chat(world, span_boldannounce("[X]")); log_world(X)
/datum/controller/subsystem/mapping/proc/LoadGroup(list/errorList, name, path, files, list/traits, list/default_traits, silent = FALSE)
. = list()
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
var/list/parsed_maps = list()
for (var/file in files)
var/full_path = "_maps/[path]/[file]"
var/datum/parsed_map/pm = new(file(full_path))
var/bounds = pm?.bounds
if (!bounds)
errorList |= full_path
continue
parsed_maps[pm] = total_z // save the start Z of this file
total_z += bounds[MAP_MAXZ] - bounds[MAP_MINZ] + 1
if (!length(traits)) // null or empty - default
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/P in parsed_maps)
var/datum/parsed_map/pm = P
if (!pm.load(1, 1, start_z + parsed_maps[P], no_changeturf = TRUE))
errorList |= pm.original_path
if(!silent)
INIT_ANNOUNCE("Loaded [name] in [(REALTIMEOFDAY - start_time)/10]s!")
return parsed_maps
/datum/controller/subsystem/mapping/proc/loadWorld()
//if any of these fail, something has gone horribly, HORRIBLY, wrong
var/list/FailedZs = list()
// ensure we have space_level datums for compiled-in maps
InitializeDefaultZLevels()
// 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/db_query/query_round_map_name = SSdbcore.NewQuery({"
UPDATE [format_table_name("round")] SET map_name = :map_name WHERE id = :round_id
"}, list("map_name" = config.map_name, "round_id" = GLOB.round_id))
query_round_map_name.Execute()
qdel(query_round_map_name)
#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 < config.space_ruin_levels)
++space_levels_so_far
add_new_zlevel("Empty Area [space_levels_so_far]", ZTRAITS_SPACE)
if(config.minetype == "lavaland")
LoadGroup(FailedZs, "Lavaland", "map_files/Mining", "Lavaland.dmm", default_traits = ZTRAITS_LAVALAND)
else if (!isnull(config.minetype) && config.minetype != "none")
INIT_ANNOUNCE("WARNING: An unknown minetype '[config.minetype]' was set! This is being ignored! Update the maploader code!")
#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]]"
if(FailedZs.len > 1)
for(var/I in 2 to FailedZs.len)
msg += ", [FailedZs[I]]"
msg += ". Yell at your server host!"
INIT_ANNOUNCE(msg)
#undef INIT_ANNOUNCE
// Custom maps are removed after station loading so the map files does not persist for no reason.
if(config.map_path == CUSTOM_MAP_PATH)
fdel("_maps/custom/[config.map_file]")
// And as the file is now removed set the next map to default.
next_map_config = load_default_map_config()
GLOBAL_LIST_EMPTY(the_station_areas)
/datum/controller/subsystem/mapping/proc/generate_station_area_list()
var/static/list/station_areas_blacklist = typecacheof(list(/area/space, /area/mine, /area/ruin, /area/asteroid/nearstation))
for(var/area/A in world)
if (is_type_in_typecache(A, station_areas_blacklist))
continue
if (!A.contents.len || !(A.area_flags & UNIQUE_AREA))
continue
var/turf/picked = A.contents[1]
if (is_station_level(picked.z))
GLOB.the_station_areas += A.type
if(!GLOB.the_station_areas.len)
log_world("ERROR: Station areas list failed to generate!")
/datum/controller/subsystem/mapping/proc/run_map_generation()
for(var/area/A in world)
A.RunGeneration()
/datum/controller/subsystem/mapping/proc/maprotate()
if(map_voted || SSmapping.next_map_config) //If voted or set by other means.
return
var/players = GLOB.clients.len
var/list/mapvotes = list()
//count votes
var/pmv = CONFIG_GET(flag/preference_map_voting)
if(pmv)
for (var/client/c in GLOB.clients)
var/vote = c.prefs.read_preference(/datum/preference/choiced/preferred_map)
if (!vote)
if (global.config.defaultmap)
mapvotes[global.config.defaultmap.map_name] += 1
continue
mapvotes[vote] += 1
else
for(var/M in global.config.maplist)
mapvotes[M] = 1
//filter votes
for (var/map in mapvotes)
if (!map)
mapvotes.Remove(map)
continue
if (!(map in global.config.maplist))
mapvotes.Remove(map)
continue
if(map in SSpersistence.blocked_maps)
mapvotes.Remove(map)
continue
var/datum/map_config/VM = global.config.maplist[map]
if (!VM)
mapvotes.Remove(map)
continue
if (VM.voteweight <= 0)
mapvotes.Remove(map)
continue
if (VM.config_min_users > 0 && players < VM.config_min_users)
mapvotes.Remove(map)
continue
if (VM.config_max_users > 0 && players > VM.config_max_users)
mapvotes.Remove(map)
continue
if(pmv)
mapvotes[map] = mapvotes[map]*VM.voteweight
var/pickedmap = pick_weight(mapvotes)
if (!pickedmap)
return
var/datum/map_config/VM = global.config.maplist[pickedmap]
message_admins("Randomly rotating map to [VM.map_name]")
. = changemap(VM)
if (. && VM.map_name != config.map_name)
to_chat(world, span_boldannounce("Map rotation has chosen [VM.map_name] for next round!"))
/datum/controller/subsystem/mapping/proc/mapvote()
if(map_voted || SSmapping.next_map_config) //If voted or set by other means.
return
if(SSvote.mode) //Theres already a vote running, default to rotation.
maprotate()
SSvote.initiate_vote("map", "automatic map rotation")
/datum/controller/subsystem/mapping/proc/changemap(datum/map_config/VM)
if(!VM.MakeNextMap())
next_map_config = load_default_map_config()
message_admins("Failed to set new map with next_map.json for [VM.map_name]! Using default as backup!")
return
if (VM.config_min_users > 0 && GLOB.clients.len < VM.config_min_users)
message_admins("[VM.map_name] was chosen for the next map, despite there being less current players than its set minimum population range!")
log_game("[VM.map_name] was chosen for the next map, despite there being less current players than its set minimum population range!")
if (VM.config_max_users > 0 && GLOB.clients.len > VM.config_max_users)
message_admins("[VM.map_name] was chosen for the next map, despite there being more current players than its set maximum population range!")
log_game("[VM.map_name] was chosen for the next map, despite there being more current players than its set maximum population range!")
next_map_config = VM
return TRUE
/datum/controller/subsystem/mapping/proc/preloadTemplates(path = "_maps/templates/") //see master controller setup
var/list/filelist = flist(path)
for(var/map in filelist)
var/datum/map_template/T = new(path = "[path][map]", rename = "[map]")
map_templates[T.name] = T
preloadRuinTemplates()
preloadShuttleTemplates()
preloadShelterTemplates()
preloadHolodeckTemplates()
/datum/controller/subsystem/mapping/proc/preloadRuinTemplates()
// Still supporting bans by filename
var/list/banned = generateMapList("lavaruinblacklist.txt")
banned += generateMapList("spaceruinblacklist.txt")
banned += generateMapList("iceruinblacklist.txt")
for(var/item in sort_list(subtypesof(/datum/map_template/ruin), /proc/cmp_ruincost_priority))
var/datum/map_template/ruin/ruin_type = item
// screen out the abstract subtypes
if(!initial(ruin_type.id))
continue
var/datum/map_template/ruin/R = new ruin_type()
if(banned.Find(R.mappath))
continue
map_templates[R.name] = R
ruins_templates[R.name] = R
if (!(R.ruin_type in themed_ruins))
themed_ruins[R.ruin_type] = list()
themed_ruins[R.ruin_type][R.name] = R
/datum/controller/subsystem/mapping/proc/preloadShuttleTemplates()
var/list/unbuyable = generateMapList("unbuyableshuttles.txt")
for(var/item in subtypesof(/datum/map_template/shuttle))
var/datum/map_template/shuttle/shuttle_type = item
if(!(initial(shuttle_type.suffix)))
continue
var/datum/map_template/shuttle/S = new shuttle_type()
if(unbuyable.Find(S.mappath))
S.who_can_purchase = null
shuttle_templates[S.shuttle_id] = S
map_templates[S.shuttle_id] = S
/datum/controller/subsystem/mapping/proc/preloadShelterTemplates()
for(var/item in subtypesof(/datum/map_template/shelter))
var/datum/map_template/shelter/shelter_type = item
if(!(initial(shelter_type.mappath)))
continue
var/datum/map_template/shelter/S = new shelter_type()
shelter_templates[S.shelter_id] = S
map_templates[S.shelter_id] = S
/datum/controller/subsystem/mapping/proc/preloadHolodeckTemplates()
for(var/item in subtypesof(/datum/map_template/holodeck))
var/datum/map_template/holodeck/holodeck_type = item
if(!(initial(holodeck_type.mappath)))
continue
var/datum/map_template/holodeck/holo_template = new holodeck_type()
holodeck_templates[holo_template.template_id] = holo_template
//Manual loading of away missions.
/client/proc/admin_away()
set name = "Load Away Mission"
set category = "Admin.Events"
if(!holder ||!check_rights(R_FUN))
return
if(!GLOB.the_gateway)
if(tgui_alert(usr, "There's no home gateway on the station. You sure you want to continue ?", "Uh oh", list("Yes", "No")) != "Yes")
return
var/list/possible_options = GLOB.potentialRandomZlevels + "Custom"
var/away_name
var/datum/space_level/away_level
var/secret = FALSE
if(tgui_alert(usr, "Do you want your mission secret? (This will prevent ghosts from looking at your map in any way other than through a living player's eyes.)", "Are you $$$ekret?", list("Yes", "No")) == "Yes")
secret = TRUE
var/answer = input("What kind?","Away") as null|anything in possible_options
switch(answer)
if("Custom")
var/mapfile = input("Pick file:", "File") as null|file
if(!mapfile)
return
away_name = "[mapfile] custom"
to_chat(usr,span_notice("Loading [away_name]..."))
var/datum/map_template/template = new(mapfile, "Away Mission")
away_level = template.load_new_z(secret)
else
if(answer in GLOB.potentialRandomZlevels)
away_name = answer
to_chat(usr,span_notice("Loading [away_name]..."))
var/datum/map_template/template = new(away_name, "Away Mission")
away_level = template.load_new_z(secret)
else
return
message_admins("Admin [key_name_admin(usr)] has loaded [away_name] away mission.")
log_admin("Admin [key_name(usr)] has loaded [away_name] away mission.")
if(!away_level)
message_admins("Loading [away_name] failed!")
return
/datum/controller/subsystem/mapping/proc/RequestBlockReservation(width, height, z, type = /datum/turf_reservation, turf_type_override)
UNTIL((!z || reservation_ready["[z]"]) && !clearing_reserved_turfs)
var/datum/turf_reservation/reserve = new type
if(turf_type_override)
reserve.turf_type = turf_type_override
if(!z)
for(var/i in levels_by_trait(ZTRAIT_RESERVED))
if(reserve.Reserve(width, height, i))
return reserve
//If we didn't return at this point, theres a good chance we ran out of room on the exisiting reserved z levels, so lets try a new one
num_of_res_levels += 1
var/datum/space_level/newReserved = add_new_zlevel("Transit/Reserved [num_of_res_levels]", list(ZTRAIT_RESERVED = TRUE))
initialize_reserved_level(newReserved.z_value)
if(reserve.Reserve(width, height, newReserved.z_value))
return reserve
else
if(!level_trait(z, ZTRAIT_RESERVED))
qdel(reserve)
return
else
if(reserve.Reserve(width, height, z))
return reserve
QDEL_NULL(reserve)
//This is not for wiping reserved levels, use wipe_reservations() for that.
/datum/controller/subsystem/mapping/proc/initialize_reserved_level(z)
UNTIL(!clearing_reserved_turfs) //regardless, lets add a check just in case.
clearing_reserved_turfs = TRUE //This operation will likely clear any existing reservations, so lets make sure nothing tries to make one while we're doing it.
if(!level_trait(z,ZTRAIT_RESERVED))
clearing_reserved_turfs = FALSE
CRASH("Invalid z level prepared for reservations.")
var/turf/A = get_turf(locate(SHUTTLE_TRANSIT_BORDER,SHUTTLE_TRANSIT_BORDER,z))
var/turf/B = get_turf(locate(world.maxx - SHUTTLE_TRANSIT_BORDER,world.maxy - SHUTTLE_TRANSIT_BORDER,z))
var/block = block(A, B)
for(var/t in block)
// No need to empty() these, because it's world init and they're
// already /turf/open/space/basic.
var/turf/T = t
T.flags_1 |= UNUSED_RESERVATION_TURF
unused_turfs["[z]"] = block
reservation_ready["[z]"] = TRUE
clearing_reserved_turfs = FALSE
/datum/controller/subsystem/mapping/proc/reserve_turfs(list/turfs)
for(var/i in turfs)
var/turf/T = i
T.empty(RESERVED_TURF_TYPE, RESERVED_TURF_TYPE, null, TRUE)
LAZYINITLIST(unused_turfs["[T.z]"])
unused_turfs["[T.z]"] |= T
T.flags_1 |= UNUSED_RESERVATION_TURF
GLOB.areas_by_type[world.area].contents += T
CHECK_TICK
//DO NOT CALL THIS PROC DIRECTLY, CALL wipe_reservations().
/datum/controller/subsystem/mapping/proc/do_wipe_turf_reservations()
PRIVATE_PROC(TRUE)
UNTIL(initialized) //This proc is for AFTER init, before init turf reservations won't even exist and using this will likely break things.
for(var/i in turf_reservations)
var/datum/turf_reservation/TR = i
if(!QDELETED(TR))
qdel(TR, TRUE)
UNSETEMPTY(turf_reservations)
var/list/clearing = list()
for(var/l in unused_turfs) //unused_turfs is an assoc list by z = list(turfs)
if(islist(unused_turfs[l]))
clearing |= unused_turfs[l]
clearing |= used_turfs //used turfs is an associative list, BUT, reserve_turfs() can still handle it. If the code above works properly, this won't even be needed as the turfs would be freed already.
unused_turfs.Cut()
used_turfs.Cut()
reserve_turfs(clearing)
///Initialize all biomes, assoc as type || instance
/datum/controller/subsystem/mapping/proc/initialize_biomes()
for(var/biome_path in subtypesof(/datum/biome))
var/datum/biome/biome_instance = new biome_path()
biomes[biome_path] += biome_instance
/datum/controller/subsystem/mapping/proc/reg_in_areas_in_z(list/areas)
for(var/B in areas)
var/area/A = B
A.reg_in_areas_in_z()
/datum/controller/subsystem/mapping/proc/get_isolated_ruin_z()
if(!isolated_ruins_z)
isolated_ruins_z = add_new_zlevel("Isolated Ruins/Reserved", list(ZTRAIT_RESERVED = TRUE, ZTRAIT_ISOLATED_RUINS = TRUE))
initialize_reserved_level(isolated_ruins_z.z_value)
return isolated_ruins_z.z_value
/datum/controller/subsystem/mapping/proc/spawn_maintenance_loot()
for(var/obj/effect/spawner/random/maintenance/spawner as anything in GLOB.maintenance_loot_spawners)
CHECK_TICK
spawner.spawn_loot()
qdel(spawner)