ZAS updates from Bay in a shitty attempt to fix our lagspikes.

This commit is contained in:
Rob Nelson
2014-04-10 22:29:26 -07:00
parent 31abdccc4c
commit ea94270c84
23 changed files with 3065 additions and 587 deletions

View File

@@ -247,6 +247,7 @@
#include "code\game\gamemodes\cult\ritual.dm"
#include "code\game\gamemodes\cult\runes.dm"
#include "code\game\gamemodes\cult\talisman.dm"
#include "code\game\gamemodes\endgame\endgame.dm"
#include "code\game\gamemodes\events\biomass.dm"
#include "code\game\gamemodes\events\black_hole.dm"
#include "code\game\gamemodes\events\clang.dm"
@@ -1430,17 +1431,21 @@
#include "code\WorkInProgress\Ported\policetape.dm"
#include "code\WorkInProgress\SkyMarshal\officer_stuff.dm"
#include "code\WorkInProgress\SkyMarshal\Ultralight_procs.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\NewSettings.dm"
#include "code\ZAS\Plasma.dm"
#include "code\ZAS\ZAS_Turfs.dm"
#include "code\ZAS\ZAS_Zones.dm"
#include "code\ZAS\Turf.dm"
#include "code\ZAS\Zone.dm"
#include "interface\interface.dm"
#include "interface\skin.dmf"
#include "maps\tgstation.2.1.0.0.1.dmm"

View File

@@ -149,10 +149,12 @@ datum/pipeline
air.merge(air_sample)
//turf_air already modified by equalize_gases()
/*
if(istype(target) && !target.processing && !iscatwalk(target))
if(target.air)
if(target.air.check_tile_graphic())
target.update_visuals(target.air)
*/
if(network)
network.update = 1

View File

@@ -124,6 +124,7 @@ obj/item/check_airflow_movable(n)
if(4,5)
if(n < zas_settings.Get(/datum/ZAS_Setting/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.
@@ -240,7 +241,7 @@ proc/AirflowSpace(zone/A)
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

66
code/ZAS/Atom.dm Normal file
View File

@@ -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

View File

@@ -1,403 +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
var/ref_A
var/ref_B
var/indirect = CONNECTION_DIRECT //If the connection is purely indirect, the zones should not join.
var/last_updated //The tick at which this was last updated.
var/no_zone_count = 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
ref_A = "\ref[A]"
if(!B.zone.connections) B.zone.connections = list()
B.zone.connections += src
zone_B = B.zone
ref_B = "\ref[B]"
if(ref_A in air_master.turfs_with_connections)
var/list/connections = air_master.turfs_with_connections[ref_A]
connections.Add(src)
else
air_master.turfs_with_connections[ref_A] = list(src)
if(ref_B in air_master.turfs_with_connections)
var/list/connections = air_master.turfs_with_connections[ref_B]
connections.Add(src)
else
air_master.turfs_with_connections[ref_B] = list(src)
if(A.CanPass(null, B, 0, 0))
ConnectZones(A.zone, B.zone, 1)
if(A.HasDoor(B) || B.HasDoor(A))
indirect = CONNECTION_INDIRECT
else
ConnectZones(A.zone, B.zone)
indirect = CONNECTION_CLOSED
/connection/var/connection_edge/edge
/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])"
del(src)
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(ref_B in air_master.turfs_with_connections)
var/list/connections = air_master.turfs_with_connections[ref_B]
connections.Remove(src)
/connection/proc/mark_indirect()
state &= ~CONNECTION_DIRECT
//world << "Marked indirect."
if(ref_A in air_master.turfs_with_connections)
var/list/connections = air_master.turfs_with_connections[ref_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
if(A && A.zone && B && B.zone)
DisconnectZones(A.zone, B.zone)
//Finally, preform actual deletion.
. = ..()
/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
//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
/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
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)
del src
//Check sanity: zones are different
if(A.zone == B.zone)
del src
//Check sanity: same turfs as before.
if(ref_A != "\ref[A]" || ref_B != "\ref[B]")
del src
//Handle zones changing on a turf.
if((A.zone && A.zone != zone_A) || (B.zone && B.zone != zone_B))
Sanitize()
//Manage sudden loss of a turfs zone. (e.g. a wall being built)
if(!A.zone || !B.zone)
no_zone_count++
if(no_zone_count >= 5)
//world.log << "Connection removed: [A] or [B] missing a zone."
del src
return 0
return 1
/connection/proc/CheckPassSanity()
//Sanity check, first.
Cleanup()
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, 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
del src
mark_direct()
/connection/proc/Sanitize()
//If the zones change on connected turfs, update it.
var/b_is_space = !istype(B,/turf/simulated)
//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
var/temp_ref = ref_A
ref_A = ref_B
ref_B = temp_ref
if(state & CONNECTION_SPACE)
if(!b_is_space)
//world << "Invalid B."
erase()
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."

