mirror of
https://github.com/Citadel-Station-13/Citadel-Station-13-RP.git
synced 2025-12-10 15:27:21 +00:00
* 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.
408 lines
15 KiB
Plaintext
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
|