diff --git a/code/__HELPERS/_lists.dm b/code/__HELPERS/_lists.dm
index 361bd10f329..7af30c8d972 100644
--- a/code/__HELPERS/_lists.dm
+++ b/code/__HELPERS/_lists.dm
@@ -97,6 +97,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; \
+ } \
+ } \
+ }
+
#define reverseList(L) reverse_range(L.Copy())
/// Passed into BINARY_INSERT to compare keys
diff --git a/code/__HELPERS/areas.dm b/code/__HELPERS/areas.dm
index dec768a6a0c..8df182c90d0 100644
--- a/code/__HELPERS/areas.dm
+++ b/code/__HELPERS/areas.dm
@@ -273,13 +273,11 @@ GLOBAL_LIST_INIT(typecache_powerfailure_safe_areas, typecacheof(list(
// Now their turfs
var/list/turfs = list()
for(var/area/pull_from as anything in areas_to_pull)
- var/list/our_turfs = pull_from.get_contained_turfs()
- if(target_z == 0)
- turfs += our_turfs
+ if (target_z == 0)
+ for (var/list/zlevel_turfs as anything in pull_from.get_zlevel_turf_lists())
+ turfs += zlevel_turfs
else
- for(var/turf/turf_in_area as anything in our_turfs)
- if(target_z == turf_in_area.z)
- turfs += turf_in_area
+ turfs += pull_from.get_turfs_by_zlevel(target_z)
return turfs
diff --git a/code/controllers/subsystem/area_contents.dm b/code/controllers/subsystem/area_contents.dm
index 904714f0ba0..054de9d8efc 100644
--- a/code/controllers/subsystem/area_contents.dm
+++ b/code/controllers/subsystem/area_contents.dm
@@ -1,4 +1,4 @@
-#define ALLOWED_LOOSE_TURFS 500
+#define ALLOWED_LOOSE_TURFS 100
/**
* Responsible for managing the sizes of area.contained_turfs and area.turfs_to_uncontain
* These lists do not check for duplicates, which is fine, but it also means they can balloon in size over time
@@ -17,8 +17,11 @@ SUBSYSTEM_DEF(area_contents)
var/total_clearing_from = 0
var/total_to_clear = 0
for(var/area/to_clear as anything in marked_for_clearing)
- total_to_clear += length(to_clear.turfs_to_uncontain)
- total_clearing_from += length(to_clear.contained_turfs)
+ for (var/area_zlevel in 1 to length(to_clear.turfs_to_uncontain_by_zlevel))
+ if (length(to_clear.turfs_to_uncontain_by_zlevel[area_zlevel]))
+ total_to_clear += length(to_clear.turfs_to_uncontain_by_zlevel[area_zlevel])
+ if (length(to_clear.turfs_by_zlevel) >= area_zlevel) //this should always be true, but stat_entry is no place for runtimes. fire() can handle that
+ total_clearing_from += length(to_clear.turfs_by_zlevel[area_zlevel])
msg = "A:[length(currentrun)] MR:[length(marked_for_clearing)] TC:[total_to_clear] CF:[total_clearing_from]"
return ..()
@@ -29,8 +32,10 @@ SUBSYSTEM_DEF(area_contents)
while(length(currentrun))
var/area/test = currentrun[length(currentrun)]
- if(length(test.turfs_to_uncontain) > ALLOWED_LOOSE_TURFS)
- marked_for_clearing |= test
+ for (var/area_zlevel in 1 to length(test.turfs_to_uncontain_by_zlevel))
+ if(length(test.turfs_to_uncontain_by_zlevel[area_zlevel]) > ALLOWED_LOOSE_TURFS)
+ marked_for_clearing |= test
+ break
currentrun.len--
if(MC_TICK_CHECK)
return
@@ -39,19 +44,27 @@ SUBSYSTEM_DEF(area_contents)
while(length(marked_for_clearing))
var/area/clear = marked_for_clearing[length(marked_for_clearing)]
- // The operation of cutting large lists can be expensive
- // It scales almost directly with the size of the list we're cutting with
- // Because of this, we're gonna stick to cutting 1 entry at a time
- // There's no reason to batch it I promise, this is faster. No overtime too
- var/amount_cut = 0
- var/list/cut_from = clear.turfs_to_uncontain
- for(amount_cut in 1 to length(cut_from))
- clear.contained_turfs -= cut_from[amount_cut]
- if(MC_TICK_CHECK)
- cut_from.Cut(1, amount_cut + 1)
- return
-
- clear.turfs_to_uncontain = list()
+ for (var/area_zlevel in 1 to length(clear.turfs_to_uncontain_by_zlevel))
+ if (!length(clear.turfs_to_uncontain_by_zlevel[area_zlevel]))
+ continue
+ if (length(clear.turfs_by_zlevel) < area_zlevel)
+ stack_trace("[clear]([clear.type])'s turfs_by_zlevel is length [length(clear.turfs_by_zlevel)] but we are being asked to remove turfs from zlevel [area_zlevel] from it.")
+ clear.turfs_to_uncontain_by_zlevel[area_zlevel] = list()
+ continue
+
+ // The operation of cutting large lists can be expensive
+ // It scales almost directly with the size of the list we're cutting with
+ // Because of this, we're gonna stick to cutting 1 entry at a time
+ // There's no reason to batch it I promise, this is faster. No overtime too
+ var/amount_cut = 0
+ var/list/cut_from = clear.turfs_to_uncontain_by_zlevel[area_zlevel]
+ for(amount_cut in 1 to length(cut_from))
+ clear.turfs_by_zlevel[area_zlevel] -= cut_from[amount_cut]
+ if(MC_TICK_CHECK)
+ cut_from.Cut(1, amount_cut + 1)
+ return
+
+ clear.turfs_to_uncontain_by_zlevel = list()
marked_for_clearing.len--
#undef ALLOWED_LOOSE_TURFS
diff --git a/code/controllers/subsystem/job.dm b/code/controllers/subsystem/job.dm
index 38ef544e04c..4a8345728e5 100644
--- a/code/controllers/subsystem/job.dm
+++ b/code/controllers/subsystem/job.dm
@@ -714,13 +714,14 @@ SUBSYSTEM_DEF(job)
var/area/shuttle/arrival/arrivals_area = GLOB.areas_by_type[/area/shuttle/arrival]
if(!isnull(arrivals_area))
var/list/turf/available_turfs = list()
- for(var/turf/arrivals_turf as anything in arrivals_area.get_contained_turfs())
- var/obj/structure/chair/shuttle_chair = locate() in arrivals_turf
- if(!isnull(shuttle_chair))
- return shuttle_chair
- if(arrivals_turf.is_blocked_turf(TRUE))
- continue
- available_turfs += arrivals_turf
+ for (var/list/zlevel_turfs as anything in arrivals_area.get_zlevel_turf_lists())
+ for (var/turf/arrivals_turf as anything in zlevel_turfs)
+ var/obj/structure/chair/shuttle_chair = locate() in arrivals_turf
+ if(!isnull(shuttle_chair))
+ return shuttle_chair
+ if(arrivals_turf.is_blocked_turf(TRUE))
+ continue
+ available_turfs += arrivals_turf
if(length(available_turfs))
return pick(available_turfs)
diff --git a/code/controllers/subsystem/mapping.dm b/code/controllers/subsystem/mapping.dm
index 5881405da0b..986a4c1bc8d 100644
--- a/code/controllers/subsystem/mapping.dm
+++ b/code/controllers/subsystem/mapping.dm
@@ -173,7 +173,7 @@ SUBSYSTEM_DEF(mapping)
// Cache for sonic speed
var/list/unused_turfs = src.unused_turfs
var/list/world_contents = GLOB.areas_by_type[world.area].contents
- var/list/world_turf_contents = GLOB.areas_by_type[world.area].contained_turfs
+ var/list/world_turf_contents_by_z = GLOB.areas_by_type[world.area].turfs_by_zlevel
var/list/lists_to_reserve = src.lists_to_reserve
var/index = 0
while(index < length(lists_to_reserve))
@@ -189,10 +189,12 @@ SUBSYSTEM_DEF(mapping)
LAZYINITLIST(unused_turfs["[T.z]"])
unused_turfs["[T.z]"] |= T
var/area/old_area = T.loc
- old_area.turfs_to_uncontain += T
+ LISTASSERTLEN(old_area.turfs_to_uncontain_by_zlevel, T.z, list())
+ old_area.turfs_to_uncontain_by_zlevel[T.z] += T
T.turf_flags = UNUSED_RESERVATION_TURF
world_contents += T
- world_turf_contents += T
+ LISTASSERTLEN(world_turf_contents_by_z, T.z, list())
+ world_turf_contents_by_z[T.z] += T
packet.len--
packetlen = length(packet)
@@ -835,12 +837,14 @@ GLOBAL_LIST_EMPTY(the_station_areas)
// Faster
if(space_guaranteed)
var/area/global_area = GLOB.areas_by_type[world.area]
- global_area.contained_turfs += Z_TURFS(z_level)
+ LISTASSERTLEN(global_area.turfs_by_zlevel, z_level, list())
+ global_area.turfs_by_zlevel[z_level] = Z_TURFS(z_level)
return
for(var/turf/to_contain as anything in Z_TURFS(z_level))
var/area/our_area = to_contain.loc
- our_area.contained_turfs += to_contain
+ LISTASSERTLEN(our_area.turfs_by_zlevel, z_level, list())
+ our_area.turfs_by_zlevel[z_level] += to_contain
/datum/controller/subsystem/mapping/proc/update_plane_tracking(datum/space_level/update_with)
// We're essentially going to walk down the stack of connected z levels, and set their plane offset as we go
diff --git a/code/controllers/subsystem/radioactive_nebula.dm b/code/controllers/subsystem/radioactive_nebula.dm
index 3b11a7870af..17c0cc9b3da 100644
--- a/code/controllers/subsystem/radioactive_nebula.dm
+++ b/code/controllers/subsystem/radioactive_nebula.dm
@@ -50,9 +50,10 @@ SUBSYSTEM_DEF(radioactive_nebula)
/// Loop through radioactive space (with lag checks) and make it all radioactive!
/datum/controller/subsystem/radioactive_nebula/proc/irradiate_everything()
for (var/area/area as anything in get_areas(radioactive_nebula.radioactive_areas))
- for (var/turf/turf as anything in area.get_contained_turfs())
- for (var/atom/movable/target as anything in turf)
- fake_irradiate(target)
+ for (var/list/zlevel_turfs as anything in area.get_zlevel_turf_lists())
+ for (var/turf/area_turf as anything in zlevel_turfs)
+ for (var/atom/movable/target as anything in area_turf)
+ fake_irradiate(target)
CHECK_TICK
diff --git a/code/controllers/subsystem/shuttle.dm b/code/controllers/subsystem/shuttle.dm
index bb88e8d6824..ce7645529cb 100644
--- a/code/controllers/subsystem/shuttle.dm
+++ b/code/controllers/subsystem/shuttle.dm
@@ -645,7 +645,7 @@ SUBSYSTEM_DEF(shuttle)
var/datum/turf_reservation/proposal = SSmapping.request_turf_block_reservation(
transit_width,
transit_height,
- 1,
+ z_size = 1, //if this is changed the turf uncontain code below has to be updated to support multiple zs
reservation_type = /datum/turf_reservation/transit,
turf_type_override = transit_path,
)
@@ -679,17 +679,22 @@ SUBSYSTEM_DEF(shuttle)
if(!midpoint)
qdel(proposal)
return FALSE
+
var/area/old_area = midpoint.loc
- old_area.turfs_to_uncontain += proposal.reserved_turfs
- var/area/shuttle/transit/A = new()
- A.parallax_movedir = travel_dir
- A.contents = proposal.reserved_turfs
- A.contained_turfs = proposal.reserved_turfs
+ LISTASSERTLEN(old_area.turfs_to_uncontain_by_zlevel, bottomleft.z, list())
+ old_area.turfs_to_uncontain_by_zlevel[bottomleft.z] += proposal.reserved_turfs
+
+ var/area/shuttle/transit/new_area = new()
+ new_area.parallax_movedir = travel_dir
+ new_area.contents = proposal.reserved_turfs
+ LISTASSERTLEN(new_area.turfs_by_zlevel, bottomleft.z, list())
+ new_area.turfs_by_zlevel[bottomleft.z] = proposal.reserved_turfs
+
var/obj/docking_port/stationary/transit/new_transit_dock = new(midpoint)
new_transit_dock.reserved_area = proposal
new_transit_dock.name = "Transit for [M.shuttle_id]/[M.name]"
new_transit_dock.owner = M
- new_transit_dock.assigned_area = A
+ new_transit_dock.assigned_area = new_area
// Add 180, because ports point inwards, rather than outwards
new_transit_dock.setDir(angle2dir(dock_angle))
diff --git a/code/datums/achievements/misc_achievements.dm b/code/datums/achievements/misc_achievements.dm
index 3a62e6c940a..231ab8aaabc 100644
--- a/code/datums/achievements/misc_achievements.dm
+++ b/code/datums/achievements/misc_achievements.dm
@@ -68,6 +68,7 @@
name = "One Lean, Mean, Cleaning Machine"
desc = "How does it feel to know that your workplace values a mop bucket on wheels more than you?" // i can do better than this give me time
database_id = MEDAL_CLEANBOSS
+ icon = "cleanboss"
/datum/award/achievement/misc/rule8
name = "Rule 8"
diff --git a/code/datums/station_traits/negative_traits.dm b/code/datums/station_traits/negative_traits.dm
index 7b0ecf5b097..87e521b9533 100644
--- a/code/datums/station_traits/negative_traits.dm
+++ b/code/datums/station_traits/negative_traits.dm
@@ -381,69 +381,69 @@
/datum/station_trait/revolutionary_trashing/proc/trash_this_place()
for(var/area/station/command/area_to_trash in GLOB.areas)
-
- for(var/turf/current_turf as anything in area_to_trash.get_contained_turfs())
- if(isclosedturf(current_turf))
- continue
- if(prob(25))
- var/obj/effect/decal/cleanable/crayon/created_art
- created_art = new(current_turf, RANDOM_COLOUR, pick(trash_talk))
- created_art.pixel_x = rand(-10, 10)
- created_art.pixel_y = rand(-10, 10)
-
- if(prob(0.01))
- new /obj/effect/mob_spawn/corpse/human/assistant(current_turf)
- continue
-
- for(var/atom/current_thing as anything in current_turf.contents)
- if(istype(current_thing, /obj/machinery/light) && prob(40))
- var/obj/machinery/light/light_to_smash = current_thing
- light_to_smash.break_light_tube(skip_sound_and_sparks = TRUE)
- continue
-
- if(istype(current_thing, /obj/structure/window))
- if(prob(15))
- current_thing.take_damage(rand(30, 90))
- continue
-
- if(istype(current_thing, /obj/structure/table) && prob(40))
- current_thing.take_damage(100)
- continue
-
- if(istype(current_thing, /obj/structure/chair) && prob(60))
- current_thing.take_damage(150)
- continue
-
- if(istype(current_thing, /obj/machinery/computer) && prob(30))
- if(istype(current_thing, /obj/machinery/computer/communications))
- continue //To prevent the shuttle from getting autocalled at the start of the round
- current_thing.take_damage(160)
+ for (var/list/zlevel_turfs as anything in area_to_trash.get_zlevel_turf_lists())
+ for (var/turf/current_turf as anything in zlevel_turfs)
+ if(isclosedturf(current_turf))
continue
-
- if(istype(current_thing, /obj/machinery/vending) && prob(45))
- var/obj/machinery/vending/vendor_to_trash = current_thing
- if(prob(50))
- vendor_to_trash.tilt(get_turf(vendor_to_trash), 0) // crit effects can do some real weird shit, lets disable it
-
- if(prob(50))
- vendor_to_trash.take_damage(150)
- continue
-
- if(istype(current_thing, /obj/structure/fireaxecabinet)) //A staple of revolutionary behavior
- current_thing.take_damage(90)
- continue
-
- if(istype(current_thing, /obj/item/bedsheet/captain))
- new /obj/item/bedsheet/rev(current_thing.loc)
- qdel(current_thing)
- continue
-
- if(istype(current_thing, /obj/item/bedsheet/captain/double))
- new /obj/item/bedsheet/rev/double(current_thing.loc)
- qdel(current_thing)
+ if(prob(25))
+ var/obj/effect/decal/cleanable/crayon/created_art
+ created_art = new(current_turf, RANDOM_COLOUR, pick(trash_talk))
+ created_art.pixel_x = rand(-10, 10)
+ created_art.pixel_y = rand(-10, 10)
+
+ if(prob(0.01))
+ new /obj/effect/mob_spawn/corpse/human/assistant(current_turf)
continue
- CHECK_TICK
+ for(var/atom/current_thing as anything in current_turf.contents)
+ if(istype(current_thing, /obj/machinery/light) && prob(40))
+ var/obj/machinery/light/light_to_smash = current_thing
+ light_to_smash.break_light_tube(skip_sound_and_sparks = TRUE)
+ continue
+
+ if(istype(current_thing, /obj/structure/window))
+ if(prob(15))
+ current_thing.take_damage(rand(30, 90))
+ continue
+
+ if(istype(current_thing, /obj/structure/table) && prob(40))
+ current_thing.take_damage(100)
+ continue
+
+ if(istype(current_thing, /obj/structure/chair) && prob(60))
+ current_thing.take_damage(150)
+ continue
+
+ if(istype(current_thing, /obj/machinery/computer) && prob(30))
+ if(istype(current_thing, /obj/machinery/computer/communications))
+ continue //To prevent the shuttle from getting autocalled at the start of the round
+ current_thing.take_damage(160)
+ continue
+
+ if(istype(current_thing, /obj/machinery/vending) && prob(45))
+ var/obj/machinery/vending/vendor_to_trash = current_thing
+ if(prob(50))
+ vendor_to_trash.tilt(get_turf(vendor_to_trash), 0) // crit effects can do some real weird shit, lets disable it
+
+ if(prob(50))
+ vendor_to_trash.take_damage(150)
+ continue
+
+ if(istype(current_thing, /obj/structure/fireaxecabinet)) //A staple of revolutionary behavior
+ current_thing.take_damage(90)
+ continue
+
+ if(istype(current_thing, /obj/item/bedsheet/captain))
+ new /obj/item/bedsheet/rev(current_thing.loc)
+ qdel(current_thing)
+ continue
+
+ if(istype(current_thing, /obj/item/bedsheet/captain/double))
+ new /obj/item/bedsheet/rev/double(current_thing.loc)
+ qdel(current_thing)
+ continue
+
+ CHECK_TICK
///Station traits that influence the space background and apply some unique effects!
/datum/station_trait/nebula
diff --git a/code/game/area/areas.dm b/code/game/area/areas.dm
index e6002412339..46ee3669b83 100644
--- a/code/game/area/areas.dm
+++ b/code/game/area/areas.dm
@@ -13,14 +13,16 @@
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
invisibility = INVISIBILITY_LIGHTING
- /// List of all turfs currently inside this area. Acts as a filtered bersion of area.contents
- /// For faster lookup (area.contents is actually a filtered loop over world)
+ /// List of all turfs currently inside this area as nested lists indexed by zlevel.
+ /// Acts as a filtered bersion of area.contents For faster lookup
+ /// (area.contents is actually a filtered loop over world)
/// Semi fragile, but it prevents stupid so I think it's worth it
- var/list/turf/contained_turfs = list()
- /// Contained turfs is a MASSIVE list, so rather then adding/removing from it each time we have a problem turf
+ var/list/list/turf/turfs_by_zlevel = list()
+ /// turfs_by_z_level can become MASSIVE lists, so rather then adding/removing from it each time we have a problem turf
/// We should instead store a list of turfs to REMOVE from it, then hook into a getter for it
/// There is a risk of this and contained_turfs leaking, so a subsystem will run it down to 0 incrementally if it gets too large
- var/list/turf/turfs_to_uncontain = list()
+ /// This uses the same nested list format as turfs_by_zlevel
+ var/list/list/turf/turfs_to_uncontain_by_zlevel = list()
var/area_flags = VALID_TERRITORY | BLOBS_ALLOWED | UNIQUE_AREA | CULT_PERMITTED
@@ -224,24 +226,93 @@ GLOBAL_LIST_EMPTY(teleportlocs)
turfs += T
map_generator.generate_terrain(turfs, src)
-/area/proc/get_contained_turfs()
- if(length(turfs_to_uncontain))
+/// Returns the highest zlevel that this area contains turfs for
+/area/proc/get_highest_zlevel()
+ for (var/area_zlevel in length(turfs_by_zlevel) to 1 step -1)
+ if (length(turfs_to_uncontain_by_zlevel) >= area_zlevel)
+ if (length(turfs_by_zlevel[area_zlevel]) - length(turfs_to_uncontain_by_zlevel[area_zlevel]) > 0)
+ return area_zlevel
+ else
+ if (length(turfs_by_zlevel[area_zlevel]))
+ return area_zlevel
+ return 0
+
+/// Returns a nested list of lists with all turfs split by zlevel.
+/// only zlevels with turfs are returned. The order of the list is not guaranteed.
+/area/proc/get_zlevel_turf_lists()
+ if(length(turfs_to_uncontain_by_zlevel))
cannonize_contained_turfs()
- return contained_turfs
+
+ var/list/zlevel_turf_lists = list()
+
+ for (var/list/zlevel_turfs as anything in turfs_by_zlevel)
+ if (length(zlevel_turfs))
+ zlevel_turf_lists[++zlevel_turf_lists.len] = zlevel_turfs
+
+ return zlevel_turf_lists
+
+/// Returns a list with all turfs in this zlevel.
+/area/proc/get_turfs_by_zlevel(zlevel)
+ if (length(turfs_to_uncontain_by_zlevel) >= zlevel && length(turfs_to_uncontain_by_zlevel[zlevel]))
+ cannonize_contained_turfs_by_zlevel(zlevel)
+
+ if (length(turfs_by_zlevel) < zlevel)
+ return list()
+
+ return turfs_by_zlevel[zlevel]
+
+
+/// Merges a list containing all of the turfs zlevel lists from get_zlevel_turf_lists inside one list. Use get_zlevel_turf_lists() or get_turfs_by_zlevel() unless you need all the turfs in one list to avoid generating large lists
+/area/proc/get_turfs_from_all_zlevels()
+ . = list()
+ for (var/list/zlevel_turfs as anything in get_zlevel_turf_lists())
+ . += zlevel_turfs
/// Ensures that the contained_turfs list properly represents the turfs actually inside us
-/area/proc/cannonize_contained_turfs()
+/area/proc/cannonize_contained_turfs_by_zlevel(zlevel_to_clean, _autoclean = TRUE)
// This is massively suboptimal for LARGE removal lists
// Try and keep the mass removal as low as you can. We'll do this by ensuring
// We only actually add to contained turfs after large changes (Also the management subsystem)
// Do your damndest to keep turfs out of /area/space as a stepping stone
- // That sucker gets HUGE and will make this take actual tens of seconds if you stuff turfs_to_uncontain
- contained_turfs -= turfs_to_uncontain
- turfs_to_uncontain = list()
+ // That sucker gets HUGE and will make this take actual seconds
+ if (zlevel_to_clean <= length(turfs_by_zlevel) && zlevel_to_clean <= length(turfs_to_uncontain_by_zlevel))
+ turfs_by_zlevel[zlevel_to_clean] -= turfs_to_uncontain_by_zlevel[zlevel_to_clean]
+
+ if (_autoclean) // Removes empty lists from the end of this list
+ var/new_length = length(turfs_to_uncontain_by_zlevel)
+ // Walk backwards thru the list
+ for (var/i in length(turfs_to_uncontain_by_zlevel) to 0 step -1)
+ if (i && length(turfs_to_uncontain_by_zlevel[i]))
+ break // Stop the moment we find a useful list
+ new_length = i
+
+ if (new_length < length(turfs_to_uncontain_by_zlevel))
+ turfs_to_uncontain_by_zlevel.len = new_length
+
+ if (new_length >= zlevel_to_clean)
+ turfs_to_uncontain_by_zlevel[zlevel_to_clean] = list()
+ else
+ turfs_to_uncontain_by_zlevel[zlevel_to_clean] = list()
+
+
+/// Ensures that the contained_turfs list properly represents the turfs actually inside us
+/area/proc/cannonize_contained_turfs()
+ for (var/area_zlevel in 1 to length(turfs_to_uncontain_by_zlevel))
+ cannonize_contained_turfs_by_zlevel(area_zlevel, _autoclean = FALSE)
+
+ turfs_to_uncontain_by_zlevel = list()
+
/// Returns TRUE if we have contained turfs, FALSE otherwise
/area/proc/has_contained_turfs()
- return length(contained_turfs) - length(turfs_to_uncontain) > 0
+ for (var/area_zlevel in 1 to length(turfs_by_zlevel))
+ if (length(turfs_to_uncontain_by_zlevel) >= area_zlevel)
+ if (length(turfs_by_zlevel[area_zlevel]) - length(turfs_to_uncontain_by_zlevel[area_zlevel]) > 0)
+ return TRUE
+ else
+ if (length(turfs_by_zlevel[area_zlevel]))
+ return TRUE
+ return FALSE
/**
* Register this area as belonging to a z level
@@ -286,8 +357,8 @@ GLOBAL_LIST_EMPTY(teleportlocs)
air_vents = null
air_scrubbers = null
//turf cleanup
- contained_turfs = null
- turfs_to_uncontain = null
+ turfs_by_zlevel = null
+ turfs_to_uncontain_by_zlevel = null
//parent cleanup
return ..()
@@ -521,8 +592,9 @@ GLOBAL_LIST_EMPTY(teleportlocs)
if(outdoors)
return FALSE
areasize = 0
- for(var/turf/open/T in get_contained_turfs())
- areasize++
+ for (var/list/zlevel_turfs as anything in get_zlevel_turf_lists())
+ for(var/turf/open/thisvarisunused in zlevel_turfs)
+ areasize++
/**
* Causes a runtime error
diff --git a/code/game/atom/_atom.dm b/code/game/atom/_atom.dm
index 66ed2048665..5bd8346f57f 100644
--- a/code/game/atom/_atom.dm
+++ b/code/game/atom/_atom.dm
@@ -296,7 +296,7 @@
if(mobile_docking_port.launch_status != check_for_launch_status)
continue
for(var/area/shuttle/shuttle_area as anything in mobile_docking_port.shuttle_areas)
- if(current_turf in shuttle_area.get_contained_turfs())
+ if(shuttle_area == current_turf.loc)
return TRUE
return FALSE
diff --git a/code/game/objects/items/blueprints.dm b/code/game/objects/items/blueprints.dm
index 4ccbf8319b0..82fc3437f20 100644
--- a/code/game/objects/items/blueprints.dm
+++ b/code/game/objects/items/blueprints.dm
@@ -238,7 +238,7 @@
return
//stuff tied to the area to rename
- var/list/to_rename = list(
+ var/static/list/to_rename = typecacheof(list(
/obj/machinery/airalarm,
/obj/machinery/atmospherics/components/unary/vent_scrubber,
/obj/machinery/atmospherics/components/unary/vent_pump,
@@ -246,11 +246,9 @@
/obj/machinery/firealarm,
/obj/machinery/light_switch,
/obj/machinery/power/apc,
- )
-
- for(var/turf/area_turf as anything in area.get_contained_turfs())
- for(var/obj/machine in area_turf)
- if(!is_type_in_list(machine, to_rename))
- continue
- machine.name = replacetext(machine.name, oldtitle, title)
+ ))
+ for (var/list/zlevel_turfs as anything in area.get_zlevel_turf_lists())
+ for (var/turf/area_turf as anything in zlevel_turfs)
+ for(var/obj/machine as anything in typecache_filter_list(area_turf.contents, to_rename))
+ machine.name = replacetext(machine.name, oldtitle, title)
//TODO: much much more. Unnamed airlocks, cameras, etc.
diff --git a/code/game/turfs/turf.dm b/code/game/turfs/turf.dm
index 8f29bb2e2c2..d62d523321b 100644
--- a/code/game/turfs/turf.dm
+++ b/code/game/turfs/turf.dm
@@ -228,9 +228,11 @@ GLOBAL_LIST_EMPTY(station_turfs)
return
//move the turf
- old_area.turfs_to_uncontain += src
+ LISTASSERTLEN(old_area.turfs_to_uncontain_by_zlevel, z, list())
+ LISTASSERTLEN(new_area.turfs_by_zlevel, z, list())
+ old_area.turfs_to_uncontain_by_zlevel[z] += src
+ new_area.turfs_by_zlevel[z] += src
new_area.contents += src
- new_area.contained_turfs += src
//changes to make after turf has moved
on_change_area(old_area, new_area)
diff --git a/code/game/world.dm b/code/game/world.dm
index 99af6a6dbf0..ba8c653088d 100644
--- a/code/game/world.dm
+++ b/code/game/world.dm
@@ -389,7 +389,7 @@ GLOBAL_VAR(restart_counter)
else if(SSticker.current_state == GAME_STATE_SETTING_UP)
new_status += "
Starting: Now"
else if(SSticker.IsRoundInProgress())
- new_status += "
Time: [time2text(((world.time - SSticker.round_start_time)/10), "hh:mm")]"
+ new_status += "
Time: [time2text(STATION_TIME_PASSED(), "hh:mm")]"
if(SSshuttle?.emergency && SSshuttle?.emergency?.mode != (SHUTTLE_IDLE || SHUTTLE_ENDGAME))
new_status += " | Shuttle: [SSshuttle.emergency.getModeStr()] [SSshuttle.emergency.getTimerStr()]"
else if(SSticker.current_state == GAME_STATE_FINISHED)
@@ -424,10 +424,13 @@ GLOBAL_VAR(restart_counter)
if(!map_load_z_cutoff)
return
var/area/global_area = GLOB.areas_by_type[world.area] // We're guaranteed to be touching the global area, so we'll just do this
- var/list/to_add = block(
- locate(old_max + 1, 1, 1),
- locate(maxx, maxy, map_load_z_cutoff))
- global_area.contained_turfs += to_add
+ LISTASSERTLEN(global_area.turfs_by_zlevel, map_load_z_cutoff, list())
+ for (var/zlevel in 1 to map_load_z_cutoff)
+ var/list/to_add = block(
+ locate(old_max + 1, 1, zlevel),
+ locate(maxx, maxy, zlevel))
+
+ global_area.turfs_by_zlevel[zlevel] += to_add
/world/proc/increase_max_y(new_maxy, map_load_z_cutoff = maxz)
if(new_maxy <= maxy)
@@ -437,10 +440,12 @@ GLOBAL_VAR(restart_counter)
if(!map_load_z_cutoff)
return
var/area/global_area = GLOB.areas_by_type[world.area] // We're guarenteed to be touching the global area, so we'll just do this
- var/list/to_add = block(
- locate(1, old_maxy + 1, 1),
- locate(maxx, maxy, map_load_z_cutoff))
- global_area.contained_turfs += to_add
+ LISTASSERTLEN(global_area.turfs_by_zlevel, map_load_z_cutoff, list())
+ for (var/zlevel in 1 to map_load_z_cutoff)
+ var/list/to_add = block(
+ locate(1, old_maxy + 1, 1),
+ locate(maxx, maxy, map_load_z_cutoff))
+ global_area.turfs_by_zlevel[zlevel] += to_add
/world/proc/incrementMaxZ()
maxz++
diff --git a/code/modules/admin/verbs/adminjump.dm b/code/modules/admin/verbs/adminjump.dm
index 1bdde584691..dfcc5f60dd0 100644
--- a/code/modules/admin/verbs/adminjump.dm
+++ b/code/modules/admin/verbs/adminjump.dm
@@ -10,10 +10,10 @@
return
var/list/turfs = list()
- for(var/turf/T in A.get_contained_turfs())
- if(T.density)
- continue
- turfs.Add(T)
+ for (var/list/zlevel_turfs as anything in A.get_zlevel_turf_lists())
+ for (var/turf/area_turf as anything in zlevel_turfs)
+ if(!area_turf.density)
+ turfs.Add(area_turf)
if(length(turfs))
var/turf/T = pick(turfs)
diff --git a/code/modules/antagonists/nukeop/nukeop.dm b/code/modules/antagonists/nukeop/nukeop.dm
index 68fe9b24778..f2a43718067 100644
--- a/code/modules/antagonists/nukeop/nukeop.dm
+++ b/code/modules/antagonists/nukeop/nukeop.dm
@@ -596,7 +596,7 @@
break
var/list/turf/options = list()
- for(var/turf/open/open_turf in spawn_in?.get_contained_turfs())
+ for(var/turf/open/open_turf in spawn_in?.get_turfs_from_all_zlevels())
if(open_turf.is_blocked_turf())
continue
options += open_turf
diff --git a/code/modules/antagonists/traitor/objectives/kidnapping.dm b/code/modules/antagonists/traitor/objectives/kidnapping.dm
index 3a614b348b0..5132815a864 100644
--- a/code/modules/antagonists/traitor/objectives/kidnapping.dm
+++ b/code/modules/antagonists/traitor/objectives/kidnapping.dm
@@ -275,7 +275,8 @@
return
var/list/possible_turfs = list()
- for(var/turf/open/open_turf in dropoff_area.get_contained_turfs())
+
+ for(var/turf/open/open_turf in dropoff_area.get_turfs_from_all_zlevels())
if(open_turf.is_blocked_turf() || isspaceturf(open_turf))
continue
possible_turfs += open_turf
diff --git a/code/modules/bitrunning/objects/landmarks.dm b/code/modules/bitrunning/objects/landmarks.dm
index 0eb26b9c588..3a90939dae1 100644
--- a/code/modules/bitrunning/objects/landmarks.dm
+++ b/code/modules/bitrunning/objects/landmarks.dm
@@ -39,14 +39,15 @@
var/obj/structure/closet/crate/secure/bitrunning/encrypted/encrypted_crate
var/area/my_area = get_area(src)
- for(var/turf/area_turf as anything in my_area.get_contained_turfs())
- for(var/obj/structure/closet/crate/crate_to_check in area_turf)
- if(istype(crate_to_check, /obj/structure/closet/crate/secure/bitrunning/encrypted))
- encrypted_crate = crate_to_check
- crate_to_check.desc += span_hypnophrase(" This feels like the crate we're looking for!")
- else
- crate_list += crate_to_check
- crate_to_check.name = "Unidentified Crate"
+ for (var/list/zlevel_turfs as anything in my_area.get_zlevel_turf_lists())
+ for (var/turf/area_turf as anything in zlevel_turfs)
+ for(var/obj/structure/closet/crate/crate_to_check in area_turf)
+ if(istype(crate_to_check, /obj/structure/closet/crate/secure/bitrunning/encrypted))
+ encrypted_crate = crate_to_check
+ crate_to_check.desc += span_hypnophrase(" This feels like the crate we're looking for!")
+ else
+ crate_list += crate_to_check
+ crate_to_check.name = "Unidentified Crate"
if(!encrypted_crate)
stack_trace("Bitrunning Goal Crate Randomizer failed to find an encrypted crate to swap positions for.")
diff --git a/code/modules/cargo/expressconsole.dm b/code/modules/cargo/expressconsole.dm
index 6babda045e0..79ae0b0fbf8 100644
--- a/code/modules/cargo/expressconsole.dm
+++ b/code/modules/cargo/expressconsole.dm
@@ -185,7 +185,7 @@
if (!landingzone)
WARNING("[src] couldnt find a Quartermaster/Storage (aka cargobay) area on the station, and as such it has set the supplypod landingzone to the area it resides in.")
landingzone = get_area(src)
- for(var/turf/open/floor/T in landingzone.get_contained_turfs())//uses default landing zone
+ for(var/turf/open/floor/T in landingzone.get_turfs_from_all_zlevels())//uses default landing zone
if(T.is_blocked_turf())
continue
LAZYADD(empty_turfs, T)
@@ -204,7 +204,7 @@
else
if(SO.pack.get_cost() * (0.72*MAX_EMAG_ROCKETS) <= points_to_check) // bulk discount :^)
landingzone = GLOB.areas_by_type[pick(GLOB.the_station_areas)] //override default landing zone
- for(var/turf/open/floor/T in landingzone.get_contained_turfs())
+ for(var/turf/open/floor/T in landingzone.get_turfs_from_all_zlevels())
if(T.is_blocked_turf())
continue
LAZYADD(empty_turfs, T)
diff --git a/code/modules/clothing/glasses/engine_goggles.dm b/code/modules/clothing/glasses/engine_goggles.dm
index d91a5c3bb4a..c9fbb159126 100644
--- a/code/modules/clothing/glasses/engine_goggles.dm
+++ b/code/modules/clothing/glasses/engine_goggles.dm
@@ -97,15 +97,16 @@
return
var/list/shuttle_areas = port.shuttle_areas
for(var/area/region as anything in shuttle_areas)
- for(var/turf/place as anything in region.get_contained_turfs())
- if(get_dist(user, place) > 7)
- continue
- var/image/pic
- if(isshuttleturf(place))
- pic = new('icons/turf/overlays.dmi', place, "greenOverlay", AREA_LAYER)
- else
- pic = new('icons/turf/overlays.dmi', place, "redOverlay", AREA_LAYER)
- flick_overlay_global(pic, list(user.client), 8)
+ for (var/list/zlevel_turfs as anything in region.get_zlevel_turf_lists())
+ for (var/turf/place as anything in zlevel_turfs)
+ if(get_dist(user, place) > 7)
+ continue
+ var/image/pic
+ if(isshuttleturf(place))
+ pic = new('icons/turf/overlays.dmi', place, "greenOverlay", AREA_LAYER)
+ else
+ pic = new('icons/turf/overlays.dmi', place, "redOverlay", AREA_LAYER)
+ flick_overlay_global(pic, list(user.client), 8)
/obj/item/clothing/glasses/meson/engine/proc/show_connections()
var/mob/living/carbon/human/user = loc
diff --git a/code/modules/events/aurora_caelus.dm b/code/modules/events/aurora_caelus.dm
index 8efe01169ce..34681825502 100644
--- a/code/modules/events/aurora_caelus.dm
+++ b/code/modules/events/aurora_caelus.dm
@@ -52,7 +52,7 @@
set_starlight(aurora_color)
for(var/area/station/service/kitchen/affected_area in GLOB.areas)
- for(var/turf/open/kitchen_floor in affected_area.get_contained_turfs())
+ for(var/turf/open/kitchen_floor in affected_area.get_turfs_from_all_zlevels())
kitchen_floor.set_light(l_color = aurora_color)
/datum/round_event/aurora_caelus/end()
@@ -108,9 +108,9 @@
var/walked_range = LERP(start_range, end_range, i/5)
var/walked_power = LERP(start_power, end_power, i/5)
for(var/area/station/service/kitchen/affected_area in GLOB.areas)
- for(var/turf/open/kitchen_floor in affected_area.get_contained_turfs())
+ for(var/turf/open/kitchen_floor in affected_area.get_turfs_from_all_zlevels())
kitchen_floor.set_light(walked_range, walked_power, walked_color)
sleep(8 SECONDS)
for(var/area/station/service/kitchen/affected_area in GLOB.areas)
- for(var/turf/open/kitchen_floor in affected_area.get_contained_turfs())
+ for(var/turf/open/kitchen_floor in affected_area.get_turfs_from_all_zlevels())
kitchen_floor.set_light(end_range, end_power, end_color)
diff --git a/code/modules/events/shuttle_loan/shuttle_loan_event.dm b/code/modules/events/shuttle_loan/shuttle_loan_event.dm
index e0b5cccb1ee..ac5faa6971f 100644
--- a/code/modules/events/shuttle_loan/shuttle_loan_event.dm
+++ b/code/modules/events/shuttle_loan/shuttle_loan_event.dm
@@ -80,7 +80,7 @@
var/list/blocked_shutte_turfs = list()
var/list/area/shuttle/shuttle_areas = SSshuttle.supply.shuttle_areas
for(var/area/shuttle/shuttle_area as anything in shuttle_areas)
- for(var/turf/open/floor/shuttle_turf in shuttle_area.get_contained_turfs())
+ for(var/turf/open/floor/shuttle_turf in shuttle_area.get_turfs_from_all_zlevels())
if(shuttle_turf.is_blocked_turf())
blocked_shutte_turfs += shuttle_turf
continue
diff --git a/code/modules/events/space_vines/vine_event.dm b/code/modules/events/space_vines/vine_event.dm
index 46cf91a33e6..03350abdb09 100644
--- a/code/modules/events/space_vines/vine_event.dm
+++ b/code/modules/events/space_vines/vine_event.dm
@@ -38,7 +38,7 @@
var/obj/structure/spacevine/vine = new()
for(var/area/station/hallway/area in GLOB.areas)
- for(var/turf/open/floor in area.get_contained_turfs())
+ for(var/turf/open/floor in area.get_turfs_from_all_zlevels())
if(floor.Enter(vine))
turfs += floor
diff --git a/code/modules/holodeck/items.dm b/code/modules/holodeck/items.dm
index 10229929c84..eb0261739ad 100644
--- a/code/modules/holodeck/items.dm
+++ b/code/modules/holodeck/items.dm
@@ -119,11 +119,12 @@
var/numbuttons = 0
var/numready = 0
- for(var/turf/area_turf as anything in currentarea.get_contained_turfs())
- for(var/obj/machinery/readybutton/button in area_turf)
- numbuttons++
- if(button.ready)
- numready++
+ for (var/list/zlevel_turfs as anything in currentarea.get_zlevel_turf_lists())
+ for (var/turf/area_turf as anything in zlevel_turfs)
+ for(var/obj/machinery/readybutton/button in area_turf)
+ numbuttons++
+ if(button.ready)
+ numready++
if(numbuttons == numready)
begin_event()
@@ -136,13 +137,14 @@
eventstarted = TRUE
- for(var/turf/area_turf as anything in currentarea.get_contained_turfs())
- for(var/obj/structure/window/barrier in area_turf)
- if((barrier.obj_flags & NO_DECONSTRUCTION) || (barrier.flags_1 & HOLOGRAM_1))// Just in case: only holo-windows
- qdel(barrier)
+ for (var/list/zlevel_turfs as anything in currentarea.get_zlevel_turf_lists())
+ for(var/turf/area_turf as anything in zlevel_turfs)
+ for(var/obj/structure/window/barrier in area_turf)
+ if((barrier.obj_flags & NO_DECONSTRUCTION) || (barrier.flags_1 & HOLOGRAM_1))// Just in case: only holo-windows
+ qdel(barrier)
- for(var/mob/contestant in area_turf)
- to_chat(contestant, span_userdanger("FIGHT!"))
+ for(var/mob/contestant in area_turf)
+ to_chat(contestant, span_userdanger("FIGHT!"))
/obj/machinery/conveyor/holodeck
diff --git a/code/modules/lighting/lighting_area.dm b/code/modules/lighting/lighting_area.dm
index c6f427f592f..84170b6964f 100644
--- a/code/modules/lighting/lighting_area.dm
+++ b/code/modules/lighting/lighting_area.dm
@@ -51,9 +51,10 @@
UnregisterSignal(SSdcs, COMSIG_STARLIGHT_COLOR_CHANGED)
var/list/z_offsets = SSmapping.z_level_to_plane_offset
if(length(lighting_effects) > 1)
- for(var/turf/T as anything in get_contained_turfs())
- if(z_offsets[T.z])
- T.cut_overlay(lighting_effects[z_offsets[T.z] + 1])
+ for(var/area_zlevel as anything in 1 to get_highest_zlevel())
+ if(z_offsets[area_zlevel])
+ for(var/turf/T as anything in get_turfs_by_zlevel(area_zlevel))
+ T.cut_overlay(lighting_effects[z_offsets[T.z] + 1])
cut_overlay(lighting_effects[1])
lighting_effects = null
area_has_base_lighting = FALSE
@@ -91,17 +92,18 @@
add_overlay(lighting_effects[1])
var/list/z_offsets = SSmapping.z_level_to_plane_offset
- if(length(lighting_effects) > 1)
- // This inside loop is EXTREMELY hot because it's run by space tiles. Don't want no part in that
- for(var/turf/T as anything in get_contained_turfs())
- T.luminosity = 1
- // We will only add overlays to turfs not on the first z layer, because that's a significantly lesser portion
- // And we need to do them separate, or lighting will go fuckey
- if(z_offsets[T.z])
- T.add_overlay(lighting_effects[z_offsets[T.z] + 1])
- else
- for(var/turf/T as anything in get_contained_turfs())
- T.luminosity = 1
+ for (var/area_zlevel in 1 to get_highest_zlevel())
+ // We will only add overlays to turfs not on the first z layer, because that's a significantly lesser portion
+ // And we need to do them separate, or lighting will go fuckey
+ // This inside loop is EXTREMELY hot because it's run by space tiles, so we do the if check once on the outside
+ if(length(lighting_effects) > 1 && z_offsets[area_zlevel])
+ var/lighting_effect_to_add = lighting_effects[z_offsets[area_zlevel] + 1]
+ for(var/turf/area_turf as anything in get_turfs_by_zlevel(area_zlevel))
+ area_turf.luminosity = 1
+ area_turf.add_overlay(lighting_effect_to_add)
+ else
+ for(var/turf/area_turf as anything in get_turfs_by_zlevel(area_zlevel))
+ area_turf.luminosity = 1
area_has_base_lighting = TRUE
diff --git a/code/modules/lighting/lighting_setup.dm b/code/modules/lighting/lighting_setup.dm
index d6bf19711ef..c148530d1cd 100644
--- a/code/modules/lighting/lighting_setup.dm
+++ b/code/modules/lighting/lighting_setup.dm
@@ -1,12 +1,12 @@
/proc/create_all_lighting_objects()
- for(var/area/A as anything in GLOB.areas)
- if(!A.static_lighting)
+ for(var/area/area as anything in GLOB.areas)
+ if(!area.static_lighting)
continue
-
- for(var/turf/T as anything in A.get_contained_turfs())
- if(T.space_lit)
- continue
- new /datum/lighting_object(T)
+ for (var/list/zlevel_turfs as anything in area.get_zlevel_turf_lists())
+ for(var/turf/area_turf as anything in zlevel_turfs)
+ if(area_turf.space_lit)
+ continue
+ new /datum/lighting_object(area_turf)
CHECK_TICK
CHECK_TICK
diff --git a/code/modules/mapping/mapping_helpers.dm b/code/modules/mapping/mapping_helpers.dm
index 9c5ac879aee..7676801a0a9 100644
--- a/code/modules/mapping/mapping_helpers.dm
+++ b/code/modules/mapping/mapping_helpers.dm
@@ -867,11 +867,12 @@ INITIALIZE_IMMEDIATE(/obj/effect/mapping_helpers/no_lava)
/obj/effect/mapping_helpers/dead_body_placer/LateInitialize()
var/area/morgue_area = get_area(src)
var/list/obj/structure/bodycontainer/morgue/trays = list()
- for(var/turf/area_turf as anything in morgue_area.get_contained_turfs())
- var/obj/structure/bodycontainer/morgue/morgue_tray = locate() in area_turf
- if(isnull(morgue_tray) || !morgue_tray.beeper || morgue_tray.connected.loc != morgue_tray)
- continue
- trays += morgue_tray
+ for (var/list/zlevel_turfs as anything in morgue_area.get_zlevel_turf_lists())
+ for(var/turf/area_turf as anything in zlevel_turfs)
+ var/obj/structure/bodycontainer/morgue/morgue_tray = locate() in area_turf
+ if(isnull(morgue_tray) || !morgue_tray.beeper || morgue_tray.connected.loc != morgue_tray)
+ continue
+ trays += morgue_tray
var/numtrays = length(trays)
if(numtrays == 0)
@@ -959,14 +960,15 @@ INITIALIZE_IMMEDIATE(/obj/effect/mapping_helpers/no_lava)
var/list/table_turfs = list()
var/list/open_turfs = list()
var/turf/dogbed_turf
- for(var/turf/area_turf as anything in celebration_area.get_contained_turfs())
- if(locate(/obj/structure/table/reinforced) in area_turf)
- table_turfs += area_turf
- if(locate(/obj/structure/bed/dogbed/ian) in area_turf)
- dogbed_turf = area_turf
- if(isopenturf(area_turf))
- new /obj/effect/decal/cleanable/confetti(area_turf)
- open_turfs += area_turf
+ for (var/list/zlevel_turfs as anything in celebration_area.get_zlevel_turf_lists())
+ for(var/turf/area_turf as anything in zlevel_turfs)
+ if(locate(/obj/structure/table/reinforced) in area_turf)
+ table_turfs += area_turf
+ if(locate(/obj/structure/bed/dogbed/ian) in area_turf)
+ dogbed_turf = area_turf
+ if(isopenturf(area_turf))
+ new /obj/effect/decal/cleanable/confetti(area_turf)
+ open_turfs += area_turf
if(isnull(dogbed_turf) && map_warning)
log_mapping("[src] in [celebration_area] could not find Ian's dogbed.")
@@ -1031,11 +1033,12 @@ INITIALIZE_IMMEDIATE(/obj/effect/mapping_helpers/no_lava)
var/area/celebration_area = get_area(src)
var/list/table_turfs = list()
var/turf/dogbed_turf
- for(var/turf/area_turf as anything in celebration_area.get_contained_turfs())
- if(locate(/obj/structure/table/reinforced) in area_turf)
- table_turfs += area_turf
- if(locate(/obj/structure/bed/dogbed/ian) in area_turf)
- dogbed_turf = area_turf
+ for (var/list/zlevel_turfs as anything in celebration_area.get_zlevel_turf_lists())
+ for(var/turf/area_turf as anything in zlevel_turfs)
+ if(locate(/obj/structure/table/reinforced) in area_turf)
+ table_turfs += area_turf
+ if(locate(/obj/structure/bed/dogbed/ian) in area_turf)
+ dogbed_turf = area_turf
if(isnull(dogbed_turf))
log_mapping("[src] in [celebration_area] could not find Ian's dogbed.")
diff --git a/code/modules/mapping/reader.dm b/code/modules/mapping/reader.dm
index ab6743ff17a..b8db314117f 100644
--- a/code/modules/mapping/reader.dm
+++ b/code/modules/mapping/reader.dm
@@ -930,8 +930,10 @@ GLOBAL_LIST_EMPTY(map_model_default)
if(!new_z)
old_area = crds.loc
- old_area.turfs_to_uncontain += crds
- area_instance.contained_turfs.Add(crds)
+ LISTASSERTLEN(old_area.turfs_to_uncontain_by_zlevel, crds.z, list())
+ LISTASSERTLEN(area_instance.turfs_by_zlevel, crds.z, list())
+ old_area.turfs_to_uncontain_by_zlevel[crds.z] += crds
+ area_instance.turfs_by_zlevel[crds.z] += crds
area_instance.contents.Add(crds)
if(GLOB.use_preloader)
diff --git a/code/modules/mapping/space_management/space_reservation.dm b/code/modules/mapping/space_management/space_reservation.dm
index 2809ae65e6c..04ba59ab5a4 100644
--- a/code/modules/mapping/space_management/space_reservation.dm
+++ b/code/modules/mapping/space_management/space_reservation.dm
@@ -81,9 +81,13 @@
for(var/turf/cordon_turf as anything in cordon_turfs)
var/area/misc/cordon/cordon_area = GLOB.areas_by_type[/area/misc/cordon] || new
var/area/old_area = cordon_turf.loc
- old_area.turfs_to_uncontain += cordon_turf
- cordon_area.contained_turfs += cordon_turf
+
+ LISTASSERTLEN(old_area.turfs_to_uncontain_by_zlevel, cordon_turf.z, list())
+ LISTASSERTLEN(cordon_area.turfs_by_zlevel, cordon_turf.z, list())
+ old_area.turfs_to_uncontain_by_zlevel[cordon_turf.z] += cordon_turf
+ cordon_area.turfs_by_zlevel[cordon_turf.z] += cordon_turf
cordon_area.contents += cordon_turf
+
// Its no longer unused, but its also not "used"
cordon_turf.turf_flags &= ~UNUSED_RESERVATION_TURF
cordon_turf.ChangeTurf(/turf/cordon, /turf/cordon)
diff --git a/code/modules/mob/living/simple_animal/bot/mulebot.dm b/code/modules/mob/living/simple_animal/bot/mulebot.dm
index 88210a714c8..7e3b0ab0c2e 100644
--- a/code/modules/mob/living/simple_animal/bot/mulebot.dm
+++ b/code/modules/mob/living/simple_animal/bot/mulebot.dm
@@ -815,39 +815,37 @@
load(AM)
-/mob/living/simple_animal/bot/mulebot/paranormal/load(atom/movable/AM)
- if(load || AM.anchored)
+/mob/living/simple_animal/bot/mulebot/paranormal/load(atom/movable/movable_atom)
+ if(load || movable_atom.anchored)
return
- if(!isturf(AM.loc)) //To prevent the loading from stuff from someone's inventory or screen icons.
+ if(!isturf(movable_atom.loc)) //To prevent the loading from stuff from someone's inventory or screen icons.
return
- if(isobserver(AM))
+ if(isobserver(movable_atom))
visible_message(span_warning("A ghostly figure appears on [src]!"))
- RegisterSignal(AM, COMSIG_MOVABLE_MOVED, PROC_REF(ghostmoved))
- AM.forceMove(src)
+ movable_atom.forceMove(src)
+ RegisterSignal(movable_atom, COMSIG_MOVABLE_MOVED, PROC_REF(ghostmoved))
else if(!wires.is_cut(WIRE_LOADCHECK))
buzz(SIGH)
return // if not hacked, only allow ghosts to be loaded
- else if(isobj(AM))
- var/obj/O = AM
- if(O.has_buckled_mobs() || (locate(/mob) in AM)) //can't load non crates objects with mobs buckled to it or inside it.
+ else if(isobj(movable_atom))
+ if(movable_atom.has_buckled_mobs() || (locate(/mob) in movable_atom)) //can't load non crates objects with mobs buckled to it or inside it.
buzz(SIGH)
return
- if(istype(O, /obj/structure/closet/crate))
- var/obj/structure/closet/crate/crate = O
+ if(istype(movable_atom, /obj/structure/closet/crate))
+ var/obj/structure/closet/crate/crate = movable_atom
crate.close() //make sure it's closed
- O.forceMove(src)
+ movable_atom.forceMove(src)
- else if(isliving(AM))
- if(!load_mob(AM)) //buckling is handled in forceMove()
- return
+ else if(isliving(movable_atom) && !load_mob(movable_atom))
+ return
- load = AM
+ load = movable_atom
mode = BOT_IDLE
update_appearance()
diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm
index 6be52bf1c85..e394ea18db9 100644
--- a/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm
+++ b/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm
@@ -366,7 +366,7 @@
var/area/current_area = get_area(src)
if (current_area in converted_areas)
return FALSE
- terrain_theme.apply_theme_to_list_of_turfs(current_area.get_contained_turfs())
+ terrain_theme.apply_theme_to_list_of_turfs(current_area.get_turfs_from_all_zlevels())
converted_areas += current_area
return TRUE
diff --git a/code/modules/power/apc/apc_main.dm b/code/modules/power/apc/apc_main.dm
index cbc1db293e7..b11b6c8b413 100644
--- a/code/modules/power/apc/apc_main.dm
+++ b/code/modules/power/apc/apc_main.dm
@@ -463,12 +463,13 @@
update()
if("emergency_lighting")
emergency_lights = !emergency_lights
- for(var/turf/area_turf as anything in area.get_contained_turfs())
- for(var/obj/machinery/light/area_light in area_turf)
- if(!initial(area_light.no_low_power)) //If there was an override set on creation, keep that override
- area_light.no_low_power = emergency_lights
- INVOKE_ASYNC(area_light, TYPE_PROC_REF(/obj/machinery/light/, update), FALSE)
- CHECK_TICK
+ for (var/list/zlevel_turfs as anything in area.get_zlevel_turf_lists())
+ for(var/turf/area_turf as anything in zlevel_turfs)
+ for(var/obj/machinery/light/area_light in area_turf)
+ if(!initial(area_light.no_low_power)) //If there was an override set on creation, keep that override
+ area_light.no_low_power = emergency_lights
+ INVOKE_ASYNC(area_light, TYPE_PROC_REF(/obj/machinery/light/, update), FALSE)
+ CHECK_TICK
return TRUE
/obj/machinery/power/apc/ui_close(mob/user)
@@ -678,11 +679,12 @@
INVOKE_ASYNC(src, PROC_REF(break_lights))
/obj/machinery/power/apc/proc/break_lights()
- for(var/turf/area_turf as anything in area.get_contained_turfs())
- for(var/obj/machinery/light/breaked_light in area_turf)
- breaked_light.on = TRUE
- breaked_light.break_light_tube()
- stoplag()
+ for (var/list/zlevel_turfs as anything in area.get_zlevel_turf_lists())
+ for(var/turf/area_turf as anything in zlevel_turfs)
+ for(var/obj/machinery/light/breaked_light in area_turf)
+ breaked_light.on = TRUE
+ breaked_light.break_light_tube()
+ stoplag()
/obj/machinery/power/apc/should_atmos_process(datum/gas_mixture/air, exposed_temperature)
return (exposed_temperature > 2000)
diff --git a/code/modules/power/apc/apc_power_proc.dm b/code/modules/power/apc/apc_power_proc.dm
index 6118791c15c..52a671f00f5 100644
--- a/code/modules/power/apc/apc_power_proc.dm
+++ b/code/modules/power/apc/apc_power_proc.dm
@@ -138,9 +138,10 @@
if(nightshift_lights == on)
return //no change
nightshift_lights = on
- for(var/turf/area_turf as anything in area.get_contained_turfs())
- for(var/obj/machinery/light/night_light in area_turf)
- if(night_light.nightshift_allowed)
- night_light.nightshift_enabled = nightshift_lights
- night_light.update(FALSE)
- CHECK_TICK
+ for (var/list/zlevel_turfs as anything in area.get_zlevel_turf_lists())
+ for(var/turf/area_turf as anything in zlevel_turfs)
+ for(var/obj/machinery/light/night_light in area_turf)
+ if(night_light.nightshift_allowed)
+ night_light.nightshift_enabled = nightshift_lights
+ night_light.update(FALSE)
+ CHECK_TICK
diff --git a/code/modules/research/xenobiology/crossbreeding/chilling.dm b/code/modules/research/xenobiology/crossbreeding/chilling.dm
index 1eec00033e5..8890db9a890 100644
--- a/code/modules/research/xenobiology/crossbreeding/chilling.dm
+++ b/code/modules/research/xenobiology/crossbreeding/chilling.dm
@@ -58,9 +58,10 @@ Chilling extracts:
to_chat(user, span_warning("[src] can't affect such a large area."))
return
user.visible_message(span_notice("[src] shatters, and a healing aura fills the room briefly."))
- for(var/turf/area_turf as anything in user_area.get_contained_turfs())
- for(var/mob/living/carbon/nearby in area_turf)
- nearby.reagents?.add_reagent(/datum/reagent/medicine/regen_jelly,10)
+ for (var/list/zlevel_turfs as anything in user_area.get_zlevel_turf_lists())
+ for(var/turf/area_turf as anything in zlevel_turfs)
+ for(var/mob/living/carbon/nearby in area_turf)
+ nearby.reagents?.add_reagent(/datum/reagent/medicine/regen_jelly,10)
..()
/obj/item/slimecross/chilling/blue
@@ -108,7 +109,7 @@ Chilling extracts:
to_chat(user, span_warning("[src] can't affect such a large area."))
return
var/filtered = FALSE
- for(var/turf/open/T in A.get_contained_turfs())
+ for(var/turf/open/T in A.get_turfs_from_all_zlevels())
var/datum/gas_mixture/G = T.air
if(istype(G))
G.assert_gas(/datum/gas/plasma)
@@ -336,7 +337,8 @@ Chilling extracts:
to_chat(user, span_warning("[src] can't affect such a large area."))
return
user.visible_message(span_warning("[src] reflects an array of dazzling colors and light, energy rushing to nearby doors!"))
- for(var/turf/area_turf as anything in area.get_contained_turfs())
- for(var/obj/machinery/door/airlock/door in area_turf)
- new /obj/effect/forcefield/slimewall/rainbow(door.loc)
+ for (var/list/zlevel_turfs as anything in area.get_zlevel_turf_lists())
+ for(var/turf/area_turf as anything in zlevel_turfs)
+ for(var/obj/machinery/door/airlock/door in area_turf)
+ new /obj/effect/forcefield/slimewall/rainbow(door.loc)
return ..()
diff --git a/code/modules/research/xenobiology/xenobiology.dm b/code/modules/research/xenobiology/xenobiology.dm
index efc6f3aa8b0..49f7bfa61f3 100644
--- a/code/modules/research/xenobiology/xenobiology.dm
+++ b/code/modules/research/xenobiology/xenobiology.dm
@@ -1065,9 +1065,10 @@
/obj/item/areaeditor/blueprints/slime/edit_area()
..()
- var/area/A = get_area(src)
- for(var/turf/T as anything in A.get_contained_turfs())
- T.remove_atom_colour(WASHABLE_COLOUR_PRIORITY)
- T.add_atom_colour("#2956B2", FIXED_COLOUR_PRIORITY)
- A.area_flags |= XENOBIOLOGY_COMPATIBLE
+ var/area/area = get_area(src)
+ for (var/list/zlevel_turfs as anything in area.get_zlevel_turf_lists())
+ for(var/turf/area_turf as anything in zlevel_turfs)
+ area_turf.remove_atom_colour(WASHABLE_COLOUR_PRIORITY)
+ area_turf.add_atom_colour("#2956B2", FIXED_COLOUR_PRIORITY)
+ area.area_flags |= XENOBIOLOGY_COMPATIBLE
qdel(src)
diff --git a/code/modules/security_levels/keycard_authentication.dm b/code/modules/security_levels/keycard_authentication.dm
index a3cda3fda0f..5e193e4dbf6 100644
--- a/code/modules/security_levels/keycard_authentication.dm
+++ b/code/modules/security_levels/keycard_authentication.dm
@@ -180,21 +180,25 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/keycard_auth, 26)
GLOBAL_VAR_INIT(emergency_access, FALSE)
/proc/make_maint_all_access()
- for(var/area/station/maintenance/A in GLOB.areas)
- for(var/turf/in_area as anything in A.get_contained_turfs())
- for(var/obj/machinery/door/airlock/D in in_area)
- D.emergency = TRUE
- D.update_icon(ALL, 0)
+ for(var/area/station/maintenance/area in GLOB.areas)
+ for (var/list/zlevel_turfs as anything in area.get_zlevel_turf_lists())
+ for(var/turf/area_turf as anything in zlevel_turfs)
+ for(var/obj/machinery/door/airlock/airlock in area_turf)
+ airlock.emergency = TRUE
+ airlock.update_icon(ALL, 0)
+
minor_announce("Access restrictions on maintenance and external airlocks have been lifted.", "Attention! Station-wide emergency declared!",1)
GLOB.emergency_access = TRUE
SSblackbox.record_feedback("nested tally", "keycard_auths", 1, list("emergency maintenance access", "enabled"))
/proc/revoke_maint_all_access()
- for(var/area/station/maintenance/A in GLOB.areas)
- for(var/turf/in_area as anything in A.get_contained_turfs())
- for(var/obj/machinery/door/airlock/D in in_area)
- D.emergency = FALSE
- D.update_icon(ALL, 0)
+ for(var/area/station/maintenance/area in GLOB.areas)
+ for (var/list/zlevel_turfs as anything in area.get_zlevel_turf_lists())
+ for(var/turf/area_turf as anything in zlevel_turfs)
+ for(var/obj/machinery/door/airlock/airlock in area_turf)
+ airlock.emergency = FALSE
+ airlock.update_icon(ALL, 0)
+
minor_announce("Access restrictions in maintenance areas have been restored.", "Attention! Station-wide emergency rescinded:")
GLOB.emergency_access = FALSE
SSblackbox.record_feedback("nested tally", "keycard_auths", 1, list("emergency maintenance access", "disabled"))
diff --git a/code/modules/shuttle/arrivals.dm b/code/modules/shuttle/arrivals.dm
index 81b456603b4..3b32bec5539 100644
--- a/code/modules/shuttle/arrivals.dm
+++ b/code/modules/shuttle/arrivals.dm
@@ -37,11 +37,12 @@
var/list/new_latejoin = list()
for(var/area/shuttle/arrival/arrival_area in GLOB.areas)
- for(var/turf/arrival_turf as anything in arrival_area.get_contained_turfs())
- for(var/obj/structure/chair/shuttle_chair in arrival_turf)
- new_latejoin += shuttle_chair
- if(isnull(console))
- console = locate() in arrival_turf
+ for (var/list/zlevel_turfs as anything in arrival_area.get_zlevel_turf_lists())
+ for(var/turf/arrival_turf as anything in zlevel_turfs)
+ for(var/obj/structure/chair/shuttle_chair in arrival_turf)
+ new_latejoin += shuttle_chair
+ if(isnull(console))
+ console = locate() in arrival_turf
areas += arrival_area
if(SSjob.latejoin_trackers.len)
diff --git a/code/modules/shuttle/navigation_computer.dm b/code/modules/shuttle/navigation_computer.dm
index e564247bee4..a5479e02016 100644
--- a/code/modules/shuttle/navigation_computer.dm
+++ b/code/modules/shuttle/navigation_computer.dm
@@ -96,17 +96,18 @@
the_eye.setDir(shuttle_port.dir)
var/turf/origin = locate(shuttle_port.x + x_offset, shuttle_port.y + y_offset, shuttle_port.z)
for(var/area/shuttle_area as anything in shuttle_port.shuttle_areas)
- for(var/turf/shuttle_turf as anything in shuttle_area.get_contained_turfs())
- if(shuttle_turf.z != origin.z)
- continue
- var/image/I = image('icons/effects/alphacolors.dmi', origin, "red")
- var/x_off = shuttle_turf.x - origin.x
- var/y_off = shuttle_turf.y - origin.y
- I.loc = locate(origin.x + x_off, origin.y + y_off, origin.z) //we have to set this after creating the image because it might be null, and images created in nullspace are immutable.
- I.layer = ABOVE_NORMAL_TURF_LAYER
- SET_PLANE(I, ABOVE_GAME_PLANE, shuttle_turf)
- I.mouse_opacity = MOUSE_OPACITY_TRANSPARENT
- the_eye.placement_images[I] = list(x_off, y_off)
+ for (var/list/zlevel_turfs as anything in shuttle_area.get_zlevel_turf_lists())
+ for(var/turf/shuttle_turf as anything in zlevel_turfs)
+ if(shuttle_turf.z != origin.z)
+ continue
+ var/image/I = image('icons/effects/alphacolors.dmi', origin, "red")
+ var/x_off = shuttle_turf.x - origin.x
+ var/y_off = shuttle_turf.y - origin.y
+ I.loc = locate(origin.x + x_off, origin.y + y_off, origin.z) //we have to set this after creating the image because it might be null, and images created in nullspace are immutable.
+ I.layer = ABOVE_NORMAL_TURF_LAYER
+ SET_PLANE(I, ABOVE_GAME_PLANE, shuttle_turf)
+ I.mouse_opacity = MOUSE_OPACITY_TRANSPARENT
+ the_eye.placement_images[I] = list(x_off, y_off)
/obj/machinery/computer/camera_advanced/shuttle_docker/give_eye_control(mob/user)
..()
diff --git a/code/modules/shuttle/shuttle.dm b/code/modules/shuttle/shuttle.dm
index d9e20b07f1b..c46f68c2fb2 100644
--- a/code/modules/shuttle/shuttle.dm
+++ b/code/modules/shuttle/shuttle.dm
@@ -516,13 +516,14 @@
var/min_y = -1
var/max_x = WORLDMAXX_CUTOFF
var/max_y = WORLDMAXY_CUTOFF
- for(var/area/area as anything in shuttle_areas)
- for(var/turf/turf as anything in area.get_contained_turfs())
- min_x = max(turf.x, min_x)
- max_x = min(turf.x, max_x)
- min_y = max(turf.y, min_y)
- max_y = min(turf.y, max_y)
- CHECK_TICK
+ for(var/area/shuttle_area as anything in shuttle_areas)
+ for (var/list/zlevel_turfs as anything in shuttle_area.get_zlevel_turf_lists())
+ for(var/turf/turf as anything in zlevel_turfs)
+ min_x = max(turf.x, min_x)
+ max_x = min(turf.x, max_x)
+ min_y = max(turf.y, min_y)
+ max_y = min(turf.y, max_y)
+ CHECK_TICK
if(min_x == -1 || max_x == WORLDMAXX_CUTOFF)
CRASH("Failed to locate shuttle boundaries when iterating through shuttle areas, somehow.")
diff --git a/code/modules/shuttle/supply.dm b/code/modules/shuttle/supply.dm
index 515d3b38cfe..f07444c22f1 100644
--- a/code/modules/shuttle/supply.dm
+++ b/code/modules/shuttle/supply.dm
@@ -59,22 +59,23 @@ GLOBAL_LIST_INIT(blacklisted_cargo_types, typecacheof(list(
return ..()
/obj/docking_port/mobile/supply/proc/check_blacklist(areaInstances)
- for(var/place in areaInstances)
- var/area/shuttle/shuttle_area = place
- for(var/turf/shuttle_turf in shuttle_area.get_contained_turfs())
- for(var/atom/passenger in shuttle_turf.get_all_contents())
- if((is_type_in_typecache(passenger, GLOB.blacklisted_cargo_types) || HAS_TRAIT(passenger, TRAIT_BANNED_FROM_CARGO_SHUTTLE)) && !istype(passenger, /obj/docking_port))
- return FALSE
+ for(var/area/shuttle_area as anything in areaInstances)
+ for (var/list/zlevel_turfs as anything in shuttle_area.get_zlevel_turf_lists())
+ for(var/turf/shuttle_turf as anything in zlevel_turfs)
+ for(var/atom/passenger in shuttle_turf.get_all_contents())
+ if((is_type_in_typecache(passenger, GLOB.blacklisted_cargo_types) || HAS_TRAIT(passenger, TRAIT_BANNED_FROM_CARGO_SHUTTLE)) && !istype(passenger, /obj/docking_port))
+ return FALSE
return TRUE
/// Returns anything on the cargo blacklist found within areas_to_check back to the turf of the home docking port via Centcom branded supply pod.
/obj/docking_port/mobile/supply/proc/return_blacklisted_things_home(list/area/areas_to_check, obj/docking_port/stationary/home)
var/list/stuff_to_send_home = list()
for(var/area/shuttle_area as anything in areas_to_check)
- for(var/turf/shuttle_turf in shuttle_area.get_contained_turfs())
- for(var/atom/passenger in shuttle_turf.get_all_contents())
- if((is_type_in_typecache(passenger, GLOB.blacklisted_cargo_types) || HAS_TRAIT(passenger, TRAIT_BANNED_FROM_CARGO_SHUTTLE)) && !istype(passenger, /obj/docking_port))
- stuff_to_send_home += passenger
+ for (var/list/zlevel_turfs as anything in shuttle_area.get_zlevel_turf_lists())
+ for(var/turf/shuttle_turf as anything in zlevel_turfs)
+ for(var/atom/passenger in shuttle_turf.get_all_contents())
+ if((is_type_in_typecache(passenger, GLOB.blacklisted_cargo_types) || HAS_TRAIT(passenger, TRAIT_BANNED_FROM_CARGO_SHUTTLE)) && !istype(passenger, /obj/docking_port))
+ stuff_to_send_home += passenger
if(!length(stuff_to_send_home))
return FALSE
@@ -135,7 +136,7 @@ GLOBAL_LIST_INIT(blacklisted_cargo_types, typecacheof(list(
var/list/empty_turfs = list()
for(var/area/shuttle/shuttle_area as anything in shuttle_areas)
- for(var/turf/open/floor/shuttle_turf in shuttle_area.get_contained_turfs())
+ for(var/turf/open/floor/shuttle_turf in shuttle_area.get_turfs_from_all_zlevels())
if(shuttle_turf.is_blocked_turf())
continue
empty_turfs += shuttle_turf
@@ -283,13 +284,14 @@ GLOBAL_LIST_INIT(blacklisted_cargo_types, typecacheof(list(
var/datum/export_report/report = new
for(var/area/shuttle/shuttle_area as anything in shuttle_areas)
- for(var/turf/shuttle_turf as anything in shuttle_area.get_contained_turfs())
- for(var/atom/movable/exporting_atom in shuttle_turf)
- if(iscameramob(exporting_atom))
- continue
- if(exporting_atom.anchored)
- continue
- export_item_and_contents(exporting_atom, apply_elastic = TRUE, dry_run = FALSE, external_report = report)
+ for (var/list/zlevel_turfs as anything in shuttle_area.get_zlevel_turf_lists())
+ for(var/turf/shuttle_turf as anything in zlevel_turfs)
+ for(var/atom/movable/exporting_atom in shuttle_turf)
+ if(iscameramob(exporting_atom))
+ continue
+ if(exporting_atom.anchored)
+ continue
+ export_item_and_contents(exporting_atom, apply_elastic = TRUE, dry_run = FALSE, external_report = report)
if(report.exported_atoms)
report.exported_atoms += "." //ugh
@@ -318,7 +320,7 @@ GLOBAL_LIST_INIT(blacklisted_cargo_types, typecacheof(list(
//spawn crate
var/list/empty_turfs = list()
for(var/area/shuttle/shuttle_area as anything in shuttle_areas)
- for(var/turf/open/floor/shuttle_floor in shuttle_area.get_contained_turfs())
+ for(var/turf/open/floor/shuttle_floor in shuttle_area.get_turfs_from_all_zlevels())
if(shuttle_floor.is_blocked_turf())
continue
empty_turfs += shuttle_floor
diff --git a/code/modules/unit_tests/area_contents.dm b/code/modules/unit_tests/area_contents.dm
index 8a48d644ee9..52394dd60ec 100644
--- a/code/modules/unit_tests/area_contents.dm
+++ b/code/modules/unit_tests/area_contents.dm
@@ -6,23 +6,28 @@
/datum/unit_test/area_contents/Run()
// First, we check that there are no entries in more then one area
// That or duplicate entries
- for(var/area/space in GLOB.areas)
- for(var/turf/position as anything in space.get_contained_turfs())
- if(!isturf(position))
- TEST_FAIL("Found a [position.type] in [space.type]'s turf listing")
+ for (var/area/area_to_test in GLOB.areas)
+ area_to_test.cannonize_contained_turfs()
+ for (var/i in 1 to area_to_test.turfs_by_zlevel.len)
+ if (!islist(area_to_test.turfs_by_zlevel[i]))
+ TEST_FAIL("zlevel index [i] in [area_to_test.type] is not a list.")
- if(position.in_contents_of)
- var/area/existing = position.in_contents_of
- if(existing == space)
- TEST_FAIL("Found a duplicate turf [position.type] inside [space.type]'s turf listing")
- else
- TEST_FAIL("Found a shared turf [position.type] between [space.type] and [existing.type]'s turf listings")
+ for (var/turf/turf_to_check as anything in area_to_test.turfs_by_zlevel[i])
+ if (!isturf(turf_to_check))
+ TEST_FAIL("Found a [turf_to_check.type] in [area_to_test.type]'s turf listing")
- var/area/dream_spot = position.loc
- if(dream_spot != space)
- TEST_FAIL("Found a turf [position.type] which is IN [dream_spot.type], but is registered as being in [space.type]")
+ if (turf_to_check.in_contents_of)
+ var/area/existing = turf_to_check.in_contents_of
+ if (existing == turf_to_check)
+ TEST_FAIL("Found a duplicate turf [turf_to_check.type] inside [area_to_test.type]'s turf listing")
+ else
+ TEST_FAIL("Found a shared turf [turf_to_check.type] between [area_to_test.type] and [existing.type]'s turf listings")
- position.in_contents_of = space
+ var/area/turfs_actual_area = turf_to_check.loc
+ if (turfs_actual_area != area_to_test)
+ TEST_FAIL("Found a turf [turf_to_check.type] which is IN [turfs_actual_area.type], but is registered as being in [area_to_test.type]")
+
+ turf_to_check.in_contents_of = turf_to_check
for(var/turf/position in ALL_TURFS())
if(!position.in_contents_of)
diff --git a/code/modules/unit_tests/atmospherics_sanity.dm b/code/modules/unit_tests/atmospherics_sanity.dm
index c4f024e9178..ff177ae4517 100644
--- a/code/modules/unit_tests/atmospherics_sanity.dm
+++ b/code/modules/unit_tests/atmospherics_sanity.dm
@@ -101,7 +101,7 @@
UNTIL(crawls == 0)
for(var/area/missed as anything in remaining_areas)
if(missed.has_contained_turfs())
- var/turf/first_turf = missed.get_contained_turfs()[1]
+ var/turf/first_turf = missed.get_zlevel_turf_lists()[1][1]
TEST_FAIL("Disconnected Area '[missed]'([missed.type]) at ([first_turf.x], [first_turf.y], [first_turf.z])")
else
TEST_NOTICE(src, "Disconnected Area '[missed]'([missed.type]) with no turfs?")
diff --git a/html/changelogs/AutoChangeLog-pr-80986.yml b/html/changelogs/AutoChangeLog-pr-80986.yml
new file mode 100644
index 00000000000..0adaeb6b55f
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-80986.yml
@@ -0,0 +1,4 @@
+author: "Ghommie"
+delete-after: True
+changes:
+ - image: "The \"One Lean, Mean, Cleaning Machine\" achievement now has its own icon."
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-80993.yml b/html/changelogs/AutoChangeLog-pr-80993.yml
new file mode 100644
index 00000000000..d12b8c5b1d7
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-80993.yml
@@ -0,0 +1,4 @@
+author: "Ben10Omintrix"
+delete-after: True
+changes:
+ - bugfix: "fix a runtime when loading ghosts to a mulebot"
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-81006.yml b/html/changelogs/AutoChangeLog-pr-81006.yml
new file mode 100644
index 00000000000..ee0e3d328b5
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-81006.yml
@@ -0,0 +1,4 @@
+author: "13spacemen"
+delete-after: True
+changes:
+ - bugfix: "Hub time should be correct again"
\ No newline at end of file
diff --git a/icons/ui_icons/achievements/achievements.dmi b/icons/ui_icons/achievements/achievements.dmi
index 619ef96dda9..d8420439f4d 100644
Binary files a/icons/ui_icons/achievements/achievements.dmi and b/icons/ui_icons/achievements/achievements.dmi differ
diff --git a/modular_nova/modules/automapper/code/area_spawn_subsystem.dm b/modular_nova/modules/automapper/code/area_spawn_subsystem.dm
index f95548cb50c..358dc8c29fb 100644
--- a/modular_nova/modules/automapper/code/area_spawn_subsystem.dm
+++ b/modular_nova/modules/automapper/code/area_spawn_subsystem.dm
@@ -91,11 +91,12 @@ SUBSYSTEM_DEF(area_spawn)
turf_list = area_turf_info["[mode]"] = list()
// Get highest priority items
- for(var/turf/iterating_turf as anything in area.get_contained_turfs())
- // Only retain turfs of the highest priority
- var/priority = process_turf(iterating_turf, mode)
- if(priority > 0)
- LAZYADDASSOC(turf_list, "[priority]", list(iterating_turf))
+ for(var/list/zlevel_turfs as anything in area.get_zlevel_turf_lists())
+ for(var/turf/iterating_turf as anything in zlevel_turfs)
+ // Only retain turfs of the highest priority
+ var/priority = process_turf(iterating_turf, mode)
+ if(priority > 0)
+ LAZYADDASSOC(turf_list, "[priority]", list(iterating_turf))
// Sort the priorities descending
return sortTim(turf_list, GLOBAL_PROC_REF(cmp_num_string_asc))
@@ -333,17 +334,18 @@ SUBSYSTEM_DEF(area_spawn)
if(!found_area)
continue
- for(var/turf/candidate_turf as anything in found_area.get_contained_turfs())
- // Don't spawn if there's already a desired_atom here.
- if(is_type_on_turf(candidate_turf, desired_atom))
- continue
+ for(var/list/zlevel_turfs as anything in found_area.get_zlevel_turf_lists())
+ for(var/turf/candidate_turf as anything in zlevel_turfs)
+ // Don't spawn if there's already a desired_atom here.
+ if(is_type_on_turf(candidate_turf, desired_atom))
+ continue
- for(var/over_atom_type in over_atoms)
- // Spawn on the first one we find in the turf and stop.
- if(is_type_on_turf(candidate_turf, over_atom_type))
- new desired_atom(candidate_turf)
- // Break the over_atom_type loop.
- break
+ for(var/over_atom_type in over_atoms)
+ // Spawn on the first one we find in the turf and stop.
+ if(is_type_on_turf(candidate_turf, over_atom_type))
+ new desired_atom(candidate_turf)
+ // Break the over_atom_type loop.
+ break
/obj/effect/turf_test
name = "PASS"
@@ -361,9 +363,10 @@ SUBSYSTEM_DEF(area_spawn)
set name = "Test Area Spawner"
set desc = "Show area spawner placement candidates as an overlay."
- for(var/turf/area_turf as anything in area.get_contained_turfs())
- for(var/obj/effect/turf_test/old_test in area_turf)
- qdel(old_test)
+ for(var/list/zlevel_turfs as anything in area.get_zlevel_turf_lists())
+ for(var/turf/area_turf as anything in zlevel_turfs)
+ for(var/obj/effect/turf_test/old_test in area_turf)
+ qdel(old_test)
SSarea_spawn.clear_cache()
for(var/mode in 0 to AREA_SPAWN_MODE_COUNT - 1)
diff --git a/modular_nova/modules/clock_cult/code/structures/technologists_lectern.dm b/modular_nova/modules/clock_cult/code/structures/technologists_lectern.dm
index fbbb8de5e4f..11759912286 100644
--- a/modular_nova/modules/clock_cult/code/structures/technologists_lectern.dm
+++ b/modular_nova/modules/clock_cult/code/structures/technologists_lectern.dm
@@ -428,10 +428,11 @@
apc_loop:
for(var/obj/machinery/power/apc/controller as anything in SSmachines.get_machines_by_type_and_subtypes(/obj/machinery/power/apc))
var/area/apc_area = get_area(controller) // make sure that no "critical" APCs lose their power (SM, namely)
- for(var/turf/turf as anything in apc_area.get_contained_turfs())
- for(var/obj/machinery/depowered_machinery in turf)
- if(depowered_machinery.critical_machine)
- continue apc_loop
+ for(var/list/zlevel_turfs as anything in apc_area.get_zlevel_turf_lists())
+ for(var/turf/turf as anything in zlevel_turfs)
+ for(var/obj/machinery/depowered_machinery in turf)
+ if(depowered_machinery.critical_machine)
+ continue apc_loop
controller.cell?.charge = 0
diff --git a/modular_nova/modules/decay_subsystem/code/decaySS.dm b/modular_nova/modules/decay_subsystem/code/decaySS.dm
index c3b59c479c0..d2c141f8b26 100644
--- a/modular_nova/modules/decay_subsystem/code/decaySS.dm
+++ b/modular_nova/modules/decay_subsystem/code/decaySS.dm
@@ -56,10 +56,11 @@ SUBSYSTEM_DEF(decay)
possible_areas += iterating_area
// Now add the turfs
- for(var/turf/iterating_turf as anything in iterating_area.get_contained_turfs())
- if(!(iterating_turf.flags_1 & CAN_BE_DIRTY_1))
- continue
- possible_turfs += iterating_turf
+ for(var/list/zlevel_turfs as anything in iterating_area.get_zlevel_turf_lists())
+ for(var/turf/iterating_turf as anything in zlevel_turfs)
+ if(!(iterating_turf.flags_1 & CAN_BE_DIRTY_1))
+ continue
+ possible_turfs += iterating_turf
if(!possible_turfs)
CRASH("SSDecay had no possible turfs to use!")
diff --git a/modular_nova/modules/mold/code/mold_event.dm b/modular_nova/modules/mold/code/mold_event.dm
index 7c8f32caff0..8c97b8328b0 100644
--- a/modular_nova/modules/mold/code/mold_event.dm
+++ b/modular_nova/modules/mold/code/mold_event.dm
@@ -48,17 +48,18 @@
if(!is_type_in_typecache(checked_area, possible_spawn_areas))
continue
- for(var/turf/open/floor in checked_area.get_contained_turfs())
- if(isopenspaceturf(floor))
- continue
+ for(var/list/zlevel_turfs as anything in checked_area.get_zlevel_turf_lists())
+ for(var/turf/open/floor in zlevel_turfs)
+ if(isopenspaceturf(floor))
+ continue
- if(!floor.Enter(test_resin))
- continue
+ if(!floor.Enter(test_resin))
+ continue
- if(locate(/turf/closed) in range(2, floor))
- continue
+ if(locate(/turf/closed) in range(2, floor))
+ continue
- turfs += floor
+ turfs += floor
qdel(test_resin)