Files
Citadel-Station-13-RP/code/controllers/subsystem/mapping/_mapping.dm
Zandario 5a7f357b85 CitadelRP (#4108)
* dme and dmb

* Heeeere we gooo

* fasease

* Continued.

* AAAAAAAAAAAAAAAAAAAAAAAA

* Brain going numb

* agh

* The suffering will be worth it... right?

* Going blank...

* Am I done?

* AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

* Fixes

* Should be everything I care about right now.
2022-05-26 01:37:55 -07:00

408 lines
15 KiB
Plaintext

#define INIT_ANNOUNCE(X) to_chat(world, "<span class='boldannounce'>[X]</span>"); log_world(X)
GLOBAL_VAR_INIT(used_engine, "None")
// Handles map-related tasks, mostly here to ensure it does so after the MC initializes.
SUBSYSTEM_DEF(mapping)
name = "Mapping"
init_order = INIT_ORDER_MAPPING
subsystem_flags = SS_NO_FIRE
var/list/areas_in_z = list()
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
/// Zlevel manager list of zlevels.
var/list/z_list
var/datum/space_level/transit
var/num_of_res_levels = 1
/// The current map config datum the round is using
var/static/datum/map_config/config
/// The next map to load
var/static/datum/map_config/next_map_config
/// Cached map name for statpanel
var/static/stat_map_name = "Loading..."
// Obfuscation Module
/// "secret" key
var/obfuscation_secret
//dlete dis once #39770 is resolved
/datum/controller/subsystem/mapping/proc/HACK_LoadMapConfig()
if(!config)
#ifdef FORCE_MAP
config = load_map_config(FORCE_MAP)
#else
config = load_map_config(error_if_missing = FALSE)
#endif
stat_map_name = config.map_name
/datum/controller/subsystem/mapping/Initialize(timeofday)
HACK_LoadMapConfig()
if(initialized)
return
if(config.defaulted)
var/old_config = config
config = global.config.defaultmap
if(!config || config.defaulted)
to_chat(world, "<span class='boldannounce'>Unable to load next or default map config, defaulting to Tethermap</span>")
config = old_config
loadWorld()
repopulate_sorted_areas()
world.max_z_changed() // This is to set up the player z-level list, maxz hasn't actually changed (probably)
maploader = new()
load_map_templates()
loadEngine()
preloadShelterTemplates()
// Mining generation probably should be here too
GLOB.using_map.perform_map_generation()
// TODO - Other stuff related to maps and areas could be moved here too. Look at /tg
if(GLOB.using_map)
loadLateMaps()
if(!GLOB.using_map.overmap_z)
build_overmap()
// basemap - REEVALUATE when runtime maploading is in
transit = z_list[1]
initialize_reserved_level(transit.z_value)
// initialize_reserved_level(transit.z_value)
// Set up antagonists.
populate_antag_type_list()
//Set up spawn points.
populate_spawn_points()
repopulate_sorted_areas()
return ..()
/datum/controller/subsystem/mapping/proc/LoadGroup(list/errorList, name, path, files, list/traits, list/default_traits, silent = FALSE, orientation = SOUTH)
/*
. = 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, orientation = orientation))
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()
// Until we have runtime maploading, just load traits from config
if(!config)
INIT_ANNOUNCE("WARNING: FAILED TO LOAD MAP CONFIG. THIS ROUND WILL BREAK. REBOOTING WILL LIKELY NOT FIX IT AT THIS POINT, CONTACT A MAINTAINER IMMEDIATELY.")
return
else
z_list = list()
add_new_zlevel("RESERVED LEVEL", list(ZTRAIT_RESERVED = TRUE))
var/current = 0
if((length(config.traits) + 1) != world.maxz)
INIT_ANNOUNCE("WARNING: MAP CONFIG TRAIT LIST MISMATCHES WITH ZCOUNT ([length(config.traits)] vs [world.maxz - 1] actual). ERRORS WILL OCCUR.")
for(var/i in config.traits)
var/list/traits = i
current++
if(!istype(traits))
INIT_ANNOUNCE("WARNING: ERROR DETECTED IN ZTRAIT LIST.")
add_new_zlevel("ERROR - Z [current]", list())
else
add_new_zlevel(traits[ZTRAIT_NAME] || "[config.map_name] [current]", traits)
if(length(z_list) != world.maxz)
INIT_ANNOUNCE("WARNING: Z_LIST LENGTH [length(z_list)] NOT MATCHING ZCOUNT [world.maxz]. THIS ROUND WILL BREAK. REBOOTING WILL LIKELY NOT FIX IT AT THIS POINT, CONTACT A MAINTAINER IMMEDIATELY.")
/*
//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, FALSE, config.orientation)
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()
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)
// load mining
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)
*/
/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)
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)
*/
/datum/controller/subsystem/mapping/proc/RequestBlockReservation(width, height, z, type = /datum/turf_reservation, turf_type_override, border_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(border_type_override)
reserve.borderturf = border_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/space/basic.
var/turf/T = t
T.turf_flags |= 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.turf_flags |= 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()
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 a 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)
//
// Mapping subsystem handles initialization of random map elements at server start
// For us that means loading our random roundstart engine!
//
/datum/controller/subsystem/mapping
var/list/map_templates = list()
var/dmm_suite/maploader = null
var/atom/movable/landmark/engine_loader/engine_loader
var/list/shelter_templates = list()
/datum/controller/subsystem/mapping/Recover()
subsystem_flags |= SS_NO_INIT // Make extra sure we don't initialize twice.
shelter_templates = SSmapping.shelter_templates
/datum/controller/subsystem/mapping/proc/load_map_templates()
for(var/T in subtypesof(/datum/map_template))
var/datum/map_template/template = T
template = new T
if(!template.mappath)
qdel(template)
continue
map_templates[template.name] = template
return TRUE
/datum/controller/subsystem/mapping/proc/loadEngine()
if(!engine_loader)
return // Seems this map doesn't need an engine loaded.
var/turf/T = get_turf(engine_loader)
if(!isturf(T))
subsystem_log("[log_info_line(engine_loader)] not on a turf! Cannot place engine template.")
return
// Choose an engine type
var/datum/map_template/engine/chosen_type = null
var/list/probabilities = CONFIG_GET(keyed_list/engine_submap)
if (length(probabilities))
var/chosen_name = lowertext(pickweightAllowZero(probabilities))
for(var/mapname in map_templates)
// yeah yeah yeah inefficient fight me someone can code me a better subsystem if they want to bother
if(lowertext(mapname) == chosen_name)
chosen_type = map_templates[mapname]
if(!istype(chosen_type))
log_config("Configured engine map [chosen_name] is not a valid engine map name!")
if(!istype(chosen_type))
var/list/engine_types = list()
for(var/map in map_templates)
var/datum/map_template/engine/MT = map_templates[map]
if(istype(MT))
engine_types += MT
chosen_type = pick(engine_types)
subsystem_log("Chose Engine Map: [chosen_type.name]")
admin_notice("<span class='danger'>Chose Engine Map: [chosen_type.name]</span>", R_DEBUG)
to_chat(world, "<span class='danger'>Engine loaded: [chosen_type.display_name]</span>")
GLOB.used_engine = chosen_type.display_name
// Annihilate movable atoms
engine_loader.annihilate_bounds()
//CHECK_TICK //Don't let anything else happen for now
// Actually load it
chosen_type.load(T)
/datum/controller/subsystem/mapping/proc/loadLateMaps()
var/list/deffo_load = GLOB.using_map.lateload_z_levels
var/list/maybe_load = GLOB.using_map.lateload_single_pick
for(var/list/maplist in deffo_load)
if(!islist(maplist))
log_world("Lateload Z level [maplist] is not a list! Must be in a list!")
continue
for(var/mapname in maplist)
var/datum/map_template/MT = map_templates[mapname]
if(!istype(MT))
log_world("Lateload Z level \"[mapname]\" is not a valid map!")
continue
MT.load_new_z(centered = FALSE)
CHECK_TICK
if(LAZYLEN(maybe_load))
var/picklist = pick(maybe_load)
if(!picklist) //No lateload maps at all
return
if(!islist(picklist)) //So you can have a 'chain' of z-levels that make up one away mission
log_world("Randompick Z level [picklist] is not a list! Must be in a list!")
return
for(var/map in picklist)
var/datum/map_template/MT = map_templates[map]
if(!istype(MT))
log_world("Randompick Z level \"[map]\" is not a valid map!")
else
MT.load_new_z(centered = FALSE)
/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