Merge remote-tracking branch 'upstream/dev' into dev

This commit is contained in:
Vetinari
2014-02-21 20:01:33 +11:00
69 changed files with 2240 additions and 2952 deletions

View File

@@ -1325,17 +1325,20 @@
#include "code\WorkInProgress\Ported\policetape.dm" #include "code\WorkInProgress\Ported\policetape.dm"
#include "code\WorkInProgress\SkyMarshal\officer_stuff.dm" #include "code\WorkInProgress\SkyMarshal\officer_stuff.dm"
#include "code\WorkInProgress\SkyMarshal\Ultralight_procs.dm" #include "code\WorkInProgress\SkyMarshal\Ultralight_procs.dm"
#include "code\ZAS\_docs.dm"
#include "code\ZAS\_gas_mixture.dm"
#include "code\ZAS\Airflow.dm" #include "code\ZAS\Airflow.dm"
#include "code\ZAS\Atom.dm"
#include "code\ZAS\Connection.dm" #include "code\ZAS\Connection.dm"
#include "code\ZAS\ConnectionGroup.dm"
#include "code\ZAS\Controller.dm"
#include "code\ZAS\Debug.dm" #include "code\ZAS\Debug.dm"
#include "code\ZAS\FEA_gas_mixture.dm" #include "code\ZAS\Diagnostic.dm"
#include "code\ZAS\FEA_system.dm"
#include "code\ZAS\Fire.dm" #include "code\ZAS\Fire.dm"
#include "code\ZAS\Functions.dm"
#include "code\ZAS\Plasma.dm" #include "code\ZAS\Plasma.dm"
#include "code\ZAS\Turf.dm"
#include "code\ZAS\Variable Settings.dm" #include "code\ZAS\Variable Settings.dm"
#include "code\ZAS\ZAS_Turfs.dm" #include "code\ZAS\Zone.dm"
#include "code\ZAS\ZAS_Zones.dm"
#include "interface\interface.dm" #include "interface\interface.dm"
#include "interface\skin.dmf" #include "interface\skin.dmf"
#include "maps\tgstation2.dmm" #include "maps\tgstation2.dmm"

View File

@@ -100,7 +100,7 @@
if(pressure_checks&2) if(pressure_checks&2)
pressure_delta = min(pressure_delta, (air_contents.return_pressure() - internal_pressure_bound)) pressure_delta = min(pressure_delta, (air_contents.return_pressure() - internal_pressure_bound))
if(pressure_delta > 0) if(pressure_delta > 0.5)
if(air_contents.temperature > 0) if(air_contents.temperature > 0)
var/transfer_moles = pressure_delta*environment.volume/(air_contents.temperature * R_IDEAL_GAS_EQUATION) var/transfer_moles = pressure_delta*environment.volume/(air_contents.temperature * R_IDEAL_GAS_EQUATION)
@@ -118,7 +118,7 @@
if(pressure_checks&2) if(pressure_checks&2)
pressure_delta = min(pressure_delta, (internal_pressure_bound - air_contents.return_pressure())) pressure_delta = min(pressure_delta, (internal_pressure_bound - air_contents.return_pressure()))
if(pressure_delta > 0) if(pressure_delta > 0.5)
if(environment.temperature > 0) if(environment.temperature > 0)
var/transfer_moles = pressure_delta*air_contents.volume/(environment.temperature * R_IDEAL_GAS_EQUATION) var/transfer_moles = pressure_delta*air_contents.volume/(environment.temperature * R_IDEAL_GAS_EQUATION)

View File

@@ -63,7 +63,7 @@ obj/machinery/atmospherics/pipe/attackby(var/obj/item/weapon/W as obj, var/mob/u
return ..() return ..()
if(istype(W,/obj/item/device/pipe_painter)) if(istype(W,/obj/item/device/pipe_painter))
return 1 return 0
if (!istype(W, /obj/item/weapon/wrench)) if (!istype(W, /obj/item/weapon/wrench))
return ..() return ..()
@@ -118,6 +118,7 @@ obj/machinery/atmospherics/pipe/simple
obj/machinery/atmospherics/pipe/simple/New() obj/machinery/atmospherics/pipe/simple/New()
..() ..()
alpha = 255
switch(dir) switch(dir)
if(SOUTH || NORTH) if(SOUTH || NORTH)
initialize_directions = SOUTH|NORTH initialize_directions = SOUTH|NORTH
@@ -220,15 +221,15 @@ obj/machinery/atmospherics/pipe/simple/pipeline_expansion()
obj/machinery/atmospherics/pipe/simple/update_icon() obj/machinery/atmospherics/pipe/simple/update_icon()
if(node1&&node2) if(node1&&node2)
var/C = ""
switch(pipe_color) switch(pipe_color)
if ("red") C = "-r" if ("red") color = COLOR_RED
if ("blue") C = "-b" if ("blue") color = COLOR_BLUE
if ("cyan") C = "-c" if ("cyan") color = COLOR_CYAN
if ("green") C = "-g" if ("green") color = COLOR_GREEN
if ("yellow") C = "-y" if ("yellow") color = "#FFCC00"
if ("purple") C = "-p" if ("purple") color = "#5C1EC0"
icon_state = "intact[C][invisibility ? "-f" : "" ]" if ("grey") color = null
icon_state = "intact[invisibility ? "-f" : "" ]"
//var/node1_direction = get_dir(src, node1) //var/node1_direction = get_dir(src, node1)
//var/node2_direction = get_dir(src, node2) //var/node2_direction = get_dir(src, node2)
@@ -294,42 +295,43 @@ obj/machinery/atmospherics/pipe/simple/visible
obj/machinery/atmospherics/pipe/simple/visible/scrubbers obj/machinery/atmospherics/pipe/simple/visible/scrubbers
name="Scrubbers pipe" name="Scrubbers pipe"
color="#FF0000" color=COLOR_RED
obj/machinery/atmospherics/pipe/simple/visible/supply obj/machinery/atmospherics/pipe/simple/visible/supply
name="Air supply pipe" name="Air supply pipe"
color="#0000FF" color=COLOR_BLUE
obj/machinery/atmospherics/pipe/simple/visible/yellow obj/machinery/atmospherics/pipe/simple/visible/yellow
color="#FFCC00" color="#FFCC00"
obj/machinery/atmospherics/pipe/simple/visible/cyan obj/machinery/atmospherics/pipe/simple/visible/cyan
color="#00FFFF" color=COLOR_CYAN
obj/machinery/atmospherics/pipe/simple/visible/green obj/machinery/atmospherics/pipe/simple/visible/green
color="#00FF00" color=COLOR_GREEN
obj/machinery/atmospherics/pipe/simple/hidden obj/machinery/atmospherics/pipe/simple/hidden
level = 1 level = 1
icon_state = "intact-f" icon_state = "intact-f"
alpha = 192 //set for the benefit of mapping - this is reset to opaque when the pipe is spawned in game
obj/machinery/atmospherics/pipe/simple/hidden/scrubbers obj/machinery/atmospherics/pipe/simple/hidden/scrubbers
name="Scrubbers pipe" name="Scrubbers pipe"
color="#FF0000" color=COLOR_RED
obj/machinery/atmospherics/pipe/simple/hidden/supply obj/machinery/atmospherics/pipe/simple/hidden/supply
name="Air supply pipe" name="Air supply pipe"
color="#0000FF" color=COLOR_BLUE
obj/machinery/atmospherics/pipe/simple/hidden/yellow obj/machinery/atmospherics/pipe/simple/hidden/yellow
color="#FFCC00" color="#FFCC00"
obj/machinery/atmospherics/pipe/simple/hidden/cyan obj/machinery/atmospherics/pipe/simple/hidden/cyan
color="#00FFFF" color=COLOR_CYAN
obj/machinery/atmospherics/pipe/simple/hidden/green obj/machinery/atmospherics/pipe/simple/hidden/green
color="#00FF00" color=COLOR_GREEN
obj/machinery/atmospherics/pipe/simple/insulated obj/machinery/atmospherics/pipe/simple/insulated
@@ -364,6 +366,7 @@ obj/machinery/atmospherics/pipe/manifold
layer = 2.4 //under wires with their 2.44 layer = 2.4 //under wires with their 2.44
obj/machinery/atmospherics/pipe/manifold/New() obj/machinery/atmospherics/pipe/manifold/New()
alpha = 255
switch(dir) switch(dir)
if(NORTH) if(NORTH)
initialize_directions = EAST|SOUTH|WEST initialize_directions = EAST|SOUTH|WEST
@@ -442,15 +445,15 @@ obj/machinery/atmospherics/pipe/manifold/disconnect(obj/machinery/atmospherics/r
obj/machinery/atmospherics/pipe/manifold/update_icon() obj/machinery/atmospherics/pipe/manifold/update_icon()
if(node1&&node2&&node3) if(node1&&node2&&node3)
var/C = ""
switch(pipe_color) switch(pipe_color)
if ("red") C = "-r" if ("red") color = COLOR_RED
if ("blue") C = "-b" if ("blue") color = COLOR_BLUE
if ("cyan") C = "-c" if ("cyan") color = COLOR_CYAN
if ("green") C = "-g" if ("green") color = COLOR_GREEN
if ("yellow") C = "-y" if ("yellow") color = "#FFCC00"
if ("purple") C = "-p" if ("purple") color = "#5C1EC0"
icon_state = "manifold[C][invisibility ? "-f" : ""]" if ("grey") color = null
icon_state = "manifold[invisibility ? "-f" : "" ]"
else else
var/connected = 0 var/connected = 0
@@ -520,48 +523,48 @@ obj/machinery/atmospherics/pipe/manifold/visible
obj/machinery/atmospherics/pipe/manifold/visible/supply obj/machinery/atmospherics/pipe/manifold/visible/supply
name="Air supply pipe" name="Air supply pipe"
color="#0000FF" color=COLOR_BLUE
obj/machinery/atmospherics/pipe/manifold/visible/scrubbers obj/machinery/atmospherics/pipe/manifold/visible/scrubbers
name="Scrubbers pipe" name="Scrubbers pipe"
color="#FF0000" color=COLOR_RED
obj/machinery/atmospherics/pipe/manifold/visible/yellow obj/machinery/atmospherics/pipe/manifold/visible/yellow
color="#FFCC00" color="#FFCC00"
obj/machinery/atmospherics/pipe/manifold/visible/cyan obj/machinery/atmospherics/pipe/manifold/visible/cyan
color="#00FFFF" color=COLOR_CYAN
obj/machinery/atmospherics/pipe/manifold/visible/green obj/machinery/atmospherics/pipe/manifold/visible/green
color="#00FF00" color=COLOR_GREEN
obj/machinery/atmospherics/pipe/manifold/hidden obj/machinery/atmospherics/pipe/manifold/hidden
level = 1 level = 1
icon_state = "manifold-f" icon_state = "manifold-f"
alpha = 192 //set for the benefit of mapping - this is reset to opaque when the pipe is spawned in game
obj/machinery/atmospherics/pipe/manifold/hidden/supply obj/machinery/atmospherics/pipe/manifold/hidden/supply
name="Air supply pipe" name="Air supply pipe"
color="#0000FF" color=COLOR_BLUE
obj/machinery/atmospherics/pipe/manifold/hidden/scrubbers obj/machinery/atmospherics/pipe/manifold/hidden/scrubbers
name="Scrubbers pipe" name="Scrubbers pipe"
color="#FF0000" color = COLOR_RED
obj/machinery/atmospherics/pipe/manifold/hidden/yellow obj/machinery/atmospherics/pipe/manifold/hidden/yellow
color="#FFCC00" color="#FFCC00"
obj/machinery/atmospherics/pipe/manifold/hidden/cyan obj/machinery/atmospherics/pipe/manifold/hidden/cyan
color="#00FFFF" color=COLOR_CYAN
obj/machinery/atmospherics/pipe/manifold/hidden/green obj/machinery/atmospherics/pipe/manifold/hidden/green
color="#00FF00" color=COLOR_GREEN
obj/machinery/atmospherics/pipe/manifold4w obj/machinery/atmospherics/pipe/manifold4w
icon = 'icons/obj/atmospherics/pipe_manifold.dmi' icon = 'icons/obj/atmospherics/pipe_manifold.dmi'
icon_state = "manifold4w-f"
name = "4-way pipe manifold" name = "4-way pipe manifold"
desc = "A manifold composed of regular pipes" desc = "A manifold composed of regular pipes"
@@ -579,6 +582,10 @@ obj/machinery/atmospherics/pipe/manifold4w
level = 1 level = 1
layer = 2.4 //under wires with their 2.44 layer = 2.4 //under wires with their 2.44
obj/machinery/atmospherics/pipe/manifold4w/New()
..()
alpha = 255
obj/machinery/atmospherics/pipe/manifold4w/hide(var/i) obj/machinery/atmospherics/pipe/manifold4w/hide(var/i)
if(level == 1 && istype(loc, /turf/simulated)) if(level == 1 && istype(loc, /turf/simulated))
invisibility = i ? 101 : 0 invisibility = i ? 101 : 0
@@ -651,15 +658,15 @@ obj/machinery/atmospherics/pipe/manifold4w/disconnect(obj/machinery/atmospherics
obj/machinery/atmospherics/pipe/manifold4w/update_icon() obj/machinery/atmospherics/pipe/manifold4w/update_icon()
overlays.Cut() overlays.Cut()
if(node1&&node2&&node3&&node4) if(node1&&node2&&node3&&node4)
var/C = ""
switch(pipe_color) switch(pipe_color)
if ("red") C = "-r" if ("red") color = COLOR_RED
if ("blue") C = "-b" if ("blue") color = COLOR_BLUE
if ("cyan") C = "-c" if ("cyan") color = COLOR_CYAN
if ("green") C = "-g" if ("green") color = COLOR_GREEN
if ("yellow") C = "-y" if ("yellow") color = "#FFCC00"
if ("purple") C = "-p" if ("purple") color = "#5C1EC0"
icon_state = "manifold4w[C][invisibility ? "-f" : ""]" if ("grey") color = null
icon_state = "manifold4w[invisibility ? "-f" : "" ]"
else else
icon_state = "manifold4w_ex" icon_state = "manifold4w_ex"
@@ -712,42 +719,43 @@ obj/machinery/atmospherics/pipe/manifold4w/visible
obj/machinery/atmospherics/pipe/manifold4w/visible/supply obj/machinery/atmospherics/pipe/manifold4w/visible/supply
name="Air supply pipe" name="Air supply pipe"
color="#0000FF" color=COLOR_BLUE
obj/machinery/atmospherics/pipe/manifold4w/visible/scrubbers obj/machinery/atmospherics/pipe/manifold4w/visible/scrubbers
name="Scrubbers pipe" name="Scrubbers pipe"
color="#FF0000" color=COLOR_RED
obj/machinery/atmospherics/pipe/manifold4w/visible/yellow obj/machinery/atmospherics/pipe/manifold4w/visible/yellow
color="#FFCC00" color="#FFCC00"
obj/machinery/atmospherics/pipe/manifold4w/visible/cyan obj/machinery/atmospherics/pipe/manifold4w/visible/cyan
color="#00FFFF" color=COLOR_CYAN
obj/machinery/atmospherics/pipe/manifold4w/visible/green obj/machinery/atmospherics/pipe/manifold4w/visible/green
color="#00FF00" color=COLOR_GREEN
obj/machinery/atmospherics/pipe/manifold4w/hidden obj/machinery/atmospherics/pipe/manifold4w/hidden
level = 1 level = 1
icon_state = "manifold4w-f" icon_state = "manifold4w-f"
alpha = 192 //set for the benefit of mapping - this is reset to opaque when the pipe is spawned in game
obj/machinery/atmospherics/pipe/manifold4w/hidden/supply obj/machinery/atmospherics/pipe/manifold4w/hidden/supply
name="Air supply pipe" name="Air supply pipe"
color="#0000FF" color=COLOR_BLUE
obj/machinery/atmospherics/pipe/manifold4w/hidden/scrubbers obj/machinery/atmospherics/pipe/manifold4w/hidden/scrubbers
name="Scrubbers pipe" name="Scrubbers pipe"
color="#FF0000" color=COLOR_RED
obj/machinery/atmospherics/pipe/manifold4w/hidden/yellow obj/machinery/atmospherics/pipe/manifold4w/hidden/yellow
color="#FFCC00" color="#FFCC00"
obj/machinery/atmospherics/pipe/manifold4w/hidden/cyan obj/machinery/atmospherics/pipe/manifold4w/hidden/cyan
color="#00FFFF" color=COLOR_CYAN
obj/machinery/atmospherics/pipe/manifold4w/hidden/green obj/machinery/atmospherics/pipe/manifold4w/hidden/green
color="#00FF00" color=COLOR_GREEN
obj/machinery/atmospherics/pipe/cap obj/machinery/atmospherics/pipe/cap
@@ -1099,4 +1107,4 @@ obj/machinery/atmospherics/pipe/vent/hide(var/i) //to make the little pipe secti
icon_state = "[i == 1 && istype(loc, /turf/simulated) ? "h" : "" ]intact" icon_state = "[i == 1 && istype(loc, /turf/simulated) ? "h" : "" ]intact"
dir = get_dir(src, node1) dir = get_dir(src, node1)
else else
icon_state = "exposed" icon_state = "exposed"

View File

@@ -1,53 +1,8 @@
/* /*
Contains helper procs for airflow, handled in /connection_group.
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/var/tmp/last_airflow_stun = 0
mob/proc/airflow_stun() mob/proc/airflow_stun()
if(stat == 2) if(stat == 2)
@@ -108,124 +63,6 @@ obj/item/check_airflow_movable(n)
if(4,5) if(4,5)
if(n < vsc.airflow_medium_pressure) return 0 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/turf/airflow_dest
/atom/movable/var/tmp/airflow_speed = 0 /atom/movable/var/tmp/airflow_speed = 0
/atom/movable/var/tmp/airflow_time = 0 /atom/movable/var/tmp/airflow_time = 0
@@ -416,4 +253,4 @@ zone/proc/movables()
for(var/atom/A in T) for(var/atom/A in T)
if(istype(A, /obj/effect) || istype(A, /mob/aiEye)) if(istype(A, /obj/effect) || istype(A, /mob/aiEye))
continue continue
. += A . += A

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

@@ -0,0 +1,57 @@
/atom/var/pressure_resistance = ONE_ATMOSPHERE
atom/proc/CanPass(atom/movable/mover, turf/target, height=1.5, air_group = 0)
//Purpose: Determines if the object (or airflow) can pass this atom.
//Called by: Movement, airflow.
//Inputs: The moving atom (optional), target turf, "height" and air group
//Outputs: Boolean if can pass.
return (!density || !height || air_group)
/turf/CanPass(atom/movable/mover, turf/target, height=1.5,air_group=0)
if(!target) return 0
if(istype(mover)) // turf/Enter(...) will perform more advanced checks
return !density
else // Now, doing more detailed checks for air movement and air group formation
if(target.blocks_air||blocks_air)
return 0
for(var/obj/obstacle in src)
if(!obstacle.CanPass(mover, target, height, air_group))
return 0
if(target != src)
for(var/obj/obstacle in target)
if(!obstacle.CanPass(mover, src, height, air_group))
return 0
return 1
//Basically another way of calling CanPass(null, other, 0, 0) and CanPass(null, other, 1.5, 1).
//Returns:
// 0 - Not blocked
// AIR_BLOCKED - Blocked
// ZONE_BLOCKED - Not blocked, but zone boundaries will not cross.
// BLOCKED - Blocked, zone boundaries will not cross even if opened.
atom/proc/c_airblock(turf/other)
#ifdef ZASDBG
ASSERT(isturf(other))
#endif
return !CanPass(null, other, 0, 0) + 2*!CanPass(null, other, 1.5, 1)
turf/c_airblock(turf/other)
#ifdef ZASDBG
ASSERT(isturf(other))
#endif
if(blocks_air)
return BLOCKED
else
var/result = 0
for(var/atom/movable/M in contents)
result |= M.c_airblock(other)
if(result == BLOCKED) return BLOCKED
return result

View File

@@ -1,448 +1,159 @@
/* #define CONNECTION_DIRECT 2
This object is contained within zone/var/connections. It's generated whenever two turfs from different zones are linked. #define CONNECTION_SPACE 4
Indirect connections will not merge the two zones after they reach equilibrium. #define CONNECTION_INVALID 8
*/
#define CONNECTION_DIRECT 2
#define CONNECTION_INDIRECT 1
#define CONNECTION_CLOSED 0
/connection /turf/simulated/var/tmp/connection_manager/connections = new
var/turf/simulated/A
var/turf/simulated/B
var/zone/zone_A
var/zone/zone_B
var/indirect = CONNECTION_DIRECT //If the connection is purely indirect, the zones should not join.
/connection/New(turf/T,turf/O) /connection_manager/var/connection/N
. = ..() /connection_manager/var/connection/S
/connection_manager/var/connection/E
/connection_manager/var/connection/W
A = T /connection_manager/proc/get(d)
B = O 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
if(A.zone && B.zone) /connection_manager/proc/place(connection/c, d)
if(!A.zone.connections) switch(d)
A.zone.connections = list() if(NORTH) N = c
A.zone.connections += src if(SOUTH) S = c
zone_A = A.zone if(EAST) E = c
if(WEST) W = c
if(!B.zone.connections) /connection_manager/proc/update_all()
B.zone.connections = list() if(check(N)) N.update()
B.zone.connections += src if(check(S)) S.update()
zone_B = B.zone if(check(E)) E.update()
if(check(W)) W.update()
if(A in air_master.turfs_with_connections) /connection_manager/proc/check(connection/c)
var/list/connections = air_master.turfs_with_connections[A] return c && c.valid()
connections.Add(src)
else
air_master.turfs_with_connections[A] = list(src)
if(B in air_master.turfs_with_connections)
var/list/connections = air_master.turfs_with_connections[B]
connections.Add(src)
else
air_master.turfs_with_connections[B] = list(src)
if(A.CanPass(null, B, 0, 0)) /connection/var/turf/simulated/A
/connection/var/turf/simulated/B
/connection/var/zone/zoneA
/connection/var/zone/zoneB
if(!A.CanPass(null, B, 1.5, 1)) /connection/var/connection_edge/edge
indirect = CONNECTION_INDIRECT
ConnectZones(A.zone, B.zone, indirect) /connection/var/state = 0
else
ConnectZones(A.zone, B.zone)
indirect = CONNECTION_CLOSED
/connection/New(turf/simulated/A, turf/simulated/B)
#ifdef ZASDBG
ASSERT(air_master.has_valid_zone(A))
//ASSERT(air_master.has_valid_zone(B))
#endif
src.A = A
src.B = B
zoneA = A.zone
if(!istype(B))
mark_space()
edge = air_master.get_edge(A.zone,B)
edge.add_connection(src)
else 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])" zoneB = B.zone
SoftDelete() edge = air_master.get_edge(A.zone,B.zone)
edge.add_connection(src)
/connection/proc/mark_direct()
edge.remove_connection(src)
state |= CONNECTION_DIRECT
edge.add_connection(src)
/connection/Del() /connection/proc/mark_indirect()
//remove connections from master lists. edge.remove_connection(src)
if(B in air_master.turfs_with_connections) state &= ~CONNECTION_DIRECT
var/list/connections = air_master.turfs_with_connections[B] edge.add_connection(src)
connections.Remove(src)
if(A in air_master.turfs_with_connections) /connection/proc/mark_space()
var/list/connections = air_master.turfs_with_connections[A] state |= CONNECTION_SPACE
connections.Remove(src)
//Remove connection from zones. /connection/proc/direct()
if(A) return (state & CONNECTION_DIRECT)
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)) /connection/proc/valid()
if(zone_A.connections) return !(state & CONNECTION_INVALID)
zone_A.connections.Remove(src)
if(!zone_A.connections.len)
zone_A.connections = null
if(B) /connection/proc/erase()
if(B.zone && B.zone.connections) edge.remove_connection(src)
B.zone.connections.Remove(src) state |= CONNECTION_INVALID
if(!B.zone.connections.len)
B.zone.connections = null
if(istype(zone_B) && (!B || B.zone != zone_B)) /connection/proc/update()
if(zone_B.connections) //world << "Updated, \..."
zone_B.connections.Remove(src) if(!istype(A,/turf/simulated))
if(!zone_B.connections.len) //world << "Invalid A."
zone_B.connections = null erase()
//Disconnect zones while handling unusual conditions.
// e.g. loss of a zone on a turf
DisconnectZones(zone_A, zone_B)
//Finally, preform actual deletion.
. = ..()
/connection/proc/SoftDelete()
//remove connections from master lists.
if(B in air_master.turfs_with_connections)
var/list/connections = air_master.turfs_with_connections[B]
connections.Remove(src)
if(A in air_master.turfs_with_connections)
var/list/connections = air_master.turfs_with_connections[A]
connections.Remove(src)
//Remove connection from zones.
if(A)
if(A.zone && A.zone.connections)
A.zone.connections.Remove(src)
if(!A.zone.connections.len)
A.zone.connections = null
if(istype(zone_A) && (!A || A.zone != zone_A))
if(zone_A.connections)
zone_A.connections.Remove(src)
if(!zone_A.connections.len)
zone_A.connections = null
if(B)
if(B.zone && B.zone.connections)
B.zone.connections.Remove(src)
if(!B.zone.connections.len)
B.zone.connections = null
if(istype(zone_B) && (!B || B.zone != zone_B))
if(zone_B.connections)
zone_B.connections.Remove(src)
if(!zone_B.connections.len)
zone_B.connections = null
//Disconnect zones while handling unusual conditions.
// e.g. loss of a zone on a turf
DisconnectZones(zone_A, zone_B)
/connection/proc/ConnectZones(var/zone/zone_1, var/zone/zone_2, open = 0)
//Sanity checking
if(!istype(zone_1) || !istype(zone_2))
return return
//Handle zones connecting indirectly/directly. var/block_status = air_master.air_blocked(A,B)
if(open) if(block_status & AIR_BLOCKED)
//world << "Blocked connection."
//Create the lists if necessary. erase()
if(!zone_1.connected_zones)
zone_1.connected_zones = list()
if(!zone_2.connected_zones)
zone_2.connected_zones = list()
//Increase the number of connections between zones.
if(zone_2 in zone_1.connected_zones)
zone_1.connected_zones[zone_2]++
else
zone_1.connected_zones += zone_2
zone_1.connected_zones[zone_2] = 1
if(zone_1 in zone_2.connected_zones)
zone_2.connected_zones[zone_1]++
else
zone_2.connected_zones += zone_1
zone_2.connected_zones[zone_1] = 1
if(open == CONNECTION_DIRECT)
if(!zone_1.direct_connections)
zone_1.direct_connections = list(src)
else
zone_1.direct_connections += src
if(!zone_2.direct_connections)
zone_2.direct_connections = list(src)
else
zone_2.direct_connections += src
//Handle closed connections.
else
//Create the lists
if(!zone_1.closed_connection_zones)
zone_1.closed_connection_zones = list()
if(!zone_2.closed_connection_zones)
zone_2.closed_connection_zones = list()
//Increment the connections.
if(zone_2 in zone_1.closed_connection_zones)
zone_1.closed_connection_zones[zone_2]++
else
zone_1.closed_connection_zones += zone_2
zone_1.closed_connection_zones[zone_2] = 1
if(zone_1 in zone_2.closed_connection_zones)
zone_2.closed_connection_zones[zone_1]++
else
zone_2.closed_connection_zones += zone_1
zone_2.closed_connection_zones[zone_1] = 1
if(zone_1.status == ZONE_SLEEPING)
zone_1.SetStatus(ZONE_ACTIVE)
if(zone_2.status == ZONE_SLEEPING)
zone_2.SetStatus(ZONE_ACTIVE)
/connection/proc/DisconnectZones(var/zone/zone_1, var/zone/zone_2)
//Sanity checking
if(!istype(zone_1) || !istype(zone_2))
return return
else if(block_status & ZONE_BLOCKED)
if(indirect != CONNECTION_CLOSED) if(direct())
//Handle disconnection of indirectly or directly connected zones. mark_indirect()
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 else
SoftDelete() mark_direct()
var/b_is_space = !istype(B,/turf/simulated)
if(state & CONNECTION_SPACE)
if(!b_is_space)
//world << "Invalid B."
erase()
return return
if(A.zone != zoneA)
/connection/proc/Sanitize() //world << "Zone changed, \..."
//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) if(!A.zone)
zone_A = A.zone erase()
//world << "erased."
return
else
edge.remove_connection(src)
edge = air_master.get_edge(A.zone, B)
edge.add_connection(src)
zoneA = A.zone
if(!B.zone) //world << "valid."
zone_B = B.zone return
else if(b_is_space)
//world << "Invalid B."
erase()
return
if(A.zone == B.zone)
//world << "A == B"
erase()
return
if(A.zone != zoneA || (zoneB && (B.zone != zoneB)))
//world << "Zones changed, \..."
if(A.zone && B.zone)
edge.remove_connection(src)
edge = air_master.get_edge(A.zone, B.zone)
edge.add_connection(src)
zoneA = A.zone
zoneB = B.zone
else
//world << "erased."
erase()
return 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. //world << "valid."
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

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

