diff --git a/_maps/__MAP_DEFINES.dm b/_maps/__MAP_DEFINES.dm index 8f55a0aea114..a275d07a744c 100644 --- a/_maps/__MAP_DEFINES.dm +++ b/_maps/__MAP_DEFINES.dm @@ -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 diff --git a/code/__DEFINES/_globals.dm b/code/__DEFINES/_globals.dm index 083abd4108ef..f80661688148 100644 --- a/code/__DEFINES/_globals.dm +++ b/code/__DEFINES/_globals.dm @@ -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) diff --git a/code/__DEFINES/flags.dm b/code/__DEFINES/flags.dm index 08c5087b4b09..63fddee37456 100644 --- a/code/__DEFINES/flags.dm +++ b/code/__DEFINES/flags.dm @@ -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 diff --git a/code/__DEFINES/shuttle_defines.dm b/code/__DEFINES/shuttle_defines.dm index 43470086ce22..a656e661a9dd 100644 --- a/code/__DEFINES/shuttle_defines.dm +++ b/code/__DEFINES/shuttle_defines.dm @@ -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 diff --git a/code/__DEFINES/turfs.dm b/code/__DEFINES/turfs.dm index f383b4d5540c..f3149bb42aaa 100644 --- a/code/__DEFINES/turfs.dm +++ b/code/__DEFINES/turfs.dm @@ -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)) diff --git a/code/__HELPERS/lists.dm b/code/__HELPERS/lists.dm index 47947cb245f1..8ff538f3d37f 100644 --- a/code/__HELPERS/lists.dm +++ b/code/__HELPERS/lists.dm @@ -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) diff --git a/code/_globalvars/bitfields.dm b/code/_globalvars/bitfields.dm index e165eabdb705..3f1de6a67fde 100644 --- a/code/_globalvars/bitfields.dm +++ b/code/_globalvars/bitfields.dm @@ -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, diff --git a/code/_globalvars/lists/objects.dm b/code/_globalvars/lists/objects.dm index f918fe47277f..5448b02c2e9a 100644 --- a/code/_globalvars/lists/objects.dm +++ b/code/_globalvars/lists/objects.dm @@ -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(rcd_list) //list of Rapid Construction Devices. diff --git a/code/controllers/subsystem/non_firing/SSmapping.dm b/code/controllers/subsystem/non_firing/SSmapping.dm index a236dabf7f7a..6cf89d96c4a8 100644 --- a/code/controllers/subsystem/non_firing/SSmapping.dm +++ b/code/controllers/subsystem/non_firing/SSmapping.dm @@ -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) diff --git a/code/game/area/misc_areas.dm b/code/game/area/misc_areas.dm index e9bd5f40a6a4..d1e2b01583c7 100644 --- a/code/game/area/misc_areas.dm +++ b/code/game/area/misc_areas.dm @@ -63,3 +63,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 diff --git a/code/game/turfs/turf.dm b/code/game/turfs/turf.dm index b2c02b58364d..b27a906a9720 100644 --- a/code/game/turfs/turf.dm +++ b/code/game/turfs/turf.dm @@ -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) diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm index f12dd40466da..b5a6fd1d5fcd 100644 --- a/code/modules/admin/admin_verbs.dm +++ b/code/modules/admin/admin_verbs.dm @@ -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, diff --git a/code/modules/admin/verbs/map_template_loadverb.dm b/code/modules/admin/verbs/map_template_loadverb.dm index 80d46e18550d..c05060131fcf 100644 --- a/code/modules/admin/verbs/map_template_loadverb.dm +++ b/code/modules/admin/verbs/map_template_loadverb.dm @@ -56,3 +56,23 @@ message_admins("[key_name_admin(usr)] has uploaded a map template ([map]). Took [stop_watch(timer)]s.") 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("[key_name_admin(usr)] is lazyloading the map template ([template.name]).") + var/datum/turf_reservation/reserve = SSmapping.lazy_load_template(template) + if(!istype(reserve)) + message_admins("Lazyloading [template.name] failed! You should report this as a bug.") + return + message_admins("[key_name_admin(usr)] has lazyloaded the map template ([template.name]) at [ADMIN_JMP(reserve.bottom_left_turf)]") + diff --git a/code/modules/awaymissions/cordon.dm b/code/modules/awaymissions/cordon.dm new file mode 100644 index 000000000000..4d44766ac668 --- /dev/null +++ b/code/modules/awaymissions/cordon.dm @@ -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 diff --git a/code/modules/space_management/heap_space_level.dm b/code/modules/space_management/heap_space_level.dm deleted file mode 100644 index 666bfa9fa821..000000000000 --- a/code/modules/space_management/heap_space_level.dm +++ /dev/null @@ -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() diff --git a/code/modules/space_management/level_traits.dm b/code/modules/space_management/level_traits.dm index 0634d9299902..aa02bdaa44c1 100644 --- a/code/modules/space_management/level_traits.dm +++ b/code/modules/space_management/level_traits.dm @@ -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) diff --git a/code/modules/space_management/turf_reservation.dm b/code/modules/space_management/turf_reservation.dm new file mode 100644 index 000000000000..febe4b34bba4 --- /dev/null +++ b/code/modules/space_management/turf_reservation.dm @@ -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 + diff --git a/code/modules/space_management/zlevel_manager.dm b/code/modules/space_management/zlevel_manager.dm index d2364652ee6a..4887257493c9 100644 --- a/code/modules/space_management/zlevel_manager.dm +++ b/code/modules/space_management/zlevel_manager.dm @@ -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) diff --git a/icons/turf/areas.dmi b/icons/turf/areas.dmi index d27c76b49de6..69aca13c76e9 100755 Binary files a/icons/turf/areas.dmi and b/icons/turf/areas.dmi differ diff --git a/icons/turf/walls.dmi b/icons/turf/walls.dmi index d8d4d8fdd790..fb71d66c556c 100644 Binary files a/icons/turf/walls.dmi and b/icons/turf/walls.dmi differ diff --git a/paradise.dme b/paradise.dme index 94f084b0272a..2e85095448db 100644 --- a/paradise.dme +++ b/paradise.dme @@ -453,8 +453,8 @@ #include "code\datums\components\footstep.dm" #include "code\datums\components\forces_doors_open.dm" #include "code\datums\components\fullauto.dm" -#include "code\datums\components\jetpack_component.dm" #include "code\datums\components\ghost_direct_control.dm" +#include "code\datums\components\jetpack_component.dm" #include "code\datums\components\label.dm" #include "code\datums\components\largeobjecttransparency.dm" #include "code\datums\components\material_container.dm" @@ -1531,8 +1531,8 @@ #include "code\modules\antagonists\_common\antag_hud.dm" #include "code\modules\antagonists\_common\antag_spawner.dm" #include "code\modules\antagonists\_common\antag_team.dm" -#include "code\modules\antagonists\abductor\team_abductor.dm" #include "code\modules\antagonists\abductor\datum_abductor.dm" +#include "code\modules\antagonists\abductor\team_abductor.dm" #include "code\modules\antagonists\antag_org\antag_org_datum.dm" #include "code\modules\antagonists\antag_org\antag_org_syndicate.dm" #include "code\modules\antagonists\changeling\changeling_power.dm" @@ -1692,6 +1692,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" @@ -2874,12 +2875,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"