diff --git a/code/controllers/subsystem/mapping.dm b/code/controllers/subsystem/mapping.dm
index 167e664b87a..a48360803ba 100644
--- a/code/controllers/subsystem/mapping.dm
+++ b/code/controllers/subsystem/mapping.dm
@@ -1,7 +1,9 @@
SUBSYSTEM_DEF(mapping)
name = "Mapping"
init_order = INIT_ORDER_MAPPING
- flags = NONE
+ flags = SS_NO_FIRE
+
+ var/world_loaded = FALSE
var/list/nuke_tiles = list()
var/list/nuke_threats = list()
@@ -25,7 +27,7 @@ SUBSYSTEM_DEF(mapping)
var/list/datum/turf_reservations //list of turf reservations
var/list/used_turfs = list() //list of turf = datum/turf_reservation
- var/clearing_reserved_areas = FALSE
+ var/clearing_reserved_turfs = FALSE
// Z-manager stuff
var/station_start // should only be used for maploading-related tasks
@@ -46,6 +48,8 @@ SUBSYSTEM_DEF(mapping)
/datum/controller/subsystem/mapping/Initialize(timeofday)
+ if(initialized)
+ return
if(config.defaulted)
to_chat(world, "Unable to load next map config, defaulting to Box Station")
loader = new
@@ -96,9 +100,34 @@ SUBSYSTEM_DEF(mapping)
Used by the AI doomsday and the self destruct nuke.
*/
-/datum/controller/subsystem/mapping/fire()
- if(clearing_reserved_areas)
- wipe_turf_reservations()
+/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 [go_ahead/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/add_nuke_threat(datum/nuke)
nuke_threats[nuke] = TRUE
@@ -126,13 +155,13 @@ SUBSYSTEM_DEF(mapping)
shuttle_templates = SSmapping.shuttle_templates
shelter_templates = SSmapping.shelter_templates
unused_turfs = SSmapping.unused_turfs
- turf_reservations = SSmapping.turf_reservations.
+ turf_reservations = SSmapping.turf_reservations
used_turfs = SSmapping.used_turfs
config = SSmapping.config
next_map_config = SSmapping.next_map_config
- clearing_reserved_areas = SSmapping.clearing_reserved_areas
+ clearing_reserved_turfs = SSmapping.clearing_reserved_turfs
z_list = SSmapping.z_list
@@ -399,6 +428,7 @@ GLOBAL_LIST_EMPTY(the_station_areas)
GLOB.the_gateway.wait = world.time
/datum/controller/subsystem/mapping/proc/RequestBlockReservation(width, height, z, type = /datum/turf_reservation, turf_type_override)
+ UNTIL(initialized && !clearing_reserved_turfs)
var/datum/turf_reservation/reserve = new type
if(turf_type_override)
reserve.turf_type = turf_type_override
@@ -415,12 +445,15 @@ GLOBAL_LIST_EMPTY(the_station_areas)
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()
- // transit zone
+ 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.
for(var/i in levels_by_trait(ZTRAIT_RESERVED))
var/turf/A = get_turf(locate(SHUTTLE_TRANSIT_BORDER,SHUTTLE_TRANSIT_BORDER,i))
var/turf/B = get_turf(locate(world.maxx - SHUTTLE_TRANSIT_BORDER,world.maxy - SHUTTLE_TRANSIT_BORDER,i))
reserve_turfs(block(A, B))
+ clearing_reserved_turfs = FALSE
/datum/controller/subsystem/mapping/proc/reserve_turfs(list/turfs)
for(var/i in turfs)
@@ -429,20 +462,21 @@ GLOBAL_LIST_EMPTY(the_station_areas)
LAZYINITLIST(unused_turfs["[T.z]"])
unused_turfs["[T.z]"] |= T
T.flags_1 |= UNUSED_RESERVATION_TURF_1
+ CHECK_TICK
-/datum/controller/subsystem/mapping/proc/wipe_turf_reservations()
- if(SSshuttle && SSshuttle.transit_requesters)
- SSshuttle.transit_requesters.Cut()
+//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
- qdel(TR, TRUE)
- var/list/clearing = list()
- for(var/l in unused_turfs)
- for(var/i in l)
- clearing |= i
- for(var/i in used_turfs)
- clearing |= i
- unused_turfs = list()
- used_turfs = list()
- reserve_turfs(clearing)
+ 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)
diff --git a/code/controllers/subsystem/shuttle.dm b/code/controllers/subsystem/shuttle.dm
index 77c98c0c5e7..1e656ae1eb7 100644
--- a/code/controllers/subsystem/shuttle.dm
+++ b/code/controllers/subsystem/shuttle.dm
@@ -106,18 +106,19 @@ SUBSYSTEM_DEF(shuttle)
qdel(T, force=TRUE)
CheckAutoEvac()
- while(transit_requesters.len)
- var/requester = popleft(transit_requesters)
- var/success = generate_transit_dock(requester)
- if(!success) // BACK OF THE QUEUE
- transit_request_failures[requester]++
- if(transit_request_failures[requester] < MAX_TRANSIT_REQUEST_RETRIES)
- transit_requesters += requester
- else
- var/obj/docking_port/mobile/M = requester
- M.transit_failure()
- if(MC_TICK_CHECK)
- break
+ if(!SSmapping.clearing_reserved_turfs)
+ while(transit_requesters.len)
+ var/requester = popleft(transit_requesters)
+ var/success = generate_transit_dock(requester)
+ if(!success) // BACK OF THE QUEUE
+ transit_request_failures[requester]++
+ if(transit_request_failures[requester] < MAX_TRANSIT_REQUEST_RETRIES)
+ transit_requesters += requester
+ else
+ var/obj/docking_port/mobile/M = requester
+ M.transit_failure()
+ if(MC_TICK_CHECK)
+ break
/datum/controller/subsystem/shuttle/proc/CheckAutoEvac()
if(emergencyNoEscape || emergencyNoRecall || !emergency || !SSticker.HasRoundStarted())
diff --git a/code/modules/admin/verbs/debug.dm b/code/modules/admin/verbs/debug.dm
index d5b08e97d0f..d34e1ebd422 100644
--- a/code/modules/admin/verbs/debug.dm
+++ b/code/modules/admin/verbs/debug.dm
@@ -968,11 +968,10 @@ GLOBAL_PROTECT(AdminProcCallSpamPrevention)
var/answer = alert("WARNING: THIS WILL WIPE ALL RESERVED SPACE TO A CLEAN SLATE! ANY MOVING SHUTTLES, ELEVATORS, OR IN-PROGRESS PHOTOGRAPHY WILL BE DELETED!", "Really wipe dynamic turfs?", "YES", "NO")
if(answer != "YES")
return
- SSmapping.clearing_reserved_areas = TRUE
message_admins("[key_name_admin(src)] cleared dynamic transit space.")
SSblackbox.record_feedback("tally", "admin_verb", 1, "Clear Dynamic Transit") // If...
log_admin("[key_name(src)] cleared dynamic transit space.")
-
+ SSmapping.wipe_reservations() //this goes after it's logged, incase something horrible happens.
/client/proc/toggle_medal_disable()
set category = "Debug"
diff --git a/code/modules/shuttle/shuttle.dm b/code/modules/shuttle/shuttle.dm
index 48985821ddc..fc8169f6d25 100644
--- a/code/modules/shuttle/shuttle.dm
+++ b/code/modules/shuttle/shuttle.dm
@@ -219,8 +219,10 @@
if(owner.assigned_transit == src)
owner.assigned_transit = null
owner = null
- QDEL_NULL(reserved_area)
- . = ..()
+ if(!QDELETED(reserved_area))
+ qdel(reserved_area)
+ reserved_area = null
+ return ..()
/obj/docking_port/mobile
name = "shuttle"