@@ -0,0 +1,344 @@
/connection_edge/var/zone/A
/connection_edge/var/list/connecting_turfs = list()
/connection_edge/var/coefficient = 0
/connection_edge/var/id
/connection_edge/New()
CRASH("Cannot make connection edge without specifications.")
/connection_edge/proc/add_connection(connection/c)
coefficient++
//world << "Connection added. Coefficient: [coefficient]"
/connection_edge/proc/remove_connection(connection/c)
//world << "Connection removed. Coefficient: [coefficient-1]"
coefficient--
if(coefficient <= 0)
erase()
/connection_edge/proc/contains_zone(zone/Z)
/connection_edge/proc/erase()
air_master.remove_edge(src)
//world << "Erased."
/connection_edge/proc/tick()
/connection_edge/proc/flow(list/movable, differential, repelled)
for(var/atom/movable/M in movable)
//If they're already being tossed, don't do it again.
if(M.last_airflow > world.time - vsc.airflow_delay) continue
if(M.airflow_speed) continue
//Check for knocking people over
if(ismob(M) && differential > vsc.airflow_stun_pressure)
if(M:status_flags & GODMODE) continue
M:airflow_stun()
if(M.check_airflow_movable(differential))
//Check for things that are in range of the midpoint turfs.
var/list/close_turfs = list()
for(var/turf/U in connecting_turfs)
if(get_dist(M,U) < world.view) close_turfs += U
if(!close_turfs.len) continue
M.airflow_dest = pick(close_turfs) //Pick a random midpoint to fly towards.
if(repelled) spawn if(M) M.RepelAirflowDest(differential/5)
else spawn if(M) M.GotoAirflowDest(differential/10)
/connection_edge/zone/var/zone/B
/connection_edge/zone/var/direct = 0
/connection_edge/zone/New(zone/A, zone/B)
src.A = A
src.B = B
A.edges.Add(src)
B.edges.Add(src)
//id = edge_id(A,B)
//world << "New edge between [A] and [B]"
/connection_edge/zone/add_connection(connection/c)
. = ..()
connecting_turfs.Add(c.A)
if(c.direct()) direct++
/connection_edge/zone/remove_connection(connection/c)
connecting_turfs.Remove(c.A)
if(c.direct()) direct--
. = ..()
/connection_edge/zone/contains_zone(zone/Z)
return A == Z || B == Z
/connection_edge/zone/erase()
A.edges.Remove(src)
B.edges.Remove(src)
. = ..()
/connection_edge/zone/tick()
//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
/connection_edge/unsimulated/var/datum/gas_mixture/air
/connection_edge/unsimulated/New(zone/A, turf/B)
src.A = A
src.B = B
A.edges.Add(src)
air = B.return_air()
//id = 52*A.id
//world << "New edge from [A.id] to [B]."
/connection_edge/unsimulated/add_connection(connection/c)
. = ..()
connecting_turfs.Add(c.B)
/connection_edge/unsimulated/remove_connection(connection/c)
connecting_turfs.Remove(c.B)
. = ..()
/connection_edge/unsimulated/contains_zone(zone/Z)
return A == Z
/connection_edge/unsimulated/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)
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)

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

@@ -0,0 +1,254 @@
var/datum/controller/air_system/air_master
var/tick_multiplier = 2
//Geometry lists
/datum/controller/air_system/var/list/zones = list()
/datum/controller/air_system/var/list/edges = list()
//Geometry updates lists
/datum/controller/air_system/var/list/tiles_to_update = list()
/datum/controller/air_system/var/list/zones_to_update = list()
/datum/controller/air_system/var/list/active_hotspots = list()
/datum/controller/air_system/var/active_zones = 0
/datum/controller/air_system/var/current_cycle = 0
/datum/controller/air_system/var/update_delay = 5 //How long between check should it try to process atmos again.
/datum/controller/air_system/var/failed_ticks = 0 //How many ticks have runtimed?
/datum/controller/air_system/var/tick_progress = 0
/datum/controller/air_system/var/next_id = 1 //Used to keep track of zone UIDs.
/datum/controller/air_system/proc/Setup()
//Purpose: Call this at the start to setup air groups geometry
// (Warning: Very processor intensive but only must be done once per round)
//Called by: Gameticker/Master controller
//Inputs: None.
//Outputs: None.
#ifndef ZASDBG
set background = 1
#endif
world << "\red \b Processing Geometry..."
sleep(-1)
var/start_time = world.timeofday
var/simulated_turf_count = 0
for(var/turf/simulated/S in world)
simulated_turf_count++
S.update_air_properties()
world << {"<font color='red'><b>Geometry initialized in [round(0.1*(world.timeofday-start_time),0.1)] seconds.</b>
Total Simulated Turfs: [simulated_turf_count]
Total Zones: [zones.len]
Total Unsimulated Turfs: [world.maxx*world.maxy*world.maxz - simulated_turf_count]</font>"}
// spawn Start()
/datum/controller/air_system/proc/Start()
//Purpose: This is kicked off by the master controller, and controls the processing of all atmosphere.
//Called by: Master controller
//Inputs: None.
//Outputs: None.
#ifndef ZASDBG
set background = 1
#endif
while(1)
Tick()
sleep(max(5,update_delay*tick_multiplier))
/datum/controller/air_system/proc/Tick()
. = 1 //Set the default return value, for runtime detection.
current_cycle++
//If there are tiles to update, do so.
tick_progress = "updating turf properties"
var/list/updating
if(tiles_to_update.len)
updating = tiles_to_update
tiles_to_update = list()
#ifdef ZASDBG
var/updated = 0
#endif
for(var/turf/T in updating)
T.update_air_properties()
T.post_update_air_properties()
T.needs_air_update = 0
#ifdef ZASDBG
T.overlays -= mark
updated++
#endif
//sleep(1)
#ifdef ZASDBG
if(updated != updating.len)
tick_progress = "[updating.len - updated] tiles left unupdated."
world << "\red [tick_progress]"
. = 0
#endif
//Where gas exchange happens.
if(.)
tick_progress = "processing edges"
for(var/connection_edge/edge in edges)
edge.tick()
//Process fires.
if(.)
tick_progress = "processing fire"
for(var/obj/fire/fire in active_hotspots)
fire.process()
//Process zones.
if(.)
tick_progress = "updating zones"
active_zones = zones_to_update.len
if(zones_to_update.len)
updating = zones_to_update
zones_to_update = list()
for(var/zone/zone in updating)
zone.tick()
zone.needs_update = 0
if(.)
tick_progress = "success"
/datum/controller/air_system/proc/add_zone(zone/z)
zones.Add(z)
z.name = "Zone [next_id++]"
mark_zone_update(z)
/datum/controller/air_system/proc/remove_zone(zone/z)
zones.Remove(z)
/datum/controller/air_system/proc/air_blocked(turf/A, turf/B)
#ifdef ZASDBG
ASSERT(isturf(A))
ASSERT(isturf(B))
#endif
var/ablock = A.c_airblock(B)
if(ablock == BLOCKED) return BLOCKED
return ablock | B.c_airblock(A)
/datum/controller/air_system/proc/has_valid_zone(turf/simulated/T)
#ifdef ZASDBG
ASSERT(istype(T))
#endif
return istype(T) && T.zone && !T.zone.invalid
/datum/controller/air_system/proc/merge(zone/A, zone/B)
#ifdef ZASDBG
ASSERT(istype(A))
ASSERT(istype(B))
ASSERT(A != 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 |= T
#ifdef ZASDBG
T.overlays += mark
#endif
T.needs_air_update = 1
/datum/controller/air_system/proc/mark_zone_update(zone/Z)
#ifdef ZASDBG
ASSERT(istype(Z))
#endif
if(Z.needs_update) return
zones_to_update.Add(Z)
Z.needs_update = 1
/datum/controller/air_system/proc/equivalent_pressure(zone/A, zone/B)
return A.air.compare(B.air)
/datum/controller/air_system/proc/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)

View File

@@ -1,219 +1,20 @@
client/proc/ZoneTick() var/image/assigned = image('icons/Testing/Zone.dmi', icon_state = "assigned")
set category = "Debug" var/image/created = image('icons/Testing/Zone.dmi', icon_state = "created")
set name = "Process Atmos" 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() /turf/var/tmp/dbg_img
if(result) /turf/proc/dbg(image/img, d = 0)
src << "Sucessfully Processed." if(d > 0) img.dir = d
overlays -= dbg_img
overlays += img
dbg_img = img
else /turf/simulated/var/verbose = 0
src << "Failed to process! ([air_master.tick_progress])" /turf/simulated/verb/Verbose()
set src in world
verbose = !verbose
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)

233
code/ZAS/Diagnostic.dm Normal file
View 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)

View File

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

View File

@@ -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()) A.fire_act(air_contents, air_contents.temperature, air_contents.return_volume())
//spread //spread
for(var/direction in cardinal) 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) var/turf/simulated/enemy_tile = get_step(S, direction)

View File

@@ -1,243 +0,0 @@
//Global Functions
//Contents: FloodFill, ZMerge, ZConnect
//Floods outward from an initial turf to fill everywhere it's zone would reach.
proc/FloodFill(turf/simulated/start)
if(!istype(start))
return list()
//The list of tiles waiting to be evaulated.
var/list/open = list(start)
//The list of tiles which have been evaulated.
var/list/closed = list()
/////// Z-Level stuff
//List of all space tiles bordering the zone
var/list/list_space = list()
//List of all Z-Levels of the zone where it borders space
var/list/z_space = list()
/////// Z-Level stuff
//Loop through the turfs in the open list in order to find which adjacent turfs should be added to the zone.
while(open.len)
var/turf/simulated/T = pick(open)
//sanity!
if(!istype(T))
open -= T
continue
//Check all cardinal directions
for(var/d in cardinal)
var/turf/simulated/O = get_step(T,d)
//Ensure the turf is of proper type, that it is not in either list, and that air can reach it.
if(istype(O) && !(O in open) && !(O in closed) && O.ZCanPass(T))
//Handle connections from a tile with a door.
if(T.HasDoor())
//If they both have doors, then they are not able to connect period.
if(O.HasDoor())
continue
//Connect first to north and west
if(d == NORTH || d == WEST)
open += O
//If that fails, and north/west cannot be connected to, see if west or south can be connected instead.
else
var/turf/simulated/W = get_step(O, WEST)
var/turf/simulated/N = get_step(O, NORTH)
if( !O.ZCanPass(N) && !O.ZCanPass(W) )
//If it cannot connect either to the north or west, connect it!
open += O
//If no doors are involved, add it immediately.
else if(!O.HasDoor())
open += O
//Handle connecting to a tile with a door.
else
if(d == SOUTH || d == EAST)
//doors prefer connecting to zones to the north or west
closed += O
else
//see if we need to force an attempted connection
//(there are no potentially viable zones to the north/west of the door)
var/turf/simulated/W = get_step(O, WEST)
var/turf/simulated/N = get_step(O, NORTH)
if( !O.ZCanPass(N) && !O.ZCanPass(W) )
//If it cannot connect either to the north or west, connect it!
closed += O
/////// Z-Level stuff
if(istype(O,/turf/space))
if(!(O in list_space))
list_space += O
if(!(O.z in z_space))
z_space += O.z
// handle Z-level connections
var/turf/controllerlocation = locate(1, 1, T.z)
for(var/obj/effect/landmark/zcontroller/controller in controllerlocation)
// connect upwards
if(controller.up)
var/turf/above_me = locate(T.x, T.y, controller.up_target)
// add the turf above this
if(istype(above_me, /turf/simulated/floor/open) && !(above_me in open) && !(above_me in closed))
open += above_me
if(istype(above_me,/turf/space))
if(!(above_me in list_space))
list_space += above_me
if(!(above_me.z in z_space))
z_space += above_me.z
// connect downwards
if(controller.down && istype(T, /turf/simulated/floor/open))
var/turf/below_me = locate(T.x, T.y, controller.down_target)
// add the turf below this
if(!(below_me in open) && !(below_me in closed))
open += below_me
/////// Z-Level stuff
//This tile is now evaluated, and can be moved to the list of evaluated tiles.
open -= T
closed += T
/////// Z-Level stuff
// once the zone is done, check if there is space that needs to be changed to open space
if(!open.len)
var/list/temp = list()
while(list_space.len)
var/turf/S = pick(list_space)
//check if the zone has any space borders below the evaluated space tile
//if there is some, we dont need to make open_space since the zone can vent and the zone above can vent
//through the evaluated tile
//if there is none, the zone can connect upwards to either vent from there or connect with the zone there
//also check if the turf below the space is actually part of this zone to prevent the edge tiles from transforming
var/turf/controllerloc = locate(1, 1, S.z)
for(var/obj/effect/landmark/zcontroller/controller in controllerloc)
if(controller.down)
var/turf/below = locate(S.x, S.y, controller.down_target)
if(!((S.z - 1) in z_space) && below in closed)
open += S.ChangeTurf(/turf/simulated/floor/open)
list_space -= S
else
list_space -= S
temp += S
else
list_space -= S
temp += S
// make sure the turf is removed from the list
list_space -= S
z_space -= z_space
while(temp.len)
var/turf/S = pick(temp)
if(!(S.z in z_space))
z_space += S.z
list_space += S
temp -= S
/////// Z-Level stuff
return closed
//Procedure to merge two zones together.
proc/ZMerge(zone/A,zone/B)
//Sanity~
if(!istype(A) || !istype(B))
return
var/new_contents = A.contents + B.contents
//Set all the zone vars.
for(var/turf/simulated/T in B.contents)
T.zone = A
if(istype(A.air) && istype(B.air))
//Merges two zones so that they are one.
var/a_size = A.air.group_multiplier
var/b_size = B.air.group_multiplier
var/c_size = a_size + b_size
//Set air multipliers to one so air represents gas per tile.
A.air.group_multiplier = 1
B.air.group_multiplier = 1
//Remove some air proportional to the size of this zone.
A.air.remove_ratio(a_size/c_size)
B.air.remove_ratio(b_size/c_size)
//Merge the gases and set the multiplier to the sum of the old ones.
A.air.merge(B.air)
A.air.group_multiplier = c_size
//I hate when the air datum somehow disappears.
// Try to make it sorta work anyways. Fakit
else if(istype(B.air))
A.air = B.air
A.air.group_multiplier = A.contents.len
else if(istype(A.air))
A.air.group_multiplier = A.contents.len
//Doublefakit.
else
A.air = new
//Check for connections to merge into the new zone.
for(var/connection/C in B.connections)
//The Cleanup proc will delete the connection if the zones are the same.
// It will also set the zone variables correctly.
C.Cleanup()
//Add space tiles.
if(A.unsimulated_tiles && B.unsimulated_tiles)
A.unsimulated_tiles |= B.unsimulated_tiles
else if (B.unsimulated_tiles)
A.unsimulated_tiles = B.unsimulated_tiles
//Add contents.
A.contents = new_contents
//Remove the "B" zone, finally.
B.SoftDelete()
//Connects two zones by forming a connection object representing turfs A and B.
proc/ZConnect(turf/simulated/A,turf/simulated/B)
//Make sure that if it's space, it gets added to unsimulated_tiles instead.
if(!istype(B))
if(A.zone)
A.zone.AddTurf(B)
return
if(!istype(A))
if(B.zone)
B.zone.AddTurf(A)
return
if(!istype(A) || !istype(B))
return
//Make some preliminary checks to see if the connection is valid.
if(!A.zone || !B.zone) return
if(A.zone == B.zone)
air_master.AddIntrazoneConnection(A,B)
return
if(A.CanPass(null, B, 1.5, 1) && A.zone.air.compare(B.zone.air))
return ZMerge(A.zone,B.zone)
//Ensure the connection isn't already made.
if(A in air_master.turfs_with_connections)
for(var/connection/C in air_master.turfs_with_connections[A])
if(C.B == B || C.A == B)
return
//Make the connection.
new /connection(A,B)

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

