diff --git a/baystation12.dme b/baystation12.dme index 83a200fc15..430d3b1377 100644 --- a/baystation12.dme +++ b/baystation12.dme @@ -1325,17 +1325,20 @@ #include "code\WorkInProgress\Ported\policetape.dm" #include "code\WorkInProgress\SkyMarshal\officer_stuff.dm" #include "code\WorkInProgress\SkyMarshal\Ultralight_procs.dm" +#include "code\ZAS\_docs.dm" +#include "code\ZAS\_gas_mixture.dm" #include "code\ZAS\Airflow.dm" +#include "code\ZAS\Atom.dm" #include "code\ZAS\Connection.dm" +#include "code\ZAS\ConnectionGroup.dm" +#include "code\ZAS\Controller.dm" #include "code\ZAS\Debug.dm" -#include "code\ZAS\FEA_gas_mixture.dm" -#include "code\ZAS\FEA_system.dm" +#include "code\ZAS\Diagnostic.dm" #include "code\ZAS\Fire.dm" -#include "code\ZAS\Functions.dm" #include "code\ZAS\Plasma.dm" +#include "code\ZAS\Turf.dm" #include "code\ZAS\Variable Settings.dm" -#include "code\ZAS\ZAS_Turfs.dm" -#include "code\ZAS\ZAS_Zones.dm" +#include "code\ZAS\Zone.dm" #include "interface\interface.dm" #include "interface\skin.dmf" #include "maps\tgstation2.dmm" diff --git a/code/ATMOSPHERICS/components/unary/vent_pump.dm b/code/ATMOSPHERICS/components/unary/vent_pump.dm index 83f8eb7fcf..1550eb7ae9 100644 --- a/code/ATMOSPHERICS/components/unary/vent_pump.dm +++ b/code/ATMOSPHERICS/components/unary/vent_pump.dm @@ -100,7 +100,7 @@ if(pressure_checks&2) 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) var/transfer_moles = pressure_delta*environment.volume/(air_contents.temperature * R_IDEAL_GAS_EQUATION) @@ -118,7 +118,7 @@ if(pressure_checks&2) 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) var/transfer_moles = pressure_delta*air_contents.volume/(environment.temperature * R_IDEAL_GAS_EQUATION) diff --git a/code/ATMOSPHERICS/pipes.dm b/code/ATMOSPHERICS/pipes.dm index a2980482f5..f872a8d0fb 100644 --- a/code/ATMOSPHERICS/pipes.dm +++ b/code/ATMOSPHERICS/pipes.dm @@ -63,7 +63,7 @@ obj/machinery/atmospherics/pipe/attackby(var/obj/item/weapon/W as obj, var/mob/u return ..() if(istype(W,/obj/item/device/pipe_painter)) - return 1 + return 0 if (!istype(W, /obj/item/weapon/wrench)) return ..() @@ -118,6 +118,7 @@ obj/machinery/atmospherics/pipe/simple obj/machinery/atmospherics/pipe/simple/New() ..() + alpha = 255 switch(dir) if(SOUTH || NORTH) initialize_directions = SOUTH|NORTH @@ -220,15 +221,15 @@ obj/machinery/atmospherics/pipe/simple/pipeline_expansion() obj/machinery/atmospherics/pipe/simple/update_icon() if(node1&&node2) - var/C = "" switch(pipe_color) - if ("red") C = "-r" - if ("blue") C = "-b" - if ("cyan") C = "-c" - if ("green") C = "-g" - if ("yellow") C = "-y" - if ("purple") C = "-p" - icon_state = "intact[C][invisibility ? "-f" : "" ]" + if ("red") color = COLOR_RED + if ("blue") color = COLOR_BLUE + if ("cyan") color = COLOR_CYAN + if ("green") color = COLOR_GREEN + if ("yellow") color = "#FFCC00" + if ("purple") color = "#5C1EC0" + if ("grey") color = null + icon_state = "intact[invisibility ? "-f" : "" ]" //var/node1_direction = get_dir(src, node1) //var/node2_direction = get_dir(src, node2) @@ -294,42 +295,43 @@ obj/machinery/atmospherics/pipe/simple/visible obj/machinery/atmospherics/pipe/simple/visible/scrubbers name="Scrubbers pipe" - color="#FF0000" + color=COLOR_RED obj/machinery/atmospherics/pipe/simple/visible/supply name="Air supply pipe" - color="#0000FF" + color=COLOR_BLUE obj/machinery/atmospherics/pipe/simple/visible/yellow color="#FFCC00" obj/machinery/atmospherics/pipe/simple/visible/cyan - color="#00FFFF" + color=COLOR_CYAN obj/machinery/atmospherics/pipe/simple/visible/green - color="#00FF00" + color=COLOR_GREEN obj/machinery/atmospherics/pipe/simple/hidden level = 1 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 name="Scrubbers pipe" - color="#FF0000" + color=COLOR_RED obj/machinery/atmospherics/pipe/simple/hidden/supply name="Air supply pipe" - color="#0000FF" + color=COLOR_BLUE obj/machinery/atmospherics/pipe/simple/hidden/yellow color="#FFCC00" obj/machinery/atmospherics/pipe/simple/hidden/cyan - color="#00FFFF" + color=COLOR_CYAN obj/machinery/atmospherics/pipe/simple/hidden/green - color="#00FF00" + color=COLOR_GREEN obj/machinery/atmospherics/pipe/simple/insulated @@ -364,6 +366,7 @@ obj/machinery/atmospherics/pipe/manifold layer = 2.4 //under wires with their 2.44 obj/machinery/atmospherics/pipe/manifold/New() + alpha = 255 switch(dir) if(NORTH) 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() if(node1&&node2&&node3) - var/C = "" switch(pipe_color) - if ("red") C = "-r" - if ("blue") C = "-b" - if ("cyan") C = "-c" - if ("green") C = "-g" - if ("yellow") C = "-y" - if ("purple") C = "-p" - icon_state = "manifold[C][invisibility ? "-f" : ""]" + if ("red") color = COLOR_RED + if ("blue") color = COLOR_BLUE + if ("cyan") color = COLOR_CYAN + if ("green") color = COLOR_GREEN + if ("yellow") color = "#FFCC00" + if ("purple") color = "#5C1EC0" + if ("grey") color = null + icon_state = "manifold[invisibility ? "-f" : "" ]" else var/connected = 0 @@ -520,48 +523,48 @@ obj/machinery/atmospherics/pipe/manifold/visible obj/machinery/atmospherics/pipe/manifold/visible/supply name="Air supply pipe" - color="#0000FF" + color=COLOR_BLUE obj/machinery/atmospherics/pipe/manifold/visible/scrubbers name="Scrubbers pipe" - color="#FF0000" + color=COLOR_RED obj/machinery/atmospherics/pipe/manifold/visible/yellow color="#FFCC00" obj/machinery/atmospherics/pipe/manifold/visible/cyan - color="#00FFFF" + color=COLOR_CYAN obj/machinery/atmospherics/pipe/manifold/visible/green - color="#00FF00" + color=COLOR_GREEN obj/machinery/atmospherics/pipe/manifold/hidden level = 1 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 name="Air supply pipe" - color="#0000FF" + color=COLOR_BLUE obj/machinery/atmospherics/pipe/manifold/hidden/scrubbers name="Scrubbers pipe" - color="#FF0000" + color = COLOR_RED obj/machinery/atmospherics/pipe/manifold/hidden/yellow color="#FFCC00" obj/machinery/atmospherics/pipe/manifold/hidden/cyan - color="#00FFFF" + color=COLOR_CYAN obj/machinery/atmospherics/pipe/manifold/hidden/green - color="#00FF00" + color=COLOR_GREEN obj/machinery/atmospherics/pipe/manifold4w icon = 'icons/obj/atmospherics/pipe_manifold.dmi' - icon_state = "manifold4w-f" name = "4-way pipe manifold" desc = "A manifold composed of regular pipes" @@ -579,6 +582,10 @@ obj/machinery/atmospherics/pipe/manifold4w level = 1 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) if(level == 1 && istype(loc, /turf/simulated)) invisibility = i ? 101 : 0 @@ -651,15 +658,15 @@ obj/machinery/atmospherics/pipe/manifold4w/disconnect(obj/machinery/atmospherics obj/machinery/atmospherics/pipe/manifold4w/update_icon() overlays.Cut() if(node1&&node2&&node3&&node4) - var/C = "" switch(pipe_color) - if ("red") C = "-r" - if ("blue") C = "-b" - if ("cyan") C = "-c" - if ("green") C = "-g" - if ("yellow") C = "-y" - if ("purple") C = "-p" - icon_state = "manifold4w[C][invisibility ? "-f" : ""]" + if ("red") color = COLOR_RED + if ("blue") color = COLOR_BLUE + if ("cyan") color = COLOR_CYAN + if ("green") color = COLOR_GREEN + if ("yellow") color = "#FFCC00" + if ("purple") color = "#5C1EC0" + if ("grey") color = null + icon_state = "manifold4w[invisibility ? "-f" : "" ]" else icon_state = "manifold4w_ex" @@ -712,42 +719,43 @@ obj/machinery/atmospherics/pipe/manifold4w/visible obj/machinery/atmospherics/pipe/manifold4w/visible/supply name="Air supply pipe" - color="#0000FF" + color=COLOR_BLUE obj/machinery/atmospherics/pipe/manifold4w/visible/scrubbers name="Scrubbers pipe" - color="#FF0000" + color=COLOR_RED obj/machinery/atmospherics/pipe/manifold4w/visible/yellow color="#FFCC00" obj/machinery/atmospherics/pipe/manifold4w/visible/cyan - color="#00FFFF" + color=COLOR_CYAN obj/machinery/atmospherics/pipe/manifold4w/visible/green - color="#00FF00" + color=COLOR_GREEN obj/machinery/atmospherics/pipe/manifold4w/hidden level = 1 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 name="Air supply pipe" - color="#0000FF" + color=COLOR_BLUE obj/machinery/atmospherics/pipe/manifold4w/hidden/scrubbers name="Scrubbers pipe" - color="#FF0000" + color=COLOR_RED obj/machinery/atmospherics/pipe/manifold4w/hidden/yellow color="#FFCC00" obj/machinery/atmospherics/pipe/manifold4w/hidden/cyan - color="#00FFFF" + color=COLOR_CYAN obj/machinery/atmospherics/pipe/manifold4w/hidden/green - color="#00FF00" + color=COLOR_GREEN 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" dir = get_dir(src, node1) else - icon_state = "exposed" \ No newline at end of file + icon_state = "exposed" diff --git a/code/ZAS/Airflow.dm b/code/ZAS/Airflow.dm index 894dc92c72..896cda8440 100644 --- a/code/ZAS/Airflow.dm +++ b/code/ZAS/Airflow.dm @@ -1,53 +1,8 @@ /* - -CONTAINS: -All AirflowX() procs, all Variable Setting Controls for airflow, save/load variable tweaks for airflow. - -VARIABLES: - -atom/movable/airflow_dest - The destination turf of a flying object. - -atom/movable/airflow_speed - The speed (1-15) at which a flying object is traveling to airflow_dest. Decays over time. - - -OVERLOADABLE PROCS: - -mob/airflow_stun() - Contains checks for and results of being stunned by airflow. - Called when airflow quantities exceed airflow_medium_pressure. - RETURNS: Null - -atom/movable/check_airflow_movable(n) - Contains checks for moving any object due to airflow. - n is the pressure that is flowing. - RETURNS: 1 if the object moves under the air conditions, 0 if it stays put. - -atom/movable/airflow_hit(atom/A) - Contains results of hitting a solid object (A) due to airflow. - A is the dense object hit. - Use airflow_speed to determine how fast the projectile was going. - - -AUTOMATIC PROCS: - -Airflow(zone/A, zone/B) - Causes objects to fly along a pressure gradient. - Called by zone updates. A and B are two connected zones. - -AirflowSpace(zone/A) - Causes objects to fly into space. - Called by zone updates. A is a zone connected to space. - -atom/movable/GotoAirflowDest(n) -atom/movable/RepelAirflowDest(n) - Called by main airflow procs to cause the object to fly to or away from destination at speed n. - Probably shouldn't call this directly unless you know what you're - doing and have set airflow_dest. airflow_hit() will be called if the object collides with an obstacle. - +Contains helper procs for airflow, handled in /connection_group. */ + mob/var/tmp/last_airflow_stun = 0 mob/proc/airflow_stun() if(stat == 2) @@ -108,124 +63,6 @@ obj/item/check_airflow_movable(n) if(4,5) if(n < vsc.airflow_medium_pressure) return 0 -//The main airflow code. Called by zone updates. -//Zones A and B are air zones. n represents the amount of air moved. - -proc/Airflow(zone/A, zone/B) - - var/n = B.air.return_pressure() - A.air.return_pressure() - - //Don't go any further if n is lower than the lowest value needed for airflow. - if(abs(n) < vsc.airflow_lightest_pressure) return - - //These turfs are the midway point between A and B, and will be the destination point for thrown objects. - var/list/connection/connections_A = A.connections - var/list/turf/connected_turfs = list() - for(var/connection/C in connections_A) //Grab the turf that is in the zone we are flowing to (determined by n) - if( ( A == C.A.zone || A == C.zone_A ) && ( B == C.B.zone || B == C.zone_B ) ) - if(n < 0) - connected_turfs |= C.B - else - connected_turfs |= C.A - else if( ( A == C.B.zone || A == C.zone_B ) && ( B == C.A.zone || B == C.zone_A ) ) - if(n < 0) - connected_turfs |= C.A - else - connected_turfs |= C.B - - //Get lists of things that can be thrown across the room for each zone (assumes air is moving from zone B to zone A) - var/list/air_sucked = B.movables() - var/list/air_repelled = A.movables() - if(n < 0) - //air is moving from zone A to zone B - var/list/temporary_pplz = air_sucked - air_sucked = air_repelled - air_repelled = temporary_pplz - - for(var/atom/movable/M in air_sucked) - - if(M.last_airflow > world.time - vsc.airflow_delay) continue - - //Check for knocking people over - if(ismob(M) && n > vsc.airflow_stun_pressure) - if(M:status_flags & GODMODE) continue - M:airflow_stun() - - if(M.check_airflow_movable(n)) - - //Check for things that are in range of the midpoint turfs. - var/list/close_turfs = list() - for(var/turf/U in connected_turfs) - if(M in range(U)) close_turfs += U - if(!close_turfs.len) continue - - //If they're already being tossed, don't do it again. - if(!M.airflow_speed) - - M.airflow_dest = pick(close_turfs) //Pick a random midpoint to fly towards. - - spawn M.GotoAirflowDest(abs(n)/5) - - //Do it again for the stuff in the other zone, making it fly away. - for(var/atom/movable/M in air_repelled) - - if(M.last_airflow > world.time - vsc.airflow_delay) continue - - if(ismob(M) && abs(n) > vsc.airflow_medium_pressure) - if(M:status_flags & GODMODE) continue - M:airflow_stun() - - if(M.check_airflow_movable(abs(n))) - - var/list/close_turfs = list() - for(var/turf/U in connected_turfs) - if(M in range(U)) close_turfs += U - if(!close_turfs.len) continue - - //If they're already being tossed, don't do it again. - if(!M.airflow_speed) - - M.airflow_dest = pick(close_turfs) //Pick a random midpoint to fly towards. - - spawn M.RepelAirflowDest(abs(n)/5) - -proc/AirflowSpace(zone/A) - - //The space version of the Airflow(A,B,n) proc. - - var/n = A.air.return_pressure() - //Here, n is determined by only the pressure in the room. - - if(n < vsc.airflow_lightest_pressure) return - - var/list/connected_turfs = A.unsimulated_tiles //The midpoints are now all the space connections. - var/list/pplz = A.movables() //We only need to worry about things in the zone, not things in space. - - for(var/atom/movable/M in pplz) - - if(M.last_airflow > world.time - vsc.airflow_delay) continue - - if(ismob(M) && n > vsc.airflow_stun_pressure) - var/mob/O = M - if(O.status_flags & GODMODE) continue - O.airflow_stun() - - if(M.check_airflow_movable(n)) - - var/list/close_turfs = list() - for(var/turf/U in connected_turfs) - if(M in range(U)) close_turfs += U - if(!close_turfs.len) continue - - //If they're already being tossed, don't do it again. - if(!M.airflow_speed) - - M.airflow_dest = pick(close_turfs) //Pick a random midpoint to fly towards. - spawn - if(M) M.GotoAirflowDest(n/10) - //Sometimes shit breaks, and M isn't there after the spawn. - - /atom/movable/var/tmp/turf/airflow_dest /atom/movable/var/tmp/airflow_speed = 0 /atom/movable/var/tmp/airflow_time = 0 @@ -416,4 +253,4 @@ zone/proc/movables() for(var/atom/A in T) if(istype(A, /obj/effect) || istype(A, /mob/aiEye)) continue - . += A + . += A \ No newline at end of file diff --git a/code/ZAS/Atom.dm b/code/ZAS/Atom.dm new file mode 100644 index 0000000000..38e57d6165 --- /dev/null +++ b/code/ZAS/Atom.dm @@ -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 \ No newline at end of file diff --git a/code/ZAS/Connection.dm b/code/ZAS/Connection.dm index 1bb8f9ca54..e3ce7373a6 100644 --- a/code/ZAS/Connection.dm +++ b/code/ZAS/Connection.dm @@ -1,448 +1,159 @@ -/* -This object is contained within zone/var/connections. It's generated whenever two turfs from different zones are linked. -Indirect connections will not merge the two zones after they reach equilibrium. -*/ -#define CONNECTION_DIRECT 2 -#define CONNECTION_INDIRECT 1 -#define CONNECTION_CLOSED 0 +#define CONNECTION_DIRECT 2 +#define CONNECTION_SPACE 4 +#define CONNECTION_INVALID 8 -/connection - 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. +/turf/simulated/var/tmp/connection_manager/connections = new -/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 - B = O +/connection_manager/proc/get(d) + switch(d) + if(NORTH) + if(check(N)) return N + else return null + if(SOUTH) + if(check(S)) return S + else return null + if(EAST) + if(check(E)) return E + else return null + if(WEST) + if(check(W)) return W + else return null - if(A.zone && B.zone) - if(!A.zone.connections) - A.zone.connections = list() - A.zone.connections += src - zone_A = A.zone +/connection_manager/proc/place(connection/c, d) + switch(d) + if(NORTH) N = c + if(SOUTH) S = c + if(EAST) E = c + if(WEST) W = c - if(!B.zone.connections) - B.zone.connections = list() - B.zone.connections += src - zone_B = B.zone +/connection_manager/proc/update_all() + if(check(N)) N.update() + if(check(S)) S.update() + if(check(E)) E.update() + if(check(W)) W.update() - if(A in air_master.turfs_with_connections) - var/list/connections = air_master.turfs_with_connections[A] - connections.Add(src) - else - air_master.turfs_with_connections[A] = list(src) +/connection_manager/proc/check(connection/c) + return c && c.valid() - 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)) - indirect = CONNECTION_INDIRECT +/connection/var/connection_edge/edge - ConnectZones(A.zone, B.zone, indirect) - - else - ConnectZones(A.zone, B.zone) - indirect = CONNECTION_CLOSED +/connection/var/state = 0 +/connection/New(turf/simulated/A, turf/simulated/B) + #ifdef ZASDBG + ASSERT(air_master.has_valid_zone(A)) + //ASSERT(air_master.has_valid_zone(B)) + #endif + src.A = A + src.B = B + zoneA = A.zone + if(!istype(B)) + mark_space() + edge = air_master.get_edge(A.zone,B) + edge.add_connection(src) else - world.log << "Attempted to create connection object for non-zone tiles: [T] ([T.x],[T.y],[T.z]) -> [O] ([O.x],[O.y],[O.z])" - SoftDelete() + zoneB = B.zone + edge = air_master.get_edge(A.zone,B.zone) + edge.add_connection(src) +/connection/proc/mark_direct() + edge.remove_connection(src) + state |= CONNECTION_DIRECT + edge.add_connection(src) -/connection/Del() - //remove connections from master lists. - if(B in air_master.turfs_with_connections) - var/list/connections = air_master.turfs_with_connections[B] - connections.Remove(src) +/connection/proc/mark_indirect() + edge.remove_connection(src) + state &= ~CONNECTION_DIRECT + edge.add_connection(src) - if(A in air_master.turfs_with_connections) - var/list/connections = air_master.turfs_with_connections[A] - connections.Remove(src) +/connection/proc/mark_space() + state |= CONNECTION_SPACE - //Remove connection from zones. - if(A) - if(A.zone && A.zone.connections) - A.zone.connections.Remove(src) - if(!A.zone.connections.len) - A.zone.connections = null +/connection/proc/direct() + return (state & CONNECTION_DIRECT) - if(istype(zone_A) && (!A || A.zone != zone_A)) - if(zone_A.connections) - zone_A.connections.Remove(src) - if(!zone_A.connections.len) - zone_A.connections = null +/connection/proc/valid() + return !(state & CONNECTION_INVALID) - if(B) - if(B.zone && B.zone.connections) - B.zone.connections.Remove(src) - if(!B.zone.connections.len) - B.zone.connections = null +/connection/proc/erase() + edge.remove_connection(src) + state |= CONNECTION_INVALID - if(istype(zone_B) && (!B || B.zone != zone_B)) - if(zone_B.connections) - zone_B.connections.Remove(src) - if(!zone_B.connections.len) - zone_B.connections = null - - //Disconnect zones while handling unusual conditions. - // e.g. loss of a zone on a turf - DisconnectZones(zone_A, zone_B) - - //Finally, preform actual deletion. - . = ..() - - -/connection/proc/SoftDelete() - //remove connections from master lists. - if(B in air_master.turfs_with_connections) - var/list/connections = air_master.turfs_with_connections[B] - connections.Remove(src) - - if(A in air_master.turfs_with_connections) - var/list/connections = air_master.turfs_with_connections[A] - connections.Remove(src) - - //Remove connection from zones. - if(A) - if(A.zone && A.zone.connections) - A.zone.connections.Remove(src) - if(!A.zone.connections.len) - A.zone.connections = null - - if(istype(zone_A) && (!A || A.zone != zone_A)) - if(zone_A.connections) - zone_A.connections.Remove(src) - if(!zone_A.connections.len) - zone_A.connections = null - - if(B) - if(B.zone && B.zone.connections) - B.zone.connections.Remove(src) - if(!B.zone.connections.len) - B.zone.connections = null - - if(istype(zone_B) && (!B || B.zone != zone_B)) - if(zone_B.connections) - zone_B.connections.Remove(src) - if(!zone_B.connections.len) - zone_B.connections = null - - //Disconnect zones while handling unusual conditions. - // e.g. loss of a zone on a turf - DisconnectZones(zone_A, zone_B) - - -/connection/proc/ConnectZones(var/zone/zone_1, var/zone/zone_2, open = 0) - - //Sanity checking - if(!istype(zone_1) || !istype(zone_2)) +/connection/proc/update() + //world << "Updated, \..." + if(!istype(A,/turf/simulated)) + //world << "Invalid A." + erase() return - //Handle zones connecting indirectly/directly. - if(open) - - //Create the lists if necessary. - if(!zone_1.connected_zones) - zone_1.connected_zones = list() - - if(!zone_2.connected_zones) - zone_2.connected_zones = list() - - //Increase the number of connections between zones. - if(zone_2 in zone_1.connected_zones) - zone_1.connected_zones[zone_2]++ - else - zone_1.connected_zones += zone_2 - zone_1.connected_zones[zone_2] = 1 - - if(zone_1 in zone_2.connected_zones) - zone_2.connected_zones[zone_1]++ - else - zone_2.connected_zones += zone_1 - zone_2.connected_zones[zone_1] = 1 - - if(open == CONNECTION_DIRECT) - if(!zone_1.direct_connections) - zone_1.direct_connections = list(src) - else - zone_1.direct_connections += src - - if(!zone_2.direct_connections) - zone_2.direct_connections = list(src) - else - zone_2.direct_connections += src - - - //Handle closed connections. - else - - //Create the lists - if(!zone_1.closed_connection_zones) - zone_1.closed_connection_zones = list() - - if(!zone_2.closed_connection_zones) - zone_2.closed_connection_zones = list() - - //Increment the connections. - if(zone_2 in zone_1.closed_connection_zones) - zone_1.closed_connection_zones[zone_2]++ - else - zone_1.closed_connection_zones += zone_2 - zone_1.closed_connection_zones[zone_2] = 1 - - if(zone_1 in zone_2.closed_connection_zones) - zone_2.closed_connection_zones[zone_1]++ - else - zone_2.closed_connection_zones += zone_1 - zone_2.closed_connection_zones[zone_1] = 1 - - if(zone_1.status == ZONE_SLEEPING) - zone_1.SetStatus(ZONE_ACTIVE) - - if(zone_2.status == ZONE_SLEEPING) - zone_2.SetStatus(ZONE_ACTIVE) - -/connection/proc/DisconnectZones(var/zone/zone_1, var/zone/zone_2) - //Sanity checking - if(!istype(zone_1) || !istype(zone_2)) + var/block_status = air_master.air_blocked(A,B) + if(block_status & AIR_BLOCKED) + //world << "Blocked connection." + erase() return - - if(indirect != CONNECTION_CLOSED) - //Handle disconnection of indirectly or directly connected zones. - if( (zone_1 in zone_2.connected_zones) || (zone_2 in zone_1.connected_zones) ) - - //If there are more than one connection, decrement the number of connections - //Otherwise, remove all connections between the zones. - if(zone_2 in zone_1.connected_zones) - if(zone_1.connected_zones[zone_2] > 1) - zone_1.connected_zones[zone_2]-- - else - zone_1.connected_zones -= zone_2 - //remove the list if it is empty - if(!zone_1.connected_zones.len) - zone_1.connected_zones = null - - //Then do the same for the other zone. - if(zone_1 in zone_2.connected_zones) - if(zone_2.connected_zones[zone_1] > 1) - zone_2.connected_zones[zone_1]-- - else - zone_2.connected_zones -= zone_1 - if(!zone_2.connected_zones.len) - zone_2.connected_zones = null - - if(indirect == CONNECTION_DIRECT) - zone_1.direct_connections -= src - if(!zone_1.direct_connections.len) - zone_1.direct_connections = null - - zone_2.direct_connections -= src - if(!zone_2.direct_connections.len) - zone_2.direct_connections = null - - else - //Handle disconnection of closed zones. - if( (zone_1 in zone_2.closed_connection_zones) || (zone_2 in zone_1.closed_connection_zones) ) - - //If there are more than one connection, decrement the number of connections - //Otherwise, remove all connections between the zones. - if(zone_2 in zone_1.closed_connection_zones) - if(zone_1.closed_connection_zones[zone_2] > 1) - zone_1.closed_connection_zones[zone_2]-- - else - zone_1.closed_connection_zones -= zone_2 - //remove the list if it is empty - if(!zone_1.closed_connection_zones.len) - zone_1.closed_connection_zones = null - - //Then do the same for the other zone. - if(zone_1 in zone_2.closed_connection_zones) - if(zone_2.closed_connection_zones[zone_1] > 1) - zone_2.closed_connection_zones[zone_1]-- - else - zone_2.closed_connection_zones -= zone_1 - if(!zone_2.closed_connection_zones.len) - zone_2.closed_connection_zones = null - - -/connection/proc/Cleanup() - - //Check sanity: existance of turfs - if(!A || !B) - SoftDelete() - return - - //Check sanity: loss of zone - if(!A.zone || !B.zone) - SoftDelete() - return - - //Check sanity: zones are different - if(A.zone == B.zone) - SoftDelete() - return - - //Handle zones changing on a turf. - if((A.zone && A.zone != zone_A) || (B.zone && B.zone != zone_B)) - Sanitize() - - if(A.zone && B.zone) - - //If no walls are blocking us... - if(A.ZAirPass(B)) - //...we check to see if there is a door in the way... - var/door_pass = A.CanPass(null,B,1.5,1) - //...and if it is opened. - if(door_pass || A.CanPass(null,B,0,0)) - - //Make and remove connections to let air pass. - if(indirect == CONNECTION_CLOSED) - DisconnectZones(A.zone, B.zone) - ConnectZones(A.zone, B.zone, door_pass + 1) - - if(door_pass) - indirect = CONNECTION_DIRECT - else if(!door_pass) - indirect = CONNECTION_INDIRECT - - //The door is instead closed. - else if(indirect > CONNECTION_CLOSED) - DisconnectZones(A.zone, B.zone) - indirect = CONNECTION_CLOSED - ConnectZones(A.zone, B.zone) - - //If I can no longer pass air, better delete + else if(block_status & ZONE_BLOCKED) + if(direct()) + mark_indirect() else - SoftDelete() + mark_direct() + + var/b_is_space = !istype(B,/turf/simulated) + + if(state & CONNECTION_SPACE) + if(!b_is_space) + //world << "Invalid B." + erase() return - -/connection/proc/Sanitize() - //If the zones change on connected turfs, update it. - - //Both zones changed (wat) - if(A.zone && A.zone != zone_A && B.zone && B.zone != zone_B) - - //If the zones have gotten swapped - // (do not ask me how, I am just being anal retentive about sanity) - if(A.zone == zone_B && B.zone == zone_A) - var/turf/temp = B - B = A - A = temp - zone_B = B.zone - zone_A = A.zone - return - - //Handle removal of connections from archived zones. - if(zone_A && zone_A.connections) - zone_A.connections.Remove(src) - if(!zone_A.connections.len) - zone_A.connections = null - - if(zone_B && zone_B.connections) - zone_B.connections.Remove(src) - if(!zone_B.connections.len) - zone_B.connections = null - - if(A.zone) - if(!A.zone.connections) - A.zone.connections = list() - A.zone.connections |= src - - if(B.zone) - if(!B.zone.connections) - B.zone.connections = list() - B.zone.connections |= src - - //If either zone is null, we disconnect the archived ones after cleaning up the connections. - if(!A.zone || !B.zone) - if(zone_A && zone_B) - DisconnectZones(zone_B, zone_A) - + if(A.zone != zoneA) + //world << "Zone changed, \..." if(!A.zone) - zone_A = A.zone + erase() + //world << "erased." + return + else + edge.remove_connection(src) + edge = air_master.get_edge(A.zone, B) + edge.add_connection(src) + zoneA = A.zone - if(!B.zone) - zone_B = B.zone + //world << "valid." + return + + else if(b_is_space) + //world << "Invalid B." + erase() + return + + if(A.zone == B.zone) + //world << "A == B" + erase() + return + + if(A.zone != zoneA || (zoneB && (B.zone != zoneB))) + + //world << "Zones changed, \..." + if(A.zone && B.zone) + edge.remove_connection(src) + edge = air_master.get_edge(A.zone, B.zone) + edge.add_connection(src) + zoneA = A.zone + zoneB = B.zone + else + //world << "erased." + erase() return - //Handle diconnection and reconnection of zones. - if(zone_A && zone_B) - DisconnectZones(zone_A, zone_B) - ConnectZones(A.zone, B.zone, indirect) - //resetting values of archived values. - zone_B = B.zone - zone_A = A.zone - - //The "A" zone changed. - else if(A.zone && A.zone != zone_A) - - //Handle connection cleanup - if(zone_A) - if(zone_A.connections) - zone_A.connections.Remove(src) - if(!zone_A.connections.len) - zone_A.connections = null - - if(A.zone) - if(!A.zone.connections) - A.zone.connections = list() - A.zone.connections |= src - - //If the "A" zone is null, we disconnect the archived ones after cleaning up the connections. - if(!A.zone) - if(zone_A && zone_B) - DisconnectZones(zone_A, zone_B) - zone_A = A.zone - return - - //Handle diconnection and reconnection of zones. - if(zone_A && zone_B) - DisconnectZones(zone_A, zone_B) - ConnectZones(A.zone, B.zone, indirect) - zone_A = A.zone - - //The "B" zone changed. - else if(B.zone && B.zone != zone_B) - - //Handle connection cleanup - if(zone_B) - if(zone_B.connections) - zone_B.connections.Remove(src) - if(!zone_B.connections.len) - zone_B.connections = null - - if(B.zone) - if(!B.zone.connections) - B.zone.connections = list() - B.zone.connections |= src - - //If the "B" zone is null, we disconnect the archived ones after cleaning up the connections. - if(!B.zone) - if(zone_A && zone_B) - DisconnectZones(zone_A, zone_B) - zone_B = B.zone - return - - //Handle diconnection and reconnection of zones. - if(zone_A && zone_B) - DisconnectZones(zone_A, zone_B) - ConnectZones(A.zone, B.zone, indirect) - zone_B = B.zone - - -#undef CONNECTION_DIRECT -#undef CONNECTION_INDIRECT -#undef CONNECTION_CLOSED + //world << "valid." \ No newline at end of file diff --git a/code/ZAS/ConnectionGroup.dm b/code/ZAS/ConnectionGroup.dm new file mode 100644 index 0000000000..2a4c374f78 --- /dev/null +++ b/code/ZAS/ConnectionGroup.dm @@ -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) \ No newline at end of file diff --git a/code/ZAS/Controller.dm b/code/ZAS/Controller.dm new file mode 100644 index 0000000000..32899b1556 --- /dev/null +++ b/code/ZAS/Controller.dm @@ -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 << {"Geometry initialized in [round(0.1*(world.timeofday-start_time),0.1)] seconds. +Total Simulated Turfs: [simulated_turf_count] +Total Zones: [zones.len] +Total Unsimulated Turfs: [world.maxx*world.maxy*world.maxz - simulated_turf_count]"} + +// spawn Start() + + +/datum/controller/air_system/proc/Start() + //Purpose: This is kicked off by the master controller, and controls the processing of all atmosphere. + //Called by: Master controller + //Inputs: None. + //Outputs: None. + + #ifndef ZASDBG + set background = 1 + #endif + + while(1) + Tick() + sleep(max(5,update_delay*tick_multiplier)) + + +/datum/controller/air_system/proc/Tick() + . = 1 //Set the default return value, for runtime detection. + + current_cycle++ + + //If there are tiles to update, do so. + tick_progress = "updating turf properties" + + var/list/updating + + if(tiles_to_update.len) + updating = tiles_to_update + tiles_to_update = list() + + #ifdef ZASDBG + var/updated = 0 + #endif + for(var/turf/T in updating) + T.update_air_properties() + T.post_update_air_properties() + T.needs_air_update = 0 + #ifdef ZASDBG + T.overlays -= mark + updated++ + #endif + //sleep(1) + + #ifdef ZASDBG + if(updated != updating.len) + tick_progress = "[updating.len - updated] tiles left unupdated." + world << "\red [tick_progress]" + . = 0 + #endif + + //Where gas exchange happens. + if(.) + tick_progress = "processing edges" + + for(var/connection_edge/edge in edges) + edge.tick() + + //Process fires. + if(.) + tick_progress = "processing fire" + + for(var/obj/fire/fire in active_hotspots) + fire.process() + + //Process zones. + if(.) + tick_progress = "updating zones" + + active_zones = zones_to_update.len + if(zones_to_update.len) + updating = zones_to_update + zones_to_update = list() + for(var/zone/zone in updating) + zone.tick() + zone.needs_update = 0 + + if(.) + tick_progress = "success" + +/datum/controller/air_system/proc/add_zone(zone/z) + zones.Add(z) + z.name = "Zone [next_id++]" + mark_zone_update(z) + +/datum/controller/air_system/proc/remove_zone(zone/z) + zones.Remove(z) + +/datum/controller/air_system/proc/air_blocked(turf/A, turf/B) + #ifdef ZASDBG + ASSERT(isturf(A)) + ASSERT(isturf(B)) + #endif + var/ablock = A.c_airblock(B) + if(ablock == BLOCKED) return BLOCKED + return ablock | B.c_airblock(A) + +/datum/controller/air_system/proc/has_valid_zone(turf/simulated/T) + #ifdef ZASDBG + ASSERT(istype(T)) + #endif + return istype(T) && T.zone && !T.zone.invalid + +/datum/controller/air_system/proc/merge(zone/A, zone/B) + #ifdef ZASDBG + ASSERT(istype(A)) + ASSERT(istype(B)) + ASSERT(A != 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) \ No newline at end of file diff --git a/code/ZAS/Debug.dm b/code/ZAS/Debug.dm index 39f5a7caab..e9ac325d53 100644 --- a/code/ZAS/Debug.dm +++ b/code/ZAS/Debug.dm @@ -1,219 +1,20 @@ -client/proc/ZoneTick() - set category = "Debug" - set name = "Process Atmos" +var/image/assigned = image('icons/Testing/Zone.dmi', icon_state = "assigned") +var/image/created = image('icons/Testing/Zone.dmi', icon_state = "created") +var/image/merged = image('icons/Testing/Zone.dmi', icon_state = "merged") +var/image/invalid_zone = image('icons/Testing/Zone.dmi', icon_state = "invalid") +var/image/air_blocked = image('icons/Testing/Zone.dmi', icon_state = "block") +var/image/zone_blocked = image('icons/Testing/Zone.dmi', icon_state = "zoneblock") +var/image/blocked = image('icons/Testing/Zone.dmi', icon_state = "fullblock") +var/image/mark = image('icons/Testing/Zone.dmi', icon_state = "mark") - var/result = air_master.Tick() - if(result) - src << "Sucessfully Processed." +/turf/var/tmp/dbg_img +/turf/proc/dbg(image/img, d = 0) + if(d > 0) img.dir = d + overlays -= dbg_img + overlays += img + dbg_img = img - else - src << "Failed to process! ([air_master.tick_progress])" - - -client/proc/Zone_Info(turf/T as null|turf) - set category = "Debug" - if(T) - if(T.zone) - T.zone.DebugDisplay(src) - else - mob << "No zone here." - else - if(zone_debug_images) - for(var/zone in zone_debug_images) - images -= zone_debug_images[zone] - zone_debug_images = null - -client/var/list/zone_debug_images - -client/proc/Test_ZAS_Connection(var/turf/simulated/T as turf) - set category = "Debug" - if(!istype(T)) - return - - var/direction_list = list(\ - "North" = NORTH,\ - "South" = SOUTH,\ - "East" = EAST,\ - "West" = WEST,\ - "N/A" = null) - var/direction = input("What direction do you wish to test?","Set direction") as null|anything in direction_list - if(!direction) - return - - if(direction == "N/A") - if(T.CanPass(null, T, 0,0)) - mob << "The turf can pass air! :D" - else - mob << "No air passage :x" - return - - var/turf/simulated/other_turf = get_step(T, direction_list[direction]) - if(!istype(other_turf)) - return - - var/pass_directions = T.CanPass(null, other_turf, 0, 0) + 2 * other_turf.CanPass(null, T, 0, 0) - - switch(pass_directions) - if(0) - mob << "Neither turf can connect. :(" - - if(1) - mob << "The initial turf only can connect. :\\" - - if(2) - mob << "The other turf can connect, but not the initial turf. :/" - - if(3) - mob << "Both turfs can connect! :)" - - -zone/proc/DebugDisplay(client/client) - if(!istype(client)) - return - - if(!dbg_output) - dbg_output = 1 //Don't want to be spammed when someone investigates a zone... - - if(!client.zone_debug_images) - client.zone_debug_images = list() - - var/list/current_zone_images = list() - - for(var/turf/T in contents) - current_zone_images += image('icons/misc/debug_group.dmi', T, null, TURF_LAYER) - - for(var/turf/space/S in unsimulated_tiles) - current_zone_images += image('icons/misc/debug_space.dmi', S, null, TURF_LAYER) - - client << "Zone Air Contents" - client << "Oxygen: [air.oxygen]" - client << "Nitrogen: [air.nitrogen]" - client << "Plasma: [air.toxins]" - client << "Carbon Dioxide: [air.carbon_dioxide]" - client << "Temperature: [air.temperature] K" - client << "Heat Energy: [air.temperature * air.heat_capacity()] J" - client << "Pressure: [air.return_pressure()] KPa" - client << "" - client << "Space Tiles: [length(unsimulated_tiles)]" - client << "Movable Objects: [length(movables())]" - client << "Connections: [length(connections)]" - - for(var/connection/C in connections) - client << "\ref[C] [C.A] --> [C.B] [(C.indirect?"Open":"Closed")]" - current_zone_images += image('icons/misc/debug_connect.dmi', C.A, null, TURF_LAYER) - current_zone_images += image('icons/misc/debug_connect.dmi', C.B, null, TURF_LAYER) - - client << "Connected Zones:" - for(var/zone/zone in connected_zones) - client << "\ref[zone] [zone] - [connected_zones[zone]] (Connected)" - - for(var/zone/zone in closed_connection_zones) - client << "\ref[zone] [zone] - [closed_connection_zones[zone]] (Unconnected)" - - for(var/C in connections) - if(!istype(C,/connection)) - client << "[C] (Not Connection!)" - - if(!client.zone_debug_images) - client.zone_debug_images = list() - client.zone_debug_images[src] = current_zone_images - - client.images += client.zone_debug_images[src] - - else - dbg_output = 0 - - client.images -= client.zone_debug_images[src] - client.zone_debug_images.Remove(src) - - if(air_master) - for(var/zone/Z in air_master.zones) - if(Z.air == air && Z != src) - var/turf/zloc = pick(Z.contents) - client << "\red Illegal air datum shared by: [zloc.loc.name]" - - -client/proc/TestZASRebuild() - set category = "Debug" -// var/turf/turf = get_turf(mob) - var/zone/current_zone = mob.loc:zone - if(!current_zone) - src << "There is no zone there!" - return - - var/list/current_adjacents = list() - var/list/overlays = list() - var/adjacent_id - var/lowest_id - - var/list/identical_ids = list() - var/list/turfs = current_zone.contents.Copy() - var/current_identifier = 1 - - for(var/turf/simulated/current in turfs) - lowest_id = null - current_adjacents = list() - - for(var/direction in cardinal) - var/turf/simulated/adjacent = get_step(current, direction) - if(!current.ZCanPass(adjacent)) - continue - if(turfs.Find(adjacent)) - current_adjacents += adjacent - adjacent_id = turfs[adjacent] - - if(adjacent_id && (!lowest_id || adjacent_id < lowest_id)) - lowest_id = adjacent_id - - if(!lowest_id) - lowest_id = current_identifier++ - identical_ids += lowest_id - overlays += image('icons/misc/debug_rebuild.dmi',, "[lowest_id]") - - for(var/turf/simulated/adjacent in current_adjacents) - adjacent_id = turfs[adjacent] - if(adjacent_id != lowest_id) - if(adjacent_id) - adjacent.overlays -= overlays[adjacent_id] - identical_ids[adjacent_id] = lowest_id - - turfs[adjacent] = lowest_id - adjacent.overlays += overlays[lowest_id] - - sleep(5) - - if(turfs[current]) - current.overlays -= overlays[turfs[current]] - turfs[current] = lowest_id - current.overlays += overlays[lowest_id] - sleep(5) - - var/list/final_arrangement = list() - - for(var/turf/simulated/current in turfs) - current_identifier = identical_ids[turfs[current]] - current.overlays -= overlays[turfs[current]] - current.overlays += overlays[current_identifier] - sleep(5) - - if( current_identifier > final_arrangement.len ) - final_arrangement.len = current_identifier - final_arrangement[current_identifier] = list(current) - - else - final_arrangement[current_identifier] += current - - //lazy but fast - final_arrangement.Remove(null) - - src << "There are [final_arrangement.len] unique segments." - - for(var/turf/current in turfs) - current.overlays -= overlays - - return final_arrangement - -/client/proc/ZASSettings() - set category = "Debug" - - vsc.SetDefault(mob) \ No newline at end of file +/turf/simulated/var/verbose = 0 +/turf/simulated/verb/Verbose() + set src in world + verbose = !verbose \ No newline at end of file diff --git a/code/ZAS/Diagnostic.dm b/code/ZAS/Diagnostic.dm new file mode 100644 index 0000000000..07f06a5514 --- /dev/null +++ b/code/ZAS/Diagnostic.dm @@ -0,0 +1,233 @@ +client/proc/ZoneTick() + set category = "Debug" + set name = "Process Atmos" + + var/result = air_master.Tick() + if(result) + src << "Sucessfully Processed." + + else + src << "Failed to process! ([air_master.tick_progress])" + + +client/proc/Zone_Info(turf/T as null|turf) + set category = "Debug" + if(T) + if(istype(T,/turf/simulated) && T:zone) + T:zone:dbg_data(src) + else + mob << "No zone here." + else + if(zone_debug_images) + for(var/zone in zone_debug_images) + images -= zone_debug_images[zone] + zone_debug_images = null + +client/var/list/zone_debug_images + +client/proc/Test_ZAS_Connection(var/turf/simulated/T as turf) + set category = "Debug" + if(!istype(T)) + return + + var/direction_list = list(\ + "North" = NORTH,\ + "South" = SOUTH,\ + "East" = EAST,\ + "West" = WEST,\ + "N/A" = null) + var/direction = input("What direction do you wish to test?","Set direction") as null|anything in direction_list + if(!direction) + return + + if(direction == "N/A") + if(!(T.c_airblock(T) & AIR_BLOCKED)) + mob << "The turf can pass air! :D" + else + mob << "No air passage :x" + return + + var/turf/simulated/other_turf = get_step(T, direction_list[direction]) + if(!istype(other_turf)) + return + + var/t_block = T.c_airblock(other_turf) + var/o_block = other_turf.c_airblock(T) + + if(o_block & AIR_BLOCKED) + if(t_block & AIR_BLOCKED) + mob << "Neither turf can connect. :(" + + else + mob << "The initial turf only can connect. :\\" + else + if(t_block & AIR_BLOCKED) + mob << "The other turf can connect, but not the initial turf. :/" + + else + mob << "Both turfs can connect! :)" + + mob << "Additionally, \..." + + if(o_block & ZONE_BLOCKED) + if(t_block & ZONE_BLOCKED) + mob << "neither turf can merge." + else + mob << "the other turf cannot merge." + else + if(t_block & ZONE_BLOCKED) + mob << "the initial turf cannot merge." + else + mob << "both turfs can merge." + + +/*zone/proc/DebugDisplay(client/client) + if(!istype(client)) + return + + if(!dbg_output) + dbg_output = 1 //Don't want to be spammed when someone investigates a zone... + + if(!client.zone_debug_images) + client.zone_debug_images = list() + + var/list/current_zone_images = list() + + for(var/turf/T in contents) + current_zone_images += image('icons/misc/debug_group.dmi', T, null, TURF_LAYER) + + for(var/turf/space/S in unsimulated_tiles) + current_zone_images += image('icons/misc/debug_space.dmi', S, null, TURF_LAYER) + + client << "Zone Air Contents" + client << "Oxygen: [air.oxygen]" + client << "Nitrogen: [air.nitrogen]" + client << "Plasma: [air.toxins]" + client << "Carbon Dioxide: [air.carbon_dioxide]" + client << "Temperature: [air.temperature] K" + client << "Heat Energy: [air.temperature * air.heat_capacity()] J" + client << "Pressure: [air.return_pressure()] KPa" + client << "" + client << "Space Tiles: [length(unsimulated_tiles)]" + client << "Movable Objects: [length(movables())]" + client << "Connections: [length(connections)]" + + for(var/connection/C in connections) + client << "\ref[C] [C.A] --> [C.B] [(C.indirect?"Open":"Closed")]" + current_zone_images += image('icons/misc/debug_connect.dmi', C.A, null, TURF_LAYER) + current_zone_images += image('icons/misc/debug_connect.dmi', C.B, null, TURF_LAYER) + + client << "Connected Zones:" + for(var/zone/zone in connected_zones) + client << "\ref[zone] [zone] - [connected_zones[zone]] (Connected)" + + for(var/zone/zone in closed_connection_zones) + client << "\ref[zone] [zone] - [closed_connection_zones[zone]] (Unconnected)" + + for(var/C in connections) + if(!istype(C,/connection)) + client << "[C] (Not Connection!)" + + if(!client.zone_debug_images) + client.zone_debug_images = list() + client.zone_debug_images[src] = current_zone_images + + client.images += client.zone_debug_images[src] + + else + dbg_output = 0 + + client.images -= client.zone_debug_images[src] + client.zone_debug_images.Remove(src) + + if(air_master) + for(var/zone/Z in air_master.zones) + if(Z.air == air && Z != src) + var/turf/zloc = pick(Z.contents) + client << "\red Illegal air datum shared by: [zloc.loc.name]"*/ + + +/*client/proc/TestZASRebuild() + set category = "Debug" +// var/turf/turf = get_turf(mob) + var/zone/current_zone = mob.loc:zone + if(!current_zone) + src << "There is no zone there!" + return + + var/list/current_adjacents = list() + var/list/overlays = list() + var/adjacent_id + var/lowest_id + + var/list/identical_ids = list() + var/list/turfs = current_zone.contents.Copy() + var/current_identifier = 1 + + for(var/turf/simulated/current in turfs) + lowest_id = null + current_adjacents = list() + + for(var/direction in cardinal) + var/turf/simulated/adjacent = get_step(current, direction) + if(!current.ZCanPass(adjacent)) + continue + if(turfs.Find(adjacent)) + current_adjacents += adjacent + adjacent_id = turfs[adjacent] + + if(adjacent_id && (!lowest_id || adjacent_id < lowest_id)) + lowest_id = adjacent_id + + if(!lowest_id) + lowest_id = current_identifier++ + identical_ids += lowest_id + overlays += image('icons/misc/debug_rebuild.dmi',, "[lowest_id]") + + for(var/turf/simulated/adjacent in current_adjacents) + adjacent_id = turfs[adjacent] + if(adjacent_id != lowest_id) + if(adjacent_id) + adjacent.overlays -= overlays[adjacent_id] + identical_ids[adjacent_id] = lowest_id + + turfs[adjacent] = lowest_id + adjacent.overlays += overlays[lowest_id] + + sleep(5) + + if(turfs[current]) + current.overlays -= overlays[turfs[current]] + turfs[current] = lowest_id + current.overlays += overlays[lowest_id] + sleep(5) + + var/list/final_arrangement = list() + + for(var/turf/simulated/current in turfs) + current_identifier = identical_ids[turfs[current]] + current.overlays -= overlays[turfs[current]] + current.overlays += overlays[current_identifier] + sleep(5) + + if( current_identifier > final_arrangement.len ) + final_arrangement.len = current_identifier + final_arrangement[current_identifier] = list(current) + + else + final_arrangement[current_identifier] += current + + //lazy but fast + final_arrangement.Remove(null) + + src << "There are [final_arrangement.len] unique segments." + + for(var/turf/current in turfs) + current.overlays -= overlays + + return final_arrangement*/ + +client/proc/ZASSettings() + set category = "Debug" + + vsc.SetDefault(mob) \ No newline at end of file diff --git a/code/ZAS/FEA_system.dm b/code/ZAS/FEA_system.dm deleted file mode 100644 index 5a386d5959..0000000000 --- a/code/ZAS/FEA_system.dm +++ /dev/null @@ -1,356 +0,0 @@ -/* -Overview: - The air_master global variable is the workhorse for the system. - -Why are you archiving data before modifying it? - The general concept with archiving data and having each tile keep track of when they were last updated is to keep everything symmetric - and totally independent of the order they are read in an update cycle. - This prevents abnormalities like air/fire spreading rapidly in one direction and super slowly in the other. - -Why not just archive everything and then calculate? - Efficiency. While a for-loop that goes through all tils and groups to archive their information before doing any calculations seems simple, it is - slightly less efficient than the archive-before-modify/read method. - -Why is there a cycle check for calculating data as well? - This ensures that every connection between group-tile, tile-tile, and group-group is only evaluated once per loop. - - - - -Important variables: - air_master.groups_to_rebuild (list) - A list of air groups that have had their geometry occluded and thus may need to be split in half. - A set of adjacent groups put in here will join together if validly connected. - This is done before air system calculations for a cycle. - air_master.tiles_to_update (list) - Turfs that are in this list have their border data updated before the next air calculations for a cycle. - Place turfs in this list rather than call the proc directly to prevent race conditions - - turf/simulated.archive() and datum/air_group.archive() - This stores all data for. - If you modify, make sure to update the archived_cycle to prevent race conditions and maintain symmetry - - atom/CanPass(atom/movable/mover, turf/target, height, air_group) - returns 1 for allow pass and 0 for deny pass - Turfs automatically call this for all objects/mobs in its turf. - This is called both as source.CanPass(target, height, air_group) - and target.CanPass(source, height, air_group) - - Cases for the parameters - 1. This is called with args (mover, location, height>0, air_group=0) for normal objects. - 2. This is called with args (null, location, height=0, air_group=0) for flowing air. - 3. This is called with args (null, location, height=?, air_group=1) for determining group boundaries. - - Cases 2 and 3 would be different for doors or other objects open and close fairly often. - (Case 3 would return 0 always while Case 2 would return 0 only when the door is open) - This prevents the necessity of re-evaluating group geometry every time a door opens/closes. - - -Important Procedures - air_master.process() - This first processes the air_master update/rebuild lists then processes all groups and tiles for air calculations - -*/ - -var/tick_multiplier = 2 - -atom/proc/CanPass(atom/movable/mover, turf/target, height=1.5, air_group = 0) - //Purpose: Determines if the object (or airflow) can pass this atom. - //Called by: Movement, airflow. - //Inputs: The moving atom (optional), target turf, "height" and air group - //Outputs: Boolean if can pass. - - return (!density || !height || air_group) - -/turf/CanPass(atom/movable/mover, turf/target, height=1.5,air_group=0) - if(!target) return 0 - - if(istype(mover)) // turf/Enter(...) will perform more advanced checks - return !density - - else // Now, doing more detailed checks for air movement and air group formation - if(target.blocks_air||blocks_air) - return 0 - - for(var/obj/obstacle in src) - if(!obstacle.CanPass(mover, target, height, air_group)) - return 0 - if(target != src) - for(var/obj/obstacle in target) - if(!obstacle.CanPass(mover, src, height, air_group)) - return 0 - - return 1 - - -var/datum/controller/air_system/air_master - -/datum/controller/air_system - //Geometry lists - var/list/turfs_with_connections = list() - var/list/active_hotspots = list() - - //Special functions lists - var/reconsidering_zones = FALSE - var/list/tiles_to_reconsider_zones = list() - var/list/tiles_to_reconsider_alternate - - //Geometry updates lists - var/updating_tiles = FALSE - var/list/tiles_to_update = list() - var/list/tiles_to_update_alternate - - var/checking_connections = FALSE - var/list/connections_to_check = list() - var/list/connections_to_check_alternate - - var/list/potential_intrazone_connections = list() - - //Zone lists - var/list/active_zones = list() - var/list/zones_needing_rebuilt = list() - var/list/zones = list() - - - var/current_cycle = 0 - var/update_delay = 5 //How long between check should it try to process atmos again. - var/failed_ticks = 0 //How many ticks have runtimed? - - var/tick_progress = 0 - - -/datum/controller/air_system/proc/Setup() - //Purpose: Call this at the start to setup air groups geometry - // (Warning: Very processor intensive but only must be done once per round) - //Called by: Gameticker/Master controller - //Inputs: None. - //Outputs: None. - - set background = 1 - world << "\red \b Processing Geometry..." - sleep(-1) - - var/start_time = world.timeofday - - var/simulated_turf_count = 0 - - for(var/turf/simulated/S in world) - simulated_turf_count++ - if(!S.zone && !S.blocks_air) - if(S.CanPass(null, S, 0, 0)) - new/zone(S) - - for(var/turf/simulated/S in world) - S.update_air_properties() - - world << {"Geometry initialized in [round(0.1*(world.timeofday-start_time),0.1)] seconds. -Total Simulated Turfs: [simulated_turf_count] -Total Zones: [zones.len] -Total Unsimulated Turfs: [world.maxx*world.maxy*world.maxz - simulated_turf_count]"} - -// spawn Start() - - -/datum/controller/air_system/proc/Start() - //Purpose: This is kicked off by the master controller, and controls the processing of all atmosphere. - //Called by: Master controller - //Inputs: None. - //Outputs: None. - - - set background = 1 - - while(1) - if(!air_processing_killed) - var/success = Tick() //Changed so that a runtime does not crash the ticker. - if(!success) //Runtimed. - failed_ticks++ - if(failed_ticks > 20) - world << "ERROR IN ATMOS TICKER. Killing air simulation!" - air_processing_killed = 1 - sleep(max(5,update_delay*tick_multiplier)) - - -/datum/controller/air_system/proc/Tick() - . = 1 //Set the default return value, for runtime detection. - - current_cycle++ - - //If there are tiles to update, do so. - tick_progress = "updating turf properties" - if(tiles_to_update.len) - updating_tiles = TRUE - - for(var/turf/simulated/T in tiles_to_update) - if(. && T && !T.update_air_properties()) - //If a runtime occured, make sure we can sense it. - . = 0 - - updating_tiles = FALSE - - if(.) - if(tiles_to_update_alternate) - tiles_to_update = tiles_to_update_alternate - tiles_to_update_alternate = null - else - tiles_to_update = list() - - else if(tiles_to_update_alternate) - tiles_to_update |= tiles_to_update_alternate - tiles_to_update_alternate = null - - //Rebuild zones. - if(.) - tick_progress = "rebuilding zones" - if(zones_needing_rebuilt.len) - for(var/zone/zone in zones_needing_rebuilt) - zone.Rebuild() - - zones_needing_rebuilt = list() - - //Check sanity on connection objects. - if(.) - tick_progress = "checking/creating connections" - if(connections_to_check.len) - checking_connections = TRUE - - for(var/connection/C in connections_to_check) - C.Cleanup() - - for(var/turf/simulated/turf_1 in potential_intrazone_connections) - for(var/turf/simulated/turf_2 in potential_intrazone_connections[turf_1]) - - if(!turf_1.zone || !turf_2.zone) - continue - - if(turf_1.zone == turf_2.zone) - continue - - var/should_skip = FALSE - if(turf_1 in air_master.turfs_with_connections) - - for(var/connection/C in turfs_with_connections[turf_1]) - if(C.B == turf_2 || C.A == turf_2) - should_skip = TRUE - break - if(should_skip) - continue - - new /connection(turf_1, turf_2) - - checking_connections = FALSE - - potential_intrazone_connections = list() - - if(connections_to_check_alternate) - connections_to_check = connections_to_check_alternate - connections_to_check_alternate = null - else - connections_to_check = list() - - //Process zones. - if(.) - tick_progress = "processing zones" - for(var/zone/Z in active_zones) - if(Z.last_update < current_cycle) - var/output = Z.process() - if(Z) - Z.last_update = current_cycle - if(. && Z && !output) - . = 0 - - //Ensure tiles still have zones. - if(.) - tick_progress = "reconsidering zones on turfs" - if(tiles_to_reconsider_zones.len) - reconsidering_zones = TRUE - - for(var/turf/simulated/T in tiles_to_reconsider_zones) - if(!T.zone) - new /zone(T) - - reconsidering_zones = FALSE - - if(tiles_to_reconsider_alternate) - tiles_to_reconsider_zones = tiles_to_reconsider_alternate - tiles_to_reconsider_alternate = null - else - tiles_to_reconsider_zones = list() - - //Process fires. - if(.) - tick_progress = "processing fire" - for(var/obj/fire/F in active_hotspots) - if(. && F && !F.process()) - . = 0 - - if(.) - tick_progress = "success" - - -/datum/controller/air_system/proc/AddTurfToUpdate(turf/simulated/outdated_turf) - var/list/tiles_to_check = list() - - if(istype(outdated_turf)) - tiles_to_check |= outdated_turf - - if(istype(outdated_turf, /turf)) - for(var/direction in cardinal) - var/turf/simulated/adjacent_turf = get_step(outdated_turf, direction) - if(istype(adjacent_turf)) - tiles_to_check |= adjacent_turf - - if(updating_tiles) - if(!tiles_to_update_alternate) - tiles_to_update_alternate = tiles_to_check - else - tiles_to_update_alternate |= tiles_to_check - else - tiles_to_update |= tiles_to_check - - -/datum/controller/air_system/proc/AddConnectionToCheck(connection/connection) - if(checking_connections) - if(istype(connection, /list)) - if(!connections_to_check_alternate) - connections_to_check_alternate = connection - - else if(!connections_to_check_alternate) - connections_to_check_alternate = list() - - connections_to_check_alternate |= connection - - else - connections_to_check |= connection - - -/datum/controller/air_system/proc/ReconsiderTileZone(var/turf/simulated/zoneless_turf) - if(zoneless_turf.zone) - return - - if(reconsidering_zones) - if(!tiles_to_reconsider_alternate) - tiles_to_reconsider_alternate = list() - - tiles_to_reconsider_alternate |= zoneless_turf - - else - tiles_to_reconsider_zones |= zoneless_turf - - -/datum/controller/air_system/proc/AddIntrazoneConnection(var/turf/simulated/A, var/turf/simulated/B) - if(!istype(A) || !istype(B)) - return - - if(A in potential_intrazone_connections) - if(B in potential_intrazone_connections[A]) - return - - if (B in potential_intrazone_connections) - if(A in potential_intrazone_connections[B]) - return - - potential_intrazone_connections[B] += A - - else - potential_intrazone_connections[B] = list(A) \ No newline at end of file diff --git a/code/ZAS/Fire.dm b/code/ZAS/Fire.dm index ddc06106f3..a564de2836 100644 --- a/code/ZAS/Fire.dm +++ b/code/ZAS/Fire.dm @@ -108,7 +108,7 @@ turf/simulated/hotspot_expose(exposed_temperature, exposed_volume, soh) A.fire_act(air_contents, air_contents.temperature, air_contents.return_volume()) //spread for(var/direction in cardinal) - if(S.air_check_directions&direction) //Grab all valid bordering tiles + if(S.open_directions & direction) //Grab all valid bordering tiles var/turf/simulated/enemy_tile = get_step(S, direction) diff --git a/code/ZAS/Functions.dm b/code/ZAS/Functions.dm deleted file mode 100644 index d0f5e59329..0000000000 --- a/code/ZAS/Functions.dm +++ /dev/null @@ -1,243 +0,0 @@ -//Global Functions -//Contents: FloodFill, ZMerge, ZConnect - -//Floods outward from an initial turf to fill everywhere it's zone would reach. -proc/FloodFill(turf/simulated/start) - - if(!istype(start)) - return list() - - //The list of tiles waiting to be evaulated. - var/list/open = list(start) - //The list of tiles which have been evaulated. - var/list/closed = list() -/////// Z-Level stuff - //List of all space tiles bordering the zone - var/list/list_space = list() - //List of all Z-Levels of the zone where it borders space - var/list/z_space = list() -/////// Z-Level stuff - - //Loop through the turfs in the open list in order to find which adjacent turfs should be added to the zone. - while(open.len) - var/turf/simulated/T = pick(open) - - //sanity! - if(!istype(T)) - open -= T - continue - - //Check all cardinal directions - for(var/d in cardinal) - var/turf/simulated/O = get_step(T,d) - - //Ensure the turf is of proper type, that it is not in either list, and that air can reach it. - if(istype(O) && !(O in open) && !(O in closed) && O.ZCanPass(T)) - - //Handle connections from a tile with a door. - if(T.HasDoor()) - //If they both have doors, then they are not able to connect period. - if(O.HasDoor()) - continue - - //Connect first to north and west - if(d == NORTH || d == WEST) - open += O - - //If that fails, and north/west cannot be connected to, see if west or south can be connected instead. - else - var/turf/simulated/W = get_step(O, WEST) - var/turf/simulated/N = get_step(O, NORTH) - - if( !O.ZCanPass(N) && !O.ZCanPass(W) ) - //If it cannot connect either to the north or west, connect it! - open += O - - //If no doors are involved, add it immediately. - else if(!O.HasDoor()) - open += O - - //Handle connecting to a tile with a door. - else - if(d == SOUTH || d == EAST) - //doors prefer connecting to zones to the north or west - closed += O - - else - //see if we need to force an attempted connection - //(there are no potentially viable zones to the north/west of the door) - var/turf/simulated/W = get_step(O, WEST) - var/turf/simulated/N = get_step(O, NORTH) - - if( !O.ZCanPass(N) && !O.ZCanPass(W) ) - //If it cannot connect either to the north or west, connect it! - closed += O - -/////// Z-Level stuff - if(istype(O,/turf/space)) - if(!(O in list_space)) - list_space += O - if(!(O.z in z_space)) - z_space += O.z - - // handle Z-level connections - var/turf/controllerlocation = locate(1, 1, T.z) - for(var/obj/effect/landmark/zcontroller/controller in controllerlocation) - // connect upwards - if(controller.up) - var/turf/above_me = locate(T.x, T.y, controller.up_target) - // add the turf above this - if(istype(above_me, /turf/simulated/floor/open) && !(above_me in open) && !(above_me in closed)) - open += above_me - - if(istype(above_me,/turf/space)) - if(!(above_me in list_space)) - list_space += above_me - if(!(above_me.z in z_space)) - z_space += above_me.z - // connect downwards - if(controller.down && istype(T, /turf/simulated/floor/open)) - var/turf/below_me = locate(T.x, T.y, controller.down_target) - // add the turf below this - if(!(below_me in open) && !(below_me in closed)) - open += below_me -/////// Z-Level stuff - - //This tile is now evaluated, and can be moved to the list of evaluated tiles. - open -= T - closed += T - -/////// Z-Level stuff - // once the zone is done, check if there is space that needs to be changed to open space - if(!open.len) - var/list/temp = list() - while(list_space.len) - var/turf/S = pick(list_space) - //check if the zone has any space borders below the evaluated space tile - //if there is some, we dont need to make open_space since the zone can vent and the zone above can vent - //through the evaluated tile - //if there is none, the zone can connect upwards to either vent from there or connect with the zone there - //also check if the turf below the space is actually part of this zone to prevent the edge tiles from transforming - var/turf/controllerloc = locate(1, 1, S.z) - for(var/obj/effect/landmark/zcontroller/controller in controllerloc) - if(controller.down) - var/turf/below = locate(S.x, S.y, controller.down_target) - if(!((S.z - 1) in z_space) && below in closed) - open += S.ChangeTurf(/turf/simulated/floor/open) - list_space -= S - else - list_space -= S - temp += S - else - list_space -= S - temp += S - // make sure the turf is removed from the list - list_space -= S - z_space -= z_space - while(temp.len) - var/turf/S = pick(temp) - if(!(S.z in z_space)) - z_space += S.z - list_space += S - temp -= S -/////// Z-Level stuff - - return closed - - -//Procedure to merge two zones together. -proc/ZMerge(zone/A,zone/B) - - //Sanity~ - if(!istype(A) || !istype(B)) - return - - var/new_contents = A.contents + B.contents - - //Set all the zone vars. - for(var/turf/simulated/T in B.contents) - T.zone = A - - if(istype(A.air) && istype(B.air)) - //Merges two zones so that they are one. - var/a_size = A.air.group_multiplier - var/b_size = B.air.group_multiplier - var/c_size = a_size + b_size - - //Set air multipliers to one so air represents gas per tile. - A.air.group_multiplier = 1 - B.air.group_multiplier = 1 - - //Remove some air proportional to the size of this zone. - A.air.remove_ratio(a_size/c_size) - B.air.remove_ratio(b_size/c_size) - - //Merge the gases and set the multiplier to the sum of the old ones. - A.air.merge(B.air) - A.air.group_multiplier = c_size - - //I hate when the air datum somehow disappears. - // Try to make it sorta work anyways. Fakit - else if(istype(B.air)) - A.air = B.air - A.air.group_multiplier = A.contents.len - - else if(istype(A.air)) - A.air.group_multiplier = A.contents.len - - //Doublefakit. - else - A.air = new - - //Check for connections to merge into the new zone. - for(var/connection/C in B.connections) - //The Cleanup proc will delete the connection if the zones are the same. - // It will also set the zone variables correctly. - C.Cleanup() - - //Add space tiles. - if(A.unsimulated_tiles && B.unsimulated_tiles) - A.unsimulated_tiles |= B.unsimulated_tiles - else if (B.unsimulated_tiles) - A.unsimulated_tiles = B.unsimulated_tiles - - //Add contents. - A.contents = new_contents - - //Remove the "B" zone, finally. - B.SoftDelete() - - -//Connects two zones by forming a connection object representing turfs A and B. -proc/ZConnect(turf/simulated/A,turf/simulated/B) - - //Make sure that if it's space, it gets added to unsimulated_tiles instead. - if(!istype(B)) - if(A.zone) - A.zone.AddTurf(B) - return - if(!istype(A)) - if(B.zone) - B.zone.AddTurf(A) - return - - if(!istype(A) || !istype(B)) - return - - //Make some preliminary checks to see if the connection is valid. - if(!A.zone || !B.zone) return - if(A.zone == B.zone) - air_master.AddIntrazoneConnection(A,B) - return - - if(A.CanPass(null, B, 1.5, 1) && A.zone.air.compare(B.zone.air)) - return ZMerge(A.zone,B.zone) - - //Ensure the connection isn't already made. - if(A in air_master.turfs_with_connections) - for(var/connection/C in air_master.turfs_with_connections[A]) - if(C.B == B || C.A == B) - return - - //Make the connection. - new /connection(A,B) diff --git a/code/ZAS/Turf.dm b/code/ZAS/Turf.dm new file mode 100644 index 0000000000..cb4b9c20f5 --- /dev/null +++ b/code/ZAS/Turf.dm @@ -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 \ No newline at end of file diff --git a/code/ZAS/ZAS_Turfs.dm b/code/ZAS/ZAS_Turfs.dm deleted file mode 100644 index 43ea288762..0000000000 --- a/code/ZAS/ZAS_Turfs.dm +++ /dev/null @@ -1,344 +0,0 @@ -/atom/var/pressure_resistance = ONE_ATMOSPHERE - -/turf/var/zone/zone - -/turf/assume_air(datum/gas_mixture/giver) //use this for machines to adjust air - del(giver) - return 0 - -/turf/return_air() - //Create gas mixture to hold data for passing - var/datum/gas_mixture/GM = new - - GM.oxygen = oxygen - GM.carbon_dioxide = carbon_dioxide - GM.nitrogen = nitrogen - GM.toxins = toxins - - GM.temperature = temperature - GM.update_values() - - return GM - -/turf/remove_air(amount as num) - var/datum/gas_mixture/GM = new - - var/sum = oxygen + carbon_dioxide + nitrogen + toxins - if(sum>0) - GM.oxygen = (oxygen/sum)*amount - GM.carbon_dioxide = (carbon_dioxide/sum)*amount - GM.nitrogen = (nitrogen/sum)*amount - GM.toxins = (toxins/sum)*amount - - GM.temperature = temperature - GM.update_values() - - return GM - -/turf/simulated/var/current_graphic = null - -/turf/simulated/var/tmp/datum/gas_mixture/air - -/turf/simulated/var/tmp/air_check_directions = 0 //Do not modify this, just add turf to air_master.tiles_to_update - -/turf/simulated/var/tmp/unsim_check_directions = 0 //See above. - -/turf/simulated/var/tmp/obj/fire/active_hotspot - -/turf/simulated/proc/update_visuals() - overlays = null - - var/siding_icon_state = return_siding_icon_state() - if(siding_icon_state) - overlays += image('icons/turf/floors.dmi',siding_icon_state) - var/datum/gas_mixture/model = return_air() - switch(model.graphic) - if(1) - overlays.Add(plmaster) //TODO: Make invisible plasma an option - if(2) - overlays.Add(slmaster) - -/turf/simulated/New() - if(!blocks_air) - air = new - - air.oxygen = oxygen - air.carbon_dioxide = carbon_dioxide - air.nitrogen = nitrogen - air.toxins = toxins - - air.temperature = temperature - air.update_values() - - if(air_master) - air_master.tiles_to_update.Add(src) - - else - if(air_master) - for(var/direction in cardinal) - var/turf/simulated/floor/target = get_step(src,direction) - if(istype(target)) - air_master.tiles_to_update |= target - - . = ..() - -/turf/simulated/Del() - if(active_hotspot) - del(active_hotspot) - if(blocks_air) - for(var/direction in list(NORTH, SOUTH, EAST, WEST)) - var/turf/simulated/tile = get_step(src,direction) - if(istype(tile) && !tile.blocks_air) - air_master.tiles_to_update.Add(tile) - ..() - -/turf/simulated/assume_air(datum/gas_mixture/giver) - if(!giver) return 0 - if(zone) - zone.assume_air(giver) - return 1 - else - return ..() - -/turf/simulated/return_air() - if(zone) - return zone.air - else if(air) - return air - - else - return ..() - -/turf/simulated/remove_air(amount as num) - if(zone) - return zone.remove_air(amount) - - else if(air) - var/datum/gas_mixture/removed = null - removed = air.remove(amount) - - if(air.check_tile_graphic()) - update_visuals(air) - return removed - - else - return ..() - -/turf/simulated/proc/update_air_properties() - var/air_directions_archived = air_check_directions - air_check_directions = 0 - - var/unsim_directions_archived = unsim_check_directions - unsim_check_directions = 0 - - for(var/direction in cardinal) - var/turf/check_turf = get_step(src, direction) - if(ZAirPass(check_turf)) - if(istype(check_turf, /turf/simulated)) - air_check_directions |= direction - else if(istype(check_turf, /turf/space) || istype(check_turf, /turf/unsimulated)) - unsim_check_directions |= direction - - if(!zone && !blocks_air) //No zone, but not a wall. - for(var/direction in DoorDirections) //Check door directions first. - if(air_check_directions & direction) - var/turf/simulated/T = get_step(src, direction) - if(!istype(T)) - continue - if(T.zone) - T.zone.AddTurf(src) - break - if(!zone) //Still no zone - for(var/direction in CounterDoorDirections) //Check the others second. - if(air_check_directions & direction) - var/turf/simulated/T = get_step(src,direction) - if(!istype(T)) - continue - if(T.zone) - T.zone.AddTurf(src) - break - if(!zone) //No zone found, new zone! - new/zone(src) - if(!zone) //Still no zone, the floodfill determined it is not part of a larger zone. Force a zone on it. - new/zone(list(src)) - - //Check pass sanity of the connections. - if(src in air_master.turfs_with_connections) - air_master.AddConnectionToCheck(air_master.turfs_with_connections[src]) - - if(zone && !air_master.zones_needing_rebuilt.Find(zone)) - for(var/direction in cardinal) - var/turf/T = get_step(src,direction) - if(!istype(T)) - continue - - //I can connect to air in this direction - if(air_check_directions & direction || unsim_check_directions & direction) - - //If either block air, we must look to see if the adjacent turfs need rebuilt. - if(!CanPass(null, T, 0, 0)) - - //Target blocks air - if(!T.CanPass(null, T, 0, 0)) - var/turf/NT = get_step(T, direction) - - //If that turf is in my zone still, rebuild. - if(istype(NT,/turf/simulated) && NT in zone.contents) - air_master.zones_needing_rebuilt.Add(zone) - - //If that is an unsimulated tile in my zone, see if we need to rebuild or just remove. - else if(istype(NT) && NT in zone.unsimulated_tiles) - var/consider_rebuild = 0 - for(var/d in cardinal) - var/turf/UT = get_step(NT,d) - if(istype(UT, /turf/simulated) && UT.zone == zone && UT.CanPass(null, NT, 0, 0)) //If we find a neighboring tile that is in the same zone, check if we need to rebuild - consider_rebuild = 1 - break - if(consider_rebuild) - air_master.zones_needing_rebuilt.Add(zone) //Gotta check if we need to rebuild, dammit - else - zone.RemoveTurf(NT) //Not adjacent to anything, and unsimulated. Goodbye~ - - //To make a closed connection through closed door. - ZConnect(T, src) - - //If I block air. - else if(T.zone && !air_master.zones_needing_rebuilt.Find(T.zone)) - var/turf/NT = get_step(src, reverse_direction(direction)) - - //If I am splitting a zone, rebuild. - if(istype(NT,/turf/simulated) && (NT in T.zone.contents || (NT.zone && T in NT.zone.contents))) - air_master.zones_needing_rebuilt.Add(T.zone) - - //If NT is unsimulated, parse if I should remove it or rebuild. - else if(istype(NT) && NT in T.zone.unsimulated_tiles) - var/consider_rebuild = 0 - for(var/d in cardinal) - var/turf/UT = get_step(NT,d) - if(istype(UT, /turf/simulated) && UT.zone == T.zone && UT.CanPass(null, NT, 0, 0)) //If we find a neighboring tile that is in the same zone, check if we need to rebuild - consider_rebuild = 1 - break - - //Needs rebuilt. - if(consider_rebuild) - air_master.zones_needing_rebuilt.Add(T.zone) - - //Not adjacent to anything, and unsimulated. Goodbye~ - else - T.zone.RemoveTurf(NT) - - else - //Produce connection through open door. - ZConnect(src,T) - - //Something like a wall was built, changing the geometry. - else if(air_directions_archived & direction || unsim_directions_archived & direction) - var/turf/NT = get_step(T, direction) - - //If the tile is in our own zone, and we cannot connect to it, better rebuild. - if(istype(NT,/turf/simulated) && NT in zone.contents) - air_master.zones_needing_rebuilt.Add(zone) - - //Parse if we need to remove the tile, or rebuild the zone. - else if(istype(NT) && NT in zone.unsimulated_tiles) - var/consider_rebuild = 0 - - //Loop through all neighboring turfs to see if we should remove the turf or just rebuild. - for(var/d in cardinal) - var/turf/UT = get_step(NT,d) - - //If we find a neighboring tile that is in the same zone, rebuild - if(istype(UT, /turf/simulated) && UT.zone == zone && UT.CanPass(null, NT, 0, 0)) - consider_rebuild = 1 - break - - //The unsimulated turf is adjacent to another one of our zone's turfs, - // better rebuild to be sure we didn't get cut in twain - if(consider_rebuild) - air_master.zones_needing_rebuilt.Add(NT.zone) - - //Not adjacent to anything, and unsimulated. Goodbye~ - else - zone.RemoveTurf(NT) - - return 1 - -/turf/proc/HasDoor(turf/O) - //Checks for the presence of doors, used for zone spreading and connection. - //A positive numerical argument checks only for closed doors. - //Another turf as an argument checks for windoors between here and there. - for(var/obj/machinery/door/D in src) - if(isnum(O) && O) - if(!D.density) continue - if(istype(D,/obj/machinery/door/window)) - if(!istype(O)) - continue - if(D.dir == get_dir(D,O)) - return 1 - else - return 1 - -/turf/proc/ZCanPass(turf/simulated/T, var/include_space = 0) - //Fairly standard pass checks for turfs, objects and directional windows. Also stops at the edge of space. - if(!istype(T)) - return 0 - - if(!istype(T) && !include_space) - return 0 - else - if(T.blocks_air||blocks_air) - return 0 - - for(var/obj/obstacle in src) - if(istype(obstacle, /obj/machinery/door) && !(obstacle:air_properties_vary_with_direction)) - continue - if(!obstacle.CanPass(null, T, 1.5, 1)) - return 0 - - for(var/obj/obstacle in T) - if(istype(obstacle, /obj/machinery/door) && !(obstacle:air_properties_vary_with_direction)) - continue - if(!obstacle.CanPass(null, src, 1.5, 1)) - return 0 - - return 1 - -/turf/proc/ZAirPass(turf/T) - //Fairly standard pass checks for turfs, objects and directional windows. - if(!istype(T)) - return 0 - - if(T.blocks_air||blocks_air) - return 0 - - for(var/obj/obstacle in src) - if(istype(obstacle, /obj/machinery/door) && !(obstacle:air_properties_vary_with_direction)) - continue - if(!obstacle.CanPass(null, T, 0, 0)) - return 0 - - for(var/obj/obstacle in T) - if(istype(obstacle, /obj/machinery/door) && !(obstacle:air_properties_vary_with_direction)) - continue - if(!obstacle.CanPass(null, src, 0, 0)) - return 0 - - return 1 - -/*UNUSED -/turf/proc/check_connections() - //Checks for new connections that can be made. - for(var/d in cardinal) - var/turf/simulated/T = get_step(src,d) - if(istype(T) && ( !T.zone || !T.CanPass(0,src,0,0) ) ) - continue - if(T.zone != zone) - ZConnect(src,T) - -/turf/proc/check_for_space() - //Checks for space around the turf. - for(var/d in cardinal) - var/turf/T = get_step(src,d) - if(istype(T,/turf/space) && T.CanPass(0,src,0,0)) - zone.AddSpace(T) - */ \ No newline at end of file diff --git a/code/ZAS/ZAS_Zones.dm b/code/ZAS/ZAS_Zones.dm deleted file mode 100644 index 1726d04f5c..0000000000 --- a/code/ZAS/ZAS_Zones.dm +++ /dev/null @@ -1,906 +0,0 @@ -var/list/DoorDirections = list(NORTH,WEST) //Which directions doors turfs can connect to zones -var/list/CounterDoorDirections = list(SOUTH,EAST) //Which directions doors turfs can connect to zones - -/zone - var/dbg_output = 0 //Enables debug output. - - var/datum/gas_mixture/air //The air contents of the zone. - var/datum/gas_mixture/archived_air - - var/list/contents //All the tiles that are contained in this zone. - var/list/unsimulated_tiles // Any space tiles in this list will cause air to flow out. - - var/datum/gas_mixture/air_unsim //Overall average of the air in connected unsimualted tiles. - var/unsim_air_needs_update = 0 //Set to 1 on geometry changes, marks air_unsim as needing update. - - var/list/connections //connection objects which refer to connections with other zones, e.g. through a door. - var/list/direct_connections //connections which directly connect two zones. - - var/list/connected_zones //Parallels connections, but lists zones to which this one is connected and the number - //of points they're connected at. - var/list/closed_connection_zones //Same as connected_zones, but for zones where the door or whatever is closed. - - var/last_update = 0 - var/last_rebuilt = 0 - var/status = ZONE_ACTIVE - var/interactions_with_neighbors = 0 - var/interactions_with_unsim = 0 - var/progress = "nothing" - -//CREATION AND DELETION -/zone/New(turf/start) - . = ..() - //Get the turfs that are part of the zone using a floodfill method - if(istype(start,/list)) - contents = start - else - contents = FloodFill(start) - - //Change all the zone vars of the turfs, check for space to be added to unsimulated_tiles. - for(var/turf/T in contents) - if(T.zone && T.zone != src) - T.zone.RemoveTurf(T) - T.zone = src - if(!istype(T,/turf/simulated)) - AddTurf(T) - - //Generate the gas_mixture for use in txhis zone by using the average of the gases - //defined at startup. - //Changed to try and find the source of the error. - air = new - air.group_multiplier = contents.len - for(var/turf/simulated/T in contents) - if(!T.air) - continue - air.oxygen += T.air.oxygen / air.group_multiplier - air.nitrogen += T.air.nitrogen / air.group_multiplier - air.carbon_dioxide += T.air.carbon_dioxide / air.group_multiplier - air.toxins += T.air.toxins / air.group_multiplier - air.temperature += T.air.temperature / air.group_multiplier - for(var/datum/gas/trace in T.air.trace_gases) - var/datum/gas/corresponding_gas = locate(trace.type) in air.trace_gases - if(!corresponding_gas) - corresponding_gas = new trace.type() - air.trace_gases.Add(corresponding_gas) - corresponding_gas.moles += trace.moles - air.update_values() - - //Add this zone to the global list. - if(air_master) - air_master.zones.Add(src) - air_master.active_zones.Add(src) - - -//DO NOT USE. Use the SoftDelete proc. -/zone/Del() - //Ensuring the zone list doesn't get clogged with null values. - for(var/turf/simulated/T in contents) - RemoveTurf(T) - air_master.ReconsiderTileZone(T) - - if(air_master) - air_master.AddConnectionToCheck(connections) - - air = null - - . = ..() - - -//Handles deletion via garbage collection. -/zone/proc/SoftDelete() - air = null - - if(air_master) - air_master.zones.Remove(src) - air_master.active_zones.Remove(src) - air_master.zones_needing_rebuilt.Remove(src) - air_master.AddConnectionToCheck(connections) - - connections = null - for(var/connection/C in direct_connections) - if(C.A.zone == src) - C.A.zone = null - if(C.B.zone == src) - C.B.zone = null - if(C.zone_A == src) - C.zone_A = null - if(C.zone_B == src) - C.zone_B = null - direct_connections = null - - //Ensuring the zone list doesn't get clogged with null values. - for(var/turf/simulated/T in contents) - RemoveTurf(T) - air_master.ReconsiderTileZone(T) - - contents.Cut() - - //Removing zone connections and scheduling connection cleanup - for(var/zone/Z in connected_zones) - Z.connected_zones.Remove(src) - if(!Z.connected_zones.len) - Z.connected_zones = null - - if(Z.closed_connection_zones) - Z.closed_connection_zones.Remove(src) - if(!Z.closed_connection_zones.len) - Z.closed_connection_zones = null - - connected_zones = null - closed_connection_zones = null - - return 1 - - -//ZONE MANAGEMENT FUNCTIONS -/zone/proc/AddTurf(turf/T) - //Adds the turf to contents, increases the size of the zone, and sets the zone var. - if(istype(T, /turf/simulated)) - if(T in contents) - return - if(T.zone) - T.zone.RemoveTurf(T) - contents += T - if(air) - air.group_multiplier++ - - T.zone = src - -///// Z-Level Stuff - // also add the tile below it if its open space - if(istype(T, /turf/simulated/floor/open)) - var/turf/simulated/floor/open/T2 = T - src.AddTurf(T2.floorbelow) -///// Z-Level Stuff - - else - if(!unsimulated_tiles) - unsimulated_tiles = list() - else if(T in unsimulated_tiles) - return - unsimulated_tiles += T - contents -= T - - unsim_air_needs_update = 1 - -/zone/proc/RemoveTurf(turf/T) - //Same, but in reverse. - if(istype(T, /turf/simulated)) - if(!(T in contents)) - return - contents -= T - if(air) - air.group_multiplier-- - - if(T.zone == src) - T.zone = null - - if(!contents.len) - SoftDelete() - - else if(unsimulated_tiles) - unsimulated_tiles -= T - if(!unsimulated_tiles.len) - unsimulated_tiles = null - - unsim_air_needs_update = 1 - -//Updates the air_unsim var -/zone/proc/UpdateUnsimAvg() - if(!unsimulated_tiles || !unsimulated_tiles.len) //if we don't have any unsimulated tiles, we can't do much. - return - - if(!unsim_air_needs_update && air_unsim) //if air_unsim doesn't exist, we need to create it even if we don't need an update. - return - - //Tempfix. - if(!air) - return - - unsim_air_needs_update = 0 - - if(!air_unsim) - air_unsim = new /datum/gas_mixture - - air_unsim.oxygen = 0 - air_unsim.nitrogen = 0 - air_unsim.carbon_dioxide = 0 - air_unsim.toxins = 0 - air_unsim.temperature = 0 - - var/correction_ratio = max(1, max(max(1, air.group_multiplier) + 3, 1) + unsimulated_tiles.len) / unsimulated_tiles.len - - for(var/turf/T in unsimulated_tiles) - if(!istype(T, /turf/simulated)) - air_unsim.oxygen += T.oxygen - air_unsim.carbon_dioxide += T.carbon_dioxide - air_unsim.nitrogen += T.nitrogen - air_unsim.toxins += T.toxins - air_unsim.temperature += T.temperature/unsimulated_tiles.len - - //These values require adjustment in order to properly represent a room of the specified size. - air_unsim.oxygen *= correction_ratio - air_unsim.carbon_dioxide *= correction_ratio - air_unsim.nitrogen *= correction_ratio - air_unsim.toxins *= correction_ratio - - air_unsim.group_multiplier = unsimulated_tiles.len - - air_unsim.update_values() - return - - ////////////// - //PROCESSING// -////////////// - -#define QUANTIZE(variable) (round(variable,0.0001)) - -/zone/proc/process() - . = 1 - - progress = "problem with: SoftDelete()" - - //Deletes zone if empty. - if(!contents.len) - return SoftDelete() - - progress = "problem with: Rebuild()" - - if(!contents.len) //If we got soft deleted. - return - - progress = "problem with: air regeneration" - - //Sometimes explosions will cause the air to be deleted for some reason. - if(!air) - air = new() - air.oxygen = MOLES_O2STANDARD - air.nitrogen = MOLES_N2STANDARD - air.temperature = T0C - air.total_moles() - world.log << "Air object lost in zone. Regenerating." - - - progress = "problem with: ShareSpace()" - - if(unsim_air_needs_update) - unsim_air_needs_update = 0 - UpdateUnsimAvg() - - if(unsimulated_tiles) - if(locate(/turf/simulated) in unsimulated_tiles) - for(var/turf/simulated/T in unsimulated_tiles) - unsimulated_tiles -= T - - if(unsimulated_tiles.len) - var/moved_air = ShareSpace(air, air_unsim) - - if(!air.compare(air_unsim)) - interactions_with_unsim++ - - if(moved_air > vsc.airflow_lightest_pressure) - AirflowSpace(src) - else - unsimulated_tiles = null - - //Check the graphic. - progress = "problem with: modifying turf graphics" - - air.graphic = 0 - if(air.toxins > MOLES_PLASMA_VISIBLE) - air.graphic = 1 - else if(air.trace_gases.len) - var/datum/gas/sleeping_agent = locate(/datum/gas/sleeping_agent) in air.trace_gases - if(sleeping_agent && (sleeping_agent.moles > 1)) - air.graphic = 2 - - progress = "problem with an inbuilt byond function: some conditional checks" - - //Only run through the individual turfs if there's reason to. - if(air.graphic != air.graphic_archived || air.temperature > PLASMA_FLASHPOINT) - - progress = "problem with: turf/simulated/update_visuals()" - - for(var/turf/simulated/S in contents) - //Update overlays. - if(air.graphic != air.graphic_archived) - if(S.HasDoor(1)) - S.update_visuals() - else - S.update_visuals(air) - - progress = "problem with: item or turf temperature_expose()" - - //Expose stuff to extreme heat. - if(air.temperature > PLASMA_FLASHPOINT) - for(var/atom/movable/item in S) - item.temperature_expose(air, air.temperature, CELL_VOLUME) - S.hotspot_expose(air.temperature, CELL_VOLUME) - - progress = "problem with: calculating air graphic" - - //Archive graphic so we can know if it's different. - air.graphic_archived = air.graphic - - progress = "problem with: calculating air temp" - - //Ensure temperature does not reach absolute zero. - air.temperature = max(TCMB,air.temperature) - - progress = "problem with an inbuilt byond function: length(connections)" - - //Handle connections to other zones. - if(length(connections)) - - progress = "problem with: ZMerge(), a couple of misc procs" - - if(length(direct_connections)) - for(var/connection/C in direct_connections) - - //Do merging if conditions are met. Specifically, if there's a non-door connection - //to somewhere with space, the zones are merged regardless of equilibrium, to speed - //up spacing in areas with double-plated windows. - if(C.A.zone && C.B.zone) - if(C.A.zone.air.compare(C.B.zone.air) || unsimulated_tiles) - ZMerge(C.A.zone,C.B.zone) - - progress = "problem with: ShareRatio(), Airflow(), a couple of misc procs" - - //Share some - for(var/zone/Z in connected_zones) - //If that zone has already processed, skip it. - if(Z.last_update > last_update) - continue - - //Handle adjacent zones that are sleeping - if(Z.status == ZONE_SLEEPING) - if(air.compare(Z.air)) - continue - - else - Z.SetStatus(ZONE_ACTIVE) - - if(air && Z.air) - //Ensure we're not doing pointless calculations on equilibrium zones. - if(!air.compare(Z.air)) - if(abs(Z.air.return_pressure() - air.return_pressure()) > vsc.airflow_lightest_pressure) - Airflow(src,Z) - var/unsimulated_boost = 0 - if(unsimulated_tiles) - unsimulated_boost += unsimulated_tiles.len - if(Z.unsimulated_tiles) - unsimulated_boost += Z.unsimulated_tiles.len - unsimulated_boost = max(0, min(3, unsimulated_boost)) - ShareRatio( air , Z.air , connected_zones[Z] + unsimulated_boost) - - Z.interactions_with_neighbors++ - interactions_with_neighbors++ - - if(!vsc.connection_insulation) - for(var/zone/Z in closed_connection_zones) - //If that zone has already processed, skip it. - if(Z.last_update > last_update || !Z.air) - continue - - var/handle_temperature = abs(air.temperature - Z.air.temperature) > vsc.connection_temperature_delta - - if(Z.status == ZONE_SLEEPING) - if (handle_temperature) - Z.SetStatus(ZONE_ACTIVE) - else - continue - - if(air && Z.air) - if( handle_temperature ) - ShareHeat(air, Z.air, closed_connection_zones[Z]) - - Z.interactions_with_neighbors++ - interactions_with_neighbors++ - - if(!interactions_with_neighbors && !interactions_with_unsim) - SetStatus(ZONE_SLEEPING) - - interactions_with_neighbors = 0 - interactions_with_unsim = 0 - - progress = "all components completed successfully, the problem is not here" - - -/zone/proc/SetStatus(var/new_status) - if(status == ZONE_SLEEPING && new_status == ZONE_ACTIVE) - air_master.active_zones.Add(src) - status = ZONE_ACTIVE - - else if(status == ZONE_ACTIVE && new_status == ZONE_SLEEPING) - air_master.active_zones.Remove(src) - status = ZONE_SLEEPING - - if(unsimulated_tiles && unsimulated_tiles.len) - UpdateUnsimAvg() - air.copy_from(air_unsim) - - if(!archived_air) - archived_air = new - archived_air.copy_from(air) - - -/zone/proc/CheckStatus() - return status - - -/zone/proc/ActivateIfNeeded() - if(status == ZONE_ACTIVE) return - - var/difference = 0 - - if(unsimulated_tiles && unsimulated_tiles.len) - UpdateUnsimAvg() - if(!air.compare(air_unsim)) - difference = 1 - - if(!difference) - for(var/zone/Z in connected_zones) //Check adjacent zones for air difference. - if(!air.compare(Z.air)) - difference = 1 - break - - if(difference) //We have a difference, activate the zone. - SetStatus(ZONE_ACTIVE) - - return - - -/zone/proc/assume_air(var/datum/gas_mixture/giver) - if(status == ZONE_ACTIVE) - return air.merge(giver) - - else - if(unsimulated_tiles && unsimulated_tiles.len) - UpdateUnsimAvg() - var/datum/gas_mixture/compare_air = new - compare_air.copy_from(giver) - compare_air.add(air_unsim) - compare_air.divide(air.group_multiplier) - - if(air_unsim.compare(compare_air)) - return 0 - - var/result = air.merge(giver) - - if(!archived_air.compare(air)) - SetStatus(ZONE_ACTIVE) - return result - - -/zone/proc/remove_air(var/amount) - if(status == ZONE_ACTIVE) - return air.remove(amount) - - else - var/result = air.remove(amount) - - if(!archived_air.compare(air)) - SetStatus(ZONE_ACTIVE) - - return result - - //////////////// - //Air Movement// -//////////////// - -var/list/sharing_lookup_table = list(0.30, 0.40, 0.48, 0.54, 0.60, 0.66) - -proc/ShareRatio(datum/gas_mixture/A, datum/gas_mixture/B, connecting_tiles) - //Shares a specific ratio of gas between mixtures using simple weighted averages. - var - //WOOT WOOT TOUCH THIS AND YOU ARE A RETARD - ratio = sharing_lookup_table[6] - //WOOT WOOT TOUCH THIS AND YOU ARE A RETARD - - size = max(1,A.group_multiplier) - share_size = max(1,B.group_multiplier) - - full_oxy = A.oxygen * size - full_nitro = A.nitrogen * size - full_co2 = A.carbon_dioxide * size - full_plasma = A.toxins * size - - full_heat_capacity = A.heat_capacity() * size - - s_full_oxy = B.oxygen * share_size - s_full_nitro = B.nitrogen * share_size - s_full_co2 = B.carbon_dioxide * share_size - s_full_plasma = B.toxins * share_size - - s_full_heat_capacity = B.heat_capacity() * share_size - - oxy_avg = (full_oxy + s_full_oxy) / (size + share_size) - nit_avg = (full_nitro + s_full_nitro) / (size + share_size) - co2_avg = (full_co2 + s_full_co2) / (size + share_size) - plasma_avg = (full_plasma + s_full_plasma) / (size + share_size) - - temp_avg = (A.temperature * full_heat_capacity + B.temperature * s_full_heat_capacity) / (full_heat_capacity + s_full_heat_capacity) - - //WOOT WOOT TOUCH THIS AND YOU ARE A RETARD - if(sharing_lookup_table.len >= connecting_tiles) //6 or more interconnecting tiles will max at 42% of air moved per tick. - ratio = sharing_lookup_table[connecting_tiles] - //WOOT WOOT TOUCH THIS AND YOU ARE A RETARD - - A.oxygen = max(0, (A.oxygen - oxy_avg) * (1-ratio) + oxy_avg ) - A.nitrogen = max(0, (A.nitrogen - nit_avg) * (1-ratio) + nit_avg ) - A.carbon_dioxide = max(0, (A.carbon_dioxide - co2_avg) * (1-ratio) + co2_avg ) - A.toxins = max(0, (A.toxins - plasma_avg) * (1-ratio) + plasma_avg ) - - A.temperature = max(0, (A.temperature - temp_avg) * (1-ratio) + temp_avg ) - - B.oxygen = max(0, (B.oxygen - oxy_avg) * (1-ratio) + oxy_avg ) - B.nitrogen = max(0, (B.nitrogen - nit_avg) * (1-ratio) + nit_avg ) - B.carbon_dioxide = max(0, (B.carbon_dioxide - co2_avg) * (1-ratio) + co2_avg ) - B.toxins = max(0, (B.toxins - plasma_avg) * (1-ratio) + plasma_avg ) - - B.temperature = max(0, (B.temperature - temp_avg) * (1-ratio) + temp_avg ) - - for(var/datum/gas/G in A.trace_gases) - var/datum/gas/H = locate(G.type) in B.trace_gases - if(H) - var/G_avg = (G.moles*size + H.moles*share_size) / (size+share_size) - G.moles = (G.moles - G_avg) * (1-ratio) + G_avg - - H.moles = (H.moles - G_avg) * (1-ratio) + G_avg - else - H = new G.type - B.trace_gases += H - var/G_avg = (G.moles*size) / (size+share_size) - G.moles = (G.moles - G_avg) * (1-ratio) + G_avg - H.moles = (H.moles - G_avg) * (1-ratio) + G_avg - - for(var/datum/gas/G in B.trace_gases) - var/datum/gas/H = locate(G.type) in A.trace_gases - if(!H) - H = new G.type - A.trace_gases += H - var/G_avg = (G.moles*size) / (size+share_size) - G.moles = (G.moles - G_avg) * (1-ratio) + G_avg - H.moles = (H.moles - G_avg) * (1-ratio) + G_avg - - A.update_values() - B.update_values() - - if(A.compare(B)) return 1 - else return 0 - -proc/ShareSpace(datum/gas_mixture/A, list/unsimulated_tiles, dbg_output) - //A modified version of ShareRatio for spacing gas at the same rate as if it were going into a large airless room. - if(!unsimulated_tiles) - return 0 - - var - unsim_oxygen = 0 - unsim_nitrogen = 0 - unsim_co2 = 0 - unsim_plasma = 0 - unsim_heat_capacity = 0 - unsim_temperature = 0 - - size = max(1,A.group_multiplier) - - var/tileslen - var/share_size - - if(istype(unsimulated_tiles, /datum/gas_mixture)) - var/datum/gas_mixture/avg_unsim = unsimulated_tiles - unsim_oxygen = avg_unsim.oxygen - unsim_co2 = avg_unsim.carbon_dioxide - unsim_nitrogen = avg_unsim.nitrogen - unsim_plasma = avg_unsim.toxins - unsim_temperature = avg_unsim.temperature - share_size = max(1, max(size + 3, 1) + avg_unsim.group_multiplier) - tileslen = avg_unsim.group_multiplier - - else if(istype(unsimulated_tiles, /list)) - if(!unsimulated_tiles.len) - return 0 - // We use the same size for the potentially single space tile - // as we use for the entire room. Why is this? - // Short answer: We do not want larger rooms to depressurize more - // slowly than small rooms, preserving our good old "hollywood-style" - // oh-shit effect when large rooms get breached, but still having small - // rooms remain pressurized for long enough to make escape possible. - share_size = max(1, max(size + 3, 1) + unsimulated_tiles.len) - var/correction_ratio = share_size / unsimulated_tiles.len - - for(var/turf/T in unsimulated_tiles) - unsim_oxygen += T.oxygen - unsim_co2 += T.carbon_dioxide - unsim_nitrogen += T.nitrogen - unsim_plasma += T.toxins - unsim_temperature += T.temperature/unsimulated_tiles.len - - //These values require adjustment in order to properly represent a room of the specified size. - unsim_oxygen *= correction_ratio - unsim_co2 *= correction_ratio - unsim_nitrogen *= correction_ratio - unsim_plasma *= correction_ratio - tileslen = unsimulated_tiles.len - - else //invalid input type - return 0 - - unsim_heat_capacity = HEAT_CAPACITY_CALCULATION(unsim_oxygen, unsim_co2, unsim_nitrogen, unsim_plasma) - - var - ratio = sharing_lookup_table[6] - - old_pressure = A.return_pressure() - - full_oxy = A.oxygen * size - full_nitro = A.nitrogen * size - full_co2 = A.carbon_dioxide * size - full_plasma = A.toxins * size - - full_heat_capacity = A.heat_capacity() * size - - oxy_avg = (full_oxy + unsim_oxygen) / (size + share_size) - nit_avg = (full_nitro + unsim_nitrogen) / (size + share_size) - co2_avg = (full_co2 + unsim_co2) / (size + share_size) - plasma_avg = (full_plasma + unsim_plasma) / (size + share_size) - - temp_avg = 0 - - if((full_heat_capacity + unsim_heat_capacity) > 0) - temp_avg = (A.temperature * full_heat_capacity + unsim_temperature * unsim_heat_capacity) / (full_heat_capacity + unsim_heat_capacity) - - if(sharing_lookup_table.len >= tileslen) //6 or more interconnecting tiles will max at 42% of air moved per tick. - ratio = sharing_lookup_table[tileslen] - - A.oxygen = max(0, (A.oxygen - oxy_avg) * (1 - ratio) + oxy_avg ) - A.nitrogen = max(0, (A.nitrogen - nit_avg) * (1 - ratio) + nit_avg ) - A.carbon_dioxide = max(0, (A.carbon_dioxide - co2_avg) * (1 - ratio) + co2_avg ) - A.toxins = max(0, (A.toxins - plasma_avg) * (1 - ratio) + plasma_avg ) - - A.temperature = max(TCMB, (A.temperature - temp_avg) * (1 - ratio) + temp_avg ) - - for(var/datum/gas/G in A.trace_gases) - var/G_avg = (G.moles * size) / (size + share_size) - G.moles = (G.moles - G_avg) * (1 - ratio) + G_avg - - A.update_values() - - return abs(old_pressure - A.return_pressure()) - - -proc/ShareHeat(datum/gas_mixture/A, datum/gas_mixture/B, connecting_tiles) - //This implements a simplistic version of the Stefan-Boltzmann law. - var/energy_delta = ((A.temperature - B.temperature) ** 4) * 5.6704e-8 * connecting_tiles * 2.5 - var/maximum_energy_delta = max(0, min(A.temperature * A.heat_capacity() * A.group_multiplier, B.temperature * B.heat_capacity() * B.group_multiplier)) - if(maximum_energy_delta > abs(energy_delta)) - if(energy_delta < 0) - maximum_energy_delta *= -1 - energy_delta = maximum_energy_delta - - A.temperature -= energy_delta / (A.heat_capacity() * A.group_multiplier) - B.temperature += energy_delta / (B.heat_capacity() * B.group_multiplier) - - /* This was bad an I feel bad. - //Shares a specific ratio of gas between mixtures using simple weighted averages. - var - //WOOT WOOT TOUCH THIS AND YOU ARE A RETARD - ratio = sharing_lookup_table[6] - //WOOT WOOT TOUCH THIS AND YOU ARE A RETARD - - full_heat_capacity = A.heat_capacity() - - s_full_heat_capacity = B.heat_capacity() - - temp_avg = (A.temperature * full_heat_capacity + B.temperature * s_full_heat_capacity) / (full_heat_capacity + s_full_heat_capacity) - - //WOOT WOOT TOUCH THIS AND YOU ARE A RETARD - if(sharing_lookup_table.len >= connecting_tiles) //6 or more interconnecting tiles will max at 42% of air moved per tick. - ratio = sharing_lookup_table[connecting_tiles] - //WOOT WOOT TOUCH THIS AND YOU ARE A RETARD - - //We need to adjust it to account for the insulation settings. - ratio *= 1 - vsc.connection_insulation - - A.temperature = max(0, (A.temperature - temp_avg) * (1- (ratio / max(1,A.group_multiplier)) ) + temp_avg ) - B.temperature = max(0, (B.temperature - temp_avg) * (1- (ratio / max(1,B.group_multiplier)) ) + temp_avg ) - */ - - /////////////////// - //Zone Rebuilding// -/////////////////// -//Used for updating zone geometry when a zone is cut into two parts. - -zone/proc/Rebuild() - if(last_rebuilt == air_master.current_cycle) - return - - last_rebuilt = air_master.current_cycle - - var/list/new_zone_contents = IsolateContents() - if(new_zone_contents.len == 1) - return - - var/list/current_contents - var/list/new_zones = list() - - contents = new_zone_contents[1] - air.group_multiplier = contents.len - - for(var/identifier in 2 to new_zone_contents.len) - current_contents = new_zone_contents[identifier] - var/zone/new_zone = new (current_contents) - new_zone.air.copy_from(air) - new_zones += new_zone - - for(var/connection/connection in connections) - connection.Cleanup() - - var/turf/simulated/adjacent - - for(var/turf/unsimulated in unsimulated_tiles) - for(var/direction in cardinal) - adjacent = get_step(unsimulated, direction) - - if(istype(adjacent) && adjacent.CanPass(null, unsimulated, 0, 0)) - for(var/zone/zone in new_zones) - if(adjacent in zone) - zone.AddTurf(unsimulated) - - -//Implements a two-pass connected component labeling algorithm to determine if the zone is, in fact, split. - -/zone/proc/IsolateContents() - var/list/current_adjacents = list() - var/adjacent_id - var/lowest_id - - var/list/identical_ids = list() - var/list/turfs = contents.Copy() - var/current_identifier = 1 - - for(var/turf/simulated/current in turfs) - lowest_id = null - current_adjacents = list() - - for(var/direction in cardinal) - var/turf/simulated/adjacent = get_step(current, direction) - if(!current.ZCanPass(adjacent)) - continue - if(adjacent in turfs) - current_adjacents += adjacent - adjacent_id = turfs[adjacent] - - if(adjacent_id && (!lowest_id || adjacent_id < lowest_id)) - lowest_id = adjacent_id - -/////// Z-Level stuff - var/turf/controllerlocation = locate(1, 1, current.z) - for(var/obj/effect/landmark/zcontroller/controller in controllerlocation) - // upwards - if(controller.up) - var/turf/simulated/adjacent = locate(current.x, current.y, controller.up_target) - - if(adjacent in turfs && istype(adjacent, /turf/simulated/floor/open)) - current_adjacents += adjacent - adjacent_id = turfs[adjacent] - - if(adjacent_id && (!lowest_id || adjacent_id < lowest_id)) - lowest_id = adjacent_id - - // downwards - if(controller.down && istype(current, /turf/simulated/floor/open)) - var/turf/simulated/adjacent = locate(current.x, current.y, controller.down_target) - - if(adjacent in turfs) - current_adjacents += adjacent - adjacent_id = turfs[adjacent] - - if(adjacent_id && (!lowest_id || adjacent_id < lowest_id)) - lowest_id = adjacent_id -/////// Z-Level stuff - - if(!lowest_id) - lowest_id = current_identifier++ - identical_ids += lowest_id - - for(var/turf/simulated/adjacent in current_adjacents) - adjacent_id = turfs[adjacent] - if(adjacent_id != lowest_id) - if(adjacent_id) - identical_ids[adjacent_id] = lowest_id - turfs[adjacent] = lowest_id - turfs[current] = lowest_id - - var/list/final_arrangement = list() - - for(var/turf/simulated/current in turfs) - current_identifier = identical_ids[turfs[current]] - - if( current_identifier > final_arrangement.len ) - final_arrangement.len = current_identifier - final_arrangement[current_identifier] = list(current) - - else - //Sanity check. - if(!islist(final_arrangement[current_identifier])) - final_arrangement[current_identifier] = list() - final_arrangement[current_identifier] += current - - //lazy but fast - final_arrangement.Remove(null) - - return final_arrangement - - -/* - if(!RequiresRebuild()) - return - - //Choose a random turf and regenerate the zone from it. - var/list/new_contents - var/list/new_unsimulated - - var/list/turfs_needing_zones = list() - - var/list/zones_to_check_connections = list(src) - - if(!locate(/turf/simulated/floor) in contents) - for(var/turf/simulated/turf in contents) - air_master.ReconsiderTileZone(turf) - return SoftDelete() - - var/turfs_to_ignore = list() - if(direct_connections) - for(var/connection/connection in direct_connections) - if(connection.A.zone != src) - turfs_to_ignore += A - else if(connection.B.zone != src) - turfs_to_ignore += B - - new_unsimulated = ( unsimulated_tiles ? unsimulated_tiles : list() ) - - //Now, we have allocated the new turfs into proper lists, and we can start actually rebuilding. - - //If something isn't carried over, it will need a new zone. - for(var/turf/T in contents) - if(!(T in new_contents)) - RemoveTurf(T) - turfs_needing_zones += T - - //Handle addition of new turfs - for(var/turf/S in new_contents) - if(!istype(S, /turf/simulated)) - new_unsimulated |= S - new_contents.Remove(S) - - //If something new is added, we need to deal with it seperately. - else if(!(S in contents) && istype(S, /turf/simulated)) - if(!(S.zone in zones_to_check_connections)) - zones_to_check_connections += S.zone - - S.zone.RemoveTurf(S) - AddTurf(S) - - //Handle the addition of new unsimulated tiles. - unsimulated_tiles = null - - if(new_unsimulated.len) - for(var/turf/S in new_unsimulated) - if(istype(S, /turf/simulated)) - continue - for(var/direction in cardinal) - var/turf/simulated/T = get_step(S,direction) - if(istype(T) && T.zone && S.CanPass(null, T, 0, 0)) - T.zone.AddTurf(S) - - //Finally, handle the orphaned turfs - - for(var/turf/simulated/T in turfs_needing_zones) - if(!T.zone) - zones_to_check_connections += new /zone(T) - - for(var/zone/zone in zones_to_check_connections) - for(var/connection/C in zone.connections) - C.Cleanup()*/ - diff --git a/code/ZAS/Zone.dm b/code/ZAS/Zone.dm new file mode 100644 index 0000000000..d1cf8ec0af --- /dev/null +++ b/code/ZAS/Zone.dm @@ -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]°K ([air.temperature - T0C]°C)" + M << "O2 per N2: [(air.nitrogen ? air.oxygen/air.nitrogen : "N/A")] Moles: [air.total_moles]" + M << "Simulated: [contents.len] ([air.group_multiplier])" + //M << "Unsimulated: [unsimulated_contents.len]" + //M << "Edges: [edges.len]" + if(invalid) M << "Invalid!" + var/zone_edges = 0 + var/space_edges = 0 + var/space_coefficient = 0 + for(var/connection_edge/E in edges) + if(E.type == /connection_edge/zone) zone_edges++ + else + space_edges++ + space_coefficient += E.coefficient + + M << "Zone Edges: [zone_edges]" + M << "Space Edges: [space_edges] ([space_coefficient] connections)" + + //for(var/turf/T in unsimulated_contents) + // M << "[T] at ([T.x],[T.y])" \ No newline at end of file diff --git a/code/ZAS/_docs.dm b/code/ZAS/_docs.dm new file mode 100644 index 0000000000..706b6a40d7 --- /dev/null +++ b/code/ZAS/_docs.dm @@ -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 \ No newline at end of file diff --git a/code/ZAS/FEA_gas_mixture.dm b/code/ZAS/_gas_mixture.dm similarity index 100% rename from code/ZAS/FEA_gas_mixture.dm rename to code/ZAS/_gas_mixture.dm diff --git a/code/__HELPERS/game.dm b/code/__HELPERS/game.dm index e2c04299f1..577c809339 100644 --- a/code/__HELPERS/game.dm +++ b/code/__HELPERS/game.dm @@ -161,6 +161,8 @@ // 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. +// SCREW THAT SHIT, we're not recursing. + /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. @@ -172,18 +174,33 @@ var/list/range = hear(R, T) - for(var/atom/A in range) - if(ismob(A)) - var/mob/M = A - if(M.client) - 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 + for(var/mob/M in range) + hear += M + + var/list/objects = list() - if(isobj(A) || ismob(A)) - hear |= recursive_mob_check(A, hear, 3, 1, 0, 1) + 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 + 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 diff --git a/code/datums/sun.dm b/code/datums/sun.dm index 82acfa0cff..868fe1eed8 100644 --- a/code/datums/sun.dm +++ b/code/datums/sun.dm @@ -6,6 +6,7 @@ var/rate 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/lastAngleUpdate /datum/sun/New() @@ -28,15 +29,25 @@ counter = 0 */ angle = ((rate*world.time/100)%360 + 360)%360 + /* 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) */ - // To prevent excess server load the server only updates the sun's sight lines every 6 minutes - if(nexttime < world.time) + if(lastAngleUpdate != angle) + 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 - 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 @@ -58,22 +69,13 @@ 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) - solars_list.Remove(M) + if(!S.powernet) + solars_list.Remove(S) continue - - // Solar Tracker - 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) + if(S.control) + occlusion(S) diff --git a/code/game/area/Space Station 13 areas.dm b/code/game/area/Space Station 13 areas.dm index 53ba67ebf3..3500cdc0db 100755 --- a/code/game/area/Space Station 13 areas.dm +++ b/code/game/area/Space Station 13 areas.dm @@ -792,6 +792,14 @@ var/list/ghostteleportlocs = list() name = "\improper Security Dormitories" 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 name = "\improper Male Dorm" icon_state = "Sleep" diff --git a/code/game/dna/dna2.dm b/code/game/dna/dna2.dm index 744ef24eb4..28e297b710 100644 --- a/code/game/dna/dna2.dm +++ b/code/game/dna/dna2.dm @@ -51,6 +51,9 @@ var/global/list/datum/dna/gene/dna_genes[0] ///////////////// // 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. // 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.UpdateSE() return new_dna - /////////////////////////////////////// // UNIQUE IDENTITY /////////////////////////////////////// @@ -150,7 +152,7 @@ var/global/list/datum/dna/gene/dna_genes[0] // Set a DNA UI block's raw value. /datum/dna/proc/SetUIValue(var/block,var/value,var/defer=0) if (block<=0) return - ASSERT(value>=0) + ASSERT(value>0) ASSERT(value<=4095) UI[block]=value 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) /datum/dna/proc/SetUIValueRange(var/block,var/value,var/maxvalue,var/defer=0) 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) var/range = (4095 / maxvalue) if(value) diff --git a/code/game/dna/dna2_helpers.dm b/code/game/dna/dna2_helpers.dm index be6ced7907..4cdefb36ec 100644 --- a/code/game/dna/dna2_helpers.dm +++ b/code/game/dna/dna2_helpers.dm @@ -143,8 +143,7 @@ H.g_eyes = dna.GetUIValueRange(DNA_UI_EYES_G, 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 - max(min( round(new_s_tone), 220),1) //Warning MATH. Blame the person that wrote modules/client/preferences.dm, line 994 + H.s_tone = 35 - dna.GetUIValueRange(DNA_UI_SKIN_TONE, 220) // Value can be negative. if (dna.GetUIState(DNA_UI_GENDER)) H.gender = FEMALE @@ -171,5 +170,3 @@ // Used below, simple injection modifier. /proc/probinj(var/pr, var/inj) return prob(pr+inj*pr) - -/////////////////////////// DNA MISC-PROCS diff --git a/code/game/dna/genes/disabilities.dm b/code/game/dna/genes/disabilities.dm index d844a61f89..a119a0cf4b 100644 --- a/code/game/dna/genes/disabilities.dm +++ b/code/game/dna/genes/disabilities.dm @@ -32,7 +32,7 @@ M.mutations.Add(mutation) if(disability) M.disabilities|=disability - if(sdisability) + if(mutation) M.sdisabilities|=sdisability if(activation_message) M << "\red [activation_message]" @@ -43,9 +43,9 @@ if(mutation && (mutation in M.mutations)) M.mutations.Remove(mutation) if(disability) - M.disabilities &= ~disability - if(sdisability) - M.sdisabilities &= ~sdisability + M.disabilities-=disability + if(mutation) + M.sdisabilities-=sdisability if(deactivation_message) M << "\red [deactivation_message]" else @@ -126,4 +126,4 @@ disability=NEARSIGHTED New() - block=GLASSESBLOCK \ No newline at end of file + block=GLASSESBLOCK diff --git a/code/game/dna/genes/powers.dm b/code/game/dna/genes/powers.dm index 6684af4584..3381894f8a 100644 --- a/code/game/dna/genes/powers.dm +++ b/code/game/dna/genes/powers.dm @@ -191,6 +191,5 @@ New() block=TELEBLOCK - OnDrawUnderlays(var/mob/M,var/g,var/fat) - return "telekinesishead[fat]_s" \ No newline at end of file + return "telekinesishead[fat]_s" diff --git a/code/game/gamemodes/autotraitor/autotraitor.dm b/code/game/gamemodes/autotraitor/autotraitor.dm index 5df7f0581e..06b5274a92 100644 --- a/code/game/gamemodes/autotraitor/autotraitor.dm +++ b/code/game/gamemodes/autotraitor/autotraitor.dm @@ -137,9 +137,12 @@ newtraitor.mind.special_role = "traitor" var/obj_count = 1 newtraitor << "\blue Your current objectives:" - for(var/datum/objective/objective in newtraitor.mind.objectives) - newtraitor << "Objective #[obj_count]: [objective.explanation_text]" - obj_count++ + if(!config.objectives_disabled) + for(var/datum/objective/objective in newtraitor.mind.objectives) + newtraitor << "Objective #[obj_count]: [objective.explanation_text]" + obj_count++ + else + newtraitor << "You have been selected this round as an antagonist- Within the rules, 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 fun! 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! Please remember all rules aside from those without explicit exceptions apply to antagonist." //else //message_admins("No new traitor being added.") //else diff --git a/code/game/gamemodes/traitor/traitor.dm b/code/game/gamemodes/traitor/traitor.dm index f4015d7ad5..6da8a8ab8d 100644 --- a/code/game/gamemodes/traitor/traitor.dm +++ b/code/game/gamemodes/traitor/traitor.dm @@ -254,7 +254,7 @@ if (!R) 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 if(!R) R = locate(/obj/item/device/radio) in traitor_mob.contents @@ -262,10 +262,19 @@ if (!R) 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!" 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) . = 0 else diff --git a/code/game/machinery/atmoalter/canister.dm b/code/game/machinery/atmoalter/canister.dm index fb5e188371..7cdfe57af3 100644 --- a/code/game/machinery/atmoalter/canister.dm +++ b/code/game/machinery/atmoalter/canister.dm @@ -17,6 +17,7 @@ volume = 1000 use_power = 0 var/release_log = "" + var/update_flag = 0 /obj/machinery/portable_atmospherics/canister/sleeping_agent name = "Canister: \[N2O\]" @@ -49,30 +50,64 @@ canister_color = "grey" 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() - 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) + src.overlays = 0 src.icon_state = text("[]-1", src.canister_color) - else + if(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) - overlays += "can-connector" + src.overlays = 0 - var/tank_pressure = air_contents.return_pressure() - - if (tank_pressure < 10) - overlays += image('icons/obj/atmos.dmi', "can-o0") - else if (tank_pressure < ONE_ATMOSPHERE) - overlays += image('icons/obj/atmos.dmi', "can-o1") - else if (tank_pressure < 15*ONE_ATMOSPHERE) - overlays += image('icons/obj/atmos.dmi', "can-o2") - else - overlays += image('icons/obj/atmos.dmi', "can-o3") + if(update_flag & 1) + overlays += "can-open" + if(update_flag & 2) + overlays += "can-connector" + if(update_flag & 4) + overlays += "can-o0" + if(update_flag & 8) + overlays += "can-o1" + else if(update_flag & 16) + overlays += "can-o2" + else if(update_flag & 32) + overlays += "can-o3" return /obj/machinery/portable_atmospherics/canister/temperature_expose(datum/gas_mixture/air, exposed_temperature, exposed_volume) diff --git a/code/game/machinery/doors/door.dm b/code/game/machinery/doors/door.dm index d3f276abe0..950871d983 100644 --- a/code/game/machinery/doors/door.dm +++ b/code/game/machinery/doors/door.dm @@ -268,7 +268,7 @@ for(var/turf/simulated/turf in locs) update_heat_protection(turf) - air_master.AddTurfToUpdate(turf) + air_master.mark_for_update(turf) return 1 diff --git a/code/game/machinery/doors/poddoor.dm b/code/game/machinery/doors/poddoor.dm index b7a0a9487c..017796b593 100644 --- a/code/game/machinery/doors/poddoor.dm +++ b/code/game/machinery/doors/poddoor.dm @@ -7,6 +7,14 @@ dir = 1 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) if(!density) return ..() diff --git a/code/game/machinery/doors/windowdoor.dm b/code/game/machinery/doors/windowdoor.dm index c8d0d4d110..152d6de60c 100644 --- a/code/game/machinery/doors/windowdoor.dm +++ b/code/game/machinery/doors/windowdoor.dm @@ -18,7 +18,7 @@ if(!air_master) return 0 - air_master.AddTurfToUpdate(get_turf(src)) + air_master.mark_for_update(get_turf(src)) return 1 diff --git a/code/game/machinery/embedded_controller/airlock_controllers.dm b/code/game/machinery/embedded_controller/airlock_controllers.dm index 690cd8ff64..4ace634295 100644 --- a/code/game/machinery/embedded_controller/airlock_controllers.dm +++ b/code/game/machinery/embedded_controller/airlock_controllers.dm @@ -17,7 +17,7 @@ ui = nanomanager.try_update_ui(user, src, ui_key, ui, data) 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) @@ -59,13 +59,15 @@ data = list( "chamber_pressure" = round(program.memory["chamber_sensor_pressure"]), + "exterior_status" = program.memory["exterior_status"], + "interior_status" = program.memory["interior_status"], "processing" = program.memory["processing"], ) ui = nanomanager.try_update_ui(user, src, ui_key, ui, data) 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) @@ -117,14 +119,13 @@ data = list( "exterior_status" = program.memory["exterior_status"], "interior_status" = program.memory["interior_status"], - "processing" = program.memory["processing"], - "secure" = program.memory["secure"], + "processing" = program.memory["processing"] ) ui = nanomanager.try_update_ui(user, src, ui_key, ui, data) 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) @@ -140,11 +141,11 @@ if("cycle_int_door") clean = 1 if("force_ext") - clean = 1 + if(program.memory["interior_status"]["state"] == "closed") + clean = 1 if("force_int") - clean = 1 - if("secure") - clean = 1 + if(program.memory["exterior_status"]["state"] == "closed") + clean = 1 if(clean) program.receive_user_command(href_list["command"]) diff --git a/code/game/machinery/embedded_controller/airlock_program.dm b/code/game/machinery/embedded_controller/airlock_program.dm index c647d64302..8ce0256a96 100644 --- a/code/game/machinery/embedded_controller/airlock_program.dm +++ b/code/game/machinery/embedded_controller/airlock_program.dm @@ -124,9 +124,21 @@ if("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") 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) signalPump(tag_airpump, 0) //send a signal to stop pressurizing @@ -233,6 +245,12 @@ if(TARGET_INOPEN) toggleDoor(memory["exterior_status"], tag_exterior_door, memory["secure"], "close") 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) /*---------------------------------------------------------- diff --git a/code/game/machinery/pipe/construction.dm b/code/game/machinery/pipe/construction.dm index 72aa31ccee..d3e494b2d7 100644 --- a/code/game/machinery/pipe/construction.dm +++ b/code/game/machinery/pipe/construction.dm @@ -47,6 +47,7 @@ Buildable meters if (make_from) src.dir = make_from.dir src.pipename = make_from.name + color = make_from.color var/is_bent if (make_from.initialize_directions in list(NORTH|SOUTH, WEST|EAST)) is_bent = 0 @@ -299,6 +300,7 @@ Buildable meters switch(pipe_type) if(PIPE_SIMPLE_STRAIGHT, PIPE_SIMPLE_BENT) var/obj/machinery/atmospherics/pipe/simple/P = new( src.loc ) + P.color = color P.dir = src.dir P.initialize_directions = pipe_dir var/turf/T = P.loc @@ -351,6 +353,7 @@ Buildable meters if(PIPE_MANIFOLD) //manifold var/obj/machinery/atmospherics/pipe/manifold/M = new( src.loc ) + M.color = color M.dir = dir M.initialize_directions = pipe_dir //M.New() @@ -373,6 +376,7 @@ Buildable meters if(PIPE_MANIFOLD4W) //4-way manifold var/obj/machinery/atmospherics/pipe/manifold4w/M = new( src.loc ) + M.color = color M.dir = dir M.initialize_directions = pipe_dir //M.New() diff --git a/code/game/machinery/shieldgen.dm b/code/game/machinery/shieldgen.dm index 03d97e0e1f..972066d1fb 100644 --- a/code/game/machinery/shieldgen.dm +++ b/code/game/machinery/shieldgen.dm @@ -30,7 +30,7 @@ if(!air_master) return 0 - air_master.AddTurfToUpdate(get_turf(src)) + air_master.mark_for_update(get_turf(src)) return 1 diff --git a/code/game/mecha/mech_fabricator.dm b/code/game/mecha/mech_fabricator.dm index 539ad4374e..59abed9c23 100644 --- a/code/game/mecha/mech_fabricator.dm +++ b/code/game/mecha/mech_fabricator.dm @@ -178,8 +178,8 @@ T = 0 for(var/obj/item/weapon/stock_parts/manipulator/Ml in component_parts) T += Ml.rating - if(T>= 2) - T -= 2 + if(T>= 1) + T -= 1 diff = round(initial(time_coeff) - (initial(time_coeff)*(T))/25,0.01) if(time_coeff!=diff) time_coeff = diff diff --git a/code/game/objects/effects/effect_system.dm b/code/game/objects/effects/effect_system.dm index 499bb2c22a..159a4266dd 100644 --- a/code/game/objects/effects/effect_system.dm +++ b/code/game/objects/effects/effect_system.dm @@ -833,7 +833,7 @@ steam.start() -- spawns the effect if(!air_master) return 0 - air_master.AddTurfToUpdate(get_turf(src)) + air_master.mark_for_update(get_turf(src)) return 1 diff --git a/code/game/objects/items/devices/PDA/PDA.dm b/code/game/objects/items/devices/PDA/PDA.dm index 5fc226be19..53790716a3 100755 --- a/code/game/objects/items/devices/PDA/PDA.dm +++ b/code/game/objects/items/devices/PDA/PDA.dm @@ -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/list/conversations = list() // For keeping up with who we have PDA messsages from. 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/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) var/title = "Personal Data Assistant" - + 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["toff"] = toff // is the messenger function turned off? data["active_conversation"] = active_conversation // Which conversation are we following right now? - + data["idInserted"] = (id ? 1 : 0) 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["charges"] = cartridge.charges ? cartridge.charges : 0 data["cartridge"] = cartdata - + data["stationTime"] = worldtime2text() data["newMessage"] = newmessage - + if(mode==2) var/convopdas[0] var/pdas[0] @@ -388,7 +388,7 @@ var/global/list/obj/item/device/pda/PDAs = list() if(conversations.Find("\ref[P]")) convopdas.Add(list(list("Name" = "[P]", "Reference" = "\ref[P]", "Detonate" = "[P.detonate]", "inconvo" = "1"))) 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++ data["convopdas"] = convopdas @@ -411,7 +411,7 @@ var/global/list/obj/item/device/pda/PDAs = list() if(mode==41) data["manifest"] = data_core.get_manifest_json() - + if(mode==3) var/turf/T = get_turf_or_move(user.loc) if(!isnull(T) || mode!=3) @@ -438,15 +438,15 @@ var/global/list/obj/item/device/pda/PDAs = list() ) if(isnull(data["aircontents"])) data["aircontents"] = list("reading" = 0) - + // 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) // 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 ui = new(user, src, ui_key, "pda.tmpl", title, 520, 400) // 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 ui.open() // auto update every Master Controller tick @@ -484,17 +484,17 @@ var/global/list/obj/item/device/pda/PDAs = list() U.unset_machine() ui.close() return 0 - + add_fingerprint(U) U.set_machine(src) switch(href_list["choice"]) - + //BASIC FUNCTIONS=================================== if("Close")//Self explanatory U.unset_machine() - ui.close() + ui.close() return 0 if("Refresh")//Refresh, goes to the end of the proc. if("Return")//Return @@ -518,7 +518,7 @@ var/global/list/obj/item/device/pda/PDAs = list() var/turf/T = loc if(ismob(T)) T = T.loc - cartridge.loc = T + cartridge.loc = T mode = 0 scanmode = 0 if (cartridge.radio) @@ -681,14 +681,40 @@ var/global/list/obj/item/device/pda/PDAs = list() else 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(!(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"]) if(!isnull(P)) if (!P.toff && cartridge.charges > 0) cartridge.charges-- - var/difficulty = 0 + var/difficulty = 2 if(P.cartridge) 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_clown difficulty += P.cartridge.access_janitor - else - difficulty += 2 + difficulty += 3 * P.hidden_uplink - if(prob(difficulty * 12) || (P.hidden_uplink)) + if(prob(difficulty)) 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) 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") - 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 U.show_message("\blue Success!", 1) - log_admin("[key_name(U)] just attempted to blow up [P] with the Detomatix cartridge and succeded") - message_admins("[key_name_admin(U)] just attempted to blow up [P] with the Detomatix cartridge and succeded", 1) - P.explode() + 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 succeeded.", 1) + detonate_act(P) + else + U << "No charges left." else U << "PDA not found." 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 +/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() if (id) if (ismob(loc)) @@ -774,7 +865,7 @@ var/global/list/obj/item/device/pda/PDAs = list() if(!can_use()) return - + last_text = world.time // check if telecomms I/O route 1459 is stable //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(useTC != 2) // Does our recepient have a broadcaster on their level? - U << "ERROR: Cannot reach recepient." + U << "ERROR: Cannot reach recipient." return 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]"))) P.tnote.Add(list(list("sent" = 0, "owner" = "[owner]", "job" = "[ownjob]", "message" = "[t]", "target" = "\ref[src]"))) 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(istype(M, /mob/new_player)) + continue M.show_message("PDA Message - [owner] -> [P.owner]: [t]") 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) ai.show_message("Intercepted message from [who]: [t]") - + if (!P.silent) playsound(P.loc, 'sound/machines/twobeep.ogg', 50, 1) for (var/mob/O in hearers(3, P.loc)) @@ -842,7 +935,7 @@ var/global/list/obj/item/device/pda/PDAs = list() if(L) L << "\icon[P] Message from [src.owner] ([ownjob]), \"[t]\" (Reply)" 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 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 << "You insert [cartridge] into [src]." nanomanager.update_uis(src) // update all UIs attached to src if(cartridge.radio) - cartridge.radio.hostpda = src + cartridge.radio.hostpda = src else if(istype(C, /obj/item/weapon/card/id)) 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 -/obj/item/device/pda/proc/explode() //This needs tuning. +/obj/item/device/pda/proc/explode() //This needs tuning. //Sure did. if(!src.detonate) return var/turf/T = get_turf(src.loc) - - if (ismob(loc)) - var/mob/M = loc - M.show_message("\red Your [src] explodes!", 1) - if(T) T.hotspot_expose(700,125) - - explosion(T, -1, -1, 2, 3) - - del(src) + explosion(T, 0, 0, 1, rand(1,2)) return /obj/item/device/pda/Del() 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) ..() diff --git a/code/game/objects/items/weapons/grenades/chem_grenade.dm b/code/game/objects/items/weapons/grenades/chem_grenade.dm index 1b81d0512e..29814b14ac 100644 --- a/code/game/objects/items/weapons/grenades/chem_grenade.dm +++ b/code/game/objects/items/weapons/grenades/chem_grenade.dm @@ -151,6 +151,12 @@ for(var/obj/item/weapon/reagent_containers/glass/G in beakers) 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)) if( A == src ) continue src.reagents.reaction(A, 1, 10) diff --git a/code/game/objects/structures/crates_lockers/closets/job_closets.dm b/code/game/objects/structures/crates_lockers/closets/job_closets.dm index 8c0e3d83f5..3afa1b3e8b 100644 --- a/code/game/objects/structures/crates_lockers/closets/job_closets.dm +++ b/code/game/objects/structures/crates_lockers/closets/job_closets.dm @@ -65,6 +65,7 @@ icon_closed = "blue" /obj/structure/closet/lawcloset/New() + ..() new /obj/item/clothing/under/lawyer/female(src) new /obj/item/clothing/under/lawyer/black(src) new /obj/item/clothing/under/lawyer/red(src) diff --git a/code/game/objects/structures/crates_lockers/closets/secure/security.dm b/code/game/objects/structures/crates_lockers/closets/secure/security.dm index e44847f137..289150af22 100644 --- a/code/game/objects/structures/crates_lockers/closets/secure/security.dm +++ b/code/game/objects/structures/crates_lockers/closets/secure/security.dm @@ -301,6 +301,7 @@ var/id = null New() + ..() new /obj/item/clothing/under/color/orange( src ) new /obj/item/clothing/shoes/orange( src ) return diff --git a/code/game/objects/structures/crates_lockers/closets/utility_closets.dm b/code/game/objects/structures/crates_lockers/closets/utility_closets.dm index 8ced89cd46..c3609bdbc4 100644 --- a/code/game/objects/structures/crates_lockers/closets/utility_closets.dm +++ b/code/game/objects/structures/crates_lockers/closets/utility_closets.dm @@ -56,6 +56,7 @@ del(src)*/ /obj/structure/closet/emcloset/legacy/New() + ..() new /obj/item/weapon/tank/oxygen(src) new /obj/item/clothing/mask/gas(src) @@ -108,6 +109,7 @@ icon_opened = "toolclosetopen" /obj/structure/closet/toolcloset/New() + ..() if(prob(40)) new /obj/item/clothing/suit/storage/hazardvest(src) if(prob(70)) diff --git a/code/game/objects/structures/crates_lockers/closets/wardrobe.dm b/code/game/objects/structures/crates_lockers/closets/wardrobe.dm index 5f40f4464b..e53374c5c1 100644 --- a/code/game/objects/structures/crates_lockers/closets/wardrobe.dm +++ b/code/game/objects/structures/crates_lockers/closets/wardrobe.dm @@ -4,22 +4,13 @@ icon_state = "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 name = "security wardrobe" icon_state = "red" icon_closed = "red" /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) @@ -44,6 +35,7 @@ icon_closed = "pink" /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) @@ -58,6 +50,7 @@ icon_closed = "black" /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) @@ -77,6 +70,7 @@ icon_closed = "black" /obj/structure/closet/wardrobe/chaplain_black/New() + ..() new /obj/item/clothing/under/rank/chaplain(src) new /obj/item/clothing/shoes/black(src) new /obj/item/clothing/suit/nun(src) @@ -97,6 +91,7 @@ icon_closed = "green" /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) @@ -111,6 +106,7 @@ icon_closed = "green" /obj/structure/closet/wardrobe/xenos/New() + ..() new /obj/item/clothing/suit/unathi/mantle(src) new /obj/item/clothing/suit/unathi/robe(src) new /obj/item/clothing/shoes/sandal(src) @@ -126,6 +122,7 @@ icon_closed = "orange" /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) @@ -141,6 +138,7 @@ icon_closed = "wardrobe-y" /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) @@ -156,6 +154,7 @@ icon_closed = "yellow" /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) @@ -178,6 +177,7 @@ icon_closed = "yellow" /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) @@ -199,6 +199,7 @@ icon_closed = "white" /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) @@ -214,6 +215,7 @@ icon_closed = "white" /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/blue(src) @@ -231,6 +233,7 @@ icon_closed = "white" /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) @@ -252,6 +255,7 @@ icon_closed = "black" /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/suit/storage/labcoat(src) @@ -269,6 +273,7 @@ icon_closed = "white" /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/shoes/white(src) @@ -284,6 +289,7 @@ icon_closed = "white" /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/shoes/white(src) @@ -299,6 +305,7 @@ icon_closed = "white" /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/shoes/white(src) @@ -316,6 +323,7 @@ icon_closed = "white" /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/blue(src) @@ -336,6 +344,7 @@ icon_closed = "grey" /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) @@ -354,6 +363,7 @@ icon_closed = "mixed" /obj/structure/closet/wardrobe/mixed/New() + ..() new /obj/item/clothing/under/color/blue(src) new /obj/item/clothing/under/color/yellow(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/clothing/shoes/jackboots(src) new /obj/item/clothing/gloves/black(src) - return \ No newline at end of file + return diff --git a/code/game/objects/structures/mineral_doors.dm b/code/game/objects/structures/mineral_doors.dm index 64e9623fca..3cdc4765e6 100644 --- a/code/game/objects/structures/mineral_doors.dm +++ b/code/game/objects/structures/mineral_doors.dm @@ -158,7 +158,7 @@ proc/update_nearby_tiles(need_rebuild) //Copypasta from airlock code if(!air_master) return 0 - air_master.AddTurfToUpdate(get_turf(src)) + air_master.mark_for_update(get_turf(src)) return 1 /obj/structure/mineral_door/iron diff --git a/code/game/objects/structures/windoor_assembly.dm b/code/game/objects/structures/windoor_assembly.dm index f13f3f1dec..e9ae0567bc 100644 --- a/code/game/objects/structures/windoor_assembly.dm +++ b/code/game/objects/structures/windoor_assembly.dm @@ -297,6 +297,6 @@ obj/structure/windoor_assembly/Del() if(!air_master) return 0 - air_master.AddTurfToUpdate(loc) + air_master.mark_for_update(loc) return 1 diff --git a/code/game/objects/structures/window.dm b/code/game/objects/structures/window.dm index 482fdea6c0..7050808fb4 100644 --- a/code/game/objects/structures/window.dm +++ b/code/game/objects/structures/window.dm @@ -313,7 +313,7 @@ /obj/structure/window/proc/update_nearby_tiles(need_rebuild) if(!air_master) return 0 - air_master.AddTurfToUpdate(get_turf(src)) + air_master.mark_for_update(get_turf(src)) return 1 diff --git a/code/game/turfs/turf.dm b/code/game/turfs/turf.dm index 14b05c7298..ff47a1fa72 100644 --- a/code/game/turfs/turf.dm +++ b/code/game/turfs/turf.dm @@ -206,7 +206,7 @@ for(var/obj/effect/landmark/zcontroller/c in controller) if(c.down) 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/list/temp = list() temp += W @@ -219,10 +219,10 @@ if(ispath(N, /turf/simulated/floor)) //if the old turf had a zone, connect the new turf to it as well - Cael //Adjusted by SkyMarshal 5/10/13 - The air master will handle the addition of the new turf. - if(zone) - zone.RemoveTurf(src) - if(!zone.CheckStatus()) - zone.SetStatus(ZONE_ACTIVE) + //if(zone) + // zone.RemoveTurf(src) + // if(!zone.CheckStatus()) + // zone.SetStatus(ZONE_ACTIVE) var/turf/simulated/W = new N( locate(src.x, src.y, src.z) ) //W.Assimilate_Air() @@ -236,16 +236,16 @@ W.RemoveLattice() if(air_master) - air_master.AddTurfToUpdate(src) + air_master.mark_for_update(src) W.levelupdate() return W else - if(zone) - zone.RemoveTurf(src) - if(!zone.CheckStatus()) - zone.SetStatus(ZONE_ACTIVE) + //if(zone) + // zone.RemoveTurf(src) + // if(!zone.CheckStatus()) + // zone.SetStatus(ZONE_ACTIVE) var/turf/W = new N( locate(src.x, src.y, src.z) ) W.lighting_lumcount += old_lumcount @@ -254,7 +254,7 @@ lighting_controller.changed_turfs += W if(air_master) - air_master.AddTurfToUpdate(src) + air_master.mark_for_update(src) W.levelupdate() return W diff --git a/code/modules/admin/verbs/diagnostics.dm b/code/modules/admin/verbs/diagnostics.dm index 29840d0e61..bbf74c9dbb 100644 --- a/code/modules/admin/verbs/diagnostics.dm +++ b/code/modules/admin/verbs/diagnostics.dm @@ -6,7 +6,7 @@ alert(usr,"Master_controller or air_master not found.","Air Report") 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/hotspots = 0 @@ -18,7 +18,7 @@ for(var/zone/zone in air_master.zones) var/turf/simulated/turf = locate() in zone.contents if(turf && turf.z == 1) - if(zone.status) + if(zone.needs_update) active_on_main_station++ else inactive_on_main_station++ diff --git a/code/modules/admin/verbs/mapping.dm b/code/modules/admin/verbs/mapping.dm index 76f1b42046..f984e17447 100644 --- a/code/modules/admin/verbs/mapping.dm +++ b/code/modules/admin/verbs/mapping.dm @@ -124,49 +124,146 @@ var/intercom_range_display_status = 0 del(F) 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() set category = "Debug" set name = "Debug verbs" if(!check_rights(R_DEBUG)) return - src.verbs += /client/proc/do_not_use_these //-errorage - 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 + verbs += debug_verbs 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() set category = "Mapping" set name = "Count Objects On Level" @@ -181,7 +278,7 @@ var/intercom_range_display_status = 0 var/type_path = text2path(type_text) if(!type_path) return - var/count = 0 + var/count = 1 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.") movement_disabled_exception = usr.ckey else - message_admins("[src.ckey] used 'Disable all movement', restoring all movement.")*/ \ No newline at end of file + message_admins("[src.ckey] used 'Disable all movement', restoring all movement.")*/ diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm index e1b0d29b2a..f34f9ab467 100644 --- a/code/modules/client/preferences.dm +++ b/code/modules/client/preferences.dm @@ -247,7 +247,7 @@ datum/preferences dat += "UI Style: [UI_style]
" dat += "Custom UI(recommended for White UI):
" dat += "-Color: [UI_style_color]
__