434
code/ZAS/ConnectionGroup.dm Normal file
View File

@@ -0,0 +1,434 @@
/*
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 - zas_settings.Get(/datum/ZAS_Setting/airflow_delay))
continue
if(M.airflow_speed)
continue
//Check for knocking people over
if(ismob(M) && differential > zas_settings.Get(/datum/ZAS_Setting/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(M)
// THERE WAS A SPAWN HERE. STOP DOING THIS SHIT. - N3X
if(repelled)
M.RepelAirflowDest(differential/5)
else
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) < zas_settings.Get(/datum/ZAS_Setting/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) < zas_settings.Get(/datum/ZAS_Setting/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)

View File

@@ -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()

321
code/ZAS/Controller.dm Normal file
View File

@@ -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 << {"<font color='red'><b>Geometry initialized in [round(0.1*(world.timeofday-start_time),0.1)] seconds.</b>
Total Simulated Turfs: [simulated_turf_count]
Total Zones: [zones.len]
Total Unsimulated Turfs: [world.maxx*world.maxy*world.maxz - simulated_turf_count]</font>"}
// 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)

View File

@@ -1,124 +1,20 @@
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")
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
/connection_edge/var/dbg_out = 0
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)
for(var/turf/simulated/floor/plating/airless/catwalk/C in unsimulated_tiles)
current_zone_images += image('icons/misc/debug_space.dmi', C, null, TURF_LAYER)
client << "<u>Zone Air Contents</u>"
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 << "<u>Connections: [length(connections)]</u>"
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)
for(var/zone/Z in zones)
if(Z.air == air && Z != src)
var/turf/zloc = pick(Z.contents)
client << "\red Illegal air datum shared by: [zloc.loc.name]"
/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
proc/soft_assert(thing,fail)
if(!thing) message_admins(fail)

238
code/ZAS/Diagnostic.dm Normal file
View File

@@ -0,0 +1,238 @@
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 << "<u>Zone Air Contents</u>"
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 << "<u>Connections: [length(connections)]</u>"
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*/
/* VG - We rolled our own.
client/proc/ZASSettings()
set category = "Debug"
vsc.SetDefault(mob)
*/

View File