@@ -0,0 +1,230 @@
/turf/simulated/var/zone/zone
/turf/simulated/var/open_directions
/turf/simulated/var/gas_graphic
/turf/var/needs_air_update = 0
/turf/var/datum/gas_mixture/air
/turf/simulated/proc/set_graphic(new_graphic)
if(isnum(new_graphic))
if(new_graphic == 1) new_graphic = plmaster
else if(new_graphic == 2) new_graphic = slmaster
if(gas_graphic) overlays -= gas_graphic
if(new_graphic) overlays += new_graphic
gas_graphic = new_graphic
/turf/proc/update_air_properties()
var/block = c_airblock(src)
if(block & AIR_BLOCKED)
//dbg(blocked)
return 1
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/update_air_properties()
if(zone && zone.invalid)
c_copy_air()
zone = null //Easier than iterating through the list at the zone.
var/s_block = c_airblock(src)
if(s_block & AIR_BLOCKED)
#ifdef ZASDBG
if(verbose) world << "Self-blocked."
//dbg(blocked)
#endif
if(zone)
var/zone/z = zone
if(s_block & ZONE_BLOCKED)
z.remove(src)
else
z.rebuild()
return 1
var/previously_open = open_directions
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
//Check that our zone hasn't been cut off recently.
//This happens when windows move or are constructed. We need to rebuild.
if((previously_open & d) && istype(unsim, /turf/simulated))
var/turf/simulated/sim = unsim
if(sim.zone == zone)
zone.rebuild()
return
continue
open_directions |= d
if(istype(unsim, /turf/simulated))
var/turf/simulated/sim = unsim
if(air_master.has_valid_zone(sim))
//Might have assigned a zone, since this happens for each direction.
if(!zone)
//if((block & ZONE_BLOCKED) || (r_block & ZONE_BLOCKED && !(s_block & ZONE_BLOCKED)))
if(((block & ZONE_BLOCKED) && !(r_block & ZONE_BLOCKED)) || (r_block & ZONE_BLOCKED && !(s_block & ZONE_BLOCKED)))
#ifdef ZASDBG
if(verbose) world << "[d] is zone blocked."
//dbg(zone_blocked, d)
#endif
//Postpone this tile rather than exit, since a connection can still be made.
if(!postponed) postponed = list()
postponed.Add(sim)
else
sim.zone.add(src)
#ifdef ZASDBG
dbg(assigned)
if(verbose) world << "Added to [zone]"
#endif
else if(sim.zone != zone)
#ifdef ZASDBG
if(verbose) world << "Connecting to [sim.zone]"
#endif
air_master.connect(src, sim)
#ifdef ZASDBG
else if(verbose) world << "[d] has same zone."
else if(verbose) world << "[d] has invalid zone."
#endif
else
//Postponing connections to tiles until a zone is assured.
if(!postponed) postponed = list()
postponed.Add(unsim)
if(!air_master.has_valid_zone(src)) //Still no zone, make a new one.
var/zone/newzone = new/zone()
newzone.add(src)
#ifdef ZASDBG
dbg(created)
ASSERT(zone)
#endif
//At this point, a zone should have happened. If it hasn't, don't add more checks, fix the bug.
for(var/turf/T in postponed)
air_master.connect(src, T)
/turf/proc/post_update_air_properties()
/turf/simulated/post_update_air_properties()
connections.update_all()
/turf/assume_air(datum/gas_mixture/giver) //use this for machines to adjust air
del(giver)
return 0
/turf/return_air()
//Create gas mixture to hold data for passing
var/datum/gas_mixture/GM = new
GM.oxygen = oxygen
GM.carbon_dioxide = carbon_dioxide
GM.nitrogen = nitrogen
GM.toxins = toxins
GM.temperature = temperature
GM.update_values()
return GM
/turf/remove_air(amount as num)
var/datum/gas_mixture/GM = new
var/sum = oxygen + carbon_dioxide + nitrogen + toxins
if(sum>0)
GM.oxygen = (oxygen/sum)*amount
GM.carbon_dioxide = (carbon_dioxide/sum)*amount
GM.nitrogen = (nitrogen/sum)*amount
GM.toxins = (toxins/sum)*amount
GM.temperature = temperature
GM.update_values()
return GM
/turf/simulated/assume_air(datum/gas_mixture/giver)
var/datum/gas_mixture/my_air = return_air()
my_air.merge(giver)
/turf/simulated/remove_air(amount as num)
var/datum/gas_mixture/my_air = return_air()
return my_air.remove(amount)
/turf/simulated/return_air()
if(zone)
if(!zone.invalid)
air_master.mark_zone_update(zone)
return zone.air
else
if(!air)
make_air()
c_copy_air()
return air
else
if(!air)
make_air()
return air
/turf/proc/make_air()
air = new/datum/gas_mixture
air.temperature = temperature
air.adjust(oxygen, carbon_dioxide, nitrogen, toxins)
air.group_multiplier = 1
air.volume = CELL_VOLUME
/turf/simulated/proc/c_copy_air()
if(!air) air = new/datum/gas_mixture
air.copy_from(zone.air)
air.group_multiplier = 1

View File

@@ -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)
*/

View File

@@ -1,906 +0,0 @@
var/list/DoorDirections = list(NORTH,WEST) //Which directions doors turfs can connect to zones
var/list/CounterDoorDirections = list(SOUTH,EAST) //Which directions doors turfs can connect to zones
/zone
var/dbg_output = 0 //Enables debug output.
var/datum/gas_mixture/air //The air contents of the zone.
var/datum/gas_mixture/archived_air
var/list/contents //All the tiles that are contained in this zone.
var/list/unsimulated_tiles // Any space tiles in this list will cause air to flow out.
var/datum/gas_mixture/air_unsim //Overall average of the air in connected unsimualted tiles.
var/unsim_air_needs_update = 0 //Set to 1 on geometry changes, marks air_unsim as needing update.
var/list/connections //connection objects which refer to connections with other zones, e.g. through a door.
var/list/direct_connections //connections which directly connect two zones.
var/list/connected_zones //Parallels connections, but lists zones to which this one is connected and the number
//of points they're connected at.
var/list/closed_connection_zones //Same as connected_zones, but for zones where the door or whatever is closed.
var/last_update = 0
var/last_rebuilt = 0
var/status = ZONE_ACTIVE
var/interactions_with_neighbors = 0
var/interactions_with_unsim = 0
var/progress = "nothing"
//CREATION AND DELETION
/zone/New(turf/start)
. = ..()
//Get the turfs that are part of the zone using a floodfill method
if(istype(start,/list))
contents = start
else
contents = FloodFill(start)
//Change all the zone vars of the turfs, check for space to be added to unsimulated_tiles.
for(var/turf/T in contents)
if(T.zone && T.zone != src)
T.zone.RemoveTurf(T)
T.zone = src
if(!istype(T,/turf/simulated))
AddTurf(T)
//Generate the gas_mixture for use in txhis zone by using the average of the gases
//defined at startup.
//Changed to try and find the source of the error.
air = new
air.group_multiplier = contents.len
for(var/turf/simulated/T in contents)
if(!T.air)
continue
air.oxygen += T.air.oxygen / air.group_multiplier
air.nitrogen += T.air.nitrogen / air.group_multiplier
air.carbon_dioxide += T.air.carbon_dioxide / air.group_multiplier
air.toxins += T.air.toxins / air.group_multiplier
air.temperature += T.air.temperature / air.group_multiplier
for(var/datum/gas/trace in T.air.trace_gases)
var/datum/gas/corresponding_gas = locate(trace.type) in air.trace_gases
if(!corresponding_gas)
corresponding_gas = new trace.type()
air.trace_gases.Add(corresponding_gas)
corresponding_gas.moles += trace.moles
air.update_values()
//Add this zone to the global list.
if(air_master)
air_master.zones.Add(src)
air_master.active_zones.Add(src)
//DO NOT USE. Use the SoftDelete proc.
/zone/Del()
//Ensuring the zone list doesn't get clogged with null values.
for(var/turf/simulated/T in contents)
RemoveTurf(T)
air_master.ReconsiderTileZone(T)
if(air_master)
air_master.AddConnectionToCheck(connections)
air = null
. = ..()
//Handles deletion via garbage collection.
/zone/proc/SoftDelete()
air = null
if(air_master)
air_master.zones.Remove(src)
air_master.active_zones.Remove(src)
air_master.zones_needing_rebuilt.Remove(src)
air_master.AddConnectionToCheck(connections)
connections = null
for(var/connection/C in direct_connections)
if(C.A.zone == src)
C.A.zone = null
if(C.B.zone == src)
C.B.zone = null
if(C.zone_A == src)
C.zone_A = null
if(C.zone_B == src)
C.zone_B = null
direct_connections = null
//Ensuring the zone list doesn't get clogged with null values.
for(var/turf/simulated/T in contents)
RemoveTurf(T)
air_master.ReconsiderTileZone(T)
contents.Cut()
//Removing zone connections and scheduling connection cleanup
for(var/zone/Z in connected_zones)
Z.connected_zones.Remove(src)
if(!Z.connected_zones.len)
Z.connected_zones = null
if(Z.closed_connection_zones)
Z.closed_connection_zones.Remove(src)
if(!Z.closed_connection_zones.len)
Z.closed_connection_zones = null
connected_zones = null
closed_connection_zones = null
return 1
//ZONE MANAGEMENT FUNCTIONS
/zone/proc/AddTurf(turf/T)
//Adds the turf to contents, increases the size of the zone, and sets the zone var.
if(istype(T, /turf/simulated))
if(T in contents)
return
if(T.zone)
T.zone.RemoveTurf(T)
contents += T
if(air)
air.group_multiplier++
T.zone = src
///// Z-Level Stuff
// also add the tile below it if its open space
if(istype(T, /turf/simulated/floor/open))
var/turf/simulated/floor/open/T2 = T
src.AddTurf(T2.floorbelow)
///// Z-Level Stuff
else
if(!unsimulated_tiles)
unsimulated_tiles = list()
else if(T in unsimulated_tiles)
return
unsimulated_tiles += T
contents -= T
unsim_air_needs_update = 1
/zone/proc/RemoveTurf(turf/T)
//Same, but in reverse.
if(istype(T, /turf/simulated))
if(!(T in contents))
return
contents -= T
if(air)
air.group_multiplier--
if(T.zone == src)
T.zone = null
if(!contents.len)
SoftDelete()
else if(unsimulated_tiles)
unsimulated_tiles -= T
if(!unsimulated_tiles.len)
unsimulated_tiles = null
unsim_air_needs_update = 1
//Updates the air_unsim var
/zone/proc/UpdateUnsimAvg()
if(!unsimulated_tiles || !unsimulated_tiles.len) //if we don't have any unsimulated tiles, we can't do much.
return
if(!unsim_air_needs_update && air_unsim) //if air_unsim doesn't exist, we need to create it even if we don't need an update.
return
//Tempfix.
if(!air)
return
unsim_air_needs_update = 0
if(!air_unsim)
air_unsim = new /datum/gas_mixture
air_unsim.oxygen = 0
air_unsim.nitrogen = 0
air_unsim.carbon_dioxide = 0
air_unsim.toxins = 0
air_unsim.temperature = 0
var/correction_ratio = max(1, max(max(1, air.group_multiplier) + 3, 1) + unsimulated_tiles.len) / unsimulated_tiles.len
for(var/turf/T in unsimulated_tiles)
if(!istype(T, /turf/simulated))
air_unsim.oxygen += T.oxygen
air_unsim.carbon_dioxide += T.carbon_dioxide
air_unsim.nitrogen += T.nitrogen
air_unsim.toxins += T.toxins
air_unsim.temperature += T.temperature/unsimulated_tiles.len
//These values require adjustment in order to properly represent a room of the specified size.
air_unsim.oxygen *= correction_ratio
air_unsim.carbon_dioxide *= correction_ratio
air_unsim.nitrogen *= correction_ratio
air_unsim.toxins *= correction_ratio
air_unsim.group_multiplier = unsimulated_tiles.len
air_unsim.update_values()
return
//////////////
//PROCESSING//
//////////////
#define QUANTIZE(variable) (round(variable,0.0001))
/zone/proc/process()
. = 1
progress = "problem with: SoftDelete()"
//Deletes zone if empty.
if(!contents.len)
return SoftDelete()
progress = "problem with: Rebuild()"
if(!contents.len) //If we got soft deleted.
return
progress = "problem with: air regeneration"
//Sometimes explosions will cause the air to be deleted for some reason.
if(!air)
air = new()
air.oxygen = MOLES_O2STANDARD
air.nitrogen = MOLES_N2STANDARD
air.temperature = T0C
air.total_moles()
world.log << "Air object lost in zone. Regenerating."
progress = "problem with: ShareSpace()"
if(unsim_air_needs_update)
unsim_air_needs_update = 0
UpdateUnsimAvg()
if(unsimulated_tiles)
if(locate(/turf/simulated) in unsimulated_tiles)
for(var/turf/simulated/T in unsimulated_tiles)
unsimulated_tiles -= T
if(unsimulated_tiles.len)
var/moved_air = ShareSpace(air, air_unsim)
if(!air.compare(air_unsim))
interactions_with_unsim++
if(moved_air > vsc.airflow_lightest_pressure)
AirflowSpace(src)
else
unsimulated_tiles = null
//Check the graphic.
progress = "problem with: modifying turf graphics"
air.graphic = 0
if(air.toxins > MOLES_PLASMA_VISIBLE)
air.graphic = 1
else if(air.trace_gases.len)
var/datum/gas/sleeping_agent = locate(/datum/gas/sleeping_agent) in air.trace_gases
if(sleeping_agent && (sleeping_agent.moles > 1))
air.graphic = 2
progress = "problem with an inbuilt byond function: some conditional checks"
//Only run through the individual turfs if there's reason to.
if(air.graphic != air.graphic_archived || air.temperature > PLASMA_FLASHPOINT)
progress = "problem with: turf/simulated/update_visuals()"
for(var/turf/simulated/S in contents)
//Update overlays.
if(air.graphic != air.graphic_archived)
if(S.HasDoor(1))
S.update_visuals()
else
S.update_visuals(air)
progress = "problem with: item or turf temperature_expose()"
//Expose stuff to extreme heat.
if(air.temperature > PLASMA_FLASHPOINT)
for(var/atom/movable/item in S)
item.temperature_expose(air, air.temperature, CELL_VOLUME)
S.hotspot_expose(air.temperature, CELL_VOLUME)
progress = "problem with: calculating air graphic"
//Archive graphic so we can know if it's different.
air.graphic_archived = air.graphic
progress = "problem with: calculating air temp"
//Ensure temperature does not reach absolute zero.
air.temperature = max(TCMB,air.temperature)
progress = "problem with an inbuilt byond function: length(connections)"
//Handle connections to other zones.
if(length(connections))
progress = "problem with: ZMerge(), a couple of misc procs"
if(length(direct_connections))
for(var/connection/C in direct_connections)
//Do merging if conditions are met. Specifically, if there's a non-door connection
//to somewhere with space, the zones are merged regardless of equilibrium, to speed
//up spacing in areas with double-plated windows.
if(C.A.zone && C.B.zone)
if(C.A.zone.air.compare(C.B.zone.air) || unsimulated_tiles)
ZMerge(C.A.zone,C.B.zone)
progress = "problem with: ShareRatio(), Airflow(), a couple of misc procs"
//Share some
for(var/zone/Z in connected_zones)
//If that zone has already processed, skip it.
if(Z.last_update > last_update)
continue
//Handle adjacent zones that are sleeping
if(Z.status == ZONE_SLEEPING)
if(air.compare(Z.air))
continue
else
Z.SetStatus(ZONE_ACTIVE)
if(air && Z.air)
//Ensure we're not doing pointless calculations on equilibrium zones.
if(!air.compare(Z.air))
if(abs(Z.air.return_pressure() - air.return_pressure()) > vsc.airflow_lightest_pressure)
Airflow(src,Z)
var/unsimulated_boost = 0
if(unsimulated_tiles)
unsimulated_boost += unsimulated_tiles.len
if(Z.unsimulated_tiles)
unsimulated_boost += Z.unsimulated_tiles.len
unsimulated_boost = max(0, min(3, unsimulated_boost))
ShareRatio( air , Z.air , connected_zones[Z] + unsimulated_boost)
Z.interactions_with_neighbors++
interactions_with_neighbors++
if(!vsc.connection_insulation)
for(var/zone/Z in closed_connection_zones)
//If that zone has already processed, skip it.
if(Z.last_update > last_update || !Z.air)
continue
var/handle_temperature = abs(air.temperature - Z.air.temperature) > vsc.connection_temperature_delta
if(Z.status == ZONE_SLEEPING)
if (handle_temperature)
Z.SetStatus(ZONE_ACTIVE)
else
continue
if(air && Z.air)
if( handle_temperature )
ShareHeat(air, Z.air, closed_connection_zones[Z])
Z.interactions_with_neighbors++
interactions_with_neighbors++
if(!interactions_with_neighbors && !interactions_with_unsim)
SetStatus(ZONE_SLEEPING)
interactions_with_neighbors = 0
interactions_with_unsim = 0
progress = "all components completed successfully, the problem is not here"
/zone/proc/SetStatus(var/new_status)
if(status == ZONE_SLEEPING && new_status == ZONE_ACTIVE)
air_master.active_zones.Add(src)
status = ZONE_ACTIVE
else if(status == ZONE_ACTIVE && new_status == ZONE_SLEEPING)
air_master.active_zones.Remove(src)
status = ZONE_SLEEPING
if(unsimulated_tiles && unsimulated_tiles.len)
UpdateUnsimAvg()
air.copy_from(air_unsim)
if(!archived_air)
archived_air = new
archived_air.copy_from(air)
/zone/proc/CheckStatus()
return status
/zone/proc/ActivateIfNeeded()
if(status == ZONE_ACTIVE) return
var/difference = 0
if(unsimulated_tiles && unsimulated_tiles.len)
UpdateUnsimAvg()
if(!air.compare(air_unsim))
difference = 1
if(!difference)
for(var/zone/Z in connected_zones) //Check adjacent zones for air difference.
if(!air.compare(Z.air))
difference = 1
break
if(difference) //We have a difference, activate the zone.
SetStatus(ZONE_ACTIVE)
return
/zone/proc/assume_air(var/datum/gas_mixture/giver)
if(status == ZONE_ACTIVE)
return air.merge(giver)
else
if(unsimulated_tiles && unsimulated_tiles.len)
UpdateUnsimAvg()
var/datum/gas_mixture/compare_air = new
compare_air.copy_from(giver)
compare_air.add(air_unsim)
compare_air.divide(air.group_multiplier)
if(air_unsim.compare(compare_air))
return 0
var/result = air.merge(giver)
if(!archived_air.compare(air))
SetStatus(ZONE_ACTIVE)
return result
/zone/proc/remove_air(var/amount)
if(status == ZONE_ACTIVE)
return air.remove(amount)
else
var/result = air.remove(amount)
if(!archived_air.compare(air))
SetStatus(ZONE_ACTIVE)
return result
////////////////
//Air Movement//
////////////////
var/list/sharing_lookup_table = list(0.30, 0.40, 0.48, 0.54, 0.60, 0.66)
proc/ShareRatio(datum/gas_mixture/A, datum/gas_mixture/B, connecting_tiles)
//Shares a specific ratio of gas between mixtures using simple weighted averages.
var
//WOOT WOOT TOUCH THIS AND YOU ARE A RETARD
ratio = sharing_lookup_table[6]
//WOOT WOOT TOUCH THIS AND YOU ARE A RETARD
size = max(1,A.group_multiplier)
share_size = max(1,B.group_multiplier)
full_oxy = A.oxygen * size
full_nitro = A.nitrogen * size
full_co2 = A.carbon_dioxide * size
full_plasma = A.toxins * size
full_heat_capacity = A.heat_capacity() * size
s_full_oxy = B.oxygen * share_size
s_full_nitro = B.nitrogen * share_size
s_full_co2 = B.carbon_dioxide * share_size
s_full_plasma = B.toxins * share_size
s_full_heat_capacity = B.heat_capacity() * share_size
oxy_avg = (full_oxy + s_full_oxy) / (size + share_size)
nit_avg = (full_nitro + s_full_nitro) / (size + share_size)
co2_avg = (full_co2 + s_full_co2) / (size + share_size)
plasma_avg = (full_plasma + s_full_plasma) / (size + share_size)
temp_avg = (A.temperature * full_heat_capacity + B.temperature * s_full_heat_capacity) / (full_heat_capacity + s_full_heat_capacity)
//WOOT WOOT TOUCH THIS AND YOU ARE A RETARD
if(sharing_lookup_table.len >= connecting_tiles) //6 or more interconnecting tiles will max at 42% of air moved per tick.
ratio = sharing_lookup_table[connecting_tiles]
//WOOT WOOT TOUCH THIS AND YOU ARE A RETARD
A.oxygen = max(0, (A.oxygen - oxy_avg) * (1-ratio) + oxy_avg )
A.nitrogen = max(0, (A.nitrogen - nit_avg) * (1-ratio) + nit_avg )
A.carbon_dioxide = max(0, (A.carbon_dioxide - co2_avg) * (1-ratio) + co2_avg )
A.toxins = max(0, (A.toxins - plasma_avg) * (1-ratio) + plasma_avg )
A.temperature = max(0, (A.temperature - temp_avg) * (1-ratio) + temp_avg )
B.oxygen = max(0, (B.oxygen - oxy_avg) * (1-ratio) + oxy_avg )
B.nitrogen = max(0, (B.nitrogen - nit_avg) * (1-ratio) + nit_avg )
B.carbon_dioxide = max(0, (B.carbon_dioxide - co2_avg) * (1-ratio) + co2_avg )
B.toxins = max(0, (B.toxins - plasma_avg) * (1-ratio) + plasma_avg )
B.temperature = max(0, (B.temperature - temp_avg) * (1-ratio) + temp_avg )
for(var/datum/gas/G in A.trace_gases)
var/datum/gas/H = locate(G.type) in B.trace_gases
if(H)
var/G_avg = (G.moles*size + H.moles*share_size) / (size+share_size)
G.moles = (G.moles - G_avg) * (1-ratio) + G_avg
H.moles = (H.moles - G_avg) * (1-ratio) + G_avg
else
H = new G.type
B.trace_gases += H
var/G_avg = (G.moles*size) / (size+share_size)
G.moles = (G.moles - G_avg) * (1-ratio) + G_avg
H.moles = (H.moles - G_avg) * (1-ratio) + G_avg
for(var/datum/gas/G in B.trace_gases)
var/datum/gas/H = locate(G.type) in A.trace_gases
if(!H)
H = new G.type
A.trace_gases += H
var/G_avg = (G.moles*size) / (size+share_size)
G.moles = (G.moles - G_avg) * (1-ratio) + G_avg
H.moles = (H.moles - G_avg) * (1-ratio) + G_avg
A.update_values()
B.update_values()
if(A.compare(B)) return 1
else return 0
proc/ShareSpace(datum/gas_mixture/A, list/unsimulated_tiles, dbg_output)
//A modified version of ShareRatio for spacing gas at the same rate as if it were going into a large airless room.
if(!unsimulated_tiles)
return 0
var
unsim_oxygen = 0
unsim_nitrogen = 0
unsim_co2 = 0
unsim_plasma = 0
unsim_heat_capacity = 0
unsim_temperature = 0
size = max(1,A.group_multiplier)
var/tileslen
var/share_size
if(istype(unsimulated_tiles, /datum/gas_mixture))
var/datum/gas_mixture/avg_unsim = unsimulated_tiles
unsim_oxygen = avg_unsim.oxygen
unsim_co2 = avg_unsim.carbon_dioxide
unsim_nitrogen = avg_unsim.nitrogen
unsim_plasma = avg_unsim.toxins
unsim_temperature = avg_unsim.temperature
share_size = max(1, max(size + 3, 1) + avg_unsim.group_multiplier)
tileslen = avg_unsim.group_multiplier
else if(istype(unsimulated_tiles, /list))
if(!unsimulated_tiles.len)
return 0
// We use the same size for the potentially single space tile
// as we use for the entire room. Why is this?
// Short answer: We do not want larger rooms to depressurize more
// slowly than small rooms, preserving our good old "hollywood-style"
// oh-shit effect when large rooms get breached, but still having small
// rooms remain pressurized for long enough to make escape possible.
share_size = max(1, max(size + 3, 1) + unsimulated_tiles.len)
var/correction_ratio = share_size / unsimulated_tiles.len
for(var/turf/T in unsimulated_tiles)
unsim_oxygen += T.oxygen
unsim_co2 += T.carbon_dioxide
unsim_nitrogen += T.nitrogen
unsim_plasma += T.toxins
unsim_temperature += T.temperature/unsimulated_tiles.len
//These values require adjustment in order to properly represent a room of the specified size.
unsim_oxygen *= correction_ratio
unsim_co2 *= correction_ratio
unsim_nitrogen *= correction_ratio
unsim_plasma *= correction_ratio
tileslen = unsimulated_tiles.len
else //invalid input type
return 0
unsim_heat_capacity = HEAT_CAPACITY_CALCULATION(unsim_oxygen, unsim_co2, unsim_nitrogen, unsim_plasma)
var
ratio = sharing_lookup_table[6]
old_pressure = A.return_pressure()
full_oxy = A.oxygen * size
full_nitro = A.nitrogen * size
full_co2 = A.carbon_dioxide * size
full_plasma = A.toxins * size
full_heat_capacity = A.heat_capacity() * size
oxy_avg = (full_oxy + unsim_oxygen) / (size + share_size)
nit_avg = (full_nitro + unsim_nitrogen) / (size + share_size)
co2_avg = (full_co2 + unsim_co2) / (size + share_size)
plasma_avg = (full_plasma + unsim_plasma) / (size + share_size)
temp_avg = 0
if((full_heat_capacity + unsim_heat_capacity) > 0)
temp_avg = (A.temperature * full_heat_capacity + unsim_temperature * unsim_heat_capacity) / (full_heat_capacity + unsim_heat_capacity)
if(sharing_lookup_table.len >= tileslen) //6 or more interconnecting tiles will max at 42% of air moved per tick.
ratio = sharing_lookup_table[tileslen]
A.oxygen = max(0, (A.oxygen - oxy_avg) * (1 - ratio) + oxy_avg )
A.nitrogen = max(0, (A.nitrogen - nit_avg) * (1 - ratio) + nit_avg )
A.carbon_dioxide = max(0, (A.carbon_dioxide - co2_avg) * (1 - ratio) + co2_avg )
A.toxins = max(0, (A.toxins - plasma_avg) * (1 - ratio) + plasma_avg )
A.temperature = max(TCMB, (A.temperature - temp_avg) * (1 - ratio) + temp_avg )
for(var/datum/gas/G in A.trace_gases)
var/G_avg = (G.moles * size) / (size + share_size)
G.moles = (G.moles - G_avg) * (1 - ratio) + G_avg
A.update_values()
return abs(old_pressure - A.return_pressure())
proc/ShareHeat(datum/gas_mixture/A, datum/gas_mixture/B, connecting_tiles)
//This implements a simplistic version of the Stefan-Boltzmann law.
var/energy_delta = ((A.temperature - B.temperature) ** 4) * 5.6704e-8 * connecting_tiles * 2.5
var/maximum_energy_delta = max(0, min(A.temperature * A.heat_capacity() * A.group_multiplier, B.temperature * B.heat_capacity() * B.group_multiplier))
if(maximum_energy_delta > abs(energy_delta))
if(energy_delta < 0)
maximum_energy_delta *= -1
energy_delta = maximum_energy_delta
A.temperature -= energy_delta / (A.heat_capacity() * A.group_multiplier)
B.temperature += energy_delta / (B.heat_capacity() * B.group_multiplier)
/* This was bad an I feel bad.
//Shares a specific ratio of gas between mixtures using simple weighted averages.
var
//WOOT WOOT TOUCH THIS AND YOU ARE A RETARD
ratio = sharing_lookup_table[6]
//WOOT WOOT TOUCH THIS AND YOU ARE A RETARD
full_heat_capacity = A.heat_capacity()
s_full_heat_capacity = B.heat_capacity()
temp_avg = (A.temperature * full_heat_capacity + B.temperature * s_full_heat_capacity) / (full_heat_capacity + s_full_heat_capacity)
//WOOT WOOT TOUCH THIS AND YOU ARE A RETARD
if(sharing_lookup_table.len >= connecting_tiles) //6 or more interconnecting tiles will max at 42% of air moved per tick.
ratio = sharing_lookup_table[connecting_tiles]
//WOOT WOOT TOUCH THIS AND YOU ARE A RETARD
//We need to adjust it to account for the insulation settings.
ratio *= 1 - vsc.connection_insulation
A.temperature = max(0, (A.temperature - temp_avg) * (1- (ratio / max(1,A.group_multiplier)) ) + temp_avg )
B.temperature = max(0, (B.temperature - temp_avg) * (1- (ratio / max(1,B.group_multiplier)) ) + temp_avg )
*/
///////////////////
//Zone Rebuilding//
///////////////////
//Used for updating zone geometry when a zone is cut into two parts.
zone/proc/Rebuild()
if(last_rebuilt == air_master.current_cycle)
return
last_rebuilt = air_master.current_cycle
var/list/new_zone_contents = IsolateContents()
if(new_zone_contents.len == 1)
return
var/list/current_contents
var/list/new_zones = list()
contents = new_zone_contents[1]
air.group_multiplier = contents.len
for(var/identifier in 2 to new_zone_contents.len)
current_contents = new_zone_contents[identifier]
var/zone/new_zone = new (current_contents)
new_zone.air.copy_from(air)
new_zones += new_zone
for(var/connection/connection in connections)
connection.Cleanup()
var/turf/simulated/adjacent
for(var/turf/unsimulated in unsimulated_tiles)
for(var/direction in cardinal)
adjacent = get_step(unsimulated, direction)
if(istype(adjacent) && adjacent.CanPass(null, unsimulated, 0, 0))
for(var/zone/zone in new_zones)
if(adjacent in zone)
zone.AddTurf(unsimulated)
//Implements a two-pass connected component labeling algorithm to determine if the zone is, in fact, split.
/zone/proc/IsolateContents()
var/list/current_adjacents = list()
var/adjacent_id
var/lowest_id
var/list/identical_ids = list()
var/list/turfs = contents.Copy()
var/current_identifier = 1
for(var/turf/simulated/current in turfs)
lowest_id = null
current_adjacents = list()
for(var/direction in cardinal)
var/turf/simulated/adjacent = get_step(current, direction)
if(!current.ZCanPass(adjacent))
continue
if(adjacent in turfs)
current_adjacents += adjacent
adjacent_id = turfs[adjacent]
if(adjacent_id && (!lowest_id || adjacent_id < lowest_id))
lowest_id = adjacent_id
/////// Z-Level stuff
var/turf/controllerlocation = locate(1, 1, current.z)
for(var/obj/effect/landmark/zcontroller/controller in controllerlocation)
// upwards
if(controller.up)
var/turf/simulated/adjacent = locate(current.x, current.y, controller.up_target)
if(adjacent in turfs && istype(adjacent, /turf/simulated/floor/open))
current_adjacents += adjacent
adjacent_id = turfs[adjacent]
if(adjacent_id && (!lowest_id || adjacent_id < lowest_id))
lowest_id = adjacent_id
// downwards
if(controller.down && istype(current, /turf/simulated/floor/open))
var/turf/simulated/adjacent = locate(current.x, current.y, controller.down_target)
if(adjacent in turfs)
current_adjacents += adjacent
adjacent_id = turfs[adjacent]
if(adjacent_id && (!lowest_id || adjacent_id < lowest_id))
lowest_id = adjacent_id
/////// Z-Level stuff
if(!lowest_id)
lowest_id = current_identifier++
identical_ids += lowest_id
for(var/turf/simulated/adjacent in current_adjacents)
adjacent_id = turfs[adjacent]
if(adjacent_id != lowest_id)
if(adjacent_id)
identical_ids[adjacent_id] = lowest_id
turfs[adjacent] = lowest_id
turfs[current] = lowest_id
var/list/final_arrangement = list()
for(var/turf/simulated/current in turfs)
current_identifier = identical_ids[turfs[current]]
if( current_identifier > final_arrangement.len )
final_arrangement.len = current_identifier
final_arrangement[current_identifier] = list(current)
else
//Sanity check.
if(!islist(final_arrangement[current_identifier]))
final_arrangement[current_identifier] = list()
final_arrangement[current_identifier] += current
//lazy but fast
final_arrangement.Remove(null)
return final_arrangement
/*
if(!RequiresRebuild())
return
//Choose a random turf and regenerate the zone from it.
var/list/new_contents
var/list/new_unsimulated
var/list/turfs_needing_zones = list()
var/list/zones_to_check_connections = list(src)
if(!locate(/turf/simulated/floor) in contents)
for(var/turf/simulated/turf in contents)
air_master.ReconsiderTileZone(turf)
return SoftDelete()
var/turfs_to_ignore = list()
if(direct_connections)
for(var/connection/connection in direct_connections)
if(connection.A.zone != src)
turfs_to_ignore += A
else if(connection.B.zone != src)
turfs_to_ignore += B
new_unsimulated = ( unsimulated_tiles ? unsimulated_tiles : list() )
//Now, we have allocated the new turfs into proper lists, and we can start actually rebuilding.
//If something isn't carried over, it will need a new zone.
for(var/turf/T in contents)
if(!(T in new_contents))
RemoveTurf(T)
turfs_needing_zones += T
//Handle addition of new turfs
for(var/turf/S in new_contents)
if(!istype(S, /turf/simulated))
new_unsimulated |= S
new_contents.Remove(S)
//If something new is added, we need to deal with it seperately.
else if(!(S in contents) && istype(S, /turf/simulated))
if(!(S.zone in zones_to_check_connections))
zones_to_check_connections += S.zone
S.zone.RemoveTurf(S)
AddTurf(S)
//Handle the addition of new unsimulated tiles.
unsimulated_tiles = null
if(new_unsimulated.len)
for(var/turf/S in new_unsimulated)
if(istype(S, /turf/simulated))
continue
for(var/direction in cardinal)
var/turf/simulated/T = get_step(S,direction)
if(istype(T) && T.zone && S.CanPass(null, T, 0, 0))
T.zone.AddTurf(S)
//Finally, handle the orphaned turfs
for(var/turf/simulated/T in turfs_needing_zones)
if(!T.zone)
zones_to_check_connections += new /zone(T)
for(var/zone/zone in zones_to_check_connections)
for(var/connection/C in zone.connections)
C.Cleanup()*/

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

