mirror of
https://github.com/Aurorastation/Aurora.3.git
synced 2026-07-26 07:17:49 +01:00
acd8c6d9d5
Ports https://github.com/tgstation/tgstation/pull/38098 and https://github.com/tgstation/tgstation/pull/77786 Prereq for shuttle rotation. No player-facing changes.
278 lines
11 KiB
Plaintext
278 lines
11 KiB
Plaintext
SUBSYSTEM_DEF(mapping)
|
|
name = "Mapping"
|
|
init_order = INIT_ORDER_AWAY_MAPS
|
|
flags = SS_NO_FIRE
|
|
|
|
var/list/map_templates = list()
|
|
var/list/space_ruins_templates = list()
|
|
var/list/exoplanet_ruins_templates = list()
|
|
var/list/away_sites_templates = list()
|
|
var/list/submaps = list()
|
|
|
|
/// Not actually unused turfs they're unused but reserved for use for whatever requests them. "[zlevel_of_turf]" = list(turfs)
|
|
var/list/unused_turfs = list()
|
|
/// List of actual datum/turf_reservations - reservation-centric
|
|
var/list/turf_reservations = list()
|
|
/// List of turfs that are currently reserved and in use - turf-centric, used to get the reservations from turfs
|
|
var/list/used_turfs = list()
|
|
var/list/reservation_ready = list()
|
|
var/clearing_reserved_turfs = FALSE
|
|
var/num_of_res_levels = 0
|
|
|
|
/// List of z level (as number) -> list of all z levels vertically connected to ours
|
|
/// Useful for fast grouping lookups and such
|
|
var/list/z_level_to_stack = list()
|
|
|
|
///list of all z level datums in the order of their z (z level 1 is at index 1, etc.)
|
|
var/list/datum/space_level/z_list = list()
|
|
|
|
///list of all z level indices that form multiz connections and whether theyre linked up or down.
|
|
///list of lists, inner lists are of the form: list("up or down link direction" = TRUE)
|
|
var/list/multiz_levels = list()
|
|
|
|
/// list of traits and their associated z leves
|
|
var/list/z_trait_levels = list()
|
|
|
|
/// True when in the process of adding a new Z-level, global locking
|
|
var/adding_new_zlevel = FALSE
|
|
|
|
/datum/controller/subsystem/mapping/Initialize(timeofday)
|
|
//If we're in fastboot and not spawning exoplanets or awaysites
|
|
//this is different from TG and Bay, which always preload, but it saves a lot of time for us
|
|
//so we'll do it this way and hope for the best
|
|
if(!GLOB.config.fastboot || GLOB.config.exoplanets["enable_loading"] || GLOB.config.awaysites["enable_loading"])
|
|
// Load templates and build away sites.
|
|
preloadTemplates()
|
|
|
|
SSatlas.current_map.build_away_sites()
|
|
SSatlas.current_map.build_exoplanets()
|
|
|
|
return SS_INIT_SUCCESS
|
|
|
|
/datum/controller/subsystem/mapping/Recover()
|
|
flags |= SS_NO_INIT
|
|
map_templates = SSmapping.map_templates
|
|
space_ruins_templates = SSmapping.space_ruins_templates
|
|
exoplanet_ruins_templates = SSmapping.exoplanet_ruins_templates
|
|
away_sites_templates = SSmapping.away_sites_templates
|
|
unused_turfs = SSmapping.unused_turfs
|
|
turf_reservations = SSmapping.turf_reservations
|
|
used_turfs = SSmapping.used_turfs
|
|
reservation_ready = SSmapping.reservation_ready
|
|
clearing_reserved_turfs = SSmapping.clearing_reserved_turfs
|
|
num_of_res_levels = SSmapping.num_of_res_levels
|
|
|
|
/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.id] = T
|
|
preloadBlacklistableTemplates()
|
|
|
|
/datum/controller/subsystem/mapping/proc/preloadBlacklistableTemplates()
|
|
// Still supporting bans by filename
|
|
var/list/banned_exoplanet_dmms = generateMapList("config/exoplanet_ruin_blacklist.txt")
|
|
var/list/banned_space_dmms = generateMapList("config/space_ruin_blacklist.txt")
|
|
var/list/banned_away_site_dmms = generateMapList("config/away_site_blacklist.txt")
|
|
|
|
if (!banned_exoplanet_dmms || !banned_space_dmms || !banned_away_site_dmms)
|
|
log_admin("One or more map blacklist files are not present in the config directory!")
|
|
|
|
var/list/banned_maps = list() + banned_exoplanet_dmms + banned_space_dmms + banned_away_site_dmms
|
|
|
|
for(var/item in sortList(subtypesof(/datum/map_template), GLOBAL_PROC_REF(cmp_ruincost_priority)))
|
|
var/datum/map_template/map_template_type = item
|
|
// screen out the abstract subtypes
|
|
if(!initial(map_template_type.id))
|
|
continue
|
|
var/datum/map_template/MT = new map_template_type()
|
|
|
|
if (banned_maps)
|
|
if(banned_maps.Find(MT.mappath))
|
|
continue
|
|
|
|
map_templates[MT.id] = MT
|
|
|
|
// This is nasty..
|
|
if(istype(MT, /datum/map_template/ruin/exoplanet))
|
|
exoplanet_ruins_templates[MT.id] = MT
|
|
else if(istype(MT, /datum/map_template/ruin/space))
|
|
space_ruins_templates[MT.id] = MT
|
|
else if(istype(MT, /datum/map_template/ruin/away_site))
|
|
away_sites_templates[MT.id] = MT
|
|
|
|
/datum/controller/subsystem/mapping/proc/generate_linkages_for_z_level(z_level)
|
|
if(!isnum(z_level) || z_level <= 0)
|
|
return FALSE
|
|
|
|
if(multiz_levels.len < z_level)
|
|
multiz_levels.len = z_level
|
|
|
|
var/z_above = level_trait(z_level, ZTRAIT_UP)
|
|
var/z_below = level_trait(z_level, ZTRAIT_DOWN)
|
|
if(!(z_above == TRUE || z_above == FALSE || z_above == null) || !(z_below == TRUE || z_below == FALSE || z_below == null))
|
|
stack_trace("Warning, numeric mapping offsets are deprecated. Instead, mark z level connections by setting UP/DOWN to true if the connection is allowed")
|
|
multiz_levels[z_level] = new /list(LARGEST_Z_LEVEL_INDEX)
|
|
multiz_levels[z_level][Z_LEVEL_UP] = !!z_above
|
|
multiz_levels[z_level][Z_LEVEL_DOWN] = !!z_below
|
|
|
|
/// Takes a z level datum, and tells the mapping subsystem to manage it
|
|
/// Also handles things like plane offset generation, and other things that happen on a z level to z level basis
|
|
/datum/controller/subsystem/mapping/proc/manage_z_level(datum/space_level/new_z, filled_with_space, contain_turfs = TRUE)
|
|
// First, add the z
|
|
z_list += new_z
|
|
|
|
// Then we build our lookup lists
|
|
var/z_value = new_z.z_value
|
|
|
|
z_level_to_stack.len += 1
|
|
// Bare minimum we have ourselves
|
|
z_level_to_stack[z_value] = list(z_value)
|
|
|
|
/datum/controller/subsystem/mapping/proc/get_reservation_from_turf(turf/T)
|
|
RETURN_TYPE(/datum/turf_reservation)
|
|
return used_turfs[T]
|
|
|
|
/// Adds a new reservation z level. A bit of space that can be handed out on request
|
|
/// Of note, reservations default to transit turfs, to make their most common use, shuttles, faster
|
|
/datum/controller/subsystem/mapping/proc/add_reservation_zlevel(for_shuttles)
|
|
num_of_res_levels++
|
|
return add_new_zlevel("Transit/Reserved #[num_of_res_levels]", list(ZTRAIT_RESERVED = TRUE), contain_turfs = FALSE)
|
|
|
|
/// Sets up a z level as reserved
|
|
/// This is not for wiping reserved levels, use wipe_reservations() for that.
|
|
/// If this is called after SSatom init, it will call Initialize on all turfs on the passed z, as its name promises
|
|
/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/min_x = SHUTTLE_TRANSIT_BORDER
|
|
var/min_y = SHUTTLE_TRANSIT_BORDER
|
|
var/max_x = world.maxx - SHUTTLE_TRANSIT_BORDER
|
|
var/max_y = world.maxy - SHUTTLE_TRANSIT_BORDER
|
|
if(min_x > max_x || min_y > max_y)
|
|
clearing_reserved_turfs = FALSE
|
|
CRASH("World bounds are too small for dynamic reservations with SHUTTLE_TRANSIT_BORDER [SHUTTLE_TRANSIT_BORDER].")
|
|
|
|
var/list/reserved_block = block(locate(min_x, min_y, z), locate(max_x, max_y, z))
|
|
var/list/prepared_block = list()
|
|
var/area/space/space_area = locate(/area/space)
|
|
for(var/turf/T as anything in reserved_block)
|
|
var/turf/reservation_turf = reset_turf_for_reservation(T, space_area)
|
|
if(!reservation_turf)
|
|
continue
|
|
reservation_turf.turf_flags = (reservation_turf.turf_flags | UNUSED_RESERVATION_TURF) & ~RESERVATION_TURF
|
|
prepared_block += reservation_turf
|
|
CHECK_TICK
|
|
|
|
// Gotta create these suckers if we've not done so already
|
|
if(SSatoms.initialized)
|
|
SSatoms.InitializeAtoms(Z_TURFS(z))
|
|
|
|
unused_turfs["[z]"] = prepared_block
|
|
reservation_ready["[z]"] = TRUE
|
|
clearing_reserved_turfs = FALSE
|
|
|
|
/// Requests a /datum/turf_reservation based on the given width, height, and z_size. You can specify a z_reservation to use a specific z level, or leave it null to use any z level.
|
|
/datum/controller/subsystem/mapping/proc/request_turf_block_reservation(
|
|
width,
|
|
height,
|
|
z_size = 1,
|
|
z_reservation = null,
|
|
reservation_type = /datum/turf_reservation,
|
|
turf_type_override = null,
|
|
)
|
|
UNTIL(!clearing_reserved_turfs)
|
|
|
|
var/datum/turf_reservation/reserve = new reservation_type
|
|
if(!isnull(turf_type_override))
|
|
reserve.turf_type = turf_type_override
|
|
|
|
if(!z_reservation)
|
|
for(var/reserved_z in levels_by_trait(ZTRAIT_RESERVED))
|
|
if(!reservation_ready["[reserved_z]"])
|
|
continue
|
|
if(reserve.reserve(width, height, z_size, reserved_z))
|
|
return reserve
|
|
|
|
// If we didn't return at this point, theres a good chance we ran out of room on the existing reserved z levels, so lets try a new one
|
|
var/datum/space_level/newReserved = add_reservation_zlevel()
|
|
initialize_reserved_level(newReserved.z_value)
|
|
if(reserve.reserve(width, height, z_size, newReserved.z_value))
|
|
return reserve
|
|
else
|
|
if(!level_trait(z_reservation, ZTRAIT_RESERVED))
|
|
qdel(reserve)
|
|
return
|
|
if(!reservation_ready["[z_reservation]"])
|
|
initialize_reserved_level(z_reservation)
|
|
if(reserve.reserve(width, height, z_size, z_reservation))
|
|
return reserve
|
|
|
|
QDEL_NULL(reserve)
|
|
|
|
/// Schedules a group of turfs to be handed back to the reservation system's control
|
|
/// If await is true, will sleep until the turfs are finished work
|
|
/datum/controller/subsystem/mapping/proc/reserve_turfs(list/turfs, await = FALSE)
|
|
if(!islist(turfs) || !length(turfs))
|
|
return
|
|
if(await)
|
|
return reset_turf_reservation_list(turfs)
|
|
INVOKE_ASYNC(src, PROC_REF(reset_turf_reservation_list), turfs)
|
|
|
|
/datum/controller/subsystem/mapping/proc/reset_turf_reservation_list(list/turfs)
|
|
var/area/space/space_area = locate(/area/space)
|
|
while(length(turfs))
|
|
var/turf/T = turfs[length(turfs)]
|
|
turfs.len--
|
|
if(!istype(T))
|
|
continue
|
|
var/turf/reservation_turf = reset_turf_for_reservation(T, space_area)
|
|
if(!reservation_turf)
|
|
continue
|
|
reservation_turf.turf_flags = (reservation_turf.turf_flags | UNUSED_RESERVATION_TURF) & ~RESERVATION_TURF
|
|
LAZYINITLIST(unused_turfs["[reservation_turf.z]"])
|
|
unused_turfs["[reservation_turf.z]"] |= reservation_turf
|
|
CHECK_TICK
|
|
|
|
/datum/controller/subsystem/mapping/proc/reset_turf_for_reservation(turf/T, area/target_area)
|
|
RETURN_TYPE(/turf)
|
|
if(!istype(T))
|
|
return
|
|
if(T.type != RESERVED_TURF_TYPE)
|
|
T = T.ChangeTurf(RESERVED_TURF_TYPE, TRUE, FALSE, TRUE)
|
|
if(target_area && T.loc != target_area)
|
|
T.change_area(T.loc, target_area)
|
|
T.baseturf = RESERVED_TURF_TYPE
|
|
T.blocks_air = TRUE
|
|
return T
|
|
|
|
|
|
/proc/generateMapList(filename)
|
|
var/list/potentialMaps = list()
|
|
var/list/Lines = world.file2list(filename)
|
|
if(!Lines.len)
|
|
return
|
|
for (var/t in Lines)
|
|
if (!t)
|
|
continue
|
|
t = trim(t)
|
|
if (length(t) == 0)
|
|
continue
|
|
else if (copytext(t, 1, 2) == "#")
|
|
continue
|
|
var/pos = findtext(t, " ")
|
|
var/name = null
|
|
if (pos)
|
|
name = lowertext(copytext(t, 1, pos))
|
|
else
|
|
name = lowertext(t)
|
|
if (!name)
|
|
continue
|
|
potentialMaps.Add(t)
|
|
return potentialMaps
|