diff --git a/baystation12.dme b/baystation12.dme
index 83a200fc15c..35fb099fd89 100644
--- a/baystation12.dme
+++ b/baystation12.dme
@@ -74,13 +74,11 @@
#include "code\ATMOSPHERICS\components\unary\unary_base.dm"
#include "code\ATMOSPHERICS\components\unary\vent_pump.dm"
#include "code\ATMOSPHERICS\components\unary\vent_scrubber.dm"
-#include "code\controllers\_DynamicAreaLighting_TG.dm"
#include "code\controllers\autotransfer.dm"
#include "code\controllers\configuration.dm"
#include "code\controllers\failsafe.dm"
#include "code\controllers\hooks-defs.dm"
#include "code\controllers\hooks.dm"
-#include "code\controllers\lighting_controller.dm"
#include "code\controllers\master_controller.dm"
#include "code\controllers\shuttle_controller.dm"
#include "code\controllers\verbs.dm"
@@ -421,6 +419,7 @@
#include "code\game\objects\closets\walllocker.dm"
#include "code\game\objects\effects\aliens.dm"
#include "code\game\objects\effects\bump_teleporter.dm"
+#include "code\game\objects\effects\chemsmoke.dm"
#include "code\game\objects\effects\effect_system.dm"
#include "code\game\objects\effects\gibs.dm"
#include "code\game\objects\effects\glowshroom.dm"
@@ -1284,6 +1283,12 @@
#include "code\WorkInProgress\explosion_particles.dm"
#include "code\WorkInProgress\periodic_news.dm"
#include "code\WorkInProgress\Apples\artifacts.dm"
+#include "code\WorkInProgress\Aryn\Lighting\Compatibility.dm"
+#include "code\WorkInProgress\Aryn\Lighting\Controller.dm"
+#include "code\WorkInProgress\Aryn\Lighting\Engine.dm"
+#include "code\WorkInProgress\Aryn\Lighting\Light.dm"
+#include "code\WorkInProgress\Aryn\Lighting\Lightpoint.dm"
+#include "code\WorkInProgress\Aryn\Lighting\Math.dm"
#include "code\WorkInProgress\Cael_Aislinn\Jungle\falsewall.dm"
#include "code\WorkInProgress\Cael_Aislinn\Jungle\jungle.dm"
#include "code\WorkInProgress\Cael_Aislinn\Jungle\jungle_animals.dm"
@@ -1325,17 +1330,22 @@
#include "code\WorkInProgress\Ported\policetape.dm"
#include "code\WorkInProgress\SkyMarshal\officer_stuff.dm"
#include "code\WorkInProgress\SkyMarshal\Ultralight_procs.dm"
+#include "code\WorkInProgress\Wrongnumber\weldbackpack.dm"
+#include "code\ZAS\_docs.dm"
+#include "code\ZAS\_gas_mixture.dm"
#include "code\ZAS\Airflow.dm"
+#include "code\ZAS\Atom.dm"
#include "code\ZAS\Connection.dm"
+#include "code\ZAS\ConnectionGroup.dm"
+#include "code\ZAS\ConnectionManager.dm"
+#include "code\ZAS\Controller.dm"
#include "code\ZAS\Debug.dm"
-#include "code\ZAS\FEA_gas_mixture.dm"
-#include "code\ZAS\FEA_system.dm"
+#include "code\ZAS\Diagnostic.dm"
#include "code\ZAS\Fire.dm"
-#include "code\ZAS\Functions.dm"
#include "code\ZAS\Plasma.dm"
+#include "code\ZAS\Turf.dm"
#include "code\ZAS\Variable Settings.dm"
-#include "code\ZAS\ZAS_Turfs.dm"
-#include "code\ZAS\ZAS_Zones.dm"
+#include "code\ZAS\Zone.dm"
#include "interface\interface.dm"
#include "interface\skin.dmf"
#include "maps\tgstation2.dmm"
diff --git a/baystation12.int b/baystation12.int
index b82874fded5..28f433f0ff9 100644
--- a/baystation12.int
+++ b/baystation12.int
@@ -3,4 +3,4 @@
MAP_ICON_TYPE: 0
AUTO_FILE_DIR: OFF
*/
-// END_INTERNALS
+// END_INTERNALS
\ No newline at end of file
diff --git a/code/WorkInProgress/Aryn/Lighting/Compatibility.dm b/code/WorkInProgress/Aryn/Lighting/Compatibility.dm
new file mode 100644
index 00000000000..9c98f5f6c9c
--- /dev/null
+++ b/code/WorkInProgress/Aryn/Lighting/Compatibility.dm
@@ -0,0 +1,45 @@
+area/var/lighting_use_dynamic
+
+turf/space/is_outside = 1
+turf/simulated/shuttle/is_outside = 1
+
+/datum/controller/lighting/var/processing = 1
+/datum/controller/lighting/var/iteration = 0
+
+//Because so many objects jump the gun.
+proc/lighting_ready()
+ return lighting_controller && lighting_controller.started
+
+turf_light_data
+ var/light_overlay
+ var/lightNW
+ var/lightSW
+ var/lightNE
+ var/lightSE
+ var/lit_by
+
+turf_light_data/proc/copy_from(turf/T)
+ light_overlay = T.light_overlay
+ lightNW = T.lightNW
+ lightSW = T.lightSW
+ lightNE = T.lightNE
+ lightSE = T.lightSE
+ lit_by = T.lit_by
+
+turf_light_data/proc/copy_to(turf/T)
+ T.light_overlay = light_overlay
+ T.lightNW = lightNW
+ T.lightSW = lightSW
+ T.lightNE = lightNE
+ T.lightSE = lightSE
+ T.lit_by = lit_by
+ //T.ResetValue()
+
+atom/proc/SetLuminosity(n)
+ n = min(n,10) //Caelcode.
+ if(n > 0)
+ SetLight(max(1,n>>1),n)
+ else
+ SetLight(0,0)
+ luminosity = n
+ //else lighting_controller.initial_lights.Add(src)
\ No newline at end of file
diff --git a/code/WorkInProgress/Aryn/Lighting/Controller.dm b/code/WorkInProgress/Aryn/Lighting/Controller.dm
new file mode 100644
index 00000000000..c5a0fee140e
--- /dev/null
+++ b/code/WorkInProgress/Aryn/Lighting/Controller.dm
@@ -0,0 +1,120 @@
+/*
+
+Overview:
+ Unlike the previous lighting controller, this is mostly here to hold global lighting procs and vars.
+ It does not process every tick, because not even DAL did something so stupid.
+
+Global Vars:
+ initial_lights - This holds all lights formed before the lighting controller started up. It becomes null on initialization.
+
+Class Vars:
+ starlight - The light value of space.
+
+ icon_updates - The list of turfs which need an update to their overlays.
+
+ light_border - Space turfs which are adjacent to non-space turfs.
+
+Class Procs:
+ Initialize()
+ Starts the lighting system, creating all light points and turf overlays.
+
+ StarLight(n)
+ Sets the light produced by space. If a solar eclipse suddenly happens, it'll probably lag.
+
+ MarkIconUpdate(turf/T)
+ Called when a turf needs an update to its light icon. Ensures that it only gets calculated once per turf.
+
+ FlushIconUpdates()
+ Called when a light is done marking icon updates. Updates every marked turf.
+
+ AddBorder(turf/T) & RemoveBorder(turf/T)
+ Called by turf/CheckForOpaqueObjects() to modify the light_border list.
+
+
+*/
+
+var/datum/controller/lighting/lighting_controller
+
+var/all_lightpoints_made = 0
+
+var/list/lit_z_levels = list(1,5)
+
+/datum/controller/lighting
+
+ var/starlight = 4
+ var/list/icon_updates = list()
+
+ var/list/light_border = list()
+
+ var/started = 0
+
+ //var/icon/border = icon('Icons/Test.dmi', "border")
+
+/datum/controller/lighting/New()
+ lighting_controller = src
+
+/datum/controller/lighting/proc/Initialize()
+
+ set background = 1
+
+ var/start_time = world.timeofday
+ world << "Processing lights..."
+
+ sleep(1)
+
+ var/turfs_updated = 0
+ var/total_turfs = world.maxx*world.maxy*lit_z_levels.len
+
+ for(var/z in lit_z_levels)
+ for(var/y = 0, y <= world.maxy, y++)
+ for(var/x = 0, x <= world.maxx, x++)
+ if(x > 0 && y > 0)
+
+ turfs_updated++
+ if((turfs_updated % (total_turfs>>2)) == 0)
+ sleep(1)
+ world << "Progress: [round((turfs_updated/total_turfs)*100, 0.01)]% ([turfs_updated]/[total_turfs])"
+
+ var/turf/T = locate(x,y,z)
+ if(!T.is_outside)
+ T.light_overlay = new(T)
+ //T.ResetValue()
+ if(!all_lightpoints_made) new/lightpoint(x+0.5,y+0.5,z)
+
+
+
+ //world << "[x],[y],[z]"
+
+ all_lightpoints_made = 1
+ started = 1
+
+ for(var/turf/T)
+ if(!T.is_outside && T.light_overlay)
+
+ T.ResetValue()
+ T.UpdateLight()
+
+ world << "Lighting initialization took [(world.timeofday-start_time)/world.fps] seconds."
+ world << "Updated [turfs_updated] turfs."
+
+/datum/controller/lighting/proc/MarkIconUpdate(turf/T)
+ if(!T.needs_light_update)
+ icon_updates.Add(T)
+ T.needs_light_update = 1
+
+/datum/controller/lighting/proc/FlushIconUpdates()
+ for(var/turf/T in icon_updates)
+ T.UpdateLight()
+ T.needs_light_update = 0
+ icon_updates = list()
+
+/datum/controller/lighting/proc/AddBorder(turf/T)
+ if(!T.is_border)
+ light_border.Add(T)
+ T.is_border = 1
+ //T.overlays.Add(border)
+
+/datum/controller/lighting/proc/RemoveBorder(turf/T)
+ if(T.is_border)
+ light_border.Remove(T)
+ T.is_border = 0
diff --git a/code/WorkInProgress/Aryn/Lighting/Engine.dm b/code/WorkInProgress/Aryn/Lighting/Engine.dm
new file mode 100644
index 00000000000..061918b4145
--- /dev/null
+++ b/code/WorkInProgress/Aryn/Lighting/Engine.dm
@@ -0,0 +1,227 @@
+/*
+
+Overview:
+ Procs given to atom and turf by the lighting engine, as well as the lighting overlay object.
+
+Atom Vars:
+ light - Contains the light object this atom is currently shining with.
+
+Turf Vars:
+ light_overlay - Contains an object showing the lighting icon over this turf.
+
+ lit_value - Stores how brightly lit the turf is.
+
+ has_opaque - A cached value updated by CheckForOpaqueObjects()
+
+ is_outside - Any turf with this set to true will be considered as bright as space.
+
+ needs_light_update - Turf is marked for icon updates when true.
+
+ lightNE, lightSE, lightNW, lightSW - Hold the lightpoints on the four corners of this turf. See Lightpoint.dm
+
+ lit_by - A list of lights that are lighting this turf.
+
+Atom Procs:
+ SetLight(intensity, radius)
+ A more versatile SetLuminosity() that allows independent control of intensity and radius.
+ Called behind the scenes of SetLuminosity().
+
+ SetOpacity(opacity)
+ Does the same thing as DAL.
+
+Turf Procs:
+ UpdateLight()
+ Called by the lighting controller. It is advisable not to call this manually due to the cost of lightpoint/max_value()
+
+ AddLight(light/light)
+ Called by light/Reset() to light this turf with a particular light.
+
+ RemoveLight(light/light)
+ Called by light/Off() to unlight turfs that were lit by it.
+
+ ResetValue()
+ Called when lights are reset or starlight is changed.
+
+ ResetCachedValues()
+ Resets cached values of all four light points. Called by ResetValue().
+
+ CheckForOpaqueObjects()
+ Called by lighting_controller.Initialize(), SetOpacity() or when a turf might change opacity.
+ Resets the opacity cache and looks for opaque objects. Also responsible for adding and removing borders to space.
+*/
+
+
+#define LIGHTCLAMP(x) ( max(0,min(3,round(x,1))) )
+
+atom/movable/lighting_overlay
+ name = ""
+ anchored = 1
+ layer = 9
+ mouse_opacity = 0
+ icon = 'icons/effects/ArynLights.dmi'
+ icon_state = "0000"
+ invisibility = INVISIBILITY_LIGHTING
+
+atom/var/light/light
+
+turf/var/atom/movable/lighting_overlay/light_overlay
+
+turf/var/lit_value = 0
+turf/var/max_brightness = 0
+turf/var/has_opaque = 0
+turf/var/is_outside = 0
+turf/var/is_border = 0
+turf/var/needs_light_update = 0
+
+turf/var/lightpoint/lightNE
+turf/var/lightpoint/lightNW
+turf/var/lightpoint/lightSE
+turf/var/lightpoint/lightSW
+turf/var/list/lit_by
+
+atom/movable/New()
+ . = ..()
+ if(luminosity)
+ if(!light)
+ SetLight(luminosity,luminosity)
+ else
+ light.Reset()
+ if(opacity)
+ if(lighting_ready())
+ opacity = 0
+ SetOpacity(1)
+
+atom/movable/Move()
+ var/o = opacity
+ if(o) SetOpacity(0)
+ . = ..()
+ if(.)
+ if(o) SetOpacity(1)
+ if(light)
+ light.Reset()
+ if(lighting_ready()) lighting_controller.FlushIconUpdates()
+
+atom/proc/SetLight(intensity, radius)
+ //if(lights_verbose) world << "SetLight([intensity],[radius])"
+ if(!intensity)
+ if(!light || !light.intensity)
+ //if(lights_verbose) world << "Still off."
+ return
+ //if(lights_verbose) world << "Shut off light with [light.lit_turfs.len] turfs lit."
+ light.Off()
+ light.intensity = 0
+ if(lighting_ready()) lighting_controller.FlushIconUpdates()
+ return
+ if(!light)
+ //if(lights_verbose) world << "New light."
+ light = new(src)
+ if(light.intensity == intensity)
+ //if(lights_verbose) world << "Same intensity."
+ return
+ light.radius = min(radius,15)
+ light.intensity = intensity
+ light.Reset()
+ if(lighting_ready()) lighting_controller.FlushIconUpdates()
+
+atom/proc/SetOpacity(o)
+ if(o == opacity) return
+ opacity = o
+ var/turf/T = loc
+ if(isturf(T))
+ for(var/light/A in T.lit_by)
+ A.Reset()
+ lighting_controller.FlushIconUpdates()
+
+turf/proc/UpdateLight()
+ if(light_overlay)
+ light_overlay.icon_state = "[lightSE.max_value()][lightSW.max_value()][lightNW.max_value()][lightNE.max_value()]"
+
+turf/proc/AddLight(light/light)
+ if(is_outside) return
+
+ var/brightness = light.CalculateBrightness(src)
+ if(brightness <= 0) return
+
+ if(!lit_by) lit_by = list()
+ lit_by.Add(light)
+
+
+ lit_by[light] = brightness
+
+ if(!has_opaque && lighting_ready())
+ if(brightness > max_brightness)
+ lit_value = LIGHTCLAMP(brightness)
+ max_brightness = brightness
+ ResetCachedValues()
+ for(var/turf/T in range(1,src))
+ lighting_controller.MarkIconUpdate(T)
+
+turf/proc/RemoveLight(light/light)
+ if(lit_by)
+ var/brightness = lit_by[light]
+ lit_by.Remove(light)
+ if(brightness == max_brightness)
+ ResetValue()
+ if(!lit_by.len) lit_by = null
+
+//Only called by ChangeTurf, because it really needs it.
+turf/proc/ResetAllLights()
+ for(var/light/light in lit_by)
+ light.Reset()
+
+/turf/space/ResetAllLights()
+ var/atom/movable/lighting_overlay/overlay = locate() in src
+ if (overlay)
+ overlay.loc = null
+ light_overlay = null
+ is_outside = 1
+ . = ..()
+
+turf/proc/ResetValue()
+ if(is_outside)
+ max_brightness = lighting_controller.starlight
+ lit_value = LIGHTCLAMP(lighting_controller.starlight)
+ return
+
+ CheckForOpaqueObjects()
+ if(has_opaque)
+ lit_value = 0
+ else
+ the_part_where_I_calculate_brightness()
+
+ if(lighting_ready())
+ the_part_where_I_use_range()
+
+turf/proc
+ the_part_where_I_calculate_brightness()
+ max_brightness = 0
+ for(var/light/light in lit_by)
+ var/brightness = lit_by[light]//light.CalculateBrightness(src)
+ if(brightness > max_brightness)
+ max_brightness = brightness
+ lit_value = LIGHTCLAMP(max_brightness)
+
+ the_part_where_I_use_range()
+ ResetCachedValues()
+ for(var/turf/T in range(1,src))
+ lighting_controller.MarkIconUpdate(T)
+
+turf/proc/ResetCachedValues()
+ if(lightNE)
+ lightNE.cached_value = -1
+ if(lightNW)
+ lightNW.cached_value = -1
+ if(lightSE)
+ lightSE.cached_value = -1
+ if(lightSW)
+ lightSW.cached_value = -1
+
+turf/proc/CheckForOpaqueObjects()
+ has_opaque = opacity
+ if(!opacity)
+ for(var/atom/movable/M in contents)
+ if(M.opacity)
+ has_opaque = 1
+ break
+
+#undef LIGHTCLAMP
\ No newline at end of file
diff --git a/code/WorkInProgress/Aryn/Lighting/Light.dm b/code/WorkInProgress/Aryn/Lighting/Light.dm
new file mode 100644
index 00000000000..e354154180d
--- /dev/null
+++ b/code/WorkInProgress/Aryn/Lighting/Light.dm
@@ -0,0 +1,75 @@
+/*
+
+Overview:
+ This object functions similarly to /tg/'s /light. It is responsible for calculating what turfs are lit by it.
+
+Class Vars:
+ radius - Set by atom/SetLight(). This stores how far out turfs will be lit up.
+ intensity - Set by atom/SetLight(). Stores the amount of light generated at the center.
+ lit_turfs - A list of turfs being lit by this light.
+ atom - The atom this light is attached to.
+
+Class Procs:
+ Reset()
+ This is called whenever the light changes, or the underlying atom changes position.
+
+ Off()
+ A quick way to turn off a light. Removes the light from all turfs in lit_turfs.
+
+ CalculateBrightness(turf/T)
+ Returns the brightness that should be displayed by this light on a specific turf.
+
+*/
+
+
+light/var/radius = 0
+light/var/intensity = 0
+light/var/list/lit_turfs
+light/var/atom/atom
+
+light/New(atom/atom)
+ src.atom = atom
+
+light/proc/Reset()
+ //if(atom.lights_verbose) world << "light.Reset()"
+ Off()
+ if(intensity > 0)
+ //if(atom.lights_verbose) world << "Restoring light."
+ for(var/turf/T in view(get_turf(atom),radius+1))
+ if(!T.is_outside)
+ T.AddLight(src)
+ lit_turfs.Add(T)
+ //if(atom.lights_verbose) world << "[lit_turfs.len] turfs added."
+
+light/proc/Off()
+ //if(atom.lights_verbose) world << "light.Off()"
+ for(var/turf/T in lit_turfs)
+ T.RemoveLight(src)
+ lit_turfs = list()
+
+light/proc/CalculateBrightness(turf/T)
+ if (!atom)
+ return 0
+ var/square = get_square_dist(atom.x,atom.y,atom.z,T.x,T.y,T.z)
+ if(square > (radius+2)*(radius+2)) return 0
+ //+2 offset gives an ambient light effect.
+
+ var/value = ((radius)/(2*fsqrt(square) + 1)) * intensity - 0.48
+ /*
+ lightRadius
+ ---------------- * lightValue - 0.48
+ 2 * distance + 1
+
+ The light decreases by twice the distance, starting from the radius.
+ The + 1 causes the graph to shift to the left one unit so that division by zero is prevented on the source tile.
+
+ This is then multiplied by the light value to give the final result.
+ The -0.48 offset causes the value to be near zero at the radius.
+
+ This gives a result which is likely close to the inverse-square law in two dimensions instead of three.
+ */
+
+
+ return max(min( value , intensity), 0) //Ensure the value never goes above the maximum light value or below zero.
+
+ //return cos(90 * sqrt(square) / max(1,lightRadius)) * lightValue
\ No newline at end of file
diff --git a/code/WorkInProgress/Aryn/Lighting/Lightpoint.dm b/code/WorkInProgress/Aryn/Lighting/Lightpoint.dm
new file mode 100644
index 00000000000..5b9aade35e6
--- /dev/null
+++ b/code/WorkInProgress/Aryn/Lighting/Lightpoint.dm
@@ -0,0 +1,65 @@
+/*
+
+Overview:
+ Perhaps the most cryptic datum in the lighting engine, there are four of these at the corners of every turf.
+ Any two turfs that share a corner will also have the same lightpoint. Because of the nature of the icons used,
+ light is shown at the corner of the turf rather than in the middle, necessitating some way to keep track of what
+ icon state to use.
+
+Class Vars:
+ x, y, z - The position of the lightpoint. x and y will usually be expressed in terms of 0.5 due to its location on the corner.
+
+ NE, NW, SE, SW - The turfs that are in these directions relative to the lightpoint.
+
+ cached_value - A cached value of max_value().
+
+Class Procs:
+ max_value()
+ The maximum of the light amounts on the four turfs of this light point.
+
+*/
+
+lightpoint
+ var/x
+ var/y
+ var/z
+
+ var/turf/NE
+ var/turf/NW
+ var/turf/SW
+ var/turf/SE
+
+ var/cached_value = -1
+
+ New(x,y,z)
+ var/turf/T = locate(x+0.5,y+0.5,z)
+ if(T)
+ NE = T
+ T.lightSW = src
+ T = locate(x-0.5,y+0.5,z)
+ if(T)
+ NW = T
+ T.lightSE = src
+ T = locate(x-0.5,y-0.5,z)
+ if(T)
+ SW = T
+ T.lightNE = src
+ T = locate(x+0.5,y-0.5,z)
+ if(T)
+ SE = T
+ T.lightNW = src
+
+ proc/max_value()
+ if(cached_value < 0)
+ var
+ valueA = value_of(NW)
+ valueB = value_of(NE)
+ valueC = value_of(SW)
+ valueD = value_of(SE)
+ cached_value = max(valueA,valueB,valueC,valueD)
+ return cached_value
+
+ proc/value_of(turf/T)
+ if(!T) return 0
+ if(T.is_outside) return min(lighting_controller.starlight,3)
+ return T.lit_value
\ No newline at end of file
diff --git a/code/WorkInProgress/Aryn/Lighting/Math.dm b/code/WorkInProgress/Aryn/Lighting/Math.dm
new file mode 100644
index 00000000000..cd8716373be
--- /dev/null
+++ b/code/WorkInProgress/Aryn/Lighting/Math.dm
@@ -0,0 +1,22 @@
+/*
+
+Some math procs used by lighting, including ul's fastroot.
+
+*/
+
+var/list/fastroot = list(0, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7)
+
+proc/get_square_dist(Ax,Ay,Az,Bx,By,Bz)
+ var/X = (Ax - Bx)
+ var/Y = (Ay - By)
+ var/Z = (Az - Bz)
+ return (X * X + Y * Y + Z * Z)
+
+proc/fsqrt(n)
+ if (n > fastroot.len)
+ //world << "Adding [n-fastroot.len] entries to root table."
+ for(var/i = fastroot.len, i <= n, i++)
+ fastroot += round(sqrt(i))
+ return fastroot[n + 1]
\ No newline at end of file
diff --git a/code/WorkInProgress/Aryn/Lighting/Verbs.dm b/code/WorkInProgress/Aryn/Lighting/Verbs.dm
new file mode 100644
index 00000000000..bf1b40f1ba1
--- /dev/null
+++ b/code/WorkInProgress/Aryn/Lighting/Verbs.dm
@@ -0,0 +1,33 @@
+var/icon/lighting_dbg = icon('icons/Testing/Zone.dmi', "created")
+
+atom/var/lights_verbose = 0
+
+obj/machinery/light/verb/ShowInfluence()
+ set src in world
+
+ lights_verbose = 1
+ usr << "[src]"
+ if(light)
+ usr << "Intensity: [light.intensity]"
+ usr << "Radius: [light.radius]"
+
+ for(var/turf/T in light.lit_turfs)
+ T.overlays += lighting_dbg
+ spawn(50)
+ for(var/turf/T in light.lit_turfs)
+ T.overlays -= lighting_dbg
+
+turf/verb/ShowData()
+ set src in world
+
+ usr << "[src]"
+ usr << "Lit Value: [lit_value]"
+ usr << "Max Brightness: [max_brightness]"
+
+ usr << "Lit By: "
+ for(var/light/light in lit_by)
+ usr << " - [light.atom] \[[lit_by[light]]\][(lit_by[light] == max_brightness ? "(MAX)" : "")]"
+ light.atom.overlays += lighting_dbg
+ spawn(50)
+ for(var/light/light in lit_by)
+ light.atom.overlays -= lighting_dbg
\ No newline at end of file
diff --git a/code/ZAS/Airflow.dm b/code/ZAS/Airflow.dm
index 894dc92c72b..896cda8440b 100644
--- a/code/ZAS/Airflow.dm
+++ b/code/ZAS/Airflow.dm
@@ -1,53 +1,8 @@
/*
-
-CONTAINS:
-All AirflowX() procs, all Variable Setting Controls for airflow, save/load variable tweaks for airflow.
-
-VARIABLES:
-
-atom/movable/airflow_dest
- The destination turf of a flying object.
-
-atom/movable/airflow_speed
- The speed (1-15) at which a flying object is traveling to airflow_dest. Decays over time.
-
-
-OVERLOADABLE PROCS:
-
-mob/airflow_stun()
- Contains checks for and results of being stunned by airflow.
- Called when airflow quantities exceed airflow_medium_pressure.
- RETURNS: Null
-
-atom/movable/check_airflow_movable(n)
- Contains checks for moving any object due to airflow.
- n is the pressure that is flowing.
- RETURNS: 1 if the object moves under the air conditions, 0 if it stays put.
-
-atom/movable/airflow_hit(atom/A)
- Contains results of hitting a solid object (A) due to airflow.
- A is the dense object hit.
- Use airflow_speed to determine how fast the projectile was going.
-
-
-AUTOMATIC PROCS:
-
-Airflow(zone/A, zone/B)
- Causes objects to fly along a pressure gradient.
- Called by zone updates. A and B are two connected zones.
-
-AirflowSpace(zone/A)
- Causes objects to fly into space.
- Called by zone updates. A is a zone connected to space.
-
-atom/movable/GotoAirflowDest(n)
-atom/movable/RepelAirflowDest(n)
- Called by main airflow procs to cause the object to fly to or away from destination at speed n.
- Probably shouldn't call this directly unless you know what you're
- doing and have set airflow_dest. airflow_hit() will be called if the object collides with an obstacle.
-
+Contains helper procs for airflow, handled in /connection_group.
*/
+
mob/var/tmp/last_airflow_stun = 0
mob/proc/airflow_stun()
if(stat == 2)
@@ -108,124 +63,6 @@ obj/item/check_airflow_movable(n)
if(4,5)
if(n < vsc.airflow_medium_pressure) return 0
-//The main airflow code. Called by zone updates.
-//Zones A and B are air zones. n represents the amount of air moved.
-
-proc/Airflow(zone/A, zone/B)
-
- var/n = B.air.return_pressure() - A.air.return_pressure()
-
- //Don't go any further if n is lower than the lowest value needed for airflow.
- if(abs(n) < vsc.airflow_lightest_pressure) return
-
- //These turfs are the midway point between A and B, and will be the destination point for thrown objects.
- var/list/connection/connections_A = A.connections
- var/list/turf/connected_turfs = list()
- for(var/connection/C in connections_A) //Grab the turf that is in the zone we are flowing to (determined by n)
- if( ( A == C.A.zone || A == C.zone_A ) && ( B == C.B.zone || B == C.zone_B ) )
- if(n < 0)
- connected_turfs |= C.B
- else
- connected_turfs |= C.A
- else if( ( A == C.B.zone || A == C.zone_B ) && ( B == C.A.zone || B == C.zone_A ) )
- if(n < 0)
- connected_turfs |= C.A
- else
- connected_turfs |= C.B
-
- //Get lists of things that can be thrown across the room for each zone (assumes air is moving from zone B to zone A)
- var/list/air_sucked = B.movables()
- var/list/air_repelled = A.movables()
- if(n < 0)
- //air is moving from zone A to zone B
- var/list/temporary_pplz = air_sucked
- air_sucked = air_repelled
- air_repelled = temporary_pplz
-
- for(var/atom/movable/M in air_sucked)
-
- if(M.last_airflow > world.time - vsc.airflow_delay) continue
-
- //Check for knocking people over
- if(ismob(M) && n > vsc.airflow_stun_pressure)
- if(M:status_flags & GODMODE) continue
- M:airflow_stun()
-
- if(M.check_airflow_movable(n))
-
- //Check for things that are in range of the midpoint turfs.
- var/list/close_turfs = list()
- for(var/turf/U in connected_turfs)
- if(M in range(U)) close_turfs += U
- if(!close_turfs.len) continue
-
- //If they're already being tossed, don't do it again.
- if(!M.airflow_speed)
-
- M.airflow_dest = pick(close_turfs) //Pick a random midpoint to fly towards.
-
- spawn M.GotoAirflowDest(abs(n)/5)
-
- //Do it again for the stuff in the other zone, making it fly away.
- for(var/atom/movable/M in air_repelled)
-
- if(M.last_airflow > world.time - vsc.airflow_delay) continue
-
- if(ismob(M) && abs(n) > vsc.airflow_medium_pressure)
- if(M:status_flags & GODMODE) continue
- M:airflow_stun()
-
- if(M.check_airflow_movable(abs(n)))
-
- var/list/close_turfs = list()
- for(var/turf/U in connected_turfs)
- if(M in range(U)) close_turfs += U
- if(!close_turfs.len) continue
-
- //If they're already being tossed, don't do it again.
- if(!M.airflow_speed)
-
- M.airflow_dest = pick(close_turfs) //Pick a random midpoint to fly towards.
-
- spawn M.RepelAirflowDest(abs(n)/5)
-
-proc/AirflowSpace(zone/A)
-
- //The space version of the Airflow(A,B,n) proc.
-
- var/n = A.air.return_pressure()
- //Here, n is determined by only the pressure in the room.
-
- if(n < vsc.airflow_lightest_pressure) return
-
- var/list/connected_turfs = A.unsimulated_tiles //The midpoints are now all the space connections.
- var/list/pplz = A.movables() //We only need to worry about things in the zone, not things in space.
-
- for(var/atom/movable/M in pplz)
-
- if(M.last_airflow > world.time - vsc.airflow_delay) continue
-
- if(ismob(M) && n > vsc.airflow_stun_pressure)
- var/mob/O = M
- if(O.status_flags & GODMODE) continue
- O.airflow_stun()
-
- if(M.check_airflow_movable(n))
-
- var/list/close_turfs = list()
- for(var/turf/U in connected_turfs)
- if(M in range(U)) close_turfs += U
- if(!close_turfs.len) continue
-
- //If they're already being tossed, don't do it again.
- if(!M.airflow_speed)
-
- M.airflow_dest = pick(close_turfs) //Pick a random midpoint to fly towards.
- spawn
- if(M) M.GotoAirflowDest(n/10)
- //Sometimes shit breaks, and M isn't there after the spawn.
-
-
/atom/movable/var/tmp/turf/airflow_dest
/atom/movable/var/tmp/airflow_speed = 0
/atom/movable/var/tmp/airflow_time = 0
@@ -416,4 +253,4 @@ zone/proc/movables()
for(var/atom/A in T)
if(istype(A, /obj/effect) || istype(A, /mob/aiEye))
continue
- . += A
+ . += A
\ No newline at end of file
diff --git a/code/ZAS/Atom.dm b/code/ZAS/Atom.dm
new file mode 100644
index 00000000000..b4d1de20af4
--- /dev/null
+++ b/code/ZAS/Atom.dm
@@ -0,0 +1,66 @@
+
+
+/atom/var/pressure_resistance = ONE_ATMOSPHERE
+
+atom/proc/CanPass(atom/movable/mover, turf/target, height=1.5, air_group = 0)
+ //Purpose: Determines if the object (or airflow) can pass this atom.
+ //Called by: Movement, airflow.
+ //Inputs: The moving atom (optional), target turf, "height" and air group
+ //Outputs: Boolean if can pass.
+
+ return (!density || !height || air_group)
+
+/turf/CanPass(atom/movable/mover, turf/target, height=1.5,air_group=0)
+ if(!target) return 0
+
+ if(istype(mover)) // turf/Enter(...) will perform more advanced checks
+ return !density
+
+ else // Now, doing more detailed checks for air movement and air group formation
+ if(target.blocks_air||blocks_air)
+ return 0
+
+ for(var/obj/obstacle in src)
+ if(!obstacle.CanPass(mover, target, height, air_group))
+ return 0
+ if(target != src)
+ for(var/obj/obstacle in target)
+ if(!obstacle.CanPass(mover, src, height, air_group))
+ return 0
+
+ return 1
+
+//Basically another way of calling CanPass(null, other, 0, 0) and CanPass(null, other, 1.5, 1).
+//Returns:
+// 0 - Not blocked
+// AIR_BLOCKED - Blocked
+// ZONE_BLOCKED - Not blocked, but zone boundaries will not cross.
+// BLOCKED - Blocked, zone boundaries will not cross even if opened.
+atom/proc/c_airblock(turf/other)
+ #ifdef ZASDBG
+ ASSERT(isturf(other))
+ #endif
+ return !CanPass(null, other, 0, 0) + 2*!CanPass(null, other, 1.5, 1)
+
+
+turf/c_airblock(turf/other)
+ #ifdef ZASDBG
+ ASSERT(isturf(other))
+ #endif
+ if(blocks_air)
+ return BLOCKED
+
+ //Z-level handling code. Always block if there isn't an open space.
+ #ifdef ZLEVELS
+ if(other.z != src.z)
+ if(other.z < src.z)
+ if(!istype(src, /turf/simulated/floor/open)) return BLOCKED
+ else
+ if(!istype(other, /turf/simulated/floor/open)) return BLOCKED
+ #endif
+
+ var/result = 0
+ for(var/atom/movable/M in contents)
+ result |= M.c_airblock(other)
+ if(result == BLOCKED) return BLOCKED
+ return result
\ No newline at end of file
diff --git a/code/ZAS/Connection.dm b/code/ZAS/Connection.dm
index 1bb8f9ca54d..b9cf2278a18 100644
--- a/code/ZAS/Connection.dm
+++ b/code/ZAS/Connection.dm
@@ -1,448 +1,165 @@
+#define CONNECTION_DIRECT 2
+#define CONNECTION_SPACE 4
+#define CONNECTION_INVALID 8
+
/*
-This object is contained within zone/var/connections. It's generated whenever two turfs from different zones are linked.
-Indirect connections will not merge the two zones after they reach equilibrium.
+
+Overview:
+ Connections are made between turfs by air_master.connect(). They represent a single point where two zones converge.
+
+Class Vars:
+ A - Always a simulated turf.
+ B - A simulated or unsimulated turf.
+
+ zoneA - The archived zone of A. Used to check that the zone hasn't changed.
+ zoneB - The archived zone of B. May be null in case of unsimulated connections.
+
+ edge - Stores the edge this connection is in. Can reference an edge that is no longer processed
+ after this connection is removed, so make sure to check edge.coefficient > 0 before re-adding it.
+
+Class Procs:
+
+ mark_direct()
+ Marks this connection as direct. Does not update the edge.
+ Called when the connection is made and there are no doors between A and B.
+ Also called by update() as a correction.
+
+ mark_indirect()
+ Unmarks this connection as direct. Does not update the edge.
+ Called by update() as a correction.
+
+ mark_space()
+ Marks this connection as unsimulated. Updating the connection will check the validity of this.
+ Called when the connection is made.
+ This will not be called as a correction, any connections failing a check against this mark are erased and rebuilt.
+
+ direct()
+ Returns 1 if no doors are in between A and B.
+
+ valid()
+ Returns 1 if the connection has not been erased.
+
+ erase()
+ Called by update() and connection_manager/erase_all().
+ Marks the connection as erased and removes it from its edge.
+
+ update()
+ Called by connection_manager/update_all().
+ Makes numerous checks to decide whether the connection is still valid. Erases it automatically if not.
+
*/
-#define CONNECTION_DIRECT 2
-#define CONNECTION_INDIRECT 1
-#define CONNECTION_CLOSED 0
-/connection
- var/turf/simulated/A
- var/turf/simulated/B
+/connection/var/turf/simulated/A
+/connection/var/turf/simulated/B
+/connection/var/zone/zoneA
+/connection/var/zone/zoneB
- var/zone/zone_A
- var/zone/zone_B
+/connection/var/connection_edge/edge
- var/indirect = CONNECTION_DIRECT //If the connection is purely indirect, the zones should not join.
-
-
-/connection/New(turf/T,turf/O)
- . = ..()
-
- A = T
- B = O
-
- if(A.zone && B.zone)
- if(!A.zone.connections)
- A.zone.connections = list()
- A.zone.connections += src
- zone_A = A.zone
-
- if(!B.zone.connections)
- B.zone.connections = list()
- B.zone.connections += src
- zone_B = B.zone
-
- if(A in air_master.turfs_with_connections)
- var/list/connections = air_master.turfs_with_connections[A]
- connections.Add(src)
- else
- air_master.turfs_with_connections[A] = list(src)
-
- if(B in air_master.turfs_with_connections)
- var/list/connections = air_master.turfs_with_connections[B]
- connections.Add(src)
- else
- air_master.turfs_with_connections[B] = list(src)
-
- if(A.CanPass(null, B, 0, 0))
-
- if(!A.CanPass(null, B, 1.5, 1))
- indirect = CONNECTION_INDIRECT
-
- ConnectZones(A.zone, B.zone, indirect)
-
- else
- ConnectZones(A.zone, B.zone)
- indirect = CONNECTION_CLOSED
+/connection/var/state = 0
+/connection/New(turf/simulated/A, turf/simulated/B)
+ #ifdef ZASDBG
+ ASSERT(air_master.has_valid_zone(A))
+ //ASSERT(air_master.has_valid_zone(B))
+ #endif
+ src.A = A
+ src.B = B
+ zoneA = A.zone
+ if(!istype(B))
+ mark_space()
+ edge = air_master.get_edge(A.zone,B)
+ edge.add_connection(src)
else
- world.log << "Attempted to create connection object for non-zone tiles: [T] ([T.x],[T.y],[T.z]) -> [O] ([O.x],[O.y],[O.z])"
- SoftDelete()
+ zoneB = B.zone
+ edge = air_master.get_edge(A.zone,B.zone)
+ edge.add_connection(src)
+/connection/proc/mark_direct()
+ state |= CONNECTION_DIRECT
+ //world << "Marked direct."
-/connection/Del()
- //remove connections from master lists.
- if(B in air_master.turfs_with_connections)
- var/list/connections = air_master.turfs_with_connections[B]
- connections.Remove(src)
+/connection/proc/mark_indirect()
+ state &= ~CONNECTION_DIRECT
+ //world << "Marked indirect."
- if(A in air_master.turfs_with_connections)
- var/list/connections = air_master.turfs_with_connections[A]
- connections.Remove(src)
+/connection/proc/mark_space()
+ state |= CONNECTION_SPACE
- //Remove connection from zones.
- if(A)
- if(A.zone && A.zone.connections)
- A.zone.connections.Remove(src)
- if(!A.zone.connections.len)
- A.zone.connections = null
+/connection/proc/direct()
+ return (state & CONNECTION_DIRECT)
- if(istype(zone_A) && (!A || A.zone != zone_A))
- if(zone_A.connections)
- zone_A.connections.Remove(src)
- if(!zone_A.connections.len)
- zone_A.connections = null
+/connection/proc/valid()
+ return !(state & CONNECTION_INVALID)
- if(B)
- if(B.zone && B.zone.connections)
- B.zone.connections.Remove(src)
- if(!B.zone.connections.len)
- B.zone.connections = null
+/connection/proc/erase()
+ edge.remove_connection(src)
+ state |= CONNECTION_INVALID
+ //world << "Connection Erased: [state]"
- if(istype(zone_B) && (!B || B.zone != zone_B))
- if(zone_B.connections)
- zone_B.connections.Remove(src)
- if(!zone_B.connections.len)
- zone_B.connections = null
-
- //Disconnect zones while handling unusual conditions.
- // e.g. loss of a zone on a turf
- DisconnectZones(zone_A, zone_B)
-
- //Finally, preform actual deletion.
- . = ..()
-
-
-/connection/proc/SoftDelete()
- //remove connections from master lists.
- if(B in air_master.turfs_with_connections)
- var/list/connections = air_master.turfs_with_connections[B]
- connections.Remove(src)
-
- if(A in air_master.turfs_with_connections)
- var/list/connections = air_master.turfs_with_connections[A]
- connections.Remove(src)
-
- //Remove connection from zones.
- if(A)
- if(A.zone && A.zone.connections)
- A.zone.connections.Remove(src)
- if(!A.zone.connections.len)
- A.zone.connections = null
-
- if(istype(zone_A) && (!A || A.zone != zone_A))
- if(zone_A.connections)
- zone_A.connections.Remove(src)
- if(!zone_A.connections.len)
- zone_A.connections = null
-
- if(B)
- if(B.zone && B.zone.connections)
- B.zone.connections.Remove(src)
- if(!B.zone.connections.len)
- B.zone.connections = null
-
- if(istype(zone_B) && (!B || B.zone != zone_B))
- if(zone_B.connections)
- zone_B.connections.Remove(src)
- if(!zone_B.connections.len)
- zone_B.connections = null
-
- //Disconnect zones while handling unusual conditions.
- // e.g. loss of a zone on a turf
- DisconnectZones(zone_A, zone_B)
-
-
-/connection/proc/ConnectZones(var/zone/zone_1, var/zone/zone_2, open = 0)
-
- //Sanity checking
- if(!istype(zone_1) || !istype(zone_2))
+/connection/proc/update()
+ //world << "Updated, \..."
+ if(!istype(A,/turf/simulated))
+ //world << "Invalid A."
+ erase()
return
- //Handle zones connecting indirectly/directly.
- if(open)
-
- //Create the lists if necessary.
- if(!zone_1.connected_zones)
- zone_1.connected_zones = list()
-
- if(!zone_2.connected_zones)
- zone_2.connected_zones = list()
-
- //Increase the number of connections between zones.
- if(zone_2 in zone_1.connected_zones)
- zone_1.connected_zones[zone_2]++
- else
- zone_1.connected_zones += zone_2
- zone_1.connected_zones[zone_2] = 1
-
- if(zone_1 in zone_2.connected_zones)
- zone_2.connected_zones[zone_1]++
- else
- zone_2.connected_zones += zone_1
- zone_2.connected_zones[zone_1] = 1
-
- if(open == CONNECTION_DIRECT)
- if(!zone_1.direct_connections)
- zone_1.direct_connections = list(src)
- else
- zone_1.direct_connections += src
-
- if(!zone_2.direct_connections)
- zone_2.direct_connections = list(src)
- else
- zone_2.direct_connections += src
-
-
- //Handle closed connections.
- else
-
- //Create the lists
- if(!zone_1.closed_connection_zones)
- zone_1.closed_connection_zones = list()
-
- if(!zone_2.closed_connection_zones)
- zone_2.closed_connection_zones = list()
-
- //Increment the connections.
- if(zone_2 in zone_1.closed_connection_zones)
- zone_1.closed_connection_zones[zone_2]++
- else
- zone_1.closed_connection_zones += zone_2
- zone_1.closed_connection_zones[zone_2] = 1
-
- if(zone_1 in zone_2.closed_connection_zones)
- zone_2.closed_connection_zones[zone_1]++
- else
- zone_2.closed_connection_zones += zone_1
- zone_2.closed_connection_zones[zone_1] = 1
-
- if(zone_1.status == ZONE_SLEEPING)
- zone_1.SetStatus(ZONE_ACTIVE)
-
- if(zone_2.status == ZONE_SLEEPING)
- zone_2.SetStatus(ZONE_ACTIVE)
-
-/connection/proc/DisconnectZones(var/zone/zone_1, var/zone/zone_2)
- //Sanity checking
- if(!istype(zone_1) || !istype(zone_2))
+ var/block_status = air_master.air_blocked(A,B)
+ if(block_status & AIR_BLOCKED)
+ //world << "Blocked connection."
+ erase()
return
-
- if(indirect != CONNECTION_CLOSED)
- //Handle disconnection of indirectly or directly connected zones.
- if( (zone_1 in zone_2.connected_zones) || (zone_2 in zone_1.connected_zones) )
-
- //If there are more than one connection, decrement the number of connections
- //Otherwise, remove all connections between the zones.
- if(zone_2 in zone_1.connected_zones)
- if(zone_1.connected_zones[zone_2] > 1)
- zone_1.connected_zones[zone_2]--
- else
- zone_1.connected_zones -= zone_2
- //remove the list if it is empty
- if(!zone_1.connected_zones.len)
- zone_1.connected_zones = null
-
- //Then do the same for the other zone.
- if(zone_1 in zone_2.connected_zones)
- if(zone_2.connected_zones[zone_1] > 1)
- zone_2.connected_zones[zone_1]--
- else
- zone_2.connected_zones -= zone_1
- if(!zone_2.connected_zones.len)
- zone_2.connected_zones = null
-
- if(indirect == CONNECTION_DIRECT)
- zone_1.direct_connections -= src
- if(!zone_1.direct_connections.len)
- zone_1.direct_connections = null
-
- zone_2.direct_connections -= src
- if(!zone_2.direct_connections.len)
- zone_2.direct_connections = null
-
- else
- //Handle disconnection of closed zones.
- if( (zone_1 in zone_2.closed_connection_zones) || (zone_2 in zone_1.closed_connection_zones) )
-
- //If there are more than one connection, decrement the number of connections
- //Otherwise, remove all connections between the zones.
- if(zone_2 in zone_1.closed_connection_zones)
- if(zone_1.closed_connection_zones[zone_2] > 1)
- zone_1.closed_connection_zones[zone_2]--
- else
- zone_1.closed_connection_zones -= zone_2
- //remove the list if it is empty
- if(!zone_1.closed_connection_zones.len)
- zone_1.closed_connection_zones = null
-
- //Then do the same for the other zone.
- if(zone_1 in zone_2.closed_connection_zones)
- if(zone_2.closed_connection_zones[zone_1] > 1)
- zone_2.closed_connection_zones[zone_1]--
- else
- zone_2.closed_connection_zones -= zone_1
- if(!zone_2.closed_connection_zones.len)
- zone_2.closed_connection_zones = null
-
-
-/connection/proc/Cleanup()
-
- //Check sanity: existance of turfs
- if(!A || !B)
- SoftDelete()
- return
-
- //Check sanity: loss of zone
- if(!A.zone || !B.zone)
- SoftDelete()
- return
-
- //Check sanity: zones are different
- if(A.zone == B.zone)
- SoftDelete()
- return
-
- //Handle zones changing on a turf.
- if((A.zone && A.zone != zone_A) || (B.zone && B.zone != zone_B))
- Sanitize()
-
- if(A.zone && B.zone)
-
- //If no walls are blocking us...
- if(A.ZAirPass(B))
- //...we check to see if there is a door in the way...
- var/door_pass = A.CanPass(null,B,1.5,1)
- //...and if it is opened.
- if(door_pass || A.CanPass(null,B,0,0))
-
- //Make and remove connections to let air pass.
- if(indirect == CONNECTION_CLOSED)
- DisconnectZones(A.zone, B.zone)
- ConnectZones(A.zone, B.zone, door_pass + 1)
-
- if(door_pass)
- indirect = CONNECTION_DIRECT
- else if(!door_pass)
- indirect = CONNECTION_INDIRECT
-
- //The door is instead closed.
- else if(indirect > CONNECTION_CLOSED)
- DisconnectZones(A.zone, B.zone)
- indirect = CONNECTION_CLOSED
- ConnectZones(A.zone, B.zone)
-
- //If I can no longer pass air, better delete
+ else if(block_status & ZONE_BLOCKED)
+ if(direct())
+ mark_indirect()
else
- SoftDelete()
+ mark_direct()
+
+ var/b_is_space = !istype(B,/turf/simulated)
+
+ if(state & CONNECTION_SPACE)
+ if(!b_is_space)
+ //world << "Invalid B."
+ erase()
return
-
-/connection/proc/Sanitize()
- //If the zones change on connected turfs, update it.
-
- //Both zones changed (wat)
- if(A.zone && A.zone != zone_A && B.zone && B.zone != zone_B)
-
- //If the zones have gotten swapped
- // (do not ask me how, I am just being anal retentive about sanity)
- if(A.zone == zone_B && B.zone == zone_A)
- var/turf/temp = B
- B = A
- A = temp
- zone_B = B.zone
- zone_A = A.zone
- return
-
- //Handle removal of connections from archived zones.
- if(zone_A && zone_A.connections)
- zone_A.connections.Remove(src)
- if(!zone_A.connections.len)
- zone_A.connections = null
-
- if(zone_B && zone_B.connections)
- zone_B.connections.Remove(src)
- if(!zone_B.connections.len)
- zone_B.connections = null
-
- if(A.zone)
- if(!A.zone.connections)
- A.zone.connections = list()
- A.zone.connections |= src
-
- if(B.zone)
- if(!B.zone.connections)
- B.zone.connections = list()
- B.zone.connections |= src
-
- //If either zone is null, we disconnect the archived ones after cleaning up the connections.
- if(!A.zone || !B.zone)
- if(zone_A && zone_B)
- DisconnectZones(zone_B, zone_A)
-
+ if(A.zone != zoneA)
+ //world << "Zone changed, \..."
if(!A.zone)
- zone_A = A.zone
+ erase()
+ //world << "erased."
+ return
+ else
+ edge.remove_connection(src)
+ edge = air_master.get_edge(A.zone, B)
+ edge.add_connection(src)
+ zoneA = A.zone
- if(!B.zone)
- zone_B = B.zone
+ //world << "valid."
+ return
+
+ else if(b_is_space)
+ //world << "Invalid B."
+ erase()
+ return
+
+ if(A.zone == B.zone)
+ //world << "A == B"
+ erase()
+ return
+
+ if(A.zone != zoneA || (zoneB && (B.zone != zoneB)))
+
+ //world << "Zones changed, \..."
+ if(A.zone && B.zone)
+ edge.remove_connection(src)
+ edge = air_master.get_edge(A.zone, B.zone)
+ edge.add_connection(src)
+ zoneA = A.zone
+ zoneB = B.zone
+ else
+ //world << "erased."
+ erase()
return
- //Handle diconnection and reconnection of zones.
- if(zone_A && zone_B)
- DisconnectZones(zone_A, zone_B)
- ConnectZones(A.zone, B.zone, indirect)
- //resetting values of archived values.
- zone_B = B.zone
- zone_A = A.zone
-
- //The "A" zone changed.
- else if(A.zone && A.zone != zone_A)
-
- //Handle connection cleanup
- if(zone_A)
- if(zone_A.connections)
- zone_A.connections.Remove(src)
- if(!zone_A.connections.len)
- zone_A.connections = null
-
- if(A.zone)
- if(!A.zone.connections)
- A.zone.connections = list()
- A.zone.connections |= src
-
- //If the "A" zone is null, we disconnect the archived ones after cleaning up the connections.
- if(!A.zone)
- if(zone_A && zone_B)
- DisconnectZones(zone_A, zone_B)
- zone_A = A.zone
- return
-
- //Handle diconnection and reconnection of zones.
- if(zone_A && zone_B)
- DisconnectZones(zone_A, zone_B)
- ConnectZones(A.zone, B.zone, indirect)
- zone_A = A.zone
-
- //The "B" zone changed.
- else if(B.zone && B.zone != zone_B)
-
- //Handle connection cleanup
- if(zone_B)
- if(zone_B.connections)
- zone_B.connections.Remove(src)
- if(!zone_B.connections.len)
- zone_B.connections = null
-
- if(B.zone)
- if(!B.zone.connections)
- B.zone.connections = list()
- B.zone.connections |= src
-
- //If the "B" zone is null, we disconnect the archived ones after cleaning up the connections.
- if(!B.zone)
- if(zone_A && zone_B)
- DisconnectZones(zone_A, zone_B)
- zone_B = B.zone
- return
-
- //Handle diconnection and reconnection of zones.
- if(zone_A && zone_B)
- DisconnectZones(zone_A, zone_B)
- ConnectZones(A.zone, B.zone, indirect)
- zone_B = B.zone
-
-
-#undef CONNECTION_DIRECT
-#undef CONNECTION_INDIRECT
-#undef CONNECTION_CLOSED
+ //world << "valid."
\ No newline at end of file
diff --git a/code/ZAS/ConnectionGroup.dm b/code/ZAS/ConnectionGroup.dm
new file mode 100644
index 00000000000..c31dbf9a034
--- /dev/null
+++ b/code/ZAS/ConnectionGroup.dm
@@ -0,0 +1,427 @@
+/*
+
+Overview:
+ These are what handle gas transfers between zones and into space.
+ They are found in a zone's edges list and in air_master.edges.
+ Each edge updates every air tick due to their role in gas transfer.
+ They come in two flavors, /connection_edge/zone and /connection_edge/unsimulated.
+ As the type names might suggest, they handle inter-zone and spacelike connections respectively.
+
+Class Vars:
+
+ A - This always holds a zone. In unsimulated edges, it holds the only zone.
+
+ connecting_turfs - This holds a list of connected turfs, mainly for the sake of airflow.
+
+ coefficent - This is a marker for how many connections are on this edge. Used to determine the ratio of flow.
+
+ connection_edge/zone
+
+ B - This holds the second zone with which the first zone equalizes.
+
+ direct - This counts the number of direct (i.e. with no doors) connections on this edge.
+ Any value of this is sufficient to make the zones mergeable.
+
+ connection_edge/unsimulated
+
+ B - This holds an unsimulated turf which has the gas values this edge is mimicing.
+
+ air - Retrieved from B on creation and used as an argument for the legacy ShareSpace() proc.
+
+Class Procs:
+
+ add_connection(connection/c)
+ Adds a connection to this edge. Usually increments the coefficient and adds a turf to connecting_turfs.
+
+ remove_connection(connection/c)
+ Removes a connection from this edge. This works even if c is not in the edge, so be careful.
+ If the coefficient reaches zero as a result, the edge is erased.
+
+ contains_zone(zone/Z)
+ Returns true if either A or B is equal to Z. Unsimulated connections return true only on A.
+
+ erase()
+ Removes this connection from processing and zone edge lists.
+
+ tick()
+ Called every air tick on edges in the processing list. Equalizes gas.
+
+ flow(list/movable, differential, repelled)
+ Airflow proc causing all objects in movable to be checked against a pressure differential.
+ If repelled is true, the objects move away from any turf in connecting_turfs, otherwise they approach.
+ A check against vsc.lightest_airflow_pressure should generally be performed before calling this.
+
+ get_connected_zone(zone/from)
+ Helper proc that allows getting the other zone of an edge given one of them.
+ Only on /connection_edge/zone, otherwise use A.
+
+*/
+
+
+/connection_edge/var/zone/A
+
+/connection_edge/var/list/connecting_turfs = list()
+
+/connection_edge/var/coefficient = 0
+
+/connection_edge/New()
+ CRASH("Cannot make connection edge without specifications.")
+
+/connection_edge/proc/add_connection(connection/c)
+ coefficient++
+ //world << "Connection added: [type] Coefficient: [coefficient]"
+
+/connection_edge/proc/remove_connection(connection/c)
+ //world << "Connection removed: [type] Coefficient: [coefficient-1]"
+ coefficient--
+ if(coefficient <= 0)
+ erase()
+
+/connection_edge/proc/contains_zone(zone/Z)
+
+/connection_edge/proc/erase()
+ air_master.remove_edge(src)
+ //world << "[type] Erased."
+
+/connection_edge/proc/tick()
+
+/connection_edge/proc/flow(list/movable, differential, repelled)
+ for(var/atom/movable/M in movable)
+
+ //If they're already being tossed, don't do it again.
+ if(M.last_airflow > world.time - vsc.airflow_delay) continue
+ if(M.airflow_speed) continue
+
+ //Check for knocking people over
+ if(ismob(M) && differential > vsc.airflow_stun_pressure)
+ if(M:status_flags & GODMODE) continue
+ M:airflow_stun()
+
+ if(M.check_airflow_movable(differential))
+ //Check for things that are in range of the midpoint turfs.
+ var/list/close_turfs = list()
+ for(var/turf/U in connecting_turfs)
+ if(get_dist(M,U) < world.view) close_turfs += U
+ if(!close_turfs.len) continue
+
+ M.airflow_dest = pick(close_turfs) //Pick a random midpoint to fly towards.
+
+ if(repelled) spawn if(M) M.RepelAirflowDest(differential/5)
+ else spawn if(M) M.GotoAirflowDest(differential/10)
+
+
+
+
+/connection_edge/zone/var/zone/B
+/connection_edge/zone/var/direct = 0
+
+/connection_edge/zone/New(zone/A, zone/B)
+
+ src.A = A
+ src.B = B
+ A.edges.Add(src)
+ B.edges.Add(src)
+ //id = edge_id(A,B)
+ //world << "New edge between [A] and [B]"
+
+/connection_edge/zone/add_connection(connection/c)
+ . = ..()
+ connecting_turfs.Add(c.A)
+ if(c.direct()) direct++
+
+/connection_edge/zone/remove_connection(connection/c)
+ connecting_turfs.Remove(c.A)
+ if(c.direct()) direct--
+ . = ..()
+
+/connection_edge/zone/contains_zone(zone/Z)
+ return A == Z || B == Z
+
+/connection_edge/zone/erase()
+ A.edges.Remove(src)
+ B.edges.Remove(src)
+ . = ..()
+
+/connection_edge/zone/tick()
+ if(A.invalid || B.invalid)
+ erase()
+ return
+ //world << "[id]: Tick [air_master.current_cycle]: \..."
+ if(direct)
+ if(air_master.equivalent_pressure(A, B))
+ //world << "merged."
+ erase()
+ air_master.merge(A, B)
+ //world << "zones merged."
+ return
+
+ //air_master.equalize(A, B)
+ ShareRatio(A.air,B.air,coefficient)
+ air_master.mark_zone_update(A)
+ air_master.mark_zone_update(B)
+ //world << "equalized."
+
+ var/differential = A.air.return_pressure() - B.air.return_pressure()
+ if(abs(differential) < vsc.airflow_lightest_pressure) return
+
+ var/list/attracted
+ var/list/repelled
+ if(differential > 0)
+ attracted = A.movables()
+ repelled = B.movables()
+ else
+ attracted = B.movables()
+ repelled = A.movables()
+
+ flow(attracted, abs(differential), 0)
+ flow(repelled, abs(differential), 1)
+
+//Helper proc to get connections for a zone.
+/connection_edge/zone/proc/get_connected_zone(zone/from)
+ if(A == from) return B
+ else return A
+
+/connection_edge/unsimulated/var/turf/B
+/connection_edge/unsimulated/var/datum/gas_mixture/air
+
+/connection_edge/unsimulated/New(zone/A, turf/B)
+ src.A = A
+ src.B = B
+ A.edges.Add(src)
+ air = B.return_air()
+ //id = 52*A.id
+ //world << "New edge from [A] to [B]."
+
+/connection_edge/unsimulated/add_connection(connection/c)
+ . = ..()
+ connecting_turfs.Add(c.B)
+ air.group_multiplier = coefficient
+
+/connection_edge/unsimulated/remove_connection(connection/c)
+ connecting_turfs.Remove(c.B)
+ air.group_multiplier = coefficient
+ . = ..()
+
+/connection_edge/unsimulated/erase()
+ A.edges.Remove(src)
+ . = ..()
+
+/connection_edge/unsimulated/contains_zone(zone/Z)
+ return A == Z
+
+/connection_edge/unsimulated/tick()
+ if(A.invalid)
+ erase()
+ return
+ //world << "[id]: Tick [air_master.current_cycle]: To [B]!"
+ //A.air.mimic(B, coefficient)
+ ShareSpace(A.air,air,dbg_out)
+ air_master.mark_zone_update(A)
+
+ var/differential = A.air.return_pressure() - air.return_pressure()
+ if(abs(differential) < vsc.airflow_lightest_pressure) return
+
+ var/list/attracted = A.movables()
+ flow(attracted, abs(differential), differential < 0)
+
+var/list/sharing_lookup_table = list(0.30, 0.40, 0.48, 0.54, 0.60, 0.66)
+
+proc/ShareRatio(datum/gas_mixture/A, datum/gas_mixture/B, connecting_tiles)
+ //Shares a specific ratio of gas between mixtures using simple weighted averages.
+ var
+ //WOOT WOOT TOUCH THIS AND YOU ARE A RETARD
+ ratio = sharing_lookup_table[6]
+ //WOOT WOOT TOUCH THIS AND YOU ARE A RETARD
+
+ size = max(1,A.group_multiplier)
+ share_size = max(1,B.group_multiplier)
+
+ full_oxy = A.oxygen * size
+ full_nitro = A.nitrogen * size
+ full_co2 = A.carbon_dioxide * size
+ full_plasma = A.toxins * size
+
+ full_heat_capacity = A.heat_capacity() * size
+
+ s_full_oxy = B.oxygen * share_size
+ s_full_nitro = B.nitrogen * share_size
+ s_full_co2 = B.carbon_dioxide * share_size
+ s_full_plasma = B.toxins * share_size
+
+ s_full_heat_capacity = B.heat_capacity() * share_size
+
+ oxy_avg = (full_oxy + s_full_oxy) / (size + share_size)
+ nit_avg = (full_nitro + s_full_nitro) / (size + share_size)
+ co2_avg = (full_co2 + s_full_co2) / (size + share_size)
+ plasma_avg = (full_plasma + s_full_plasma) / (size + share_size)
+
+ temp_avg = (A.temperature * full_heat_capacity + B.temperature * s_full_heat_capacity) / (full_heat_capacity + s_full_heat_capacity)
+
+ //WOOT WOOT TOUCH THIS AND YOU ARE A RETARD
+ if(sharing_lookup_table.len >= connecting_tiles) //6 or more interconnecting tiles will max at 42% of air moved per tick.
+ ratio = sharing_lookup_table[connecting_tiles]
+ //WOOT WOOT TOUCH THIS AND YOU ARE A RETARD
+
+ A.oxygen = max(0, (A.oxygen - oxy_avg) * (1-ratio) + oxy_avg )
+ A.nitrogen = max(0, (A.nitrogen - nit_avg) * (1-ratio) + nit_avg )
+ A.carbon_dioxide = max(0, (A.carbon_dioxide - co2_avg) * (1-ratio) + co2_avg )
+ A.toxins = max(0, (A.toxins - plasma_avg) * (1-ratio) + plasma_avg )
+
+ A.temperature = max(0, (A.temperature - temp_avg) * (1-ratio) + temp_avg )
+
+ B.oxygen = max(0, (B.oxygen - oxy_avg) * (1-ratio) + oxy_avg )
+ B.nitrogen = max(0, (B.nitrogen - nit_avg) * (1-ratio) + nit_avg )
+ B.carbon_dioxide = max(0, (B.carbon_dioxide - co2_avg) * (1-ratio) + co2_avg )
+ B.toxins = max(0, (B.toxins - plasma_avg) * (1-ratio) + plasma_avg )
+
+ B.temperature = max(0, (B.temperature - temp_avg) * (1-ratio) + temp_avg )
+
+ for(var/datum/gas/G in A.trace_gases)
+ var/datum/gas/H = locate(G.type) in B.trace_gases
+ if(H)
+ var/G_avg = (G.moles*size + H.moles*share_size) / (size+share_size)
+ G.moles = (G.moles - G_avg) * (1-ratio) + G_avg
+
+ H.moles = (H.moles - G_avg) * (1-ratio) + G_avg
+ else
+ H = new G.type
+ B.trace_gases += H
+ var/G_avg = (G.moles*size) / (size+share_size)
+ G.moles = (G.moles - G_avg) * (1-ratio) + G_avg
+ H.moles = (H.moles - G_avg) * (1-ratio) + G_avg
+
+ for(var/datum/gas/G in B.trace_gases)
+ var/datum/gas/H = locate(G.type) in A.trace_gases
+ if(!H)
+ H = new G.type
+ A.trace_gases += H
+ var/G_avg = (G.moles*size) / (size+share_size)
+ G.moles = (G.moles - G_avg) * (1-ratio) + G_avg
+ H.moles = (H.moles - G_avg) * (1-ratio) + G_avg
+
+ A.update_values()
+ B.update_values()
+
+ if(A.compare(B)) return 1
+ else return 0
+
+proc/ShareSpace(datum/gas_mixture/A, list/unsimulated_tiles, dbg_output)
+ //A modified version of ShareRatio for spacing gas at the same rate as if it were going into a large airless room.
+ if(!unsimulated_tiles)
+ return 0
+
+ var
+ unsim_oxygen = 0
+ unsim_nitrogen = 0
+ unsim_co2 = 0
+ unsim_plasma = 0
+ unsim_heat_capacity = 0
+ unsim_temperature = 0
+
+ size = max(1,A.group_multiplier)
+
+ var/tileslen
+ var/share_size
+
+ if(istype(unsimulated_tiles, /datum/gas_mixture))
+ var/datum/gas_mixture/avg_unsim = unsimulated_tiles
+ unsim_oxygen = avg_unsim.oxygen
+ unsim_co2 = avg_unsim.carbon_dioxide
+ unsim_nitrogen = avg_unsim.nitrogen
+ unsim_plasma = avg_unsim.toxins
+ unsim_temperature = avg_unsim.temperature
+ share_size = max(1, max(size + 3, 1) + avg_unsim.group_multiplier)
+ tileslen = avg_unsim.group_multiplier
+
+ if(dbg_output)
+ world << "O2: [unsim_oxygen] N2: [unsim_nitrogen] Size: [share_size] Tiles: [tileslen]"
+
+ else if(istype(unsimulated_tiles, /list))
+ if(!unsimulated_tiles.len)
+ return 0
+ // We use the same size for the potentially single space tile
+ // as we use for the entire room. Why is this?
+ // Short answer: We do not want larger rooms to depressurize more
+ // slowly than small rooms, preserving our good old "hollywood-style"
+ // oh-shit effect when large rooms get breached, but still having small
+ // rooms remain pressurized for long enough to make escape possible.
+ share_size = max(1, max(size + 3, 1) + unsimulated_tiles.len)
+ var/correction_ratio = share_size / unsimulated_tiles.len
+
+ for(var/turf/T in unsimulated_tiles)
+ unsim_oxygen += T.oxygen
+ unsim_co2 += T.carbon_dioxide
+ unsim_nitrogen += T.nitrogen
+ unsim_plasma += T.toxins
+ unsim_temperature += T.temperature/unsimulated_tiles.len
+
+ //These values require adjustment in order to properly represent a room of the specified size.
+ unsim_oxygen *= correction_ratio
+ unsim_co2 *= correction_ratio
+ unsim_nitrogen *= correction_ratio
+ unsim_plasma *= correction_ratio
+ tileslen = unsimulated_tiles.len
+
+ else //invalid input type
+ return 0
+
+ unsim_heat_capacity = HEAT_CAPACITY_CALCULATION(unsim_oxygen, unsim_co2, unsim_nitrogen, unsim_plasma)
+
+ var
+ ratio = sharing_lookup_table[6]
+
+ old_pressure = A.return_pressure()
+
+ full_oxy = A.oxygen * size
+ full_nitro = A.nitrogen * size
+ full_co2 = A.carbon_dioxide * size
+ full_plasma = A.toxins * size
+
+ full_heat_capacity = A.heat_capacity() * size
+
+ oxy_avg = (full_oxy + unsim_oxygen*share_size) / (size + share_size)
+ nit_avg = (full_nitro + unsim_nitrogen*share_size) / (size + share_size)
+ co2_avg = (full_co2 + unsim_co2*share_size) / (size + share_size)
+ plasma_avg = (full_plasma + unsim_plasma*share_size) / (size + share_size)
+
+ temp_avg = 0
+
+ if((full_heat_capacity + unsim_heat_capacity) > 0)
+ temp_avg = (A.temperature * full_heat_capacity + unsim_temperature * unsim_heat_capacity) / (full_heat_capacity + unsim_heat_capacity)
+
+ if(sharing_lookup_table.len >= tileslen) //6 or more interconnecting tiles will max at 42% of air moved per tick.
+ ratio = sharing_lookup_table[tileslen]
+
+ if(dbg_output)
+ world << "Ratio: [ratio]"
+ world << "Avg O2: [oxy_avg] N2: [nit_avg]"
+
+ A.oxygen = max(0, (A.oxygen - oxy_avg) * (1 - ratio) + oxy_avg )
+ A.nitrogen = max(0, (A.nitrogen - nit_avg) * (1 - ratio) + nit_avg )
+ A.carbon_dioxide = max(0, (A.carbon_dioxide - co2_avg) * (1 - ratio) + co2_avg )
+ A.toxins = max(0, (A.toxins - plasma_avg) * (1 - ratio) + plasma_avg )
+
+ A.temperature = max(TCMB, (A.temperature - temp_avg) * (1 - ratio) + temp_avg )
+
+ for(var/datum/gas/G in A.trace_gases)
+ var/G_avg = (G.moles * size) / (size + share_size)
+ G.moles = (G.moles - G_avg) * (1 - ratio) + G_avg
+
+ A.update_values()
+
+ if(dbg_output) world << "Result: [abs(old_pressure - A.return_pressure())] kPa"
+
+ return abs(old_pressure - A.return_pressure())
+
+
+proc/ShareHeat(datum/gas_mixture/A, datum/gas_mixture/B, connecting_tiles)
+ //This implements a simplistic version of the Stefan-Boltzmann law.
+ var/energy_delta = ((A.temperature - B.temperature) ** 4) * 5.6704e-8 * connecting_tiles * 2.5
+ var/maximum_energy_delta = max(0, min(A.temperature * A.heat_capacity() * A.group_multiplier, B.temperature * B.heat_capacity() * B.group_multiplier))
+ if(maximum_energy_delta > abs(energy_delta))
+ if(energy_delta < 0)
+ maximum_energy_delta *= -1
+ energy_delta = maximum_energy_delta
+
+ A.temperature -= energy_delta / (A.heat_capacity() * A.group_multiplier)
+ B.temperature += energy_delta / (B.heat_capacity() * B.group_multiplier)
\ No newline at end of file
diff --git a/code/ZAS/ConnectionManager.dm b/code/ZAS/ConnectionManager.dm
new file mode 100644
index 00000000000..1c101f4b45a
--- /dev/null
+++ b/code/ZAS/ConnectionManager.dm
@@ -0,0 +1,102 @@
+/*
+
+Overview:
+ The connection_manager class stores connections in each cardinal direction on a turf.
+ It isn't always present if a turf has no connections, check if(connections) before using.
+ Contains procs for mass manipulation of connection data.
+
+Class Vars:
+
+ NSEWUD - Connections to this turf in each cardinal direction.
+
+Class Procs:
+
+ get(d)
+ Returns the connection (if any) in this direction.
+ Preferable to accessing the connection directly because it checks validity.
+
+ place(connection/c, d)
+ Called by air_master.connect(). Sets the connection in the specified direction to c.
+
+ update_all()
+ Called after turf/update_air_properties(). Updates the validity of all connections on this turf.
+
+ erase_all()
+ Called when the turf is changed with ChangeTurf(). Erases all existing connections.
+
+ check(connection/c)
+ Checks for connection validity. It's possible to have a reference to a connection that has been erased.
+
+
+*/
+
+/turf/var/tmp/connection_manager/connections
+
+/connection_manager/var/connection/N
+/connection_manager/var/connection/S
+/connection_manager/var/connection/E
+/connection_manager/var/connection/W
+
+#ifdef ZLEVELS
+/connection_manager/var/connection/U
+/connection_manager/var/connection/D
+#endif
+
+/connection_manager/proc/get(d)
+ switch(d)
+ if(NORTH)
+ if(check(N)) return N
+ else return null
+ if(SOUTH)
+ if(check(S)) return S
+ else return null
+ if(EAST)
+ if(check(E)) return E
+ else return null
+ if(WEST)
+ if(check(W)) return W
+ else return null
+
+ #ifdef ZLEVELS
+ if(UP)
+ if(check(U)) return U
+ else return null
+ if(DOWN)
+ if(check(D)) return D
+ else return null
+ #endif
+
+/connection_manager/proc/place(connection/c, d)
+ switch(d)
+ if(NORTH) N = c
+ if(SOUTH) S = c
+ if(EAST) E = c
+ if(WEST) W = c
+
+ #ifdef ZLEVELS
+ if(UP) U = c
+ if(DOWN) D = c
+ #endif
+
+/connection_manager/proc/update_all()
+ if(check(N)) N.update()
+ if(check(S)) S.update()
+ if(check(E)) E.update()
+ if(check(W)) W.update()
+ #ifdef ZLEVELS
+ if(check(U)) U.update()
+ if(check(D)) D.update()
+ #endif
+
+/connection_manager/proc/erase_all()
+ if(check(N)) N.erase()
+ if(check(S)) S.erase()
+ if(check(E)) E.erase()
+ if(check(W)) W.erase()
+ #ifdef ZLEVELS
+ if(check(U)) U.erase()
+ if(check(D)) D.erase()
+ #endif
+
+/connection_manager/proc/check(connection/c)
+ return c && c.valid()
\ No newline at end of file
diff --git a/code/ZAS/Controller.dm b/code/ZAS/Controller.dm
new file mode 100644
index 00000000000..83b4bf3f3b4
--- /dev/null
+++ b/code/ZAS/Controller.dm
@@ -0,0 +1,321 @@
+var/datum/controller/air_system/air_master
+
+var/tick_multiplier = 2
+
+/*
+
+Overview:
+ The air controller does everything. There are tons of procs in here.
+
+Class Vars:
+ zones - All zones currently holding one or more turfs.
+ edges - All processing edges.
+
+ tiles_to_update - Tiles scheduled to update next tick.
+ zones_to_update - Zones which have had their air changed and need air archival.
+ active_hotspots - All processing fire objects.
+
+ active_zones - The number of zones which were archived last tick. Used in debug verbs.
+ next_id - The next UID to be applied to a zone. Mostly useful for debugging purposes as zones do not need UIDs to function.
+
+Class Procs:
+
+ mark_for_update(turf/T)
+ Adds the turf to the update list. When updated, update_air_properties() will be called.
+ When stuff changes that might affect airflow, call this. It's basically the only thing you need.
+
+ add_zone(zone/Z) and remove_zone(zone/Z)
+ Adds zones to the zones list. Does not mark them for update.
+
+ air_blocked(turf/A, turf/B)
+ Returns a bitflag consisting of:
+ AIR_BLOCKED - The connection between turfs is physically blocked. No air can pass.
+ ZONE_BLOCKED - There is a door between the turfs, so zones cannot cross. Air may or may not be permeable.
+
+ has_valid_zone(turf/T)
+ Checks the presence and validity of T's zone.
+ May be called on unsimulated turfs, returning 0.
+
+ merge(zone/A, zone/B)
+ Called when zones have a direct connection and equivalent pressure and temperature.
+ Merges the zones to create a single zone.
+
+ connect(turf/simulated/A, turf/B)
+ Called by turf/update_air_properties(). The first argument must be simulated.
+ Creates a connection between A and B.
+
+ mark_zone_update(zone/Z)
+ Adds zone to the update list. Unlike mark_for_update(), this one is called automatically whenever
+ air is returned from a simulated turf.
+
+ equivalent_pressure(zone/A, zone/B)
+ Currently identical to A.air.compare(B.air). Returns 1 when directly connected zones are ready to be merged.
+
+ get_edge(zone/A, zone/B)
+ get_edge(zone/A, turf/B)
+ Gets a valid connection_edge between A and B, creating a new one if necessary.
+
+ has_same_air(turf/A, turf/B)
+ Used to determine if an unsimulated edge represents a specific turf.
+ Simulated edges use connection_edge/contains_zone() for the same purpose.
+ Returns 1 if A has identical gases and temperature to B.
+
+ remove_edge(connection_edge/edge)
+ Called when an edge is erased. Removes it from processing.
+
+*/
+
+
+//Geometry lists
+/datum/controller/air_system/var/list/zones = list()
+/datum/controller/air_system/var/list/edges = list()
+
+//Geometry updates lists
+/datum/controller/air_system/var/list/tiles_to_update = list()
+/datum/controller/air_system/var/list/zones_to_update = list()
+/datum/controller/air_system/var/list/active_hotspots = list()
+
+/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.
+
+/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.
+
+ #ifndef ZASDBG
+ set background = 1
+ #endif
+
+ world << "\red \b Processing Geometry..."
+ 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()
+
+ world << {"Geometry initialized in [round(0.1*(world.timeofday-start_time),0.1)] seconds.
+Total Simulated Turfs: [simulated_turf_count]
+Total Zones: [zones.len]
+Total Unsimulated Turfs: [world.maxx*world.maxy*world.maxz - simulated_turf_count]"}
+
+// 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
+ for(var/turf/T in updating)
+ T.update_air_properties()
+ T.post_update_air_properties()
+ T.needs_air_update = 0
+ #ifdef ZASDBG
+ T.overlays -= mark
+ updated++
+ #endif
+ //sleep(1)
+
+ #ifdef ZASDBG
+ if(updated != updating.len)
+ tick_progress = "[updating.len - updated] tiles left unupdated."
+ world << "\red [tick_progress]"
+ . = 0
+ #endif
+
+ //Where gas exchange happens.
+ if(.)
+ tick_progress = "processing edges"
+
+ for(var/connection_edge/edge in edges)
+ edge.tick()
+
+ //Process fires.
+ if(.)
+ tick_progress = "processing fire"
+
+ 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++]"
+ mark_zone_update(z)
+
+/datum/controller/air_system/proc/remove_zone(zone/z)
+ zones.Remove(z)
+
+/datum/controller/air_system/proc/air_blocked(turf/A, turf/B)
+ #ifdef ZASDBG
+ ASSERT(isturf(A))
+ ASSERT(isturf(B))
+ #endif
+ var/ablock = A.c_airblock(B)
+ if(ablock == BLOCKED) return BLOCKED
+ return ablock | B.c_airblock(A)
+
+/datum/controller/air_system/proc/has_valid_zone(turf/simulated/T)
+ #ifdef ZASDBG
+ ASSERT(istype(T))
+ #endif
+ return istype(T) && T.zone && !T.zone.invalid
+
+/datum/controller/air_system/proc/merge(zone/A, zone/B)
+ #ifdef ZASDBG
+ ASSERT(istype(A))
+ ASSERT(istype(B))
+ ASSERT(!A.invalid)
+ ASSERT(!B.invalid)
+ ASSERT(A != B)
+ #endif
+ if(A.contents.len < B.contents.len)
+ A.c_merge(B)
+ mark_zone_update(B)
+ else
+ B.c_merge(A)
+ mark_zone_update(A)
+
+/datum/controller/air_system/proc/connect(turf/simulated/A, turf/simulated/B)
+ #ifdef ZASDBG
+ ASSERT(istype(A))
+ ASSERT(isturf(B))
+ ASSERT(A.zone)
+ ASSERT(!A.zone.invalid)
+ //ASSERT(B.zone)
+ ASSERT(A != B)
+ #endif
+
+ var/block = air_master.air_blocked(A,B)
+ if(block & AIR_BLOCKED) return
+
+ var/direct = !(block & ZONE_BLOCKED)
+ var/space = !istype(B)
+
+ if(direct && !space)
+ if(equivalent_pressure(A.zone,B.zone) || current_cycle == 0)
+ merge(A.zone,B.zone)
+ return
+
+ var
+ a_to_b = get_dir(A,B)
+ b_to_a = get_dir(B,A)
+
+ if(!A.connections) A.connections = new
+ if(!B.connections) B.connections = new
+
+ if(A.connections.get(a_to_b)) return
+ if(B.connections.get(b_to_a)) return
+ if(!space)
+ if(A.zone == B.zone) return
+
+
+ var/connection/c = new /connection(A,B)
+
+ A.connections.place(c, a_to_b)
+ B.connections.place(c, b_to_a)
+
+ if(direct) c.mark_direct()
+
+/datum/controller/air_system/proc/mark_for_update(turf/T)
+ #ifdef ZASDBG
+ ASSERT(isturf(T))
+ #endif
+ if(T.needs_air_update) return
+ tiles_to_update |= T
+ #ifdef ZASDBG
+ T.overlays += mark
+ #endif
+ T.needs_air_update = 1
+
+/datum/controller/air_system/proc/mark_zone_update(zone/Z)
+ #ifdef ZASDBG
+ ASSERT(istype(Z))
+ #endif
+ if(Z.needs_update) return
+ zones_to_update.Add(Z)
+ Z.needs_update = 1
+
+/datum/controller/air_system/proc/equivalent_pressure(zone/A, zone/B)
+ return A.air.compare(B.air)
+
+/datum/controller/air_system/proc/get_edge(zone/A, zone/B)
+
+ if(istype(B))
+ for(var/connection_edge/zone/edge in A.edges)
+ if(edge.contains_zone(B)) return edge
+ var/connection_edge/edge = new/connection_edge/zone(A,B)
+ edges.Add(edge)
+ return edge
+ else
+ for(var/connection_edge/unsimulated/edge in A.edges)
+ if(has_same_air(edge.B,B)) return edge
+ var/connection_edge/edge = new/connection_edge/unsimulated(A,B)
+ edges.Add(edge)
+ return edge
+
+/datum/controller/air_system/proc/has_same_air(turf/A, turf/B)
+ if(A.oxygen != B.oxygen) return 0
+ if(A.nitrogen != B.nitrogen) return 0
+ if(A.toxins != B.toxins) return 0
+ if(A.carbon_dioxide != B.carbon_dioxide) return 0
+ if(A.temperature != B.temperature) return 0
+ return 1
+
+/datum/controller/air_system/proc/remove_edge(connection/c)
+ edges.Remove(c)
\ No newline at end of file
diff --git a/code/ZAS/Debug.dm b/code/ZAS/Debug.dm
index 39f5a7caabd..6f0a67fd3eb 100644
--- a/code/ZAS/Debug.dm
+++ b/code/ZAS/Debug.dm
@@ -1,219 +1,20 @@
-client/proc/ZoneTick()
- set category = "Debug"
- set name = "Process Atmos"
+var/image/assigned = image('icons/Testing/Zone.dmi', icon_state = "assigned")
+var/image/created = image('icons/Testing/Zone.dmi', icon_state = "created")
+var/image/merged = image('icons/Testing/Zone.dmi', icon_state = "merged")
+var/image/invalid_zone = image('icons/Testing/Zone.dmi', icon_state = "invalid")
+var/image/air_blocked = image('icons/Testing/Zone.dmi', icon_state = "block")
+var/image/zone_blocked = image('icons/Testing/Zone.dmi', icon_state = "zoneblock")
+var/image/blocked = image('icons/Testing/Zone.dmi', icon_state = "fullblock")
+var/image/mark = image('icons/Testing/Zone.dmi', icon_state = "mark")
- var/result = air_master.Tick()
- if(result)
- src << "Sucessfully Processed."
+/connection_edge/var/dbg_out = 0
- else
- src << "Failed to process! ([air_master.tick_progress])"
+/turf/var/tmp/dbg_img
+/turf/proc/dbg(image/img, d = 0)
+ if(d > 0) img.dir = d
+ overlays -= dbg_img
+ overlays += img
+ dbg_img = img
-
-client/proc/Zone_Info(turf/T as null|turf)
- set category = "Debug"
- if(T)
- if(T.zone)
- T.zone.DebugDisplay(src)
- else
- mob << "No zone here."
- else
- if(zone_debug_images)
- for(var/zone in zone_debug_images)
- images -= zone_debug_images[zone]
- zone_debug_images = null
-
-client/var/list/zone_debug_images
-
-client/proc/Test_ZAS_Connection(var/turf/simulated/T as turf)
- set category = "Debug"
- if(!istype(T))
- return
-
- var/direction_list = list(\
- "North" = NORTH,\
- "South" = SOUTH,\
- "East" = EAST,\
- "West" = WEST,\
- "N/A" = null)
- var/direction = input("What direction do you wish to test?","Set direction") as null|anything in direction_list
- if(!direction)
- return
-
- if(direction == "N/A")
- if(T.CanPass(null, T, 0,0))
- mob << "The turf can pass air! :D"
- else
- mob << "No air passage :x"
- return
-
- var/turf/simulated/other_turf = get_step(T, direction_list[direction])
- if(!istype(other_turf))
- return
-
- var/pass_directions = T.CanPass(null, other_turf, 0, 0) + 2 * other_turf.CanPass(null, T, 0, 0)
-
- switch(pass_directions)
- if(0)
- mob << "Neither turf can connect. :("
-
- if(1)
- mob << "The initial turf only can connect. :\\"
-
- if(2)
- mob << "The other turf can connect, but not the initial turf. :/"
-
- if(3)
- mob << "Both turfs can connect! :)"
-
-
-zone/proc/DebugDisplay(client/client)
- if(!istype(client))
- return
-
- if(!dbg_output)
- dbg_output = 1 //Don't want to be spammed when someone investigates a zone...
-
- if(!client.zone_debug_images)
- client.zone_debug_images = list()
-
- var/list/current_zone_images = list()
-
- for(var/turf/T in contents)
- current_zone_images += image('icons/misc/debug_group.dmi', T, null, TURF_LAYER)
-
- for(var/turf/space/S in unsimulated_tiles)
- current_zone_images += image('icons/misc/debug_space.dmi', S, null, TURF_LAYER)
-
- client << "Zone Air Contents"
- client << "Oxygen: [air.oxygen]"
- client << "Nitrogen: [air.nitrogen]"
- client << "Plasma: [air.toxins]"
- client << "Carbon Dioxide: [air.carbon_dioxide]"
- client << "Temperature: [air.temperature] K"
- client << "Heat Energy: [air.temperature * air.heat_capacity()] J"
- client << "Pressure: [air.return_pressure()] KPa"
- client << ""
- client << "Space Tiles: [length(unsimulated_tiles)]"
- client << "Movable Objects: [length(movables())]"
- client << "Connections: [length(connections)]"
-
- for(var/connection/C in connections)
- client << "\ref[C] [C.A] --> [C.B] [(C.indirect?"Open":"Closed")]"
- current_zone_images += image('icons/misc/debug_connect.dmi', C.A, null, TURF_LAYER)
- current_zone_images += image('icons/misc/debug_connect.dmi', C.B, null, TURF_LAYER)
-
- client << "Connected Zones:"
- for(var/zone/zone in connected_zones)
- client << "\ref[zone] [zone] - [connected_zones[zone]] (Connected)"
-
- for(var/zone/zone in closed_connection_zones)
- client << "\ref[zone] [zone] - [closed_connection_zones[zone]] (Unconnected)"
-
- for(var/C in connections)
- if(!istype(C,/connection))
- client << "[C] (Not Connection!)"
-
- if(!client.zone_debug_images)
- client.zone_debug_images = list()
- client.zone_debug_images[src] = current_zone_images
-
- client.images += client.zone_debug_images[src]
-
- else
- dbg_output = 0
-
- client.images -= client.zone_debug_images[src]
- client.zone_debug_images.Remove(src)
-
- if(air_master)
- for(var/zone/Z in air_master.zones)
- if(Z.air == air && Z != src)
- var/turf/zloc = pick(Z.contents)
- client << "\red Illegal air datum shared by: [zloc.loc.name]"
-
-
-client/proc/TestZASRebuild()
- set category = "Debug"
-// var/turf/turf = get_turf(mob)
- var/zone/current_zone = mob.loc:zone
- if(!current_zone)
- src << "There is no zone there!"
- return
-
- var/list/current_adjacents = list()
- var/list/overlays = list()
- var/adjacent_id
- var/lowest_id
-
- var/list/identical_ids = list()
- var/list/turfs = current_zone.contents.Copy()
- var/current_identifier = 1
-
- for(var/turf/simulated/current in turfs)
- lowest_id = null
- current_adjacents = list()
-
- for(var/direction in cardinal)
- var/turf/simulated/adjacent = get_step(current, direction)
- if(!current.ZCanPass(adjacent))
- continue
- if(turfs.Find(adjacent))
- current_adjacents += adjacent
- adjacent_id = turfs[adjacent]
-
- if(adjacent_id && (!lowest_id || adjacent_id < lowest_id))
- lowest_id = adjacent_id
-
- if(!lowest_id)
- lowest_id = current_identifier++
- identical_ids += lowest_id
- overlays += image('icons/misc/debug_rebuild.dmi',, "[lowest_id]")
-
- for(var/turf/simulated/adjacent in current_adjacents)
- adjacent_id = turfs[adjacent]
- if(adjacent_id != lowest_id)
- if(adjacent_id)
- adjacent.overlays -= overlays[adjacent_id]
- identical_ids[adjacent_id] = lowest_id
-
- turfs[adjacent] = lowest_id
- adjacent.overlays += overlays[lowest_id]
-
- sleep(5)
-
- if(turfs[current])
- current.overlays -= overlays[turfs[current]]
- turfs[current] = lowest_id
- current.overlays += overlays[lowest_id]
- sleep(5)
-
- var/list/final_arrangement = list()
-
- for(var/turf/simulated/current in turfs)
- current_identifier = identical_ids[turfs[current]]
- current.overlays -= overlays[turfs[current]]
- current.overlays += overlays[current_identifier]
- sleep(5)
-
- if( current_identifier > final_arrangement.len )
- final_arrangement.len = current_identifier
- final_arrangement[current_identifier] = list(current)
-
- else
- final_arrangement[current_identifier] += current
-
- //lazy but fast
- final_arrangement.Remove(null)
-
- src << "There are [final_arrangement.len] unique segments."
-
- for(var/turf/current in turfs)
- current.overlays -= overlays
-
- return final_arrangement
-
-/client/proc/ZASSettings()
- set category = "Debug"
-
- vsc.SetDefault(mob)
\ No newline at end of file
+proc/soft_assert(thing,fail)
+ if(!thing) message_admins(fail)
\ No newline at end of file
diff --git a/code/ZAS/Diagnostic.dm b/code/ZAS/Diagnostic.dm
new file mode 100644
index 00000000000..eb5d72788e2
--- /dev/null
+++ b/code/ZAS/Diagnostic.dm
@@ -0,0 +1,236 @@
+client/proc/ZoneTick()
+ set category = "Debug"
+ set name = "Process Atmos"
+
+ var/result = air_master.Tick()
+ if(result)
+ src << "Sucessfully Processed."
+
+ else
+ src << "Failed to process! ([air_master.tick_progress])"
+
+
+client/proc/Zone_Info(turf/T as null|turf)
+ set category = "Debug"
+ if(T)
+ if(istype(T,/turf/simulated) && T:zone)
+ T:zone:dbg_data(src)
+ else
+ mob << "No zone here."
+ var/datum/gas_mixture/mix = T.return_air()
+ mob << "[mix.return_pressure()] kPa [mix.temperature]C"
+ mob << "O2: [mix.oxygen] N2: [mix.nitrogen] CO2: [mix.carbon_dioxide] TX: [mix.toxins]"
+ else
+ if(zone_debug_images)
+ for(var/zone in zone_debug_images)
+ images -= zone_debug_images[zone]
+ zone_debug_images = null
+
+client/var/list/zone_debug_images
+
+client/proc/Test_ZAS_Connection(var/turf/simulated/T as turf)
+ set category = "Debug"
+ if(!istype(T))
+ return
+
+ var/direction_list = list(\
+ "North" = NORTH,\
+ "South" = SOUTH,\
+ "East" = EAST,\
+ "West" = WEST,\
+ "N/A" = null)
+ var/direction = input("What direction do you wish to test?","Set direction") as null|anything in direction_list
+ if(!direction)
+ return
+
+ if(direction == "N/A")
+ if(!(T.c_airblock(T) & AIR_BLOCKED))
+ mob << "The turf can pass air! :D"
+ else
+ mob << "No air passage :x"
+ return
+
+ var/turf/simulated/other_turf = get_step(T, direction_list[direction])
+ if(!istype(other_turf))
+ return
+
+ var/t_block = T.c_airblock(other_turf)
+ var/o_block = other_turf.c_airblock(T)
+
+ if(o_block & AIR_BLOCKED)
+ if(t_block & AIR_BLOCKED)
+ mob << "Neither turf can connect. :("
+
+ else
+ mob << "The initial turf only can connect. :\\"
+ else
+ if(t_block & AIR_BLOCKED)
+ mob << "The other turf can connect, but not the initial turf. :/"
+
+ else
+ mob << "Both turfs can connect! :)"
+
+ mob << "Additionally, \..."
+
+ if(o_block & ZONE_BLOCKED)
+ if(t_block & ZONE_BLOCKED)
+ mob << "neither turf can merge."
+ else
+ mob << "the other turf cannot merge."
+ else
+ if(t_block & ZONE_BLOCKED)
+ mob << "the initial turf cannot merge."
+ else
+ mob << "both turfs can merge."
+
+
+/*zone/proc/DebugDisplay(client/client)
+ if(!istype(client))
+ return
+
+ if(!dbg_output)
+ dbg_output = 1 //Don't want to be spammed when someone investigates a zone...
+
+ if(!client.zone_debug_images)
+ client.zone_debug_images = list()
+
+ var/list/current_zone_images = list()
+
+ for(var/turf/T in contents)
+ current_zone_images += image('icons/misc/debug_group.dmi', T, null, TURF_LAYER)
+
+ for(var/turf/space/S in unsimulated_tiles)
+ current_zone_images += image('icons/misc/debug_space.dmi', S, null, TURF_LAYER)
+
+ client << "Zone Air Contents"
+ client << "Oxygen: [air.oxygen]"
+ client << "Nitrogen: [air.nitrogen]"
+ client << "Plasma: [air.toxins]"
+ client << "Carbon Dioxide: [air.carbon_dioxide]"
+ client << "Temperature: [air.temperature] K"
+ client << "Heat Energy: [air.temperature * air.heat_capacity()] J"
+ client << "Pressure: [air.return_pressure()] KPa"
+ client << ""
+ client << "Space Tiles: [length(unsimulated_tiles)]"
+ client << "Movable Objects: [length(movables())]"
+ client << "Connections: [length(connections)]"
+
+ for(var/connection/C in connections)
+ client << "\ref[C] [C.A] --> [C.B] [(C.indirect?"Open":"Closed")]"
+ current_zone_images += image('icons/misc/debug_connect.dmi', C.A, null, TURF_LAYER)
+ current_zone_images += image('icons/misc/debug_connect.dmi', C.B, null, TURF_LAYER)
+
+ client << "Connected Zones:"
+ for(var/zone/zone in connected_zones)
+ client << "\ref[zone] [zone] - [connected_zones[zone]] (Connected)"
+
+ for(var/zone/zone in closed_connection_zones)
+ client << "\ref[zone] [zone] - [closed_connection_zones[zone]] (Unconnected)"
+
+ for(var/C in connections)
+ if(!istype(C,/connection))
+ client << "[C] (Not Connection!)"
+
+ if(!client.zone_debug_images)
+ client.zone_debug_images = list()
+ client.zone_debug_images[src] = current_zone_images
+
+ client.images += client.zone_debug_images[src]
+
+ else
+ dbg_output = 0
+
+ client.images -= client.zone_debug_images[src]
+ client.zone_debug_images.Remove(src)
+
+ if(air_master)
+ for(var/zone/Z in air_master.zones)
+ if(Z.air == air && Z != src)
+ var/turf/zloc = pick(Z.contents)
+ client << "\red Illegal air datum shared by: [zloc.loc.name]"*/
+
+
+/*client/proc/TestZASRebuild()
+ set category = "Debug"
+// var/turf/turf = get_turf(mob)
+ var/zone/current_zone = mob.loc:zone
+ if(!current_zone)
+ src << "There is no zone there!"
+ return
+
+ var/list/current_adjacents = list()
+ var/list/overlays = list()
+ var/adjacent_id
+ var/lowest_id
+
+ var/list/identical_ids = list()
+ var/list/turfs = current_zone.contents.Copy()
+ var/current_identifier = 1
+
+ for(var/turf/simulated/current in turfs)
+ lowest_id = null
+ current_adjacents = list()
+
+ for(var/direction in cardinal)
+ var/turf/simulated/adjacent = get_step(current, direction)
+ if(!current.ZCanPass(adjacent))
+ continue
+ if(turfs.Find(adjacent))
+ current_adjacents += adjacent
+ adjacent_id = turfs[adjacent]
+
+ if(adjacent_id && (!lowest_id || adjacent_id < lowest_id))
+ lowest_id = adjacent_id
+
+ if(!lowest_id)
+ lowest_id = current_identifier++
+ identical_ids += lowest_id
+ overlays += image('icons/misc/debug_rebuild.dmi',, "[lowest_id]")
+
+ for(var/turf/simulated/adjacent in current_adjacents)
+ adjacent_id = turfs[adjacent]
+ if(adjacent_id != lowest_id)
+ if(adjacent_id)
+ adjacent.overlays -= overlays[adjacent_id]
+ identical_ids[adjacent_id] = lowest_id
+
+ turfs[adjacent] = lowest_id
+ adjacent.overlays += overlays[lowest_id]
+
+ sleep(5)
+
+ if(turfs[current])
+ current.overlays -= overlays[turfs[current]]
+ turfs[current] = lowest_id
+ current.overlays += overlays[lowest_id]
+ sleep(5)
+
+ var/list/final_arrangement = list()
+
+ for(var/turf/simulated/current in turfs)
+ current_identifier = identical_ids[turfs[current]]
+ current.overlays -= overlays[turfs[current]]
+ current.overlays += overlays[current_identifier]
+ sleep(5)
+
+ if( current_identifier > final_arrangement.len )
+ final_arrangement.len = current_identifier
+ final_arrangement[current_identifier] = list(current)
+
+ else
+ final_arrangement[current_identifier] += current
+
+ //lazy but fast
+ final_arrangement.Remove(null)
+
+ src << "There are [final_arrangement.len] unique segments."
+
+ for(var/turf/current in turfs)
+ current.overlays -= overlays
+
+ return final_arrangement*/
+
+client/proc/ZASSettings()
+ set category = "Debug"
+
+ vsc.SetDefault(mob)
\ No newline at end of file
diff --git a/code/ZAS/FEA_system.dm b/code/ZAS/FEA_system.dm
deleted file mode 100644
index 5a386d5959e..00000000000
--- a/code/ZAS/FEA_system.dm
+++ /dev/null
@@ -1,356 +0,0 @@
-/*
-Overview:
- The air_master global variable is the workhorse for the system.
-
-Why are you archiving data before modifying it?
- The general concept with archiving data and having each tile keep track of when they were last updated is to keep everything symmetric
- and totally independent of the order they are read in an update cycle.
- This prevents abnormalities like air/fire spreading rapidly in one direction and super slowly in the other.
-
-Why not just archive everything and then calculate?
- Efficiency. While a for-loop that goes through all tils and groups to archive their information before doing any calculations seems simple, it is
- slightly less efficient than the archive-before-modify/read method.
-
-Why is there a cycle check for calculating data as well?
- This ensures that every connection between group-tile, tile-tile, and group-group is only evaluated once per loop.
-
-
-
-
-Important variables:
- air_master.groups_to_rebuild (list)
- A list of air groups that have had their geometry occluded and thus may need to be split in half.
- A set of adjacent groups put in here will join together if validly connected.
- This is done before air system calculations for a cycle.
- air_master.tiles_to_update (list)
- Turfs that are in this list have their border data updated before the next air calculations for a cycle.
- Place turfs in this list rather than call the proc directly to prevent race conditions
-
- turf/simulated.archive() and datum/air_group.archive()
- This stores all data for.
- If you modify, make sure to update the archived_cycle to prevent race conditions and maintain symmetry
-
- atom/CanPass(atom/movable/mover, turf/target, height, air_group)
- returns 1 for allow pass and 0 for deny pass
- Turfs automatically call this for all objects/mobs in its turf.
- This is called both as source.CanPass(target, height, air_group)
- and target.CanPass(source, height, air_group)
-
- Cases for the parameters
- 1. This is called with args (mover, location, height>0, air_group=0) for normal objects.
- 2. This is called with args (null, location, height=0, air_group=0) for flowing air.
- 3. This is called with args (null, location, height=?, air_group=1) for determining group boundaries.
-
- Cases 2 and 3 would be different for doors or other objects open and close fairly often.
- (Case 3 would return 0 always while Case 2 would return 0 only when the door is open)
- This prevents the necessity of re-evaluating group geometry every time a door opens/closes.
-
-
-Important Procedures
- air_master.process()
- This first processes the air_master update/rebuild lists then processes all groups and tiles for air calculations
-
-*/
-
-var/tick_multiplier = 2
-
-atom/proc/CanPass(atom/movable/mover, turf/target, height=1.5, air_group = 0)
- //Purpose: Determines if the object (or airflow) can pass this atom.
- //Called by: Movement, airflow.
- //Inputs: The moving atom (optional), target turf, "height" and air group
- //Outputs: Boolean if can pass.
-
- return (!density || !height || air_group)
-
-/turf/CanPass(atom/movable/mover, turf/target, height=1.5,air_group=0)
- if(!target) return 0
-
- if(istype(mover)) // turf/Enter(...) will perform more advanced checks
- return !density
-
- else // Now, doing more detailed checks for air movement and air group formation
- if(target.blocks_air||blocks_air)
- return 0
-
- for(var/obj/obstacle in src)
- if(!obstacle.CanPass(mover, target, height, air_group))
- return 0
- if(target != src)
- for(var/obj/obstacle in target)
- if(!obstacle.CanPass(mover, src, height, air_group))
- return 0
-
- return 1
-
-
-var/datum/controller/air_system/air_master
-
-/datum/controller/air_system
- //Geometry lists
- var/list/turfs_with_connections = list()
- var/list/active_hotspots = list()
-
- //Special functions lists
- var/reconsidering_zones = FALSE
- var/list/tiles_to_reconsider_zones = list()
- var/list/tiles_to_reconsider_alternate
-
- //Geometry updates lists
- var/updating_tiles = FALSE
- var/list/tiles_to_update = list()
- var/list/tiles_to_update_alternate
-
- var/checking_connections = FALSE
- var/list/connections_to_check = list()
- var/list/connections_to_check_alternate
-
- var/list/potential_intrazone_connections = list()
-
- //Zone lists
- var/list/active_zones = list()
- var/list/zones_needing_rebuilt = list()
- var/list/zones = list()
-
-
- var/current_cycle = 0
- var/update_delay = 5 //How long between check should it try to process atmos again.
- var/failed_ticks = 0 //How many ticks have runtimed?
-
- var/tick_progress = 0
-
-
-/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.
-
- set background = 1
- world << "\red \b Processing Geometry..."
- sleep(-1)
-
- var/start_time = world.timeofday
-
- var/simulated_turf_count = 0
-
- for(var/turf/simulated/S in world)
- simulated_turf_count++
- if(!S.zone && !S.blocks_air)
- if(S.CanPass(null, S, 0, 0))
- new/zone(S)
-
- for(var/turf/simulated/S in world)
- S.update_air_properties()
-
- world << {"Geometry initialized in [round(0.1*(world.timeofday-start_time),0.1)] seconds.
-Total Simulated Turfs: [simulated_turf_count]
-Total Zones: [zones.len]
-Total Unsimulated Turfs: [world.maxx*world.maxy*world.maxz - simulated_turf_count]"}
-
-// 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.
-
-
- set background = 1
-
- while(1)
- if(!air_processing_killed)
- var/success = Tick() //Changed so that a runtime does not crash the ticker.
- if(!success) //Runtimed.
- failed_ticks++
- if(failed_ticks > 20)
- world << "ERROR IN ATMOS TICKER. Killing air simulation!"
- air_processing_killed = 1
- 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"
- if(tiles_to_update.len)
- updating_tiles = TRUE
-
- for(var/turf/simulated/T in tiles_to_update)
- if(. && T && !T.update_air_properties())
- //If a runtime occured, make sure we can sense it.
- . = 0
-
- updating_tiles = FALSE
-
- if(.)
- if(tiles_to_update_alternate)
- tiles_to_update = tiles_to_update_alternate
- tiles_to_update_alternate = null
- else
- tiles_to_update = list()
-
- else if(tiles_to_update_alternate)
- tiles_to_update |= tiles_to_update_alternate
- tiles_to_update_alternate = null
-
- //Rebuild zones.
- if(.)
- tick_progress = "rebuilding zones"
- if(zones_needing_rebuilt.len)
- for(var/zone/zone in zones_needing_rebuilt)
- zone.Rebuild()
-
- zones_needing_rebuilt = list()
-
- //Check sanity on connection objects.
- if(.)
- tick_progress = "checking/creating connections"
- if(connections_to_check.len)
- checking_connections = TRUE
-
- for(var/connection/C in connections_to_check)
- C.Cleanup()
-
- for(var/turf/simulated/turf_1 in potential_intrazone_connections)
- for(var/turf/simulated/turf_2 in potential_intrazone_connections[turf_1])
-
- if(!turf_1.zone || !turf_2.zone)
- continue
-
- if(turf_1.zone == turf_2.zone)
- continue
-
- var/should_skip = FALSE
- if(turf_1 in air_master.turfs_with_connections)
-
- for(var/connection/C in turfs_with_connections[turf_1])
- if(C.B == turf_2 || C.A == turf_2)
- should_skip = TRUE
- break
- if(should_skip)
- continue
-
- new /connection(turf_1, turf_2)
-
- checking_connections = FALSE
-
- potential_intrazone_connections = list()
-
- if(connections_to_check_alternate)
- connections_to_check = connections_to_check_alternate
- connections_to_check_alternate = null
- else
- connections_to_check = list()
-
- //Process zones.
- if(.)
- tick_progress = "processing zones"
- for(var/zone/Z in active_zones)
- if(Z.last_update < current_cycle)
- var/output = Z.process()
- if(Z)
- Z.last_update = current_cycle
- if(. && Z && !output)
- . = 0
-
- //Ensure tiles still have zones.
- if(.)
- tick_progress = "reconsidering zones on turfs"
- if(tiles_to_reconsider_zones.len)
- reconsidering_zones = TRUE
-
- for(var/turf/simulated/T in tiles_to_reconsider_zones)
- if(!T.zone)
- new /zone(T)
-
- reconsidering_zones = FALSE
-
- if(tiles_to_reconsider_alternate)
- tiles_to_reconsider_zones = tiles_to_reconsider_alternate
- tiles_to_reconsider_alternate = null
- else
- tiles_to_reconsider_zones = list()
-
- //Process fires.
- if(.)
- tick_progress = "processing fire"
- for(var/obj/fire/F in active_hotspots)
- if(. && F && !F.process())
- . = 0
-
- if(.)
- tick_progress = "success"
-
-
-/datum/controller/air_system/proc/AddTurfToUpdate(turf/simulated/outdated_turf)
- var/list/tiles_to_check = list()
-
- if(istype(outdated_turf))
- tiles_to_check |= outdated_turf
-
- if(istype(outdated_turf, /turf))
- for(var/direction in cardinal)
- var/turf/simulated/adjacent_turf = get_step(outdated_turf, direction)
- if(istype(adjacent_turf))
- tiles_to_check |= adjacent_turf
-
- if(updating_tiles)
- if(!tiles_to_update_alternate)
- tiles_to_update_alternate = tiles_to_check
- else
- tiles_to_update_alternate |= tiles_to_check
- else
- tiles_to_update |= tiles_to_check
-
-
-/datum/controller/air_system/proc/AddConnectionToCheck(connection/connection)
- if(checking_connections)
- if(istype(connection, /list))
- if(!connections_to_check_alternate)
- connections_to_check_alternate = connection
-
- else if(!connections_to_check_alternate)
- connections_to_check_alternate = list()
-
- connections_to_check_alternate |= connection
-
- else
- connections_to_check |= connection
-
-
-/datum/controller/air_system/proc/ReconsiderTileZone(var/turf/simulated/zoneless_turf)
- if(zoneless_turf.zone)
- return
-
- if(reconsidering_zones)
- if(!tiles_to_reconsider_alternate)
- tiles_to_reconsider_alternate = list()
-
- tiles_to_reconsider_alternate |= zoneless_turf
-
- else
- tiles_to_reconsider_zones |= zoneless_turf
-
-
-/datum/controller/air_system/proc/AddIntrazoneConnection(var/turf/simulated/A, var/turf/simulated/B)
- if(!istype(A) || !istype(B))
- return
-
- if(A in potential_intrazone_connections)
- if(B in potential_intrazone_connections[A])
- return
-
- if (B in potential_intrazone_connections)
- if(A in potential_intrazone_connections[B])
- return
-
- potential_intrazone_connections[B] += A
-
- else
- potential_intrazone_connections[B] = list(A)
\ No newline at end of file
diff --git a/code/ZAS/Fire.dm b/code/ZAS/Fire.dm
index ddc06106f38..a564de2836f 100644
--- a/code/ZAS/Fire.dm
+++ b/code/ZAS/Fire.dm
@@ -108,7 +108,7 @@ turf/simulated/hotspot_expose(exposed_temperature, exposed_volume, soh)
A.fire_act(air_contents, air_contents.temperature, air_contents.return_volume())
//spread
for(var/direction in cardinal)
- if(S.air_check_directions&direction) //Grab all valid bordering tiles
+ if(S.open_directions & direction) //Grab all valid bordering tiles
var/turf/simulated/enemy_tile = get_step(S, direction)
diff --git a/code/ZAS/Functions.dm b/code/ZAS/Functions.dm
deleted file mode 100644
index d0f5e593295..00000000000
--- a/code/ZAS/Functions.dm
+++ /dev/null
@@ -1,243 +0,0 @@
-//Global Functions
-//Contents: FloodFill, ZMerge, ZConnect
-
-//Floods outward from an initial turf to fill everywhere it's zone would reach.
-proc/FloodFill(turf/simulated/start)
-
- if(!istype(start))
- return list()
-
- //The list of tiles waiting to be evaulated.
- var/list/open = list(start)
- //The list of tiles which have been evaulated.
- var/list/closed = list()
-/////// Z-Level stuff
- //List of all space tiles bordering the zone
- var/list/list_space = list()
- //List of all Z-Levels of the zone where it borders space
- var/list/z_space = list()
-/////// Z-Level stuff
-
- //Loop through the turfs in the open list in order to find which adjacent turfs should be added to the zone.
- while(open.len)
- var/turf/simulated/T = pick(open)
-
- //sanity!
- if(!istype(T))
- open -= T
- continue
-
- //Check all cardinal directions
- for(var/d in cardinal)
- var/turf/simulated/O = get_step(T,d)
-
- //Ensure the turf is of proper type, that it is not in either list, and that air can reach it.
- if(istype(O) && !(O in open) && !(O in closed) && O.ZCanPass(T))
-
- //Handle connections from a tile with a door.
- if(T.HasDoor())
- //If they both have doors, then they are not able to connect period.
- if(O.HasDoor())
- continue
-
- //Connect first to north and west
- if(d == NORTH || d == WEST)
- open += O
-
- //If that fails, and north/west cannot be connected to, see if west or south can be connected instead.
- else
- var/turf/simulated/W = get_step(O, WEST)
- var/turf/simulated/N = get_step(O, NORTH)
-
- if( !O.ZCanPass(N) && !O.ZCanPass(W) )
- //If it cannot connect either to the north or west, connect it!
- open += O
-
- //If no doors are involved, add it immediately.
- else if(!O.HasDoor())
- open += O
-
- //Handle connecting to a tile with a door.
- else
- if(d == SOUTH || d == EAST)
- //doors prefer connecting to zones to the north or west
- closed += O
-
- else
- //see if we need to force an attempted connection
- //(there are no potentially viable zones to the north/west of the door)
- var/turf/simulated/W = get_step(O, WEST)
- var/turf/simulated/N = get_step(O, NORTH)
-
- if( !O.ZCanPass(N) && !O.ZCanPass(W) )
- //If it cannot connect either to the north or west, connect it!
- closed += O
-
-/////// Z-Level stuff
- if(istype(O,/turf/space))
- if(!(O in list_space))
- list_space += O
- if(!(O.z in z_space))
- z_space += O.z
-
- // handle Z-level connections
- var/turf/controllerlocation = locate(1, 1, T.z)
- for(var/obj/effect/landmark/zcontroller/controller in controllerlocation)
- // connect upwards
- if(controller.up)
- var/turf/above_me = locate(T.x, T.y, controller.up_target)
- // add the turf above this
- if(istype(above_me, /turf/simulated/floor/open) && !(above_me in open) && !(above_me in closed))
- open += above_me
-
- if(istype(above_me,/turf/space))
- if(!(above_me in list_space))
- list_space += above_me
- if(!(above_me.z in z_space))
- z_space += above_me.z
- // connect downwards
- if(controller.down && istype(T, /turf/simulated/floor/open))
- var/turf/below_me = locate(T.x, T.y, controller.down_target)
- // add the turf below this
- if(!(below_me in open) && !(below_me in closed))
- open += below_me
-/////// Z-Level stuff
-
- //This tile is now evaluated, and can be moved to the list of evaluated tiles.
- open -= T
- closed += T
-
-/////// Z-Level stuff
- // once the zone is done, check if there is space that needs to be changed to open space
- if(!open.len)
- var/list/temp = list()
- while(list_space.len)
- var/turf/S = pick(list_space)
- //check if the zone has any space borders below the evaluated space tile
- //if there is some, we dont need to make open_space since the zone can vent and the zone above can vent
- //through the evaluated tile
- //if there is none, the zone can connect upwards to either vent from there or connect with the zone there
- //also check if the turf below the space is actually part of this zone to prevent the edge tiles from transforming
- var/turf/controllerloc = locate(1, 1, S.z)
- for(var/obj/effect/landmark/zcontroller/controller in controllerloc)
- if(controller.down)
- var/turf/below = locate(S.x, S.y, controller.down_target)
- if(!((S.z - 1) in z_space) && below in closed)
- open += S.ChangeTurf(/turf/simulated/floor/open)
- list_space -= S
- else
- list_space -= S
- temp += S
- else
- list_space -= S
- temp += S
- // make sure the turf is removed from the list
- list_space -= S
- z_space -= z_space
- while(temp.len)
- var/turf/S = pick(temp)
- if(!(S.z in z_space))
- z_space += S.z
- list_space += S
- temp -= S
-/////// Z-Level stuff
-
- return closed
-
-
-//Procedure to merge two zones together.
-proc/ZMerge(zone/A,zone/B)
-
- //Sanity~
- if(!istype(A) || !istype(B))
- return
-
- var/new_contents = A.contents + B.contents
-
- //Set all the zone vars.
- for(var/turf/simulated/T in B.contents)
- T.zone = A
-
- if(istype(A.air) && istype(B.air))
- //Merges two zones so that they are one.
- var/a_size = A.air.group_multiplier
- var/b_size = B.air.group_multiplier
- var/c_size = a_size + b_size
-
- //Set air multipliers to one so air represents gas per tile.
- A.air.group_multiplier = 1
- B.air.group_multiplier = 1
-
- //Remove some air proportional to the size of this zone.
- A.air.remove_ratio(a_size/c_size)
- B.air.remove_ratio(b_size/c_size)
-
- //Merge the gases and set the multiplier to the sum of the old ones.
- A.air.merge(B.air)
- A.air.group_multiplier = c_size
-
- //I hate when the air datum somehow disappears.
- // Try to make it sorta work anyways. Fakit
- else if(istype(B.air))
- A.air = B.air
- A.air.group_multiplier = A.contents.len
-
- else if(istype(A.air))
- A.air.group_multiplier = A.contents.len
-
- //Doublefakit.
- else
- A.air = new
-
- //Check for connections to merge into the new zone.
- for(var/connection/C in B.connections)
- //The Cleanup proc will delete the connection if the zones are the same.
- // It will also set the zone variables correctly.
- C.Cleanup()
-
- //Add space tiles.
- if(A.unsimulated_tiles && B.unsimulated_tiles)
- A.unsimulated_tiles |= B.unsimulated_tiles
- else if (B.unsimulated_tiles)
- A.unsimulated_tiles = B.unsimulated_tiles
-
- //Add contents.
- A.contents = new_contents
-
- //Remove the "B" zone, finally.
- B.SoftDelete()
-
-
-//Connects two zones by forming a connection object representing turfs A and B.
-proc/ZConnect(turf/simulated/A,turf/simulated/B)
-
- //Make sure that if it's space, it gets added to unsimulated_tiles instead.
- if(!istype(B))
- if(A.zone)
- A.zone.AddTurf(B)
- return
- if(!istype(A))
- if(B.zone)
- B.zone.AddTurf(A)
- return
-
- if(!istype(A) || !istype(B))
- return
-
- //Make some preliminary checks to see if the connection is valid.
- if(!A.zone || !B.zone) return
- if(A.zone == B.zone)
- air_master.AddIntrazoneConnection(A,B)
- return
-
- if(A.CanPass(null, B, 1.5, 1) && A.zone.air.compare(B.zone.air))
- return ZMerge(A.zone,B.zone)
-
- //Ensure the connection isn't already made.
- if(A in air_master.turfs_with_connections)
- for(var/connection/C in air_master.turfs_with_connections[A])
- if(C.B == B || C.A == B)
- return
-
- //Make the connection.
- new /connection(A,B)
diff --git a/code/ZAS/Turf.dm b/code/ZAS/Turf.dm
new file mode 100644
index 00000000000..fece782aa71
--- /dev/null
+++ b/code/ZAS/Turf.dm
@@ -0,0 +1,241 @@
+/turf/simulated/var/zone/zone
+/turf/simulated/var/open_directions
+/turf/simulated/var/gas_graphic
+
+/turf/var/needs_air_update = 0
+/turf/var/datum/gas_mixture/air
+
+/turf/simulated/proc/set_graphic(new_graphic)
+ if(isnum(new_graphic))
+ if(new_graphic == 1) new_graphic = plmaster
+ else if(new_graphic == 2) new_graphic = slmaster
+ if(gas_graphic) overlays -= gas_graphic
+ if(new_graphic) overlays += new_graphic
+ gas_graphic = new_graphic
+
+/turf/proc/update_air_properties()
+ var/block = c_airblock(src)
+ if(block & AIR_BLOCKED)
+ //dbg(blocked)
+ return 1
+
+ #ifdef ZLEVELS
+ for(var/d = 1, d < 64, d *= 2)
+ #else
+ for(var/d = 1, d < 16, d *= 2)
+ #endif
+
+ var/turf/unsim = get_step(src, d)
+ block = unsim.c_airblock(src)
+
+ if(block & AIR_BLOCKED)
+ //unsim.dbg(air_blocked, turn(180,d))
+ continue
+
+ var/r_block = c_airblock(unsim)
+
+ if(r_block & AIR_BLOCKED)
+ continue
+
+ if(istype(unsim, /turf/simulated))
+
+ var/turf/simulated/sim = unsim
+ if(air_master.has_valid_zone(sim))
+
+ air_master.connect(sim, src)
+
+/turf/simulated/update_air_properties()
+ if(zone && zone.invalid)
+ c_copy_air()
+ zone = null //Easier than iterating through the list at the zone.
+
+ var/s_block = c_airblock(src)
+ if(s_block & AIR_BLOCKED)
+ #ifdef ZASDBG
+ if(verbose) world << "Self-blocked."
+ //dbg(blocked)
+ #endif
+ if(zone)
+ var/zone/z = zone
+ if(locate(/obj/machinery/door/airlock) in src) //Hacky, but prevents normal airlocks from rebuilding zones all the time
+ z.remove(src)
+ else
+ z.rebuild()
+
+ return 1
+
+ var/previously_open = open_directions
+ open_directions = 0
+
+ var/list/postponed
+ #ifdef ZLEVELS
+ for(var/d = 1, d < 64, d *= 2)
+ #else
+ for(var/d = 1, d < 16, d *= 2)
+ #endif
+
+ var/turf/unsim = get_step(src, d)
+ var/block = unsim.c_airblock(src)
+ if(block & AIR_BLOCKED)
+
+ #ifdef ZASDBG
+ if(verbose) world << "[d] is blocked."
+ //unsim.dbg(air_blocked, turn(180,d))
+ #endif
+
+ continue
+
+ var/r_block = c_airblock(unsim)
+ if(r_block & AIR_BLOCKED)
+
+ #ifdef ZASDBG
+ if(verbose) world << "[d] is blocked."
+ //dbg(air_blocked, d)
+ #endif
+
+ //Check that our zone hasn't been cut off recently.
+ //This happens when windows move or are constructed. We need to rebuild.
+ if((previously_open & d) && istype(unsim, /turf/simulated))
+ var/turf/simulated/sim = unsim
+ if(sim.zone == zone)
+ zone.rebuild()
+ return
+
+ continue
+
+ open_directions |= d
+
+ if(istype(unsim, /turf/simulated))
+
+ var/turf/simulated/sim = unsim
+ if(air_master.has_valid_zone(sim))
+
+ //Might have assigned a zone, since this happens for each direction.
+ if(!zone)
+
+ //if((block & ZONE_BLOCKED) || (r_block & ZONE_BLOCKED && !(s_block & ZONE_BLOCKED)))
+ if(((block & ZONE_BLOCKED) && !(r_block & ZONE_BLOCKED)) || (r_block & ZONE_BLOCKED && !(s_block & ZONE_BLOCKED)))
+ #ifdef ZASDBG
+ if(verbose) world << "[d] is zone blocked."
+ //dbg(zone_blocked, d)
+ #endif
+
+ //Postpone this tile rather than exit, since a connection can still be made.
+ if(!postponed) postponed = list()
+ postponed.Add(sim)
+
+ else
+
+ sim.zone.add(src)
+
+ #ifdef ZASDBG
+ dbg(assigned)
+ if(verbose) world << "Added to [zone]"
+ #endif
+
+ else if(sim.zone != zone)
+
+ #ifdef ZASDBG
+ if(verbose) world << "Connecting to [sim.zone]"
+ #endif
+
+ air_master.connect(src, sim)
+
+
+ #ifdef ZASDBG
+ else if(verbose) world << "[d] has same zone."
+
+ else if(verbose) world << "[d] has invalid zone."
+ #endif
+
+ else
+
+ //Postponing connections to tiles until a zone is assured.
+ if(!postponed) postponed = list()
+ postponed.Add(unsim)
+
+ if(!air_master.has_valid_zone(src)) //Still no zone, make a new one.
+ var/zone/newzone = new/zone()
+ newzone.add(src)
+
+ #ifdef ZASDBG
+ dbg(created)
+
+ ASSERT(zone)
+ #endif
+
+ //At this point, a zone should have happened. If it hasn't, don't add more checks, fix the bug.
+
+ for(var/turf/T in postponed)
+ air_master.connect(src, T)
+
+/turf/proc/post_update_air_properties()
+ if(connections) connections.update_all()
+
+/turf/assume_air(datum/gas_mixture/giver) //use this for machines to adjust air
+ del(giver)
+ return 0
+
+/turf/return_air()
+ //Create gas mixture to hold data for passing
+ var/datum/gas_mixture/GM = new
+
+ GM.oxygen = oxygen
+ GM.carbon_dioxide = carbon_dioxide
+ GM.nitrogen = nitrogen
+ GM.toxins = toxins
+
+ GM.temperature = temperature
+ GM.update_values()
+
+ return GM
+
+/turf/remove_air(amount as num)
+ var/datum/gas_mixture/GM = new
+
+ var/sum = oxygen + carbon_dioxide + nitrogen + toxins
+ if(sum>0)
+ GM.oxygen = (oxygen/sum)*amount
+ GM.carbon_dioxide = (carbon_dioxide/sum)*amount
+ GM.nitrogen = (nitrogen/sum)*amount
+ GM.toxins = (toxins/sum)*amount
+
+ GM.temperature = temperature
+ GM.update_values()
+
+ return GM
+
+/turf/simulated/assume_air(datum/gas_mixture/giver)
+ var/datum/gas_mixture/my_air = return_air()
+ my_air.merge(giver)
+
+/turf/simulated/remove_air(amount as num)
+ var/datum/gas_mixture/my_air = return_air()
+ return my_air.remove(amount)
+
+/turf/simulated/return_air()
+ if(zone)
+ if(!zone.invalid)
+ air_master.mark_zone_update(zone)
+ return zone.air
+ else
+ if(!air)
+ make_air()
+ c_copy_air()
+ return air
+ else
+ if(!air)
+ make_air()
+ return air
+
+/turf/proc/make_air()
+ air = new/datum/gas_mixture
+ air.temperature = temperature
+ air.adjust(oxygen, carbon_dioxide, nitrogen, toxins)
+ air.group_multiplier = 1
+ air.volume = CELL_VOLUME
+
+/turf/simulated/proc/c_copy_air()
+ if(!air) air = new/datum/gas_mixture
+ air.copy_from(zone.air)
+ air.group_multiplier = 1
\ No newline at end of file
diff --git a/code/ZAS/ZAS_Turfs.dm b/code/ZAS/ZAS_Turfs.dm
deleted file mode 100644
index 43ea2887627..00000000000
--- a/code/ZAS/ZAS_Turfs.dm
+++ /dev/null
@@ -1,344 +0,0 @@
-/atom/var/pressure_resistance = ONE_ATMOSPHERE
-
-/turf/var/zone/zone
-
-/turf/assume_air(datum/gas_mixture/giver) //use this for machines to adjust air
- del(giver)
- return 0
-
-/turf/return_air()
- //Create gas mixture to hold data for passing
- var/datum/gas_mixture/GM = new
-
- GM.oxygen = oxygen
- GM.carbon_dioxide = carbon_dioxide
- GM.nitrogen = nitrogen
- GM.toxins = toxins
-
- GM.temperature = temperature
- GM.update_values()
-
- return GM
-
-/turf/remove_air(amount as num)
- var/datum/gas_mixture/GM = new
-
- var/sum = oxygen + carbon_dioxide + nitrogen + toxins
- if(sum>0)
- GM.oxygen = (oxygen/sum)*amount
- GM.carbon_dioxide = (carbon_dioxide/sum)*amount
- GM.nitrogen = (nitrogen/sum)*amount
- GM.toxins = (toxins/sum)*amount
-
- GM.temperature = temperature
- GM.update_values()
-
- return GM
-
-/turf/simulated/var/current_graphic = null
-
-/turf/simulated/var/tmp/datum/gas_mixture/air
-
-/turf/simulated/var/tmp/air_check_directions = 0 //Do not modify this, just add turf to air_master.tiles_to_update
-
-/turf/simulated/var/tmp/unsim_check_directions = 0 //See above.
-
-/turf/simulated/var/tmp/obj/fire/active_hotspot
-
-/turf/simulated/proc/update_visuals()
- overlays = null
-
- var/siding_icon_state = return_siding_icon_state()
- if(siding_icon_state)
- overlays += image('icons/turf/floors.dmi',siding_icon_state)
- var/datum/gas_mixture/model = return_air()
- switch(model.graphic)
- if(1)
- overlays.Add(plmaster) //TODO: Make invisible plasma an option
- if(2)
- overlays.Add(slmaster)
-
-/turf/simulated/New()
- if(!blocks_air)
- air = new
-
- air.oxygen = oxygen
- air.carbon_dioxide = carbon_dioxide
- air.nitrogen = nitrogen
- air.toxins = toxins
-
- air.temperature = temperature
- air.update_values()
-
- if(air_master)
- air_master.tiles_to_update.Add(src)
-
- else
- if(air_master)
- for(var/direction in cardinal)
- var/turf/simulated/floor/target = get_step(src,direction)
- if(istype(target))
- air_master.tiles_to_update |= target
-
- . = ..()
-
-/turf/simulated/Del()
- if(active_hotspot)
- del(active_hotspot)
- if(blocks_air)
- for(var/direction in list(NORTH, SOUTH, EAST, WEST))
- var/turf/simulated/tile = get_step(src,direction)
- if(istype(tile) && !tile.blocks_air)
- air_master.tiles_to_update.Add(tile)
- ..()
-
-/turf/simulated/assume_air(datum/gas_mixture/giver)
- if(!giver) return 0
- if(zone)
- zone.assume_air(giver)
- return 1
- else
- return ..()
-
-/turf/simulated/return_air()
- if(zone)
- return zone.air
- else if(air)
- return air
-
- else
- return ..()
-
-/turf/simulated/remove_air(amount as num)
- if(zone)
- return zone.remove_air(amount)
-
- else if(air)
- var/datum/gas_mixture/removed = null
- removed = air.remove(amount)
-
- if(air.check_tile_graphic())
- update_visuals(air)
- return removed
-
- else
- return ..()
-
-/turf/simulated/proc/update_air_properties()
- var/air_directions_archived = air_check_directions
- air_check_directions = 0
-
- var/unsim_directions_archived = unsim_check_directions
- unsim_check_directions = 0
-
- for(var/direction in cardinal)
- var/turf/check_turf = get_step(src, direction)
- if(ZAirPass(check_turf))
- if(istype(check_turf, /turf/simulated))
- air_check_directions |= direction
- else if(istype(check_turf, /turf/space) || istype(check_turf, /turf/unsimulated))
- unsim_check_directions |= direction
-
- if(!zone && !blocks_air) //No zone, but not a wall.
- for(var/direction in DoorDirections) //Check door directions first.
- if(air_check_directions & direction)
- var/turf/simulated/T = get_step(src, direction)
- if(!istype(T))
- continue
- if(T.zone)
- T.zone.AddTurf(src)
- break
- if(!zone) //Still no zone
- for(var/direction in CounterDoorDirections) //Check the others second.
- if(air_check_directions & direction)
- var/turf/simulated/T = get_step(src,direction)
- if(!istype(T))
- continue
- if(T.zone)
- T.zone.AddTurf(src)
- break
- if(!zone) //No zone found, new zone!
- new/zone(src)
- if(!zone) //Still no zone, the floodfill determined it is not part of a larger zone. Force a zone on it.
- new/zone(list(src))
-
- //Check pass sanity of the connections.
- if(src in air_master.turfs_with_connections)
- air_master.AddConnectionToCheck(air_master.turfs_with_connections[src])
-
- if(zone && !air_master.zones_needing_rebuilt.Find(zone))
- for(var/direction in cardinal)
- var/turf/T = get_step(src,direction)
- if(!istype(T))
- continue
-
- //I can connect to air in this direction
- if(air_check_directions & direction || unsim_check_directions & direction)
-
- //If either block air, we must look to see if the adjacent turfs need rebuilt.
- if(!CanPass(null, T, 0, 0))
-
- //Target blocks air
- if(!T.CanPass(null, T, 0, 0))
- var/turf/NT = get_step(T, direction)
-
- //If that turf is in my zone still, rebuild.
- if(istype(NT,/turf/simulated) && NT in zone.contents)
- air_master.zones_needing_rebuilt.Add(zone)
-
- //If that is an unsimulated tile in my zone, see if we need to rebuild or just remove.
- else if(istype(NT) && NT in zone.unsimulated_tiles)
- var/consider_rebuild = 0
- for(var/d in cardinal)
- var/turf/UT = get_step(NT,d)
- if(istype(UT, /turf/simulated) && UT.zone == zone && UT.CanPass(null, NT, 0, 0)) //If we find a neighboring tile that is in the same zone, check if we need to rebuild
- consider_rebuild = 1
- break
- if(consider_rebuild)
- air_master.zones_needing_rebuilt.Add(zone) //Gotta check if we need to rebuild, dammit
- else
- zone.RemoveTurf(NT) //Not adjacent to anything, and unsimulated. Goodbye~
-
- //To make a closed connection through closed door.
- ZConnect(T, src)
-
- //If I block air.
- else if(T.zone && !air_master.zones_needing_rebuilt.Find(T.zone))
- var/turf/NT = get_step(src, reverse_direction(direction))
-
- //If I am splitting a zone, rebuild.
- if(istype(NT,/turf/simulated) && (NT in T.zone.contents || (NT.zone && T in NT.zone.contents)))
- air_master.zones_needing_rebuilt.Add(T.zone)
-
- //If NT is unsimulated, parse if I should remove it or rebuild.
- else if(istype(NT) && NT in T.zone.unsimulated_tiles)
- var/consider_rebuild = 0
- for(var/d in cardinal)
- var/turf/UT = get_step(NT,d)
- if(istype(UT, /turf/simulated) && UT.zone == T.zone && UT.CanPass(null, NT, 0, 0)) //If we find a neighboring tile that is in the same zone, check if we need to rebuild
- consider_rebuild = 1
- break
-
- //Needs rebuilt.
- if(consider_rebuild)
- air_master.zones_needing_rebuilt.Add(T.zone)
-
- //Not adjacent to anything, and unsimulated. Goodbye~
- else
- T.zone.RemoveTurf(NT)
-
- else
- //Produce connection through open door.
- ZConnect(src,T)
-
- //Something like a wall was built, changing the geometry.
- else if(air_directions_archived & direction || unsim_directions_archived & direction)
- var/turf/NT = get_step(T, direction)
-
- //If the tile is in our own zone, and we cannot connect to it, better rebuild.
- if(istype(NT,/turf/simulated) && NT in zone.contents)
- air_master.zones_needing_rebuilt.Add(zone)
-
- //Parse if we need to remove the tile, or rebuild the zone.
- else if(istype(NT) && NT in zone.unsimulated_tiles)
- var/consider_rebuild = 0
-
- //Loop through all neighboring turfs to see if we should remove the turf or just rebuild.
- for(var/d in cardinal)
- var/turf/UT = get_step(NT,d)
-
- //If we find a neighboring tile that is in the same zone, rebuild
- if(istype(UT, /turf/simulated) && UT.zone == zone && UT.CanPass(null, NT, 0, 0))
- consider_rebuild = 1
- break
-
- //The unsimulated turf is adjacent to another one of our zone's turfs,
- // better rebuild to be sure we didn't get cut in twain
- if(consider_rebuild)
- air_master.zones_needing_rebuilt.Add(NT.zone)
-
- //Not adjacent to anything, and unsimulated. Goodbye~
- else
- zone.RemoveTurf(NT)
-
- return 1
-
-/turf/proc/HasDoor(turf/O)
- //Checks for the presence of doors, used for zone spreading and connection.
- //A positive numerical argument checks only for closed doors.
- //Another turf as an argument checks for windoors between here and there.
- for(var/obj/machinery/door/D in src)
- if(isnum(O) && O)
- if(!D.density) continue
- if(istype(D,/obj/machinery/door/window))
- if(!istype(O))
- continue
- if(D.dir == get_dir(D,O))
- return 1
- else
- return 1
-
-/turf/proc/ZCanPass(turf/simulated/T, var/include_space = 0)
- //Fairly standard pass checks for turfs, objects and directional windows. Also stops at the edge of space.
- if(!istype(T))
- return 0
-
- if(!istype(T) && !include_space)
- return 0
- else
- if(T.blocks_air||blocks_air)
- return 0
-
- for(var/obj/obstacle in src)
- if(istype(obstacle, /obj/machinery/door) && !(obstacle:air_properties_vary_with_direction))
- continue
- if(!obstacle.CanPass(null, T, 1.5, 1))
- return 0
-
- for(var/obj/obstacle in T)
- if(istype(obstacle, /obj/machinery/door) && !(obstacle:air_properties_vary_with_direction))
- continue
- if(!obstacle.CanPass(null, src, 1.5, 1))
- return 0
-
- return 1
-
-/turf/proc/ZAirPass(turf/T)
- //Fairly standard pass checks for turfs, objects and directional windows.
- if(!istype(T))
- return 0
-
- if(T.blocks_air||blocks_air)
- return 0
-
- for(var/obj/obstacle in src)
- if(istype(obstacle, /obj/machinery/door) && !(obstacle:air_properties_vary_with_direction))
- continue
- if(!obstacle.CanPass(null, T, 0, 0))
- return 0
-
- for(var/obj/obstacle in T)
- if(istype(obstacle, /obj/machinery/door) && !(obstacle:air_properties_vary_with_direction))
- continue
- if(!obstacle.CanPass(null, src, 0, 0))
- return 0
-
- return 1
-
-/*UNUSED
-/turf/proc/check_connections()
- //Checks for new connections that can be made.
- for(var/d in cardinal)
- var/turf/simulated/T = get_step(src,d)
- if(istype(T) && ( !T.zone || !T.CanPass(0,src,0,0) ) )
- continue
- if(T.zone != zone)
- ZConnect(src,T)
-
-/turf/proc/check_for_space()
- //Checks for space around the turf.
- for(var/d in cardinal)
- var/turf/T = get_step(src,d)
- if(istype(T,/turf/space) && T.CanPass(0,src,0,0))
- zone.AddSpace(T)
- */
\ No newline at end of file
diff --git a/code/ZAS/ZAS_Zones.dm b/code/ZAS/ZAS_Zones.dm
deleted file mode 100644
index 1726d04f5c4..00000000000
--- a/code/ZAS/ZAS_Zones.dm
+++ /dev/null
@@ -1,906 +0,0 @@
-var/list/DoorDirections = list(NORTH,WEST) //Which directions doors turfs can connect to zones
-var/list/CounterDoorDirections = list(SOUTH,EAST) //Which directions doors turfs can connect to zones
-
-/zone
- var/dbg_output = 0 //Enables debug output.
-
- var/datum/gas_mixture/air //The air contents of the zone.
- var/datum/gas_mixture/archived_air
-
- var/list/contents //All the tiles that are contained in this zone.
- var/list/unsimulated_tiles // Any space tiles in this list will cause air to flow out.
-
- var/datum/gas_mixture/air_unsim //Overall average of the air in connected unsimualted tiles.
- var/unsim_air_needs_update = 0 //Set to 1 on geometry changes, marks air_unsim as needing update.
-
- var/list/connections //connection objects which refer to connections with other zones, e.g. through a door.
- var/list/direct_connections //connections which directly connect two zones.
-
- var/list/connected_zones //Parallels connections, but lists zones to which this one is connected and the number
- //of points they're connected at.
- var/list/closed_connection_zones //Same as connected_zones, but for zones where the door or whatever is closed.
-
- var/last_update = 0
- var/last_rebuilt = 0
- var/status = ZONE_ACTIVE
- var/interactions_with_neighbors = 0
- var/interactions_with_unsim = 0
- var/progress = "nothing"
-
-//CREATION AND DELETION
-/zone/New(turf/start)
- . = ..()
- //Get the turfs that are part of the zone using a floodfill method
- if(istype(start,/list))
- contents = start
- else
- contents = FloodFill(start)
-
- //Change all the zone vars of the turfs, check for space to be added to unsimulated_tiles.
- for(var/turf/T in contents)
- if(T.zone && T.zone != src)
- T.zone.RemoveTurf(T)
- T.zone = src
- if(!istype(T,/turf/simulated))
- AddTurf(T)
-
- //Generate the gas_mixture for use in txhis zone by using the average of the gases
- //defined at startup.
- //Changed to try and find the source of the error.
- air = new
- air.group_multiplier = contents.len
- for(var/turf/simulated/T in contents)
- if(!T.air)
- continue
- air.oxygen += T.air.oxygen / air.group_multiplier
- air.nitrogen += T.air.nitrogen / air.group_multiplier
- air.carbon_dioxide += T.air.carbon_dioxide / air.group_multiplier
- air.toxins += T.air.toxins / air.group_multiplier
- air.temperature += T.air.temperature / air.group_multiplier
- for(var/datum/gas/trace in T.air.trace_gases)
- var/datum/gas/corresponding_gas = locate(trace.type) in air.trace_gases
- if(!corresponding_gas)
- corresponding_gas = new trace.type()
- air.trace_gases.Add(corresponding_gas)
- corresponding_gas.moles += trace.moles
- air.update_values()
-
- //Add this zone to the global list.
- if(air_master)
- air_master.zones.Add(src)
- air_master.active_zones.Add(src)
-
-
-//DO NOT USE. Use the SoftDelete proc.
-/zone/Del()
- //Ensuring the zone list doesn't get clogged with null values.
- for(var/turf/simulated/T in contents)
- RemoveTurf(T)
- air_master.ReconsiderTileZone(T)
-
- if(air_master)
- air_master.AddConnectionToCheck(connections)
-
- air = null
-
- . = ..()
-
-
-//Handles deletion via garbage collection.
-/zone/proc/SoftDelete()
- air = null
-
- if(air_master)
- air_master.zones.Remove(src)
- air_master.active_zones.Remove(src)
- air_master.zones_needing_rebuilt.Remove(src)
- air_master.AddConnectionToCheck(connections)
-
- connections = null
- for(var/connection/C in direct_connections)
- if(C.A.zone == src)
- C.A.zone = null
- if(C.B.zone == src)
- C.B.zone = null
- if(C.zone_A == src)
- C.zone_A = null
- if(C.zone_B == src)
- C.zone_B = null
- direct_connections = null
-
- //Ensuring the zone list doesn't get clogged with null values.
- for(var/turf/simulated/T in contents)
- RemoveTurf(T)
- air_master.ReconsiderTileZone(T)
-
- contents.Cut()
-
- //Removing zone connections and scheduling connection cleanup
- for(var/zone/Z in connected_zones)
- Z.connected_zones.Remove(src)
- if(!Z.connected_zones.len)
- Z.connected_zones = null
-
- if(Z.closed_connection_zones)
- Z.closed_connection_zones.Remove(src)
- if(!Z.closed_connection_zones.len)
- Z.closed_connection_zones = null
-
- connected_zones = null
- closed_connection_zones = null
-
- return 1
-
-
-//ZONE MANAGEMENT FUNCTIONS
-/zone/proc/AddTurf(turf/T)
- //Adds the turf to contents, increases the size of the zone, and sets the zone var.
- if(istype(T, /turf/simulated))
- if(T in contents)
- return
- if(T.zone)
- T.zone.RemoveTurf(T)
- contents += T
- if(air)
- air.group_multiplier++
-
- T.zone = src
-
-///// Z-Level Stuff
- // also add the tile below it if its open space
- if(istype(T, /turf/simulated/floor/open))
- var/turf/simulated/floor/open/T2 = T
- src.AddTurf(T2.floorbelow)
-///// Z-Level Stuff
-
- else
- if(!unsimulated_tiles)
- unsimulated_tiles = list()
- else if(T in unsimulated_tiles)
- return
- unsimulated_tiles += T
- contents -= T
-
- unsim_air_needs_update = 1
-
-/zone/proc/RemoveTurf(turf/T)
- //Same, but in reverse.
- if(istype(T, /turf/simulated))
- if(!(T in contents))
- return
- contents -= T
- if(air)
- air.group_multiplier--
-
- if(T.zone == src)
- T.zone = null
-
- if(!contents.len)
- SoftDelete()
-
- else if(unsimulated_tiles)
- unsimulated_tiles -= T
- if(!unsimulated_tiles.len)
- unsimulated_tiles = null
-
- unsim_air_needs_update = 1
-
-//Updates the air_unsim var
-/zone/proc/UpdateUnsimAvg()
- if(!unsimulated_tiles || !unsimulated_tiles.len) //if we don't have any unsimulated tiles, we can't do much.
- return
-
- if(!unsim_air_needs_update && air_unsim) //if air_unsim doesn't exist, we need to create it even if we don't need an update.
- return
-
- //Tempfix.
- if(!air)
- return
-
- unsim_air_needs_update = 0
-
- if(!air_unsim)
- air_unsim = new /datum/gas_mixture
-
- air_unsim.oxygen = 0
- air_unsim.nitrogen = 0
- air_unsim.carbon_dioxide = 0
- air_unsim.toxins = 0
- air_unsim.temperature = 0
-
- var/correction_ratio = max(1, max(max(1, air.group_multiplier) + 3, 1) + unsimulated_tiles.len) / unsimulated_tiles.len
-
- for(var/turf/T in unsimulated_tiles)
- if(!istype(T, /turf/simulated))
- air_unsim.oxygen += T.oxygen
- air_unsim.carbon_dioxide += T.carbon_dioxide
- air_unsim.nitrogen += T.nitrogen
- air_unsim.toxins += T.toxins
- air_unsim.temperature += T.temperature/unsimulated_tiles.len
-
- //These values require adjustment in order to properly represent a room of the specified size.
- air_unsim.oxygen *= correction_ratio
- air_unsim.carbon_dioxide *= correction_ratio
- air_unsim.nitrogen *= correction_ratio
- air_unsim.toxins *= correction_ratio
-
- air_unsim.group_multiplier = unsimulated_tiles.len
-
- air_unsim.update_values()
- return
-
- //////////////
- //PROCESSING//
-//////////////
-
-#define QUANTIZE(variable) (round(variable,0.0001))
-
-/zone/proc/process()
- . = 1
-
- progress = "problem with: SoftDelete()"
-
- //Deletes zone if empty.
- if(!contents.len)
- return SoftDelete()
-
- progress = "problem with: Rebuild()"
-
- if(!contents.len) //If we got soft deleted.
- return
-
- progress = "problem with: air regeneration"
-
- //Sometimes explosions will cause the air to be deleted for some reason.
- if(!air)
- air = new()
- air.oxygen = MOLES_O2STANDARD
- air.nitrogen = MOLES_N2STANDARD
- air.temperature = T0C
- air.total_moles()
- world.log << "Air object lost in zone. Regenerating."
-
-
- progress = "problem with: ShareSpace()"
-
- if(unsim_air_needs_update)
- unsim_air_needs_update = 0
- UpdateUnsimAvg()
-
- if(unsimulated_tiles)
- if(locate(/turf/simulated) in unsimulated_tiles)
- for(var/turf/simulated/T in unsimulated_tiles)
- unsimulated_tiles -= T
-
- if(unsimulated_tiles.len)
- var/moved_air = ShareSpace(air, air_unsim)
-
- if(!air.compare(air_unsim))
- interactions_with_unsim++
-
- if(moved_air > vsc.airflow_lightest_pressure)
- AirflowSpace(src)
- else
- unsimulated_tiles = null
-
- //Check the graphic.
- progress = "problem with: modifying turf graphics"
-
- air.graphic = 0
- if(air.toxins > MOLES_PLASMA_VISIBLE)
- air.graphic = 1
- else if(air.trace_gases.len)
- var/datum/gas/sleeping_agent = locate(/datum/gas/sleeping_agent) in air.trace_gases
- if(sleeping_agent && (sleeping_agent.moles > 1))
- air.graphic = 2
-
- progress = "problem with an inbuilt byond function: some conditional checks"
-
- //Only run through the individual turfs if there's reason to.
- if(air.graphic != air.graphic_archived || air.temperature > PLASMA_FLASHPOINT)
-
- progress = "problem with: turf/simulated/update_visuals()"
-
- for(var/turf/simulated/S in contents)
- //Update overlays.
- if(air.graphic != air.graphic_archived)
- if(S.HasDoor(1))
- S.update_visuals()
- else
- S.update_visuals(air)
-
- progress = "problem with: item or turf temperature_expose()"
-
- //Expose stuff to extreme heat.
- if(air.temperature > PLASMA_FLASHPOINT)
- for(var/atom/movable/item in S)
- item.temperature_expose(air, air.temperature, CELL_VOLUME)
- S.hotspot_expose(air.temperature, CELL_VOLUME)
-
- progress = "problem with: calculating air graphic"
-
- //Archive graphic so we can know if it's different.
- air.graphic_archived = air.graphic
-
- progress = "problem with: calculating air temp"
-
- //Ensure temperature does not reach absolute zero.
- air.temperature = max(TCMB,air.temperature)
-
- progress = "problem with an inbuilt byond function: length(connections)"
-
- //Handle connections to other zones.
- if(length(connections))
-
- progress = "problem with: ZMerge(), a couple of misc procs"
-
- if(length(direct_connections))
- for(var/connection/C in direct_connections)
-
- //Do merging if conditions are met. Specifically, if there's a non-door connection
- //to somewhere with space, the zones are merged regardless of equilibrium, to speed
- //up spacing in areas with double-plated windows.
- if(C.A.zone && C.B.zone)
- if(C.A.zone.air.compare(C.B.zone.air) || unsimulated_tiles)
- ZMerge(C.A.zone,C.B.zone)
-
- progress = "problem with: ShareRatio(), Airflow(), a couple of misc procs"
-
- //Share some
- for(var/zone/Z in connected_zones)
- //If that zone has already processed, skip it.
- if(Z.last_update > last_update)
- continue
-
- //Handle adjacent zones that are sleeping
- if(Z.status == ZONE_SLEEPING)
- if(air.compare(Z.air))
- continue
-
- else
- Z.SetStatus(ZONE_ACTIVE)
-
- if(air && Z.air)
- //Ensure we're not doing pointless calculations on equilibrium zones.
- if(!air.compare(Z.air))
- if(abs(Z.air.return_pressure() - air.return_pressure()) > vsc.airflow_lightest_pressure)
- Airflow(src,Z)
- var/unsimulated_boost = 0
- if(unsimulated_tiles)
- unsimulated_boost += unsimulated_tiles.len
- if(Z.unsimulated_tiles)
- unsimulated_boost += Z.unsimulated_tiles.len
- unsimulated_boost = max(0, min(3, unsimulated_boost))
- ShareRatio( air , Z.air , connected_zones[Z] + unsimulated_boost)
-
- Z.interactions_with_neighbors++
- interactions_with_neighbors++
-
- if(!vsc.connection_insulation)
- for(var/zone/Z in closed_connection_zones)
- //If that zone has already processed, skip it.
- if(Z.last_update > last_update || !Z.air)
- continue
-
- var/handle_temperature = abs(air.temperature - Z.air.temperature) > vsc.connection_temperature_delta
-
- if(Z.status == ZONE_SLEEPING)
- if (handle_temperature)
- Z.SetStatus(ZONE_ACTIVE)
- else
- continue
-
- if(air && Z.air)
- if( handle_temperature )
- ShareHeat(air, Z.air, closed_connection_zones[Z])
-
- Z.interactions_with_neighbors++
- interactions_with_neighbors++
-
- if(!interactions_with_neighbors && !interactions_with_unsim)
- SetStatus(ZONE_SLEEPING)
-
- interactions_with_neighbors = 0
- interactions_with_unsim = 0
-
- progress = "all components completed successfully, the problem is not here"
-
-
-/zone/proc/SetStatus(var/new_status)
- if(status == ZONE_SLEEPING && new_status == ZONE_ACTIVE)
- air_master.active_zones.Add(src)
- status = ZONE_ACTIVE
-
- else if(status == ZONE_ACTIVE && new_status == ZONE_SLEEPING)
- air_master.active_zones.Remove(src)
- status = ZONE_SLEEPING
-
- if(unsimulated_tiles && unsimulated_tiles.len)
- UpdateUnsimAvg()
- air.copy_from(air_unsim)
-
- if(!archived_air)
- archived_air = new
- archived_air.copy_from(air)
-
-
-/zone/proc/CheckStatus()
- return status
-
-
-/zone/proc/ActivateIfNeeded()
- if(status == ZONE_ACTIVE) return
-
- var/difference = 0
-
- if(unsimulated_tiles && unsimulated_tiles.len)
- UpdateUnsimAvg()
- if(!air.compare(air_unsim))
- difference = 1
-
- if(!difference)
- for(var/zone/Z in connected_zones) //Check adjacent zones for air difference.
- if(!air.compare(Z.air))
- difference = 1
- break
-
- if(difference) //We have a difference, activate the zone.
- SetStatus(ZONE_ACTIVE)
-
- return
-
-
-/zone/proc/assume_air(var/datum/gas_mixture/giver)
- if(status == ZONE_ACTIVE)
- return air.merge(giver)
-
- else
- if(unsimulated_tiles && unsimulated_tiles.len)
- UpdateUnsimAvg()
- var/datum/gas_mixture/compare_air = new
- compare_air.copy_from(giver)
- compare_air.add(air_unsim)
- compare_air.divide(air.group_multiplier)
-
- if(air_unsim.compare(compare_air))
- return 0
-
- var/result = air.merge(giver)
-
- if(!archived_air.compare(air))
- SetStatus(ZONE_ACTIVE)
- return result
-
-
-/zone/proc/remove_air(var/amount)
- if(status == ZONE_ACTIVE)
- return air.remove(amount)
-
- else
- var/result = air.remove(amount)
-
- if(!archived_air.compare(air))
- SetStatus(ZONE_ACTIVE)
-
- return result
-
- ////////////////
- //Air Movement//
-////////////////
-
-var/list/sharing_lookup_table = list(0.30, 0.40, 0.48, 0.54, 0.60, 0.66)
-
-proc/ShareRatio(datum/gas_mixture/A, datum/gas_mixture/B, connecting_tiles)
- //Shares a specific ratio of gas between mixtures using simple weighted averages.
- var
- //WOOT WOOT TOUCH THIS AND YOU ARE A RETARD
- ratio = sharing_lookup_table[6]
- //WOOT WOOT TOUCH THIS AND YOU ARE A RETARD
-
- size = max(1,A.group_multiplier)
- share_size = max(1,B.group_multiplier)
-
- full_oxy = A.oxygen * size
- full_nitro = A.nitrogen * size
- full_co2 = A.carbon_dioxide * size
- full_plasma = A.toxins * size
-
- full_heat_capacity = A.heat_capacity() * size
-
- s_full_oxy = B.oxygen * share_size
- s_full_nitro = B.nitrogen * share_size
- s_full_co2 = B.carbon_dioxide * share_size
- s_full_plasma = B.toxins * share_size
-
- s_full_heat_capacity = B.heat_capacity() * share_size
-
- oxy_avg = (full_oxy + s_full_oxy) / (size + share_size)
- nit_avg = (full_nitro + s_full_nitro) / (size + share_size)
- co2_avg = (full_co2 + s_full_co2) / (size + share_size)
- plasma_avg = (full_plasma + s_full_plasma) / (size + share_size)
-
- temp_avg = (A.temperature * full_heat_capacity + B.temperature * s_full_heat_capacity) / (full_heat_capacity + s_full_heat_capacity)
-
- //WOOT WOOT TOUCH THIS AND YOU ARE A RETARD
- if(sharing_lookup_table.len >= connecting_tiles) //6 or more interconnecting tiles will max at 42% of air moved per tick.
- ratio = sharing_lookup_table[connecting_tiles]
- //WOOT WOOT TOUCH THIS AND YOU ARE A RETARD
-
- A.oxygen = max(0, (A.oxygen - oxy_avg) * (1-ratio) + oxy_avg )
- A.nitrogen = max(0, (A.nitrogen - nit_avg) * (1-ratio) + nit_avg )
- A.carbon_dioxide = max(0, (A.carbon_dioxide - co2_avg) * (1-ratio) + co2_avg )
- A.toxins = max(0, (A.toxins - plasma_avg) * (1-ratio) + plasma_avg )
-
- A.temperature = max(0, (A.temperature - temp_avg) * (1-ratio) + temp_avg )
-
- B.oxygen = max(0, (B.oxygen - oxy_avg) * (1-ratio) + oxy_avg )
- B.nitrogen = max(0, (B.nitrogen - nit_avg) * (1-ratio) + nit_avg )
- B.carbon_dioxide = max(0, (B.carbon_dioxide - co2_avg) * (1-ratio) + co2_avg )
- B.toxins = max(0, (B.toxins - plasma_avg) * (1-ratio) + plasma_avg )
-
- B.temperature = max(0, (B.temperature - temp_avg) * (1-ratio) + temp_avg )
-
- for(var/datum/gas/G in A.trace_gases)
- var/datum/gas/H = locate(G.type) in B.trace_gases
- if(H)
- var/G_avg = (G.moles*size + H.moles*share_size) / (size+share_size)
- G.moles = (G.moles - G_avg) * (1-ratio) + G_avg
-
- H.moles = (H.moles - G_avg) * (1-ratio) + G_avg
- else
- H = new G.type
- B.trace_gases += H
- var/G_avg = (G.moles*size) / (size+share_size)
- G.moles = (G.moles - G_avg) * (1-ratio) + G_avg
- H.moles = (H.moles - G_avg) * (1-ratio) + G_avg
-
- for(var/datum/gas/G in B.trace_gases)
- var/datum/gas/H = locate(G.type) in A.trace_gases
- if(!H)
- H = new G.type
- A.trace_gases += H
- var/G_avg = (G.moles*size) / (size+share_size)
- G.moles = (G.moles - G_avg) * (1-ratio) + G_avg
- H.moles = (H.moles - G_avg) * (1-ratio) + G_avg
-
- A.update_values()
- B.update_values()
-
- if(A.compare(B)) return 1
- else return 0
-
-proc/ShareSpace(datum/gas_mixture/A, list/unsimulated_tiles, dbg_output)
- //A modified version of ShareRatio for spacing gas at the same rate as if it were going into a large airless room.
- if(!unsimulated_tiles)
- return 0
-
- var
- unsim_oxygen = 0
- unsim_nitrogen = 0
- unsim_co2 = 0
- unsim_plasma = 0
- unsim_heat_capacity = 0
- unsim_temperature = 0
-
- size = max(1,A.group_multiplier)
-
- var/tileslen
- var/share_size
-
- if(istype(unsimulated_tiles, /datum/gas_mixture))
- var/datum/gas_mixture/avg_unsim = unsimulated_tiles
- unsim_oxygen = avg_unsim.oxygen
- unsim_co2 = avg_unsim.carbon_dioxide
- unsim_nitrogen = avg_unsim.nitrogen
- unsim_plasma = avg_unsim.toxins
- unsim_temperature = avg_unsim.temperature
- share_size = max(1, max(size + 3, 1) + avg_unsim.group_multiplier)
- tileslen = avg_unsim.group_multiplier
-
- else if(istype(unsimulated_tiles, /list))
- if(!unsimulated_tiles.len)
- return 0
- // We use the same size for the potentially single space tile
- // as we use for the entire room. Why is this?
- // Short answer: We do not want larger rooms to depressurize more
- // slowly than small rooms, preserving our good old "hollywood-style"
- // oh-shit effect when large rooms get breached, but still having small
- // rooms remain pressurized for long enough to make escape possible.
- share_size = max(1, max(size + 3, 1) + unsimulated_tiles.len)
- var/correction_ratio = share_size / unsimulated_tiles.len
-
- for(var/turf/T in unsimulated_tiles)
- unsim_oxygen += T.oxygen
- unsim_co2 += T.carbon_dioxide
- unsim_nitrogen += T.nitrogen
- unsim_plasma += T.toxins
- unsim_temperature += T.temperature/unsimulated_tiles.len
-
- //These values require adjustment in order to properly represent a room of the specified size.
- unsim_oxygen *= correction_ratio
- unsim_co2 *= correction_ratio
- unsim_nitrogen *= correction_ratio
- unsim_plasma *= correction_ratio
- tileslen = unsimulated_tiles.len
-
- else //invalid input type
- return 0
-
- unsim_heat_capacity = HEAT_CAPACITY_CALCULATION(unsim_oxygen, unsim_co2, unsim_nitrogen, unsim_plasma)
-
- var
- ratio = sharing_lookup_table[6]
-
- old_pressure = A.return_pressure()
-
- full_oxy = A.oxygen * size
- full_nitro = A.nitrogen * size
- full_co2 = A.carbon_dioxide * size
- full_plasma = A.toxins * size
-
- full_heat_capacity = A.heat_capacity() * size
-
- oxy_avg = (full_oxy + unsim_oxygen) / (size + share_size)
- nit_avg = (full_nitro + unsim_nitrogen) / (size + share_size)
- co2_avg = (full_co2 + unsim_co2) / (size + share_size)
- plasma_avg = (full_plasma + unsim_plasma) / (size + share_size)
-
- temp_avg = 0
-
- if((full_heat_capacity + unsim_heat_capacity) > 0)
- temp_avg = (A.temperature * full_heat_capacity + unsim_temperature * unsim_heat_capacity) / (full_heat_capacity + unsim_heat_capacity)
-
- if(sharing_lookup_table.len >= tileslen) //6 or more interconnecting tiles will max at 42% of air moved per tick.
- ratio = sharing_lookup_table[tileslen]
-
- A.oxygen = max(0, (A.oxygen - oxy_avg) * (1 - ratio) + oxy_avg )
- A.nitrogen = max(0, (A.nitrogen - nit_avg) * (1 - ratio) + nit_avg )
- A.carbon_dioxide = max(0, (A.carbon_dioxide - co2_avg) * (1 - ratio) + co2_avg )
- A.toxins = max(0, (A.toxins - plasma_avg) * (1 - ratio) + plasma_avg )
-
- A.temperature = max(TCMB, (A.temperature - temp_avg) * (1 - ratio) + temp_avg )
-
- for(var/datum/gas/G in A.trace_gases)
- var/G_avg = (G.moles * size) / (size + share_size)
- G.moles = (G.moles - G_avg) * (1 - ratio) + G_avg
-
- A.update_values()
-
- return abs(old_pressure - A.return_pressure())
-
-
-proc/ShareHeat(datum/gas_mixture/A, datum/gas_mixture/B, connecting_tiles)
- //This implements a simplistic version of the Stefan-Boltzmann law.
- var/energy_delta = ((A.temperature - B.temperature) ** 4) * 5.6704e-8 * connecting_tiles * 2.5
- var/maximum_energy_delta = max(0, min(A.temperature * A.heat_capacity() * A.group_multiplier, B.temperature * B.heat_capacity() * B.group_multiplier))
- if(maximum_energy_delta > abs(energy_delta))
- if(energy_delta < 0)
- maximum_energy_delta *= -1
- energy_delta = maximum_energy_delta
-
- A.temperature -= energy_delta / (A.heat_capacity() * A.group_multiplier)
- B.temperature += energy_delta / (B.heat_capacity() * B.group_multiplier)
-
- /* This was bad an I feel bad.
- //Shares a specific ratio of gas between mixtures using simple weighted averages.
- var
- //WOOT WOOT TOUCH THIS AND YOU ARE A RETARD
- ratio = sharing_lookup_table[6]
- //WOOT WOOT TOUCH THIS AND YOU ARE A RETARD
-
- full_heat_capacity = A.heat_capacity()
-
- s_full_heat_capacity = B.heat_capacity()
-
- temp_avg = (A.temperature * full_heat_capacity + B.temperature * s_full_heat_capacity) / (full_heat_capacity + s_full_heat_capacity)
-
- //WOOT WOOT TOUCH THIS AND YOU ARE A RETARD
- if(sharing_lookup_table.len >= connecting_tiles) //6 or more interconnecting tiles will max at 42% of air moved per tick.
- ratio = sharing_lookup_table[connecting_tiles]
- //WOOT WOOT TOUCH THIS AND YOU ARE A RETARD
-
- //We need to adjust it to account for the insulation settings.
- ratio *= 1 - vsc.connection_insulation
-
- A.temperature = max(0, (A.temperature - temp_avg) * (1- (ratio / max(1,A.group_multiplier)) ) + temp_avg )
- B.temperature = max(0, (B.temperature - temp_avg) * (1- (ratio / max(1,B.group_multiplier)) ) + temp_avg )
- */
-
- ///////////////////
- //Zone Rebuilding//
-///////////////////
-//Used for updating zone geometry when a zone is cut into two parts.
-
-zone/proc/Rebuild()
- if(last_rebuilt == air_master.current_cycle)
- return
-
- last_rebuilt = air_master.current_cycle
-
- var/list/new_zone_contents = IsolateContents()
- if(new_zone_contents.len == 1)
- return
-
- var/list/current_contents
- var/list/new_zones = list()
-
- contents = new_zone_contents[1]
- air.group_multiplier = contents.len
-
- for(var/identifier in 2 to new_zone_contents.len)
- current_contents = new_zone_contents[identifier]
- var/zone/new_zone = new (current_contents)
- new_zone.air.copy_from(air)
- new_zones += new_zone
-
- for(var/connection/connection in connections)
- connection.Cleanup()
-
- var/turf/simulated/adjacent
-
- for(var/turf/unsimulated in unsimulated_tiles)
- for(var/direction in cardinal)
- adjacent = get_step(unsimulated, direction)
-
- if(istype(adjacent) && adjacent.CanPass(null, unsimulated, 0, 0))
- for(var/zone/zone in new_zones)
- if(adjacent in zone)
- zone.AddTurf(unsimulated)
-
-
-//Implements a two-pass connected component labeling algorithm to determine if the zone is, in fact, split.
-
-/zone/proc/IsolateContents()
- var/list/current_adjacents = list()
- var/adjacent_id
- var/lowest_id
-
- var/list/identical_ids = list()
- var/list/turfs = contents.Copy()
- var/current_identifier = 1
-
- for(var/turf/simulated/current in turfs)
- lowest_id = null
- current_adjacents = list()
-
- for(var/direction in cardinal)
- var/turf/simulated/adjacent = get_step(current, direction)
- if(!current.ZCanPass(adjacent))
- continue
- if(adjacent in turfs)
- current_adjacents += adjacent
- adjacent_id = turfs[adjacent]
-
- if(adjacent_id && (!lowest_id || adjacent_id < lowest_id))
- lowest_id = adjacent_id
-
-/////// Z-Level stuff
- var/turf/controllerlocation = locate(1, 1, current.z)
- for(var/obj/effect/landmark/zcontroller/controller in controllerlocation)
- // upwards
- if(controller.up)
- var/turf/simulated/adjacent = locate(current.x, current.y, controller.up_target)
-
- if(adjacent in turfs && istype(adjacent, /turf/simulated/floor/open))
- current_adjacents += adjacent
- adjacent_id = turfs[adjacent]
-
- if(adjacent_id && (!lowest_id || adjacent_id < lowest_id))
- lowest_id = adjacent_id
-
- // downwards
- if(controller.down && istype(current, /turf/simulated/floor/open))
- var/turf/simulated/adjacent = locate(current.x, current.y, controller.down_target)
-
- if(adjacent in turfs)
- current_adjacents += adjacent
- adjacent_id = turfs[adjacent]
-
- if(adjacent_id && (!lowest_id || adjacent_id < lowest_id))
- lowest_id = adjacent_id
-/////// Z-Level stuff
-
- if(!lowest_id)
- lowest_id = current_identifier++
- identical_ids += lowest_id
-
- for(var/turf/simulated/adjacent in current_adjacents)
- adjacent_id = turfs[adjacent]
- if(adjacent_id != lowest_id)
- if(adjacent_id)
- identical_ids[adjacent_id] = lowest_id
- turfs[adjacent] = lowest_id
- turfs[current] = lowest_id
-
- var/list/final_arrangement = list()
-
- for(var/turf/simulated/current in turfs)
- current_identifier = identical_ids[turfs[current]]
-
- if( current_identifier > final_arrangement.len )
- final_arrangement.len = current_identifier
- final_arrangement[current_identifier] = list(current)
-
- else
- //Sanity check.
- if(!islist(final_arrangement[current_identifier]))
- final_arrangement[current_identifier] = list()
- final_arrangement[current_identifier] += current
-
- //lazy but fast
- final_arrangement.Remove(null)
-
- return final_arrangement
-
-
-/*
- if(!RequiresRebuild())
- return
-
- //Choose a random turf and regenerate the zone from it.
- var/list/new_contents
- var/list/new_unsimulated
-
- var/list/turfs_needing_zones = list()
-
- var/list/zones_to_check_connections = list(src)
-
- if(!locate(/turf/simulated/floor) in contents)
- for(var/turf/simulated/turf in contents)
- air_master.ReconsiderTileZone(turf)
- return SoftDelete()
-
- var/turfs_to_ignore = list()
- if(direct_connections)
- for(var/connection/connection in direct_connections)
- if(connection.A.zone != src)
- turfs_to_ignore += A
- else if(connection.B.zone != src)
- turfs_to_ignore += B
-
- new_unsimulated = ( unsimulated_tiles ? unsimulated_tiles : list() )
-
- //Now, we have allocated the new turfs into proper lists, and we can start actually rebuilding.
-
- //If something isn't carried over, it will need a new zone.
- for(var/turf/T in contents)
- if(!(T in new_contents))
- RemoveTurf(T)
- turfs_needing_zones += T
-
- //Handle addition of new turfs
- for(var/turf/S in new_contents)
- if(!istype(S, /turf/simulated))
- new_unsimulated |= S
- new_contents.Remove(S)
-
- //If something new is added, we need to deal with it seperately.
- else if(!(S in contents) && istype(S, /turf/simulated))
- if(!(S.zone in zones_to_check_connections))
- zones_to_check_connections += S.zone
-
- S.zone.RemoveTurf(S)
- AddTurf(S)
-
- //Handle the addition of new unsimulated tiles.
- unsimulated_tiles = null
-
- if(new_unsimulated.len)
- for(var/turf/S in new_unsimulated)
- if(istype(S, /turf/simulated))
- continue
- for(var/direction in cardinal)
- var/turf/simulated/T = get_step(S,direction)
- if(istype(T) && T.zone && S.CanPass(null, T, 0, 0))
- T.zone.AddTurf(S)
-
- //Finally, handle the orphaned turfs
-
- for(var/turf/simulated/T in turfs_needing_zones)
- if(!T.zone)
- zones_to_check_connections += new /zone(T)
-
- for(var/zone/zone in zones_to_check_connections)
- for(var/connection/C in zone.connections)
- C.Cleanup()*/
-
diff --git a/code/ZAS/Zone.dm b/code/ZAS/Zone.dm
new file mode 100644
index 00000000000..349d0e0582f
--- /dev/null
+++ b/code/ZAS/Zone.dm
@@ -0,0 +1,154 @@
+/*
+
+Overview:
+ Each zone is a self-contained area where gas values would be the same if tile-based equalization were run indefinitely.
+ If you're unfamiliar with ZAS, FEA's air groups would have similar functionality if they didn't break in a stiff breeze.
+
+Class Vars:
+ name - A name of the format "Zone [#]", used for debugging.
+ invalid - True if the zone has been erased and is no longer eligible for processing.
+ needs_update - True if the zone has been added to the update list.
+ edges - A list of edges that connect to this zone.
+ air - The gas mixture that any turfs in this zone will return. Values are per-tile with a group multiplier.
+
+Class Procs:
+ add(turf/simulated/T)
+ Adds a turf to the contents, sets its zone and merges its air.
+
+ remove(turf/simulated/T)
+ Removes a turf, sets its zone to null and erases any gas graphics.
+ Invalidates the zone if it has no more tiles.
+
+ c_merge(zone/into)
+ Invalidates this zone and adds all its former contents to into.
+
+ c_invalidate()
+ Marks this zone as invalid and removes it from processing.
+
+ rebuild()
+ Invalidates the zone and marks all its former tiles for updates.
+
+ add_tile_air(turf/simulated/T)
+ Adds the air contained in T.air to the zone's air supply. Called when adding a turf.
+
+ tick()
+ Called only when the gas content is changed. Archives values and changes gas graphics.
+
+ dbg_data(mob/M)
+ Sends M a printout of important figures for the zone.
+
+*/
+
+
+/zone/var/name
+/zone/var/invalid = 0
+/zone/var/list/contents = list()
+
+/zone/var/needs_update = 0
+
+/zone/var/list/edges = list()
+
+/zone/var/datum/gas_mixture/air = new
+
+/zone/New()
+ air_master.add_zone(src)
+ air.temperature = TCMB
+ air.group_multiplier = 1
+ air.volume = CELL_VOLUME
+
+/zone/proc/add(turf/simulated/T)
+#ifdef ZASDBG
+ ASSERT(!invalid)
+ ASSERT(istype(T))
+ ASSERT(!air_master.has_valid_zone(T))
+#endif
+
+ var/datum/gas_mixture/turf_air = T.return_air()
+ add_tile_air(turf_air)
+ T.zone = src
+ contents.Add(T)
+ T.set_graphic(air.graphic)
+
+/zone/proc/remove(turf/simulated/T)
+#ifdef ZASDBG
+ ASSERT(!invalid)
+ ASSERT(istype(T))
+ ASSERT(T.zone == src)
+ soft_assert(T in contents, "Lists are weird broseph")
+#endif
+ contents.Remove(T)
+ T.zone = null
+ T.set_graphic(0)
+ if(contents.len)
+ air.group_multiplier = contents.len
+ else
+ c_invalidate()
+
+/zone/proc/c_merge(zone/into)
+#ifdef ZASDBG
+ ASSERT(!invalid)
+ ASSERT(istype(into))
+ ASSERT(into != src)
+ ASSERT(!into.invalid)
+#endif
+ c_invalidate()
+ for(var/turf/simulated/T in contents)
+ into.add(T)
+ #ifdef ZASDBG
+ T.dbg(merged)
+ #endif
+
+/zone/proc/c_invalidate()
+ invalid = 1
+ air_master.remove_zone(src)
+ #ifdef ZASDBG
+ for(var/turf/simulated/T in contents)
+ T.dbg(invalid_zone)
+ #endif
+
+/zone/proc/rebuild()
+ if(invalid) return //Short circuit for explosions where rebuild is called many times over.
+ c_invalidate()
+ for(var/turf/simulated/T in contents)
+ //T.dbg(invalid_zone)
+ T.needs_air_update = 0 //Reset the marker so that it will be added to the list.
+ air_master.mark_for_update(T)
+
+/zone/proc/add_tile_air(datum/gas_mixture/tile_air)
+ //air.volume += CELL_VOLUME
+ air.group_multiplier = 1
+ air.multiply(contents.len)
+ air.merge(tile_air)
+ air.divide(contents.len+1)
+ air.group_multiplier = contents.len+1
+
+/zone/proc/tick()
+ air.archive()
+ if(air.check_tile_graphic())
+ for(var/turf/simulated/T in contents)
+ T.set_graphic(air.graphic)
+
+/zone/proc/dbg_data(mob/M)
+ M << name
+ M << "O2: [air.oxygen] N2: [air.nitrogen] CO2: [air.carbon_dioxide] P: [air.toxins]"
+ M << "P: [air.return_pressure()] kPa V: [air.volume]L T: [air.temperature]°K ([air.temperature - T0C]°C)"
+ M << "O2 per N2: [(air.nitrogen ? air.oxygen/air.nitrogen : "N/A")] Moles: [air.total_moles]"
+ M << "Simulated: [contents.len] ([air.group_multiplier])"
+ //M << "Unsimulated: [unsimulated_contents.len]"
+ //M << "Edges: [edges.len]"
+ if(invalid) M << "Invalid!"
+ var/zone_edges = 0
+ var/space_edges = 0
+ var/space_coefficient = 0
+ for(var/connection_edge/E in edges)
+ if(E.type == /connection_edge/zone) zone_edges++
+ else
+ space_edges++
+ space_coefficient += E.coefficient
+ M << "[E:air:return_pressure()]kPa"
+
+ M << "Zone Edges: [zone_edges]"
+ M << "Space Edges: [space_edges] ([space_coefficient] connections)"
+
+ //for(var/turf/T in unsimulated_contents)
+ // M << "[T] at ([T.x],[T.y])"
\ No newline at end of file
diff --git a/code/ZAS/_docs.dm b/code/ZAS/_docs.dm
new file mode 100644
index 00000000000..8d084dc4c95
--- /dev/null
+++ b/code/ZAS/_docs.dm
@@ -0,0 +1,35 @@
+/*
+
+Zone Air System:
+
+This air system divides the station into impermeable areas called zones.
+When something happens, i.e. a door opening or a wall being taken down,
+zones equalize and eventually merge. Making an airtight area closes the connection again.
+
+Control Flow:
+Every air tick:
+ Marked turfs are updated with update_air_properties(), followed by post_update_air_properties().
+ Edges, including those generated by connections in the previous step, are processed. This is where gas is exchanged.
+ Fire is processed.
+ Marked zones have their air archived.
+
+Important Functions:
+
+air_master.mark_for_update(turf)
+ When stuff happens, call this. It works on everything. You basically don't need to worry about any other
+ functions besides CanPass().
+
+Notes for people who used ZAS before:
+ There is no connected_zones anymore.
+ To get the zones that are connected to a zone, use this loop:
+ for(var/connection_edge/zone/edge in zone.edges)
+ var/zone/connected_zone = edge.get_connected_zone(zone)
+
+*/
+
+//#define ZASDBG
+//#define ZLEVELS
+
+#define AIR_BLOCKED 1
+#define ZONE_BLOCKED 2
+#define BLOCKED 3
\ No newline at end of file
diff --git a/code/ZAS/FEA_gas_mixture.dm b/code/ZAS/_gas_mixture.dm
similarity index 100%
rename from code/ZAS/FEA_gas_mixture.dm
rename to code/ZAS/_gas_mixture.dm
diff --git a/code/__HELPERS/global_lists.dm b/code/__HELPERS/global_lists.dm
index 22e6376836e..531dbeb0dba 100644
--- a/code/__HELPERS/global_lists.dm
+++ b/code/__HELPERS/global_lists.dm
@@ -17,6 +17,7 @@ var/global/list/landmarks_list = list() //list of all landmarks created
var/global/list/surgery_steps = list() //list of all surgery steps |BS12
var/global/list/side_effects = list() //list of all medical sideeffects types by thier names |BS12
var/global/list/mechas_list = list() //list of all mechs. Used by hostile mobs target tracking.
+var/global/list/joblist = list() //list of all jobstypes, minus borg and AI
//Languages/species/whitelist.
var/global/list/all_species[0]
@@ -85,6 +86,11 @@ var/global/list/backbaglist = list("Nothing", "Backpack", "Satchel", "Satchel Al
var/datum/medical_effect/M = new T
side_effects[M.name] = T
+ //List of job. I can't believe this was calculated multiple times per tick!
+ paths = typesof(/datum/job) -list(/datum/job,/datum/job/ai,/datum/job/cyborg)
+ for(var/T in paths)
+ var/datum/job/J = new T
+ joblist[J.title] = J
//Languages and species.
paths = typesof(/datum/language)-/datum/language
diff --git a/code/__HELPERS/mobs.dm b/code/__HELPERS/mobs.dm
index 324cfad6323..521f07c35e5 100644
--- a/code/__HELPERS/mobs.dm
+++ b/code/__HELPERS/mobs.dm
@@ -74,4 +74,26 @@ proc/age2agedescription(age)
if(45 to 60) return "middle-aged"
if(60 to 70) return "aging"
if(70 to INFINITY) return "elderly"
- else return "unknown"
\ No newline at end of file
+ else return "unknown"
+
+proc/RoundHealth(health)
+ switch(health)
+ if(100 to INFINITY)
+ return "health100"
+ if(70 to 100)
+ return "health80"
+ if(50 to 70)
+ return "health60"
+ if(30 to 50)
+ return "health40"
+ if(18 to 30)
+ return "health25"
+ if(5 to 18)
+ return "health10"
+ if(1 to 5)
+ return "health1"
+ if(-99 to 0)
+ return "health0"
+ else
+ return "health-100"
+ return "0"
diff --git a/code/__HELPERS/names.dm b/code/__HELPERS/names.dm
index 7dd535a2fe9..ca682b42611 100644
--- a/code/__HELPERS/names.dm
+++ b/code/__HELPERS/names.dm
@@ -200,7 +200,7 @@ var/syndicate_code_response//Code response for traitors.
code_phrase += " "
code_phrase += pick(last_names)
if(2)
- code_phrase += pick(get_all_jobs())//Returns a job.
+ code_phrase += pick(joblist)//Returns a job.
safety -= 1
if(2)
switch(rand(1,2))//Places or things.
@@ -270,7 +270,7 @@ var/syndicate_code_response//Code response for traitors.
if(5)
syndicate_code_phrase += pick("Do we have","Is there","Where is","Where's","Who's")
syndicate_code_phrase += " "
- syndicate_code_phrase += "[pick(get_all_jobs())]"
+ syndicate_code_phrase += "[pick(joblist)]"
syndicate_code_phrase += "?"
switch(choice)
@@ -299,7 +299,7 @@ var/syndicate_code_response//Code response for traitors.
syndicate_code_response += pick(last_names)
else
syndicate_code_response += " the "
- syndicate_code_response += "[pic(get_all_jobs())]"
+ syndicate_code_response += "[pic(joblist)]"
syndicate_code_response += "."
else
syndicate_code_response += pick("*shrug*","*smile*","*blink*","*sigh*","*laugh*","*nod*","*giggle*")
diff --git a/code/controllers/failsafe.dm b/code/controllers/failsafe.dm
index 91449e10c89..bfc0b08fd96 100644
--- a/code/controllers/failsafe.dm
+++ b/code/controllers/failsafe.dm
@@ -45,7 +45,8 @@ var/datum/controller/failsafe/Failsafe
MC_defcon = 0
MC_iteration = controller_iteration
- if(lighting_controller.processing)
+ //Lighting controller now neither processes nor iterates.
+ /*if(lighting_controller.processing)
if(lighting_iteration == lighting_controller.iteration) //master_controller hasn't finished processing in the defined interval
switch(lighting_defcon)
if(0 to 3)
@@ -60,7 +61,7 @@ var/datum/controller/failsafe/Failsafe
lighting_defcon = 0
else
lighting_defcon = 0
- lighting_iteration = lighting_controller.iteration
+ lighting_iteration = lighting_controller.iteration*/
else
MC_defcon = 0
lighting_defcon = 0
diff --git a/code/controllers/master_controller.dm b/code/controllers/master_controller.dm
index 747b3bf2ce2..5ebcd3bfac2 100644
--- a/code/controllers/master_controller.dm
+++ b/code/controllers/master_controller.dm
@@ -78,6 +78,7 @@ datum/controller/game_controller/proc/setup()
if(ticker)
ticker.pregame()
+ new/datum/controller/lighting()
lighting_controller.Initialize()
@@ -259,11 +260,28 @@ datum/controller/game_controller/proc/process_machines()
last_thing_processed = Machine.type
if(Machine.process() != PROCESS_KILL)
if(Machine)
- if(Machine.use_power)
- Machine.auto_use_power()
+
+// if(Machine.use_power)
+// Machine.auto_use_power()
+
i++
continue
machines.Cut(i,i+1)
+ i=1
+ while(i<=active_areas.len)
+ var/area/A = active_areas[i]
+ if(A.powerupdate)
+ A.powerupdate -= 1
+ for(var/obj/machinery/M in A)
+ if(M)
+ if(M.use_power)
+ M.auto_use_power()
+
+ if(A.apc.len)
+ i++
+ continue
+ active_areas.Cut(i,i+1)
+
datum/controller/game_controller/proc/process_objects()
var/i = 1
diff --git a/code/controllers/verbs.dm b/code/controllers/verbs.dm
index 3faa6e4c809..ace54c03a70 100644
--- a/code/controllers/verbs.dm
+++ b/code/controllers/verbs.dm
@@ -19,7 +19,7 @@
feedback_add_details("admin_verb","RFailsafe")
if("Lighting")
new /datum/controller/lighting()
- lighting_controller.process()
+ //lighting_controller.process()
feedback_add_details("admin_verb","RLighting")
if("Supply Shuttle")
supply_shuttle.process()
diff --git a/code/datums/mind.dm b/code/datums/mind.dm
index 8d2a926c8ea..ce47a37a761 100644
--- a/code/datums/mind.dm
+++ b/code/datums/mind.dm
@@ -357,7 +357,7 @@ datum/mind
if(!check_rights(R_ADMIN)) return
if (href_list["role_edit"])
- var/new_role = input("Select new role", "Assigned role", assigned_role) as null|anything in get_all_jobs()
+ var/new_role = input("Select new role", "Assigned role", assigned_role) as null|anything in joblist
if (!new_role) return
assigned_role = new_role
@@ -499,6 +499,9 @@ datum/mind
else if(href_list["implant"])
var/mob/living/carbon/human/H = current
+
+ H.hud_updateflag |= (1 << IMPLOYAL_HUD) // updates that players HUD images so secHUD's pick up they are implanted or not.
+
switch(href_list["implant"])
if("remove")
for(var/obj/item/weapon/implant/loyalty/I in H.contents)
@@ -540,6 +543,8 @@ datum/mind
log_admin("[key_name_admin(usr)] has de-traitor'ed [current].")
else if (href_list["revolution"])
+ current.hud_updateflag |= (1 << SPECIALROLE_HUD)
+
switch(href_list["revolution"])
if("clear")
if(src in ticker.mode.revolutionaries)
@@ -633,6 +638,7 @@ datum/mind
usr << "\red Reequipping revolutionary goes wrong!"
else if (href_list["cult"])
+ current.hud_updateflag |= (1 << SPECIALROLE_HUD)
switch(href_list["cult"])
if("clear")
if(src in ticker.mode.cult)
@@ -682,6 +688,8 @@ datum/mind
usr << "\red Spawning amulet failed!"
else if (href_list["wizard"])
+ current.hud_updateflag |= (1 << SPECIALROLE_HUD)
+
switch(href_list["wizard"])
if("clear")
if(src in ticker.mode.wizards)
@@ -710,6 +718,7 @@ datum/mind
usr << "\blue The objectives for wizard [key] have been generated. You can edit them and anounce manually."
else if (href_list["changeling"])
+ current.hud_updateflag |= (1 << SPECIALROLE_HUD)
switch(href_list["changeling"])
if("clear")
if(src in ticker.mode.changelings)
@@ -744,6 +753,10 @@ datum/mind
domutcheck(current, null)
else if (href_list["nuclear"])
+ var/mob/living/carbon/human/H = current
+
+ current.hud_updateflag |= (1 << SPECIALROLE_HUD)
+
switch(href_list["nuclear"])
if("clear")
if(src in ticker.mode.syndicates)
@@ -773,7 +786,6 @@ datum/mind
if("lair")
current.loc = get_turf(locate("landmark*Syndicate-Spawn"))
if("dressup")
- var/mob/living/carbon/human/H = current
del(H.belt)
del(H.back)
del(H.l_ear)
@@ -800,6 +812,7 @@ datum/mind
usr << "\red No valid nuke found!"
else if (href_list["traitor"])
+ current.hud_updateflag |= (1 << SPECIALROLE_HUD)
switch(href_list["traitor"])
if("clear")
if(src in ticker.mode.traitors)
@@ -882,6 +895,7 @@ datum/mind
current.radiation -= 50
else if (href_list["silicon"])
+ current.hud_updateflag |= (1 << SPECIALROLE_HUD)
switch(href_list["silicon"])
if("unmalf")
if(src in ticker.mode.malf_ai)
diff --git a/code/datums/supplypacks.dm b/code/datums/supplypacks.dm
index 0c677cc83e7..71b7bab0410 100755
--- a/code/datums/supplypacks.dm
+++ b/code/datums/supplypacks.dm
@@ -390,6 +390,15 @@ var/list/all_supply_groups = list("Operations","Security","Hospitality","Enginee
containername = "Glass sheets crate"
group = "Engineering"
+/datum/supply_packs/wood50
+ name = "50 Wooden Planks"
+ contains = list(/obj/item/stack/sheet/wood)
+ amount = 50
+ cost = 10
+ containertype = /obj/structure/closet/crate
+ containername = "Wooden planks crate"
+ group = "Engineering"
+
/datum/supply_packs/electrical
name = "Electrical maintenance crate"
contains = list(/obj/item/weapon/storage/toolbox/electrical,
diff --git a/code/game/area/Space Station 13 areas.dm b/code/game/area/Space Station 13 areas.dm
index a3412bb0054..d8d9ab57197 100755
--- a/code/game/area/Space Station 13 areas.dm
+++ b/code/game/area/Space Station 13 areas.dm
@@ -14,6 +14,7 @@ NOTE: there are two lists of areas in the end of this file: centcom and station
*/
+
/area
var/fire = null
var/atmos = 1
@@ -31,6 +32,8 @@ NOTE: there are two lists of areas in the end of this file: centcom and station
var/eject = null
+ var/debug = 0
+ var/powerupdate = 10 //We give everything 10 ticks to settle out it's power usage.
var/requires_power = 1
var/always_unpowered = 0 //this gets overriden to 1 for space in area/New()
@@ -43,7 +46,7 @@ NOTE: there are two lists of areas in the end of this file: centcom and station
var/used_environ = 0
var/has_gravity = 1
-
+ var/list/apc = list()
var/no_air = null
var/area/master // master area used for power calcluations
// (original area before splitting due to sd_DAL)
@@ -1085,7 +1088,6 @@ var/list/ghostteleportlocs = list()
icon_state = "medbay3"
music = 'sound/ambience/signal.ogg'
-
/area/medical/biostorage
name = "\improper Secondary Storage"
icon_state = "medbay2"
@@ -1111,7 +1113,7 @@ var/list/ghostteleportlocs = list()
icon_state = "patients"
/area/medical/ward
- name = "\improper Medbay Patient Ward"
+ name = "\improper Recovery Ward"
icon_state = "patients"
/area/medical/patient_a
@@ -1126,8 +1128,8 @@ var/list/ghostteleportlocs = list()
name = "\improper Isolation C"
icon_state = "patients"
-/area/medical/iso_access
- name = "\improper Isolation Access"
+/area/medical/patient_wing
+ name = "\improper Patient Wing"
icon_state = "patients"
/area/medical/cmo
@@ -1150,6 +1152,10 @@ var/list/ghostteleportlocs = list()
name = "\improper Virology"
icon_state = "virology"
+/area/medical/virologyaccess
+ name = "\improper Virology Access"
+ icon_state = "virology"
+
/area/medical/morgue
name = "\improper Morgue"
icon_state = "morgue"
@@ -1159,11 +1165,19 @@ var/list/ghostteleportlocs = list()
icon_state = "chem"
/area/medical/surgery
- name = "\improper Surgery"
+ name = "\improper Operating Theatre 1"
+ icon_state = "surgery"
+
+/area/medical/surgery2
+ name = "\improper Operating Theatre 2"
icon_state = "surgery"
/area/medical/surgeryobs
- name = "\improper Surgery Observation"
+ name = "\improper Operation Observation Room"
+ icon_state = "surgery"
+
+/area/medical/surgeryprep
+ name = "\improper Pre-Op Prep Room"
icon_state = "surgery"
/area/medical/cryo
@@ -1183,7 +1197,7 @@ var/list/ghostteleportlocs = list()
icon_state = "cloning"
/area/medical/sleeper
- name = "\improper Medical Treatment Center"
+ name = "\improper Emergency Treatment Centre"
icon_state = "exam_room"
//Security
diff --git a/code/game/area/areas.dm b/code/game/area/areas.dm
index f69b2a35e3f..7a9ac8aa37e 100644
--- a/code/game/area/areas.dm
+++ b/code/game/area/areas.dm
@@ -13,6 +13,8 @@
master = src //moved outside the spawn(1) to avoid runtimes in lighting.dm when it references loc.loc.master ~Carn
uid = ++global_uid
related = list(src)
+ active_areas += src
+ all_areas += src
if(type == /area) // override defaults for space. TODO: make space areas of type /area/space rather than /area
requires_power = 1
@@ -24,20 +26,16 @@
// lighting_state = 4
//has_gravity = 0 // Space has gravity. Because.. because.
- if(requires_power)
- luminosity = 0
- else
+ if(!requires_power)
power_light = 0 //rastaf0
power_equip = 0 //rastaf0
power_environ = 0 //rastaf0
- luminosity = 1
lighting_use_dynamic = 0
..()
// spawn(15)
power_change() // all machines set to current power level, also updates lighting icon
- InitializeLighting()
/area/proc/poweralert(var/state, var/obj/source as obj)
@@ -220,6 +218,7 @@
// called when power status changes
/area/proc/power_change()
+ master.powerupdate = 2
for(var/area/RA in related)
for(var/obj/machinery/M in RA) // for each machine in the area
M.power_change() // reverify power status (to update icons etc.)
diff --git a/code/game/gamemodes/autotraitor/autotraitor.dm b/code/game/gamemodes/autotraitor/autotraitor.dm
index 06b5274a925..43dea8c32fc 100644
--- a/code/game/gamemodes/autotraitor/autotraitor.dm
+++ b/code/game/gamemodes/autotraitor/autotraitor.dm
@@ -135,6 +135,7 @@
newtraitor << "\red ATTENTION: \black It is time to pay your debt to the Syndicate..."
newtraitor << "You are now a traitor."
newtraitor.mind.special_role = "traitor"
+ newtraitor.hud_updateflag |= 1 << SPECIALROLE_HUD
var/obj_count = 1
newtraitor << "\blue Your current objectives:"
if(!config.objectives_disabled)
diff --git a/code/game/gamemodes/cult/cult_items.dm b/code/game/gamemodes/cult/cult_items.dm
index 014371cabf9..4e38f4fe447 100644
--- a/code/game/gamemodes/cult/cult_items.dm
+++ b/code/game/gamemodes/cult/cult_items.dm
@@ -37,7 +37,7 @@
flags = FPRINT|TABLEPASS|HEADCOVERSEYES
armor = list(melee = 30, bullet = 10, laser = 5,energy = 5, bomb = 0, bio = 0, rad = 0)
cold_protection = HEAD
- min_cold_protection_temperature = SPACE_HELMET_MIN_COLD_PROTECITON_TEMPERATURE
+ min_cold_protection_temperature = SPACE_HELMET_MIN_COLD_PROTECTION_TEMPERATURE
siemens_coefficient = 0
diff --git a/code/game/gamemodes/cult/runes.dm b/code/game/gamemodes/cult/runes.dm
index 8c373aa1a78..fdc669a0c40 100644
--- a/code/game/gamemodes/cult/runes.dm
+++ b/code/game/gamemodes/cult/runes.dm
@@ -775,13 +775,9 @@ var/list/sacrificed = list()
return
cultist.buckled = null
if (cultist.handcuffed)
- cultist.handcuffed.loc = cultist.loc
- cultist.handcuffed = null
- cultist.update_inv_handcuffed()
+ cultist.drop_from_inventory(cultist.handcuffed)
if (cultist.legcuffed)
- cultist.legcuffed.loc = cultist.loc
- cultist.legcuffed = null
- cultist.update_inv_legcuffed()
+ cultist.drop_from_inventory(cultist.legcuffed)
if (istype(cultist.wear_mask, /obj/item/clothing/mask/muzzle))
cultist.u_equip(cultist.wear_mask)
if(istype(cultist.loc, /obj/structure/closet)&&cultist.loc:welded)
diff --git a/code/game/gamemodes/gameticker.dm b/code/game/gamemodes/gameticker.dm
index 371db0de19e..f0361f2245c 100644
--- a/code/game/gamemodes/gameticker.dm
+++ b/code/game/gamemodes/gameticker.dm
@@ -148,7 +148,7 @@ var/global/datum/controller/gameticker/ticker
supply_shuttle.process() //Start the supply shuttle regenerating points -- TLE
master_controller.process() //Start master_controller.process()
- lighting_controller.process() //Start processing DynamicAreaLighting updates
+ //lighting_controller.process() //Start processing DynamicAreaLighting updates
for(var/obj/multiz/ladder/L in world) L.connect() //Lazy hackfix for ladders. TODO: move this to an actual controller. ~ Z
diff --git a/code/game/gamemodes/intercept_report.dm b/code/game/gamemodes/intercept_report.dm
index 54c1ac6b079..2ee0fe7d919 100644
--- a/code/game/gamemodes/intercept_report.dm
+++ b/code/game/gamemodes/intercept_report.dm
@@ -213,11 +213,11 @@
if(prob(prob_right_job))
if(correct_person)
if(correct_person:assigned_role=="MODE")
- changeling_job = pick(get_all_jobs())
+ changeling_job = pick(joblist)
else
changeling_job = correct_person:assigned_role
else
- changeling_job = pick(get_all_jobs())
+ changeling_job = pick(joblist)
if(prob(prob_right_dude) && ticker.mode == "changeling")
if(correct_person:assigned_role=="MODE")
changeling_name = correct_person:current
diff --git a/code/game/gamemodes/revolution/revolution.dm b/code/game/gamemodes/revolution/revolution.dm
index 3211c496429..f6199dcb167 100644
--- a/code/game/gamemodes/revolution/revolution.dm
+++ b/code/game/gamemodes/revolution/revolution.dm
@@ -208,6 +208,7 @@
if(rev_mind in revolutionaries)
revolutionaries -= rev_mind
rev_mind.special_role = null
+ rev_mind.current.hud_updateflag |= 1 << SPECIALROLE_HUD
if(beingborged)
rev_mind.current << "\red The frame's firmware detects and deletes your neural reprogramming! You remember nothing from the moment you were flashed until now."
diff --git a/code/game/gamemodes/revolution/rp-revolution.dm b/code/game/gamemodes/revolution/rp-revolution.dm
index 7456e73403b..963579af7c8 100644
--- a/code/game/gamemodes/revolution/rp-revolution.dm
+++ b/code/game/gamemodes/revolution/rp-revolution.dm
@@ -304,4 +304,4 @@ mob/living/carbon/human/proc
else if(choice == "No!")
M << "\red You reject this traitorous cause!"
src << "\red [M] does not support the revolution!"
- M.mind.rev_cooldown = world.time+50
\ No newline at end of file
+ M.mind.rev_cooldown = world.time+50
diff --git a/code/game/gamemodes/revolution/rp_revolution.dm b/code/game/gamemodes/revolution/rp_revolution.dm
index f4504835b3e..70fd1387f04 100644
--- a/code/game/gamemodes/revolution/rp_revolution.dm
+++ b/code/game/gamemodes/revolution/rp_revolution.dm
@@ -116,6 +116,7 @@
if(config.objectives_disabled)
rev_mind.current << "Within the rules, try to act as an opposing force to the crew. Further RP and try to make sure other players have fun! If you are confused or at a loss, always adminhelp, and before taking extreme actions, please try to also contact the administration! Think through your actions and make the roleplay immersive! Please remember all rules aside from those without explicit exceptions apply to antagonists."
update_rev_icons_added(rev_mind)
+ H.hud_updateflag |= 1 << SPECIALROLE_HUD
return 1
/////////////////////////////
diff --git a/code/game/jobs/access.dm b/code/game/jobs/access.dm
index 3edf971d622..a6a5c38d304 100644
--- a/code/game/jobs/access.dm
+++ b/code/game/jobs/access.dm
@@ -433,10 +433,10 @@
rank = src:rank
assignment = src:assignment
- if( rank in get_all_jobs() )
+ if( rank in joblist )
return rank
- if( assignment in get_all_jobs() )
+ if( assignment in joblist )
return assignment
return "Unknown"
@@ -489,7 +489,7 @@ proc/FindNameFromID(var/mob/living/carbon/human/H)
return ID.registered_name
proc/get_all_job_icons() //For all existing HUD icons
- return get_all_jobs() + list("Prisoner")
+ return joblist + list("Prisoner")
/obj/proc/GetJobName() //Used in secHUD icon generation
if (!istype(src, /obj/item/device/pda) && !istype(src,/obj/item/weapon/card/id))
diff --git a/code/game/jobs/job_controller.dm b/code/game/jobs/job_controller.dm
index 2330ebcb748..83603ce838a 100644
--- a/code/game/jobs/job_controller.dm
+++ b/code/game/jobs/job_controller.dm
@@ -439,6 +439,10 @@ var/global/datum/controller/occupations/job_master
var/obj/item/clothing/glasses/G = H.glasses
G.prescription = 1
// H.update_icons()
+
+ H.hud_updateflag |= (1 << ID_HUD)
+ H.hud_updateflag |= (1 << IMPLOYAL_HUD)
+ H.hud_updateflag |= (1 << SPECIALROLE_HUD)
return 1
diff --git a/code/game/machinery/adv_med.dm b/code/game/machinery/adv_med.dm
index bd44ba4e7d2..b7d6ad8ef76 100644
--- a/code/game/machinery/adv_med.dm
+++ b/code/game/machinery/adv_med.dm
@@ -181,10 +181,12 @@
return
return
+/*
+
/obj/machinery/body_scanconsole/process() //not really used right now
if(stat & (NOPOWER|BROKEN))
return
- use_power(250) // power stuff
+ //use_power(250) // power stuff
// var/mob/M //occupant
// if (!( src.status )) //remove this
@@ -200,6 +202,8 @@
// src.updateDialog()
// return
+*/
+
/obj/machinery/body_scanconsole/attack_paw(user as mob)
return src.attack_hand(user)
diff --git a/code/game/machinery/atmoalter/meter.dm b/code/game/machinery/atmoalter/meter.dm
index be382551462..b73e2cbde22 100644
--- a/code/game/machinery/atmoalter/meter.dm
+++ b/code/game/machinery/atmoalter/meter.dm
@@ -10,7 +10,7 @@
var/id
use_power = 1
idle_power_usage = 2
- active_power_usage = 4
+ active_power_usage = 5
/obj/machinery/meter/New()
..()
@@ -30,7 +30,7 @@
icon_state = "meter0"
return 0
- use_power(5)
+ //use_power(5)
var/datum/gas_mixture/environment = target.return_air()
if(!environment)
diff --git a/code/game/machinery/biogenerator.dm b/code/game/machinery/biogenerator.dm
index 5bb6f1559ed..14f0151a211 100644
--- a/code/game/machinery/biogenerator.dm
+++ b/code/game/machinery/biogenerator.dm
@@ -34,7 +34,7 @@
/obj/machinery/biogenerator/attackby(var/obj/item/O as obj, var/mob/user as mob)
if(istype(O, /obj/item/weapon/reagent_containers/glass))
if(beaker)
- user << "\red The biogenerator already occuped."
+ user << "\red The biogenerator is already loaded."
else
user.before_take_item(O)
O.loc = src
diff --git a/code/game/machinery/cloning.dm b/code/game/machinery/cloning.dm
index 6f9d50053b8..6bd2fb6a6f4 100644
--- a/code/game/machinery/cloning.dm
+++ b/code/game/machinery/cloning.dm
@@ -273,7 +273,7 @@
src.locked = 0
if (!src.mess)
icon_state = "pod_0"
- use_power(200)
+ //use_power(200)
return
return
diff --git a/code/game/machinery/computer/card.dm b/code/game/machinery/computer/card.dm
index 911ccace956..1d724b83cd9 100644
--- a/code/game/machinery/computer/card.dm
+++ b/code/game/machinery/computer/card.dm
@@ -108,7 +108,7 @@
header += "
"
var/jobs_all = ""
- var/list/alljobs = (istype(src,/obj/machinery/computer/card/centcom)? get_all_centcom_jobs() : get_all_jobs()) + "Custom"
+ var/list/alljobs = (istype(src,/obj/machinery/computer/card/centcom)? get_all_centcom_jobs() : joblist) + "Custom"
for(var/job in alljobs)
jobs_all += "[replacetext(job, " ", " ")] " //make sure there isn't a line break in the middle of a job
diff --git a/code/game/machinery/computer/security.dm b/code/game/machinery/computer/security.dm
index 479633ff100..5ec442a3dda 100644
--- a/code/game/machinery/computer/security.dm
+++ b/code/game/machinery/computer/security.dm
@@ -501,7 +501,7 @@ What a mess.*/
if ((istype(active1, /datum/data/record) && L.Find(rank)))
temp = "Rank:
"
temp += ""
- for(var/rank in get_all_jobs())
+ for(var/rank in joblist)
temp += "
"
else
@@ -520,11 +520,13 @@ What a mess.*/
if ("Change Rank")
if (active1)
active1.fields["rank"] = href_list["rank"]
- if(href_list["rank"] in get_all_jobs())
+ if(href_list["rank"] in joblist)
active1.fields["real_rank"] = href_list["real_rank"]
if ("Change Criminal Status")
if (active2)
+ for(var/mob/living/carbon/human/H in player_list)
+ H.hud_updateflag |= 1 << WANTED_HUD
switch(href_list["criminal2"])
if("none")
active2.fields["criminal"] = "None"
diff --git a/code/game/machinery/computer/skills.dm b/code/game/machinery/computer/skills.dm
index 0327bb04df7..3068d993011 100644
--- a/code/game/machinery/computer/skills.dm
+++ b/code/game/machinery/computer/skills.dm
@@ -359,7 +359,7 @@ What a mess.*/
if ((istype(active1, /datum/data/record) && L.Find(rank)))
temp = "Rank:
"
temp += ""
- for(var/rank in get_all_jobs())
+ for(var/rank in joblist)
temp += "
"
else
@@ -380,7 +380,7 @@ What a mess.*/
if(PDA_Manifest.len)
PDA_Manifest.Cut()
active1.fields["rank"] = href_list["rank"]
- if(href_list["rank"] in get_all_jobs())
+ if(href_list["rank"] in joblist)
active1.fields["real_rank"] = href_list["real_rank"]
if ("Delete Record (ALL) Execute")
diff --git a/code/game/machinery/doors/airlock.dm b/code/game/machinery/doors/airlock.dm
index ccb53acc440..3168fe4e7d9 100644
--- a/code/game/machinery/doors/airlock.dm
+++ b/code/game/machinery/doors/airlock.dm
@@ -1203,6 +1203,8 @@ About the new airlock wires panel:
var/obj/item/weapon/airlock_electronics/ae
if(!electronics)
ae = new/obj/item/weapon/airlock_electronics( src.loc )
+ if(!src.req_access)
+ src.check_access()
if(src.req_access.len)
ae.conf_access = src.req_access
else if (src.req_one_access.len)
@@ -1329,4 +1331,4 @@ About the new airlock wires panel:
src.locked = 0
src.open()
src.locked = 1
- return
+ return
\ No newline at end of file
diff --git a/code/game/machinery/doors/door.dm b/code/game/machinery/doors/door.dm
index d3f276abe02..950871d983e 100644
--- a/code/game/machinery/doors/door.dm
+++ b/code/game/machinery/doors/door.dm
@@ -268,7 +268,7 @@
for(var/turf/simulated/turf in locs)
update_heat_protection(turf)
- air_master.AddTurfToUpdate(turf)
+ air_master.mark_for_update(turf)
return 1
diff --git a/code/game/machinery/doors/windowdoor.dm b/code/game/machinery/doors/windowdoor.dm
index c8d0d4d110b..61d26340a55 100644
--- a/code/game/machinery/doors/windowdoor.dm
+++ b/code/game/machinery/doors/windowdoor.dm
@@ -18,7 +18,7 @@
if(!air_master)
return 0
- air_master.AddTurfToUpdate(get_turf(src))
+ air_master.mark_for_update(get_turf(src))
return 1
@@ -130,6 +130,8 @@
var/obj/item/weapon/airlock_electronics/ae
if(!electronics)
ae = new/obj/item/weapon/airlock_electronics( src.loc )
+ if(!src.req_access)
+ src.check_access()
if(src.req_access.len)
ae.conf_access = src.req_access
else if (src.req_one_access.len)
@@ -227,6 +229,8 @@
var/obj/item/weapon/airlock_electronics/ae
if(!electronics)
ae = new/obj/item/weapon/airlock_electronics( src.loc )
+ if(!src.req_access)
+ src.check_access()
if(src.req_access.len)
ae.conf_access = src.req_access
else if (src.req_one_access.len)
@@ -342,5 +346,4 @@
/obj/machinery/door/window/brigdoor/southright
dir = SOUTH
icon_state = "rightsecure"
- base_state = "rightsecure"
-
+ base_state = "rightsecure"
diff --git a/code/game/machinery/hydroponics.dm b/code/game/machinery/hydroponics.dm
index a0f8c371e41..97621cd30ab 100644
--- a/code/game/machinery/hydroponics.dm
+++ b/code/game/machinery/hydroponics.dm
@@ -448,7 +448,7 @@ obj/machinery/hydroponics/proc/mutatepest() // Until someone makes a spaceworm,
obj/machinery/hydroponics/attackby(var/obj/item/O as obj, var/mob/user as mob)
//Called when mob user "attacks" it with object O
- if (istype(O, /obj/item/weapon/reagent_containers/glass/bucket))
+ if (istype(O, /obj/item/weapon/reagent_containers/glass))
var/b_amount = O.reagents.get_reagent_amount("water")
if(b_amount > 0 && waterlevel < 100)
if(b_amount + waterlevel > 100)
@@ -456,17 +456,17 @@ obj/machinery/hydroponics/attackby(var/obj/item/O as obj, var/mob/user as mob)
O.reagents.remove_reagent("water", b_amount)
waterlevel += b_amount
playsound(loc, 'sound/effects/slosh.ogg', 25, 1)
- user << "You fill the [src] with [b_amount] units of water."
+ user << "You fill \the [src] with [b_amount] units of water."
- // Toxicity dilutation code. The more water you put in, the lesser the toxin concentration.
+ // Toxicity dilutation code. The more water you put in, the lesser the toxin concentration.
toxic -= round(b_amount/4)
- if (toxic < 0 ) // Make sure it won't go overoboard
+ if (toxic < 0 ) // Make sure it won't go overboard
toxic = 0
else if(waterlevel >= 100)
- user << "\red The [src] is already full."
+ user << "\red \The [src] is already full."
else
- user << "\red The bucket is not filled with water."
+ user << "\red \The [O] is not filled with water."
updateicon()
else if ( istype(O, /obj/item/nutrient) )
@@ -484,7 +484,7 @@ obj/machinery/hydroponics/attackby(var/obj/item/O as obj, var/mob/user as mob)
if (planted)
if (S.mode == 1)
if(!S.reagents.total_volume)
- user << "\red The syringe is empty."
+ user << "\red \The [O] is empty."
return
user << "\red You inject the [myseed.plantname] with a chemical solution."
diff --git a/code/game/machinery/kitchen/smartfridge.dm b/code/game/machinery/kitchen/smartfridge.dm
index d02e5fd4f46..86bcf6e9ab6 100644
--- a/code/game/machinery/kitchen/smartfridge.dm
+++ b/code/game/machinery/kitchen/smartfridge.dm
@@ -14,9 +14,19 @@
var/global/max_n_of_items = 999 // Sorry but the BYOND infinite loop detector doesn't look things over 1000.
var/icon_on = "smartfridge"
var/icon_off = "smartfridge-off"
+ var/icon_panel = "smartfridge-panel"
var/item_quants = list()
var/ispowered = 1 //starts powered
var/isbroken = 0
+ var/seconds_electrified = 0;
+ var/shoot_inventory = 0
+ var/locked = 0
+ var/panel_open = 0 //Hacking a smartfridge
+ var/wires = 7
+ var/const/WIRE_SHOCK = 1
+ var/const/WIRE_SHOOTINV = 2
+ var/const/WIRE_SCANID = 3 //Only used by the secure smartfridge, but required by the cut, mend and pulse procs.
+
/obj/machinery/smartfridge/proc/accept_check(var/obj/item/O as obj)
if(istype(O,/obj/item/weapon/reagent_containers/food/snacks/grown/) || istype(O,/obj/item/seeds/))
@@ -36,15 +46,76 @@
return 1
return 0
-/obj/machinery/smartfridge/extract
+/obj/machinery/smartfridge/secure/extract
name = "\improper Slime Extract Storage"
desc = "A refrigerated storage unit for slime extracts"
+ req_access_txt = "47"
-/obj/machinery/smartfridge/extract/accept_check(var/obj/item/O as obj)
+/obj/machinery/smartfridge/secure/extract/accept_check(var/obj/item/O as obj)
if(istype(O,/obj/item/slime_extract))
return 1
return 0
+/obj/machinery/smartfridge/secure/medbay
+ name = "\improper Refrigerated Medicine Storage"
+ desc = "A refrigerated storage unit for storing medicine and chemicals."
+ icon_state = "smartfridge" //To fix the icon in the map editor.
+ icon_on = "smartfridge_chem"
+ req_one_access_txt = "5;33"
+
+/obj/machinery/smartfridge/secure/medbay/accept_check(var/obj/item/O as obj)
+ if(istype(O,/obj/item/weapon/reagent_containers/glass/))
+ return 1
+ if(istype(O,/obj/item/weapon/storage/pill_bottle/))
+ return 1
+ if(istype(O,/obj/item/weapon/reagent_containers/pill/))
+ return 1
+ return 0
+
+/obj/machinery/smartfridge/secure/virology
+ name = "\improper Refrigerated Virus Storage"
+ desc = "A refrigerated storage unit for storing viral material."
+ req_access_txt = "39"
+ icon_state = "smartfridge_virology"
+ icon_on = "smartfridge_virology"
+ icon_off = "smartfridge_virology-off"
+
+/obj/machinery/smartfridge/secure/virology/accept_check(var/obj/item/O as obj)
+ if(istype(O,/obj/item/weapon/reagent_containers/glass/beaker/vial/))
+ return 1
+ if(istype(O,/obj/item/weapon/virusdish/))
+ return 1
+ return 0
+
+/obj/machinery/smartfridge/chemistry
+ name = "\improper Smart Chemical Storage"
+ desc = "A refrigerated storage unit for medicine and chemical storage."
+
+/obj/machinery/smartfridge/chemistry/accept_check(var/obj/item/O as obj)
+ if(istype(O,/obj/item/weapon/storage/pill_bottle) || istype(O,/obj/item/weapon/reagent_containers))
+ return 1
+ return 0
+
+/obj/machinery/smartfridge/chemistry/virology
+ name = "\improper Smart Virus Storage"
+ desc = "A refrigerated storage unit for volatile sample storage."
+
+
+/obj/machinery/smartfridge/drinks
+ name = "\improper Drink Showcase"
+ desc = "A refrigerated storage unit for tasty tasty alcohol."
+
+/obj/machinery/smartfridge/drinks/accept_check(var/obj/item/O as obj)
+ if(istype(O,/obj/item/weapon/reagent_containers/glass) || istype(O,/obj/item/weapon/reagent_containers/food/drinks) || istype(O,/obj/item/weapon/reagent_containers/food/condiment))
+ return 1
+
+/obj/machinery/smartfridge/process()
+ if(!src.ispowered)
+ return
+ if(src.seconds_electrified > 0)
+ src.seconds_electrified--
+ if(src.shoot_inventory && prob(2))
+ src.throw_item()
/obj/machinery/smartfridge/power_change()
if( powered() )
@@ -111,6 +182,26 @@
updateUsrDialog()
+/obj/machinery/smartfridge/secure/attackby(var/obj/item/O as obj, var/mob/user as mob)
+ if (istype(O, /obj/item/weapon/card/emag))
+ src.emagged = 1
+ user << "You short out the product lock on [src]"
+ return
+ else if(istype(O, /obj/item/weapon/screwdriver))
+ src.panel_open = !src.panel_open
+ user << "You [src.panel_open ? "open" : "close"] the maintenance panel."
+ src.overlays.Cut()
+ if(src.panel_open)
+ src.overlays += image(src.icon, src.icon_panel)
+ src.updateUsrDialog()
+ return
+ else if(istype(O, /obj/item/device/multitool)||istype(O, /obj/item/weapon/wirecutters))
+ if(src.panel_open)
+ attack_hand(user)
+ return
+ ..()
+ return
+
/obj/machinery/smartfridge/attack_paw(mob/user as mob)
return src.attack_hand(user)
@@ -119,6 +210,9 @@
/obj/machinery/smartfridge/attack_hand(mob/user as mob)
user.set_machine(src)
+ if(src.seconds_electrified != 0)
+ if(src.shock(user, 100))
+ return
interact(user)
/*******************
@@ -151,8 +245,28 @@
dat += "
"
dat += ""
+ if(panel_open)
+ //One of the wires does absolutely nothing.
+ var/list/vendwires = list(
+ "Blue" = 1,
+ "Red" = 2,
+ "Black" = 3
+ )
+ dat += "
Access Panel
"
+ for(var/wiredesc in vendwires)
+ var/is_uncut = src.wires & APCWireColorToFlag[vendwires[wiredesc]]
+ dat += "[wiredesc] wire: "
+ if(!is_uncut)
+ dat += "Mend"
+ else
+ dat += "Cut "
+ dat += "Pulse "
+ dat += "
"
+
+ dat += "
"
+ dat += "The orange light is [(src.seconds_electrified == 0) ? "off" : "on"].
"
+ dat += "The red light is [src.shoot_inventory ? "off" : "blinking"].
"
user << browse("
"
+
+ if (contents.len == 0)
+ dat += "No product loaded!"
+ else
+ for (var/O in item_quants)
+ if(item_quants[O] > 0)
+ var/N = item_quants[O]
+ dat += "[capitalize(O)]:"
+ dat += " [N] "
+ dat += "Vend "
+ if(N > 5)
+ dat += "(x5)"
+ if(N > 10)
+ dat += "(x10)"
+ if(N > 25)
+ dat += "(x25)"
+ if(N > 1)
+ dat += "(All)"
+ dat += "
"
+
+ dat += ""
+ if(panel_open)
+ var/list/vendwires = list(
+ "Violet" = 1,
+ "Orange" = 2,
+ "Green" = 3
+ )
+ dat += "
Access Panel
"
+ for(var/wiredesc in vendwires)
+ var/is_uncut = src.wires & APCWireColorToFlag[vendwires[wiredesc]]
+ dat += "[wiredesc] wire: "
+ if(!is_uncut)
+ dat += "Mend"
+ else
+ dat += "Cut "
+ dat += "Pulse "
+ dat += "
"
+
+ dat += "
"
+ dat += "The orange light is [(src.seconds_electrified == 0) ? "off" : "on"].
"
+ //dat += "The red light is [src.shoot_inventory ? "off" : "blinking"].
"
+ dat += "The green light is [src.locked == 1 ? "off" : "[src.locked == -1 ? "blinking" : "on"]"].
"
+ user << browse("