@@ -0,0 +1,115 @@
/zone/var/name
/zone/var/invalid = 0
/zone/var/list/contents = list()
/zone/var/needs_update = 0
/zone/var/list/edges = list()
/zone/var/datum/gas_mixture/air = new
/zone/New()
air_master.add_zone(src)
air.temperature = TCMB
air.group_multiplier = 1
air.volume = CELL_VOLUME
/zone/proc/add(turf/simulated/T)
#ifdef ZASDBG
ASSERT(!invalid)
ASSERT(istype(T))
ASSERT(!air_master.has_valid_zone(T))
#endif
var/datum/gas_mixture/turf_air = T.return_air()
add_tile_air(turf_air)
T.zone = src
contents.Add(T)
T.set_graphic(air.graphic)
/zone/proc/remove(turf/simulated/T)
#ifdef ZASDBG
ASSERT(!invalid)
ASSERT(istype(T))
ASSERT(T.zone == src)
#endif
contents.Remove(T)
T.zone = null
if(contents.len)
air.group_multiplier = contents.len
else
c_invalidate()
/zone/proc/c_merge(zone/into)
#ifdef ZASDBG
ASSERT(!invalid)
ASSERT(istype(into))
ASSERT(into != src)
ASSERT(!into.invalid)
#endif
c_invalidate()
for(var/turf/simulated/T in contents)
into.add(T)
#ifdef ZASDBG
T.dbg(merged)
#endif
/zone/proc/c_invalidate()
invalid = 1
air_master.remove_zone(src)
#ifdef ZASDBG
for(var/turf/simulated/T in contents)
T.dbg(invalid_zone)
#endif
/zone/proc/rebuild()
c_invalidate()
for(var/turf/simulated/T in contents)
//T.dbg(invalid_zone)
T.needs_air_update = 0 //Reset the marker so that it will be added to the list.
air_master.mark_for_update(T)
/zone/proc/add_tile_air(datum/gas_mixture/tile_air)
//air.volume += CELL_VOLUME
air.group_multiplier = 1
air.multiply(contents.len)
air.merge(tile_air)
air.divide(contents.len+1)
air.group_multiplier = contents.len+1
/zone/proc/tick()
air.archive()
if(air.check_tile_graphic())
for(var/turf/simulated/T in contents)
T.set_graphic(air.graphic)
/zone/proc/remove_connection(connection/c)
return
/zone/proc/add_connection(connection/c)
return
/zone/proc/dbg_data(mob/M)
M << name
M << "O2: [air.oxygen] N2: [air.nitrogen] CO2: [air.carbon_dioxide] P: [air.toxins]"
M << "P: [air.return_pressure()] kPa V: [air.volume]L T: [air.temperature]<5D>K ([air.temperature - T0C]<5D>C)"
M << "O2 per N2: [(air.nitrogen ? air.oxygen/air.nitrogen : "N/A")] Moles: [air.total_moles]"
M << "Simulated: [contents.len] ([air.group_multiplier])"
//M << "Unsimulated: [unsimulated_contents.len]"
//M << "Edges: [edges.len]"
if(invalid) M << "Invalid!"
var/zone_edges = 0
var/space_edges = 0
var/space_coefficient = 0
for(var/connection_edge/E in edges)
if(E.type == /connection_edge/zone) zone_edges++
else
space_edges++
space_coefficient += E.coefficient
M << "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])"

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

@@ -0,0 +1,28 @@
/*
Zone Air System:
This air system divides the station into impermeable areas called zones.
When something happens, i.e. a door opening or a wall being taken down,
zones equalize and eventually merge. Making an airtight area closes the connection again.
Control Flow:
Every air tick:
Marked turfs are updated with update_air_properties(), followed by post_update_air_properties().
Edges, including those generated by connections in the previous step, are processed. This is where gas is exchanged.
Fire is processed.
Marked zones have their air archived.
Important Functions:
air_master.mark_for_update(turf)
When stuff happens, call this. It works on everything. You basically don't need to worry about any other
functions besides CanPass().
*/
//#define ZASDBG
#define AIR_BLOCKED 1
#define ZONE_BLOCKED 2
#define BLOCKED 3

View File

@@ -161,6 +161,8 @@
// The old system would loop through lists for a total of 5000 per function call, in an empty server. // The old system would loop through lists for a total of 5000 per function call, in an empty server.
// This new system will loop at around 1000 in an empty server. // This new system will loop at around 1000 in an empty server.
// SCREW THAT SHIT, we're not recursing.
/proc/get_mobs_in_view(var/R, var/atom/source) /proc/get_mobs_in_view(var/R, var/atom/source)
// Returns a list of mobs in range of R from source. Used in radio and say code. // Returns a list of mobs in range of R from source. Used in radio and say code.
@@ -172,18 +174,33 @@
var/list/range = hear(R, T) var/list/range = hear(R, T)
for(var/atom/A in range) for(var/mob/M in range)
if(ismob(A)) hear += M
var/mob/M = A
if(M.client) var/list/objects = list()
hear += M
//world.log << "Start = [M] - [get_turf(M)] - ([M.x], [M.y], [M.z])"
else if(istype(A, /obj/item/device/radio))
hear += A
if(isobj(A) || ismob(A)) for(var/obj/O in range) //Get a list of objects in hearing range. We'll check to see if any clients have their "eye" set to the object
hear |= recursive_mob_check(A, hear, 3, 1, 0, 1) objects += O
for(var/client/C in clients)
if(!istype(C) || !C.eye)
continue //I have no idea when this client check would be needed, but if this runtimes people won't hear anything
//So kinda paranoid about runtime avoidance.
if(istype(C.eye, /obj/machinery/camera))
continue //No microphones in cameras.
if(C.mob in hear)
continue
var/list/hear_and_objects = (hear|objects) //Combined these lists here instead of doing the combine 3 more times.
if(C.eye in hear_and_objects)
hear += C.mob
else if(C.mob.loc in hear_and_objects)
hear += C.mob
else if(C.mob.loc.loc in hear_and_objects)
hear += C.mob
return hear return hear

View File

@@ -6,6 +6,7 @@
var/rate var/rate
var/list/solars // for debugging purposes, references solars_list at the constructor var/list/solars // for debugging purposes, references solars_list at the constructor
var/nexttime = 3600 // Replacement for var/counter to force the sun to move every X IC minutes var/nexttime = 3600 // Replacement for var/counter to force the sun to move every X IC minutes
var/lastAngleUpdate
/datum/sun/New() /datum/sun/New()
@@ -28,15 +29,25 @@
counter = 0 */ counter = 0 */
angle = ((rate*world.time/100)%360 + 360)%360 angle = ((rate*world.time/100)%360 + 360)%360
/* /*
Yields a 45 - 75 IC minute rotational period Yields a 45 - 75 IC minute rotational period
Rotation rate can vary from 4.8 deg/min to 8 deg/min (288 to 480 deg/hr) Rotation rate can vary from 4.8 deg/min to 8 deg/min (288 to 480 deg/hr)
*/ */
// To prevent excess server load the server only updates the sun's sight lines every 6 minutes if(lastAngleUpdate != angle)
if(nexttime < world.time) for(var/obj/machinery/power/tracker/T in solars_list)
if(!T.powernet)
solars_list.Remove(T)
continue
T.set_angle(angle)
lastAngleUpdate=angle
if(nexttime > world.time)
return return
nexttime = nexttime + 3600 // 600 world.time ticks = 1 minute, 3600 = 6 minutes. nexttime = nexttime + 600 // 600 world.time ticks = 1 minute
// now calculate and cache the (dx,dy) increments for line drawing // now calculate and cache the (dx,dy) increments for line drawing
@@ -58,22 +69,13 @@
dy = c / abs(s) dy = c / abs(s)
for(var/obj/machinery/power/M in solars_list) for(var/obj/machinery/power/solar/S in solars_list)
if(!M.powernet) if(!S.powernet)
solars_list.Remove(M) solars_list.Remove(S)
continue continue
if(S.control)
// Solar Tracker occlusion(S)
if(istype(M, /obj/machinery/power/tracker))
var/obj/machinery/power/tracker/T = M
T.set_angle(angle)
// Solar Panel
else if(istype(M, /obj/machinery/power/solar))
var/obj/machinery/power/solar/S = M
if(S.control)
occlusion(S)

View File

@@ -792,6 +792,14 @@ var/list/ghostteleportlocs = list()
name = "\improper Security Dormitories" name = "\improper Security Dormitories"
icon_state = "Sleep" icon_state = "Sleep"
/area/crew_quarters/sleep/bedrooms
name = "\improper Dormitory Bedroom"
icon_state = "Sleep"
/area/crew_quarters/sleep/cryo
name = "\improper Cryogenic Storage"
icon_state = "Sleep"
/area/crew_quarters/sleep_male /area/crew_quarters/sleep_male
name = "\improper Male Dorm" name = "\improper Male Dorm"
icon_state = "Sleep" icon_state = "Sleep"

View File

@@ -51,6 +51,9 @@ var/global/list/datum/dna/gene/dna_genes[0]
///////////////// /////////////////
// GENE DEFINES // GENE DEFINES
///////////////// /////////////////
// Skip checking if it's already active.
// Used for genes that check for value rather than a binary on/off.
#define GENE_ALWAYS_ACTIVATE 1
// Skip checking if it's already active. // Skip checking if it's already active.
// Used for genes that check for value rather than a binary on/off. // Used for genes that check for value rather than a binary on/off.
@@ -96,7 +99,6 @@ var/global/list/datum/dna/gene/dna_genes[0]
new_dna.UpdateUI() new_dna.UpdateUI()
new_dna.UpdateSE() new_dna.UpdateSE()
return new_dna return new_dna
/////////////////////////////////////// ///////////////////////////////////////
// UNIQUE IDENTITY // UNIQUE IDENTITY
/////////////////////////////////////// ///////////////////////////////////////
@@ -150,7 +152,7 @@ var/global/list/datum/dna/gene/dna_genes[0]
// Set a DNA UI block's raw value. // Set a DNA UI block's raw value.
/datum/dna/proc/SetUIValue(var/block,var/value,var/defer=0) /datum/dna/proc/SetUIValue(var/block,var/value,var/defer=0)
if (block<=0) return if (block<=0) return
ASSERT(value>=0) ASSERT(value>0)
ASSERT(value<=4095) ASSERT(value<=4095)
UI[block]=value UI[block]=value
dirtyUI=1 dirtyUI=1
@@ -166,6 +168,7 @@ var/global/list/datum/dna/gene/dna_genes[0]
// Used in hair and facial styles (value being the index and maxvalue being the len of the hairstyle list) // Used in hair and facial styles (value being the index and maxvalue being the len of the hairstyle list)
/datum/dna/proc/SetUIValueRange(var/block,var/value,var/maxvalue,var/defer=0) /datum/dna/proc/SetUIValueRange(var/block,var/value,var/maxvalue,var/defer=0)
if (block<=0) return if (block<=0) return
if (value==0) value = 1 // FIXME: hair/beard/eye RGB values if they are 0 are not set, this is a work around we'll encode it in the DNA to be 1 instead.
ASSERT(maxvalue<=4095) ASSERT(maxvalue<=4095)
var/range = (4095 / maxvalue) var/range = (4095 / maxvalue)
if(value) if(value)

View File

