mirror of
https://github.com/ParadiseSS13/Paradise.git
synced 2026-08-29 23:17:02 +01:00
Ports TG lazy-loading of map templates using turf reservations (#28101)
* kills off heaps * turf reservations * lazy loading map templates * atmos + a comment * port LISTASSERTLEN and rename a proc * CI happy * yes * oops * removes blocks air and adds comments
This commit is contained in:
@@ -13,6 +13,8 @@
|
||||
#define STATION_CONTACT "Station Contact"
|
||||
// A level dedicated to admin use
|
||||
#define ADMIN_LEVEL "Admin Level"
|
||||
// For Z-levels dedicated to auto-spawning stuff in
|
||||
#define Z_FLAG_RESERVED "Reserved"
|
||||
// A level that can be navigated to by the crew without admin intervention or the emergency shuttle.
|
||||
#define REACHABLE_BY_CREW "Reachable"
|
||||
// For away missions - used by some consoles
|
||||
|
||||
@@ -16,23 +16,41 @@
|
||||
#define GLOBAL_PROTECT(X)
|
||||
#endif
|
||||
|
||||
/// Standard BYOND global, seriously do not use without an earthshakingly good reason
|
||||
#define GLOBAL_REAL_VAR(X) var/global/##X
|
||||
|
||||
/// Standard typed BYOND global, seriously do not use without an earthshakingly good reason
|
||||
#define GLOBAL_REAL(X, Typepath) var/global##Typepath/##X
|
||||
|
||||
/// Defines a global var on the controller, do not use
|
||||
#define GLOBAL_RAW(X) /datum/controller/global_vars/var/global##X
|
||||
|
||||
/// Create an untyped global with an initializer expression
|
||||
#define GLOBAL_VAR_INIT(X, InitValue) GLOBAL_RAW(/##X); GLOBAL_MANAGED(X, InitValue)
|
||||
|
||||
/// Create a global const var, do not use
|
||||
#define GLOBAL_VAR_CONST(X, InitValue) GLOBAL_RAW(/const/##X) = InitValue; GLOBAL_UNMANAGED(X)
|
||||
|
||||
/// Create a list global with an initializer expression
|
||||
#define GLOBAL_LIST_INIT(X, InitValue) GLOBAL_RAW(/list/##X); GLOBAL_MANAGED(X, InitValue)
|
||||
|
||||
/// Create a list global that is initialized as an empty list
|
||||
#define GLOBAL_LIST_EMPTY(X) GLOBAL_LIST_INIT(X, list())
|
||||
|
||||
/// Create a typed list global with an initializer expression
|
||||
#define GLOBAL_LIST_INIT_TYPED(X, Typepath, InitValue) GLOBAL_RAW(/list##Typepath/X); GLOBAL_MANAGED(X, InitValue)
|
||||
|
||||
/// Create a typed list global that is initialized as an empty list
|
||||
#define GLOBAL_LIST_EMPTY_TYPED(X, Typepath) GLOBAL_LIST_INIT_TYPED(X, Typepath, list())
|
||||
|
||||
/// Create a typed global with an initializer expression
|
||||
#define GLOBAL_DATUM_INIT(X, Typepath, InitValue) GLOBAL_RAW(Typepath/##X); GLOBAL_MANAGED(X, InitValue)
|
||||
|
||||
/// Create an untyped null global
|
||||
#define GLOBAL_VAR(X) GLOBAL_RAW(/##X); GLOBAL_UNMANAGED(X)
|
||||
|
||||
/// Create a null global list
|
||||
#define GLOBAL_LIST(X) GLOBAL_RAW(/list/##X); GLOBAL_UNMANAGED(X)
|
||||
|
||||
/// Create a typed null global
|
||||
#define GLOBAL_DATUM(X, Typepath) GLOBAL_RAW(Typepath/##X); GLOBAL_UNMANAGED(X)
|
||||
|
||||
@@ -144,12 +144,18 @@
|
||||
#define PASSTAKE (1<<9)
|
||||
#define PASSBARRICADE (1<<10)
|
||||
|
||||
//turf-only flags
|
||||
//turf-only flags, under the flags variable
|
||||
#define BLESSED_TILE (1<<0)
|
||||
#define NO_LAVA_GEN (1<<1) //Blocks lava rivers being generated on the turf
|
||||
#define NO_RUINS (1<<2)
|
||||
#define LAVA_BRIDGE (1<<3) //! This turf has already been reserved for a lavaland bridge placement.
|
||||
|
||||
// turf flags, under the turf_flags variable
|
||||
/// If a turf is an unused reservation turf awaiting assignment
|
||||
#define UNUSED_RESERVATION_TURF (1<<4)
|
||||
/// If a turf is a reserved turf
|
||||
#define RESERVATION_TURF (1<<5)
|
||||
|
||||
//ORGAN TYPE FLAGS
|
||||
#define AFFECT_ROBOTIC_ORGAN 1
|
||||
#define AFFECT_ORGANIC_ORGAN 2
|
||||
|
||||
@@ -14,3 +14,5 @@
|
||||
#define SHUTTLE_DOCKER_LANDING_CLEAR 1
|
||||
#define SHUTTLE_DOCKER_BLOCKED_BY_HIDDEN_PORT 2
|
||||
#define SHUTTLE_DOCKER_BLOCKED 3
|
||||
|
||||
#define SHUTTLE_TRANSIT_BORDER 16
|
||||
|
||||
@@ -13,3 +13,10 @@
|
||||
|
||||
#define MINERAL_PREVENT_DIG 0 //! A mineral turf should not be changed when mined.
|
||||
#define MINERAL_ALLOW_DIG 1 //! A mineral turf should be dug out when mined.
|
||||
|
||||
/// Returns an outline (neighboring turfs) of the given block
|
||||
#define CORNER_OUTLINE(corner, width, height) ( \
|
||||
CORNER_BLOCK_OFFSET(corner, width + 2, 1, -1, -1) + \
|
||||
CORNER_BLOCK_OFFSET(corner, width + 2, 1, -1, height) + \
|
||||
CORNER_BLOCK_OFFSET(corner, 1, height, -1, 0) + \
|
||||
CORNER_BLOCK_OFFSET(corner, 1, height, width, 0))
|
||||
|
||||
@@ -740,6 +740,19 @@
|
||||
LAZYINITLIST(lazy_list[key]); \
|
||||
lazy_list[key] |= value;
|
||||
|
||||
///Ensures the length of a list is at least I, prefilling it with V if needed. if V is a proc call, it is repeated for each new index so that list() can just make a new list for each item.
|
||||
#define LISTASSERTLEN(L, I, V...) \
|
||||
if(length(L) < I) { \
|
||||
var/_OLD_LENGTH = length(L); \
|
||||
L.len = I; \
|
||||
/* Convert the optional argument to a if check */ \
|
||||
for(var/_USELESS_VAR in list(V)) { \
|
||||
for(var/_INDEX_TO_ASSIGN_TO in _OLD_LENGTH+1 to I) { \
|
||||
L[_INDEX_TO_ASSIGN_TO] = V; \
|
||||
} \
|
||||
} \
|
||||
}
|
||||
|
||||
//same, but returns nothing and acts on list in place
|
||||
/proc/shuffle_inplace(list/L)
|
||||
if(!L)
|
||||
|
||||
@@ -112,6 +112,11 @@ DEFINE_BITFIELD(flags, list(
|
||||
"NO_SCREENTIPS" = NO_SCREENTIPS,
|
||||
))
|
||||
|
||||
DEFINE_BITFIELD(turf_flags, list(
|
||||
"UNUSED_RESERVATION_TURF" = UNUSED_RESERVATION_TURF,
|
||||
"RESERVATION_TURF" = RESERVATION_TURF
|
||||
))
|
||||
|
||||
DEFINE_BITFIELD(flags_2, list(
|
||||
"SLOWS_WHILE_IN_HAND_2" = SLOWS_WHILE_IN_HAND_2,
|
||||
"NO_EMP_WIRES_2" = NO_EMP_WIRES_2,
|
||||
|
||||
@@ -18,7 +18,7 @@ GLOBAL_LIST_EMPTY(hierophant_walls)
|
||||
GLOBAL_LIST_EMPTY(pandemics)
|
||||
|
||||
GLOBAL_LIST_EMPTY(all_areas)
|
||||
GLOBAL_LIST_EMPTY(all_unique_areas) // List of all unique areas. AKA areas with there_can_be_many = FALSE
|
||||
GLOBAL_LIST_EMPTY_TYPED(all_unique_areas, /area) // List of all unique areas. AKA areas with there_can_be_many = FALSE
|
||||
GLOBAL_LIST_EMPTY(machines)
|
||||
GLOBAL_LIST_EMPTY(telescreens) /// List of entertainment telescreens connected to the "news" cameranet
|
||||
GLOBAL_LIST_EMPTY(rcd_list) //list of Rapid Construction Devices.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
SUBSYSTEM_DEF(mapping)
|
||||
name = "Mapping"
|
||||
init_order = INIT_ORDER_MAPPING // 9
|
||||
flags = SS_NO_FIRE
|
||||
|
||||
/// What map datum are we using
|
||||
var/datum/map/map_datum
|
||||
/// What map will be used next round
|
||||
@@ -31,6 +31,15 @@ SUBSYSTEM_DEF(mapping)
|
||||
/// Ruin placement manager for lavaland levels.
|
||||
var/datum/ruin_placer/lavaland/lavaland_ruins_placer
|
||||
|
||||
var/num_of_res_levels = 0
|
||||
var/clearing_reserved_turfs = FALSE
|
||||
var/list/datum/turf_reservations //list of turf reservations
|
||||
|
||||
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/used_turfs = list() //list of turf = datum/turf_reservation
|
||||
/// List of lists of turfs to reserve
|
||||
var/list/lists_to_reserve = list()
|
||||
|
||||
// This has to be here because world/New() uses [station_name()], which looks this datum up
|
||||
/datum/controller/subsystem/mapping/PreInit()
|
||||
. = ..()
|
||||
@@ -109,6 +118,8 @@ SUBSYSTEM_DEF(mapping)
|
||||
|
||||
// Makes a blank space level for the sake of randomness
|
||||
GLOB.space_manager.add_new_zlevel("Empty Area", linkage = CROSSLINKED, traits = empty_z_traits)
|
||||
// Add a reserved z-level
|
||||
// add_reservation_zlevel() // CURRENTLY DISABLED, AS NOTHING USES IT. IF YOU WANT TO ADD LAZYLOADING TO ANYTHING, MAKE SURE TO REIMPLEMENT THIS
|
||||
|
||||
// Setup the Z-level linkage
|
||||
GLOB.space_manager.do_transition_setup()
|
||||
@@ -346,4 +357,120 @@ SUBSYSTEM_DEF(mapping)
|
||||
SSblackbox.record_feedback("nested tally", "keycard_auths", 1, list("emergency station access", "disabled"))
|
||||
|
||||
/datum/controller/subsystem/mapping/Recover()
|
||||
flags |= SS_NO_INIT
|
||||
num_of_res_levels = SSmapping.num_of_res_levels
|
||||
clearing_reserved_turfs = SSmapping.clearing_reserved_turfs
|
||||
turf_reservations = SSmapping.turf_reservations
|
||||
unused_turfs = SSmapping.unused_turfs
|
||||
used_turfs = SSmapping.used_turfs
|
||||
lists_to_reserve = SSmapping.lists_to_reserve
|
||||
|
||||
/datum/controller/subsystem/mapping/proc/get_reservation_from_turf(turf/T)
|
||||
RETURN_TYPE(/datum/turf_reservation)
|
||||
return used_turfs[T]
|
||||
|
||||
/// Requests a /datum/turf_reservation based on the given width, height.
|
||||
/datum/controller/subsystem/mapping/proc/request_turf_block_reservation(width, height)
|
||||
UNTIL(!clearing_reserved_turfs)
|
||||
log_debug("Reserving [width]x[height] turf reservation")
|
||||
var/datum/turf_reservation/reserve = new /datum/turf_reservation
|
||||
for(var/i in levels_by_trait(Z_FLAG_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
|
||||
var/z_level_num = add_reservation_zlevel()
|
||||
if(reserve.reserve(width, height, z_level_num))
|
||||
return reserve
|
||||
qdel(reserve)
|
||||
|
||||
/datum/controller/subsystem/mapping/proc/add_reservation_zlevel()
|
||||
num_of_res_levels++
|
||||
// . here is the z of the just added z-level
|
||||
. = GLOB.space_manager.add_new_zlevel("Transit/Reserved #[num_of_res_levels]", traits = list(Z_FLAG_RESERVED, BLOCK_TELEPORT, IMPEDES_MAGIC))
|
||||
initialize_reserved_level(.)
|
||||
if(!initialized)
|
||||
return
|
||||
if(length(SSidlenpcpool.idle_mobs_by_zlevel) == . || !islist(SSidlenpcpool.idle_mobs_by_zlevel)) // arbitrary chosen from these lists that require the length of the z-levels
|
||||
return
|
||||
LISTASSERTLEN(SSidlenpcpool.idle_mobs_by_zlevel, ., list())
|
||||
LISTASSERTLEN(SSmobs.clients_by_zlevel, ., list())
|
||||
LISTASSERTLEN(SSmobs.dead_players_by_zlevel, ., list())
|
||||
|
||||
///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.
|
||||
log_debug("Initializing new reserved Z-level")
|
||||
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(!check_level_trait(z, Z_FLAG_RESERVED))
|
||||
clearing_reserved_turfs = FALSE
|
||||
CRASH("Invalid z level prepared for reservations.")
|
||||
var/block = block(SHUTTLE_TRANSIT_BORDER, SHUTTLE_TRANSIT_BORDER, z, world.maxx - SHUTTLE_TRANSIT_BORDER, world.maxy - SHUTTLE_TRANSIT_BORDER)
|
||||
for(var/turf/T as anything in block)
|
||||
// No need to empty() these, because they just got created and are already /turf/space.
|
||||
T.turf_flags |= UNUSED_RESERVATION_TURF
|
||||
CHECK_TICK
|
||||
|
||||
// Gotta create these suckers if we've not done so already
|
||||
if(SSatoms.initialized)
|
||||
SSatoms.InitializeAtoms(block(1, 1, world.maxx, world.maxy, z), FALSE)
|
||||
|
||||
unused_turfs["[z]"] = block
|
||||
clearing_reserved_turfs = FALSE
|
||||
|
||||
/datum/controller/subsystem/mapping/fire(resumed)
|
||||
// Cache for sonic speed
|
||||
var/list/list/turf/unused_turfs = src.unused_turfs
|
||||
var/list/world_contents = GLOB.all_unique_areas[world.area].contents
|
||||
// var/list/world_turf_contents_by_z = GLOB.all_unique_areas[world.area].turfs_by_zlevel
|
||||
var/list/lists_to_reserve = src.lists_to_reserve
|
||||
var/index = 0
|
||||
while(index < length(lists_to_reserve))
|
||||
var/list/packet = lists_to_reserve[index + 1]
|
||||
var/packetlen = length(packet)
|
||||
while(packetlen)
|
||||
if(MC_TICK_CHECK)
|
||||
if(index)
|
||||
lists_to_reserve.Cut(1, index)
|
||||
return
|
||||
var/turf/reserving_turf = packet[packetlen]
|
||||
reserving_turf.empty(/turf/space)
|
||||
LAZYINITLIST(unused_turfs["[reserving_turf.z]"])
|
||||
if(!(reserving_turf in unused_turfs["[reserving_turf.z]"]))
|
||||
unused_turfs["[reserving_turf.z]"].Insert(1, reserving_turf)
|
||||
reserving_turf.turf_flags = UNUSED_RESERVATION_TURF
|
||||
|
||||
world_contents += reserving_turf
|
||||
packet.len--
|
||||
packetlen = length(packet)
|
||||
|
||||
index++
|
||||
lists_to_reserve.Cut(1, index)
|
||||
|
||||
/**
|
||||
* Lazy loads a template on a lazy-loaded z-level
|
||||
* If you want to use this as non-debug, make sure to uncomment add_reservation_zlevel in /datum/controller/subsystem/mapping/Initialize()
|
||||
*/
|
||||
/datum/controller/subsystem/mapping/proc/lazy_load_template(datum/map_template/template)
|
||||
RETURN_TYPE(/datum/turf_reservation)
|
||||
|
||||
UNTIL(initialized)
|
||||
var/static/lazy_loading = FALSE
|
||||
UNTIL(!lazy_loading)
|
||||
|
||||
lazy_loading = TRUE
|
||||
. = _lazy_load_template(template)
|
||||
lazy_loading = FALSE
|
||||
|
||||
/datum/controller/subsystem/mapping/proc/_lazy_load_template(datum/map_template/template)
|
||||
PRIVATE_PROC(TRUE)
|
||||
var/datum/turf_reservation/reservation = request_turf_block_reservation(template.width, template.height)
|
||||
if(!istype(reservation))
|
||||
return
|
||||
|
||||
template.load(reservation.bottom_left_turf)
|
||||
return reservation
|
||||
|
||||
/// Schedules a group of turfs to be handed back to the reservation system's control
|
||||
/datum/controller/subsystem/mapping/proc/unreserve_turfs(list/turfs)
|
||||
lists_to_reserve += list(turfs)
|
||||
|
||||
@@ -64,3 +64,11 @@
|
||||
|
||||
/area/syndicate_mothership/jail
|
||||
name = "\improper Syndicate Jail"
|
||||
|
||||
/area/cordon
|
||||
name = "CORDON"
|
||||
icon_state = "cordon"
|
||||
requires_power = FALSE
|
||||
always_unpowered = TRUE
|
||||
dynamic_lighting = DYNAMIC_LIGHTING_DISABLED
|
||||
valid_territory = FALSE
|
||||
|
||||
@@ -34,7 +34,9 @@
|
||||
|
||||
var/blocks_air = FALSE
|
||||
|
||||
flags = 0
|
||||
flags = 0 // TODO, someday move all off the flags here to turf_flags
|
||||
|
||||
var/turf_flags = NONE
|
||||
|
||||
var/image/obscured //camerachunks
|
||||
|
||||
@@ -282,8 +284,9 @@
|
||||
qdel(src) //Just get the side effects and call Destroy
|
||||
var/list/old_comp_lookup = comp_lookup?.Copy()
|
||||
var/list/old_signal_procs = signal_procs?.Copy()
|
||||
|
||||
var/carryover_turf_flags = turf_flags & (RESERVATION_TURF|UNUSED_RESERVATION_TURF)
|
||||
var/turf/W = new path(src)
|
||||
W.turf_flags |= carryover_turf_flags
|
||||
if(old_comp_lookup)
|
||||
LAZYOR(W.comp_lookup, old_comp_lookup)
|
||||
if(old_signal_procs)
|
||||
|
||||
@@ -159,6 +159,7 @@ GLOBAL_LIST_INIT(admin_verbs_debug, list(
|
||||
/proc/machine_upgrade,
|
||||
/client/proc/map_template_load,
|
||||
/client/proc/map_template_upload,
|
||||
/client/proc/map_template_load_lazy,
|
||||
/client/proc/view_runtimes,
|
||||
/client/proc/admin_serialize,
|
||||
/client/proc/uid_log,
|
||||
|
||||
@@ -56,3 +56,23 @@
|
||||
message_admins("<span class='adminnotice'>[key_name_admin(usr)] has uploaded a map template ([map]). Took [stop_watch(timer)]s.</span>")
|
||||
else
|
||||
to_chat(usr, "Map template '[map]' failed to load properly")
|
||||
|
||||
/client/proc/map_template_load_lazy()
|
||||
set category = "Debug"
|
||||
set name = "Map template - Lazy Load"
|
||||
|
||||
if(!check_rights(R_DEBUG))
|
||||
return
|
||||
|
||||
var/map = input(usr, "Choose a Map Template to place on the lazy load map level.","Place Map Template") as null|anything in GLOB.map_templates
|
||||
if(!map)
|
||||
return
|
||||
var/datum/map_template/template = GLOB.map_templates[map]
|
||||
|
||||
message_admins("<span class='adminnotice'>[key_name_admin(usr)] is lazyloading the map template ([template.name]).</span>")
|
||||
var/datum/turf_reservation/reserve = SSmapping.lazy_load_template(template)
|
||||
if(!istype(reserve))
|
||||
message_admins("<span class='danger'>Lazyloading [template.name] failed! You should report this as a bug.</span>")
|
||||
return
|
||||
message_admins("<span class='adminnotice'>[key_name_admin(usr)] has lazyloaded the map template ([template.name]) at [ADMIN_JMP(reserve.bottom_left_turf)]</span>")
|
||||
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
/// Turf type that appears to be a world border, completely impassable and non-interactable to all physical (alive) entities.
|
||||
/turf/cordon
|
||||
name = "cordon"
|
||||
icon = 'icons/turf/walls.dmi'
|
||||
icon_state = "cordon"
|
||||
invisibility = INVISIBILITY_ABSTRACT
|
||||
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
|
||||
explosion_block = 50
|
||||
rad_insulation = RAD_FULL_INSULATION
|
||||
opacity = TRUE
|
||||
density = TRUE
|
||||
blocks_air = TRUE
|
||||
baseturf = /turf/cordon
|
||||
|
||||
// /turf/cordon/rust_heretic_act()
|
||||
// return FALSE
|
||||
|
||||
/turf/cordon/acid_act(acidpwr, acid_volume, acid_id)
|
||||
return FALSE
|
||||
|
||||
/turf/cordon/singularity_act()
|
||||
return FALSE
|
||||
|
||||
/turf/cordon/TerraformTurf(path, list/new_baseturfs, flags)
|
||||
return
|
||||
|
||||
/turf/cordon/bullet_act(obj/item/projectile/hitting_projectile, def_zone, piercing_hit)
|
||||
SHOULD_CALL_PARENT(FALSE) // Fuck you
|
||||
return
|
||||
|
||||
/turf/cordon/Adjacent(atom/neighbor, atom/target, atom/movable/mover)
|
||||
return FALSE
|
||||
@@ -1,62 +0,0 @@
|
||||
// This represents a level we can carve up as we please, and hand out
|
||||
// chunks of to whatever requests it
|
||||
/datum/space_level/heap
|
||||
name = "Heap level #ERROR"
|
||||
var/datum/space_chunk/top
|
||||
linkage = UNAFFECTED
|
||||
|
||||
/datum/space_level/heap/New(z, name, transition_type, traits)
|
||||
..(z, "Heap level #[z]", UNAFFECTED, traits)
|
||||
top = new(1, 1, zpos, world.maxx, world.maxy)
|
||||
flags = traits
|
||||
|
||||
/datum/space_level/heap/proc/request(width, height)
|
||||
return top.can_fit_space(width, height)
|
||||
|
||||
// Returns a space chunk datum for some nerd to work with - tells them what's safe to write into, and such
|
||||
/datum/space_level/heap/proc/allocate(width, height)
|
||||
if(!request(width, height))
|
||||
return null
|
||||
|
||||
var/datum/space_chunk/C = top.get_optimal_chunk(width, height)
|
||||
if(!C)
|
||||
return null
|
||||
C.children.Cut()
|
||||
|
||||
if(C.width == width && C.height == height && C.is_empty)
|
||||
C.set_occupied(TRUE)
|
||||
return C
|
||||
|
||||
// Split the chunk into 4 pieces
|
||||
var/datum/space_chunk/return_chunk = new(C.x, C.y, C.zpos, width, height)
|
||||
C.children += return_chunk
|
||||
C.children += new /datum/space_chunk(C.x+width, C.y, C.zpos, C.width-width, height)
|
||||
C.children += new /datum/space_chunk(C.x, C.y+height, C.zpos, width, C.height-height)
|
||||
C.children += new /datum/space_chunk(C.x+width, C.y+height, C.zpos, C.width-width, C.height-height)
|
||||
|
||||
for(var/datum/space_chunk/C2 in C.children)
|
||||
C2.parent = C
|
||||
return_chunk.set_occupied(TRUE)
|
||||
return return_chunk
|
||||
|
||||
/datum/space_level/heap/proc/free(datum/space_chunk/C)
|
||||
if(!istype(C))
|
||||
return
|
||||
if(C.zpos != zpos)
|
||||
return
|
||||
C.set_occupied(FALSE)
|
||||
for(var/turf/T in block(C.x, C.y, C.zpos, C.x + C.width - 1, C.y + C.height - 1, C.zpos))
|
||||
for(var/atom/movable/M in T)
|
||||
if(isobserver(M))
|
||||
continue
|
||||
M.loc = null
|
||||
qdel(M, TRUE)
|
||||
T.ChangeTurf(T.baseturf)
|
||||
var/datum/space_chunk/last_empty_parent = C
|
||||
while(last_empty_parent.parent && last_empty_parent.parent.is_empty)
|
||||
last_empty_parent = last_empty_parent.parent
|
||||
// Prevent a self-qdel from killing this proc
|
||||
src = null
|
||||
for(var/datum/space_chunk/child in last_empty_parent.children)
|
||||
qdel(child)
|
||||
last_empty_parent.children.Cut()
|
||||
@@ -28,6 +28,8 @@
|
||||
/proc/level_boosts_signal(z)
|
||||
return check_level_trait(z, BOOSTS_SIGNAL)
|
||||
|
||||
#define is_reserved_level(z) check_level_trait(z, ZTRAIT_RESERVED)
|
||||
|
||||
// Used for the nuke disk, or for checking if players survived through xenos
|
||||
/proc/is_secure_level(z)
|
||||
var/secure = check_level_trait(z, STATION_LEVEL)
|
||||
|
||||
@@ -0,0 +1,153 @@
|
||||
/*
|
||||
* Turf reservations are used to reserve specific areas of the reserved z-levels for various purposes, typically for late-loading maps.
|
||||
* It ensures that reserved turfs are properly managed and can be released when no longer needed.
|
||||
* Reservations are not automatically released, so they must be manually released when no longer needed.
|
||||
*
|
||||
* Usage:
|
||||
* - To create a new reservation, call the `request_turf_block_reservation(width, height)` method from the mapping subsystem.
|
||||
* - This will return a new instance of /datum/turf_reservation if the reservation is successful.
|
||||
*
|
||||
* Releasing:
|
||||
* - Call the `Release` method on the /datum/turf_reservation instance to release the reserved turfs and cordon turfs.
|
||||
* - This will return the used turfs to the mapping subsystem to allow for reuse of the turfs.
|
||||
*/
|
||||
|
||||
/datum/turf_reservation
|
||||
/// All turfs that we've reserved
|
||||
var/list/reserved_turfs = list()
|
||||
|
||||
/// Turfs around the reservation for cordoning
|
||||
var/list/cordon_turfs = list()
|
||||
|
||||
/// Area of turfs next to the cordon to fill with pre_cordon_area's
|
||||
var/list/pre_cordon_turfs = list()
|
||||
|
||||
/// The width of the reservation
|
||||
var/width = 0
|
||||
|
||||
/// The height of the reservation
|
||||
var/height = 0
|
||||
|
||||
/// Bottom left turf of the reservation
|
||||
var/turf/bottom_left_turf
|
||||
|
||||
/// Top right turf of the reservation
|
||||
var/turf/top_right_turf
|
||||
|
||||
/// The turf type the reservation is initially made with
|
||||
var/turf_type = /turf/space
|
||||
|
||||
/datum/turf_reservation/New()
|
||||
LAZYADD(SSmapping.turf_reservations, src)
|
||||
|
||||
/datum/turf_reservation/Destroy()
|
||||
Release()
|
||||
LAZYREMOVE(SSmapping.turf_reservations, src)
|
||||
return ..()
|
||||
|
||||
/datum/turf_reservation/proc/Release()
|
||||
bottom_left_turf = null
|
||||
top_right_turf = null
|
||||
|
||||
var/list/reserved_copy = reserved_turfs.Copy()
|
||||
SSmapping.used_turfs -= reserved_turfs
|
||||
reserved_turfs = list()
|
||||
|
||||
var/list/cordon_copy = cordon_turfs.Copy()
|
||||
SSmapping.used_turfs -= cordon_turfs
|
||||
cordon_turfs = list()
|
||||
|
||||
var/release_turfs = reserved_copy + cordon_copy
|
||||
|
||||
SSmapping.unreserve_turfs(release_turfs)
|
||||
|
||||
/// Attempts to calaculate and store a list of turfs around the reservation for cordoning. Returns whether a valid cordon was calculated
|
||||
/datum/turf_reservation/proc/calculate_cordon_turfs(turf/bottom_left, turf/top_right)
|
||||
if(bottom_left.x < 2 || bottom_left.y < 2 || top_right.x > (world.maxx - 2) || top_right.y > (world.maxy - 2))
|
||||
return FALSE // no space for a cordon here
|
||||
|
||||
var/list/possible_turfs = CORNER_OUTLINE(bottom_left, width, height)
|
||||
// if they're our cordon turfs, accept them
|
||||
possible_turfs -= cordon_turfs
|
||||
for(var/turf/cordon_turf as anything in possible_turfs)
|
||||
// if we changed this to check reservation turf instead of not unused, we could have overlapping cordons.
|
||||
// Unfortunately, that means adding logic for cordons not being removed if they have multiple edges and I'm lazy
|
||||
if(!(cordon_turf.turf_flags & UNUSED_RESERVATION_TURF))
|
||||
return FALSE
|
||||
cordon_turfs |= possible_turfs
|
||||
|
||||
return TRUE
|
||||
|
||||
/// Actually generates the cordon around the reservation, and marking the cordon turfs as reserved
|
||||
/datum/turf_reservation/proc/generate_cordon()
|
||||
for(var/turf/cordon_turf as anything in cordon_turfs)
|
||||
var/area/cordon/cordon_area = GLOB.all_unique_areas[/area/cordon] || new /area/cordon
|
||||
|
||||
cordon_area.contents += cordon_turf
|
||||
|
||||
// Its no longer unused, but its also not "used"
|
||||
cordon_turf.turf_flags &= ~UNUSED_RESERVATION_TURF
|
||||
cordon_turf.empty(/turf/cordon)
|
||||
SSmapping.unused_turfs["[cordon_turf.z]"] -= cordon_turf
|
||||
// still gets linked to us though
|
||||
SSmapping.used_turfs[cordon_turf] = src
|
||||
|
||||
/// Internal proc which handles reserving the area for the reservation.
|
||||
/datum/turf_reservation/proc/_reserve_area(width, height, zlevel)
|
||||
src.width = width
|
||||
src.height = height
|
||||
if(width > world.maxx || height > world.maxy || width < 1 || height < 1)
|
||||
return FALSE
|
||||
var/list/avail = SSmapping.unused_turfs["[zlevel]"]
|
||||
var/turf/bottom_left
|
||||
var/turf/top_right
|
||||
var/list/turf/final_turfs = list()
|
||||
var/passing = FALSE
|
||||
for(var/i in avail)
|
||||
CHECK_TICK
|
||||
bottom_left = i
|
||||
if(!(bottom_left.turf_flags & UNUSED_RESERVATION_TURF))
|
||||
continue
|
||||
if(bottom_left.x + width > world.maxx || bottom_left.y + height > world.maxy)
|
||||
continue
|
||||
top_right = locate(bottom_left.x + width - 1, bottom_left.y + height - 1, bottom_left.z)
|
||||
if(!(top_right.turf_flags & UNUSED_RESERVATION_TURF))
|
||||
continue
|
||||
final_turfs = block(bottom_left, top_right)
|
||||
if(!final_turfs)
|
||||
continue
|
||||
passing = TRUE
|
||||
for(var/I in final_turfs)
|
||||
var/turf/checking = I
|
||||
if(!(checking.turf_flags & UNUSED_RESERVATION_TURF))
|
||||
passing = FALSE
|
||||
break
|
||||
if(passing) // found a potentially valid area, now try to calculate its cordon
|
||||
passing = calculate_cordon_turfs(bottom_left, top_right)
|
||||
if(!passing)
|
||||
continue
|
||||
break
|
||||
if(!passing || !istype(bottom_left) || !istype(top_right))
|
||||
return FALSE
|
||||
for(var/turf/T as anything in final_turfs)
|
||||
reserved_turfs |= T
|
||||
SSmapping.unused_turfs["[T.z]"] -= T
|
||||
SSmapping.used_turfs[T] = src
|
||||
T.turf_flags = (T.turf_flags | RESERVATION_TURF) & ~UNUSED_RESERVATION_TURF
|
||||
T.empty(turf_type)
|
||||
|
||||
bottom_left_turf = bottom_left
|
||||
top_right_turf = top_right
|
||||
return TRUE
|
||||
|
||||
/datum/turf_reservation/proc/reserve(width, height, z_reservation)
|
||||
|
||||
if(!_reserve_area(width, height, z_reservation))
|
||||
log_debug("Failed turf reservation: releasing")
|
||||
Release()
|
||||
return FALSE
|
||||
|
||||
log_debug("Turf reservation successful, generating cordon")
|
||||
generate_cordon()
|
||||
return TRUE
|
||||
|
||||
@@ -134,42 +134,3 @@ GLOBAL_DATUM_INIT(space_manager, /datum/zlev_manager, new())
|
||||
z_list.Remove(S)
|
||||
qdel(S)
|
||||
world.maxz--
|
||||
|
||||
|
||||
// An internally-used proc used for heap-zlevel management
|
||||
/datum/zlev_manager/proc/add_new_heap()
|
||||
world.maxz++
|
||||
var/our_z = world.maxz
|
||||
var/datum/space_level/yup = new /datum/space_level/heap(our_z, traits = list(BLOCK_TELEPORT, ADMIN_LEVEL))
|
||||
z_list["[our_z]"] = yup
|
||||
return yup
|
||||
|
||||
// This is what you can call to allocate a section of space
|
||||
// Later, I'll add an argument to let you define the flags on the region
|
||||
/datum/zlev_manager/proc/allocate_space(width, height)
|
||||
if(width > world.maxx || height > world.maxy)
|
||||
throw EXCEPTION("Too much space requested! \[[width],[height]\]")
|
||||
if(!length(heaps))
|
||||
heaps.len++
|
||||
heaps[length(heaps)] = add_new_heap()
|
||||
var/datum/space_level/heap/our_heap
|
||||
var/weve_got_vacancy = 0
|
||||
for(our_heap in heaps)
|
||||
weve_got_vacancy = our_heap.request(width, height)
|
||||
if(weve_got_vacancy)
|
||||
break // We're sticking with the present value of `our_heap` - it's got room
|
||||
// This loop will also run out if no vacancies are found
|
||||
|
||||
if(!weve_got_vacancy)
|
||||
heaps.len++
|
||||
our_heap = add_new_heap()
|
||||
heaps[length(heaps)] = our_heap
|
||||
return our_heap.allocate(width, height)
|
||||
|
||||
/datum/zlev_manager/proc/free_space(datum/space_chunk/C)
|
||||
if(!istype(C))
|
||||
return
|
||||
var/datum/space_level/heap/heap = z_list["[C.zpos]"]
|
||||
if(!istype(heap))
|
||||
throw EXCEPTION("Attempted to free chunk at invalid z-level ([C.x],[C.y],[C.zpos]) [C.width]x[C.height]")
|
||||
heap.free(C)
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 34 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 5.7 KiB After Width: | Height: | Size: 10 KiB |
+2
-1
@@ -1694,6 +1694,7 @@
|
||||
#include "code\modules\atmospherics\machinery\portable\portable_atmospherics.dm"
|
||||
#include "code\modules\atmospherics\machinery\portable\portable_pump.dm"
|
||||
#include "code\modules\atmospherics\machinery\portable\scrubber.dm"
|
||||
#include "code\modules\awaymissions\cordon.dm"
|
||||
#include "code\modules\awaymissions\mob_spawn.dm"
|
||||
#include "code\modules\awaymissions\zlevel_helpers.dm"
|
||||
#include "code\modules\awaymissions\maploader\dmm_suite.dm"
|
||||
@@ -2876,12 +2877,12 @@
|
||||
#include "code\modules\shuttle\shuttle_rotate.dm"
|
||||
#include "code\modules\shuttle\supply.dm"
|
||||
#include "code\modules\shuttle\syndicate_shuttles.dm"
|
||||
#include "code\modules\space_management\heap_space_level.dm"
|
||||
#include "code\modules\space_management\level_check.dm"
|
||||
#include "code\modules\space_management\level_traits.dm"
|
||||
#include "code\modules\space_management\space_chunk.dm"
|
||||
#include "code\modules\space_management\space_level.dm"
|
||||
#include "code\modules\space_management\space_transition.dm"
|
||||
#include "code\modules\space_management\turf_reservation.dm"
|
||||
#include "code\modules\space_management\zlevel_manager.dm"
|
||||
#include "code\modules\station_goals\bluespace_tap.dm"
|
||||
#include "code\modules\station_goals\bluespace_tap_events.dm"
|
||||
|
||||
Reference in New Issue
Block a user