" - dat += "-Alpha(transparence): [UI_style_alpha]
" + dat += "-Alpha(transparency): [UI_style_alpha]
" dat += "Play admin midis: [(toggles & SOUND_MIDI) ? "Yes" : "No"]
" dat += "Play lobby music: [(toggles & SOUND_LOBBY) ? "Yes" : "No"]
" dat += "Ghost ears: [(toggles & CHAT_GHOSTEARS) ? "Nearest Creatures" : "All Speech"]
" diff --git a/code/modules/mob/emote.dm b/code/modules/mob/emote.dm index e2125ada66..c33732ed13 100644 --- a/code/modules/mob/emote.dm +++ b/code/modules/mob/emote.dm @@ -38,13 +38,15 @@ // Type 1 (Visual) emotes are sent to anyone in view of the item 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) // Type 2 (Audible) emotes are sent to anyone in hear range // of the *LOCATION* -- this is important for pAIs to be heard 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) /mob/proc/emote_dead(var/message) diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm index 4ab2902c28..6a5102b18c 100644 --- a/code/modules/mob/living/carbon/human/human.dm +++ b/code/modules/mob/living/carbon/human/human.dm @@ -6,6 +6,7 @@ icon_state = "body_m_s" var/list/hud_list = list() 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 real_name = "Test Dummy" @@ -46,6 +47,7 @@ if(!dna) dna = new /datum/dna(null) + dna.species=species.name for(var/i=0;i<7;i++) // 2 for medHUDs and 5 for secHUDs hud_list += image('icons/mob/hud.dmi', src, "hudunknown") @@ -1184,8 +1186,14 @@ /mob/living/carbon/human/proc/set_species(var/new_species) - if(!new_species) - new_species = "Human" + if(!dna) + 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)) return diff --git a/code/modules/mob/living/carbon/human/human_damage.dm b/code/modules/mob/living/carbon/human/human_damage.dm index dcd3282225..91b7ef0f96 100644 --- a/code/modules/mob/living/carbon/human/human_damage.dm +++ b/code/modules/mob/living/carbon/human/human_damage.dm @@ -251,6 +251,7 @@ This function restores all organs. if( (damage > (10*W.w_class)) && ( (sharp && !ismob(W.loc)) || prob(damage/W.w_class) ) ) organ.implants += W visible_message("\The [W] sticks in the wound!") + embedded_flag = 1 src.verbs += /mob/proc/yank_out_object W.add_blood(src) if(ismob(W.loc)) @@ -258,4 +259,4 @@ This function restores all organs. H.drop_item() W.loc = src - return 1 \ No newline at end of file + return 1 diff --git a/code/modules/mob/living/carbon/human/human_defense.dm b/code/modules/mob/living/carbon/human/human_defense.dm index 9657085e2a..654538194c 100644 --- a/code/modules/mob/living/carbon/human/human_defense.dm +++ b/code/modules/mob/living/carbon/human/human_defense.dm @@ -99,6 +99,7 @@ emp_act (SP.loc) = organ organ.implants += SP visible_message("The projectile sticks in the wound!") + embedded_flag = 1 src.verbs += /mob/proc/yank_out_object SP.add_blood(src) diff --git a/code/modules/mob/living/carbon/human/human_movement.dm b/code/modules/mob/living/carbon/human/human_movement.dm index 77cd03d5ee..689319059b 100644 --- a/code/modules/mob/living/carbon/human/human_movement.dm +++ b/code/modules/mob/living/carbon/human/human_movement.dm @@ -6,7 +6,8 @@ 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 diff --git a/code/modules/mob/living/carbon/human/life.dm b/code/modules/mob/living/carbon/human/life.dm index 732a41ff24..a9680785f6 100644 --- a/code/modules/mob/living/carbon/human/life.dm +++ b/code/modules/mob/living/carbon/human/life.dm @@ -105,7 +105,7 @@ handle_environment(environment) //Status updates, death etc. - handle_regular_status_updates() //TODO: optimise ~Carn + handle_regular_status_updates() //TODO: optimise ~Carn NO SHIT ~Ccomp update_canmove() //Update our name based on whether our face is obscured/disfigured @@ -1067,6 +1067,13 @@ if(halloss > 0) adjustHalLoss(-1) + if(embedded_flag && !(life_tick % 10)) + var/list/E + E = get_visible_implants(0) + if(!E.len) + embedded_flag = 0 + + //Eyes if(sdisabilities & BLIND) //disabled-blind, doesn't get better on its own blinded = 1 diff --git a/code/modules/mob/living/carbon/monkey/monkey.dm b/code/modules/mob/living/carbon/monkey/monkey.dm index 502f6b61a8..92edb9aacd 100644 --- a/code/modules/mob/living/carbon/monkey/monkey.dm +++ b/code/modules/mob/living/carbon/monkey/monkey.dm @@ -14,7 +14,7 @@ //var/uni_append = "12C4E2" // Small appearance modifier for different species. var/list/uni_append = list(0x12C,0x4E2) // Same as above for DNA2. 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 name = "farwa" diff --git a/code/modules/mob/living/say.dm b/code/modules/mob/living/say.dm index 13523072ca..9336979358 100644 --- a/code/modules/mob/living/say.dm +++ b/code/modules/mob/living/say.dm @@ -302,7 +302,7 @@ var/list/department_radio_keys = list( var/list/listening 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) if (!M.client) continue //skip monkeys and leavers diff --git a/code/modules/mob/living/silicon/pai/software.dm b/code/modules/mob/living/silicon/pai/software.dm index 27d557c70c..6e688c2bdc 100644 --- a/code/modules/mob/living/silicon/pai/software.dm +++ b/code/modules/mob/living/silicon/pai/software.dm @@ -643,5 +643,12 @@ dat += "" dat += "" dat += "