@@ -143,8 +143,7 @@
H.g_eyes = dna.GetUIValueRange(DNA_UI_EYES_G, 255) H.g_eyes = dna.GetUIValueRange(DNA_UI_EYES_G, 255)
H.b_eyes = dna.GetUIValueRange(DNA_UI_EYES_B, 255) H.b_eyes = dna.GetUIValueRange(DNA_UI_EYES_B, 255)
var/new_s_tone = dna.GetUIValueRange(DNA_UI_SKIN_TONE, 220) H.s_tone = 35 - dna.GetUIValueRange(DNA_UI_SKIN_TONE, 220) // Value can be negative.
H.s_tone = 35 - max(min( round(new_s_tone), 220),1) //Warning MATH. Blame the person that wrote modules/client/preferences.dm, line 994
if (dna.GetUIState(DNA_UI_GENDER)) if (dna.GetUIState(DNA_UI_GENDER))
H.gender = FEMALE H.gender = FEMALE
@@ -171,5 +170,3 @@
// Used below, simple injection modifier. // Used below, simple injection modifier.
/proc/probinj(var/pr, var/inj) /proc/probinj(var/pr, var/inj)
return prob(pr+inj*pr) return prob(pr+inj*pr)
/////////////////////////// DNA MISC-PROCS

View File

@@ -32,7 +32,7 @@
M.mutations.Add(mutation) M.mutations.Add(mutation)
if(disability) if(disability)
M.disabilities|=disability M.disabilities|=disability
if(sdisability) if(mutation)
M.sdisabilities|=sdisability M.sdisabilities|=sdisability
if(activation_message) if(activation_message)
M << "\red [activation_message]" M << "\red [activation_message]"
@@ -43,9 +43,9 @@
if(mutation && (mutation in M.mutations)) if(mutation && (mutation in M.mutations))
M.mutations.Remove(mutation) M.mutations.Remove(mutation)
if(disability) if(disability)
M.disabilities &= ~disability M.disabilities-=disability
if(sdisability) if(mutation)
M.sdisabilities &= ~sdisability M.sdisabilities-=sdisability
if(deactivation_message) if(deactivation_message)
M << "\red [deactivation_message]" M << "\red [deactivation_message]"
else else
@@ -126,4 +126,4 @@
disability=NEARSIGHTED disability=NEARSIGHTED
New() New()
block=GLASSESBLOCK block=GLASSESBLOCK

View File

@@ -191,6 +191,5 @@
New() New()
block=TELEBLOCK block=TELEBLOCK
OnDrawUnderlays(var/mob/M,var/g,var/fat) OnDrawUnderlays(var/mob/M,var/g,var/fat)
return "telekinesishead[fat]_s" return "telekinesishead[fat]_s"

View File

@@ -137,9 +137,12 @@
newtraitor.mind.special_role = "traitor" newtraitor.mind.special_role = "traitor"
var/obj_count = 1 var/obj_count = 1
newtraitor << "\blue Your current objectives:" newtraitor << "\blue Your current objectives:"
for(var/datum/objective/objective in newtraitor.mind.objectives) if(!config.objectives_disabled)
newtraitor << "<B>Objective #[obj_count]</B>: [objective.explanation_text]" for(var/datum/objective/objective in newtraitor.mind.objectives)
obj_count++ newtraitor << "<B>Objective #[obj_count]</B>: [objective.explanation_text]"
obj_count++
else
newtraitor << "<i>You have been selected this round as an antagonist- <font color=blue>Within the rules,</font> try to act as an opposing force to the crew- This can be via corporate payoff, personal motives, or maybe just being a dick. Further RP and try to make sure other players have </i>fun<i>! If you are confused or at a loss, always adminhelp, and before taking extreme actions, please try to also contact the administration! Think through your actions and make the roleplay immersive! <b>Please remember all rules aside from those without explicit exceptions apply to antagonist.</i></b>"
//else //else
//message_admins("No new traitor being added.") //message_admins("No new traitor being added.")
//else //else

View File

@@ -254,7 +254,7 @@
if (!R) if (!R)
traitor_mob << "Unfortunately, neither a radio or a PDA relay could be installed." traitor_mob << "Unfortunately, neither a radio or a PDA relay could be installed."
if(traitor_mob.client.prefs.uplinklocation == "PDA") else if(traitor_mob.client.prefs.uplinklocation == "PDA")
R = locate(/obj/item/device/pda) in traitor_mob.contents R = locate(/obj/item/device/pda) in traitor_mob.contents
if(!R) if(!R)
R = locate(/obj/item/device/radio) in traitor_mob.contents R = locate(/obj/item/device/radio) in traitor_mob.contents
@@ -262,10 +262,19 @@
if (!R) if (!R)
traitor_mob << "Unfortunately, neither a radio or a PDA relay could be installed." traitor_mob << "Unfortunately, neither a radio or a PDA relay could be installed."
if(traitor_mob.client.prefs.uplinklocation == "None") else if(traitor_mob.client.prefs.uplinklocation == "None")
traitor_mob << "You have elected to not have an AntagCorp portable teleportation relay installed!" traitor_mob << "You have elected to not have an AntagCorp portable teleportation relay installed!"
R = null R = null
else
traitor_mob << "You have not selected a location for your relay in the antagonist options! Defaulting to PDA!"
R = locate(/obj/item/device/pda) in traitor_mob.contents
if (!R)
R = locate(/obj/item/device/radio) in traitor_mob.contents
traitor_mob << "Could not locate a PDA, installing into a Radio instead!"
if (!R)
traitor_mob << "Unfortunately, neither a radio or a PDA relay could be installed."
if (!R) if (!R)
. = 0 . = 0
else else

View File

@@ -17,6 +17,7 @@
volume = 1000 volume = 1000
use_power = 0 use_power = 0
var/release_log = "" var/release_log = ""
var/update_flag = 0
/obj/machinery/portable_atmospherics/canister/sleeping_agent /obj/machinery/portable_atmospherics/canister/sleeping_agent
name = "Canister: \[N2O\]" name = "Canister: \[N2O\]"
@@ -49,30 +50,64 @@
canister_color = "grey" canister_color = "grey"
can_label = 0 can_label = 0
/obj/machinery/portable_atmospherics/canister/proc/check_change()
var/old_flag = update_flag
update_flag = 0
if(holding)
update_flag |= 1
if(connected_port)
update_flag |= 2
var/tank_pressure = air_contents.return_pressure()
if(tank_pressure < 10)
update_flag |= 4
else if(tank_pressure < ONE_ATMOSPHERE)
update_flag |= 8
else if(tank_pressure < 15*ONE_ATMOSPHERE)
update_flag |= 16
else
update_flag |= 32
if(update_flag == old_flag)
return 1
else
return 0
/obj/machinery/portable_atmospherics/canister/update_icon() /obj/machinery/portable_atmospherics/canister/update_icon()
src.overlays = 0 /*
update_flag
1 = holding
2 = connected_port
4 = tank_pressure < 10
8 = tank_pressure < ONE_ATMOS
16 = tank_pressure < 15*ONE_ATMOS
32 = tank_pressure go boom.
*/
if (src.destroyed) if (src.destroyed)
src.overlays = 0
src.icon_state = text("[]-1", src.canister_color) src.icon_state = text("[]-1", src.canister_color)
else if(icon_state != "[canister_color]")
icon_state = "[canister_color]" icon_state = "[canister_color]"
if(holding)
overlays += "can-open" if(check_change()) //Returns 1 if no change needed to icons.
return
if(connected_port) src.overlays = 0
overlays += "can-connector"
var/tank_pressure = air_contents.return_pressure() if(update_flag & 1)
overlays += "can-open"
if (tank_pressure < 10) if(update_flag & 2)
overlays += image('icons/obj/atmos.dmi', "can-o0") overlays += "can-connector"
else if (tank_pressure < ONE_ATMOSPHERE) if(update_flag & 4)
overlays += image('icons/obj/atmos.dmi', "can-o1") overlays += "can-o0"
else if (tank_pressure < 15*ONE_ATMOSPHERE) if(update_flag & 8)
overlays += image('icons/obj/atmos.dmi', "can-o2") overlays += "can-o1"
else else if(update_flag & 16)
overlays += image('icons/obj/atmos.dmi', "can-o3") overlays += "can-o2"
else if(update_flag & 32)
overlays += "can-o3"
return return
/obj/machinery/portable_atmospherics/canister/temperature_expose(datum/gas_mixture/air, exposed_temperature, exposed_volume) /obj/machinery/portable_atmospherics/canister/temperature_expose(datum/gas_mixture/air, exposed_temperature, exposed_volume)

View File

@@ -268,7 +268,7 @@
for(var/turf/simulated/turf in locs) for(var/turf/simulated/turf in locs)
update_heat_protection(turf) update_heat_protection(turf)
air_master.AddTurfToUpdate(turf) air_master.mark_for_update(turf)
return 1 return 1

View File

@@ -7,6 +7,14 @@
dir = 1 dir = 1
explosion_resistance = 25 explosion_resistance = 25
/obj/machinery/door/poddoor/New()
. = ..()
if(density)
layer = 3.3 //to override door.New() proc
else
layer = initial(layer)
return
/obj/machinery/door/poddoor/Bumped(atom/AM) /obj/machinery/door/poddoor/Bumped(atom/AM)
if(!density) if(!density)
return ..() return ..()

View File

@@ -18,7 +18,7 @@
if(!air_master) if(!air_master)
return 0 return 0
air_master.AddTurfToUpdate(get_turf(src)) air_master.mark_for_update(get_turf(src))
return 1 return 1

View File

@@ -17,7 +17,7 @@
ui = nanomanager.try_update_ui(user, src, ui_key, ui, data) ui = nanomanager.try_update_ui(user, src, ui_key, ui, data)
if (!ui) if (!ui)
ui = new(user, src, ui_key, "advanced_airlock_console.tmpl", name, 470, 300) ui = new(user, src, ui_key, "advanced_airlock_console.tmpl", name, 470, 290)
ui.set_initial_data(data) ui.set_initial_data(data)
@@ -59,13 +59,15 @@
data = list( data = list(
"chamber_pressure" = round(program.memory["chamber_sensor_pressure"]), "chamber_pressure" = round(program.memory["chamber_sensor_pressure"]),
"exterior_status" = program.memory["exterior_status"],
"interior_status" = program.memory["interior_status"],
"processing" = program.memory["processing"], "processing" = program.memory["processing"],
) )
ui = nanomanager.try_update_ui(user, src, ui_key, ui, data) ui = nanomanager.try_update_ui(user, src, ui_key, ui, data)
if (!ui) if (!ui)
ui = new(user, src, ui_key, "simple_airlock_console.tmpl", name, 470, 300) ui = new(user, src, ui_key, "simple_airlock_console.tmpl", name, 470, 290)
ui.set_initial_data(data) ui.set_initial_data(data)
@@ -117,14 +119,13 @@
data = list( data = list(
"exterior_status" = program.memory["exterior_status"], "exterior_status" = program.memory["exterior_status"],
"interior_status" = program.memory["interior_status"], "interior_status" = program.memory["interior_status"],
"processing" = program.memory["processing"], "processing" = program.memory["processing"]
"secure" = program.memory["secure"],
) )
ui = nanomanager.try_update_ui(user, src, ui_key, ui, data) ui = nanomanager.try_update_ui(user, src, ui_key, ui, data)
if (!ui) if (!ui)
ui = new(user, src, ui_key, "door_access_console.tmpl", name, 470, 300) ui = new(user, src, ui_key, "door_access_console.tmpl", name, 330, 220)
ui.set_initial_data(data) ui.set_initial_data(data)
@@ -140,11 +141,11 @@
if("cycle_int_door") if("cycle_int_door")
clean = 1 clean = 1
if("force_ext") if("force_ext")
clean = 1 if(program.memory["interior_status"]["state"] == "closed")
clean = 1
if("force_int") if("force_int")
clean = 1 if(program.memory["exterior_status"]["state"] == "closed")
if("secure") clean = 1
clean = 1
if(clean) if(clean)
program.receive_user_command(href_list["command"]) program.receive_user_command(href_list["command"])

View File