@@ -38,9 +38,6 @@ turf/simulated/hotspot_expose(exposed_temperature, exposed_volume, soh)
new /obj/fire(src,1000)
//active_hotspot.just_spawned = (current_cycle < air_master.current_cycle)
//remove just_spawned protection if no longer processing this cell
return igniting
/obj/fire
@@ -78,12 +75,12 @@ turf/simulated/hotspot_expose(exposed_temperature, exposed_volume, soh)
//since the air is processed in fractions, we need to make sure not to have any minuscle residue or
//the amount of moles might get to low for some functions to catch them and thus result in wonky behaviour
if(air_contents.oxygen < 0.001)
if(air_contents.oxygen < 0.1)
air_contents.oxygen = 0
if(air_contents.toxins < 0.001)
if(air_contents.toxins < 0.1)
air_contents.toxins = 0
if(fuel)
if(fuel.moles < 0.001)
if(fuel.moles < 0.1)
air_contents.trace_gases.Remove(fuel)
//check if there is something to combust
@@ -111,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)
@@ -203,7 +200,7 @@ datum/gas_mixture/proc/zburn(obj/effect/decal/cleanable/liquid_fuel/liquid, forc
if(liquid)
//Liquid Fuel
if(liquid.amount <= 0)
if(liquid.amount <= 0.1)
del liquid
else
total_fuel += liquid.amount
@@ -256,34 +253,31 @@ datum/gas_mixture/proc/check_recombustability(obj/effect/decal/cleanable/liquid_
//this is a copy proc to continue a fire after its been started.
var/datum/gas/volatile_fuel/fuel = locate() in trace_gases
var/value = 0
if(oxygen && (toxins || fuel || liquid))
if(liquid)
value = 1
else if (toxins && !value)
value = 1
else if(fuel && !value)
value = 1
return 1
if(toxins >= 0.1)
return 1
if(fuel && fuel.moles >= 0.1)
return 1
return value
return 0
datum/gas_mixture/proc/check_combustability(obj/effect/decal/cleanable/liquid_fuel/liquid)
//this check comes up very often and is thus centralized here to ease adding stuff
var/datum/gas/volatile_fuel/fuel = locate() in trace_gases
var/value = 0
if(oxygen && (toxins || fuel || liquid))
if(liquid)
value = 1
else if (toxins >= 0.7 && !value)
value = 1
else if(fuel && !value)
if(fuel.moles >= 1.4)
value = 1
return 1
if (toxins >= 0.1)
return 1
if(fuel && fuel.moles >= 0.1)
return 1
return value
return 0
datum/gas_mixture/proc/calculate_firelevel(obj/effect/decal/cleanable/liquid_fuel/liquid)
//Calculates the firelevel based on one equation instead of having to do this multiple times in different areas.
@@ -316,24 +310,23 @@ datum/gas_mixture/proc/calculate_firelevel(obj/effect/decal/cleanable/liquid_fue
return max( 0, firelevel)
/mob/living/carbon/human/proc/FireBurn(var/firelevel, var/last_temperature, var/pressure)
// mostly using the old proc from Sky until I can think of something better
/mob/living/proc/FireBurn(var/firelevel, var/last_temperature, var/pressure)
var/mx = 5 * firelevel/zas_settings.Get(/datum/ZAS_Setting/fire_firelevel_multiplier) * min(pressure / ONE_ATMOSPHERE, 1)
apply_damage(2.5*mx, BURN)
/mob/living/carbon/human/FireBurn(var/firelevel, var/last_temperature, var/pressure)
//Burns mobs due to fire. Respects heat transfer coefficients on various body parts.
//Due to TG reworking how fireprotection works, this is kinda less meaningful.
var
head_exposure = 1
chest_exposure = 1
groin_exposure = 1
legs_exposure = 1
arms_exposure = 1
//determine the multiplier
//minimize this for low-pressure enviroments
var/mx = 5 * firelevel/zas_settings.Get(/datum/ZAS_Setting/fire_firelevel_multiplier) * min(pressure / ONE_ATMOSPHERE, 1)
var/head_exposure = 1
var/chest_exposure = 1
var/groin_exposure = 1
var/legs_exposure = 1
var/arms_exposure = 1
//Get heat transfer coefficients for clothing.
//skytodo: kill anyone who breaks things then orders me to fix them
for(var/obj/item/clothing/C in src)
if(l_hand == C || r_hand == C)
continue
@@ -349,6 +342,8 @@ datum/gas_mixture/proc/calculate_firelevel(obj/effect/decal/cleanable/liquid_fue
legs_exposure = 0
if(C.body_parts_covered & ARMS)
arms_exposure = 0
//minimize this for low-pressure enviroments
var/mx = 5 * firelevel/zas_settings.Get(/datum/ZAS_Setting/fire_firelevel_multiplier) * min(pressure / ONE_ATMOSPHERE, 1)
//Always check these damage procs first if fire damage isn't working. They're probably what's wrong.
@@ -359,5 +354,3 @@ datum/gas_mixture/proc/calculate_firelevel(obj/effect/decal/cleanable/liquid_fue
apply_damage(0.6*mx*legs_exposure, BURN, "r_leg", 0, 0, "Fire")
apply_damage(0.4*mx*arms_exposure, BURN, "l_arm", 0, 0, "Fire")
apply_damage(0.4*mx*arms_exposure, BURN, "r_arm", 0, 0, "Fire")
//flash_pain()

241
code/ZAS/Turf.dm Normal file
View File

@@ -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

152
code/ZAS/Zone.dm Normal file
View File

@@ -0,0 +1,152 @@
/*
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
var/invalid = 0
var/list/contents = list()
var/needs_update = 0
var/list/edges = list()
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.graphics)
/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.graphics)
/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]<5D>K ([air.temperature - T0C]<5D>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])"

35
code/ZAS/_docs.dm Normal file
View File

@@ -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

1138
code/ZAS/_gas_mixture.dm Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -31,10 +31,13 @@ datum/controller/game_controller
var/total_cost = 0
var/last_thing_processed
var/mob/list/expensive_mobs = list()
var/rebuild_active_areas = 0
datum/controller/game_controller/New()
//There can be only one master_controller. Out with the old and in with the new.
if(master_controller != src)
log_debug("Rebuilding Master Controller")
if(istype(master_controller))
Recover()
del(master_controller)
@@ -53,15 +56,15 @@ datum/controller/game_controller/New()
datum/controller/game_controller/proc/setup()
world.tick_lag = config.Ticklag
// notify the other process that we started up
socket_talk = new /datum/socket_talk()
// notify the other process that we started up
socket_talk.send_raw("type=startup")
createRandomZlevel()
if(!air_master)
air_master = new /datum/controller/air_system()
air_master.setup()
air_master.Setup()
if(!ticker)
ticker = new /datum/controller/gameticker()
@@ -78,7 +81,8 @@ datum/controller/game_controller/proc/setup()
for(var/i=0, i<max_secret_rooms, i++)
make_mining_asteroid_secret()
//if(config.socket_talk) spawn keepalive()
//if(config.socket_talk)
// keepalive()
spawn(0)
if(ticker)
@@ -136,14 +140,8 @@ datum/controller/game_controller/proc/process()
if(!air_processing_killed)
timer = world.timeofday
last_thing_processed = air_master.type
//air_master.tick()
//air_cost = (world.timeofday - timer) / 10 // this might make atmos slower
// 1. atmos won't process if the game is generally lagged out(no deadlocks)
// 2. if the server frequently crashes during atmos processing we will knowif(!kill_air)
//src.set_debug_state("Air Master")
air_master.current_cycle++
if(!air_master.tick()) //Runtimed.
if(!air_master.Tick()) //Runtimed.
air_master.failed_ticks++
if(air_master.failed_ticks > 5)
world << "<font color='red'><b>RUNTIMES IN ATMOS TICKER. Killing air simulation!</font></b>"
@@ -152,7 +150,8 @@ datum/controller/game_controller/proc/process()
log_admin("ZASALERT: unable run zone/process() -- [air_master.tick_progress]")
air_processing_killed = 1
air_master.failed_ticks = 0
air_cost = (world.timeofday - timer) / 10
air_cost = (world.timeofday - timer) / 10
sleep(breather_ticks)
@@ -241,14 +240,20 @@ datum/controller/game_controller/proc/process()
else
sleep(10)
/datum/controller/game_controller/proc/processMobs()
for (var/mob/Mob in mob_list)
if (Mob)
last_thing_processed = Mob.type
Mob.Life()
datum/controller/game_controller/proc/processMobs()
var/i = 1
expensive_mobs.Cut()
while(i<=mob_list.len)
var/mob/M = mob_list[i]
if(M)
var/clock = world.timeofday
last_thing_processed = M.type
M.Life()
if((world.timeofday - clock) > 1)
expensive_mobs += M
i++
continue
mob_list = mob_list - Mob
mob_list.Cut(i,i+1)
/datum/controller/game_controller/proc/processDiseases()
for (var/datum/disease/Disease in active_diseases)

View File

@@ -0,0 +1,48 @@
/**********************
* ENDGAME STUFF
**********************/
// Universal State
// Handles stuff like space icon_state, constants, etc.
// Essentially a policy manager. Once shit hits the fan, this changes its policies.
// Called by master controller.
var/global/datum/universal_state/universe = new
// Default shit.
/datum/universal_state
// Just for reference, for now.
// Might eventually add an observatory job.
var/name = "Normal"
var/desc = "Nothing seems awry."
// Sets world.turf, replaces all turfs of type /turf/space.
var/space_type = /turf/space
// Replaces all turfs of type /turf/space/transit
var/transit_space_type = /turf/space/transit
// Chance of a floor or wall getting damaged [0-100]
// Simulates stuff getting broken due to molecular bonds decaying.
var/decay_rate = 0
var/escape = 0 // NO ESCAPE
// Actually decay the turf.
/datum/universal_state/proc/DecayTurf(var/turf/T)
if(istype(T,/turf/simulated/wall))
var/turf/simulated/wall/W=T
W.melt()
return
if(istype(T,/turf/simulated/floor))
var/turf/simulated/floor/F=T
// Burnt?
if(!F.burnt)
F.burn_tile()
else
F.ReplaceWithLattice()
return
// Apply changes to a turf
/datum/universal_state/proc/FilterTurf(var/turf/T)
// Does nothing by default

View File

@@ -14,9 +14,9 @@
var/minp=16777216;
var/maxp=0;
for(var/dir in cardinal)
var/turf/T=get_turf(get_step(loc,dir))
var/turf/simulated/T=get_turf(get_step(loc,dir))
var/cp=0
if(T && istype(T,/turf/simulated) && T.zone)
if(T && istype(T) && T.zone)
var/datum/gas_mixture/environment = T.return_air()
cp = environment.return_pressure()
else

View File

@@ -360,7 +360,7 @@
O.layer = 5
O.mouse_opacity = 0
/turf/simulated/wall/proc/thermitemelt(mob/user as mob)
/turf/simulated/wall/proc/thermitemelt(var/mob/user)
if(mineral == "diamond")
return
var/obj/effect/overlay/O = new/obj/effect/overlay( src )
@@ -394,6 +394,21 @@
// F.sd_LumReset() //TODO: ~Carn
return
// Generic wall melting proc.
/turf/simulated/wall/proc/melt(var/mob/user)
if(mineral == "diamond")
return
src.ChangeTurf(/turf/simulated/floor/plating)
var/turf/simulated/floor/F = src
if(!F)
return
F.burn_tile()
F.icon_state = "wall_thermite"
// F.sd_LumReset() //TODO: ~Carn
return
/turf/simulated/wall/meteorhit(obj/M as obj)
if (prob(15) && !rotting)
dismantle_wall()

View File

@@ -241,51 +241,75 @@
if (!N)
return
#ifdef ENABLE_TRI_LEVEL
// Fuck this, for now - N3X
///// Z-Level Stuff ///// This makes sure that turfs are not changed to space when one side is part of a zone
if(N == /turf/space)
var/turf/controller = locate(1, 1, src.z)
for(var/obj/effect/landmark/zcontroller/c in controller)
if(c.down)
var/turf/below = locate(src.x, src.y, c.down_target)
if((air_master.has_valid_zone(below) || air_master.has_valid_zone(src)) && !istype(below, /turf/space)) // dont make open space into space, its pointless and makes people drop out of the station
var/turf/W = src.ChangeTurf(/turf/simulated/floor/open)
var/list/temp = list()
temp += W
c.add(temp,3,1) // report the new open space to the zcontroller
return W
///// Z-Level Stuff
#endif
var/old_lumcount = lighting_lumcount - initial(lighting_lumcount)
//world << "Replacing [src.type] with [N]"
if(connections) connections.erase_all()
if(istype(src,/turf/simulated))
//Yeah, we're just going to rebuild the whole thing.
//Despite this being called a bunch during explosions,
//the zone will only really do heavy lifting once.
var/turf/simulated/S = src
if(S.zone) S.zone.rebuild()
if(ispath(N, /turf/simulated/floor))
//if the old turf had a zone, connect the new turf to it as well - Cael
//Adjusted by SkyMarshal 5/10/13 - The air master will handle the addition of the new turf.
//if(zone)
// zone.RemoveTurf(src)
// if(!zone.CheckStatus())
// zone.SetStatus(ZONE_ACTIVE)
var/turf/simulated/W = new N( locate(src.x, src.y, src.z) )
W.copy_air_from(src)
//W.Assimilate_Air()
W.lighting_lumcount += old_lumcount
if(old_lumcount != W.lighting_lumcount || !accepts_lighting)
if(old_lumcount != W.lighting_lumcount)
W.lighting_changed = 1
lighting_controller.changed_turfs += W
if (istype(W,/turf/simulated/floor))
W.RemoveLattice()
//if the old turf had a zone, connect the new turf to it as well - Cael
if(src.zone)
src.zone.RemoveTurf(src)
W.zone = src.zone
W.zone.AddTurf(W)
for(var/turf/simulated/T in orange(src,1))
air_master.tiles_to_update.Add(T)
if(air_master)
air_master.mark_for_update(src)
W.levelupdate()
return W
else
/*if(istype(src, /turf/simulated) && src.zone)
src.zone.rebuild = 1*/
//if(zone)
// zone.RemoveTurf(src)
// if(!zone.CheckStatus())
// zone.SetStatus(ZONE_ACTIVE)
var/turf/W = new N( locate(src.x, src.y, src.z) )
W.lighting_lumcount += old_lumcount
if(old_lumcount != W.lighting_lumcount || !accepts_lighting)
if(old_lumcount != W.lighting_lumcount)
W.lighting_changed = 1
lighting_controller.changed_turfs += W
if(src.zone)
src.zone.RemoveTurf(src)
W.zone = src.zone
W.zone.AddTurf(W)
if(air_master)
for(var/turf/simulated/T in orange(src,1))
air_master.tiles_to_update.Add(T)
air_master.mark_for_update(src)
W.levelupdate()
return W
@@ -393,4 +417,4 @@
if(!t.density)
if(!LinkBlocked(src, t) && !TurfBlockedNonWindow(t))
L.Add(t)
return L
return L

View File

@@ -601,11 +601,11 @@ var/list/admin_verbs_mod = list(
set category = "Debug"
set name = "Kill Air"
set desc = "Toggle Air Processing"
if(kill_air)
kill_air = 0
if(air_processing_killed)
air_processing_killed = 0
usr << "<b>Enabled air processing.</b>"
else
kill_air = 1
air_processing_killed = 1
usr << "<b>Disabled air processing.</b>"
feedback_add_details("admin_verb","KA") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
log_admin("[key_name(usr)] used 'kill air'.")

Binary file not shown.

Before

Width:  |  Height:  |  Size: 120 KiB

After

Width:  |  Height:  |  Size: 121 KiB