From bc971b01ea49e56765653e4fc0dd37fce281f465 Mon Sep 17 00:00:00 2001 From: Aryn Date: Wed, 19 Feb 2014 15:20:05 -0700 Subject: [PATCH] New ZAS version, stable and efficient. Git broke the everything so it's in one commit. --- baystation12.int | 1 + code/ZAS/Airflow.dm | 170 +----- code/ZAS/Atom.dm | 55 ++ code/ZAS/Connection.dm | 560 +++++------------- code/ZAS/ConnectionGroup.dm | 348 +++++++++++ code/ZAS/Controller.dm | 279 +++++++++ code/ZAS/Debug.dm | 235 +------- code/ZAS/Diagnostic.dm | 233 ++++++++ code/ZAS/FEA_system.dm | 356 ----------- code/ZAS/Fire.dm | 2 +- code/ZAS/Turf.dm | 218 +++++++ code/ZAS/ZAS_Turfs.dm | 344 ----------- code/ZAS/Zone.dm | 128 ++++ .../{FEA_gas_mixture.dm => _gas_mixture.dm} | 0 icons/Testing/Zone.dmi | Bin 0 -> 955 bytes 15 files changed, 1419 insertions(+), 1510 deletions(-) create mode 100644 code/ZAS/Atom.dm create mode 100644 code/ZAS/ConnectionGroup.dm create mode 100644 code/ZAS/Controller.dm create mode 100644 code/ZAS/Diagnostic.dm delete mode 100644 code/ZAS/FEA_system.dm create mode 100644 code/ZAS/Turf.dm delete mode 100644 code/ZAS/ZAS_Turfs.dm create mode 100644 code/ZAS/Zone.dm rename code/ZAS/{FEA_gas_mixture.dm => _gas_mixture.dm} (100%) create mode 100644 icons/Testing/Zone.dmi diff --git a/baystation12.int b/baystation12.int index b82874fded..4be4a21dee 100644 --- a/baystation12.int +++ b/baystation12.int @@ -1,6 +1,7 @@ // BEGIN_INTERNALS /* MAP_ICON_TYPE: 0 +DIR: code AUTO_FILE_DIR: OFF */ // END_INTERNALS diff --git a/code/ZAS/Airflow.dm b/code/ZAS/Airflow.dm index 894dc92c72..390373c5f8 100644 --- a/code/ZAS/Airflow.dm +++ b/code/ZAS/Airflow.dm @@ -1,53 +1,3 @@ -/* - -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. - -*/ - mob/var/tmp/last_airflow_stun = 0 mob/proc/airflow_stun() if(stat == 2) @@ -108,124 +58,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 +248,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 0000000000..2630b99bef --- /dev/null +++ b/code/ZAS/Atom.dm @@ -0,0 +1,55 @@ +//#define ZASDBG + +/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 + +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) + +//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. +turf/c_airblock(turf/other) + #ifdef ZASDBG + ASSERT(isturf(other)) + #endif + if(blocks_air) + return BLOCKED + else + 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 1bb8f9ca54..35970fbc59 100644 --- a/code/ZAS/Connection.dm +++ b/code/ZAS/Connection.dm @@ -1,448 +1,162 @@ -/* -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. -*/ -#define CONNECTION_DIRECT 2 -#define CONNECTION_INDIRECT 1 -#define CONNECTION_CLOSED 0 +#define CONNECTION_DIRECT 2 +#define CONNECTION_SPACE 4 +#define CONNECTION_INVALID 8 -/connection +turf/simulated/var/tmp/connection_manager/connections = new + +connection_manager + var/connection/N + var/connection/S + var/connection/E + var/connection/W + + 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 + + proc/place(connection/c, d) + switch(d) + if(NORTH) N = c + if(SOUTH) S = c + if(EAST) E = c + if(WEST) W = c + + proc/close(d) + if(check(N) && (NORTH & d) && !N.direct()) N.erase() + if(check(S) && (SOUTH & d) && !S.direct()) S.erase() + if(check(E) && (EAST & d) && !E.direct()) E.erase() + if(check(W) && (WEST & d) && !W.direct()) W.erase() + + proc/update_all() + if(check(N)) N.update() + if(check(S)) S.update() + if(check(E)) E.update() + if(check(W)) W.update() + + /*proc/open(d) + if(check(N) && (NORTH & d) && N.indirect()) N.open() + if(check(S) && (SOUTH & d) && S.indirect()) S.open() + if(check(E) && (EAST & d) && E.indirect()) E.open() + if(check(W) && (WEST & d) && W.indirect()) W.open()*/ + + proc/check(connection/c) + return c && c.valid() + + +connection var/turf/simulated/A var/turf/simulated/B + var/zone/zoneA + var/zone/zoneB - var/zone/zone_A - var/zone/zone_B + var/connection_edge/edge - var/indirect = CONNECTION_DIRECT //If the connection is purely indirect, the zones should not join. + var/state = 0 - -/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) + 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 - air_master.turfs_with_connections[A] = list(src) + zoneB = B.zone + edge = air_master.get_edge(A.zone,B.zone) + edge.add_connection(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) + proc/mark_direct() + state |= CONNECTION_DIRECT - if(A.CanPass(null, B, 0, 0)) + proc/mark_indirect() + state &= ~CONNECTION_DIRECT - if(!A.CanPass(null, B, 1.5, 1)) - indirect = CONNECTION_INDIRECT + proc/mark_space() + state |= CONNECTION_SPACE - ConnectZones(A.zone, B.zone, indirect) + proc/direct() + return (state & CONNECTION_DIRECT) - else - ConnectZones(A.zone, B.zone) - indirect = CONNECTION_CLOSED + proc/valid() + return !(state & CONNECTION_INVALID) - 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() + proc/erase() + edge.remove_connection(src) + state |= CONNECTION_INVALID + proc/update() + //world << "Updated, \..." + if(!istype(A,/turf/simulated)) + //world << "Invalid A." + erase() + return -/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) + if(air_master.air_blocked(A,B)) + //world << "Blocked connection." + erase() + return - if(A in air_master.turfs_with_connections) - var/list/connections = air_master.turfs_with_connections[A] - connections.Remove(src) + var/b_is_space = !istype(B,/turf/simulated) - //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(state & CONNECTION_SPACE) + if(!b_is_space) + //world << "Invalid B." + erase() + return + if(A.zone != zoneA) + //world << "Zone changed, \..." + if(!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(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 + //world << "valid." + return - if(B) - if(B.zone && B.zone.connections) - B.zone.connections.Remove(src) - if(!B.zone.connections.len) - B.zone.connections = null + else if(b_is_space) + //world << "Invalid B." + erase() + return - 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 + if(A.zone == B.zone) + //world << "A == B" + erase() + return - //Disconnect zones while handling unusual conditions. - // e.g. loss of a zone on a turf - DisconnectZones(zone_A, zone_B) + if(A.zone != zoneA || (zoneB && (B.zone != zoneB))) - //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)) - 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) + //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 - zone_1.direct_connections += src - - if(!zone_2.direct_connections) - zone_2.direct_connections = list(src) - else - zone_2.direct_connections += src + //world << "erased." + erase() + return - //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)) - 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 - SoftDelete() - 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) - zone_A = A.zone - - if(!B.zone) - 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) - - //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 0000000000..b114e559ae --- /dev/null +++ b/code/ZAS/ConnectionGroup.dm @@ -0,0 +1,348 @@ +connection_edge + var/zone/A + + var/list/connecting_turfs = list() + + var/coefficient = 0 + var/id + + New() + CRASH("Cannot make connection edge without specifications.") + + proc/add_connection(connection/c) + coefficient++ + //world << "Connection added. Coefficient: [coefficient]" + + proc/remove_connection(connection/c) + //world << "Connection removed. Coefficient: [coefficient-1]" + coefficient-- + if(coefficient <= 0) + erase() + + proc/contains_zone(zone/Z) + + proc/erase() + air_master.remove_edge(src) + //world << "Erased." + + proc/tick() + + 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 + var/direct = 0 + + 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]" + + add_connection(connection/c) + . = ..() + connecting_turfs.Add(c.A) + if(c.direct()) direct++ + + remove_connection(connection/c) + connecting_turfs.Remove(c.A) + if(c.direct()) direct-- + . = ..() + + contains_zone(zone/Z) + return A == Z || B == Z + + erase() + A.edges.Remove(src) + B.edges.Remove(src) + . = ..() + + tick() + //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) + +connection_edge/unsimulated + var/turf/B + var/datum/gas_mixture/air + + 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.id] to [B]." + + add_connection(connection/c) + . = ..() + connecting_turfs.Add(c.B) + + remove_connection(connection/c) + connecting_turfs.Remove(c.B) + . = ..() + + contains_zone(zone/Z) + return A == Z + + tick() + //world << "[id]: Tick [air_master.current_cycle]: To [B]!" + //A.air.mimic(B, coefficient) + ShareSpace(A.air,air) + 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) + + +//proc/edge_id(zone/A, zone/B) +// return 52 * A.id + B.id + +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) \ No newline at end of file diff --git a/code/ZAS/Controller.dm b/code/ZAS/Controller.dm new file mode 100644 index 0000000000..a4ff7d6aaf --- /dev/null +++ b/code/ZAS/Controller.dm @@ -0,0 +1,279 @@ +var/datum/controller/air_system/air_master + +var/tick_multiplier = 2 + +var/tolerance_temp = 1 +var/tolerance_kpa = 1 +var/mimic_rate = 2 + +/datum/controller/air_system + //Geometry lists + var/list/zones = list() + var/list/edges = list() + + //Geometry updates lists + var/list/tiles_to_update = list() + var/list/zones_to_update = list() + var/list/active_hotspots = list() + + var/active_zones = 0 + + 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 + + var/next_id = 1 + +/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.c_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.c_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 + + //Rebuild zones. + if(.) + tick_progress = "rebuilding zones" + + //Check sanity on connection objects. + if(.) + tick_progress = "checking/creating connections" + + //for(var/connection/c in connections) + //if(c.valid()) c.tick() + //else connections.Remove(c) + + for(var/connection_edge/edge in edges) + edge.tick() + + //Process zones. + if(.) + tick_progress = "processing 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 + + /*for(var/zone/zone in zones) + zone.tick()*/ + + //Ensure tiles still have zones. + if(.) + tick_progress = "reconsidering zones on turfs" + + //Process fires. + if(.) + tick_progress = "processing fire" + + for(var/obj/fire/fire in active_hotspots) + fire.process() + + if(.) + tick_progress = "success" + +/datum/controller/air_system/proc/new_zone(zone/z) + zones.Add(z) + z.name = "Zone [next_id++]" + mark_zone_update(z) + +/datum/controller/air_system/proc/invalid_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 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 != 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(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.get(a_to_b)) return + if(!space) + if(A.zone == B.zone) return + if(B.connections.get(b_to_a)) return + + var/connection/c = new /connection(A,B) + + A.connections.place(c, a_to_b) + if(!space) 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.Add(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) + if(abs(A.air.return_pressure() - B.air.return_pressure()) > tolerance_kpa) return 0 + if(abs(A.air.temperature - B.air.temperature) > tolerance_temp) return 0 + return 1 + +/datum/controller/air_system/proc/equalize(zone/A, zone/B) + A.air.share(B.air) + mark_zone_update(A) + mark_zone_update(B) + +/datum/controller/air_system/proc/active_zones() + return active_zones + +/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 39f5a7caab..61303ae802 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." +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 - else - src << "Failed to process! ([air_master.tick_progress])" - - -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 +turf/simulated/var/verbose = 0 +turf/simulated/verb/Verbose() + set src in world + verbose = !verbose \ No newline at end of file diff --git a/code/ZAS/Diagnostic.dm b/code/ZAS/Diagnostic.dm new file mode 100644 index 0000000000..07f06a5514 --- /dev/null +++ b/code/ZAS/Diagnostic.dm @@ -0,0 +1,233 @@ +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." + 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 5a386d5959..0000000000 --- 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 ddc06106f3..a564de2836 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/Turf.dm b/code/ZAS/Turf.dm new file mode 100644 index 0000000000..60db8de2f7 --- /dev/null +++ b/code/ZAS/Turf.dm @@ -0,0 +1,218 @@ +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/c_update_air_properties() + var/block = c_airblock(src) + if(block & AIR_BLOCKED) + //dbg(blocked) + return 1 + + for(var/d = 1, d < 16, d *= 2) + + var/turf/unsim = get_step(src, d) + block = unsim.c_airblock(src) + + if(block & AIR_BLOCKED) + //unsim.dbg(air_blocked, turn(180,d)) + 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/c_update_air_properties() + if(zone && zone.invalid) + c_copy_air() + zone = null //Easier than iterating through the list at the zone. + + connections.update_all() + + 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(s_block & ZONE_BLOCKED) + z.remove(src) + else + z.rebuild() + + return 1 + + open_directions = 0 + + var/list/postponed + for(var/d = 1, d < 16, d *= 2) + + 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 + + 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/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 43ea288762..0000000000 --- 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/Zone.dm b/code/ZAS/Zone.dm new file mode 100644 index 0000000000..4e32562bbf --- /dev/null +++ b/code/ZAS/Zone.dm @@ -0,0 +1,128 @@ +zone + var/name + var/invalid = 0 + var/list/contents = list() + var/list/unsimulated_contents = list() + + var/needs_update = 0 + + var/list/edges = list() + + var/datum/gas_mixture/air = new + + New() + air_master.new_zone(src) + air.temperature = TCMB + air.group_multiplier = 1 + air.volume = CELL_VOLUME + + 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) + + + proc/add_unsimulated(turf/T) + #ifdef ZASDBG + ASSERT(!invalid) + ASSERT(istype(T)) + ASSERT(!istype(T,/turf/simulated)) + #endif + unsimulated_contents |= T + + proc/remove(turf/simulated/T) + #ifdef ZASDBG + ASSERT(!invalid) + ASSERT(istype(T)) + ASSERT(T.zone == src) + #endif + contents.Remove(T) + T.zone = null + if(contents.len) + air.group_multiplier = contents.len + else + c_invalidate() + + 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 + into.unsimulated_contents |= unsimulated_contents + + proc/c_invalidate() + invalid = 1 + air_master.invalid_zone(src) + #ifdef ZASDBG + for(var/turf/simulated/T in contents) + T.dbg(invalid_zone) + #endif + + proc/rebuild() + c_invalidate() + for(var/turf/simulated/T in contents) + //T.dbg(invalid_zone) + air_master.mark_for_update(T) + + 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 + + proc/tick() + air.archive() + if(air.check_tile_graphic()) + for(var/turf/simulated/T in contents) + T.set_graphic(air.graphic) + + proc/remove_connection(connection/c) + return + + proc/add_connection(connection/c) + return + + 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 << "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])" + + proc/status() + return 1 \ 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/icons/Testing/Zone.dmi b/icons/Testing/Zone.dmi new file mode 100644 index 0000000000000000000000000000000000000000..1d8ab8516693cc287bfae96341b7294a28458047 GIT binary patch literal 955 zcmeAS@N?(olHy`uVBq!ia0vp^4Is?H1|$#LC7uRSi>g8*N?cNllZ!G7N;32F7#J$% z^qxJ-b;v+~+1>va!kk{Xzw;rk!UG9QWaUzAc#f zMBGQw4S z+x~~oi#{e4BV5k(kcZ(M!vivjOYeEC_6Hg=|2?-Lnc?2*<5%1&rt3vsKKk-s#jm0t zZ|ZXF!+zfVw&mb%VUBc$f|v7|Hpxyqs`LDGAzL_u#UHK-ds)+pS;+_r@BLac>SF86 z!Wnja5pCE9HDyQI_Db8j;@AH^E|rxN4E}YKrM+-A$Cu~;Q*DQn>8z>t(^sgyWSo=# zQ&sI*4bS|JAV&tlr%DB%)`to@9Qh<@@pF5qu)~oeAme+esKb#zK*s#ldJP>vfogt- zN;({Q3=}yWBJFVG3&)&44_EOiB-OC^{j<2rE?{WGq+Y)#lttj8eB)&Mvsp|XJ3x%P ztALEY11IaxyaF<8FZ%s6SGmp)1y?j