@@ -124,9 +124,21 @@
if("purge") if("purge")
memory["purge"] = !memory["purge"] memory["purge"] = !memory["purge"]
if(memory["purge"])
toggleDoor(memory["exterior_status"], tag_exterior_door, 1, "close")
toggleDoor(memory["interior_status"], tag_interior_door, 1, "close")
state = STATE_DEPRESSURIZE
target_state = TARGET_NONE
signalPump(tag_airpump, 1, 0, 0)
if("secure") if("secure")
memory["secure"] = !memory["secure"] memory["secure"] = !memory["secure"]
if(memory["secure"])
signalDoor(tag_interior_door, "lock")
signalDoor(tag_exterior_door, "lock")
else
signalDoor(tag_interior_door, "unlock")
signalDoor(tag_exterior_door, "unlock")
if(shutdown_pump) if(shutdown_pump)
signalPump(tag_airpump, 0) //send a signal to stop pressurizing signalPump(tag_airpump, 0) //send a signal to stop pressurizing
@@ -233,6 +245,12 @@
if(TARGET_INOPEN) if(TARGET_INOPEN)
toggleDoor(memory["exterior_status"], tag_exterior_door, memory["secure"], "close") toggleDoor(memory["exterior_status"], tag_exterior_door, memory["secure"], "close")
toggleDoor(memory["interior_status"], tag_interior_door, memory["secure"], "open") toggleDoor(memory["interior_status"], tag_interior_door, memory["secure"], "open")
if(TARGET_NONE)
var/command = "unlock"
if(memory["secure"])
command = "lock"
signalDoor(tag_exterior_door, command)
signalDoor(tag_interior_door, command)
/*---------------------------------------------------------- /*----------------------------------------------------------

View File

@@ -47,6 +47,7 @@ Buildable meters
if (make_from) if (make_from)
src.dir = make_from.dir src.dir = make_from.dir
src.pipename = make_from.name src.pipename = make_from.name
color = make_from.color
var/is_bent var/is_bent
if (make_from.initialize_directions in list(NORTH|SOUTH, WEST|EAST)) if (make_from.initialize_directions in list(NORTH|SOUTH, WEST|EAST))
is_bent = 0 is_bent = 0
@@ -299,6 +300,7 @@ Buildable meters
switch(pipe_type) switch(pipe_type)
if(PIPE_SIMPLE_STRAIGHT, PIPE_SIMPLE_BENT) if(PIPE_SIMPLE_STRAIGHT, PIPE_SIMPLE_BENT)
var/obj/machinery/atmospherics/pipe/simple/P = new( src.loc ) var/obj/machinery/atmospherics/pipe/simple/P = new( src.loc )
P.color = color
P.dir = src.dir P.dir = src.dir
P.initialize_directions = pipe_dir P.initialize_directions = pipe_dir
var/turf/T = P.loc var/turf/T = P.loc
@@ -351,6 +353,7 @@ Buildable meters
if(PIPE_MANIFOLD) //manifold if(PIPE_MANIFOLD) //manifold
var/obj/machinery/atmospherics/pipe/manifold/M = new( src.loc ) var/obj/machinery/atmospherics/pipe/manifold/M = new( src.loc )
M.color = color
M.dir = dir M.dir = dir
M.initialize_directions = pipe_dir M.initialize_directions = pipe_dir
//M.New() //M.New()
@@ -373,6 +376,7 @@ Buildable meters
if(PIPE_MANIFOLD4W) //4-way manifold if(PIPE_MANIFOLD4W) //4-way manifold
var/obj/machinery/atmospherics/pipe/manifold4w/M = new( src.loc ) var/obj/machinery/atmospherics/pipe/manifold4w/M = new( src.loc )
M.color = color
M.dir = dir M.dir = dir
M.initialize_directions = pipe_dir M.initialize_directions = pipe_dir
//M.New() //M.New()

View File

@@ -30,7 +30,7 @@
if(!air_master) if(!air_master)
return 0 return 0
air_master.AddTurfToUpdate(get_turf(src)) air_master.mark_for_update(get_turf(src))
return 1 return 1

View File

@@ -178,8 +178,8 @@
T = 0 T = 0
for(var/obj/item/weapon/stock_parts/manipulator/Ml in component_parts) for(var/obj/item/weapon/stock_parts/manipulator/Ml in component_parts)
T += Ml.rating T += Ml.rating
if(T>= 2) if(T>= 1)
T -= 2 T -= 1
diff = round(initial(time_coeff) - (initial(time_coeff)*(T))/25,0.01) diff = round(initial(time_coeff) - (initial(time_coeff)*(T))/25,0.01)
if(time_coeff!=diff) if(time_coeff!=diff)
time_coeff = diff time_coeff = diff

View File

@@ -833,7 +833,7 @@ steam.start() -- spawns the effect
if(!air_master) if(!air_master)
return 0 return 0
air_master.AddTurfToUpdate(get_turf(src)) air_master.mark_for_update(get_turf(src))
return 1 return 1

View File

@@ -41,7 +41,7 @@ var/global/list/obj/item/device/pda/PDAs = list()
var/active_conversation = null // New variable that allows us to only view a single conversation. var/active_conversation = null // New variable that allows us to only view a single conversation.
var/list/conversations = list() // For keeping up with who we have PDA messsages from. var/list/conversations = list() // For keeping up with who we have PDA messsages from.
var/newmessage = 0 //To remove hackish overlay check var/newmessage = 0 //To remove hackish overlay check
var/obj/item/weapon/card/id/id = null //Making it possible to slot an ID card into the PDA so it can function as both. var/obj/item/weapon/card/id/id = null //Making it possible to slot an ID card into the PDA so it can function as both.
var/ownjob = null //related to above var/ownjob = null //related to above
@@ -321,7 +321,7 @@ var/global/list/obj/item/device/pda/PDAs = list()
/obj/item/device/pda/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null) /obj/item/device/pda/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null)
var/title = "Personal Data Assistant" var/title = "Personal Data Assistant"
var/data[0] // This is the data that will be sent to the PDA var/data[0] // This is the data that will be sent to the PDA
@@ -336,7 +336,7 @@ var/global/list/obj/item/device/pda/PDAs = list()
data["silent"] = silent // does the pda make noise when it receives a message? data["silent"] = silent // does the pda make noise when it receives a message?
data["toff"] = toff // is the messenger function turned off? data["toff"] = toff // is the messenger function turned off?
data["active_conversation"] = active_conversation // Which conversation are we following right now? data["active_conversation"] = active_conversation // Which conversation are we following right now?
data["idInserted"] = (id ? 1 : 0) data["idInserted"] = (id ? 1 : 0)
data["idLink"] = (id ? text("[id.registered_name], [id.assignment]") : "--------") data["idLink"] = (id ? text("[id.registered_name], [id.assignment]") : "--------")
@@ -375,10 +375,10 @@ var/global/list/obj/item/device/pda/PDAs = list()
cartdata["type"] = cartridge.type cartdata["type"] = cartridge.type
cartdata["charges"] = cartridge.charges ? cartridge.charges : 0 cartdata["charges"] = cartridge.charges ? cartridge.charges : 0
data["cartridge"] = cartdata data["cartridge"] = cartdata
data["stationTime"] = worldtime2text() data["stationTime"] = worldtime2text()
data["newMessage"] = newmessage data["newMessage"] = newmessage
if(mode==2) if(mode==2)
var/convopdas[0] var/convopdas[0]
var/pdas[0] var/pdas[0]
@@ -388,7 +388,7 @@ var/global/list/obj/item/device/pda/PDAs = list()
if(conversations.Find("\ref[P]")) if(conversations.Find("\ref[P]"))
convopdas.Add(list(list("Name" = "[P]", "Reference" = "\ref[P]", "Detonate" = "[P.detonate]", "inconvo" = "1"))) convopdas.Add(list(list("Name" = "[P]", "Reference" = "\ref[P]", "Detonate" = "[P.detonate]", "inconvo" = "1")))
else else
pdas.Add(list(list("Name" = "[P]", "Reference" = "\ref[P]", "Detonate" = "[P.detonate]", "inconvo" = "0"))) pdas.Add(list(list("Name" = "[P]", "Reference" = "\ref[P]", "Detonate" = "[P.detonate]", "inconvo" = "0")))
count++ count++
data["convopdas"] = convopdas data["convopdas"] = convopdas
@@ -411,7 +411,7 @@ var/global/list/obj/item/device/pda/PDAs = list()
if(mode==41) if(mode==41)
data["manifest"] = data_core.get_manifest_json() data["manifest"] = data_core.get_manifest_json()
if(mode==3) if(mode==3)
var/turf/T = get_turf_or_move(user.loc) var/turf/T = get_turf_or_move(user.loc)
if(!isnull(T) || mode!=3) if(!isnull(T) || mode!=3)
@@ -438,15 +438,15 @@ var/global/list/obj/item/device/pda/PDAs = list()
) )
if(isnull(data["aircontents"])) if(isnull(data["aircontents"]))
data["aircontents"] = list("reading" = 0) data["aircontents"] = list("reading" = 0)
// update the ui if it exists, returns null if no ui is passed/found // update the ui if it exists, returns null if no ui is passed/found
ui = nanomanager.try_update_ui(user, src, ui_key, ui, data) ui = nanomanager.try_update_ui(user, src, ui_key, ui, data)
if (!ui) if (!ui)
// the ui does not exist, so we'll create a new() one // the ui does not exist, so we'll create a new() one
// for a list of parameters and their descriptions see the code docs in \code\modules\nano\nanoui.dm // for a list of parameters and their descriptions see the code docs in \code\modules\nano\nanoui.dm
ui = new(user, src, ui_key, "pda.tmpl", title, 520, 400) ui = new(user, src, ui_key, "pda.tmpl", title, 520, 400)
// when the ui is first opened this is the data it will use // when the ui is first opened this is the data it will use
ui.set_initial_data(data) ui.set_initial_data(data)
// open the new ui window // open the new ui window
ui.open() ui.open()
// auto update every Master Controller tick // auto update every Master Controller tick
@@ -484,17 +484,17 @@ var/global/list/obj/item/device/pda/PDAs = list()
U.unset_machine() U.unset_machine()
ui.close() ui.close()
return 0 return 0
add_fingerprint(U) add_fingerprint(U)
U.set_machine(src) U.set_machine(src)
switch(href_list["choice"]) switch(href_list["choice"])
//BASIC FUNCTIONS=================================== //BASIC FUNCTIONS===================================
if("Close")//Self explanatory if("Close")//Self explanatory
U.unset_machine() U.unset_machine()
ui.close() ui.close()
return 0 return 0
if("Refresh")//Refresh, goes to the end of the proc. if("Refresh")//Refresh, goes to the end of the proc.
if("Return")//Return if("Return")//Return
@@ -518,7 +518,7 @@ var/global/list/obj/item/device/pda/PDAs = list()
var/turf/T = loc var/turf/T = loc
if(ismob(T)) if(ismob(T))
T = T.loc T = T.loc
cartridge.loc = T cartridge.loc = T
mode = 0 mode = 0
scanmode = 0 scanmode = 0
if (cartridge.radio) if (cartridge.radio)
@@ -681,14 +681,40 @@ var/global/list/obj/item/device/pda/PDAs = list()
else else
M.close() M.close()
if("Detonate")//Detonate PDA if("Detonate")//Detonate PDA... maybe
// check if telecomms I/O route 1459 is stable
//var/telecomms_intact = telecomms_process(P.owner, owner, t)
var/obj/machinery/message_server/useMS = null
if(message_servers)
for (var/obj/machinery/message_server/MS in message_servers)
//PDAs are now dependant on the Message Server.
if(MS.active)
useMS = MS
break
var/datum/signal/signal = src.telecomms_process()
var/useTC = 0
if(signal)
if(signal.data["done"])
useTC = 1
var/turf/pos = get_turf(src)
if(pos.z in signal.data["level"])
useTC = 2
if(istype(cartridge, /obj/item/weapon/cartridge/syndicate)) if(istype(cartridge, /obj/item/weapon/cartridge/syndicate))
if(!(useMS && useTC))
U.show_message("\red An error flashes on your [src]: Connection unavailable", 1)
return
if(useTC != 2) // Does our recepient have a broadcaster on their level?
U.show_message("\red An error flashes on your [src]: Recipient unavailable", 1)
return
var/obj/item/device/pda/P = locate(href_list["target"]) var/obj/item/device/pda/P = locate(href_list["target"])
if(!isnull(P)) if(!isnull(P))
if (!P.toff && cartridge.charges > 0) if (!P.toff && cartridge.charges > 0)
cartridge.charges-- cartridge.charges--
var/difficulty = 0 var/difficulty = 2
if(P.cartridge) if(P.cartridge)
difficulty += P.cartridge.access_medical difficulty += P.cartridge.access_medical
@@ -696,22 +722,23 @@ var/global/list/obj/item/device/pda/PDAs = list()
difficulty += P.cartridge.access_engine difficulty += P.cartridge.access_engine
difficulty += P.cartridge.access_clown difficulty += P.cartridge.access_clown
difficulty += P.cartridge.access_janitor difficulty += P.cartridge.access_janitor
else difficulty += 3 * P.hidden_uplink
difficulty += 2
if(prob(difficulty * 12) || (P.hidden_uplink)) if(prob(difficulty))
U.show_message("\red An error flashes on your [src].", 1) U.show_message("\red An error flashes on your [src].", 1)
else if (prob(difficulty * 3)) else if (prob(difficulty * 7))
U.show_message("\red Energy feeds back into your [src]!", 1) U.show_message("\red Energy feeds back into your [src]!", 1)
ui.close() ui.close()
explode() detonate_act(src)
log_admin("[key_name(U)] just attempted to blow up [P] with the Detomatix cartridge but failed, blowing themselves up") log_admin("[key_name(U)] just attempted to blow up [P] with the Detomatix cartridge but failed, blowing themselves up")
message_admins("[key_name_admin(U)] just attempted to blow up [P] with the Detomatix cartridge but failed, blowing themselves up", 1) message_admins("[key_name_admin(U)] just attempted to blow up [P] with the Detomatix cartridge but failed.", 1)
else else
U.show_message("\blue Success!", 1) U.show_message("\blue Success!", 1)
log_admin("[key_name(U)] just attempted to blow up [P] with the Detomatix cartridge and succeded") log_admin("[key_name(U)] just attempted to blow up [P] with the Detomatix cartridge and succeeded")
message_admins("[key_name_admin(U)] just attempted to blow up [P] with the Detomatix cartridge and succeded", 1) message_admins("[key_name_admin(U)] just attempted to blow up [P] with the Detomatix cartridge and succeeded.", 1)
P.explode() detonate_act(P)
else
U << "No charges left."
else else
U << "PDA not found." U << "PDA not found."
else else
@@ -747,6 +774,70 @@ var/global/list/obj/item/device/pda/PDAs = list()
return 1 // return 1 tells it to refresh the UI in NanoUI return 1 // return 1 tells it to refresh the UI in NanoUI
/obj/item/device/pda/proc/detonate_act(var/obj/item/device/pda/P)
//TODO: sometimes these attacks show up on the message server
var/i = rand(1,100)
var/j = rand(0,1) //Possibility of losing the PDA after the detonation
var/message = ""
var/mob/living/M = null
if(ismob(P.loc))
M = P.loc
//switch(i) //Yes, the overlapping cases are intended.
if(i<=10) //The traditional explosion
P.explode()
j=1
message += "Your [P] suddenly explodes!"
if(i>=10 && i<= 20) //The PDA burns a hole in the holder.
j=1
if(M && isliving(M))
M.apply_damage( rand(30,60) , BURN)
message += "You feel a searing heat! Your [P] is burning!"
if(i>=20 && i<=25) //EMP
empulse(P.loc, 3, 6, 1)
message += "Your [P] emits a wave of electomagnetic energy!"
if(i>=25 && i<=40) //Smoke
var/datum/effect/effect/system/smoke_spread/chem/S = new /datum/effect/effect/system/smoke_spread/chem
S.attach(P.loc)
S.set_up(P, 10, 0, P.loc)
playsound(P.loc, 'sound/effects/smoke.ogg', 50, 1, -3)
S.start()
message += "Large clouds of smoke billow forth from your [P]!"
if(i>=40 && i<=45) //Bad smoke
var/datum/effect/effect/system/smoke_spread/bad/B = new /datum/effect/effect/system/smoke_spread/bad
B.attach(P.loc)
B.set_up(P, 10, 0, P.loc)
playsound(P.loc, 'sound/effects/smoke.ogg', 50, 1, -3)
B.start()
message += "Large clouds of noxious smoke billow forth from your [P]!"
if(i>=65 && i<=75) //Weaken
if(M && isliving(M))
M.apply_effects(0,1)
message += "Your [P] flashes with a blinding white light! You feel weaker."
if(i>=75 && i<=85) //Stun and stutter
if(M && isliving(M))
M.apply_effects(1,0,0,0,1)
message += "Your [P] flashes with a blinding white light! You feel weaker."
if(i>=85) //Sparks
var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread
s.set_up(2, 1, P.loc)
s.start()
message += "Your [P] begins to spark violently!"
if(i>45 && i<65 && prob(50)) //Nothing happens
message += "Your [P] bleeps loudly."
j = prob(10)
if(j) //This kills the PDA
P.Del()
if(message)
message += "It melts in a puddle of plastic."
else
message += "Your [P] shatters in a thousand pieces!"
if(M && isliving(M))
message = "\red" + message
M.show_message(message, 1)
/obj/item/device/pda/proc/remove_id() /obj/item/device/pda/proc/remove_id()
if (id) if (id)
if (ismob(loc)) if (ismob(loc))
@@ -774,7 +865,7 @@ var/global/list/obj/item/device/pda/PDAs = list()
if(!can_use()) if(!can_use())
return return
last_text = world.time last_text = world.time
// check if telecomms I/O route 1459 is stable // check if telecomms I/O route 1459 is stable
//var/telecomms_intact = telecomms_process(P.owner, owner, t) //var/telecomms_intact = telecomms_process(P.owner, owner, t)
@@ -801,13 +892,15 @@ var/global/list/obj/item/device/pda/PDAs = list()
if(useMS && useTC) // only send the message if it's stable if(useMS && useTC) // only send the message if it's stable
if(useTC != 2) // Does our recepient have a broadcaster on their level? if(useTC != 2) // Does our recepient have a broadcaster on their level?
U << "ERROR: Cannot reach recepient." U << "ERROR: Cannot reach recipient."
return return
useMS.send_pda_message("[P.owner]","[owner]","[t]") useMS.send_pda_message("[P.owner]","[owner]","[t]")
tnote.Add(list(list("sent" = 1, "owner" = "[P.owner]", "job" = "[P.ownjob]", "message" = "[t]", "target" = "\ref[P]"))) tnote.Add(list(list("sent" = 1, "owner" = "[P.owner]", "job" = "[P.ownjob]", "message" = "[t]", "target" = "\ref[P]")))
P.tnote.Add(list(list("sent" = 0, "owner" = "[owner]", "job" = "[ownjob]", "message" = "[t]", "target" = "\ref[src]"))) P.tnote.Add(list(list("sent" = 0, "owner" = "[owner]", "job" = "[ownjob]", "message" = "[t]", "target" = "\ref[src]")))
for(var/mob/M in player_list) for(var/mob/M in player_list)
if(M.stat == DEAD && M.client && (M.client.prefs.toggles & CHAT_GHOSTEARS)) // src.client is so that ghosts don't have to listen to mice if(M.stat == DEAD && M.client && (M.client.prefs.toggles & CHAT_GHOSTEARS)) // src.client is so that ghosts don't have to listen to mice
if(istype(M, /mob/new_player))
continue
M.show_message("<span class='game say'>PDA Message - <span class='name'>[owner]</span> -> <span class='name'>[P.owner]</span>: <span class='message'>[t]</span></span>") M.show_message("<span class='game say'>PDA Message - <span class='name'>[owner]</span> -> <span class='name'>[P.owner]</span>: <span class='message'>[t]</span></span>")
if(!conversations.Find("\ref[P]")) if(!conversations.Find("\ref[P]"))
@@ -825,7 +918,7 @@ var/global/list/obj/item/device/pda/PDAs = list()
if(ai.aiPDA != P && ai.aiPDA != src) if(ai.aiPDA != P && ai.aiPDA != src)
ai.show_message("<i>Intercepted message from <b>[who]</b>: [t]</i>") ai.show_message("<i>Intercepted message from <b>[who]</b>: [t]</i>")
if (!P.silent) if (!P.silent)
playsound(P.loc, 'sound/machines/twobeep.ogg', 50, 1) playsound(P.loc, 'sound/machines/twobeep.ogg', 50, 1)
for (var/mob/O in hearers(3, P.loc)) for (var/mob/O in hearers(3, P.loc))
@@ -842,7 +935,7 @@ var/global/list/obj/item/device/pda/PDAs = list()
if(L) if(L)
L << "\icon[P] <b>Message from [src.owner] ([ownjob]), </b>\"[t]\" (<a href='byond://?src=\ref[P];choice=Message;skiprefresh=1;target=\ref[src]'>Reply</a>)" L << "\icon[P] <b>Message from [src.owner] ([ownjob]), </b>\"[t]\" (<a href='byond://?src=\ref[P];choice=Message;skiprefresh=1;target=\ref[src]'>Reply</a>)"
nanomanager.update_user_uis(L, P) // Update the recieving user's PDA UI so that they can see the new message nanomanager.update_user_uis(L, P) // Update the recieving user's PDA UI so that they can see the new message
nanomanager.update_user_uis(U, P) // Update the sending user's PDA UI so that they can see the new message nanomanager.update_user_uis(U, P) // Update the sending user's PDA UI so that they can see the new message
log_pda("[usr] (PDA: [src.name]) sent \"[t]\" to [P.name]") log_pda("[usr] (PDA: [src.name]) sent \"[t]\" to [P.name]")
@@ -924,7 +1017,7 @@ var/global/list/obj/item/device/pda/PDAs = list()
user << "<span class='notice'>You insert [cartridge] into [src].</span>" user << "<span class='notice'>You insert [cartridge] into [src].</span>"
nanomanager.update_uis(src) // update all UIs attached to src nanomanager.update_uis(src) // update all UIs attached to src
if(cartridge.radio) if(cartridge.radio)
cartridge.radio.hostpda = src cartridge.radio.hostpda = src
else if(istype(C, /obj/item/weapon/card/id)) else if(istype(C, /obj/item/weapon/card/id))
var/obj/item/weapon/card/id/idcard = C var/obj/item/weapon/card/id/idcard = C
@@ -1098,25 +1191,17 @@ var/global/list/obj/item/device/pda/PDAs = list()
user << "\blue Paper scanned." //concept of scanning paper copyright brainoblivion 2009 user << "\blue Paper scanned." //concept of scanning paper copyright brainoblivion 2009
/obj/item/device/pda/proc/explode() //This needs tuning. /obj/item/device/pda/proc/explode() //This needs tuning. //Sure did.
if(!src.detonate) return if(!src.detonate) return
var/turf/T = get_turf(src.loc) var/turf/T = get_turf(src.loc)
if (ismob(loc))
var/mob/M = loc
M.show_message("\red Your [src] explodes!", 1)
if(T) if(T)
T.hotspot_expose(700,125) T.hotspot_expose(700,125)
explosion(T, 0, 0, 1, rand(1,2))
explosion(T, -1, -1, 2, 3)
del(src)
return return
/obj/item/device/pda/Del() /obj/item/device/pda/Del()
PDAs -= src PDAs -= src
if (src.id) if (src.id && prob(90)) //IDs are kept in 90% of the cases
src.id.loc = get_turf(src.loc) src.id.loc = get_turf(src.loc)
..() ..()

View File

@@ -151,6 +151,12 @@
for(var/obj/item/weapon/reagent_containers/glass/G in beakers) for(var/obj/item/weapon/reagent_containers/glass/G in beakers)
G.reagents.trans_to(src, G.reagents.total_volume) G.reagents.trans_to(src, G.reagents.total_volume)
if(src.reagents.total_volume) //The possible reactions didnt use up all reagents.
var/datum/effect/effect/system/steam_spread/steam = new /datum/effect/effect/system/steam_spread()
steam.set_up(10, 0, get_turf(src))
steam.attach(src)
steam.start()
for(var/atom/A in view(affected_area, src.loc)) for(var/atom/A in view(affected_area, src.loc))
if( A == src ) continue if( A == src ) continue
src.reagents.reaction(A, 1, 10) src.reagents.reaction(A, 1, 10)

View File

@@ -65,6 +65,7 @@
icon_closed = "blue" icon_closed = "blue"
/obj/structure/closet/lawcloset/New() /obj/structure/closet/lawcloset/New()
..()
new /obj/item/clothing/under/lawyer/female(src) new /obj/item/clothing/under/lawyer/female(src)
new /obj/item/clothing/under/lawyer/black(src) new /obj/item/clothing/under/lawyer/black(src)
new /obj/item/clothing/under/lawyer/red(src) new /obj/item/clothing/under/lawyer/red(src)

View File

@@ -301,6 +301,7 @@
var/id = null var/id = null
New() New()
..()
new /obj/item/clothing/under/color/orange( src ) new /obj/item/clothing/under/color/orange( src )
new /obj/item/clothing/shoes/orange( src ) new /obj/item/clothing/shoes/orange( src )
return return

View File

@@ -56,6 +56,7 @@
del(src)*/ del(src)*/
/obj/structure/closet/emcloset/legacy/New() /obj/structure/closet/emcloset/legacy/New()
..()
new /obj/item/weapon/tank/oxygen(src) new /obj/item/weapon/tank/oxygen(src)
new /obj/item/clothing/mask/gas(src) new /obj/item/clothing/mask/gas(src)
@@ -108,6 +109,7 @@
icon_opened = "toolclosetopen" icon_opened = "toolclosetopen"
/obj/structure/closet/toolcloset/New() /obj/structure/closet/toolcloset/New()
..()
if(prob(40)) if(prob(40))
new /obj/item/clothing/suit/storage/hazardvest(src) new /obj/item/clothing/suit/storage/hazardvest(src)
if(prob(70)) if(prob(70))

View File

@@ -4,22 +4,13 @@
icon_state = "blue" icon_state = "blue"
icon_closed = "blue" icon_closed = "blue"
/obj/structure/closet/wardrobe/New()
new /obj/item/clothing/under/color/blue(src)
new /obj/item/clothing/under/color/blue(src)
new /obj/item/clothing/under/color/blue(src)
new /obj/item/clothing/shoes/brown(src)
new /obj/item/clothing/shoes/brown(src)
new /obj/item/clothing/shoes/brown(src)
return
/obj/structure/closet/wardrobe/red /obj/structure/closet/wardrobe/red
name = "security wardrobe" name = "security wardrobe"
icon_state = "red" icon_state = "red"
icon_closed = "red" icon_closed = "red"
/obj/structure/closet/wardrobe/red/New() /obj/structure/closet/wardrobe/red/New()
..()
new /obj/item/clothing/under/rank/security(src) new /obj/item/clothing/under/rank/security(src)
new /obj/item/clothing/under/rank/security(src) new /obj/item/clothing/under/rank/security(src)
new /obj/item/clothing/under/rank/security(src) new /obj/item/clothing/under/rank/security(src)
@@ -44,6 +35,7 @@
icon_closed = "pink" icon_closed = "pink"
/obj/structure/closet/wardrobe/pink/New() /obj/structure/closet/wardrobe/pink/New()
..()
new /obj/item/clothing/under/color/pink(src) new /obj/item/clothing/under/color/pink(src)
new /obj/item/clothing/under/color/pink(src) new /obj/item/clothing/under/color/pink(src)
new /obj/item/clothing/under/color/pink(src) new /obj/item/clothing/under/color/pink(src)
@@ -58,6 +50,7 @@
icon_closed = "black" icon_closed = "black"
/obj/structure/closet/wardrobe/black/New() /obj/structure/closet/wardrobe/black/New()
..()
new /obj/item/clothing/under/color/black(src) new /obj/item/clothing/under/color/black(src)
new /obj/item/clothing/under/color/black(src) new /obj/item/clothing/under/color/black(src)
new /obj/item/clothing/under/color/black(src) new /obj/item/clothing/under/color/black(src)
@@ -77,6 +70,7 @@
icon_closed = "black" icon_closed = "black"
/obj/structure/closet/wardrobe/chaplain_black/New() /obj/structure/closet/wardrobe/chaplain_black/New()
..()
new /obj/item/clothing/under/rank/chaplain(src) new /obj/item/clothing/under/rank/chaplain(src)
new /obj/item/clothing/shoes/black(src) new /obj/item/clothing/shoes/black(src)
new /obj/item/clothing/suit/nun(src) new /obj/item/clothing/suit/nun(src)
@@ -97,6 +91,7 @@
icon_closed = "green" icon_closed = "green"
/obj/structure/closet/wardrobe/green/New() /obj/structure/closet/wardrobe/green/New()
..()
new /obj/item/clothing/under/color/green(src) new /obj/item/clothing/under/color/green(src)
new /obj/item/clothing/under/color/green(src) new /obj/item/clothing/under/color/green(src)
new /obj/item/clothing/under/color/green(src) new /obj/item/clothing/under/color/green(src)
@@ -111,6 +106,7 @@
icon_closed = "green" icon_closed = "green"
/obj/structure/closet/wardrobe/xenos/New() /obj/structure/closet/wardrobe/xenos/New()
..()
new /obj/item/clothing/suit/unathi/mantle(src) new /obj/item/clothing/suit/unathi/mantle(src)
new /obj/item/clothing/suit/unathi/robe(src) new /obj/item/clothing/suit/unathi/robe(src)
new /obj/item/clothing/shoes/sandal(src) new /obj/item/clothing/shoes/sandal(src)
@@ -126,6 +122,7 @@
icon_closed = "orange" icon_closed = "orange"
/obj/structure/closet/wardrobe/orange/New() /obj/structure/closet/wardrobe/orange/New()
..()
new /obj/item/clothing/under/color/orange(src) new /obj/item/clothing/under/color/orange(src)
new /obj/item/clothing/under/color/orange(src) new /obj/item/clothing/under/color/orange(src)
new /obj/item/clothing/under/color/orange(src) new /obj/item/clothing/under/color/orange(src)
@@ -141,6 +138,7 @@
icon_closed = "wardrobe-y" icon_closed = "wardrobe-y"
/obj/structure/closet/wardrobe/yellow/New() /obj/structure/closet/wardrobe/yellow/New()
..()
new /obj/item/clothing/under/color/yellow(src) new /obj/item/clothing/under/color/yellow(src)
new /obj/item/clothing/under/color/yellow(src) new /obj/item/clothing/under/color/yellow(src)
new /obj/item/clothing/under/color/yellow(src) new /obj/item/clothing/under/color/yellow(src)
@@ -156,6 +154,7 @@
icon_closed = "yellow" icon_closed = "yellow"
/obj/structure/closet/wardrobe/atmospherics_yellow/New() /obj/structure/closet/wardrobe/atmospherics_yellow/New()
..()
new /obj/item/clothing/under/rank/atmospheric_technician(src) new /obj/item/clothing/under/rank/atmospheric_technician(src)
new /obj/item/clothing/under/rank/atmospheric_technician(src) new /obj/item/clothing/under/rank/atmospheric_technician(src)
new /obj/item/clothing/under/rank/atmospheric_technician(src) new /obj/item/clothing/under/rank/atmospheric_technician(src)
@@ -178,6 +177,7 @@
icon_closed = "yellow" icon_closed = "yellow"
/obj/structure/closet/wardrobe/engineering_yellow/New() /obj/structure/closet/wardrobe/engineering_yellow/New()
..()
new /obj/item/clothing/under/rank/engineer(src) new /obj/item/clothing/under/rank/engineer(src)
new /obj/item/clothing/under/rank/engineer(src) new /obj/item/clothing/under/rank/engineer(src)
new /obj/item/clothing/under/rank/engineer(src) new /obj/item/clothing/under/rank/engineer(src)
@@ -199,6 +199,7 @@
icon_closed = "white" icon_closed = "white"
/obj/structure/closet/wardrobe/white/New() /obj/structure/closet/wardrobe/white/New()
..()
new /obj/item/clothing/under/color/white(src) new /obj/item/clothing/under/color/white(src)
new /obj/item/clothing/under/color/white(src) new /obj/item/clothing/under/color/white(src)
new /obj/item/clothing/under/color/white(src) new /obj/item/clothing/under/color/white(src)
@@ -214,6 +215,7 @@
icon_closed = "white" icon_closed = "white"
/obj/structure/closet/wardrobe/pjs/New() /obj/structure/closet/wardrobe/pjs/New()
..()
new /obj/item/clothing/under/pj/red(src) new /obj/item/clothing/under/pj/red(src)
new /obj/item/clothing/under/pj/red(src) new /obj/item/clothing/under/pj/red(src)
new /obj/item/clothing/under/pj/blue(src) new /obj/item/clothing/under/pj/blue(src)
@@ -231,6 +233,7 @@
icon_closed = "white" icon_closed = "white"
/obj/structure/closet/wardrobe/toxins_white/New() /obj/structure/closet/wardrobe/toxins_white/New()
..()
new /obj/item/clothing/under/rank/scientist(src) new /obj/item/clothing/under/rank/scientist(src)
new /obj/item/clothing/under/rank/scientist(src) new /obj/item/clothing/under/rank/scientist(src)
new /obj/item/clothing/under/rank/scientist(src) new /obj/item/clothing/under/rank/scientist(src)
@@ -252,6 +255,7 @@
icon_closed = "black" icon_closed = "black"
/obj/structure/closet/wardrobe/robotics_black/New() /obj/structure/closet/wardrobe/robotics_black/New()
..()
new /obj/item/clothing/under/rank/roboticist(src) new /obj/item/clothing/under/rank/roboticist(src)
new /obj/item/clothing/under/rank/roboticist(src) new /obj/item/clothing/under/rank/roboticist(src)
new /obj/item/clothing/suit/storage/labcoat(src) new /obj/item/clothing/suit/storage/labcoat(src)
@@ -269,6 +273,7 @@
icon_closed = "white" icon_closed = "white"
/obj/structure/closet/wardrobe/chemistry_white/New() /obj/structure/closet/wardrobe/chemistry_white/New()
..()
new /obj/item/clothing/under/rank/chemist(src) new /obj/item/clothing/under/rank/chemist(src)
new /obj/item/clothing/under/rank/chemist(src) new /obj/item/clothing/under/rank/chemist(src)
new /obj/item/clothing/shoes/white(src) new /obj/item/clothing/shoes/white(src)
@@ -284,6 +289,7 @@
icon_closed = "white" icon_closed = "white"
/obj/structure/closet/wardrobe/genetics_white/New() /obj/structure/closet/wardrobe/genetics_white/New()
..()
new /obj/item/clothing/under/rank/geneticist(src) new /obj/item/clothing/under/rank/geneticist(src)
new /obj/item/clothing/under/rank/geneticist(src) new /obj/item/clothing/under/rank/geneticist(src)
new /obj/item/clothing/shoes/white(src) new /obj/item/clothing/shoes/white(src)
@@ -299,6 +305,7 @@
icon_closed = "white" icon_closed = "white"
/obj/structure/closet/wardrobe/virology_white/New() /obj/structure/closet/wardrobe/virology_white/New()
..()
new /obj/item/clothing/under/rank/virologist(src) new /obj/item/clothing/under/rank/virologist(src)
new /obj/item/clothing/under/rank/virologist(src) new /obj/item/clothing/under/rank/virologist(src)
new /obj/item/clothing/shoes/white(src) new /obj/item/clothing/shoes/white(src)
@@ -316,6 +323,7 @@
icon_closed = "white" icon_closed = "white"
/obj/structure/closet/wardrobe/medic_white/New() /obj/structure/closet/wardrobe/medic_white/New()
..()
new /obj/item/clothing/under/rank/medical(src) new /obj/item/clothing/under/rank/medical(src)
new /obj/item/clothing/under/rank/medical(src) new /obj/item/clothing/under/rank/medical(src)
new /obj/item/clothing/under/rank/medical/blue(src) new /obj/item/clothing/under/rank/medical/blue(src)
@@ -336,6 +344,7 @@
icon_closed = "grey" icon_closed = "grey"
/obj/structure/closet/wardrobe/grey/New() /obj/structure/closet/wardrobe/grey/New()
..()
new /obj/item/clothing/under/color/grey(src) new /obj/item/clothing/under/color/grey(src)
new /obj/item/clothing/under/color/grey(src) new /obj/item/clothing/under/color/grey(src)
new /obj/item/clothing/under/color/grey(src) new /obj/item/clothing/under/color/grey(src)
@@ -354,6 +363,7 @@
icon_closed = "mixed" icon_closed = "mixed"
/obj/structure/closet/wardrobe/mixed/New() /obj/structure/closet/wardrobe/mixed/New()
..()
new /obj/item/clothing/under/color/blue(src) new /obj/item/clothing/under/color/blue(src)
new /obj/item/clothing/under/color/yellow(src) new /obj/item/clothing/under/color/yellow(src)
new /obj/item/clothing/under/color/green(src) new /obj/item/clothing/under/color/green(src)
@@ -386,4 +396,4 @@
new /obj/item/weapon/storage/belt/security/tactical(src) new /obj/item/weapon/storage/belt/security/tactical(src)
new /obj/item/clothing/shoes/jackboots(src) new /obj/item/clothing/shoes/jackboots(src)
new /obj/item/clothing/gloves/black(src) new /obj/item/clothing/gloves/black(src)
return return

