From 24eb8217c08a7fdfedfb44522da142774ae712fa Mon Sep 17 00:00:00 2001 From: SkyratBot <59378654+SkyratBot@users.noreply.github.com> Date: Wed, 16 Nov 2022 20:42:22 +0100 Subject: [PATCH] [MIRROR] Builds logic that manages turfs contained inside an area [MDB IGNORE] (#17379) * Builds logic that manages turfs contained inside an area * Mirror Conflict * Modular! Co-authored-by: LemonInTheDark <58055496+LemonInTheDark@users.noreply.github.com> Co-authored-by: Funce Co-authored-by: tastyfish --- code/__DEFINES/turfs.dm | 3 + code/__HELPERS/areas.dm | 50 +++++++++-------- code/_globalvars/lists/mapping.dm | 8 ++- code/controllers/subsystem/air.dm | 3 +- code/controllers/subsystem/area_contents.dm | 55 +++++++++++++++++++ code/controllers/subsystem/mapping.dm | 33 ++++++++--- .../subsystem/processing/networks.dm | 2 +- code/controllers/subsystem/shuttle.dm | 3 + code/game/area/areas.dm | 45 +++++++++++---- code/game/gamemodes/objective.dm | 2 +- code/modules/admin/verbs/adminjump.dm | 7 ++- code/modules/admin/verbs/debug.dm | 2 +- code/modules/antagonists/blob/overmind.dm | 2 +- code/modules/antagonists/cult/cult.dm | 2 +- .../antagonists/space_ninja/space_ninja.dm | 2 +- code/modules/cargo/centcom_podlauncher.dm | 4 +- code/modules/cargo/expressconsole.dm | 4 +- .../clothing/glasses/engine_goggles.dm | 7 +-- code/modules/events/anomaly/anomaly_placer.dm | 2 +- code/modules/events/aurora_caelus.dm | 20 +++---- code/modules/events/prison_break.dm | 2 +- code/modules/events/spacevine.dm | 4 +- code/modules/events/stray_cargo.dm | 4 +- code/modules/lighting/lighting_area.dm | 4 +- code/modules/lighting/lighting_setup.dm | 5 +- code/modules/mapping/map_template.dm | 10 ++-- code/modules/mapping/reader.dm | 45 +++++++++------ .../space_management/zlevel_manager.dm | 6 +- code/modules/mob/dead/observer/observer.dm | 3 +- .../mapGenerators/repair.dm | 2 +- code/modules/religion/rites.dm | 2 +- .../security_levels/keycard_authentication.dm | 18 +++--- code/modules/shuttle/arrivals.dm | 2 +- code/modules/shuttle/emergency.dm | 6 +- code/modules/shuttle/on_move.dm | 4 ++ code/modules/shuttle/shuttle.dm | 2 + code/modules/unit_tests/_unit_tests.dm | 5 +- code/modules/unit_tests/area_contents.dm | 30 ++++++++++ code/modules/unit_tests/monkey_business.dm | 2 +- .../code/modules/events/spacevine.dm | 4 +- .../assault_operatives/code/interrogator.dm | 4 +- .../automapper/code/area_spawn_subsystem.dm | 2 +- .../modules/biohazard_blob/code/mold_event.dm | 4 +- .../modules/decay_subsystem/code/decaySS.dm | 15 +++-- tgstation.dme | 1 + 45 files changed, 299 insertions(+), 143 deletions(-) create mode 100644 code/controllers/subsystem/area_contents.dm create mode 100644 code/modules/unit_tests/area_contents.dm diff --git a/code/__DEFINES/turfs.dm b/code/__DEFINES/turfs.dm index 220a1067982..43bd601fc40 100644 --- a/code/__DEFINES/turfs.dm +++ b/code/__DEFINES/turfs.dm @@ -22,6 +22,9 @@ ///Returns all turfs in a zlevel #define Z_TURFS(ZLEVEL) block(locate(1,1,ZLEVEL), locate(world.maxx, world.maxy, ZLEVEL)) +///Returns all currently loaded turfs +#define ALL_TURFS(...) block(locate(1, 1, 1), locate(world.maxx, world.maxy, world.maxz)) + #define TURF_FROM_COORDS_LIST(List) (locate(List[1], List[2], List[3])) /// The pipes, disposals, and wires are hidden diff --git a/code/__HELPERS/areas.dm b/code/__HELPERS/areas.dm index 7f454de1916..855bf362d5e 100644 --- a/code/__HELPERS/areas.dm +++ b/code/__HELPERS/areas.dm @@ -123,7 +123,9 @@ GLOBAL_LIST_INIT(typecache_powerfailure_safe_areas, typecacheof(/area/station/en for(var/i in 1 to length(turfs)) var/turf/thing = turfs[i] var/area/old_area = thing.loc + old_area.turfs_to_uncontain += thing newA.contents += thing + newA.contained_turfs += thing thing.transfer_area_lighting(old_area, newA) newA.reg_in_areas_in_z() @@ -143,18 +145,14 @@ GLOBAL_LIST_INIT(typecache_powerfailure_safe_areas, typecacheof(/area/station/en #undef BP_MAX_ROOM_SIZE -//Repopulates sortedAreas list -/proc/repopulate_sorted_areas() - GLOB.sortedAreas = list() +/proc/require_area_resort() + GLOB.sortedAreas = null - for(var/area/A in world) - GLOB.sortedAreas.Add(A) - - sortTim(GLOB.sortedAreas, GLOBAL_PROC_REF(cmp_name_asc)) - -/area/proc/addSorted() - GLOB.sortedAreas.Add(src) - sortTim(GLOB.sortedAreas, GLOBAL_PROC_REF(cmp_name_asc)) +/// Returns a sorted version of GLOB.areas, by name +/proc/get_sorted_areas() + if(!GLOB.sortedAreas) + GLOB.sortedAreas = sortTim(GLOB.areas.Copy(), /proc/cmp_name_asc) + return GLOB.sortedAreas //Takes: Area type as a text string from a variable. //Returns: Instance for the area in the world. @@ -177,11 +175,11 @@ GLOBAL_LIST_INIT(typecache_powerfailure_safe_areas, typecacheof(/area/station/en var/list/areas = list() if(subtypes) var/list/cache = typecacheof(areatype) - for(var/area/area_to_check as anything in GLOB.sortedAreas) + for(var/area/area_to_check as anything in GLOB.areas) if(cache[area_to_check.type]) areas += area_to_check else - for(var/area/area_to_check as anything in GLOB.sortedAreas) + for(var/area/area_to_check as anything in GLOB.areas) if(area_to_check.type == areatype) areas += area_to_check return areas @@ -204,25 +202,33 @@ GLOBAL_LIST_INIT(typecache_powerfailure_safe_areas, typecacheof(/area/station/en areatype = areatemp.type else if(!ispath(areatype)) return null - - var/list/turfs = list() + // Pull out the areas + var/list/areas_to_pull = list() if(subtypes) var/list/cache = typecacheof(areatype) - for(var/area/area_to_check as anything in GLOB.sortedAreas) + for(var/area/area_to_check as anything in GLOB.areas) if(!cache[area_to_check.type]) continue - for(var/turf/turf_in_area in area_to_check) - if(target_z == 0 || target_z == turf_in_area.z) - turfs += turf_in_area + areas_to_pull += area_to_check else - for(var/area/area_to_check as anything in GLOB.sortedAreas) + for(var/area/area_to_check as anything in GLOB.areas) if(area_to_check.type != areatype) continue - for(var/turf/turf_in_area in area_to_check) - if(target_z == 0 || target_z == turf_in_area.z) + areas_to_pull += area_to_check + + // 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 + else + for(var/turf/turf_in_area as anything in our_turfs) + if(target_z == turf_in_area.z) turfs += turf_in_area return turfs + ///Takes: list of area types ///Returns: all mobs that are in an area type /proc/mobs_in_area_type(list/area/checked_areas) diff --git a/code/_globalvars/lists/mapping.dm b/code/_globalvars/lists/mapping.dm index 130ae67406b..064f97d66d0 100644 --- a/code/_globalvars/lists/mapping.dm +++ b/code/_globalvars/lists/mapping.dm @@ -119,10 +119,14 @@ GLOBAL_LIST_EMPTY(emergencyresponseteamspawn) GLOBAL_LIST_EMPTY(ruin_landmarks) GLOBAL_LIST_EMPTY(bar_areas) -//away missions +/// Away missions GLOBAL_LIST_EMPTY(vr_spawnpoints) - //used by jump-to-area etc. Updated by area/updateName() +/// Just a list of all the area objects in the game +/// Note, areas can have duplicate types +GLOBAL_LIST_EMPTY(areas) +/// Used by jump-to-area etc. Updated by area/updateName() +/// If this is null, it needs to be recalculated. Use get_sorted_areas() as a getter please GLOBAL_LIST_EMPTY(sortedAreas) /// An association from typepath to area instance. Only includes areas with `unique` set. GLOBAL_LIST_EMPTY_TYPED(areas_by_type, /area) diff --git a/code/controllers/subsystem/air.dm b/code/controllers/subsystem/air.dm index 69a3c005dee..729f82ec306 100644 --- a/code/controllers/subsystem/air.dm +++ b/code/controllers/subsystem/air.dm @@ -523,7 +523,6 @@ SUBSYSTEM_DEF(air) queued_for_activation.Cut() /datum/controller/subsystem/air/proc/setup_allturfs() - var/list/turfs_to_init = block(locate(1, 1, 1), locate(world.maxx, world.maxy, world.maxz)) var/list/active_turfs = src.active_turfs times_fired++ @@ -537,7 +536,7 @@ SUBSYSTEM_DEF(air) active_turfs.Cut() var/time = 0 - for(var/turf/T as anything in turfs_to_init) + for(var/turf/T as anything in ALL_TURFS()) if (!T.init_air) continue // We pass the tick as the current step so if we sleep the step changes diff --git a/code/controllers/subsystem/area_contents.dm b/code/controllers/subsystem/area_contents.dm new file mode 100644 index 00000000000..272c72eb7ea --- /dev/null +++ b/code/controllers/subsystem/area_contents.dm @@ -0,0 +1,55 @@ +#define ALLOWED_LOOSE_TURFS 500 +/** + * 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 + * as a consequence of repeated changes in area in a space + * They additionally may not always resolve often enough to avoid memory leaks + * This is annoying, so lets keep an eye on them and cut them down to size if needed + */ +SUBSYSTEM_DEF(area_contents) + name = "Area Contents" + flags = SS_NO_INIT + runlevels = RUNLEVEL_LOBBY|RUNLEVELS_DEFAULT + var/list/currentrun + var/list/area/marked_for_clearing = list() + +/datum/controller/subsystem/area_contents/stat_entry(msg) + 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) + msg = "A:[length(currentrun)] MR:[length(marked_for_clearing)] TC:[total_to_clear] CF:[total_clearing_from]" + return ..() + + +/datum/controller/subsystem/area_contents/fire(resumed) + if(!resumed) + currentrun = GLOB.areas.Copy() + + while(length(currentrun)) + var/area/test = currentrun[length(currentrun)] + if(length(test.turfs_to_uncontain) > ALLOWED_LOOSE_TURFS) + marked_for_clearing |= test + currentrun.len-- + if(MC_TICK_CHECK) + return + + // Alright, if we've done a scan on all our areas, it's time to knock the existing ones down to size + 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() + marked_for_clearing.len-- diff --git a/code/controllers/subsystem/mapping.dm b/code/controllers/subsystem/mapping.dm index 1c2270f0938..09688963e59 100644 --- a/code/controllers/subsystem/mapping.dm +++ b/code/controllers/subsystem/mapping.dm @@ -115,7 +115,7 @@ SUBSYSTEM_DEF(mapping) initialize_biomes() loadWorld() determine_fake_sale() - repopulate_sorted_areas() + require_area_resort() process_teleport_locs() //Sets up the wizard teleport locations preloadTemplates() @@ -142,7 +142,7 @@ SUBSYSTEM_DEF(mapping) run_map_generation() // Add the transit level transit = add_new_zlevel("Transit/Reserved", list(ZTRAIT_RESERVED = TRUE)) - repopulate_sorted_areas() + require_area_resort() // Set up Z-level transitions. setup_map_transitions() generate_station_area_list() @@ -155,6 +155,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/lists_to_reserve = src.lists_to_reserve var/index = 0 while(index < length(lists_to_reserve)) @@ -168,8 +169,11 @@ SUBSYSTEM_DEF(mapping) T.empty(RESERVED_TURF_TYPE, RESERVED_TURF_TYPE, null, TRUE) LAZYINITLIST(unused_turfs["[T.z]"]) unused_turfs["[T.z]"] |= T + var/area/old_area = T.loc + old_area.turfs_to_uncontain += T T.flags_1 |= UNUSED_RESERVATION_TURF world_contents += T + world_turf_contents += T packet.len-- packetlen = length(packet) @@ -362,7 +366,7 @@ Used by the AI doomsday and the self-destruct nuke. var/start_z = world.maxz + 1 var/i = 0 for (var/level in traits) - add_new_zlevel("[name][i ? " [i + 1]" : ""]", level) + add_new_zlevel("[name][i ? " [i + 1]" : ""]", level, contain_turfs = FALSE) ++i SSautomapper.preload_templates_from_toml(files) // SKYRAT EDIT ADDITION - We need to load our templates AFTER the Z level exists, otherwise, there is no z level to preload. @@ -372,7 +376,7 @@ Used by the AI doomsday and the self-destruct nuke. for (var/P in parsed_maps) var/datum/parsed_map/pm = P pm.turf_blacklist = turf_blacklist // SKYRAT EDIT ADDITION - apply blacklist - if (!pm.load(1, 1, start_z + parsed_maps[P], no_changeturf = TRUE)) + if (!pm.load(1, 1, start_z + parsed_maps[P], no_changeturf = TRUE, new_z = TRUE)) errorList |= pm.original_path // SKYRAT EDIT ADDITION BEGIN - We need to load our templates from cache after our space has been carved out. if(!LAZYLEN(errorList)) @@ -431,7 +435,7 @@ Used by the AI doomsday and the self-destruct nuke. GLOBAL_LIST_EMPTY(the_station_areas) /datum/controller/subsystem/mapping/proc/generate_station_area_list() - for(var/area/station/station_area in world) + for(var/area/station/station_area in GLOB.areas) if (!(station_area.area_flags & UNIQUE_AREA)) continue if (is_station_level(station_area.z)) @@ -441,7 +445,7 @@ GLOBAL_LIST_EMPTY(the_station_areas) log_world("ERROR: Station areas list failed to generate!") /datum/controller/subsystem/mapping/proc/run_map_generation() - for(var/area/A in world) + for(var/area/A as anything in GLOB.areas) A.RunGeneration() /datum/controller/subsystem/mapping/proc/maprotate() @@ -724,7 +728,7 @@ GLOBAL_LIST_EMPTY(the_station_areas) /// Takes a z level datum, and tells the mapping subsystem to manage it /// Also handles things like plane offset generation, and other things that happen on a z level to z level basis -/datum/controller/subsystem/mapping/proc/manage_z_level(datum/space_level/new_z) +/datum/controller/subsystem/mapping/proc/manage_z_level(datum/space_level/new_z, filled_with_space, contain_turfs = TRUE) // First, add the z z_list += new_z @@ -743,11 +747,26 @@ GLOBAL_LIST_EMPTY(the_station_areas) if(below_offset) update_plane_tracking(new_z) + if(contain_turfs) + build_area_turfs(z_value, filled_with_space) + // And finally, misc global generation // We'll have to update this if offsets change, because we load lowest z to highest z generate_lighting_appearance_by_z(z_value) +/datum/controller/subsystem/mapping/proc/build_area_turfs(z_level, space_guaranteed) + // If we know this is filled with default tiles, we can use the default area + // Faster + if(space_guaranteed) + var/area/global_area = GLOB.areas_by_type[world.area] + global_area.contained_turfs += 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 + /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 // Yes this will cause infinite loops if our templating is fucked. Fuck off diff --git a/code/controllers/subsystem/processing/networks.dm b/code/controllers/subsystem/processing/networks.dm index 6207f319a65..074ec4c3290 100644 --- a/code/controllers/subsystem/processing/networks.dm +++ b/code/controllers/subsystem/processing/networks.dm @@ -65,7 +65,7 @@ SUBSYSTEM_DEF(networks) /datum/controller/subsystem/networks/Initialize() station_network.register_map_supremecy() // sigh - assign_areas_root_ids(GLOB.sortedAreas) // setup area names before Initialize + assign_areas_root_ids(get_sorted_areas()) // setup area names before Initialize station_network.build_software_lists() syndie_network.build_software_lists() diff --git a/code/controllers/subsystem/shuttle.dm b/code/controllers/subsystem/shuttle.dm index 19d27ae6f12..57f85625158 100644 --- a/code/controllers/subsystem/shuttle.dm +++ b/code/controllers/subsystem/shuttle.dm @@ -619,9 +619,12 @@ SUBSYSTEM_DEF(shuttle) var/turf/midpoint = locate(transit_x, transit_y, bottomleft.z) if(!midpoint) 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 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]" diff --git a/code/game/area/areas.dm b/code/game/area/areas.dm index 514cf27fe31..d2d1f2385aa 100644 --- a/code/game/area/areas.dm +++ b/code/game/area/areas.dm @@ -13,6 +13,15 @@ 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) + /// 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 + /// 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() + var/area_flags = VALID_TERRITORY | BLOBS_ALLOWED | UNIQUE_AREA | CULT_PERMITTED ///Do we have an active fire alarm? @@ -127,20 +136,16 @@ GLOBAL_LIST_EMPTY(teleportlocs) * The returned list of turfs is sorted by name */ /proc/process_teleport_locs() - for(var/V in GLOB.sortedAreas) - var/area/AR = V + for(var/area/AR as anything in get_sorted_areas()) if(istype(AR, /area/shuttle) || AR.area_flags & NOTELEPORT) continue if(GLOB.teleportlocs[AR.name]) continue - if (!AR.contents.len) + if (!AR.has_contained_turfs()) continue - var/turf/picked = AR.contents[1] - if (picked && is_station_level(picked.z)) + if (is_station_level(AR.z)) GLOB.teleportlocs[AR.name] = AR - sortTim(GLOB.teleportlocs, GLOBAL_PROC_REF(cmp_text_asc)) - /** * Called when an area loads * @@ -151,6 +156,7 @@ GLOBAL_LIST_EMPTY(teleportlocs) // rather than waiting for atoms to initialize. if (area_flags & UNIQUE_AREA) GLOB.areas_by_type[type] = src + GLOB.areas += src power_usage = new /list(AREA_USAGE_LEN) // Some atoms would like to use power in Initialize() alarm_manager = new(src) // just in case return ..() @@ -219,6 +225,24 @@ GLOBAL_LIST_EMPTY(teleportlocs) turfs += T map_generator.generate_terrain(turfs, src) +/area/proc/get_contained_turfs() + if(length(turfs_to_uncontain)) + cannonize_contained_turfs() + return contained_turfs + +/// Ensures that the contained_turfs list properly represents the turfs actually inside us +/area/proc/cannonize_contained_turfs() + // 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() + +/// Returns TRUE if we have contained turfs, FALSE otherwise +/area/proc/has_contained_turfs() + return length(contained_turfs) - length(turfs_to_uncontain) > 0 /** * Register this area as belonging to a z level @@ -226,7 +250,7 @@ GLOBAL_LIST_EMPTY(teleportlocs) * Ensures the item is added to the SSmapping.areas_in_z list for this z */ /area/proc/reg_in_areas_in_z() - if(!length(contents)) + if(!has_contained_turfs()) return var/list/areas_in_z = SSmapping.areas_in_z update_areasize() @@ -249,6 +273,7 @@ GLOBAL_LIST_EMPTY(teleportlocs) if(GLOB.areas_by_type[type] == src) GLOB.areas_by_type[type] = null GLOB.sortedAreas -= src + GLOB.areas -= src STOP_PROCESSING(SSobj, src) QDEL_NULL(alarm_manager) return ..() @@ -472,7 +497,7 @@ GLOBAL_LIST_EMPTY(teleportlocs) always_unpowered = FALSE area_flags &= ~VALID_TERRITORY area_flags &= ~BLOBS_ALLOWED - addSorted() + require_area_resort() /** * Set the area size of the area * @@ -483,7 +508,7 @@ GLOBAL_LIST_EMPTY(teleportlocs) if(outdoors) return FALSE areasize = 0 - for(var/turf/open/T in contents) + for(var/turf/open/T in get_contained_turfs()) areasize++ /** diff --git a/code/game/gamemodes/objective.dm b/code/game/gamemodes/objective.dm index 2e945c6034e..77de1edb1e4 100644 --- a/code/game/gamemodes/objective.dm +++ b/code/game/gamemodes/objective.dm @@ -973,7 +973,7 @@ GLOBAL_LIST_EMPTY(possible_items_special) /datum/objective/contract/proc/generate_dropoff() var/found = FALSE while (!found) - var/area/dropoff_area = pick(GLOB.sortedAreas) + var/area/dropoff_area = pick(GLOB.areas) if(dropoff_area && (dropoff_area.type in GLOB.the_station_areas) && !dropoff_area.outdoors) dropoff = dropoff_area found = TRUE diff --git a/code/modules/admin/verbs/adminjump.dm b/code/modules/admin/verbs/adminjump.dm index c3f9817cc96..cdbea0e4bfb 100644 --- a/code/modules/admin/verbs/adminjump.dm +++ b/code/modules/admin/verbs/adminjump.dm @@ -1,4 +1,4 @@ -/client/proc/jumptoarea(area/A in GLOB.sortedAreas) +/client/proc/jumptoarea(area/A in get_sorted_areas()) set name = "Jump to Area" set desc = "Area to jump to" set category = "Admin.Game" @@ -159,10 +159,11 @@ if(!src.holder) to_chat(src, "Only administrators may use this command.", confidential = TRUE) return - if(!length(GLOB.sortedAreas)) + var/list/sorted_areas = get_sorted_areas() + if(!length(sorted_areas)) to_chat(src, "No areas found.", confidential = TRUE) return - var/area/target_area = tgui_input_list(src, "Pick an area", "Send Mob", GLOB.sortedAreas) + var/area/target_area = tgui_input_list(src, "Pick an area", "Send Mob", sorted_areas) if(isnull(target_area)) return if(!istype(target_area)) diff --git a/code/modules/admin/verbs/debug.dm b/code/modules/admin/verbs/debug.dm index 1da1e7a8774..e40587a71af 100644 --- a/code/modules/admin/verbs/debug.dm +++ b/code/modules/admin/verbs/debug.dm @@ -286,7 +286,7 @@ message_admins(span_adminnotice("[key_name_admin(usr)] used the Test Areas debug command checking [log_message].")) log_admin("[key_name(usr)] used the Test Areas debug command checking [log_message].") - for(var/area/A in world) + for(var/area/A as anything in GLOB.areas) if(on_station) var/list/area_turfs = get_area_turfs(A.type) if (!length(area_turfs)) diff --git a/code/modules/antagonists/blob/overmind.dm b/code/modules/antagonists/blob/overmind.dm index b55db72e1a8..c9f14f0a1a5 100644 --- a/code/modules/antagonists/blob/overmind.dm +++ b/code/modules/antagonists/blob/overmind.dm @@ -180,7 +180,7 @@ GLOBAL_LIST_EMPTY(blob_nodes) else L.fully_heal(admin_revive = FALSE) - for(var/area/A in GLOB.sortedAreas) + for(var/area/A in GLOB.areas) if(!(A.type in GLOB.the_station_areas)) continue if(!(A.area_flags & BLOBS_ALLOWED)) diff --git a/code/modules/antagonists/cult/cult.dm b/code/modules/antagonists/cult/cult.dm index d5b3836a58c..2ba1c6543ea 100644 --- a/code/modules/antagonists/cult/cult.dm +++ b/code/modules/antagonists/cult/cult.dm @@ -424,7 +424,7 @@ ..() var/sanity = 0 while(summon_spots.len < SUMMON_POSSIBILITIES && sanity < 100) - var/area/summon_area = pick(GLOB.sortedAreas - summon_spots) + var/area/summon_area = pick(GLOB.areas - summon_spots) if(summon_area && is_station_level(summon_area.z) && (summon_area.area_flags & VALID_TERRITORY)) summon_spots += summon_area sanity++ diff --git a/code/modules/antagonists/space_ninja/space_ninja.dm b/code/modules/antagonists/space_ninja/space_ninja.dm index 516f0617404..54b8f7e035f 100644 --- a/code/modules/antagonists/space_ninja/space_ninja.dm +++ b/code/modules/antagonists/space_ninja/space_ninja.dm @@ -77,7 +77,7 @@ //Explosive plant, the bomb will register its completion on priming var/datum/objective/plant_explosive/bombobjective = new /datum/objective/plant_explosive() for(var/sanity in 1 to 100) // 100 checks at most. - var/area/selected_area = pick(GLOB.sortedAreas) + var/area/selected_area = pick(GLOB.areas) if(!is_station_level(selected_area.z) || !(selected_area.area_flags & VALID_TERRITORY)) continue bombobjective.detonation_location = selected_area diff --git a/code/modules/cargo/centcom_podlauncher.dm b/code/modules/cargo/centcom_podlauncher.dm index e2b54acf7fd..0f185c0effc 100644 --- a/code/modules/cargo/centcom_podlauncher.dm +++ b/code/modules/cargo/centcom_podlauncher.dm @@ -81,9 +81,9 @@ else var/mob/user_mob = user holder = user_mob.client //if its a mob, assign the mob's client to holder - bay = locate(/area/centcom/central_command_areas/supplypod/loading/one) in GLOB.sortedAreas //Locate the default bay (one) from the centcom map + bay = locate(/area/centcom/central_command_areas/supplypod/loading/one) in GLOB.areas //Locate the default bay (one) from the centcom map bayNumber = bay.loading_id //Used as quick reference to what bay we're taking items from - var/area/pod_storage_area = locate(/area/centcom/central_command_areas/supplypod/pod_storage) in GLOB.sortedAreas + var/area/pod_storage_area = locate(/area/centcom/central_command_areas/supplypod/pod_storage) in GLOB.areas temp_pod = new(pick(get_area_turfs(pod_storage_area))) //Create a new temp_pod in the podStorage area on centcom (so users are free to look at it and change other variables if needed) orderedArea = createOrderedArea(bay) //Order all the turfs in the selected bay (top left to bottom right) to a single list. Used for the "ordered" mode (launchChoice = 1) selector = new(null, holder.mob) diff --git a/code/modules/cargo/expressconsole.dm b/code/modules/cargo/expressconsole.dm index 1a1d5b92171..b0677821f0b 100644 --- a/code/modules/cargo/expressconsole.dm +++ b/code/modules/cargo/expressconsole.dm @@ -191,7 +191,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.contents)//uses default landing zone + for(var/turf/open/floor/T in landingzone.get_contained_turfs())//uses default landing zone if(T.is_blocked_turf()) continue LAZYADD(empty_turfs, T) @@ -210,7 +210,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.contents) + for(var/turf/open/floor/T in landingzone.get_contained_turfs()) 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 9748b1d14e2..ed3bfa44b2b 100644 --- a/code/modules/clothing/glasses/engine_goggles.dm +++ b/code/modules/clothing/glasses/engine_goggles.dm @@ -99,9 +99,8 @@ if(!port) return var/list/shuttle_areas = port.shuttle_areas - for(var/r in shuttle_areas) - var/area/region = r - for(var/turf/place in region.contents) + 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 @@ -185,7 +184,7 @@ if(open.blocks_air) continue var/datum/gas_mixture/environment = open.return_air() - var/temp = round(environment.return_temperature()) + var/temp = round(environment.return_temperature()) var/image/pic = image('icons/turf/overlays.dmi', open, "greyOverlay", ABOVE_ALL_MOB_LAYER) // Lower than TEMP_SHADE_CYAN should be deep blue switch(temp) diff --git a/code/modules/events/anomaly/anomaly_placer.dm b/code/modules/events/anomaly/anomaly_placer.dm index 2486c981987..d9b597c6d56 100644 --- a/code/modules/events/anomaly/anomaly_placer.dm +++ b/code/modules/events/anomaly/anomaly_placer.dm @@ -7,7 +7,7 @@ /datum/anomaly_placer/proc/findValidArea() if(!allowed_areas) generateAllowedAreas() - var/list/possible_areas = typecache_filter_list(GLOB.sortedAreas,allowed_areas) + var/list/possible_areas = typecache_filter_list(GLOB.areas, allowed_areas) if (!length(possible_areas)) CRASH("No valid areas for anomaly found.") diff --git a/code/modules/events/aurora_caelus.dm b/code/modules/events/aurora_caelus.dm index afe90460f70..87ed5518bb8 100644 --- a/code/modules/events/aurora_caelus.dm +++ b/code/modules/events/aurora_caelus.dm @@ -29,13 +29,12 @@ M.playsound_local(M, 'sound/ambience/aurora_caelus.ogg', 20, FALSE, pressure_affected = FALSE) /datum/round_event/aurora_caelus/start() - for(var/area in GLOB.sortedAreas) - var/area/affected_area = area + for(var/area/affected_area as anything in GLOB.areas) if(affected_area.area_flags & AREA_USES_STARLIGHT) - for(var/turf/open/space/spess in affected_area) + for(var/turf/open/space/spess in affected_area.get_contained_turfs()) spess.set_light(spess.light_range * 3, spess.light_power * 0.5) if(istype(affected_area, /area/station/service/kitchen)) - for(var/turf/open/kitchen in affected_area) + for(var/turf/open/kitchen in affected_area.get_contained_turfs()) kitchen.set_light(1, 0.75) if(!prob(1) && !check_holidays(APRIL_FOOLS)) continue @@ -56,23 +55,22 @@ if(activeFor % 5 == 0) aurora_progress++ var/aurora_color = aurora_colors[aurora_progress] - for(var/area in GLOB.sortedAreas) - var/area/affected_area = area + for(var/area/affected_area as anything in GLOB.areas) if(affected_area.area_flags & AREA_USES_STARLIGHT) - for(var/turf/open/space/spess as anything in affected_area) + for(var/turf/open/space/spess in affected_area.get_contained_turfs()) spess.set_light(l_color = aurora_color) if(istype(affected_area, /area/station/service/kitchen)) - for(var/turf/open/kitchen_floor in affected_area) + for(var/turf/open/kitchen_floor in affected_area.get_contained_turfs()) kitchen_floor.set_light(l_color = aurora_color) /datum/round_event/aurora_caelus/end() - for(var/area in GLOB.sortedAreas) + for(var/area in GLOB.areas) var/area/affected_area = area if(affected_area.area_flags & AREA_USES_STARLIGHT) - for(var/turf/open/space/spess in affected_area) + for(var/turf/open/space/spess in affected_area.get_contained_turfs()) fade_to_black(spess) if(istype(affected_area, /area/station/service/kitchen)) - for(var/turf/open/superturfentent in affected_area) + for(var/turf/open/superturfentent in affected_area.get_contained_turfs()) fade_to_black(superturfentent) priority_announce("The aurora caelus event is now ending. Starlight conditions will slowly return to normal. When this has concluded, please return to your workplace and continue work as normal. Have a pleasant shift, [station_name()], and thank you for watching with us.", sound = 'sound/misc/notice2.ogg', diff --git a/code/modules/events/prison_break.dm b/code/modules/events/prison_break.dm index 80b492eb8f9..042b06dbcbd 100644 --- a/code/modules/events/prison_break.dm +++ b/code/modules/events/prison_break.dm @@ -25,7 +25,7 @@ severity = rand(1,3) for(var/i in 1 to severity) var/picked_area = pick_n_take(potential_areas) - for(var/area/A in world) + for(var/area/A as anything in GLOB.areas) if(istype(A, picked_area)) areasToOpen += A diff --git a/code/modules/events/spacevine.dm b/code/modules/events/spacevine.dm index a6a728b029c..61571a4cdd4 100644 --- a/code/modules/events/spacevine.dm +++ b/code/modules/events/spacevine.dm @@ -64,8 +64,8 @@ var/obj/structure/spacevine/vine = new() - for(var/area/station/hallway/area in world) - for(var/turf/floor in area) + for(var/area/station/hallway/area in GLOB.areas) + for(var/turf/floor as anything in area.get_contained_turfs()) if(floor.Enter(vine)) turfs += floor diff --git a/code/modules/events/stray_cargo.dm b/code/modules/events/stray_cargo.dm index 1931867dbf0..b17cfd2b2af 100644 --- a/code/modules/events/stray_cargo.dm +++ b/code/modules/events/stray_cargo.dm @@ -56,7 +56,7 @@ if(ispath(pack_type, /datum/supply_pack)) SP = new pack_type else // treat this as a supply pack id and resolving it with SSshuttle - SP = SSshuttle.supply_packs[pack_type] + SP = SSshuttle.supply_packs[pack_type] var/obj/structure/closet/crate/crate = SP.generate(null) if(crate) //empty supply packs are a thing! get memed on. crate.locked = FALSE //Unlock secure crates @@ -84,7 +84,7 @@ ///Subtypes from the above that actually should explode. var/static/list/unsafe_area_subtypes = typecacheof(list(/area/station/engineering/break_room)) allowed_areas = make_associative(GLOB.the_station_areas) - safe_area_types + unsafe_area_subtypes - var/list/possible_areas = typecache_filter_list(GLOB.sortedAreas,allowed_areas) + var/list/possible_areas = typecache_filter_list(GLOB.areas, allowed_areas) if (length(possible_areas)) return pick(possible_areas) diff --git a/code/modules/lighting/lighting_area.dm b/code/modules/lighting/lighting_area.dm index ad2a60c8f0f..c11130dcbc1 100644 --- a/code/modules/lighting/lighting_area.dm +++ b/code/modules/lighting/lighting_area.dm @@ -49,7 +49,7 @@ /area/proc/remove_base_lighting() var/list/z_offsets = SSmapping.z_level_to_plane_offset - for(var/turf/T in src) + 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]) cut_overlay(lighting_effects[1]) @@ -69,7 +69,7 @@ lighting_effects += lighting_effect add_overlay(lighting_effects[1]) var/list/z_offsets = SSmapping.z_level_to_plane_offset - for(var/turf/T in src) + for(var/turf/T as anything in get_contained_turfs()) T.luminosity = 1 // This outside loop is EXTREMELY hot because it's run by space tiles. Don't want no part in that // We will only add overlays to turfs not on the first z layer, because that's a significantly lesser portion diff --git a/code/modules/lighting/lighting_setup.dm b/code/modules/lighting/lighting_setup.dm index ff716ca4bdd..63b038b4026 100644 --- a/code/modules/lighting/lighting_setup.dm +++ b/code/modules/lighting/lighting_setup.dm @@ -1,11 +1,10 @@ /proc/create_all_lighting_objects() - for(var/area/A in world) + for(var/area/A as anything in GLOB.areas) if(!A.static_lighting) continue - // I hate this so much dude. why do areas not track their turfs lummyyyyyyy - for(var/turf/T in A) + for(var/turf/T as anything in A.get_contained_turfs()) if(T.always_lit) continue new/datum/lighting_object(T) diff --git a/code/modules/mapping/map_template.dm b/code/modules/mapping/map_template.dm index c2fa1136f9a..c5ac7edeca7 100644 --- a/code/modules/mapping/map_template.dm +++ b/code/modules/mapping/map_template.dm @@ -133,14 +133,13 @@ var/x = round((world.maxx - width) * 0.5) + 1 var/y = round((world.maxy - height) * 0.5) + 1 - var/datum/space_level/level = SSmapping.add_new_zlevel(name, secret ? ZTRAITS_AWAY_SECRET : ZTRAITS_AWAY) - var/datum/parsed_map/parsed = load_map(file(mappath), x, y, level.z_value, no_changeturf=(SSatoms.initialized == INITIALIZATION_INSSATOMS), placeOnTop=should_place_on_top) + var/datum/space_level/level = SSmapping.add_new_zlevel(name, secret ? ZTRAITS_AWAY_SECRET : ZTRAITS_AWAY, contain_turfs = FALSE) + var/datum/parsed_map/parsed = load_map(file(mappath), x, y, level.z_value, no_changeturf=(SSatoms.initialized == INITIALIZATION_INSSATOMS), placeOnTop=should_place_on_top, new_z = TRUE) var/list/bounds = parsed.bounds if(!bounds) return FALSE - repopulate_sorted_areas() - + require_area_resort() //initialize things that are normally initialized after map load initTemplateBounds(bounds) smooth_zlevel(world.maxz) @@ -181,8 +180,7 @@ if(!bounds) return - if(!SSmapping.loading_ruins) //Will be done manually during mapping ss init - repopulate_sorted_areas() + require_area_resort() //initialize things that are normally initialized after map load initTemplateBounds(bounds) diff --git a/code/modules/mapping/reader.dm b/code/modules/mapping/reader.dm index 3dee73f8647..a55e090a893 100644 --- a/code/modules/mapping/reader.dm +++ b/code/modules/mapping/reader.dm @@ -50,10 +50,10 @@ /// - `no_changeturf`: When true, [/turf/proc/AfterChange] won't be called on loaded turfs /// - `x_lower`, `x_upper`, `y_lower`, `y_upper`: Coordinates (relative to the map) to crop to (Optional). /// - `placeOnTop`: Whether to use [/turf/proc/PlaceOnTop] rather than [/turf/proc/ChangeTurf] (Optional). -/proc/load_map(dmm_file as file, x_offset as num, y_offset as num, z_offset as num, cropMap as num, measureOnly as num, no_changeturf as num, x_lower = -INFINITY as num, x_upper = INFINITY as num, y_lower = -INFINITY as num, y_upper = INFINITY as num, placeOnTop = FALSE as num) +/proc/load_map(dmm_file as file, x_offset as num, y_offset as num, z_offset as num, cropMap as num, measureOnly as num, no_changeturf as num, x_lower = -INFINITY as num, x_upper = INFINITY as num, y_lower = -INFINITY as num, y_upper = INFINITY as num, placeOnTop = FALSE as num, new_z) var/datum/parsed_map/parsed = new(dmm_file, x_lower, x_upper, y_lower, y_upper, measureOnly) if(parsed.bounds && !measureOnly) - parsed.load(x_offset, y_offset, z_offset, cropMap, no_changeturf, x_lower, x_upper, y_lower, y_upper, placeOnTop) + parsed.load(x_offset, y_offset, z_offset, cropMap, no_changeturf, x_lower, x_upper, y_lower, y_upper, placeOnTop, new_z = new_z) return parsed /// Parse a map, possibly cropping it. @@ -157,10 +157,10 @@ src.key_len = key_len /// Load the parsed map into the world. See [/proc/load_map] for arguments. -/datum/parsed_map/proc/load(x_offset, y_offset, z_offset, cropMap, no_changeturf, x_lower, x_upper, y_lower, y_upper, placeOnTop, whitelist = FALSE) +/datum/parsed_map/proc/load(x_offset, y_offset, z_offset, cropMap, no_changeturf, x_lower, x_upper, y_lower, y_upper, placeOnTop, whitelist = FALSE, new_z) //How I wish for RAII Master.StartLoadingMap() - . = _load_impl(x_offset, y_offset, z_offset, cropMap, no_changeturf, x_lower, x_upper, y_lower, y_upper, placeOnTop) + . = _load_impl(x_offset, y_offset, z_offset, cropMap, no_changeturf, x_lower, x_upper, y_lower, y_upper, placeOnTop, new_z) Master.StopLoadingMap() @@ -171,7 +171,7 @@ SSatoms.map_loader_begin(); \ } // Do not call except via load() above. -/datum/parsed_map/proc/_load_impl(x_offset = 1, y_offset = 1, z_offset = world.maxz + 1, cropMap = FALSE, no_changeturf = FALSE, x_lower = -INFINITY, x_upper = INFINITY, y_lower = -INFINITY, y_upper = INFINITY, placeOnTop = FALSE) +/datum/parsed_map/proc/_load_impl(x_offset = 1, y_offset = 1, z_offset = world.maxz + 1, cropMap = FALSE, no_changeturf = FALSE, x_lower = -INFINITY, x_upper = INFINITY, y_lower = -INFINITY, y_upper = INFINITY, placeOnTop = FALSE, new_z = FALSE) PRIVATE_PROC(TRUE) var/list/modelCache = build_cache(no_changeturf) var/space_key = modelCache[SPACE_KEY] @@ -288,7 +288,7 @@ if(!cache) SSatoms.map_loader_stop() CRASH("Undefined model key in DMM: [line]") - build_coordinate(cache, locate(true_xcrd, ycrd, zcrd), no_afterchange, placeOnTop) + build_coordinate(cache, locate(true_xcrd, ycrd, zcrd), no_afterchange, placeOnTop, new_z) // only bother with bounds that actually exist if(!first_found) @@ -320,7 +320,7 @@ if(!cache) SSatoms.map_loader_stop() CRASH("Undefined model key in DMM: [model_key]") - build_coordinate(cache, locate(xcrd, ycrd, zcrd), no_afterchange, placeOnTop) + build_coordinate(cache, locate(xcrd, ycrd, zcrd), no_afterchange, placeOnTop, new_z) // only bother with bounds that actually exist if(!first_found) @@ -347,6 +347,10 @@ //we do this after we load everything in. if we don't; we'll have weird atmos bugs regarding atmos adjacent turfs T.AfterChange(CHANGETURF_IGNORE_AIR) + if(new_z) + for(var/z_index in bounds[MAP_MINZ] to bounds[MAP_MAXZ]) + SSmapping.build_area_turfs(z_index) + if(has_expanded_world_maxx || has_expanded_world_maxy) SEND_GLOBAL_SIGNAL(COMSIG_GLOB_EXPANDED_WORLD_BOUNDS, has_expanded_world_maxx, has_expanded_world_maxy) @@ -437,7 +441,7 @@ GLOBAL_LIST_EMPTY(map_model_default) .[model_key] = list(members, members_attributes) -/datum/parsed_map/proc/build_coordinate(list/model, turf/crds, no_changeturf as num, placeOnTop as num) +/datum/parsed_map/proc/build_coordinate(list/model, turf/crds, no_changeturf as num, placeOnTop as num, new_z) // If we don't have a turf, nothing we will do next will actually acomplish anything, so just go back // Note, this would actually drop area vvs in the tile, but like, why tho if(!crds) @@ -458,29 +462,34 @@ GLOBAL_LIST_EMPTY(map_model_default) //The next part of the code assumes there's ALWAYS an /area AND a /turf on a given tile //first instance the /area and remove it from the members list index = members.len - var/atom/instance if(members[index] != /area/template_noop) + var/area/area_instance if(members_attributes[index] != default_list) world.preloader_setup(members_attributes[index], members[index])//preloader for assigning set variables on atom creation - instance = loaded_areas[members[index]] - if(!instance) + area_instance = loaded_areas[members[index]] + if(!area_instance) var/area_type = members[index] // If this parsed map doesn't have that area already, we check the global cache - instance = GLOB.areas_by_type[area_type] + area_instance = GLOB.areas_by_type[area_type] // If the global list DOESN'T have this area it's either not a unique area, or it just hasn't been created yet - if (!instance) - instance = new area_type(null) - if(!instance) + if (!area_instance) + area_instance = new area_type(null) + if(!area_instance) CRASH("[area_type] failed to be new'd, what'd you do?") - loaded_areas[area_type] = instance + loaded_areas[area_type] = area_instance - instance.contents.Add(crds) + if(!new_z) + var/area/old_area = crds.loc + old_area.turfs_to_uncontain += crds + area_instance.contained_turfs.Add(crds) + area_instance.contents.Add(crds) if(GLOB.use_preloader) - world.preloader_load(instance) + world.preloader_load(area_instance) // Index right before /area is /turf index-- + var/atom/instance //then instance the /turf //NOTE: this used to place any turfs before the last "underneath" it using .appearance and underlays //We don't actually use this, and all it did was cost cpu, so we don't do this anymore diff --git a/code/modules/mapping/space_management/zlevel_manager.dm b/code/modules/mapping/space_management/zlevel_manager.dm index 3458ae8462f..1c2e73ab003 100644 --- a/code/modules/mapping/space_management/zlevel_manager.dm +++ b/code/modules/mapping/space_management/zlevel_manager.dm @@ -16,10 +16,10 @@ for (var/I in 1 to default_map_traits.len) var/list/features = default_map_traits[I] var/datum/space_level/S = new(I, features[DL_NAME], features[DL_TRAITS]) - manage_z_level(S) + manage_z_level(S, filled_with_space = FALSE) generate_z_level_linkages() // Default Zs don't use add_new_zlevel() so they don't automatically generate z-linkages. -/datum/controller/subsystem/mapping/proc/add_new_zlevel(name, traits = list(), z_type = /datum/space_level) +/datum/controller/subsystem/mapping/proc/add_new_zlevel(name, traits = list(), z_type = /datum/space_level, contain_turfs = TRUE) UNTIL(!adding_new_zlevel) adding_new_zlevel = TRUE var/new_z = z_list.len + 1 @@ -28,7 +28,7 @@ CHECK_TICK // TODO: sleep here if the Z level needs to be cleared var/datum/space_level/S = new z_type(new_z, name, traits) - manage_z_level(S) + manage_z_level(S, filled_with_space = TRUE, contain_turfs = contain_turfs) generate_linkages_for_z_level(new_z) calculate_z_level_gravity(new_z) adding_new_zlevel = FALSE diff --git a/code/modules/mob/dead/observer/observer.dm b/code/modules/mob/dead/observer/observer.dm index adb89342e3b..982c2930efc 100644 --- a/code/modules/mob/dead/observer/observer.dm +++ b/code/modules/mob/dead/observer/observer.dm @@ -465,8 +465,7 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp to_chat(usr, span_warning("Not when you're not dead!")) return var/list/filtered = list() - for(var/V in GLOB.sortedAreas) - var/area/A = V + for(var/area/A as anything in get_sorted_areas()) if(!(A.area_flags & HIDDEN_AREA)) filtered += A var/area/thearea = tgui_input_list(usr, "Area to jump to", "BOOYEA", filtered) diff --git a/code/modules/procedural_mapping/mapGenerators/repair.dm b/code/modules/procedural_mapping/mapGenerators/repair.dm index b305ac2c438..03234bf78e5 100644 --- a/code/modules/procedural_mapping/mapGenerators/repair.dm +++ b/code/modules/procedural_mapping/mapGenerators/repair.dm @@ -34,7 +34,7 @@ var/list/obj/structure/cable/cables = list() var/list/atom/atoms = list() - repopulate_sorted_areas() + require_area_resort() for(var/L in block(locate(bounds[MAP_MINX], bounds[MAP_MINY], SSmapping.station_start), locate(bounds[MAP_MAXX], bounds[MAP_MAXY], z_offset - 1))) diff --git a/code/modules/religion/rites.dm b/code/modules/religion/rites.dm index e3e5801fda3..8bbc957a47b 100644 --- a/code/modules/religion/rites.dm +++ b/code/modules/religion/rites.dm @@ -656,7 +656,7 @@ /datum/religion_rites/declare_arena/perform_rite(mob/living/user, atom/religious_tool) var/list/filtered = list() - for(var/area/unfiltered_area as anything in GLOB.sortedAreas) + for(var/area/unfiltered_area as anything in get_sorted_areas()) if(istype(unfiltered_area, /area/centcom)) //youuu dont need thaaat continue if(!(unfiltered_area.area_flags & HIDDEN_AREA)) diff --git a/code/modules/security_levels/keycard_authentication.dm b/code/modules/security_levels/keycard_authentication.dm index 99d812ebf10..97fe0a7d3f6 100644 --- a/code/modules/security_levels/keycard_authentication.dm +++ b/code/modules/security_levels/keycard_authentication.dm @@ -151,19 +151,21 @@ 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 world) - for(var/obj/machinery/door/airlock/D in A) - D.emergency = TRUE - D.update_icon(ALL, 0) + 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) 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 world) - for(var/obj/machinery/door/airlock/D in A) - D.emergency = FALSE - D.update_icon(ALL, 0) + 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) 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 7f8db5bf451..ea1ba8ed052 100644 --- a/code/modules/shuttle/arrivals.dm +++ b/code/modules/shuttle/arrivals.dm @@ -36,7 +36,7 @@ areas = list() var/list/new_latejoin = list() - for(var/area/shuttle/arrival/A in GLOB.sortedAreas) + for(var/area/shuttle/arrival/A in GLOB.areas) for(var/obj/structure/chair/C in A) new_latejoin += C if(!console) diff --git a/code/modules/shuttle/emergency.dm b/code/modules/shuttle/emergency.dm index 3ccb5c16465..7e07b0ee535 100644 --- a/code/modules/shuttle/emergency.dm +++ b/code/modules/shuttle/emergency.dm @@ -481,7 +481,7 @@ if(time_left <= 50 && !sound_played) //4 seconds left:REV UP THOSE ENGINES BOYS. - should sync up with the launch sound_played = 1 //Only rev them up once. var/list/areas = list() - for(var/area/shuttle/escape/E in GLOB.sortedAreas) + for(var/area/shuttle/escape/E in GLOB.areas) areas += E hyperspace_sound(HYPERSPACE_WARMUP, areas) @@ -493,7 +493,7 @@ //now move the actual emergency shuttle to its transit dock var/list/areas = list() - for(var/area/shuttle/escape/E in GLOB.sortedAreas) + for(var/area/shuttle/escape/E in GLOB.areas) areas += E hyperspace_sound(HYPERSPACE_LAUNCH, areas) enterTransit() @@ -512,7 +512,7 @@ if(SHUTTLE_ESCAPE) if(sound_played && time_left <= HYPERSPACE_END_TIME) var/list/areas = list() - for(var/area/shuttle/escape/E in GLOB.sortedAreas) + for(var/area/shuttle/escape/E in GLOB.areas) areas += E hyperspace_sound(HYPERSPACE_END, areas) if(time_left <= PARALLAX_LOOP_TIME) diff --git a/code/modules/shuttle/on_move.dm b/code/modules/shuttle/on_move.dm index e6ed746d248..d4b3526a20e 100644 --- a/code/modules/shuttle/on_move.dm +++ b/code/modules/shuttle/on_move.dm @@ -171,7 +171,9 @@ All ShuttleMove procs go here return TRUE contents -= oldT + turfs_to_uncontain += oldT underlying_old_area.contents += oldT + underlying_old_area.contained_turfs += oldT oldT.transfer_area_lighting(src, underlying_old_area) //The old turf has now been given back to the area that turf originaly belonged to @@ -179,7 +181,9 @@ All ShuttleMove procs go here parallax_movedir = old_dest_area.parallax_movedir old_dest_area.contents -= newT + old_dest_area.turfs_to_uncontain += newT contents += newT + contained_turfs += newT newT.transfer_area_lighting(old_dest_area, src) return TRUE diff --git a/code/modules/shuttle/shuttle.dm b/code/modules/shuttle/shuttle.dm index fc931c09f23..08e726fbf07 100644 --- a/code/modules/shuttle/shuttle.dm +++ b/code/modules/shuttle/shuttle.dm @@ -673,7 +673,9 @@ if(!oldT || !istype(oldT.loc, area_type)) continue var/area/old_area = oldT.loc + old_area.turfs_to_uncontain += oldT underlying_area.contents += oldT + underlying_area.contained_turfs += oldT oldT.transfer_area_lighting(old_area, underlying_area) oldT.empty(FALSE) diff --git a/code/modules/unit_tests/_unit_tests.dm b/code/modules/unit_tests/_unit_tests.dm index 1ec9dff55a4..6845a9be83d 100644 --- a/code/modules/unit_tests/_unit_tests.dm +++ b/code/modules/unit_tests/_unit_tests.dm @@ -53,8 +53,8 @@ #define TEST_PRE 0 #define TEST_DEFAULT 1 -/// This should be one of the last tests to run, because we run it for a little bit over 30 seconds by design and we want to run all of the tests that take a trivial amount of time to complete beforehand. -#define TEST_MONKEY_BUSINESS 10 +/// After most test steps, used for tests that run long so shorter issues can be noticed faster +#define TEST_LONGER 10 /// This must be the last test to run due to the inherent nature of the test iterating every single tangible atom in the game and qdeleting all of them (while taking long sleeps to make sure the garbage collector fires properly) taking a large amount of time. #define TEST_CREATE_AND_DESTROY INFINITY @@ -82,6 +82,7 @@ #include "achievements.dm" #include "anchored_mobs.dm" #include "anonymous_themes.dm" +#include "area_contents.dm" #include "autowiki.dm" #include "barsigns.dm" #include "bespoke_id.dm" diff --git a/code/modules/unit_tests/area_contents.dm b/code/modules/unit_tests/area_contents.dm new file mode 100644 index 00000000000..7d5584bf8b0 --- /dev/null +++ b/code/modules/unit_tests/area_contents.dm @@ -0,0 +1,30 @@ +/// Verifies that an area's perception of their "turfs" is correct, and no other area overlaps with them +/// Quite slow, but needed +/datum/unit_test/area_contents + priority = TEST_LONGER + +/datum/unit_test/area_contents/Run() + /// assoc list of turfs -> areas + var/list/turf_to_area = list() + // 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") + var/area/existing = turf_to_area[position] + if(existing == space) + TEST_FAIL("Found a duplicate turf [position.type] inside [space.type]'s turf listing") + else if(existing) + TEST_FAIL("Found a shared turf [position.type] between [space.type] and [existing.type]'s turf listings") + + 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]") + + turf_to_area[position] = space + + for(var/turf/position in ALL_TURFS()) + if(!turf_to_area[position]) + TEST_FAIL("Found a turf [position.type] inside [position.loc.type] that is NOT stored in any area's turf listing") + diff --git a/code/modules/unit_tests/monkey_business.dm b/code/modules/unit_tests/monkey_business.dm index fbe521fc421..35e41cb1778 100644 --- a/code/modules/unit_tests/monkey_business.dm +++ b/code/modules/unit_tests/monkey_business.dm @@ -8,7 +8,7 @@ * but its also hilarious and fun to watch locally. */ /datum/unit_test/monkey_business - priority = TEST_MONKEY_BUSINESS + priority = TEST_LONGER var/monkey_timer = 30 SECONDS var/monkey_angry_nth = 5 // every nth monkey will be angry diff --git a/modular_skyrat/master_files/code/modules/events/spacevine.dm b/modular_skyrat/master_files/code/modules/events/spacevine.dm index 0f37fdfed7e..6d1f28e045d 100644 --- a/modular_skyrat/master_files/code/modules/events/spacevine.dm +++ b/modular_skyrat/master_files/code/modules/events/spacevine.dm @@ -56,8 +56,8 @@ var/obj/structure/spacevine/vine = new() - for(var/area/station/maintenance/maint_area in world) - for(var/turf/floor in maint_area) + for(var/area/station/maintenance/maint_area in GLOB.areas) + for(var/turf/floor as anything in maint_area.get_contained_turfs()) if(floor.Enter(vine)) turfs += floor diff --git a/modular_skyrat/modules/assault_operatives/code/interrogator.dm b/modular_skyrat/modules/assault_operatives/code/interrogator.dm index 49494a51948..167a2f391c1 100644 --- a/modular_skyrat/modules/assault_operatives/code/interrogator.dm +++ b/modular_skyrat/modules/assault_operatives/code/interrogator.dm @@ -204,8 +204,8 @@ var/obj/structure/test_structure = new() // This is apparently the most intuative way to check if a turf is able to support entering. - for(var/area/station/maintenance/maint_area in world) - for(var/turf/floor in maint_area) + for(var/area/station/maintenance/maint_area in GLOB.areas) + for(var/turf/floor as anything in maint_area.get_contained_turfs()) if(!is_station_level(floor.z)) continue if(floor.Enter(test_structure)) diff --git a/modular_skyrat/modules/automapper/code/area_spawn_subsystem.dm b/modular_skyrat/modules/automapper/code/area_spawn_subsystem.dm index 6ce284e31c7..a010a36e986 100644 --- a/modular_skyrat/modules/automapper/code/area_spawn_subsystem.dm +++ b/modular_skyrat/modules/automapper/code/area_spawn_subsystem.dm @@ -82,7 +82,7 @@ SUBSYSTEM_DEF(area_spawn) turf_list = area_turf_info["[mode]"] = list() // Get highest priority items - for(var/turf/iterating_turf in area) + 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) diff --git a/modular_skyrat/modules/biohazard_blob/code/mold_event.dm b/modular_skyrat/modules/biohazard_blob/code/mold_event.dm index f42c5d9a994..b0e73166ae1 100644 --- a/modular_skyrat/modules/biohazard_blob/code/mold_event.dm +++ b/modular_skyrat/modules/biohazard_blob/code/mold_event.dm @@ -30,12 +30,12 @@ var/list/possible_spawn_areas = typecacheof(typesof(/area/station/maintenance, /area/station/security/prison, /area/station/construction)) - for(var/area/A in world) + for(var/area/A as anything in GLOB.areas) if(!is_station_level(A.z)) continue if(!is_type_in_typecache(A, possible_spawn_areas)) continue - for(var/turf/open/floor in A) + for(var/turf/open/floor in A.get_contained_turfs()) if(!floor.Enter(resintest)) continue if(locate(/turf/closed) in range(2, floor)) diff --git a/modular_skyrat/modules/decay_subsystem/code/decaySS.dm b/modular_skyrat/modules/decay_subsystem/code/decaySS.dm index 16350edbab2..2aa0274ca8a 100644 --- a/modular_skyrat/modules/decay_subsystem/code/decaySS.dm +++ b/modular_skyrat/modules/decay_subsystem/code/decaySS.dm @@ -45,18 +45,17 @@ SUBSYSTEM_DEF(decay) log_world("SSDecay will not interact with this round.") return SS_INIT_NO_NEED - for(var/turf/iterating_turf in world) - if(!is_station_level(iterating_turf.z)) - continue - if(!(iterating_turf.flags_1 & CAN_BE_DIRTY_1)) - continue - possible_turfs += iterating_turf - - for(var/area/iterating_area in world) + for(var/area/iterating_area as anything in GLOB.areas) if(!is_station_level(iterating_area.z)) continue 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 + if(!possible_turfs) CRASH("SSDecay had no possible turfs to use!") diff --git a/tgstation.dme b/tgstation.dme index 1b098e9a2b5..787a1d7a5fc 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -562,6 +562,7 @@ #include "code\controllers\subsystem\ai_controllers.dm" #include "code\controllers\subsystem\air.dm" #include "code\controllers\subsystem\ambience.dm" +#include "code\controllers\subsystem\area_contents.dm" #include "code\controllers\subsystem\asset_loading.dm" #include "code\controllers\subsystem\assets.dm" #include "code\controllers\subsystem\atoms.dm"