" - dat += "Messages:
[pda.tnote]" - return dat \ No newline at end of file + for(var/index in pda.tnote) + if(index["sent"]) + dat += addtext("→ To ", index["owner"],":
", index["message"], "
") + else + dat += addtext("← From ", index["owner"],":
", index["message"], "
") + + + + return dat diff --git a/code/modules/power/apc.dm b/code/modules/power/apc.dm index aac6dc0a42..7a5f17eeca 100644 --- a/code/modules/power/apc.dm +++ b/code/modules/power/apc.dm @@ -3,7 +3,33 @@ #define APC_WIRE_MAIN_POWER2 3 #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) // one per area, needs wire conection to power network @@ -18,7 +44,6 @@ /obj/machinery/power/apc name = "area power controller" - icon_state = "apc0" anchored = 1 use_power = 0 @@ -64,8 +89,16 @@ "Yellow" = 4, ) var/longtermpower = 10 + var/update_state = -1 + var/update_overlay = -1 + var/global/status_overlays = 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() //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 usr << "The cover is closed." - // update the APC icon to show the three base states // also add overlays for indicator lights /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() - if(opened) - var/basestate = "apc[ cell ? "2" : "1" ]" // if opened, show cell if it's inserted - if (opened==1) - if (stat & (MAINT|BROKEN)) - icon_state = "apcmaint" //disassembled APC cannot hold cell - else - icon_state = basestate - else if (opened == 2) - icon_state = "[basestate]-nocover" - else if (stat & BROKEN) - icon_state = "apc-b" - else if(emagged || malfai) - icon_state = "apcemag" - else if(wiresexposed) - icon_state = "apcewires" - else - icon_state = "apc0" - // if closed, update overlays for channel status - if(!(stat & (BROKEN|MAINT))) - overlays.Add("apcox-[locked]","apco3-[charging]") // 0=blue 1=red // 0=red, 1=yellow/black 2=green + status_overlays_lock.len = 2 + status_overlays_charging.len = 3 + status_overlays_equipment.len = 4 + status_overlays_lighting.len = 4 + status_overlays_environ.len = 4 + + status_overlays_lock[1] = image(icon, "apcox-0") // 0=blue 1=red + status_overlays_lock[2] = image(icon, "apcox-1") + + status_overlays_charging[1] = image(icon, "apco3-0") + status_overlays_charging[2] = image(icon, "apco3-1") + status_overlays_charging[3] = image(icon, "apco3-2") + + status_overlays_equipment[1] = image(icon, "apco0-0") // 0=red, 1=green, 2=blue + status_overlays_equipment[2] = image(icon, "apco0-1") + status_overlays_equipment[3] = image(icon, "apco0-2") + status_overlays_equipment[4] = image(icon, "apco0-3") + + status_overlays_lighting[1] = image(icon, "apco1-0") + status_overlays_lighting[2] = image(icon, "apco1-1") + 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) - 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() if(!updating_icon) @@ -214,6 +373,7 @@ update_icon() updating_icon = 0 + //attack with an item - open/close cover, insert cell, or (un)lock interface /obj/machinery/power/apc/attackby(obj/item/W, mob/user) @@ -1143,8 +1303,6 @@ update() else if (last_ch != charging) queue_icon_update() - - //src.updateDialog() src.updateDialog() // val 0=off, 1=off(auto) 2=on 3=on(auto) @@ -1267,4 +1425,4 @@ else return 0 -#undef APC_UPDATE_ICON_COOLDOWN \ No newline at end of file +#undef APC_UPDATE_ICON_COOLDOWN diff --git a/code/modules/reagents/Chemistry-Recipes.dm b/code/modules/reagents/Chemistry-Recipes.dm index 5203f82891..06ebdc6c9a 100644 --- a/code/modules/reagents/Chemistry-Recipes.dm +++ b/code/modules/reagents/Chemistry-Recipes.dm @@ -452,13 +452,13 @@ datum id = "chemsmoke" result = null required_reagents = list("potassium" = 1, "sugar" = 1, "phosphorus" = 1) - result_amount = null + result_amount = 0.4 secondary = 1 on_reaction(var/datum/reagents/holder, var/created_volume) var/location = get_turf(holder.my_atom) var/datum/effect/effect/system/smoke_spread/chem/S = new /datum/effect/effect/system/smoke_spread/chem 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) spawn(0) S.start() diff --git a/code/modules/supermatter/supermatter.dm b/code/modules/supermatter/supermatter.dm index 8547c42fdd..4bdd2431ce 100644 --- a/code/modules/supermatter/supermatter.dm +++ b/code/modules/supermatter/supermatter.dm @@ -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. 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((world.timeofday - lastwarning) / 10 >= WARNING_DELAY) var/stability = num2text(round((damage / explosion_point) * 100)) diff --git a/html/changelog.html b/html/changelog.html index 098a6cb547..4ae2b5a0a2 100644 --- a/html/changelog.html +++ b/html/changelog.html @@ -35,8 +35,8 @@ Header Section @@ -56,6 +56,13 @@ should be listed in the changelog upon commit though. Thanks. --> +
+

