diff --git a/code/ZAS/Controller.dm b/code/ZAS/Controller.dm
index 9688fabf72..af0dbe838d 100644
--- a/code/ZAS/Controller.dm
+++ b/code/ZAS/Controller.dm
@@ -80,176 +80,9 @@ Class Procs:
/datum/controller/air_system/var/active_zones = 0
/datum/controller/air_system/var/current_cycle = 0
-/datum/controller/air_system/var/update_delay = 5 //How long between check should it try to process atmos again.
-/datum/controller/air_system/var/failed_ticks = 0 //How many ticks have runtimed?
-
-/datum/controller/air_system/var/tick_progress = 0
/datum/controller/air_system/var/next_id = 1 //Used to keep track of zone UIDs.
-
-#if UNIT_TEST
-#define CHECK_SLEEP_ZAS_SETUP // For unit tests we don't care about a smooth lobby screen experience. We care about speed.
-#else
-#define CHECK_SLEEP_ZAS_SETUP if(++done_turfs > 10000) { done_turfs=0;sleep(world.tick_lag); }
-#endif
-/datum/controller/air_system/proc/Setup()
- //Purpose: Call this at the start to setup air groups geometry
- // (Warning: Very processor intensive but only must be done once per round)
- //Called by: Gameticker/Master controller
- //Inputs: None.
- //Outputs: None.
-
- #if !UNIT_TEST
- var/done_turfs = 0
- #endif
- #ifndef ZASDBG
- set background = 1
- #endif
-
- admin_notice("Processing Geometry...", R_DEBUG)
- sleep(-1)
-
- var/start_time = world.timeofday
-
- var/simulated_turf_count = 0
-
- for(var/turf/simulated/S in world)
- simulated_turf_count++
- S.update_air_properties()
- CHECK_SLEEP_ZAS_SETUP
-
- admin_notice({"Geometry initialized in [round(0.1*(world.timeofday-start_time),0.1)] seconds.
-
-Total Simulated Turfs: [simulated_turf_count]
-Total Zones: [zones.len]
-Total Edges: [edges.len]
-Total Active Edges: [active_edges.len ? "[active_edges.len]" : "None"]
-Total Unsimulated Turfs: [world.maxx*world.maxy*world.maxz - simulated_turf_count]
-"}, R_DEBUG)
-
- // Uncomment this if you're having problems finding where active edges are.
- /*
- for(var/connection_edge/E in active_edges)
- world << "Edge became active: [E]."
- var/i = 1
- for(var/turf/T in E.connecting_turfs)
- world << "[i] [T]:[T.x],[T.y],[T.z]"
- i++
- */
-
-
-// spawn Start()
-
-
-/datum/controller/air_system/proc/Start()
- //Purpose: This is kicked off by the master controller, and controls the processing of all atmosphere.
- //Called by: Master controller
- //Inputs: None.
- //Outputs: None.
-
- #ifndef ZASDBG
- set background = 1
- #endif
-
- while(1)
- Tick()
- sleep(max(5,update_delay*tick_multiplier))
-
-
-/datum/controller/air_system/proc/Tick()
- . = 1 //Set the default return value, for runtime detection.
-
- current_cycle++
-
- //If there are tiles to update, do so.
- tick_progress = "updating turf properties"
-
- var/list/updating
-
- if(tiles_to_update.len)
- updating = tiles_to_update
- tiles_to_update = list()
-
- #ifdef ZASDBG
- var/updated = 0
- #endif
-
- //defer updating of self-zone-blocked turfs until after all other turfs have been updated.
- //this hopefully ensures that non-self-zone-blocked turfs adjacent to self-zone-blocked ones
- //have valid zones when the self-zone-blocked turfs update.
-
- //This ensures that doorways don't form their own single-turf zones, since doorways are self-zone-blocked and
- //can merge with an adjacent zone, whereas zones that are formed on adjacent turfs cannot merge with the doorway.
- var/list/deferred = list()
-
- for(var/turf/T in updating)
- //check if the turf is self-zone-blocked
- if(T.c_airblock(T) & ZONE_BLOCKED)
- deferred += T
- continue
-
- T.update_air_properties()
- T.post_update_air_properties()
- T.needs_air_update = 0
- #ifdef ZASDBG
- T.overlays -= mark
- updated++
- #endif
- //sleep(1)
-
- for(var/turf/T in deferred)
- T.update_air_properties()
- T.post_update_air_properties()
- T.needs_air_update = 0
- #ifdef ZASDBG
- T.overlays -= mark
- updated++
- #endif
-
- #ifdef ZASDBG
- if(updated != updating.len)
- tick_progress = "[updating.len - updated] tiles left unupdated."
- world << "[tick_progress]"
- . = 0
- #endif
-
- //Where gas exchange happens.
- if(.)
- tick_progress = "processing edges"
-
- for(var/connection_edge/edge in active_edges)
- edge.tick()
-
- //Process fire zones.
- if(.)
- tick_progress = "processing fire zones"
-
- for(var/zone/Z in active_fire_zones)
- Z.process_fire()
-
- //Process hotspots.
- if(.)
- tick_progress = "processing hotspots"
-
- for(var/obj/fire/fire in active_hotspots)
- fire.process()
-
- //Process zones.
- if(.)
- tick_progress = "updating zones"
-
- active_zones = zones_to_update.len
- if(zones_to_update.len)
- updating = zones_to_update
- zones_to_update = list()
- for(var/zone/zone in updating)
- zone.tick()
- zone.needs_update = 0
-
- if(.)
- tick_progress = "success"
-
/datum/controller/air_system/proc/add_zone(zone/z)
zones.Add(z)
z.name = "Zone [next_id++]"
diff --git a/code/ZAS/Diagnostic.dm b/code/ZAS/Diagnostic.dm
index cbf35d93bd..29ab678ef1 100644
--- a/code/ZAS/Diagnostic.dm
+++ b/code/ZAS/Diagnostic.dm
@@ -1,6 +1,12 @@
client/proc/ZoneTick()
set category = "Debug"
set name = "Process Atmos"
+ set desc = "Manually run a single tick of the air subsystem"
+
+ // TODO - This might be a useful diagnostic tool. However its complicated to do with StonedMC
+ // Therefore it is left unimplemented for now until its use actually becomes required. ~Leshana
+ /*
+ if(!check_rights(R_DEBUG)) return
var/result = air_master.Tick()
if(result)
@@ -8,7 +14,7 @@ client/proc/ZoneTick()
else
src << "Failed to process! ([air_master.tick_progress])"
-
+ */
client/proc/Zone_Info(turf/T as null|turf)
set category = "Debug"
diff --git a/code/ZAS/_docs.dm b/code/ZAS/_docs.dm
index 7b28d52688..382f1f0abb 100644
--- a/code/ZAS/_docs.dm
+++ b/code/ZAS/_docs.dm
@@ -26,11 +26,3 @@ Notes for people who used ZAS before:
var/zone/connected_zone = edge.get_connected_zone(zone)
*/
-
-//#define ZASDBG
-#define MULTIZAS
-#define AIR_BLOCKED 1
-#define ZONE_BLOCKED 2
-#define BLOCKED 3
-
-#define ZONE_MIN_SIZE 14 //zones with less than this many turfs will always merge, even if the connection is not direct
diff --git a/code/__defines/ZAS.dm b/code/__defines/ZAS.dm
new file mode 100644
index 0000000000..8712dd2bf7
--- /dev/null
+++ b/code/__defines/ZAS.dm
@@ -0,0 +1,6 @@
+// Bitflag values for c_airblock()
+#define AIR_BLOCKED 1 // Blocked
+#define ZONE_BLOCKED 2 // Not blocked, but zone boundaries will not cross.
+#define BLOCKED 3 // Blocked, zone boundaries will not cross even if opened.
+
+#define ZONE_MIN_SIZE 14 // Zones with less than this many turfs will always merge, even if the connection is not direct
diff --git a/code/__defines/_compile_options.dm b/code/__defines/_compile_options.dm
index 3190e391a4..229d0b94ad 100644
--- a/code/__defines/_compile_options.dm
+++ b/code/__defines/_compile_options.dm
@@ -7,6 +7,10 @@
2 for preloading absolutely everything;
*/
+// ZAS Compile Options
+//#define ZASDBG // Uncomment to turn on super detailed ZAS debugging that probably won't even compile.
+#define MULTIZAS // Uncomment to turn on Multi-Z ZAS Support!
+
// If we are doing the map test build, do not include the main maps, only the submaps.
#if MAP_TEST
#define USING_MAP_DATUM /datum/map
diff --git a/code/__defines/subsystems.dm b/code/__defines/subsystems.dm
index eb350656b9..bec4a7953a 100644
--- a/code/__defines/subsystems.dm
+++ b/code/__defines/subsystems.dm
@@ -18,3 +18,4 @@ var/global/list/runlevel_flags = list(RUNLEVEL_LOBBY, RUNLEVEL_SETUP, RUNLEVEL_G
#define INIT_ORDER_MAPPING 20 // VOREStation Edit
#define INIT_ORDER_MACHINES 10
#define INIT_ORDER_LIGHTING 0
+#define INIT_ORDER_AIR -1
diff --git a/code/_helpers/logging.dm b/code/_helpers/logging.dm
index c58b2d1381..07e2985bbb 100644
--- a/code/_helpers/logging.dm
+++ b/code/_helpers/logging.dm
@@ -88,6 +88,10 @@
/proc/log_unit_test(text)
world.log << "## UNIT_TEST: [text]"
+/proc/report_progress(var/progress_message)
+ admin_notice("[progress_message]", R_DEBUG)
+ to_world_log(progress_message)
+
//pretty print a direction bitflag, can be useful for debugging.
/proc/print_dir(var/dir)
var/list/comps = list()
diff --git a/code/controllers/master.dm b/code/controllers/master.dm
index 3ecb3b7fcb..961f657b2d 100644
--- a/code/controllers/master.dm
+++ b/code/controllers/master.dm
@@ -573,14 +573,11 @@ var/datum/controller/master/Master = new()
var/datum/controller/subsystem/SS = S
SS.StartLoadingMap()
- // ZAS might displace objects as the map loads if an air tick is processed mid-load.
- air_processing_killed = TRUE
map_loading = TRUE
/datum/controller/master/StopLoadingMap(var/quiet = TRUE)
if(!quiet)
admin_notice("Map is finished. Unlocking.", R_DEBUG)
- air_processing_killed = FALSE
map_loading = FALSE
for(var/S in subsystems)
var/datum/controller/subsystem/SS = S
diff --git a/code/controllers/master_controller.dm b/code/controllers/master_controller.dm
index ab72c18292..e92ec6a3df 100644
--- a/code/controllers/master_controller.dm
+++ b/code/controllers/master_controller.dm
@@ -11,7 +11,6 @@ var/global/datum/controller/game_controller/master_controller //Set in world.New
var/global/controller_iteration = 0
var/global/last_tick_duration = 0
-var/global/air_processing_killed = 0
var/global/pipe_processing_killed = 0
datum/controller/game_controller
diff --git a/code/controllers/subsystems/air.dm b/code/controllers/subsystems/air.dm
new file mode 100644
index 0000000000..b5d007504a
--- /dev/null
+++ b/code/controllers/subsystems/air.dm
@@ -0,0 +1,278 @@
+// Air update stages
+#define SSAIR_TURFS 1
+#define SSAIR_EDGES 2
+#define SSAIR_FIREZONES 3
+#define SSAIR_HOTSPOTS 4
+#define SSAIR_ZONES 5
+#define SSAIR_DONE 6
+
+SUBSYSTEM_DEF(air)
+ name = "Air"
+ init_order = INIT_ORDER_AIR
+ priority = 20
+ wait = 2 SECONDS // seconds (We probably can speed this up actually)
+ flags = SS_BACKGROUND // TODO - Should this really be background? It might be important.
+ runlevels = RUNLEVEL_GAME | RUNLEVEL_POSTGAME
+ var/static/list/part_names = list("turfs", "edges", "fire zones", "hotspots", "zones")
+
+ var/cost_turfs = 0
+ var/cost_edges = 0
+ var/cost_firezones = 0
+ var/cost_hotspots = 0
+ var/cost_zones = 0
+
+ var/list/currentrun = null
+ var/current_step = null
+
+ // Updating zone tiles requires temporary storage location of self-zone-blocked turfs across resumes. Used only by process_tiles_to_update.
+ var/list/selfblock_deferred = null
+
+/datum/controller/subsystem/air/PreInit()
+ // Initialize the singleton /datum/controller/air_system
+ // TODO - We could actually incorporate that into this subsystem! But in the spirit of not fucking with ZAS more than necessary, lets not for now. ~Leshana
+ air_master = new()
+
+/datum/controller/subsystem/air/Initialize(timeofday)
+ report_progress("Processing Geometry...")
+
+ var/simulated_turf_count = 0
+ for(var/turf/simulated/S in world)
+ simulated_turf_count++
+ S.update_air_properties()
+ CHECK_TICK
+
+ admin_notice({"Geometry initialized in [round(0.1*(REALTIMEOFDAY-timeofday),0.1)] seconds.
+
+Total Simulated Turfs: [simulated_turf_count]
+Total Zones: [air_master.zones.len]
+Total Edges: [air_master.edges.len]
+Total Active Edges: [air_master.active_edges.len ? "[air_master.active_edges.len]" : "None"]
+Total Unsimulated Turfs: [world.maxx*world.maxy*world.maxz - simulated_turf_count]
+"}, R_DEBUG)
+
+ // Note - Baystation settles the air by running for one tick. We prefer to not have active edges.
+ // Maps should not have active edges on boot. If we've got some, log it so it can get fixed.
+ if(air_master.active_edges.len)
+ var/list/edge_log = list()
+ for(var/connection_edge/E in air_master.active_edges)
+ edge_log += "Active Edge [E] ([E.type])"
+ for(var/turf/T in E.connecting_turfs)
+ edge_log += "+--- Connecting Turf [T] @ [T.x], [T.y], [T.z]"
+ log_debug("Active Edges on ZAS Startup\n" + edge_log.Join("\n"))
+
+ ..()
+
+/datum/controller/subsystem/air/fire(resumed = 0)
+ var/timer
+ if(!resumed)
+ ASSERT(LAZYLEN(currentrun) == 0) // Santity checks to make sure we don't somehow have items left over from last cycle
+ ASSERT(current_step == null) // Or somehow didn't finish all the steps from last cycle
+ air_master.current_cycle++ // Begin a new air_master cycle!
+ current_step = SSAIR_TURFS // Start with Step 1 of course
+
+ INTERNAL_PROCESS_STEP(SSAIR_TURFS, TRUE, process_tiles_to_update, cost_turfs, SSAIR_EDGES)
+ INTERNAL_PROCESS_STEP(SSAIR_EDGES, FALSE, process_active_edges, cost_edges, SSAIR_FIREZONES)
+ INTERNAL_PROCESS_STEP(SSAIR_FIREZONES, FALSE, process_active_fire_zones, cost_firezones, SSAIR_HOTSPOTS)
+ INTERNAL_PROCESS_STEP(SSAIR_HOTSPOTS, FALSE, process_active_hotspots, cost_hotspots, SSAIR_ZONES)
+ INTERNAL_PROCESS_STEP(SSAIR_ZONES, FALSE, process_zones_to_update, cost_zones, SSAIR_DONE)
+
+ // Okay, we're done! Woo! Got thru a whole air_master cycle!
+ ASSERT(LAZYLEN(currentrun) == 0) // Sanity checks to make sure there are really none left
+ ASSERT(current_step == SSAIR_DONE) // And that we didn't somehow skip past the last step
+ currentrun = null
+ current_step = null
+
+/datum/controller/subsystem/air/proc/process_tiles_to_update(resumed = 0)
+ if (!resumed)
+ // NOT a copy, because we are supposed to drain active turfs each cycle anyway, so just replace with empty list.
+ // We still use a separate list tho, to ensure we don't process a turf twice during a single cycle!
+ src.currentrun = air_master.tiles_to_update
+ air_master.tiles_to_update = list()
+
+ //defer updating of self-zone-blocked turfs until after all other turfs have been updated.
+ //this hopefully ensures that non-self-zone-blocked turfs adjacent to self-zone-blocked ones
+ //have valid zones when the self-zone-blocked turfs update.
+ //This ensures that doorways don't form their own single-turf zones, since doorways are self-zone-blocked and
+ //can merge with an adjacent zone, whereas zones that are formed on adjacent turfs cannot merge with the doorway.
+ ASSERT(src.selfblock_deferred == null) // Sanity check to make sure it was not remaining from last cycle somehow.
+ src.selfblock_deferred = list()
+
+ //cache for sanic speed (lists are references anyways)
+ var/list/currentrun = src.currentrun
+ var/list/selfblock_deferred = src.selfblock_deferred
+
+ // Run thru the list, processing non-self-zone-blocked and deferring self-zone-blocked
+ while(currentrun.len)
+ var/turf/T = currentrun[currentrun.len]
+ currentrun.len--
+ //check if the turf is self-zone-blocked
+ if(T.c_airblock(T) & ZONE_BLOCKED)
+ selfblock_deferred += T
+ if(MC_TICK_CHECK)
+ return
+ else
+ continue
+ T.update_air_properties()
+ T.post_update_air_properties()
+ T.needs_air_update = 0
+ #ifdef ZASDBG
+ T.overlays -= mark
+ #endif
+ if(MC_TICK_CHECK)
+ return
+
+ ASSERT(LAZYLEN(currentrun) == 0)
+
+ // Run thru the deferred list and processing them
+ while(selfblock_deferred.len)
+ var/turf/T = selfblock_deferred[selfblock_deferred.len]
+ selfblock_deferred.len--
+ T.update_air_properties()
+ T.post_update_air_properties()
+ T.needs_air_update = 0
+ #ifdef ZASDBG
+ T.overlays -= mark
+ #endif
+ if(MC_TICK_CHECK)
+ return
+
+ ASSERT(LAZYLEN(selfblock_deferred) == 0)
+ src.selfblock_deferred = null
+
+/datum/controller/subsystem/air/proc/process_active_edges(resumed = 0)
+ if (!resumed)
+ src.currentrun = air_master.active_edges.Copy()
+ //cache for sanic speed (lists are references anyways)
+ var/list/currentrun = src.currentrun
+ while(currentrun.len)
+ var/connection_edge/edge = currentrun[currentrun.len]
+ currentrun.len--
+ if(edge) // TODO - Do we need to check this? Old one didn't, but old one was single-threaded.
+ edge.tick()
+ if(MC_TICK_CHECK)
+ return
+
+/datum/controller/subsystem/air/proc/process_active_fire_zones(resumed = 0)
+ if (!resumed)
+ src.currentrun = air_master.active_fire_zones.Copy()
+ //cache for sanic speed (lists are references anyways)
+ var/list/currentrun = src.currentrun
+ while(currentrun.len)
+ var/zone/Z = currentrun[currentrun.len]
+ currentrun.len--
+ if(Z) // TODO - Do we need to check this? Old one didn't, but old one was single-threaded.
+ Z.process_fire()
+ if(MC_TICK_CHECK)
+ return
+
+/datum/controller/subsystem/air/proc/process_active_hotspots(resumed = 0)
+ if (!resumed)
+ src.currentrun = air_master.active_hotspots.Copy()
+ //cache for sanic speed (lists are references anyways)
+ var/list/currentrun = src.currentrun
+ while(currentrun.len)
+ var/obj/fire/fire = currentrun[currentrun.len]
+ currentrun.len--
+ if(fire) // TODO - Do we need to check this? Old one didn't, but old one was single-threaded.
+ fire.process()
+ if(MC_TICK_CHECK)
+ return
+
+/datum/controller/subsystem/air/proc/process_zones_to_update(resumed = 0)
+ if (!resumed)
+ air_master.active_zones = air_master.zones_to_update.len // Save how many zones there were to update this cycle (used by some debugging stuff)
+ if(!air_master.zones_to_update.len)
+ return // Nothing to do here this cycle!
+ // NOT a copy, because we are supposed to drain active turfs each cycle anyway, so just replace with empty list.
+ // Blanking the public list means we actually are removing processed ones from the list! Maybe we could we use zones_for_update directly?
+ // But if we dom any zones added to zones_to_update DURING this step will get processed again during this step.
+ // I don't know if that actually happens? But if it does, it could lead to an infinate loop. Better preserve original semantics.
+ src.currentrun = air_master.zones_to_update
+ air_master.zones_to_update = list()
+
+ //cache for sanic speed (lists are references anyways)
+ var/list/currentrun = src.currentrun
+ while(currentrun.len)
+ var/zone/zone = currentrun[currentrun.len]
+ currentrun.len--
+ if(zone) // TODO - Do we need to check this? Old one didn't, but old one was single-threaded.
+ zone.tick()
+ zone.needs_update = 0
+ if(MC_TICK_CHECK)
+ return
+
+/datum/controller/subsystem/air/stat_entry(msg_prefix)
+ var/list/msg = list(msg_prefix)
+ msg += "S:[current_step ? part_names[current_step] : ""] "
+ msg += "C:{"
+ msg += "T [round(cost_turfs, 1)] | "
+ msg += "E [round(cost_edges, 1)] | "
+ msg += "F [round(cost_firezones, 1)] | "
+ msg += "H [round(cost_hotspots, 1)] | "
+ msg += "Z [round(cost_zones, 1)] "
+ msg += "}"
+ if(air_master)
+ msg += "T:[round((cost ? air_master.tiles_to_update.len/cost : 0), 0.1)]"
+ ..(msg.Join())
+ if(air_master)
+ air_master.stat_entry()
+
+
+// Since air_master is still a separate controller from SSAir (Wait, why is that again? Get on that...)
+// I want it showing up in the statpanel too. We'll just hack it in as a separate line for now.
+/datum/controller/air_system/stat_entry()
+ if(!statclick)
+ statclick = new/obj/effect/statclick/debug(null, "Initializing...", src)
+
+ var/title = " air_master"
+ var/list/msg = list()
+ msg += "Zones: [zones.len] "
+ msg += "Edges: [edges.len] "
+ msg += "Cycle: [current_cycle] {"
+ msg += "T [tiles_to_update.len] | "
+ msg += "E [active_edges.len] | "
+ msg += "F [active_fire_zones.len] | "
+ msg += "H [active_hotspots.len] | "
+ msg += "Z [zones_to_update.len] "
+ msg += "}"
+
+ stat(title, statclick.update(msg.Join()))
+
+// ZAS might displace objects as the map loads if an air tick is processed mid-load.
+/datum/controller/subsystem/air/StartLoadingMap(var/quiet = TRUE)
+ can_fire = FALSE
+ . = ..()
+
+/datum/controller/subsystem/air/StopLoadingMap(var/quiet = TRUE)
+ can_fire = TRUE
+ . = ..()
+
+// Reboot the air master. A bit hacky right now, but sometimes necessary still.
+// TODO - Make this better by SSair and air_master together, then just reboot SSair
+/datum/controller/subsystem/air/proc/RebootZAS()
+ can_fire = FALSE // Pause processing while we reboot
+ // If we should happen to be in the middle of processing... wait until that finishes.
+ if (state != SS_IDLE)
+ report_progress("ZAS Rebuild initiated. Waiting for current air tick to complete before continuing.")
+ while (state != SS_IDLE)
+ stoplag()
+
+ var/datum/controller/air_system/old_air = global.air_master
+ // Invalidate all zones
+ for(var/zone/zone in old_air.zones)
+ zone.c_invalidate()
+ // Destroy the air_master and create a new one.
+ qdel(old_air)
+ global.air_master = new
+ Initialize(REALTIMEOFDAY)
+
+ // Update next_fire so the MC doesn't try to make up for missed ticks.
+ next_fire = world.time + wait
+ can_fire = TRUE // Unpause
+
+#undef SSAIR_TURFS
+#undef SSAIR_EDGES
+#undef SSAIR_FIREZONES
+#undef SSAIR_HOTSPOTS
+#undef SSAIR_ZONES
+#undef SSAIR_DONE
diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm
index 1946a79730..c43a3d54f4 100644
--- a/code/modules/admin/admin_verbs.dm
+++ b/code/modules/admin/admin_verbs.dm
@@ -786,11 +786,11 @@ var/list/admin_verbs_event_manager = list(
set category = "Debug"
set name = "Kill Air"
set desc = "Toggle Air Processing"
- if(air_processing_killed)
- air_processing_killed = 0
+ if(!SSair.can_fire)
+ SSair.can_fire = TRUE
usr << "Enabled air processing."
else
- air_processing_killed = 1
+ SSair.can_fire = FALSE
usr << "Disabled air processing."
feedback_add_details("admin_verb","KA") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
log_admin("[key_name(usr)] used 'kill air'.")
diff --git a/code/modules/admin/verbs/grief_fixers.dm b/code/modules/admin/verbs/grief_fixers.dm
index dd28127e4a..7059fef907 100644
--- a/code/modules/admin/verbs/grief_fixers.dm
+++ b/code/modules/admin/verbs/grief_fixers.dm
@@ -45,10 +45,7 @@
usr << "\[4/5\] - All turfs reset to roundstart values."
- qdel(air_master)
- air_master = new
- air_master.Setup()
- spawn air_master.Start()
+ SSair.RebootZAS()
usr << "\[5/5\] - ZAS Rebooted"
world << "Atmosphere restart completed in [(world.timeofday - current_time)/10] seconds."
\ No newline at end of file
diff --git a/code/modules/admin/verbs/mapping.dm b/code/modules/admin/verbs/mapping.dm
index 09b7b7bf53..41b8a22f9b 100644
--- a/code/modules/admin/verbs/mapping.dm
+++ b/code/modules/admin/verbs/mapping.dm
@@ -274,14 +274,7 @@ var/list/debug_verbs = list (
set name = "Reboot ZAS"
if(alert("This will destroy and remake all zone geometry on the whole map.","Reboot ZAS","Reboot ZAS","Nevermind") == "Reboot ZAS")
- var/datum/controller/air_system/old_air = air_master
- for(var/zone/zone in old_air.zones)
- zone.c_invalidate()
- qdel(old_air)
- air_master = new
- air_master.Setup()
- spawn air_master.Start()
-
+ SSair.RebootZAS()
/client/proc/count_objects_on_z_level()
set category = "Mapping"
diff --git a/vorestation.dme b/vorestation.dme
index 0d77a1e700..3d26a9f182 100644
--- a/vorestation.dme
+++ b/vorestation.dme
@@ -59,6 +59,7 @@
#include "code\__defines\turfs.dm"
#include "code\__defines\unit_tests.dm"
#include "code\__defines\xenoarcheaology.dm"
+#include "code\__defines\ZAS.dm"
#include "code\_compatibility\509\_JSON.dm"
#include "code\_compatibility\509\JSON Reader.dm"
#include "code\_compatibility\509\JSON Writer.dm"
@@ -172,7 +173,6 @@
#include "code\controllers\verbs.dm"
#include "code\controllers\voting.dm"
#include "code\controllers\observer_listener\atom\observer.dm"
-#include "code\controllers\Processes\air.dm"
#include "code\controllers\Processes\alarm.dm"
#include "code\controllers\Processes\chemistry.dm"
#include "code\controllers\Processes\emergencyShuttle.dm"
@@ -192,6 +192,7 @@
#include "code\controllers\Processes\vote.dm"
#include "code\controllers\ProcessScheduler\core\process.dm"
#include "code\controllers\ProcessScheduler\core\processScheduler.dm"
+#include "code\controllers\subsystems\air.dm"
#include "code\controllers\subsystems\creation.dm"
#include "code\controllers\subsystems\garbage.dm"
#include "code\controllers\subsystems\lighting.dm"
@@ -2734,7 +2735,6 @@
#include "code\unit_tests\integrated_circuits\converter.dm"
#include "code\unit_tests\integrated_circuits\logic.dm"
#include "code\unit_tests\integrated_circuits\trig.dm"
-#include "code\ZAS\_docs.dm"
#include "code\ZAS\Airflow.dm"
#include "code\ZAS\Atom.dm"
#include "code\ZAS\Connection.dm"