View File

@@ -158,7 +158,7 @@
proc/update_nearby_tiles(need_rebuild) //Copypasta from airlock code proc/update_nearby_tiles(need_rebuild) //Copypasta from airlock code
if(!air_master) if(!air_master)
return 0 return 0
air_master.AddTurfToUpdate(get_turf(src)) air_master.mark_for_update(get_turf(src))
return 1 return 1
/obj/structure/mineral_door/iron /obj/structure/mineral_door/iron

View File

@@ -297,6 +297,6 @@ obj/structure/windoor_assembly/Del()
if(!air_master) if(!air_master)
return 0 return 0
air_master.AddTurfToUpdate(loc) air_master.mark_for_update(loc)
return 1 return 1

View File

@@ -313,7 +313,7 @@
/obj/structure/window/proc/update_nearby_tiles(need_rebuild) /obj/structure/window/proc/update_nearby_tiles(need_rebuild)
if(!air_master) if(!air_master)
return 0 return 0
air_master.AddTurfToUpdate(get_turf(src)) air_master.mark_for_update(get_turf(src))
return 1 return 1

View File

@@ -206,7 +206,7 @@
for(var/obj/effect/landmark/zcontroller/c in controller) for(var/obj/effect/landmark/zcontroller/c in controller)
if(c.down) if(c.down)
var/turf/below = locate(src.x, src.y, c.down_target) var/turf/below = locate(src.x, src.y, c.down_target)
if((below.zone || zone) && !istype(below, /turf/space)) // dont make open space into space, its pointless and makes people drop out of the station if((air_master.has_valid_zone(below) || air_master.has_valid_zone(src)) && !istype(below, /turf/space)) // dont make open space into space, its pointless and makes people drop out of the station
var/turf/W = src.ChangeTurf(/turf/simulated/floor/open) var/turf/W = src.ChangeTurf(/turf/simulated/floor/open)
var/list/temp = list() var/list/temp = list()
temp += W temp += W
@@ -219,10 +219,10 @@
if(ispath(N, /turf/simulated/floor)) if(ispath(N, /turf/simulated/floor))
//if the old turf had a zone, connect the new turf to it as well - Cael //if the old turf had a zone, connect the new turf to it as well - Cael
//Adjusted by SkyMarshal 5/10/13 - The air master will handle the addition of the new turf. //Adjusted by SkyMarshal 5/10/13 - The air master will handle the addition of the new turf.
if(zone) //if(zone)
zone.RemoveTurf(src) // zone.RemoveTurf(src)
if(!zone.CheckStatus()) // if(!zone.CheckStatus())
zone.SetStatus(ZONE_ACTIVE) // zone.SetStatus(ZONE_ACTIVE)
var/turf/simulated/W = new N( locate(src.x, src.y, src.z) ) var/turf/simulated/W = new N( locate(src.x, src.y, src.z) )
//W.Assimilate_Air() //W.Assimilate_Air()
@@ -236,16 +236,16 @@
W.RemoveLattice() W.RemoveLattice()
if(air_master) if(air_master)
air_master.AddTurfToUpdate(src) air_master.mark_for_update(src)
W.levelupdate() W.levelupdate()
return W return W
else else
if(zone) //if(zone)
zone.RemoveTurf(src) // zone.RemoveTurf(src)
if(!zone.CheckStatus()) // if(!zone.CheckStatus())
zone.SetStatus(ZONE_ACTIVE) // zone.SetStatus(ZONE_ACTIVE)
var/turf/W = new N( locate(src.x, src.y, src.z) ) var/turf/W = new N( locate(src.x, src.y, src.z) )
W.lighting_lumcount += old_lumcount W.lighting_lumcount += old_lumcount
@@ -254,7 +254,7 @@
lighting_controller.changed_turfs += W lighting_controller.changed_turfs += W
if(air_master) if(air_master)
air_master.AddTurfToUpdate(src) air_master.mark_for_update(src)
W.levelupdate() W.levelupdate()
return W return W

View File

@@ -6,7 +6,7 @@
alert(usr,"Master_controller or air_master not found.","Air Report") alert(usr,"Master_controller or air_master not found.","Air Report")
return return
var/active_groups = air_master.active_zones.len var/active_groups = air_master.active_zones
var/inactive_groups = air_master.zones.len - active_groups var/inactive_groups = air_master.zones.len - active_groups
var/hotspots = 0 var/hotspots = 0
@@ -18,7 +18,7 @@
for(var/zone/zone in air_master.zones) for(var/zone/zone in air_master.zones)
var/turf/simulated/turf = locate() in zone.contents var/turf/simulated/turf = locate() in zone.contents
if(turf && turf.z == 1) if(turf && turf.z == 1)
if(zone.status) if(zone.needs_update)
active_on_main_station++ active_on_main_station++
else else
inactive_on_main_station++ inactive_on_main_station++

View File

@@ -124,49 +124,146 @@ var/intercom_range_display_status = 0
del(F) del(F)
feedback_add_details("admin_verb","mIRD") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! feedback_add_details("admin_verb","mIRD") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
var/list/debug_verbs = list (
/client/proc/do_not_use_these
,/client/proc/camera_view
,/client/proc/sec_camera_report
,/client/proc/intercom_view
,/client/proc/air_status
,/client/proc/Cell
,/client/proc/atmosscan
,/client/proc/powerdebug
,/client/proc/count_objects_on_z_level
,/client/proc/count_objects_all
,/client/proc/cmd_assume_direct_control
,/client/proc/jump_to_dead_group
,/client/proc/startSinglo
,/client/proc/ticklag
,/client/proc/cmd_admin_grantfullaccess
,/client/proc/kaboom
,/client/proc/splash
,/client/proc/cmd_admin_areatest
,/client/proc/cmd_admin_rejuvenate
,/datum/admins/proc/show_traitor_panel
,/client/proc/print_jobban_old
,/client/proc/print_jobban_old_filter
,/client/proc/forceEvent
,/client/proc/break_all_air_groups
,/client/proc/regroup_all_air_groups
,/client/proc/kill_pipe_processing
,/client/proc/kill_air_processing
,/client/proc/disable_communication
,/client/proc/disable_movement
,/client/proc/Zone_Info
,/client/proc/Test_ZAS_Connection
,/client/proc/ZoneTick
,/client/proc/hide_debug_verbs
,/client/proc/testZAScolors
,/client/proc/testZAScolors_remove
)
/client/proc/enable_debug_verbs() /client/proc/enable_debug_verbs()
set category = "Debug" set category = "Debug"
set name = "Debug verbs" set name = "Debug verbs"
if(!check_rights(R_DEBUG)) return if(!check_rights(R_DEBUG)) return
src.verbs += /client/proc/do_not_use_these //-errorage verbs += debug_verbs
src.verbs += /client/proc/camera_view //-errorage
src.verbs += /client/proc/sec_camera_report //-errorage
src.verbs += /client/proc/intercom_view //-errorage
src.verbs += /client/proc/air_status //Air things
src.verbs += /client/proc/Cell //More air things
src.verbs += /client/proc/atmosscan //check plumbing
src.verbs += /client/proc/powerdebug //check power
src.verbs += /client/proc/count_objects_on_z_level
src.verbs += /client/proc/count_objects_all
src.verbs += /client/proc/cmd_assume_direct_control //-errorage
src.verbs += /client/proc/jump_to_dead_group
src.verbs += /client/proc/startSinglo
src.verbs += /client/proc/ticklag //allows you to set the ticklag.
src.verbs += /client/proc/cmd_admin_grantfullaccess
src.verbs += /client/proc/kaboom
src.verbs += /client/proc/splash
src.verbs += /client/proc/cmd_admin_areatest
src.verbs += /client/proc/cmd_admin_rejuvenate
src.verbs += /datum/admins/proc/show_traitor_panel
src.verbs += /client/proc/print_jobban_old
src.verbs += /client/proc/print_jobban_old_filter
src.verbs += /client/proc/forceEvent
src.verbs += /client/proc/break_all_air_groups
src.verbs += /client/proc/regroup_all_air_groups
src.verbs += /client/proc/kill_pipe_processing
src.verbs += /client/proc/kill_air_processing
src.verbs += /client/proc/disable_communication
src.verbs += /client/proc/disable_movement
src.verbs += /client/proc/Zone_Info
src.verbs += /client/proc/Test_ZAS_Connection
src.verbs += /client/proc/ZoneTick
src.verbs += /client/proc/TestZASRebuild
//src.verbs += /client/proc/cmd_admin_rejuvenate
feedback_add_details("admin_verb","mDV") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! feedback_add_details("admin_verb","mDV") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/client/proc/hide_debug_verbs()
set category = "Debug"
set name = "Hide Debug verbs"
if(!check_rights(R_DEBUG)) return
verbs -= debug_verbs
feedback_add_details("admin_verb","hDV") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/client/var/list/testZAScolors_turfs = list()
/client/var/list/testZAScolors_zones = list()
/client/var/usedZAScolors = 0
/client/var/list/image/ZAScolors = list()
/client/proc/recurse_zone(var/zone/Z, var/recurse_level =1)
testZAScolors_zones += Z
if(recurse_level > 10)
return
var/icon/yellow = new('icons/misc/debug_group.dmi', "yellow")
for(var/turf/T in Z.contents)
images += image(yellow, T, "zasdebug", TURF_LAYER)
testZAScolors_turfs += T
for(var/zone/connected in Z.connected_zones)
if(connected in testZAScolors_zones)
continue
recurse_zone(connected,recurse_level+1)
/client/proc/testZAScolors()
set category = "ZAS"
set name = "Check ZAS connections"
if(!check_rights(R_DEBUG)) return
testZAScolors_remove()
var/turf/location = get_turf(usr)
if(!istype(location, /turf/simulated)) // We're in space, let's not cause runtimes.
usr << "\red this debug tool cannot be used from space"
return
var/icon/red = new('icons/misc/debug_group.dmi', "red") //created here so we don't have to make thousands of these.
var/icon/green = new('icons/misc/debug_group.dmi', "green")
var/icon/blue = new('icons/misc/debug_group.dmi', "blue")
if(!usedZAScolors)
usr << "ZAS Test Colors"
usr << "Green = Zone you are standing in"
usr << "Blue = Connected zone to the zone you are standing in"
usr << "Yellow = A zone that is connected but not one adjacent to your connected zone"
usr << "Red = Not connected"
usedZAScolors = 1
testZAScolors_zones += location.zone
for(var/turf/T in location.zone.contents)
images += image(green, T,"zasdebug", TURF_LAYER)
testZAScolors_turfs += T
for(var/zone/Z in location.zone.connected_zones)
testZAScolors_zones += Z
for(var/turf/T in Z.contents)
images += image(blue, T,"zasdebug",TURF_LAYER)
testZAScolors_turfs += T
for(var/zone/connected in Z.connected_zones)
if(connected in testZAScolors_zones)
continue
recurse_zone(connected,1)
for(var/turf/T in range(25,location))
if(!istype(T))
continue
if(T in testZAScolors_turfs)
continue
images += image(red, T, "zasdebug", TURF_LAYER)
testZAScolors_turfs += T
/client/proc/testZAScolors_remove()
set category = "ZAS"
set name = "Remove ZAS connection colors"
testZAScolors_turfs.Cut()
testZAScolors_zones.Cut()
if(images.len)
for(var/image/i in images)
if(i.icon_state == "zasdebug")
images.Remove(i)
/client/proc/count_objects_on_z_level() /client/proc/count_objects_on_z_level()
set category = "Mapping" set category = "Mapping"
set name = "Count Objects On Level" set name = "Count Objects On Level"
@@ -181,7 +278,7 @@ var/intercom_range_display_status = 0
var/type_path = text2path(type_text) var/type_path = text2path(type_text)
if(!type_path) return if(!type_path) return
var/count = 0 var/count = 1
var/list/atom/atom_list = list() var/list/atom/atom_list = list()
@@ -312,4 +409,4 @@ var/global/movement_disabled_exception //This is the client that calls the proc,
message_admins("[src.ckey] used 'Disable all movement', killing all movement.") message_admins("[src.ckey] used 'Disable all movement', killing all movement.")
movement_disabled_exception = usr.ckey movement_disabled_exception = usr.ckey
else else
message_admins("[src.ckey] used 'Disable all movement', restoring all movement.")*/ message_admins("[src.ckey] used 'Disable all movement', restoring all movement.")*/

View File

@@ -247,7 +247,7 @@ datum/preferences
dat += "<b>UI Style:</b> <a href='?_src_=prefs;preference=ui'><b>[UI_style]</b></a><br>" dat += "<b>UI Style:</b> <a href='?_src_=prefs;preference=ui'><b>[UI_style]</b></a><br>"
dat += "<b>Custom UI</b>(recommended for White UI):<br>" dat += "<b>Custom UI</b>(recommended for White UI):<br>"
dat += "-Color: <a href='?_src_=prefs;preference=UIcolor'><b>[UI_style_color]</b></a> <table style='display:inline;' bgcolor='[UI_style_color]'><tr><td>__</td></tr></table><br>" dat += "-Color: <a href='?_src_=prefs;preference=UIcolor'><b>[UI_style_color]</b></a> <table style='display:inline;' bgcolor='[UI_style_color]'><tr><td>__</td></tr></table><br>"
dat += "-Alpha(transparence): <a href='?_src_=prefs;preference=UIalpha'><b>[UI_style_alpha]</b></a><br>" dat += "-Alpha(transparency): <a href='?_src_=prefs;preference=UIalpha'><b>[UI_style_alpha]</b></a><br>"
dat += "<b>Play admin midis:</b> <a href='?_src_=prefs;preference=hear_midis'><b>[(toggles & SOUND_MIDI) ? "Yes" : "No"]</b></a><br>" dat += "<b>Play admin midis:</b> <a href='?_src_=prefs;preference=hear_midis'><b>[(toggles & SOUND_MIDI) ? "Yes" : "No"]</b></a><br>"
dat += "<b>Play lobby music:</b> <a href='?_src_=prefs;preference=lobby_music'><b>[(toggles & SOUND_LOBBY) ? "Yes" : "No"]</b></a><br>" dat += "<b>Play lobby music:</b> <a href='?_src_=prefs;preference=lobby_music'><b>[(toggles & SOUND_LOBBY) ? "Yes" : "No"]</b></a><br>"
dat += "<b>Ghost ears:</b> <a href='?_src_=prefs;preference=ghost_ears'><b>[(toggles & CHAT_GHOSTEARS) ? "Nearest Creatures" : "All Speech"]</b></a><br>" dat += "<b>Ghost ears:</b> <a href='?_src_=prefs;preference=ghost_ears'><b>[(toggles & CHAT_GHOSTEARS) ? "Nearest Creatures" : "All Speech"]</b></a><br>"

View File

@@ -38,13 +38,15 @@
// Type 1 (Visual) emotes are sent to anyone in view of the item // Type 1 (Visual) emotes are sent to anyone in view of the item
if (m_type & 1) if (m_type & 1)
for (var/mob/O in viewers(src, null)) var/list/can_see = get_mobs_in_view(1,src) //Allows silicon & mmi mobs carried around to see the emotes of the person carrying them around.
can_see |= viewers(src,null)
for (var/mob/O in can_see)
O.show_message(message, m_type) O.show_message(message, m_type)
// Type 2 (Audible) emotes are sent to anyone in hear range // Type 2 (Audible) emotes are sent to anyone in hear range
// of the *LOCATION* -- this is important for pAIs to be heard // of the *LOCATION* -- this is important for pAIs to be heard
else if (m_type & 2) else if (m_type & 2)
for (var/mob/O in hearers(get_turf(src), null)) for (var/mob/O in get_mobs_in_view(7,src))
O.show_message(message, m_type) O.show_message(message, m_type)
/mob/proc/emote_dead(var/message) /mob/proc/emote_dead(var/message)

View File

@@ -6,6 +6,7 @@
icon_state = "body_m_s" icon_state = "body_m_s"
var/list/hud_list = list() var/list/hud_list = list()
var/datum/species/species //Contains icon generation and language information, set during New(). var/datum/species/species //Contains icon generation and language information, set during New().
var/embedded_flag //To check if we've need to roll for damage on movement while an item is imbedded in us.
/mob/living/carbon/human/dummy /mob/living/carbon/human/dummy
real_name = "Test Dummy" real_name = "Test Dummy"
@@ -46,6 +47,7 @@
if(!dna) if(!dna)
dna = new /datum/dna(null) dna = new /datum/dna(null)
dna.species=species.name
for(var/i=0;i<7;i++) // 2 for medHUDs and 5 for secHUDs for(var/i=0;i<7;i++) // 2 for medHUDs and 5 for secHUDs
hud_list += image('icons/mob/hud.dmi', src, "hudunknown") hud_list += image('icons/mob/hud.dmi', src, "hudunknown")
@@ -1184,8 +1186,14 @@
/mob/living/carbon/human/proc/set_species(var/new_species) /mob/living/carbon/human/proc/set_species(var/new_species)
if(!new_species) if(!dna)
new_species = "Human" if(!new_species)
new_species = "Human"
else
if(!new_species)
new_species = dna.species
else
dna.species = new_species
if(species && (species.name && species.name == new_species)) if(species && (species.name && species.name == new_species))
return return

View File

@@ -251,6 +251,7 @@ This function restores all organs.
if( (damage > (10*W.w_class)) && ( (sharp && !ismob(W.loc)) || prob(damage/W.w_class) ) ) if( (damage > (10*W.w_class)) && ( (sharp && !ismob(W.loc)) || prob(damage/W.w_class) ) )
organ.implants += W organ.implants += W
visible_message("<span class='danger'>\The [W] sticks in the wound!</span>") visible_message("<span class='danger'>\The [W] sticks in the wound!</span>")
embedded_flag = 1
src.verbs += /mob/proc/yank_out_object src.verbs += /mob/proc/yank_out_object
W.add_blood(src) W.add_blood(src)
if(ismob(W.loc)) if(ismob(W.loc))
@@ -258,4 +259,4 @@ This function restores all organs.
H.drop_item() H.drop_item()
W.loc = src W.loc = src
return 1 return 1

View File

@@ -99,6 +99,7 @@ emp_act
(SP.loc) = organ (SP.loc) = organ
organ.implants += SP organ.implants += SP
visible_message("<span class='danger'>The projectile sticks in the wound!</span>") visible_message("<span class='danger'>The projectile sticks in the wound!</span>")
embedded_flag = 1
src.verbs += /mob/proc/yank_out_object src.verbs += /mob/proc/yank_out_object
SP.add_blood(src) SP.add_blood(src)

View File

@@ -6,7 +6,8 @@
if (istype(loc, /turf/space)) return -1 // It's hard to be slowed down in space by... anything if (istype(loc, /turf/space)) return -1 // It's hard to be slowed down in space by... anything
handle_embedded_objects() //Moving with objects stuck in you can cause bad times. if(embedded_flag)
handle_embedded_objects() //Moving with objects stuck in you can cause bad times.
if(reagents.has_reagent("hyperzine")) return -1 if(reagents.has_reagent("hyperzine")) return -1

View File