19 February 2014

+

Aryn updated:

+ +

18 December 2013

diff --git a/icons/Testing/Zone.dmi b/icons/Testing/Zone.dmi new file mode 100644 index 0000000000..1d8ab85166 Binary files /dev/null and b/icons/Testing/Zone.dmi differ diff --git a/icons/misc/debug_group.dmi b/icons/misc/debug_group.dmi index 2c87c66010..a9cf3ee8ab 100644 Binary files a/icons/misc/debug_group.dmi and b/icons/misc/debug_group.dmi differ diff --git a/icons/mob/head.dmi b/icons/mob/head.dmi index 878b8317bb..caef139553 100644 Binary files a/icons/mob/head.dmi and b/icons/mob/head.dmi differ diff --git a/nano/templates/door_access_console.tmpl b/nano/templates/door_access_console.tmpl index 966efd3971..c755d7c806 100644 --- a/nano/templates/door_access_console.tmpl +++ b/nano/templates/door_access_console.tmpl @@ -1,35 +1,42 @@
-
+
Exterior Door Status:
- {{:exterior_status.state}} - {{:exterior_status.lock}} + {{if exterior_status.state == "closed"}} + Locked + {{else}} + Open + {{/if}}
-
+
Interior Door Status:
- {{:interior_status.state}} - {{:interior_status.lock}} + {{if interior_status.state == "closed"}} + Locked + {{else}} + Open + {{/if}}
-
+
+ {{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)}} + {{/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)}} + {{/if}}
-
- {{:~link('Force exterior door', 'alert', {'command' : 'force_ext'}, null, processing ? 'yellowBackground' : null)}} - {{:~link('Force interior door', 'alert', {'command' : 'force_int'}, null, processing ? 'yellowBackground' : null)}} -
-
-
-
- {{:~link('Secure', secure ? 'locked' : 'unlocked', {'command' : 'secure'}, processing ? 'disabled' : null, secure ? 'linkOn' : null)}} -
\ No newline at end of file diff --git a/nano/templates/simple_airlock_console.tmpl b/nano/templates/simple_airlock_console.tmpl index 5d2a8b8098..9975295859 100644 --- a/nano/templates/simple_airlock_console.tmpl +++ b/nano/templates/simple_airlock_console.tmpl @@ -13,16 +13,24 @@
-
+
{{:~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)}}
-
+
+ {{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)}} + {{/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)}} + {{/if}}
-
+
{{:~link('Abort', 'cancel', {'command' : 'abort'}, processing ? null : 'disabled', processing ? 'redBackground' : null)}}
\ No newline at end of file
- Code: 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
- Sprites: Apple_Master, Arcalane, Chinsky, CompactNinja, Deus Dactyl, Erthilo, Flashkirby, Miniature, Searif, Xenone, faux
+ Code: 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
+ Sprites: Apple_Master, Arcalane, Chinsky, CompactNinja, Deus Dactyl, Erthilo, Flashkirby, JoeyJo0, Miniature, Searif, Xenone, faux
Sounds: Aryn
Thanks To: /tg/ station, Goonstation, Animus Station, Daedalus, and original Spacestation 13 devs. Skibiliano for the IRC bot.