diff --git a/baystation12.dme b/baystation12.dme
index 83a200fc15c..57711d68ba8 100644
--- a/baystation12.dme
+++ b/baystation12.dme
@@ -421,6 +421,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"
@@ -1325,17 +1326,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/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/controllers/master_controller.dm b/code/controllers/master_controller.dm
index 747b3bf2ce2..6a9edcc36a5 100644
--- a/code/controllers/master_controller.dm
+++ b/code/controllers/master_controller.dm
@@ -259,11 +259,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/datums/supplypacks.dm b/code/datums/supplypacks.dm
index 77b1d8db45a..fd846ecdc9d 100755
--- a/code/datums/supplypacks.dm
+++ b/code/datums/supplypacks.dm
@@ -404,6 +404,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 191d47fb17e..fdd840589df 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..f4ce267bd92 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
@@ -220,6 +222,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/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/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/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 289cd495354..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
@@ -346,4 +346,4 @@
/obj/machinery/door/window/brigdoor/southright
dir = SOUTH
icon_state = "rightsecure"
- base_state = "rightsecure"
\ No newline at end of file
+ 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 += "