@@ -105,7 +105,7 @@
handle_environment(environment) handle_environment(environment)
//Status updates, death etc. //Status updates, death etc.
handle_regular_status_updates() //TODO: optimise ~Carn handle_regular_status_updates() //TODO: optimise ~Carn NO SHIT ~Ccomp
update_canmove() update_canmove()
//Update our name based on whether our face is obscured/disfigured //Update our name based on whether our face is obscured/disfigured
@@ -1067,6 +1067,13 @@
if(halloss > 0) if(halloss > 0)
adjustHalLoss(-1) adjustHalLoss(-1)
if(embedded_flag && !(life_tick % 10))
var/list/E
E = get_visible_implants(0)
if(!E.len)
embedded_flag = 0
//Eyes //Eyes
if(sdisabilities & BLIND) //disabled-blind, doesn't get better on its own if(sdisabilities & BLIND) //disabled-blind, doesn't get better on its own
blinded = 1 blinded = 1

View File

@@ -14,7 +14,7 @@
//var/uni_append = "12C4E2" // Small appearance modifier for different species. //var/uni_append = "12C4E2" // Small appearance modifier for different species.
var/list/uni_append = list(0x12C,0x4E2) // Same as above for DNA2. var/list/uni_append = list(0x12C,0x4E2) // Same as above for DNA2.
var/update_muts = 1 // Monkey gene must be set at start. var/update_muts = 1 // Monkey gene must be set at start.
var/alien = 0 //Used for reagent metabolism. var/alien = 0 //Used for reagent metabolism.
/mob/living/carbon/monkey/tajara /mob/living/carbon/monkey/tajara
name = "farwa" name = "farwa"

View File

@@ -302,7 +302,7 @@ var/list/department_radio_keys = list(
var/list/listening var/list/listening
listening = get_mobs_in_view(message_range, src) listening = get_mobs_in_view(message_range, src)
var/list/onscreen = get_mobs_in_view(7, src) var/list/onscreen = viewers()
for(var/mob/M in player_list) for(var/mob/M in player_list)
if (!M.client) if (!M.client)
continue //skip monkeys and leavers continue //skip monkeys and leavers

View File

@@ -643,5 +643,12 @@
dat += "</li>" dat += "</li>"
dat += "</ul>" dat += "</ul>"
dat += "<br><br>" dat += "<br><br>"
dat += "Messages: <hr> [pda.tnote]" for(var/index in pda.tnote)
return dat if(index["sent"])
dat += addtext("<i><b>&rarr; To <a href='byond://?src=\ref[src];software=pdamessage;target=",index["src"],"'>", index["owner"],"</a>:</b></i><br>", index["message"], "<br>")
else
dat += addtext("<i><b>&larr; From <a href='byond://?src=\ref[src];software=pdamessage;target=",index["target"],"'>", index["owner"],"</a>:</b></i><br>", index["message"], "<br>")
return dat

View File

@@ -3,7 +3,33 @@
#define APC_WIRE_MAIN_POWER2 3 #define APC_WIRE_MAIN_POWER2 3
#define APC_WIRE_AI_CONTROL 4 #define APC_WIRE_AI_CONTROL 4
#define APC_UPDATE_ICON_COOLDOWN 200 // 20 seconds //update_state
#define UPSTATE_CELL_IN 1
#define UPSTATE_OPENED1 2
#define UPSTATE_OPENED2 4
#define UPSTATE_MAINT 8
#define UPSTATE_BROKE 16
#define UPSTATE_BLUESCREEN 32
#define UPSTATE_WIREEXP 64
#define UPSTATE_ALLGOOD 128
//update_overlay
#define APC_UPOVERLAY_CHARGEING0 1
#define APC_UPOVERLAY_CHARGEING1 2
#define APC_UPOVERLAY_CHARGEING2 4
#define APC_UPOVERLAY_EQUIPMENT0 8
#define APC_UPOVERLAY_EQUIPMENT1 16
#define APC_UPOVERLAY_EQUIPMENT2 32
#define APC_UPOVERLAY_LIGHTING0 64
#define APC_UPOVERLAY_LIGHTING1 128
#define APC_UPOVERLAY_LIGHTING2 256
#define APC_UPOVERLAY_ENVIRON0 512
#define APC_UPOVERLAY_ENVIRON1 1024
#define APC_UPOVERLAY_ENVIRON2 2048
#define APC_UPOVERLAY_LOCKED 4096
#define APC_UPDATE_ICON_COOLDOWN 100 // 10 seconds
// the Area Power Controller (APC), formerly Power Distribution Unit (PDU) // the Area Power Controller (APC), formerly Power Distribution Unit (PDU)
// one per area, needs wire conection to power network // one per area, needs wire conection to power network
@@ -18,7 +44,6 @@
/obj/machinery/power/apc /obj/machinery/power/apc
name = "area power controller" name = "area power controller"
icon_state = "apc0" icon_state = "apc0"
anchored = 1 anchored = 1
use_power = 0 use_power = 0
@@ -64,8 +89,16 @@
"Yellow" = 4, "Yellow" = 4,
) )
var/longtermpower = 10 var/longtermpower = 10
var/update_state = -1
var/update_overlay = -1
var/global/status_overlays = 0
var/updating_icon = 0 var/updating_icon = 0
//var/debug = 0 var/global/list/status_overlays_lock
var/global/list/status_overlays_charging
var/global/list/status_overlays_equipment
var/global/list/status_overlays_lighting
var/global/list/status_overlays_environ
/proc/RandomAPCWires() /proc/RandomAPCWires()
//to make this not randomize the wires, just set index to 1 and increment it in the flag for loop (after doing everything else). //to make this not randomize the wires, just set index to 1 and increment it in the flag for loop (after doing everything else).
@@ -175,36 +208,162 @@
else else
usr << "The cover is closed." usr << "The cover is closed."
// update the APC icon to show the three base states // update the APC icon to show the three base states
// also add overlays for indicator lights // also add overlays for indicator lights
/obj/machinery/power/apc/update_icon() /obj/machinery/power/apc/update_icon()
if (!status_overlays)
status_overlays = 1
status_overlays_lock = new
status_overlays_charging = new
status_overlays_equipment = new
status_overlays_lighting = new
status_overlays_environ = new
overlays.Cut() status_overlays_lock.len = 2
if(opened) status_overlays_charging.len = 3
var/basestate = "apc[ cell ? "2" : "1" ]" // if opened, show cell if it's inserted status_overlays_equipment.len = 4
if (opened==1) status_overlays_lighting.len = 4
if (stat & (MAINT|BROKEN)) status_overlays_environ.len = 4
icon_state = "apcmaint" //disassembled APC cannot hold cell
else status_overlays_lock[1] = image(icon, "apcox-0") // 0=blue 1=red
icon_state = basestate status_overlays_lock[2] = image(icon, "apcox-1")
else if (opened == 2)
icon_state = "[basestate]-nocover" status_overlays_charging[1] = image(icon, "apco3-0")
else if (stat & BROKEN) status_overlays_charging[2] = image(icon, "apco3-1")
icon_state = "apc-b" status_overlays_charging[3] = image(icon, "apco3-2")
else if(emagged || malfai)
icon_state = "apcemag" status_overlays_equipment[1] = image(icon, "apco0-0") // 0=red, 1=green, 2=blue
else if(wiresexposed) status_overlays_equipment[2] = image(icon, "apco0-1")
icon_state = "apcewires" status_overlays_equipment[3] = image(icon, "apco0-2")
else status_overlays_equipment[4] = image(icon, "apco0-3")
icon_state = "apc0"
// if closed, update overlays for channel status status_overlays_lighting[1] = image(icon, "apco1-0")
if(!(stat & (BROKEN|MAINT))) status_overlays_lighting[2] = image(icon, "apco1-1")
overlays.Add("apcox-[locked]","apco3-[charging]") // 0=blue 1=red // 0=red, 1=yellow/black 2=green status_overlays_lighting[3] = image(icon, "apco1-2")
status_overlays_lighting[4] = image(icon, "apco1-3")
status_overlays_environ[1] = image(icon, "apco2-0")
status_overlays_environ[2] = image(icon, "apco2-1")
status_overlays_environ[3] = image(icon, "apco2-2")
status_overlays_environ[4] = image(icon, "apco2-3")
var/update = check_updates() //returns 0 if no need to update icons.
// 1 if we need to update the icon_state
// 2 if we need to update the overlays
if(!update)
return
if(update & 1) // Updating the icon state
if(update_state & UPSTATE_ALLGOOD)
icon_state = "apc0"
else if(update_state & (UPSTATE_OPENED1|UPSTATE_OPENED2))
var/basestate = "apc[ cell ? "2" : "1" ]"
if(update_state & UPSTATE_OPENED1)
if(update_state & (UPSTATE_MAINT|UPSTATE_BROKE))
icon_state = "apcmaint" //disabled APC cannot hold cell
else
icon_state = basestate
else if(update_state & UPSTATE_OPENED2)
icon_state = "[basestate]-nocover"
else if(update_state & UPSTATE_BROKE)
icon_state = "apc-b"
else if(update_state & UPSTATE_BLUESCREEN)
icon_state = "apcemag"
else if(update_state & UPSTATE_WIREEXP)
icon_state = "apcewires"
if(!(update_state & UPSTATE_ALLGOOD))
if(overlays.len)
overlays = 0
return
if(update & 2)
if(overlays.len)
overlays = 0
if(!(stat & (BROKEN|MAINT)) && update_state & UPSTATE_ALLGOOD)
overlays += status_overlays_lock[locked+1]
overlays += status_overlays_charging[charging+1]
if(operating) if(operating)
overlays.Add("apco0-[equipment]","apco1-[lighting]","apco2-[environ]") // 0=red, 1=green, 2=blue overlays += status_overlays_equipment[equipment+1]
overlays += status_overlays_lighting[lighting+1]
overlays += status_overlays_environ[environ+1]
/obj/machinery/power/apc/proc/check_updates()
var/last_update_state = update_state
var/last_update_overlay = update_overlay
update_state = 0
update_overlay = 0
if(cell)
update_state |= UPSTATE_CELL_IN
if(stat & BROKEN)
update_state |= UPSTATE_BROKE
if(stat & MAINT)
update_state |= UPSTATE_MAINT
if(opened)
if(opened==1)
update_state |= UPSTATE_OPENED1
if(opened==2)
update_state |= UPSTATE_OPENED2
else if(emagged || malfai)
update_state |= UPSTATE_BLUESCREEN
else if(wiresexposed)
update_state |= UPSTATE_WIREEXP
if(update_state <= 1)
update_state |= UPSTATE_ALLGOOD
if(update_state & UPSTATE_ALLGOOD)
if(locked)
update_overlay |= APC_UPOVERLAY_LOCKED
if(!charging)
update_overlay |= APC_UPOVERLAY_CHARGEING0
else if(charging == 1)
update_overlay |= APC_UPOVERLAY_CHARGEING1
else if(charging == 2)
update_overlay |= APC_UPOVERLAY_CHARGEING2
if (!equipment)
update_overlay |= APC_UPOVERLAY_EQUIPMENT0
else if(equipment == 1)
update_overlay |= APC_UPOVERLAY_EQUIPMENT1
else if(equipment == 2)
update_overlay |= APC_UPOVERLAY_EQUIPMENT2
if(!lighting)
update_overlay |= APC_UPOVERLAY_LIGHTING0
else if(lighting == 1)
update_overlay |= APC_UPOVERLAY_LIGHTING1
else if(lighting == 2)
update_overlay |= APC_UPOVERLAY_LIGHTING2
if(!environ)
update_overlay |= APC_UPOVERLAY_ENVIRON0
else if(environ==1)
update_overlay |= APC_UPOVERLAY_ENVIRON1
else if(environ==2)
update_overlay |= APC_UPOVERLAY_ENVIRON2
var/results = 0
if(last_update_state == update_state && last_update_overlay == update_overlay)
return 0
if(last_update_state != update_state)
results += 1
if(last_update_overlay != update_overlay && update_overlay != 0)
results += 2
return results
// Used in process so it doesn't update the icon too much
/obj/machinery/power/apc/proc/queue_icon_update() /obj/machinery/power/apc/proc/queue_icon_update()
if(!updating_icon) if(!updating_icon)
@@ -214,6 +373,7 @@
update_icon() update_icon()
updating_icon = 0 updating_icon = 0
//attack with an item - open/close cover, insert cell, or (un)lock interface //attack with an item - open/close cover, insert cell, or (un)lock interface
/obj/machinery/power/apc/attackby(obj/item/W, mob/user) /obj/machinery/power/apc/attackby(obj/item/W, mob/user)
@@ -1143,8 +1303,6 @@
update() update()
else if (last_ch != charging) else if (last_ch != charging)
queue_icon_update() queue_icon_update()
//src.updateDialog()
src.updateDialog() src.updateDialog()
// val 0=off, 1=off(auto) 2=on 3=on(auto) // val 0=off, 1=off(auto) 2=on 3=on(auto)
@@ -1267,4 +1425,4 @@
else else
return 0 return 0
#undef APC_UPDATE_ICON_COOLDOWN #undef APC_UPDATE_ICON_COOLDOWN

View File

@@ -452,13 +452,13 @@ datum
id = "chemsmoke" id = "chemsmoke"
result = null result = null
required_reagents = list("potassium" = 1, "sugar" = 1, "phosphorus" = 1) required_reagents = list("potassium" = 1, "sugar" = 1, "phosphorus" = 1)
result_amount = null result_amount = 0.4
secondary = 1 secondary = 1
on_reaction(var/datum/reagents/holder, var/created_volume) on_reaction(var/datum/reagents/holder, var/created_volume)
var/location = get_turf(holder.my_atom) var/location = get_turf(holder.my_atom)
var/datum/effect/effect/system/smoke_spread/chem/S = new /datum/effect/effect/system/smoke_spread/chem var/datum/effect/effect/system/smoke_spread/chem/S = new /datum/effect/effect/system/smoke_spread/chem
S.attach(location) S.attach(location)
S.set_up(holder, created_volume/7.5, 0, location) S.set_up(holder, created_volume, 0, location)
playsound(location, 'sound/effects/smoke.ogg', 50, 1, -3) playsound(location, 'sound/effects/smoke.ogg', 50, 1, -3)
spawn(0) spawn(0)
S.start() S.start()

View File

@@ -93,6 +93,9 @@
if(!istype(L)) //We are in a crate or somewhere that isn't turf, if we return to turf resume processing but for now. if(!istype(L)) //We are in a crate or somewhere that isn't turf, if we return to turf resume processing but for now.
return //Yeah just stop. return //Yeah just stop.
if(istype(L, /turf/space)) // Stop processing this stuff if we've been ejected.
return
if(damage > warning_point) // while the core is still damaged and it's still worth noting its status if(damage > warning_point) // while the core is still damaged and it's still worth noting its status
if((world.timeofday - lastwarning) / 10 >= WARNING_DELAY) if((world.timeofday - lastwarning) / 10 >= WARNING_DELAY)
var/stability = num2text(round((damage / explosion_point) * 100)) var/stability = num2text(round((damage / explosion_point) * 100))

View File

@@ -35,8 +35,8 @@ Header Section
<table align='center' class="top"> <table align='center' class="top">
<tr> <tr>
<td valign='top'> <td valign='top'>
<font size='2'><b>Code:</b> Abi79, Aryn, Cael_Aislinn,Ccomp5950 ,Chinsky, cib, CompactNinja, DopeGhoti, Erthilo, Hawk_v3, Head, Ispil, Lexusjjss, Melonstorm, Miniature, Mloc, NerdyBoy1104, SkyMarshal, Snapshot, Spectre, Strumpetplaya, Sunfall, Tastyfish, Uristqwerty<br></font> <font size='2'><b>Code:</b> Abi79, Aryn, Cael_Aislinn, Ccomp5950 ,Chinsky, cib, CompactNinja, DopeGhoti, Erthilo, Hawk_v3, Head, Ispil, JoeyJo0, Lexusjjss, Melonstorm, Miniature, Mloc, NerdyBoy1104, SkyMarshal, Snapshot, Spectre, Strumpetplaya, Sunfall, Tastyfish, Uristqwerty<br></font>
<font size='2'><b>Sprites:</b> Apple_Master, Arcalane, Chinsky, CompactNinja, Deus Dactyl, Erthilo, Flashkirby, Miniature, Searif, Xenone, faux<br></font> <font size='2'><b>Sprites:</b> Apple_Master, Arcalane, Chinsky, CompactNinja, Deus Dactyl, Erthilo, Flashkirby, JoeyJo0, Miniature, Searif, Xenone, faux<br></font>
<font size='2'><b>Sounds:</b> Aryn<br></font> <font size='2'><b>Sounds:</b> Aryn<br></font>
<font size='2'><b>Thanks To:</b> /tg/ station, Goonstation, Animus Station, Daedalus, and original Spacestation 13 devs. Skibiliano for the IRC bot.</font> <font size='2'><b>Thanks To:</b> /tg/ station, Goonstation, Animus Station, Daedalus, and original Spacestation 13 devs. Skibiliano for the IRC bot.</font>
</td> </td>
@@ -56,6 +56,13 @@ should be listed in the changelog upon commit though. Thanks. -->
<!-- DO NOT REMOVE, MOVE, OR COPY THIS COMMENT! THIS MUST BE THE LAST NON-EMPTY LINE BEFORE THE LOGS #ADDTOCHANGELOGMARKER# --> <!-- DO NOT REMOVE, MOVE, OR COPY THIS COMMENT! THIS MUST BE THE LAST NON-EMPTY LINE BEFORE THE LOGS #ADDTOCHANGELOGMARKER# -->
<div class='commit sansserif'>
<h2 class='date'>19 February 2014</h2>
<h3 class='author'>Aryn updated:</h3>
<ul class='changes bgimages16'>
<li class='experiment'>New air model. Nothing should change to a great degree, but temperature flow might be affected due to closed connections not sticking around.</li>
</ul>
</div>
<div class='commit sansserif'> <div class='commit sansserif'>
<h2 class='date'>18 December 2013</h2> <h2 class='date'>18 December 2013</h2>

BIN
icons/Testing/Zone.dmi Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 955 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 225 B

After

Width:  |  Height:  |  Size: 400 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 120 KiB

After

Width:  |  Height:  |  Size: 118 KiB

View File

@@ -1,35 +1,42 @@
<div class="item" style="padding-top: 10px"> <div class="item" style="padding-top: 10px">
<div class="item"> <div class="item">
<div class="itemLabel"> <div class="itemLabel" style="width: 150px">
Exterior Door Status: Exterior Door Status:
</div> </div>
<div class="statusValue"> <div class="statusValue">
{{:exterior_status.state}} - {{:exterior_status.lock}} {{if exterior_status.state == "closed"}}
Locked
{{else}}
Open
{{/if}}
</div> </div>
</div> </div>
<div class="item"> <div class="item">
<div class="itemLabel"> <div class="itemLabel" style="width: 150px">
Interior Door Status: Interior Door Status:
</div> </div>
<div class="statusValue"> <div class="statusValue">
{{:interior_status.state}} - {{:interior_status.lock}} {{if interior_status.state == "closed"}}
Locked
{{else}}
Open
{{/if}}
</div> </div>
</div> </div>
</div> </div>
<div class="item" style="padding-top: 10px"> <div class="item" style="padding-top: 10px">
<div class="item"> <div class="item">
<div class="itemContent"> <div class="itemContent" style="width: 100%">
{{if exterior_status.state == "open"}}
{{:~link('Lock Exterior Door', 'alert', {'command' : 'force_ext'}, processing ? 'disabled' : null)}}
{{else}}
{{:~link('Cycle to Exterior', 'arrowthickstop-1-w', {'command' : 'cycle_ext_door'}, processing ? 'disabled' : null)}} {{:~link('Cycle to Exterior', 'arrowthickstop-1-w', {'command' : 'cycle_ext_door'}, processing ? 'disabled' : null)}}
{{/if}}
{{if interior_status.state == "open"}}
{{:~link('Lock Interior Door', 'alert', {'command' : 'force_int'}, processing ? 'disabled' : null)}}
{{else}}
{{:~link('Cycle to Interior', 'arrowthickstop-1-e', {'command' : 'cycle_int_door'}, processing ? 'disabled' : null)}} {{:~link('Cycle to Interior', 'arrowthickstop-1-e', {'command' : 'cycle_int_door'}, processing ? 'disabled' : null)}}
{{/if}}
</div> </div>
<div class="itemContent" style="padding-top: 2px">
{{:~link('Force exterior door', 'alert', {'command' : 'force_ext'}, null, processing ? 'yellowBackground' : null)}}
{{:~link('Force interior door', 'alert', {'command' : 'force_int'}, null, processing ? 'yellowBackground' : null)}}
</div>
</div>
<div class="item">
<div class="itemContent" style="width: auto">
{{:~link('Secure', secure ? 'locked' : 'unlocked', {'command' : 'secure'}, processing ? 'disabled' : null, secure ? 'linkOn' : null)}}
</div>
</div> </div>
</div> </div>

View File

@@ -13,16 +13,24 @@
</div> </div>
<div class="item" style="padding-top: 10px"> <div class="item" style="padding-top: 10px">
<div class="item"> <div class="item">
<div class="itemContent"> <div class="itemContent" style="width: 100%">
{{:~link('Cycle to Exterior', 'arrowthickstop-1-w', {'command' : 'cycle_ext'}, processing ? 'disabled' : null)}} {{:~link('Cycle to Exterior', 'arrowthickstop-1-w', {'command' : 'cycle_ext'}, processing ? 'disabled' : null)}}
{{:~link('Cycle to Interior', 'arrowthickstop-1-e', {'command' : 'cycle_int'}, processing ? 'disabled' : null)}} {{:~link('Cycle to Interior', 'arrowthickstop-1-e', {'command' : 'cycle_int'}, processing ? 'disabled' : null)}}
</div> </div>
<div class="itemContent" style="padding-top: 2px"> <div class="itemContent" style="padding-top: 2px; width: 100%">
{{if interior_status.state == "open"}}
{{:~link('Force exterior door', 'alert', {'command' : 'force_ext'}, null, 'redBackground')}}
{{else}}
{{:~link('Force exterior door', 'alert', {'command' : 'force_ext'}, null, processing ? 'yellowBackground' : null)}} {{:~link('Force exterior door', 'alert', {'command' : 'force_ext'}, null, processing ? 'yellowBackground' : null)}}
{{/if}}
{{if exterior_status.state == "open"}}
{{:~link('Force interior door', 'alert', {'command' : 'force_int'}, null, 'redBackground')}}
{{else}}
{{:~link('Force interior door', 'alert', {'command' : 'force_int'}, null, processing ? 'yellowBackground' : null)}} {{:~link('Force interior door', 'alert', {'command' : 'force_int'}, null, processing ? 'yellowBackground' : null)}}
{{/if}}
</div> </div>
</div> </div>
<div class="item" style="padding-top: 10px"> <div class="item" style="padding-top: 10px; width: 100%">
{{:~link('Abort', 'cancel', {'command' : 'abort'}, processing ? null : 'disabled', processing ? 'redBackground' : null)}} {{:~link('Abort', 'cancel', {'command' : 'abort'}, processing ? null : 'disabled', processing ? 'redBackground' : null)}}
</div> </div>
</div> </div>