mirror of
https://github.com/CHOMPStation2/CHOMPStation2.git
synced 2025-12-10 10:12:45 +00:00
New ZAS version, stable and efficient. Git broke the everything so it's in one commit.
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
// BEGIN_INTERNALS
|
||||
/*
|
||||
MAP_ICON_TYPE: 0
|
||||
DIR: code
|
||||
AUTO_FILE_DIR: OFF
|
||||
*/
|
||||
// END_INTERNALS
|
||||
|
||||
@@ -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
|
||||
55
code/ZAS/Atom.dm
Normal file
55
code/ZAS/Atom.dm
Normal file
@@ -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
|
||||
@@ -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."
|
||||
348
code/ZAS/ConnectionGroup.dm
Normal file
348
code/ZAS/ConnectionGroup.dm
Normal file
@@ -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)
|
||||
279
code/ZAS/Controller.dm
Normal file
279
code/ZAS/Controller.dm
Normal file
@@ -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 << {"<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.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)
|
||||
@@ -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 << "<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
|
||||
|
||||
/client/proc/ZASSettings()
|
||||
set category = "Debug"
|
||||
|
||||
vsc.SetDefault(mob)
|
||||
turf/simulated/var/verbose = 0
|
||||
turf/simulated/verb/Verbose()
|
||||
set src in world
|
||||
verbose = !verbose
|
||||
233
code/ZAS/Diagnostic.dm
Normal file
233
code/ZAS/Diagnostic.dm
Normal file
@@ -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 << "<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*/
|
||||
|
||||
client/proc/ZASSettings()
|
||||
set category = "Debug"
|
||||
|
||||
vsc.SetDefault(mob)
|
||||
@@ -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 << {"<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.
|
||||
|
||||
|
||||
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 << "<font color='red'><b>ERROR IN ATMOS TICKER. Killing air simulation!</font></b>"
|
||||
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)
|
||||
@@ -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)
|
||||
|
||||
|
||||
218
code/ZAS/Turf.dm
Normal file
218
code/ZAS/Turf.dm
Normal file
@@ -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
|
||||
@@ -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)
|
||||
*/
|
||||
128
code/ZAS/Zone.dm
Normal file
128
code/ZAS/Zone.dm
Normal file
@@ -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]<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 << "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
|
||||
BIN
icons/Testing/Zone.dmi
Normal file
BIN
icons/Testing/Zone.dmi
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 955 B |
Reference in New Issue
Block a user