mirror of
https://github.com/PolarisSS13/Polaris.git
synced 2026-08-25 14:16:30 +01:00
Merge pull request #4657 from Baystation12/dev-freeze
0.1.10 Staging [Standby]
This commit is contained in:
+11
-5
@@ -421,6 +421,7 @@
|
||||
#include "code\game\objects\closets\walllocker.dm"
|
||||
#include "code\game\objects\effects\aliens.dm"
|
||||
#include "code\game\objects\effects\bump_teleporter.dm"
|
||||
#include "code\game\objects\effects\chemsmoke.dm"
|
||||
#include "code\game\objects\effects\effect_system.dm"
|
||||
#include "code\game\objects\effects\gibs.dm"
|
||||
#include "code\game\objects\effects\glowshroom.dm"
|
||||
@@ -1325,17 +1326,22 @@
|
||||
#include "code\WorkInProgress\Ported\policetape.dm"
|
||||
#include "code\WorkInProgress\SkyMarshal\officer_stuff.dm"
|
||||
#include "code\WorkInProgress\SkyMarshal\Ultralight_procs.dm"
|
||||
#include "code\WorkInProgress\Wrongnumber\weldbackpack.dm"
|
||||
#include "code\ZAS\_docs.dm"
|
||||
#include "code\ZAS\_gas_mixture.dm"
|
||||
#include "code\ZAS\Airflow.dm"
|
||||
#include "code\ZAS\Atom.dm"
|
||||
#include "code\ZAS\Connection.dm"
|
||||
#include "code\ZAS\ConnectionGroup.dm"
|
||||
#include "code\ZAS\ConnectionManager.dm"
|
||||
#include "code\ZAS\Controller.dm"
|
||||
#include "code\ZAS\Debug.dm"
|
||||
#include "code\ZAS\FEA_gas_mixture.dm"
|
||||
#include "code\ZAS\FEA_system.dm"
|
||||
#include "code\ZAS\Diagnostic.dm"
|
||||
#include "code\ZAS\Fire.dm"
|
||||
#include "code\ZAS\Functions.dm"
|
||||
#include "code\ZAS\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"
|
||||
|
||||
+1
-1
@@ -3,4 +3,4 @@
|
||||
MAP_ICON_TYPE: 0
|
||||
AUTO_FILE_DIR: OFF
|
||||
*/
|
||||
// END_INTERNALS
|
||||
// END_INTERNALS
|
||||
+3
-166
@@ -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
|
||||
@@ -0,0 +1,66 @@
|
||||
|
||||
|
||||
/atom/var/pressure_resistance = ONE_ATMOSPHERE
|
||||
|
||||
atom/proc/CanPass(atom/movable/mover, turf/target, height=1.5, air_group = 0)
|
||||
//Purpose: Determines if the object (or airflow) can pass this atom.
|
||||
//Called by: Movement, airflow.
|
||||
//Inputs: The moving atom (optional), target turf, "height" and air group
|
||||
//Outputs: Boolean if can pass.
|
||||
|
||||
return (!density || !height || air_group)
|
||||
|
||||
/turf/CanPass(atom/movable/mover, turf/target, height=1.5,air_group=0)
|
||||
if(!target) return 0
|
||||
|
||||
if(istype(mover)) // turf/Enter(...) will perform more advanced checks
|
||||
return !density
|
||||
|
||||
else // Now, doing more detailed checks for air movement and air group formation
|
||||
if(target.blocks_air||blocks_air)
|
||||
return 0
|
||||
|
||||
for(var/obj/obstacle in src)
|
||||
if(!obstacle.CanPass(mover, target, height, air_group))
|
||||
return 0
|
||||
if(target != src)
|
||||
for(var/obj/obstacle in target)
|
||||
if(!obstacle.CanPass(mover, src, height, air_group))
|
||||
return 0
|
||||
|
||||
return 1
|
||||
|
||||
//Basically another way of calling CanPass(null, other, 0, 0) and CanPass(null, other, 1.5, 1).
|
||||
//Returns:
|
||||
// 0 - Not blocked
|
||||
// AIR_BLOCKED - Blocked
|
||||
// ZONE_BLOCKED - Not blocked, but zone boundaries will not cross.
|
||||
// BLOCKED - Blocked, zone boundaries will not cross even if opened.
|
||||
atom/proc/c_airblock(turf/other)
|
||||
#ifdef ZASDBG
|
||||
ASSERT(isturf(other))
|
||||
#endif
|
||||
return !CanPass(null, other, 0, 0) + 2*!CanPass(null, other, 1.5, 1)
|
||||
|
||||
|
||||
turf/c_airblock(turf/other)
|
||||
#ifdef ZASDBG
|
||||
ASSERT(isturf(other))
|
||||
#endif
|
||||
if(blocks_air)
|
||||
return BLOCKED
|
||||
|
||||
//Z-level handling code. Always block if there isn't an open space.
|
||||
#ifdef ZLEVELS
|
||||
if(other.z != src.z)
|
||||
if(other.z < src.z)
|
||||
if(!istype(src, /turf/simulated/floor/open)) return BLOCKED
|
||||
else
|
||||
if(!istype(other, /turf/simulated/floor/open)) return BLOCKED
|
||||
#endif
|
||||
|
||||
var/result = 0
|
||||
for(var/atom/movable/M in contents)
|
||||
result |= M.c_airblock(other)
|
||||
if(result == BLOCKED) return BLOCKED
|
||||
return result
|
||||
+141
-424
@@ -1,448 +1,165 @@
|
||||
#define CONNECTION_DIRECT 2
|
||||
#define CONNECTION_SPACE 4
|
||||
#define CONNECTION_INVALID 8
|
||||
|
||||
/*
|
||||
This object is contained within zone/var/connections. It's generated whenever two turfs from different zones are linked.
|
||||
Indirect connections will not merge the two zones after they reach equilibrium.
|
||||
|
||||
Overview:
|
||||
Connections are made between turfs by air_master.connect(). They represent a single point where two zones converge.
|
||||
|
||||
Class Vars:
|
||||
A - Always a simulated turf.
|
||||
B - A simulated or unsimulated turf.
|
||||
|
||||
zoneA - The archived zone of A. Used to check that the zone hasn't changed.
|
||||
zoneB - The archived zone of B. May be null in case of unsimulated connections.
|
||||
|
||||
edge - Stores the edge this connection is in. Can reference an edge that is no longer processed
|
||||
after this connection is removed, so make sure to check edge.coefficient > 0 before re-adding it.
|
||||
|
||||
Class Procs:
|
||||
|
||||
mark_direct()
|
||||
Marks this connection as direct. Does not update the edge.
|
||||
Called when the connection is made and there are no doors between A and B.
|
||||
Also called by update() as a correction.
|
||||
|
||||
mark_indirect()
|
||||
Unmarks this connection as direct. Does not update the edge.
|
||||
Called by update() as a correction.
|
||||
|
||||
mark_space()
|
||||
Marks this connection as unsimulated. Updating the connection will check the validity of this.
|
||||
Called when the connection is made.
|
||||
This will not be called as a correction, any connections failing a check against this mark are erased and rebuilt.
|
||||
|
||||
direct()
|
||||
Returns 1 if no doors are in between A and B.
|
||||
|
||||
valid()
|
||||
Returns 1 if the connection has not been erased.
|
||||
|
||||
erase()
|
||||
Called by update() and connection_manager/erase_all().
|
||||
Marks the connection as erased and removes it from its edge.
|
||||
|
||||
update()
|
||||
Called by connection_manager/update_all().
|
||||
Makes numerous checks to decide whether the connection is still valid. Erases it automatically if not.
|
||||
|
||||
*/
|
||||
#define CONNECTION_DIRECT 2
|
||||
#define CONNECTION_INDIRECT 1
|
||||
#define CONNECTION_CLOSED 0
|
||||
|
||||
/connection
|
||||
var/turf/simulated/A
|
||||
var/turf/simulated/B
|
||||
/connection/var/turf/simulated/A
|
||||
/connection/var/turf/simulated/B
|
||||
/connection/var/zone/zoneA
|
||||
/connection/var/zone/zoneB
|
||||
|
||||
var/zone/zone_A
|
||||
var/zone/zone_B
|
||||
/connection/var/connection_edge/edge
|
||||
|
||||
var/indirect = CONNECTION_DIRECT //If the connection is purely indirect, the zones should not join.
|
||||
|
||||
|
||||
/connection/New(turf/T,turf/O)
|
||||
. = ..()
|
||||
|
||||
A = T
|
||||
B = O
|
||||
|
||||
if(A.zone && B.zone)
|
||||
if(!A.zone.connections)
|
||||
A.zone.connections = list()
|
||||
A.zone.connections += src
|
||||
zone_A = A.zone
|
||||
|
||||
if(!B.zone.connections)
|
||||
B.zone.connections = list()
|
||||
B.zone.connections += src
|
||||
zone_B = B.zone
|
||||
|
||||
if(A in air_master.turfs_with_connections)
|
||||
var/list/connections = air_master.turfs_with_connections[A]
|
||||
connections.Add(src)
|
||||
else
|
||||
air_master.turfs_with_connections[A] = list(src)
|
||||
|
||||
if(B in air_master.turfs_with_connections)
|
||||
var/list/connections = air_master.turfs_with_connections[B]
|
||||
connections.Add(src)
|
||||
else
|
||||
air_master.turfs_with_connections[B] = list(src)
|
||||
|
||||
if(A.CanPass(null, B, 0, 0))
|
||||
|
||||
if(!A.CanPass(null, B, 1.5, 1))
|
||||
indirect = CONNECTION_INDIRECT
|
||||
|
||||
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()
|
||||
state |= CONNECTION_DIRECT
|
||||
//world << "Marked direct."
|
||||
|
||||
/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()
|
||||
state &= ~CONNECTION_DIRECT
|
||||
//world << "Marked indirect."
|
||||
|
||||
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
|
||||
//world << "Connection Erased: [state]"
|
||||
|
||||
if(istype(zone_B) && (!B || B.zone != zone_B))
|
||||
if(zone_B.connections)
|
||||
zone_B.connections.Remove(src)
|
||||
if(!zone_B.connections.len)
|
||||
zone_B.connections = null
|
||||
|
||||
//Disconnect zones while handling unusual conditions.
|
||||
// e.g. loss of a zone on a turf
|
||||
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."
|
||||
@@ -0,0 +1,427 @@
|
||||
/*
|
||||
|
||||
Overview:
|
||||
These are what handle gas transfers between zones and into space.
|
||||
They are found in a zone's edges list and in air_master.edges.
|
||||
Each edge updates every air tick due to their role in gas transfer.
|
||||
They come in two flavors, /connection_edge/zone and /connection_edge/unsimulated.
|
||||
As the type names might suggest, they handle inter-zone and spacelike connections respectively.
|
||||
|
||||
Class Vars:
|
||||
|
||||
A - This always holds a zone. In unsimulated edges, it holds the only zone.
|
||||
|
||||
connecting_turfs - This holds a list of connected turfs, mainly for the sake of airflow.
|
||||
|
||||
coefficent - This is a marker for how many connections are on this edge. Used to determine the ratio of flow.
|
||||
|
||||
connection_edge/zone
|
||||
|
||||
B - This holds the second zone with which the first zone equalizes.
|
||||
|
||||
direct - This counts the number of direct (i.e. with no doors) connections on this edge.
|
||||
Any value of this is sufficient to make the zones mergeable.
|
||||
|
||||
connection_edge/unsimulated
|
||||
|
||||
B - This holds an unsimulated turf which has the gas values this edge is mimicing.
|
||||
|
||||
air - Retrieved from B on creation and used as an argument for the legacy ShareSpace() proc.
|
||||
|
||||
Class Procs:
|
||||
|
||||
add_connection(connection/c)
|
||||
Adds a connection to this edge. Usually increments the coefficient and adds a turf to connecting_turfs.
|
||||
|
||||
remove_connection(connection/c)
|
||||
Removes a connection from this edge. This works even if c is not in the edge, so be careful.
|
||||
If the coefficient reaches zero as a result, the edge is erased.
|
||||
|
||||
contains_zone(zone/Z)
|
||||
Returns true if either A or B is equal to Z. Unsimulated connections return true only on A.
|
||||
|
||||
erase()
|
||||
Removes this connection from processing and zone edge lists.
|
||||
|
||||
tick()
|
||||
Called every air tick on edges in the processing list. Equalizes gas.
|
||||
|
||||
flow(list/movable, differential, repelled)
|
||||
Airflow proc causing all objects in movable to be checked against a pressure differential.
|
||||
If repelled is true, the objects move away from any turf in connecting_turfs, otherwise they approach.
|
||||
A check against vsc.lightest_airflow_pressure should generally be performed before calling this.
|
||||
|
||||
get_connected_zone(zone/from)
|
||||
Helper proc that allows getting the other zone of an edge given one of them.
|
||||
Only on /connection_edge/zone, otherwise use A.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
/connection_edge/var/zone/A
|
||||
|
||||
/connection_edge/var/list/connecting_turfs = list()
|
||||
|
||||
/connection_edge/var/coefficient = 0
|
||||
|
||||
/connection_edge/New()
|
||||
CRASH("Cannot make connection edge without specifications.")
|
||||
|
||||
/connection_edge/proc/add_connection(connection/c)
|
||||
coefficient++
|
||||
//world << "Connection added: [type] Coefficient: [coefficient]"
|
||||
|
||||
/connection_edge/proc/remove_connection(connection/c)
|
||||
//world << "Connection removed: [type] Coefficient: [coefficient-1]"
|
||||
coefficient--
|
||||
if(coefficient <= 0)
|
||||
erase()
|
||||
|
||||
/connection_edge/proc/contains_zone(zone/Z)
|
||||
|
||||
/connection_edge/proc/erase()
|
||||
air_master.remove_edge(src)
|
||||
//world << "[type] Erased."
|
||||
|
||||
/connection_edge/proc/tick()
|
||||
|
||||
/connection_edge/proc/flow(list/movable, differential, repelled)
|
||||
for(var/atom/movable/M in movable)
|
||||
|
||||
//If they're already being tossed, don't do it again.
|
||||
if(M.last_airflow > world.time - 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()
|
||||
if(A.invalid || B.invalid)
|
||||
erase()
|
||||
return
|
||||
//world << "[id]: Tick [air_master.current_cycle]: \..."
|
||||
if(direct)
|
||||
if(air_master.equivalent_pressure(A, B))
|
||||
//world << "merged."
|
||||
erase()
|
||||
air_master.merge(A, B)
|
||||
//world << "zones merged."
|
||||
return
|
||||
|
||||
//air_master.equalize(A, B)
|
||||
ShareRatio(A.air,B.air,coefficient)
|
||||
air_master.mark_zone_update(A)
|
||||
air_master.mark_zone_update(B)
|
||||
//world << "equalized."
|
||||
|
||||
var/differential = A.air.return_pressure() - B.air.return_pressure()
|
||||
if(abs(differential) < 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)
|
||||
|
||||
//Helper proc to get connections for a zone.
|
||||
/connection_edge/zone/proc/get_connected_zone(zone/from)
|
||||
if(A == from) return B
|
||||
else return A
|
||||
|
||||
/connection_edge/unsimulated/var/turf/B
|
||||
/connection_edge/unsimulated/var/datum/gas_mixture/air
|
||||
|
||||
/connection_edge/unsimulated/New(zone/A, turf/B)
|
||||
src.A = A
|
||||
src.B = B
|
||||
A.edges.Add(src)
|
||||
air = B.return_air()
|
||||
//id = 52*A.id
|
||||
//world << "New edge from [A] to [B]."
|
||||
|
||||
/connection_edge/unsimulated/add_connection(connection/c)
|
||||
. = ..()
|
||||
connecting_turfs.Add(c.B)
|
||||
air.group_multiplier = coefficient
|
||||
|
||||
/connection_edge/unsimulated/remove_connection(connection/c)
|
||||
connecting_turfs.Remove(c.B)
|
||||
air.group_multiplier = coefficient
|
||||
. = ..()
|
||||
|
||||
/connection_edge/unsimulated/erase()
|
||||
A.edges.Remove(src)
|
||||
. = ..()
|
||||
|
||||
/connection_edge/unsimulated/contains_zone(zone/Z)
|
||||
return A == Z
|
||||
|
||||
/connection_edge/unsimulated/tick()
|
||||
if(A.invalid)
|
||||
erase()
|
||||
return
|
||||
//world << "[id]: Tick [air_master.current_cycle]: To [B]!"
|
||||
//A.air.mimic(B, coefficient)
|
||||
ShareSpace(A.air,air,dbg_out)
|
||||
air_master.mark_zone_update(A)
|
||||
|
||||
var/differential = A.air.return_pressure() - air.return_pressure()
|
||||
if(abs(differential) < 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
|
||||
|
||||
if(dbg_output)
|
||||
world << "O2: [unsim_oxygen] N2: [unsim_nitrogen] Size: [share_size] Tiles: [tileslen]"
|
||||
|
||||
else if(istype(unsimulated_tiles, /list))
|
||||
if(!unsimulated_tiles.len)
|
||||
return 0
|
||||
// We use the same size for the potentially single space tile
|
||||
// as we use for the entire room. Why is this?
|
||||
// Short answer: We do not want larger rooms to depressurize more
|
||||
// slowly than small rooms, preserving our good old "hollywood-style"
|
||||
// oh-shit effect when large rooms get breached, but still having small
|
||||
// rooms remain pressurized for long enough to make escape possible.
|
||||
share_size = max(1, max(size + 3, 1) + unsimulated_tiles.len)
|
||||
var/correction_ratio = share_size / unsimulated_tiles.len
|
||||
|
||||
for(var/turf/T in unsimulated_tiles)
|
||||
unsim_oxygen += T.oxygen
|
||||
unsim_co2 += T.carbon_dioxide
|
||||
unsim_nitrogen += T.nitrogen
|
||||
unsim_plasma += T.toxins
|
||||
unsim_temperature += T.temperature/unsimulated_tiles.len
|
||||
|
||||
//These values require adjustment in order to properly represent a room of the specified size.
|
||||
unsim_oxygen *= correction_ratio
|
||||
unsim_co2 *= correction_ratio
|
||||
unsim_nitrogen *= correction_ratio
|
||||
unsim_plasma *= correction_ratio
|
||||
tileslen = unsimulated_tiles.len
|
||||
|
||||
else //invalid input type
|
||||
return 0
|
||||
|
||||
unsim_heat_capacity = HEAT_CAPACITY_CALCULATION(unsim_oxygen, unsim_co2, unsim_nitrogen, unsim_plasma)
|
||||
|
||||
var
|
||||
ratio = sharing_lookup_table[6]
|
||||
|
||||
old_pressure = A.return_pressure()
|
||||
|
||||
full_oxy = A.oxygen * size
|
||||
full_nitro = A.nitrogen * size
|
||||
full_co2 = A.carbon_dioxide * size
|
||||
full_plasma = A.toxins * size
|
||||
|
||||
full_heat_capacity = A.heat_capacity() * size
|
||||
|
||||
oxy_avg = (full_oxy + unsim_oxygen*share_size) / (size + share_size)
|
||||
nit_avg = (full_nitro + unsim_nitrogen*share_size) / (size + share_size)
|
||||
co2_avg = (full_co2 + unsim_co2*share_size) / (size + share_size)
|
||||
plasma_avg = (full_plasma + unsim_plasma*share_size) / (size + share_size)
|
||||
|
||||
temp_avg = 0
|
||||
|
||||
if((full_heat_capacity + unsim_heat_capacity) > 0)
|
||||
temp_avg = (A.temperature * full_heat_capacity + unsim_temperature * unsim_heat_capacity) / (full_heat_capacity + unsim_heat_capacity)
|
||||
|
||||
if(sharing_lookup_table.len >= tileslen) //6 or more interconnecting tiles will max at 42% of air moved per tick.
|
||||
ratio = sharing_lookup_table[tileslen]
|
||||
|
||||
if(dbg_output)
|
||||
world << "Ratio: [ratio]"
|
||||
world << "Avg O2: [oxy_avg] N2: [nit_avg]"
|
||||
|
||||
A.oxygen = max(0, (A.oxygen - oxy_avg) * (1 - ratio) + oxy_avg )
|
||||
A.nitrogen = max(0, (A.nitrogen - nit_avg) * (1 - ratio) + nit_avg )
|
||||
A.carbon_dioxide = max(0, (A.carbon_dioxide - co2_avg) * (1 - ratio) + co2_avg )
|
||||
A.toxins = max(0, (A.toxins - plasma_avg) * (1 - ratio) + plasma_avg )
|
||||
|
||||
A.temperature = max(TCMB, (A.temperature - temp_avg) * (1 - ratio) + temp_avg )
|
||||
|
||||
for(var/datum/gas/G in A.trace_gases)
|
||||
var/G_avg = (G.moles * size) / (size + share_size)
|
||||
G.moles = (G.moles - G_avg) * (1 - ratio) + G_avg
|
||||
|
||||
A.update_values()
|
||||
|
||||
if(dbg_output) world << "Result: [abs(old_pressure - A.return_pressure())] kPa"
|
||||
|
||||
return abs(old_pressure - A.return_pressure())
|
||||
|
||||
|
||||
proc/ShareHeat(datum/gas_mixture/A, datum/gas_mixture/B, connecting_tiles)
|
||||
//This implements a simplistic version of the Stefan-Boltzmann law.
|
||||
var/energy_delta = ((A.temperature - B.temperature) ** 4) * 5.6704e-8 * connecting_tiles * 2.5
|
||||
var/maximum_energy_delta = max(0, min(A.temperature * A.heat_capacity() * A.group_multiplier, B.temperature * B.heat_capacity() * B.group_multiplier))
|
||||
if(maximum_energy_delta > abs(energy_delta))
|
||||
if(energy_delta < 0)
|
||||
maximum_energy_delta *= -1
|
||||
energy_delta = maximum_energy_delta
|
||||
|
||||
A.temperature -= energy_delta / (A.heat_capacity() * A.group_multiplier)
|
||||
B.temperature += energy_delta / (B.heat_capacity() * B.group_multiplier)
|
||||
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
|
||||
Overview:
|
||||
The connection_manager class stores connections in each cardinal direction on a turf.
|
||||
It isn't always present if a turf has no connections, check if(connections) before using.
|
||||
Contains procs for mass manipulation of connection data.
|
||||
|
||||
Class Vars:
|
||||
|
||||
NSEWUD - Connections to this turf in each cardinal direction.
|
||||
|
||||
Class Procs:
|
||||
|
||||
get(d)
|
||||
Returns the connection (if any) in this direction.
|
||||
Preferable to accessing the connection directly because it checks validity.
|
||||
|
||||
place(connection/c, d)
|
||||
Called by air_master.connect(). Sets the connection in the specified direction to c.
|
||||
|
||||
update_all()
|
||||
Called after turf/update_air_properties(). Updates the validity of all connections on this turf.
|
||||
|
||||
erase_all()
|
||||
Called when the turf is changed with ChangeTurf(). Erases all existing connections.
|
||||
|
||||
check(connection/c)
|
||||
Checks for connection validity. It's possible to have a reference to a connection that has been erased.
|
||||
|
||||
|
||||
*/
|
||||
|
||||
/turf/var/tmp/connection_manager/connections
|
||||
|
||||
/connection_manager/var/connection/N
|
||||
/connection_manager/var/connection/S
|
||||
/connection_manager/var/connection/E
|
||||
/connection_manager/var/connection/W
|
||||
|
||||
#ifdef ZLEVELS
|
||||
/connection_manager/var/connection/U
|
||||
/connection_manager/var/connection/D
|
||||
#endif
|
||||
|
||||
/connection_manager/proc/get(d)
|
||||
switch(d)
|
||||
if(NORTH)
|
||||
if(check(N)) return N
|
||||
else return null
|
||||
if(SOUTH)
|
||||
if(check(S)) return S
|
||||
else return null
|
||||
if(EAST)
|
||||
if(check(E)) return E
|
||||
else return null
|
||||
if(WEST)
|
||||
if(check(W)) return W
|
||||
else return null
|
||||
|
||||
#ifdef ZLEVELS
|
||||
if(UP)
|
||||
if(check(U)) return U
|
||||
else return null
|
||||
if(DOWN)
|
||||
if(check(D)) return D
|
||||
else return null
|
||||
#endif
|
||||
|
||||
/connection_manager/proc/place(connection/c, d)
|
||||
switch(d)
|
||||
if(NORTH) N = c
|
||||
if(SOUTH) S = c
|
||||
if(EAST) E = c
|
||||
if(WEST) W = c
|
||||
|
||||
#ifdef ZLEVELS
|
||||
if(UP) U = c
|
||||
if(DOWN) D = c
|
||||
#endif
|
||||
|
||||
/connection_manager/proc/update_all()
|
||||
if(check(N)) N.update()
|
||||
if(check(S)) S.update()
|
||||
if(check(E)) E.update()
|
||||
if(check(W)) W.update()
|
||||
#ifdef ZLEVELS
|
||||
if(check(U)) U.update()
|
||||
if(check(D)) D.update()
|
||||
#endif
|
||||
|
||||
/connection_manager/proc/erase_all()
|
||||
if(check(N)) N.erase()
|
||||
if(check(S)) S.erase()
|
||||
if(check(E)) E.erase()
|
||||
if(check(W)) W.erase()
|
||||
#ifdef ZLEVELS
|
||||
if(check(U)) U.erase()
|
||||
if(check(D)) D.erase()
|
||||
#endif
|
||||
|
||||
/connection_manager/proc/check(connection/c)
|
||||
return c && c.valid()
|
||||
@@ -0,0 +1,321 @@
|
||||
var/datum/controller/air_system/air_master
|
||||
|
||||
var/tick_multiplier = 2
|
||||
|
||||
/*
|
||||
|
||||
Overview:
|
||||
The air controller does everything. There are tons of procs in here.
|
||||
|
||||
Class Vars:
|
||||
zones - All zones currently holding one or more turfs.
|
||||
edges - All processing edges.
|
||||
|
||||
tiles_to_update - Tiles scheduled to update next tick.
|
||||
zones_to_update - Zones which have had their air changed and need air archival.
|
||||
active_hotspots - All processing fire objects.
|
||||
|
||||
active_zones - The number of zones which were archived last tick. Used in debug verbs.
|
||||
next_id - The next UID to be applied to a zone. Mostly useful for debugging purposes as zones do not need UIDs to function.
|
||||
|
||||
Class Procs:
|
||||
|
||||
mark_for_update(turf/T)
|
||||
Adds the turf to the update list. When updated, update_air_properties() will be called.
|
||||
When stuff changes that might affect airflow, call this. It's basically the only thing you need.
|
||||
|
||||
add_zone(zone/Z) and remove_zone(zone/Z)
|
||||
Adds zones to the zones list. Does not mark them for update.
|
||||
|
||||
air_blocked(turf/A, turf/B)
|
||||
Returns a bitflag consisting of:
|
||||
AIR_BLOCKED - The connection between turfs is physically blocked. No air can pass.
|
||||
ZONE_BLOCKED - There is a door between the turfs, so zones cannot cross. Air may or may not be permeable.
|
||||
|
||||
has_valid_zone(turf/T)
|
||||
Checks the presence and validity of T's zone.
|
||||
May be called on unsimulated turfs, returning 0.
|
||||
|
||||
merge(zone/A, zone/B)
|
||||
Called when zones have a direct connection and equivalent pressure and temperature.
|
||||
Merges the zones to create a single zone.
|
||||
|
||||
connect(turf/simulated/A, turf/B)
|
||||
Called by turf/update_air_properties(). The first argument must be simulated.
|
||||
Creates a connection between A and B.
|
||||
|
||||
mark_zone_update(zone/Z)
|
||||
Adds zone to the update list. Unlike mark_for_update(), this one is called automatically whenever
|
||||
air is returned from a simulated turf.
|
||||
|
||||
equivalent_pressure(zone/A, zone/B)
|
||||
Currently identical to A.air.compare(B.air). Returns 1 when directly connected zones are ready to be merged.
|
||||
|
||||
get_edge(zone/A, zone/B)
|
||||
get_edge(zone/A, turf/B)
|
||||
Gets a valid connection_edge between A and B, creating a new one if necessary.
|
||||
|
||||
has_same_air(turf/A, turf/B)
|
||||
Used to determine if an unsimulated edge represents a specific turf.
|
||||
Simulated edges use connection_edge/contains_zone() for the same purpose.
|
||||
Returns 1 if A has identical gases and temperature to B.
|
||||
|
||||
remove_edge(connection_edge/edge)
|
||||
Called when an edge is erased. Removes it from processing.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
//Geometry lists
|
||||
/datum/controller/air_system/var/list/zones = list()
|
||||
/datum/controller/air_system/var/list/edges = list()
|
||||
|
||||
//Geometry updates lists
|
||||
/datum/controller/air_system/var/list/tiles_to_update = list()
|
||||
/datum/controller/air_system/var/list/zones_to_update = list()
|
||||
/datum/controller/air_system/var/list/active_hotspots = list()
|
||||
|
||||
/datum/controller/air_system/var/active_zones = 0
|
||||
|
||||
/datum/controller/air_system/var/current_cycle = 0
|
||||
/datum/controller/air_system/var/update_delay = 5 //How long between check should it try to process atmos again.
|
||||
/datum/controller/air_system/var/failed_ticks = 0 //How many ticks have runtimed?
|
||||
|
||||
/datum/controller/air_system/var/tick_progress = 0
|
||||
|
||||
/datum/controller/air_system/var/next_id = 1 //Used to keep track of zone UIDs.
|
||||
|
||||
/datum/controller/air_system/proc/Setup()
|
||||
//Purpose: Call this at the start to setup air groups geometry
|
||||
// (Warning: Very processor intensive but only must be done once per round)
|
||||
//Called by: Gameticker/Master controller
|
||||
//Inputs: None.
|
||||
//Outputs: None.
|
||||
|
||||
#ifndef ZASDBG
|
||||
set background = 1
|
||||
#endif
|
||||
|
||||
world << "\red \b Processing Geometry..."
|
||||
sleep(-1)
|
||||
|
||||
var/start_time = world.timeofday
|
||||
|
||||
var/simulated_turf_count = 0
|
||||
|
||||
for(var/turf/simulated/S in world)
|
||||
simulated_turf_count++
|
||||
S.update_air_properties()
|
||||
|
||||
world << {"<font color='red'><b>Geometry initialized in [round(0.1*(world.timeofday-start_time),0.1)] seconds.</b>
|
||||
Total Simulated Turfs: [simulated_turf_count]
|
||||
Total Zones: [zones.len]
|
||||
Total Unsimulated Turfs: [world.maxx*world.maxy*world.maxz - simulated_turf_count]</font>"}
|
||||
|
||||
// spawn Start()
|
||||
|
||||
|
||||
/datum/controller/air_system/proc/Start()
|
||||
//Purpose: This is kicked off by the master controller, and controls the processing of all atmosphere.
|
||||
//Called by: Master controller
|
||||
//Inputs: None.
|
||||
//Outputs: None.
|
||||
|
||||
#ifndef ZASDBG
|
||||
set background = 1
|
||||
#endif
|
||||
|
||||
while(1)
|
||||
Tick()
|
||||
sleep(max(5,update_delay*tick_multiplier))
|
||||
|
||||
|
||||
/datum/controller/air_system/proc/Tick()
|
||||
. = 1 //Set the default return value, for runtime detection.
|
||||
|
||||
current_cycle++
|
||||
|
||||
//If there are tiles to update, do so.
|
||||
tick_progress = "updating turf properties"
|
||||
|
||||
var/list/updating
|
||||
|
||||
if(tiles_to_update.len)
|
||||
updating = tiles_to_update
|
||||
tiles_to_update = list()
|
||||
|
||||
#ifdef ZASDBG
|
||||
var/updated = 0
|
||||
#endif
|
||||
for(var/turf/T in updating)
|
||||
T.update_air_properties()
|
||||
T.post_update_air_properties()
|
||||
T.needs_air_update = 0
|
||||
#ifdef ZASDBG
|
||||
T.overlays -= mark
|
||||
updated++
|
||||
#endif
|
||||
//sleep(1)
|
||||
|
||||
#ifdef ZASDBG
|
||||
if(updated != updating.len)
|
||||
tick_progress = "[updating.len - updated] tiles left unupdated."
|
||||
world << "\red [tick_progress]"
|
||||
. = 0
|
||||
#endif
|
||||
|
||||
//Where gas exchange happens.
|
||||
if(.)
|
||||
tick_progress = "processing edges"
|
||||
|
||||
for(var/connection_edge/edge in edges)
|
||||
edge.tick()
|
||||
|
||||
//Process fires.
|
||||
if(.)
|
||||
tick_progress = "processing fire"
|
||||
|
||||
for(var/obj/fire/fire in active_hotspots)
|
||||
fire.process()
|
||||
|
||||
//Process zones.
|
||||
if(.)
|
||||
tick_progress = "updating zones"
|
||||
|
||||
active_zones = zones_to_update.len
|
||||
if(zones_to_update.len)
|
||||
updating = zones_to_update
|
||||
zones_to_update = list()
|
||||
for(var/zone/zone in updating)
|
||||
zone.tick()
|
||||
zone.needs_update = 0
|
||||
|
||||
if(.)
|
||||
tick_progress = "success"
|
||||
|
||||
/datum/controller/air_system/proc/add_zone(zone/z)
|
||||
zones.Add(z)
|
||||
z.name = "Zone [next_id++]"
|
||||
mark_zone_update(z)
|
||||
|
||||
/datum/controller/air_system/proc/remove_zone(zone/z)
|
||||
zones.Remove(z)
|
||||
|
||||
/datum/controller/air_system/proc/air_blocked(turf/A, turf/B)
|
||||
#ifdef ZASDBG
|
||||
ASSERT(isturf(A))
|
||||
ASSERT(isturf(B))
|
||||
#endif
|
||||
var/ablock = A.c_airblock(B)
|
||||
if(ablock == BLOCKED) return BLOCKED
|
||||
return ablock | B.c_airblock(A)
|
||||
|
||||
/datum/controller/air_system/proc/has_valid_zone(turf/simulated/T)
|
||||
#ifdef ZASDBG
|
||||
ASSERT(istype(T))
|
||||
#endif
|
||||
return istype(T) && T.zone && !T.zone.invalid
|
||||
|
||||
/datum/controller/air_system/proc/merge(zone/A, zone/B)
|
||||
#ifdef ZASDBG
|
||||
ASSERT(istype(A))
|
||||
ASSERT(istype(B))
|
||||
ASSERT(!A.invalid)
|
||||
ASSERT(!B.invalid)
|
||||
ASSERT(A != B)
|
||||
#endif
|
||||
if(A.contents.len < B.contents.len)
|
||||
A.c_merge(B)
|
||||
mark_zone_update(B)
|
||||
else
|
||||
B.c_merge(A)
|
||||
mark_zone_update(A)
|
||||
|
||||
/datum/controller/air_system/proc/connect(turf/simulated/A, turf/simulated/B)
|
||||
#ifdef ZASDBG
|
||||
ASSERT(istype(A))
|
||||
ASSERT(isturf(B))
|
||||
ASSERT(A.zone)
|
||||
ASSERT(!A.zone.invalid)
|
||||
//ASSERT(B.zone)
|
||||
ASSERT(A != B)
|
||||
#endif
|
||||
|
||||
var/block = air_master.air_blocked(A,B)
|
||||
if(block & AIR_BLOCKED) return
|
||||
|
||||
var/direct = !(block & ZONE_BLOCKED)
|
||||
var/space = !istype(B)
|
||||
|
||||
if(direct && !space)
|
||||
if(equivalent_pressure(A.zone,B.zone) || current_cycle == 0)
|
||||
merge(A.zone,B.zone)
|
||||
return
|
||||
|
||||
var
|
||||
a_to_b = get_dir(A,B)
|
||||
b_to_a = get_dir(B,A)
|
||||
|
||||
if(!A.connections) A.connections = new
|
||||
if(!B.connections) B.connections = new
|
||||
|
||||
if(A.connections.get(a_to_b)) return
|
||||
if(B.connections.get(b_to_a)) return
|
||||
if(!space)
|
||||
if(A.zone == B.zone) return
|
||||
|
||||
|
||||
var/connection/c = new /connection(A,B)
|
||||
|
||||
A.connections.place(c, a_to_b)
|
||||
B.connections.place(c, b_to_a)
|
||||
|
||||
if(direct) c.mark_direct()
|
||||
|
||||
/datum/controller/air_system/proc/mark_for_update(turf/T)
|
||||
#ifdef ZASDBG
|
||||
ASSERT(isturf(T))
|
||||
#endif
|
||||
if(T.needs_air_update) return
|
||||
tiles_to_update |= T
|
||||
#ifdef ZASDBG
|
||||
T.overlays += mark
|
||||
#endif
|
||||
T.needs_air_update = 1
|
||||
|
||||
/datum/controller/air_system/proc/mark_zone_update(zone/Z)
|
||||
#ifdef ZASDBG
|
||||
ASSERT(istype(Z))
|
||||
#endif
|
||||
if(Z.needs_update) return
|
||||
zones_to_update.Add(Z)
|
||||
Z.needs_update = 1
|
||||
|
||||
/datum/controller/air_system/proc/equivalent_pressure(zone/A, zone/B)
|
||||
return A.air.compare(B.air)
|
||||
|
||||
/datum/controller/air_system/proc/get_edge(zone/A, zone/B)
|
||||
|
||||
if(istype(B))
|
||||
for(var/connection_edge/zone/edge in A.edges)
|
||||
if(edge.contains_zone(B)) return edge
|
||||
var/connection_edge/edge = new/connection_edge/zone(A,B)
|
||||
edges.Add(edge)
|
||||
return edge
|
||||
else
|
||||
for(var/connection_edge/unsimulated/edge in A.edges)
|
||||
if(has_same_air(edge.B,B)) return edge
|
||||
var/connection_edge/edge = new/connection_edge/unsimulated(A,B)
|
||||
edges.Add(edge)
|
||||
return edge
|
||||
|
||||
/datum/controller/air_system/proc/has_same_air(turf/A, turf/B)
|
||||
if(A.oxygen != B.oxygen) return 0
|
||||
if(A.nitrogen != B.nitrogen) return 0
|
||||
if(A.toxins != B.toxins) return 0
|
||||
if(A.carbon_dioxide != B.carbon_dioxide) return 0
|
||||
if(A.temperature != B.temperature) return 0
|
||||
return 1
|
||||
|
||||
/datum/controller/air_system/proc/remove_edge(connection/c)
|
||||
edges.Remove(c)
|
||||
+17
-216
@@ -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."
|
||||
/connection_edge/var/dbg_out = 0
|
||||
|
||||
else
|
||||
src << "Failed to process! ([air_master.tick_progress])"
|
||||
/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
|
||||
|
||||
|
||||
client/proc/Zone_Info(turf/T as null|turf)
|
||||
set category = "Debug"
|
||||
if(T)
|
||||
if(T.zone)
|
||||
T.zone.DebugDisplay(src)
|
||||
else
|
||||
mob << "No zone here."
|
||||
else
|
||||
if(zone_debug_images)
|
||||
for(var/zone in zone_debug_images)
|
||||
images -= zone_debug_images[zone]
|
||||
zone_debug_images = null
|
||||
|
||||
client/var/list/zone_debug_images
|
||||
|
||||
client/proc/Test_ZAS_Connection(var/turf/simulated/T as turf)
|
||||
set category = "Debug"
|
||||
if(!istype(T))
|
||||
return
|
||||
|
||||
var/direction_list = list(\
|
||||
"North" = NORTH,\
|
||||
"South" = SOUTH,\
|
||||
"East" = EAST,\
|
||||
"West" = WEST,\
|
||||
"N/A" = null)
|
||||
var/direction = input("What direction do you wish to test?","Set direction") as null|anything in direction_list
|
||||
if(!direction)
|
||||
return
|
||||
|
||||
if(direction == "N/A")
|
||||
if(T.CanPass(null, T, 0,0))
|
||||
mob << "The turf can pass air! :D"
|
||||
else
|
||||
mob << "No air passage :x"
|
||||
return
|
||||
|
||||
var/turf/simulated/other_turf = get_step(T, direction_list[direction])
|
||||
if(!istype(other_turf))
|
||||
return
|
||||
|
||||
var/pass_directions = T.CanPass(null, other_turf, 0, 0) + 2 * other_turf.CanPass(null, T, 0, 0)
|
||||
|
||||
switch(pass_directions)
|
||||
if(0)
|
||||
mob << "Neither turf can connect. :("
|
||||
|
||||
if(1)
|
||||
mob << "The initial turf only can connect. :\\"
|
||||
|
||||
if(2)
|
||||
mob << "The other turf can connect, but not the initial turf. :/"
|
||||
|
||||
if(3)
|
||||
mob << "Both turfs can connect! :)"
|
||||
|
||||
|
||||
zone/proc/DebugDisplay(client/client)
|
||||
if(!istype(client))
|
||||
return
|
||||
|
||||
if(!dbg_output)
|
||||
dbg_output = 1 //Don't want to be spammed when someone investigates a zone...
|
||||
|
||||
if(!client.zone_debug_images)
|
||||
client.zone_debug_images = list()
|
||||
|
||||
var/list/current_zone_images = list()
|
||||
|
||||
for(var/turf/T in contents)
|
||||
current_zone_images += image('icons/misc/debug_group.dmi', T, null, TURF_LAYER)
|
||||
|
||||
for(var/turf/space/S in unsimulated_tiles)
|
||||
current_zone_images += image('icons/misc/debug_space.dmi', S, null, TURF_LAYER)
|
||||
|
||||
client << "<u>Zone Air Contents</u>"
|
||||
client << "Oxygen: [air.oxygen]"
|
||||
client << "Nitrogen: [air.nitrogen]"
|
||||
client << "Plasma: [air.toxins]"
|
||||
client << "Carbon Dioxide: [air.carbon_dioxide]"
|
||||
client << "Temperature: [air.temperature] K"
|
||||
client << "Heat Energy: [air.temperature * air.heat_capacity()] J"
|
||||
client << "Pressure: [air.return_pressure()] KPa"
|
||||
client << ""
|
||||
client << "Space Tiles: [length(unsimulated_tiles)]"
|
||||
client << "Movable Objects: [length(movables())]"
|
||||
client << "<u>Connections: [length(connections)]</u>"
|
||||
|
||||
for(var/connection/C in connections)
|
||||
client << "\ref[C] [C.A] --> [C.B] [(C.indirect?"Open":"Closed")]"
|
||||
current_zone_images += image('icons/misc/debug_connect.dmi', C.A, null, TURF_LAYER)
|
||||
current_zone_images += image('icons/misc/debug_connect.dmi', C.B, null, TURF_LAYER)
|
||||
|
||||
client << "Connected Zones:"
|
||||
for(var/zone/zone in connected_zones)
|
||||
client << "\ref[zone] [zone] - [connected_zones[zone]] (Connected)"
|
||||
|
||||
for(var/zone/zone in closed_connection_zones)
|
||||
client << "\ref[zone] [zone] - [closed_connection_zones[zone]] (Unconnected)"
|
||||
|
||||
for(var/C in connections)
|
||||
if(!istype(C,/connection))
|
||||
client << "[C] (Not Connection!)"
|
||||
|
||||
if(!client.zone_debug_images)
|
||||
client.zone_debug_images = list()
|
||||
client.zone_debug_images[src] = current_zone_images
|
||||
|
||||
client.images += client.zone_debug_images[src]
|
||||
|
||||
else
|
||||
dbg_output = 0
|
||||
|
||||
client.images -= client.zone_debug_images[src]
|
||||
client.zone_debug_images.Remove(src)
|
||||
|
||||
if(air_master)
|
||||
for(var/zone/Z in air_master.zones)
|
||||
if(Z.air == air && Z != src)
|
||||
var/turf/zloc = pick(Z.contents)
|
||||
client << "\red Illegal air datum shared by: [zloc.loc.name]"
|
||||
|
||||
|
||||
client/proc/TestZASRebuild()
|
||||
set category = "Debug"
|
||||
// var/turf/turf = get_turf(mob)
|
||||
var/zone/current_zone = mob.loc:zone
|
||||
if(!current_zone)
|
||||
src << "There is no zone there!"
|
||||
return
|
||||
|
||||
var/list/current_adjacents = list()
|
||||
var/list/overlays = list()
|
||||
var/adjacent_id
|
||||
var/lowest_id
|
||||
|
||||
var/list/identical_ids = list()
|
||||
var/list/turfs = current_zone.contents.Copy()
|
||||
var/current_identifier = 1
|
||||
|
||||
for(var/turf/simulated/current in turfs)
|
||||
lowest_id = null
|
||||
current_adjacents = list()
|
||||
|
||||
for(var/direction in cardinal)
|
||||
var/turf/simulated/adjacent = get_step(current, direction)
|
||||
if(!current.ZCanPass(adjacent))
|
||||
continue
|
||||
if(turfs.Find(adjacent))
|
||||
current_adjacents += adjacent
|
||||
adjacent_id = turfs[adjacent]
|
||||
|
||||
if(adjacent_id && (!lowest_id || adjacent_id < lowest_id))
|
||||
lowest_id = adjacent_id
|
||||
|
||||
if(!lowest_id)
|
||||
lowest_id = current_identifier++
|
||||
identical_ids += lowest_id
|
||||
overlays += image('icons/misc/debug_rebuild.dmi',, "[lowest_id]")
|
||||
|
||||
for(var/turf/simulated/adjacent in current_adjacents)
|
||||
adjacent_id = turfs[adjacent]
|
||||
if(adjacent_id != lowest_id)
|
||||
if(adjacent_id)
|
||||
adjacent.overlays -= overlays[adjacent_id]
|
||||
identical_ids[adjacent_id] = lowest_id
|
||||
|
||||
turfs[adjacent] = lowest_id
|
||||
adjacent.overlays += overlays[lowest_id]
|
||||
|
||||
sleep(5)
|
||||
|
||||
if(turfs[current])
|
||||
current.overlays -= overlays[turfs[current]]
|
||||
turfs[current] = lowest_id
|
||||
current.overlays += overlays[lowest_id]
|
||||
sleep(5)
|
||||
|
||||
var/list/final_arrangement = list()
|
||||
|
||||
for(var/turf/simulated/current in turfs)
|
||||
current_identifier = identical_ids[turfs[current]]
|
||||
current.overlays -= overlays[turfs[current]]
|
||||
current.overlays += overlays[current_identifier]
|
||||
sleep(5)
|
||||
|
||||
if( current_identifier > final_arrangement.len )
|
||||
final_arrangement.len = current_identifier
|
||||
final_arrangement[current_identifier] = list(current)
|
||||
|
||||
else
|
||||
final_arrangement[current_identifier] += current
|
||||
|
||||
//lazy but fast
|
||||
final_arrangement.Remove(null)
|
||||
|
||||
src << "There are [final_arrangement.len] unique segments."
|
||||
|
||||
for(var/turf/current in turfs)
|
||||
current.overlays -= overlays
|
||||
|
||||
return final_arrangement
|
||||
|
||||
/client/proc/ZASSettings()
|
||||
set category = "Debug"
|
||||
|
||||
vsc.SetDefault(mob)
|
||||
proc/soft_assert(thing,fail)
|
||||
if(!thing) message_admins(fail)
|
||||
@@ -0,0 +1,236 @@
|
||||
client/proc/ZoneTick()
|
||||
set category = "Debug"
|
||||
set name = "Process Atmos"
|
||||
|
||||
var/result = air_master.Tick()
|
||||
if(result)
|
||||
src << "Sucessfully Processed."
|
||||
|
||||
else
|
||||
src << "Failed to process! ([air_master.tick_progress])"
|
||||
|
||||
|
||||
client/proc/Zone_Info(turf/T as null|turf)
|
||||
set category = "Debug"
|
||||
if(T)
|
||||
if(istype(T,/turf/simulated) && T:zone)
|
||||
T:zone:dbg_data(src)
|
||||
else
|
||||
mob << "No zone here."
|
||||
var/datum/gas_mixture/mix = T.return_air()
|
||||
mob << "[mix.return_pressure()] kPa [mix.temperature]C"
|
||||
mob << "O2: [mix.oxygen] N2: [mix.nitrogen] CO2: [mix.carbon_dioxide] TX: [mix.toxins]"
|
||||
else
|
||||
if(zone_debug_images)
|
||||
for(var/zone in zone_debug_images)
|
||||
images -= zone_debug_images[zone]
|
||||
zone_debug_images = null
|
||||
|
||||
client/var/list/zone_debug_images
|
||||
|
||||
client/proc/Test_ZAS_Connection(var/turf/simulated/T as turf)
|
||||
set category = "Debug"
|
||||
if(!istype(T))
|
||||
return
|
||||
|
||||
var/direction_list = list(\
|
||||
"North" = NORTH,\
|
||||
"South" = SOUTH,\
|
||||
"East" = EAST,\
|
||||
"West" = WEST,\
|
||||
"N/A" = null)
|
||||
var/direction = input("What direction do you wish to test?","Set direction") as null|anything in direction_list
|
||||
if(!direction)
|
||||
return
|
||||
|
||||
if(direction == "N/A")
|
||||
if(!(T.c_airblock(T) & AIR_BLOCKED))
|
||||
mob << "The turf can pass air! :D"
|
||||
else
|
||||
mob << "No air passage :x"
|
||||
return
|
||||
|
||||
var/turf/simulated/other_turf = get_step(T, direction_list[direction])
|
||||
if(!istype(other_turf))
|
||||
return
|
||||
|
||||
var/t_block = T.c_airblock(other_turf)
|
||||
var/o_block = other_turf.c_airblock(T)
|
||||
|
||||
if(o_block & AIR_BLOCKED)
|
||||
if(t_block & AIR_BLOCKED)
|
||||
mob << "Neither turf can connect. :("
|
||||
|
||||
else
|
||||
mob << "The initial turf only can connect. :\\"
|
||||
else
|
||||
if(t_block & AIR_BLOCKED)
|
||||
mob << "The other turf can connect, but not the initial turf. :/"
|
||||
|
||||
else
|
||||
mob << "Both turfs can connect! :)"
|
||||
|
||||
mob << "Additionally, \..."
|
||||
|
||||
if(o_block & ZONE_BLOCKED)
|
||||
if(t_block & ZONE_BLOCKED)
|
||||
mob << "neither turf can merge."
|
||||
else
|
||||
mob << "the other turf cannot merge."
|
||||
else
|
||||
if(t_block & ZONE_BLOCKED)
|
||||
mob << "the initial turf cannot merge."
|
||||
else
|
||||
mob << "both turfs can merge."
|
||||
|
||||
|
||||
/*zone/proc/DebugDisplay(client/client)
|
||||
if(!istype(client))
|
||||
return
|
||||
|
||||
if(!dbg_output)
|
||||
dbg_output = 1 //Don't want to be spammed when someone investigates a zone...
|
||||
|
||||
if(!client.zone_debug_images)
|
||||
client.zone_debug_images = list()
|
||||
|
||||
var/list/current_zone_images = list()
|
||||
|
||||
for(var/turf/T in contents)
|
||||
current_zone_images += image('icons/misc/debug_group.dmi', T, null, TURF_LAYER)
|
||||
|
||||
for(var/turf/space/S in unsimulated_tiles)
|
||||
current_zone_images += image('icons/misc/debug_space.dmi', S, null, TURF_LAYER)
|
||||
|
||||
client << "<u>Zone Air Contents</u>"
|
||||
client << "Oxygen: [air.oxygen]"
|
||||
client << "Nitrogen: [air.nitrogen]"
|
||||
client << "Plasma: [air.toxins]"
|
||||
client << "Carbon Dioxide: [air.carbon_dioxide]"
|
||||
client << "Temperature: [air.temperature] K"
|
||||
client << "Heat Energy: [air.temperature * air.heat_capacity()] J"
|
||||
client << "Pressure: [air.return_pressure()] KPa"
|
||||
client << ""
|
||||
client << "Space Tiles: [length(unsimulated_tiles)]"
|
||||
client << "Movable Objects: [length(movables())]"
|
||||
client << "<u>Connections: [length(connections)]</u>"
|
||||
|
||||
for(var/connection/C in connections)
|
||||
client << "\ref[C] [C.A] --> [C.B] [(C.indirect?"Open":"Closed")]"
|
||||
current_zone_images += image('icons/misc/debug_connect.dmi', C.A, null, TURF_LAYER)
|
||||
current_zone_images += image('icons/misc/debug_connect.dmi', C.B, null, TURF_LAYER)
|
||||
|
||||
client << "Connected Zones:"
|
||||
for(var/zone/zone in connected_zones)
|
||||
client << "\ref[zone] [zone] - [connected_zones[zone]] (Connected)"
|
||||
|
||||
for(var/zone/zone in closed_connection_zones)
|
||||
client << "\ref[zone] [zone] - [closed_connection_zones[zone]] (Unconnected)"
|
||||
|
||||
for(var/C in connections)
|
||||
if(!istype(C,/connection))
|
||||
client << "[C] (Not Connection!)"
|
||||
|
||||
if(!client.zone_debug_images)
|
||||
client.zone_debug_images = list()
|
||||
client.zone_debug_images[src] = current_zone_images
|
||||
|
||||
client.images += client.zone_debug_images[src]
|
||||
|
||||
else
|
||||
dbg_output = 0
|
||||
|
||||
client.images -= client.zone_debug_images[src]
|
||||
client.zone_debug_images.Remove(src)
|
||||
|
||||
if(air_master)
|
||||
for(var/zone/Z in air_master.zones)
|
||||
if(Z.air == air && Z != src)
|
||||
var/turf/zloc = pick(Z.contents)
|
||||
client << "\red Illegal air datum shared by: [zloc.loc.name]"*/
|
||||
|
||||
|
||||
/*client/proc/TestZASRebuild()
|
||||
set category = "Debug"
|
||||
// var/turf/turf = get_turf(mob)
|
||||
var/zone/current_zone = mob.loc:zone
|
||||
if(!current_zone)
|
||||
src << "There is no zone there!"
|
||||
return
|
||||
|
||||
var/list/current_adjacents = list()
|
||||
var/list/overlays = list()
|
||||
var/adjacent_id
|
||||
var/lowest_id
|
||||
|
||||
var/list/identical_ids = list()
|
||||
var/list/turfs = current_zone.contents.Copy()
|
||||
var/current_identifier = 1
|
||||
|
||||
for(var/turf/simulated/current in turfs)
|
||||
lowest_id = null
|
||||
current_adjacents = list()
|
||||
|
||||
for(var/direction in cardinal)
|
||||
var/turf/simulated/adjacent = get_step(current, direction)
|
||||
if(!current.ZCanPass(adjacent))
|
||||
continue
|
||||
if(turfs.Find(adjacent))
|
||||
current_adjacents += adjacent
|
||||
adjacent_id = turfs[adjacent]
|
||||
|
||||
if(adjacent_id && (!lowest_id || adjacent_id < lowest_id))
|
||||
lowest_id = adjacent_id
|
||||
|
||||
if(!lowest_id)
|
||||
lowest_id = current_identifier++
|
||||
identical_ids += lowest_id
|
||||
overlays += image('icons/misc/debug_rebuild.dmi',, "[lowest_id]")
|
||||
|
||||
for(var/turf/simulated/adjacent in current_adjacents)
|
||||
adjacent_id = turfs[adjacent]
|
||||
if(adjacent_id != lowest_id)
|
||||
if(adjacent_id)
|
||||
adjacent.overlays -= overlays[adjacent_id]
|
||||
identical_ids[adjacent_id] = lowest_id
|
||||
|
||||
turfs[adjacent] = lowest_id
|
||||
adjacent.overlays += overlays[lowest_id]
|
||||
|
||||
sleep(5)
|
||||
|
||||
if(turfs[current])
|
||||
current.overlays -= overlays[turfs[current]]
|
||||
turfs[current] = lowest_id
|
||||
current.overlays += overlays[lowest_id]
|
||||
sleep(5)
|
||||
|
||||
var/list/final_arrangement = list()
|
||||
|
||||
for(var/turf/simulated/current in turfs)
|
||||
current_identifier = identical_ids[turfs[current]]
|
||||
current.overlays -= overlays[turfs[current]]
|
||||
current.overlays += overlays[current_identifier]
|
||||
sleep(5)
|
||||
|
||||
if( current_identifier > final_arrangement.len )
|
||||
final_arrangement.len = current_identifier
|
||||
final_arrangement[current_identifier] = list(current)
|
||||
|
||||
else
|
||||
final_arrangement[current_identifier] += current
|
||||
|
||||
//lazy but fast
|
||||
final_arrangement.Remove(null)
|
||||
|
||||
src << "There are [final_arrangement.len] unique segments."
|
||||
|
||||
for(var/turf/current in turfs)
|
||||
current.overlays -= overlays
|
||||
|
||||
return final_arrangement*/
|
||||
|
||||
client/proc/ZASSettings()
|
||||
set category = "Debug"
|
||||
|
||||
vsc.SetDefault(mob)
|
||||
@@ -1,356 +0,0 @@
|
||||
/*
|
||||
Overview:
|
||||
The air_master global variable is the workhorse for the system.
|
||||
|
||||
Why are you archiving data before modifying it?
|
||||
The general concept with archiving data and having each tile keep track of when they were last updated is to keep everything symmetric
|
||||
and totally independent of the order they are read in an update cycle.
|
||||
This prevents abnormalities like air/fire spreading rapidly in one direction and super slowly in the other.
|
||||
|
||||
Why not just archive everything and then calculate?
|
||||
Efficiency. While a for-loop that goes through all tils and groups to archive their information before doing any calculations seems simple, it is
|
||||
slightly less efficient than the archive-before-modify/read method.
|
||||
|
||||
Why is there a cycle check for calculating data as well?
|
||||
This ensures that every connection between group-tile, tile-tile, and group-group is only evaluated once per loop.
|
||||
|
||||
|
||||
|
||||
|
||||
Important variables:
|
||||
air_master.groups_to_rebuild (list)
|
||||
A list of air groups that have had their geometry occluded and thus may need to be split in half.
|
||||
A set of adjacent groups put in here will join together if validly connected.
|
||||
This is done before air system calculations for a cycle.
|
||||
air_master.tiles_to_update (list)
|
||||
Turfs that are in this list have their border data updated before the next air calculations for a cycle.
|
||||
Place turfs in this list rather than call the proc directly to prevent race conditions
|
||||
|
||||
turf/simulated.archive() and datum/air_group.archive()
|
||||
This stores all data for.
|
||||
If you modify, make sure to update the archived_cycle to prevent race conditions and maintain symmetry
|
||||
|
||||
atom/CanPass(atom/movable/mover, turf/target, height, air_group)
|
||||
returns 1 for allow pass and 0 for deny pass
|
||||
Turfs automatically call this for all objects/mobs in its turf.
|
||||
This is called both as source.CanPass(target, height, air_group)
|
||||
and target.CanPass(source, height, air_group)
|
||||
|
||||
Cases for the parameters
|
||||
1. This is called with args (mover, location, height>0, air_group=0) for normal objects.
|
||||
2. This is called with args (null, location, height=0, air_group=0) for flowing air.
|
||||
3. This is called with args (null, location, height=?, air_group=1) for determining group boundaries.
|
||||
|
||||
Cases 2 and 3 would be different for doors or other objects open and close fairly often.
|
||||
(Case 3 would return 0 always while Case 2 would return 0 only when the door is open)
|
||||
This prevents the necessity of re-evaluating group geometry every time a door opens/closes.
|
||||
|
||||
|
||||
Important Procedures
|
||||
air_master.process()
|
||||
This first processes the air_master update/rebuild lists then processes all groups and tiles for air calculations
|
||||
|
||||
*/
|
||||
|
||||
var/tick_multiplier = 2
|
||||
|
||||
atom/proc/CanPass(atom/movable/mover, turf/target, height=1.5, air_group = 0)
|
||||
//Purpose: Determines if the object (or airflow) can pass this atom.
|
||||
//Called by: Movement, airflow.
|
||||
//Inputs: The moving atom (optional), target turf, "height" and air group
|
||||
//Outputs: Boolean if can pass.
|
||||
|
||||
return (!density || !height || air_group)
|
||||
|
||||
/turf/CanPass(atom/movable/mover, turf/target, height=1.5,air_group=0)
|
||||
if(!target) return 0
|
||||
|
||||
if(istype(mover)) // turf/Enter(...) will perform more advanced checks
|
||||
return !density
|
||||
|
||||
else // Now, doing more detailed checks for air movement and air group formation
|
||||
if(target.blocks_air||blocks_air)
|
||||
return 0
|
||||
|
||||
for(var/obj/obstacle in src)
|
||||
if(!obstacle.CanPass(mover, target, height, air_group))
|
||||
return 0
|
||||
if(target != src)
|
||||
for(var/obj/obstacle in target)
|
||||
if(!obstacle.CanPass(mover, src, height, air_group))
|
||||
return 0
|
||||
|
||||
return 1
|
||||
|
||||
|
||||
var/datum/controller/air_system/air_master
|
||||
|
||||
/datum/controller/air_system
|
||||
//Geometry lists
|
||||
var/list/turfs_with_connections = list()
|
||||
var/list/active_hotspots = list()
|
||||
|
||||
//Special functions lists
|
||||
var/reconsidering_zones = FALSE
|
||||
var/list/tiles_to_reconsider_zones = list()
|
||||
var/list/tiles_to_reconsider_alternate
|
||||
|
||||
//Geometry updates lists
|
||||
var/updating_tiles = FALSE
|
||||
var/list/tiles_to_update = list()
|
||||
var/list/tiles_to_update_alternate
|
||||
|
||||
var/checking_connections = FALSE
|
||||
var/list/connections_to_check = list()
|
||||
var/list/connections_to_check_alternate
|
||||
|
||||
var/list/potential_intrazone_connections = list()
|
||||
|
||||
//Zone lists
|
||||
var/list/active_zones = list()
|
||||
var/list/zones_needing_rebuilt = list()
|
||||
var/list/zones = list()
|
||||
|
||||
|
||||
var/current_cycle = 0
|
||||
var/update_delay = 5 //How long between check should it try to process atmos again.
|
||||
var/failed_ticks = 0 //How many ticks have runtimed?
|
||||
|
||||
var/tick_progress = 0
|
||||
|
||||
|
||||
/datum/controller/air_system/proc/Setup()
|
||||
//Purpose: Call this at the start to setup air groups geometry
|
||||
// (Warning: Very processor intensive but only must be done once per round)
|
||||
//Called by: Gameticker/Master controller
|
||||
//Inputs: None.
|
||||
//Outputs: None.
|
||||
|
||||
set background = 1
|
||||
world << "\red \b Processing Geometry..."
|
||||
sleep(-1)
|
||||
|
||||
var/start_time = world.timeofday
|
||||
|
||||
var/simulated_turf_count = 0
|
||||
|
||||
for(var/turf/simulated/S in world)
|
||||
simulated_turf_count++
|
||||
if(!S.zone && !S.blocks_air)
|
||||
if(S.CanPass(null, S, 0, 0))
|
||||
new/zone(S)
|
||||
|
||||
for(var/turf/simulated/S in world)
|
||||
S.update_air_properties()
|
||||
|
||||
world << {"<font color='red'><b>Geometry initialized in [round(0.1*(world.timeofday-start_time),0.1)] seconds.</b>
|
||||
Total Simulated Turfs: [simulated_turf_count]
|
||||
Total Zones: [zones.len]
|
||||
Total Unsimulated Turfs: [world.maxx*world.maxy*world.maxz - simulated_turf_count]</font>"}
|
||||
|
||||
// spawn Start()
|
||||
|
||||
|
||||
/datum/controller/air_system/proc/Start()
|
||||
//Purpose: This is kicked off by the master controller, and controls the processing of all atmosphere.
|
||||
//Called by: Master controller
|
||||
//Inputs: None.
|
||||
//Outputs: None.
|
||||
|
||||
|
||||
set background = 1
|
||||
|
||||
while(1)
|
||||
if(!air_processing_killed)
|
||||
var/success = Tick() //Changed so that a runtime does not crash the ticker.
|
||||
if(!success) //Runtimed.
|
||||
failed_ticks++
|
||||
if(failed_ticks > 20)
|
||||
world << "<font color='red'><b>ERROR IN ATMOS TICKER. Killing air simulation!</font></b>"
|
||||
air_processing_killed = 1
|
||||
sleep(max(5,update_delay*tick_multiplier))
|
||||
|
||||
|
||||
/datum/controller/air_system/proc/Tick()
|
||||
. = 1 //Set the default return value, for runtime detection.
|
||||
|
||||
current_cycle++
|
||||
|
||||
//If there are tiles to update, do so.
|
||||
tick_progress = "updating turf properties"
|
||||
if(tiles_to_update.len)
|
||||
updating_tiles = TRUE
|
||||
|
||||
for(var/turf/simulated/T in tiles_to_update)
|
||||
if(. && T && !T.update_air_properties())
|
||||
//If a runtime occured, make sure we can sense it.
|
||||
. = 0
|
||||
|
||||
updating_tiles = FALSE
|
||||
|
||||
if(.)
|
||||
if(tiles_to_update_alternate)
|
||||
tiles_to_update = tiles_to_update_alternate
|
||||
tiles_to_update_alternate = null
|
||||
else
|
||||
tiles_to_update = list()
|
||||
|
||||
else if(tiles_to_update_alternate)
|
||||
tiles_to_update |= tiles_to_update_alternate
|
||||
tiles_to_update_alternate = null
|
||||
|
||||
//Rebuild zones.
|
||||
if(.)
|
||||
tick_progress = "rebuilding zones"
|
||||
if(zones_needing_rebuilt.len)
|
||||
for(var/zone/zone in zones_needing_rebuilt)
|
||||
zone.Rebuild()
|
||||
|
||||
zones_needing_rebuilt = list()
|
||||
|
||||
//Check sanity on connection objects.
|
||||
if(.)
|
||||
tick_progress = "checking/creating connections"
|
||||
if(connections_to_check.len)
|
||||
checking_connections = TRUE
|
||||
|
||||
for(var/connection/C in connections_to_check)
|
||||
C.Cleanup()
|
||||
|
||||
for(var/turf/simulated/turf_1 in potential_intrazone_connections)
|
||||
for(var/turf/simulated/turf_2 in potential_intrazone_connections[turf_1])
|
||||
|
||||
if(!turf_1.zone || !turf_2.zone)
|
||||
continue
|
||||
|
||||
if(turf_1.zone == turf_2.zone)
|
||||
continue
|
||||
|
||||
var/should_skip = FALSE
|
||||
if(turf_1 in air_master.turfs_with_connections)
|
||||
|
||||
for(var/connection/C in turfs_with_connections[turf_1])
|
||||
if(C.B == turf_2 || C.A == turf_2)
|
||||
should_skip = TRUE
|
||||
break
|
||||
if(should_skip)
|
||||
continue
|
||||
|
||||
new /connection(turf_1, turf_2)
|
||||
|
||||
checking_connections = FALSE
|
||||
|
||||
potential_intrazone_connections = list()
|
||||
|
||||
if(connections_to_check_alternate)
|
||||
connections_to_check = connections_to_check_alternate
|
||||
connections_to_check_alternate = null
|
||||
else
|
||||
connections_to_check = list()
|
||||
|
||||
//Process zones.
|
||||
if(.)
|
||||
tick_progress = "processing zones"
|
||||
for(var/zone/Z in active_zones)
|
||||
if(Z.last_update < current_cycle)
|
||||
var/output = Z.process()
|
||||
if(Z)
|
||||
Z.last_update = current_cycle
|
||||
if(. && Z && !output)
|
||||
. = 0
|
||||
|
||||
//Ensure tiles still have zones.
|
||||
if(.)
|
||||
tick_progress = "reconsidering zones on turfs"
|
||||
if(tiles_to_reconsider_zones.len)
|
||||
reconsidering_zones = TRUE
|
||||
|
||||
for(var/turf/simulated/T in tiles_to_reconsider_zones)
|
||||
if(!T.zone)
|
||||
new /zone(T)
|
||||
|
||||
reconsidering_zones = FALSE
|
||||
|
||||
if(tiles_to_reconsider_alternate)
|
||||
tiles_to_reconsider_zones = tiles_to_reconsider_alternate
|
||||
tiles_to_reconsider_alternate = null
|
||||
else
|
||||
tiles_to_reconsider_zones = list()
|
||||
|
||||
//Process fires.
|
||||
if(.)
|
||||
tick_progress = "processing fire"
|
||||
for(var/obj/fire/F in active_hotspots)
|
||||
if(. && F && !F.process())
|
||||
. = 0
|
||||
|
||||
if(.)
|
||||
tick_progress = "success"
|
||||
|
||||
|
||||
/datum/controller/air_system/proc/AddTurfToUpdate(turf/simulated/outdated_turf)
|
||||
var/list/tiles_to_check = list()
|
||||
|
||||
if(istype(outdated_turf))
|
||||
tiles_to_check |= outdated_turf
|
||||
|
||||
if(istype(outdated_turf, /turf))
|
||||
for(var/direction in cardinal)
|
||||
var/turf/simulated/adjacent_turf = get_step(outdated_turf, direction)
|
||||
if(istype(adjacent_turf))
|
||||
tiles_to_check |= adjacent_turf
|
||||
|
||||
if(updating_tiles)
|
||||
if(!tiles_to_update_alternate)
|
||||
tiles_to_update_alternate = tiles_to_check
|
||||
else
|
||||
tiles_to_update_alternate |= tiles_to_check
|
||||
else
|
||||
tiles_to_update |= tiles_to_check
|
||||
|
||||
|
||||
/datum/controller/air_system/proc/AddConnectionToCheck(connection/connection)
|
||||
if(checking_connections)
|
||||
if(istype(connection, /list))
|
||||
if(!connections_to_check_alternate)
|
||||
connections_to_check_alternate = connection
|
||||
|
||||
else if(!connections_to_check_alternate)
|
||||
connections_to_check_alternate = list()
|
||||
|
||||
connections_to_check_alternate |= connection
|
||||
|
||||
else
|
||||
connections_to_check |= connection
|
||||
|
||||
|
||||
/datum/controller/air_system/proc/ReconsiderTileZone(var/turf/simulated/zoneless_turf)
|
||||
if(zoneless_turf.zone)
|
||||
return
|
||||
|
||||
if(reconsidering_zones)
|
||||
if(!tiles_to_reconsider_alternate)
|
||||
tiles_to_reconsider_alternate = list()
|
||||
|
||||
tiles_to_reconsider_alternate |= zoneless_turf
|
||||
|
||||
else
|
||||
tiles_to_reconsider_zones |= zoneless_turf
|
||||
|
||||
|
||||
/datum/controller/air_system/proc/AddIntrazoneConnection(var/turf/simulated/A, var/turf/simulated/B)
|
||||
if(!istype(A) || !istype(B))
|
||||
return
|
||||
|
||||
if(A in potential_intrazone_connections)
|
||||
if(B in potential_intrazone_connections[A])
|
||||
return
|
||||
|
||||
if (B in potential_intrazone_connections)
|
||||
if(A in potential_intrazone_connections[B])
|
||||
return
|
||||
|
||||
potential_intrazone_connections[B] += A
|
||||
|
||||
else
|
||||
potential_intrazone_connections[B] = list(A)
|
||||
+1
-1
@@ -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)
|
||||
|
||||
|
||||
@@ -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)
|
||||
@@ -0,0 +1,241 @@
|
||||
/turf/simulated/var/zone/zone
|
||||
/turf/simulated/var/open_directions
|
||||
/turf/simulated/var/gas_graphic
|
||||
|
||||
/turf/var/needs_air_update = 0
|
||||
/turf/var/datum/gas_mixture/air
|
||||
|
||||
/turf/simulated/proc/set_graphic(new_graphic)
|
||||
if(isnum(new_graphic))
|
||||
if(new_graphic == 1) new_graphic = plmaster
|
||||
else if(new_graphic == 2) new_graphic = slmaster
|
||||
if(gas_graphic) overlays -= gas_graphic
|
||||
if(new_graphic) overlays += new_graphic
|
||||
gas_graphic = new_graphic
|
||||
|
||||
/turf/proc/update_air_properties()
|
||||
var/block = c_airblock(src)
|
||||
if(block & AIR_BLOCKED)
|
||||
//dbg(blocked)
|
||||
return 1
|
||||
|
||||
#ifdef ZLEVELS
|
||||
for(var/d = 1, d < 64, d *= 2)
|
||||
#else
|
||||
for(var/d = 1, d < 16, d *= 2)
|
||||
#endif
|
||||
|
||||
var/turf/unsim = get_step(src, d)
|
||||
block = unsim.c_airblock(src)
|
||||
|
||||
if(block & AIR_BLOCKED)
|
||||
//unsim.dbg(air_blocked, turn(180,d))
|
||||
continue
|
||||
|
||||
var/r_block = c_airblock(unsim)
|
||||
|
||||
if(r_block & AIR_BLOCKED)
|
||||
continue
|
||||
|
||||
if(istype(unsim, /turf/simulated))
|
||||
|
||||
var/turf/simulated/sim = unsim
|
||||
if(air_master.has_valid_zone(sim))
|
||||
|
||||
air_master.connect(sim, src)
|
||||
|
||||
/turf/simulated/update_air_properties()
|
||||
if(zone && zone.invalid)
|
||||
c_copy_air()
|
||||
zone = null //Easier than iterating through the list at the zone.
|
||||
|
||||
var/s_block = c_airblock(src)
|
||||
if(s_block & AIR_BLOCKED)
|
||||
#ifdef ZASDBG
|
||||
if(verbose) world << "Self-blocked."
|
||||
//dbg(blocked)
|
||||
#endif
|
||||
if(zone)
|
||||
var/zone/z = zone
|
||||
if(locate(/obj/machinery/door/airlock) in src) //Hacky, but prevents normal airlocks from rebuilding zones all the time
|
||||
z.remove(src)
|
||||
else
|
||||
z.rebuild()
|
||||
|
||||
return 1
|
||||
|
||||
var/previously_open = open_directions
|
||||
open_directions = 0
|
||||
|
||||
var/list/postponed
|
||||
#ifdef ZLEVELS
|
||||
for(var/d = 1, d < 64, d *= 2)
|
||||
#else
|
||||
for(var/d = 1, d < 16, d *= 2)
|
||||
#endif
|
||||
|
||||
var/turf/unsim = get_step(src, d)
|
||||
var/block = unsim.c_airblock(src)
|
||||
if(block & AIR_BLOCKED)
|
||||
|
||||
#ifdef ZASDBG
|
||||
if(verbose) world << "[d] is blocked."
|
||||
//unsim.dbg(air_blocked, turn(180,d))
|
||||
#endif
|
||||
|
||||
continue
|
||||
|
||||
var/r_block = c_airblock(unsim)
|
||||
if(r_block & AIR_BLOCKED)
|
||||
|
||||
#ifdef ZASDBG
|
||||
if(verbose) world << "[d] is blocked."
|
||||
//dbg(air_blocked, d)
|
||||
#endif
|
||||
|
||||
//Check that our zone hasn't been cut off recently.
|
||||
//This happens when windows move or are constructed. We need to rebuild.
|
||||
if((previously_open & d) && istype(unsim, /turf/simulated))
|
||||
var/turf/simulated/sim = unsim
|
||||
if(sim.zone == zone)
|
||||
zone.rebuild()
|
||||
return
|
||||
|
||||
continue
|
||||
|
||||
open_directions |= d
|
||||
|
||||
if(istype(unsim, /turf/simulated))
|
||||
|
||||
var/turf/simulated/sim = unsim
|
||||
if(air_master.has_valid_zone(sim))
|
||||
|
||||
//Might have assigned a zone, since this happens for each direction.
|
||||
if(!zone)
|
||||
|
||||
//if((block & ZONE_BLOCKED) || (r_block & ZONE_BLOCKED && !(s_block & ZONE_BLOCKED)))
|
||||
if(((block & ZONE_BLOCKED) && !(r_block & ZONE_BLOCKED)) || (r_block & ZONE_BLOCKED && !(s_block & ZONE_BLOCKED)))
|
||||
#ifdef ZASDBG
|
||||
if(verbose) world << "[d] is zone blocked."
|
||||
//dbg(zone_blocked, d)
|
||||
#endif
|
||||
|
||||
//Postpone this tile rather than exit, since a connection can still be made.
|
||||
if(!postponed) postponed = list()
|
||||
postponed.Add(sim)
|
||||
|
||||
else
|
||||
|
||||
sim.zone.add(src)
|
||||
|
||||
#ifdef ZASDBG
|
||||
dbg(assigned)
|
||||
if(verbose) world << "Added to [zone]"
|
||||
#endif
|
||||
|
||||
else if(sim.zone != zone)
|
||||
|
||||
#ifdef ZASDBG
|
||||
if(verbose) world << "Connecting to [sim.zone]"
|
||||
#endif
|
||||
|
||||
air_master.connect(src, sim)
|
||||
|
||||
|
||||
#ifdef ZASDBG
|
||||
else if(verbose) world << "[d] has same zone."
|
||||
|
||||
else if(verbose) world << "[d] has invalid zone."
|
||||
#endif
|
||||
|
||||
else
|
||||
|
||||
//Postponing connections to tiles until a zone is assured.
|
||||
if(!postponed) postponed = list()
|
||||
postponed.Add(unsim)
|
||||
|
||||
if(!air_master.has_valid_zone(src)) //Still no zone, make a new one.
|
||||
var/zone/newzone = new/zone()
|
||||
newzone.add(src)
|
||||
|
||||
#ifdef ZASDBG
|
||||
dbg(created)
|
||||
|
||||
ASSERT(zone)
|
||||
#endif
|
||||
|
||||
//At this point, a zone should have happened. If it hasn't, don't add more checks, fix the bug.
|
||||
|
||||
for(var/turf/T in postponed)
|
||||
air_master.connect(src, T)
|
||||
|
||||
/turf/proc/post_update_air_properties()
|
||||
if(connections) connections.update_all()
|
||||
|
||||
/turf/assume_air(datum/gas_mixture/giver) //use this for machines to adjust air
|
||||
del(giver)
|
||||
return 0
|
||||
|
||||
/turf/return_air()
|
||||
//Create gas mixture to hold data for passing
|
||||
var/datum/gas_mixture/GM = new
|
||||
|
||||
GM.oxygen = oxygen
|
||||
GM.carbon_dioxide = carbon_dioxide
|
||||
GM.nitrogen = nitrogen
|
||||
GM.toxins = toxins
|
||||
|
||||
GM.temperature = temperature
|
||||
GM.update_values()
|
||||
|
||||
return GM
|
||||
|
||||
/turf/remove_air(amount as num)
|
||||
var/datum/gas_mixture/GM = new
|
||||
|
||||
var/sum = oxygen + carbon_dioxide + nitrogen + toxins
|
||||
if(sum>0)
|
||||
GM.oxygen = (oxygen/sum)*amount
|
||||
GM.carbon_dioxide = (carbon_dioxide/sum)*amount
|
||||
GM.nitrogen = (nitrogen/sum)*amount
|
||||
GM.toxins = (toxins/sum)*amount
|
||||
|
||||
GM.temperature = temperature
|
||||
GM.update_values()
|
||||
|
||||
return GM
|
||||
|
||||
/turf/simulated/assume_air(datum/gas_mixture/giver)
|
||||
var/datum/gas_mixture/my_air = return_air()
|
||||
my_air.merge(giver)
|
||||
|
||||
/turf/simulated/remove_air(amount as num)
|
||||
var/datum/gas_mixture/my_air = return_air()
|
||||
return my_air.remove(amount)
|
||||
|
||||
/turf/simulated/return_air()
|
||||
if(zone)
|
||||
if(!zone.invalid)
|
||||
air_master.mark_zone_update(zone)
|
||||
return zone.air
|
||||
else
|
||||
if(!air)
|
||||
make_air()
|
||||
c_copy_air()
|
||||
return air
|
||||
else
|
||||
if(!air)
|
||||
make_air()
|
||||
return air
|
||||
|
||||
/turf/proc/make_air()
|
||||
air = new/datum/gas_mixture
|
||||
air.temperature = temperature
|
||||
air.adjust(oxygen, carbon_dioxide, nitrogen, toxins)
|
||||
air.group_multiplier = 1
|
||||
air.volume = CELL_VOLUME
|
||||
|
||||
/turf/simulated/proc/c_copy_air()
|
||||
if(!air) air = new/datum/gas_mixture
|
||||
air.copy_from(zone.air)
|
||||
air.group_multiplier = 1
|
||||
@@ -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)
|
||||
*/
|
||||
@@ -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()*/
|
||||
|
||||
@@ -0,0 +1,154 @@
|
||||
/*
|
||||
|
||||
Overview:
|
||||
Each zone is a self-contained area where gas values would be the same if tile-based equalization were run indefinitely.
|
||||
If you're unfamiliar with ZAS, FEA's air groups would have similar functionality if they didn't break in a stiff breeze.
|
||||
|
||||
Class Vars:
|
||||
name - A name of the format "Zone [#]", used for debugging.
|
||||
invalid - True if the zone has been erased and is no longer eligible for processing.
|
||||
needs_update - True if the zone has been added to the update list.
|
||||
edges - A list of edges that connect to this zone.
|
||||
air - The gas mixture that any turfs in this zone will return. Values are per-tile with a group multiplier.
|
||||
|
||||
Class Procs:
|
||||
add(turf/simulated/T)
|
||||
Adds a turf to the contents, sets its zone and merges its air.
|
||||
|
||||
remove(turf/simulated/T)
|
||||
Removes a turf, sets its zone to null and erases any gas graphics.
|
||||
Invalidates the zone if it has no more tiles.
|
||||
|
||||
c_merge(zone/into)
|
||||
Invalidates this zone and adds all its former contents to into.
|
||||
|
||||
c_invalidate()
|
||||
Marks this zone as invalid and removes it from processing.
|
||||
|
||||
rebuild()
|
||||
Invalidates the zone and marks all its former tiles for updates.
|
||||
|
||||
add_tile_air(turf/simulated/T)
|
||||
Adds the air contained in T.air to the zone's air supply. Called when adding a turf.
|
||||
|
||||
tick()
|
||||
Called only when the gas content is changed. Archives values and changes gas graphics.
|
||||
|
||||
dbg_data(mob/M)
|
||||
Sends M a printout of important figures for the zone.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
/zone/var/name
|
||||
/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)
|
||||
soft_assert(T in contents, "Lists are weird broseph")
|
||||
#endif
|
||||
contents.Remove(T)
|
||||
T.zone = null
|
||||
T.set_graphic(0)
|
||||
if(contents.len)
|
||||
air.group_multiplier = contents.len
|
||||
else
|
||||
c_invalidate()
|
||||
|
||||
/zone/proc/c_merge(zone/into)
|
||||
#ifdef ZASDBG
|
||||
ASSERT(!invalid)
|
||||
ASSERT(istype(into))
|
||||
ASSERT(into != src)
|
||||
ASSERT(!into.invalid)
|
||||
#endif
|
||||
c_invalidate()
|
||||
for(var/turf/simulated/T in contents)
|
||||
into.add(T)
|
||||
#ifdef ZASDBG
|
||||
T.dbg(merged)
|
||||
#endif
|
||||
|
||||
/zone/proc/c_invalidate()
|
||||
invalid = 1
|
||||
air_master.remove_zone(src)
|
||||
#ifdef ZASDBG
|
||||
for(var/turf/simulated/T in contents)
|
||||
T.dbg(invalid_zone)
|
||||
#endif
|
||||
|
||||
/zone/proc/rebuild()
|
||||
if(invalid) return //Short circuit for explosions where rebuild is called many times over.
|
||||
c_invalidate()
|
||||
for(var/turf/simulated/T in contents)
|
||||
//T.dbg(invalid_zone)
|
||||
T.needs_air_update = 0 //Reset the marker so that it will be added to the list.
|
||||
air_master.mark_for_update(T)
|
||||
|
||||
/zone/proc/add_tile_air(datum/gas_mixture/tile_air)
|
||||
//air.volume += CELL_VOLUME
|
||||
air.group_multiplier = 1
|
||||
air.multiply(contents.len)
|
||||
air.merge(tile_air)
|
||||
air.divide(contents.len+1)
|
||||
air.group_multiplier = contents.len+1
|
||||
|
||||
/zone/proc/tick()
|
||||
air.archive()
|
||||
if(air.check_tile_graphic())
|
||||
for(var/turf/simulated/T in contents)
|
||||
T.set_graphic(air.graphic)
|
||||
|
||||
/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 << "[E:air:return_pressure()]kPa"
|
||||
|
||||
M << "Zone Edges: [zone_edges]"
|
||||
M << "Space Edges: [space_edges] ([space_coefficient] connections)"
|
||||
|
||||
//for(var/turf/T in unsimulated_contents)
|
||||
// M << "[T] at ([T.x],[T.y])"
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
|
||||
Zone Air System:
|
||||
|
||||
This air system divides the station into impermeable areas called zones.
|
||||
When something happens, i.e. a door opening or a wall being taken down,
|
||||
zones equalize and eventually merge. Making an airtight area closes the connection again.
|
||||
|
||||
Control Flow:
|
||||
Every air tick:
|
||||
Marked turfs are updated with update_air_properties(), followed by post_update_air_properties().
|
||||
Edges, including those generated by connections in the previous step, are processed. This is where gas is exchanged.
|
||||
Fire is processed.
|
||||
Marked zones have their air archived.
|
||||
|
||||
Important Functions:
|
||||
|
||||
air_master.mark_for_update(turf)
|
||||
When stuff happens, call this. It works on everything. You basically don't need to worry about any other
|
||||
functions besides CanPass().
|
||||
|
||||
Notes for people who used ZAS before:
|
||||
There is no connected_zones anymore.
|
||||
To get the zones that are connected to a zone, use this loop:
|
||||
for(var/connection_edge/zone/edge in zone.edges)
|
||||
var/zone/connected_zone = edge.get_connected_zone(zone)
|
||||
|
||||
*/
|
||||
|
||||
//#define ZASDBG
|
||||
//#define ZLEVELS
|
||||
|
||||
#define AIR_BLOCKED 1
|
||||
#define ZONE_BLOCKED 2
|
||||
#define BLOCKED 3
|
||||
@@ -259,11 +259,28 @@ datum/controller/game_controller/proc/process_machines()
|
||||
last_thing_processed = Machine.type
|
||||
if(Machine.process() != PROCESS_KILL)
|
||||
if(Machine)
|
||||
if(Machine.use_power)
|
||||
Machine.auto_use_power()
|
||||
|
||||
// if(Machine.use_power)
|
||||
// Machine.auto_use_power()
|
||||
|
||||
i++
|
||||
continue
|
||||
machines.Cut(i,i+1)
|
||||
i=1
|
||||
while(i<=active_areas.len)
|
||||
var/area/A = active_areas[i]
|
||||
if(A.powerupdate)
|
||||
A.powerupdate -= 1
|
||||
for(var/obj/machinery/M in A)
|
||||
if(M)
|
||||
if(M.use_power)
|
||||
M.auto_use_power()
|
||||
|
||||
if(A.apc.len)
|
||||
i++
|
||||
continue
|
||||
active_areas.Cut(i,i+1)
|
||||
|
||||
|
||||
datum/controller/game_controller/proc/process_objects()
|
||||
var/i = 1
|
||||
|
||||
@@ -404,6 +404,15 @@ var/list/all_supply_groups = list("Operations","Security","Hospitality","Enginee
|
||||
containername = "Glass sheets crate"
|
||||
group = "Engineering"
|
||||
|
||||
/datum/supply_packs/wood50
|
||||
name = "50 Wooden Planks"
|
||||
contains = list(/obj/item/stack/sheet/wood)
|
||||
amount = 50
|
||||
cost = 10
|
||||
containertype = /obj/structure/closet/crate
|
||||
containername = "Wooden planks crate"
|
||||
group = "Engineering"
|
||||
|
||||
/datum/supply_packs/electrical
|
||||
name = "Electrical maintenance crate"
|
||||
contains = list(/obj/item/weapon/storage/toolbox/electrical,
|
||||
|
||||
@@ -14,6 +14,7 @@ NOTE: there are two lists of areas in the end of this file: centcom and station
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/area
|
||||
var/fire = null
|
||||
var/atmos = 1
|
||||
@@ -31,6 +32,8 @@ NOTE: there are two lists of areas in the end of this file: centcom and station
|
||||
|
||||
var/eject = null
|
||||
|
||||
var/debug = 0
|
||||
var/powerupdate = 10 //We give everything 10 ticks to settle out it's power usage.
|
||||
var/requires_power = 1
|
||||
var/always_unpowered = 0 //this gets overriden to 1 for space in area/New()
|
||||
|
||||
@@ -43,7 +46,7 @@ NOTE: there are two lists of areas in the end of this file: centcom and station
|
||||
var/used_environ = 0
|
||||
|
||||
var/has_gravity = 1
|
||||
|
||||
var/list/apc = list()
|
||||
var/no_air = null
|
||||
var/area/master // master area used for power calcluations
|
||||
// (original area before splitting due to sd_DAL)
|
||||
@@ -1085,7 +1088,6 @@ var/list/ghostteleportlocs = list()
|
||||
icon_state = "medbay3"
|
||||
music = 'sound/ambience/signal.ogg'
|
||||
|
||||
|
||||
/area/medical/biostorage
|
||||
name = "\improper Secondary Storage"
|
||||
icon_state = "medbay2"
|
||||
@@ -1111,7 +1113,7 @@ var/list/ghostteleportlocs = list()
|
||||
icon_state = "patients"
|
||||
|
||||
/area/medical/ward
|
||||
name = "\improper Medbay Patient Ward"
|
||||
name = "\improper Recovery Ward"
|
||||
icon_state = "patients"
|
||||
|
||||
/area/medical/patient_a
|
||||
@@ -1126,8 +1128,8 @@ var/list/ghostteleportlocs = list()
|
||||
name = "\improper Isolation C"
|
||||
icon_state = "patients"
|
||||
|
||||
/area/medical/iso_access
|
||||
name = "\improper Isolation Access"
|
||||
/area/medical/patient_wing
|
||||
name = "\improper Patient Wing"
|
||||
icon_state = "patients"
|
||||
|
||||
/area/medical/cmo
|
||||
@@ -1150,6 +1152,10 @@ var/list/ghostteleportlocs = list()
|
||||
name = "\improper Virology"
|
||||
icon_state = "virology"
|
||||
|
||||
/area/medical/virologyaccess
|
||||
name = "\improper Virology Access"
|
||||
icon_state = "virology"
|
||||
|
||||
/area/medical/morgue
|
||||
name = "\improper Morgue"
|
||||
icon_state = "morgue"
|
||||
@@ -1159,11 +1165,19 @@ var/list/ghostteleportlocs = list()
|
||||
icon_state = "chem"
|
||||
|
||||
/area/medical/surgery
|
||||
name = "\improper Surgery"
|
||||
name = "\improper Operating Theatre 1"
|
||||
icon_state = "surgery"
|
||||
|
||||
/area/medical/surgery2
|
||||
name = "\improper Operating Theatre 2"
|
||||
icon_state = "surgery"
|
||||
|
||||
/area/medical/surgeryobs
|
||||
name = "\improper Surgery Observation"
|
||||
name = "\improper Operation Observation Room"
|
||||
icon_state = "surgery"
|
||||
|
||||
/area/medical/surgeryprep
|
||||
name = "\improper Pre-Op Prep Room"
|
||||
icon_state = "surgery"
|
||||
|
||||
/area/medical/cryo
|
||||
@@ -1183,7 +1197,7 @@ var/list/ghostteleportlocs = list()
|
||||
icon_state = "cloning"
|
||||
|
||||
/area/medical/sleeper
|
||||
name = "\improper Medical Treatment Center"
|
||||
name = "\improper Emergency Treatment Centre"
|
||||
icon_state = "exam_room"
|
||||
|
||||
//Security
|
||||
|
||||
@@ -13,6 +13,8 @@
|
||||
master = src //moved outside the spawn(1) to avoid runtimes in lighting.dm when it references loc.loc.master ~Carn
|
||||
uid = ++global_uid
|
||||
related = list(src)
|
||||
active_areas += src
|
||||
all_areas += src
|
||||
|
||||
if(type == /area) // override defaults for space. TODO: make space areas of type /area/space rather than /area
|
||||
requires_power = 1
|
||||
@@ -220,6 +222,7 @@
|
||||
// called when power status changes
|
||||
|
||||
/area/proc/power_change()
|
||||
master.powerupdate = 2
|
||||
for(var/area/RA in related)
|
||||
for(var/obj/machinery/M in RA) // for each machine in the area
|
||||
M.power_change() // reverify power status (to update icons etc.)
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
flags = FPRINT|TABLEPASS|HEADCOVERSEYES
|
||||
armor = list(melee = 30, bullet = 10, laser = 5,energy = 5, bomb = 0, bio = 0, rad = 0)
|
||||
cold_protection = HEAD
|
||||
min_cold_protection_temperature = SPACE_HELMET_MIN_COLD_PROTECITON_TEMPERATURE
|
||||
min_cold_protection_temperature = SPACE_HELMET_MIN_COLD_PROTECTION_TEMPERATURE
|
||||
siemens_coefficient = 0
|
||||
|
||||
|
||||
|
||||
@@ -775,13 +775,9 @@ var/list/sacrificed = list()
|
||||
return
|
||||
cultist.buckled = null
|
||||
if (cultist.handcuffed)
|
||||
cultist.handcuffed.loc = cultist.loc
|
||||
cultist.handcuffed = null
|
||||
cultist.update_inv_handcuffed()
|
||||
cultist.drop_from_inventory(cultist.handcuffed)
|
||||
if (cultist.legcuffed)
|
||||
cultist.legcuffed.loc = cultist.loc
|
||||
cultist.legcuffed = null
|
||||
cultist.update_inv_legcuffed()
|
||||
cultist.drop_from_inventory(cultist.legcuffed)
|
||||
if (istype(cultist.wear_mask, /obj/item/clothing/mask/muzzle))
|
||||
cultist.u_equip(cultist.wear_mask)
|
||||
if(istype(cultist.loc, /obj/structure/closet)&&cultist.loc:welded)
|
||||
|
||||
@@ -181,10 +181,12 @@
|
||||
return
|
||||
return
|
||||
|
||||
/*
|
||||
|
||||
/obj/machinery/body_scanconsole/process() //not really used right now
|
||||
if(stat & (NOPOWER|BROKEN))
|
||||
return
|
||||
use_power(250) // power stuff
|
||||
//use_power(250) // power stuff
|
||||
|
||||
// var/mob/M //occupant
|
||||
// if (!( src.status )) //remove this
|
||||
@@ -200,6 +202,8 @@
|
||||
// src.updateDialog()
|
||||
// return
|
||||
|
||||
*/
|
||||
|
||||
|
||||
/obj/machinery/body_scanconsole/attack_paw(user as mob)
|
||||
return src.attack_hand(user)
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
var/id
|
||||
use_power = 1
|
||||
idle_power_usage = 2
|
||||
active_power_usage = 4
|
||||
active_power_usage = 5
|
||||
|
||||
/obj/machinery/meter/New()
|
||||
..()
|
||||
@@ -30,7 +30,7 @@
|
||||
icon_state = "meter0"
|
||||
return 0
|
||||
|
||||
use_power(5)
|
||||
//use_power(5)
|
||||
|
||||
var/datum/gas_mixture/environment = target.return_air()
|
||||
if(!environment)
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
/obj/machinery/biogenerator/attackby(var/obj/item/O as obj, var/mob/user as mob)
|
||||
if(istype(O, /obj/item/weapon/reagent_containers/glass))
|
||||
if(beaker)
|
||||
user << "\red The biogenerator already occuped."
|
||||
user << "\red The biogenerator is already loaded."
|
||||
else
|
||||
user.before_take_item(O)
|
||||
O.loc = src
|
||||
|
||||
@@ -273,7 +273,7 @@
|
||||
src.locked = 0
|
||||
if (!src.mess)
|
||||
icon_state = "pod_0"
|
||||
use_power(200)
|
||||
//use_power(200)
|
||||
return
|
||||
|
||||
return
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -346,4 +346,4 @@
|
||||
/obj/machinery/door/window/brigdoor/southright
|
||||
dir = SOUTH
|
||||
icon_state = "rightsecure"
|
||||
base_state = "rightsecure"
|
||||
base_state = "rightsecure"
|
||||
|
||||
@@ -448,7 +448,7 @@ obj/machinery/hydroponics/proc/mutatepest() // Until someone makes a spaceworm,
|
||||
obj/machinery/hydroponics/attackby(var/obj/item/O as obj, var/mob/user as mob)
|
||||
|
||||
//Called when mob user "attacks" it with object O
|
||||
if (istype(O, /obj/item/weapon/reagent_containers/glass/bucket))
|
||||
if (istype(O, /obj/item/weapon/reagent_containers/glass))
|
||||
var/b_amount = O.reagents.get_reagent_amount("water")
|
||||
if(b_amount > 0 && waterlevel < 100)
|
||||
if(b_amount + waterlevel > 100)
|
||||
@@ -456,17 +456,17 @@ obj/machinery/hydroponics/attackby(var/obj/item/O as obj, var/mob/user as mob)
|
||||
O.reagents.remove_reagent("water", b_amount)
|
||||
waterlevel += b_amount
|
||||
playsound(loc, 'sound/effects/slosh.ogg', 25, 1)
|
||||
user << "You fill the [src] with [b_amount] units of water."
|
||||
user << "You fill \the [src] with [b_amount] units of water."
|
||||
|
||||
// Toxicity dilutation code. The more water you put in, the lesser the toxin concentration.
|
||||
// Toxicity dilutation code. The more water you put in, the lesser the toxin concentration.
|
||||
toxic -= round(b_amount/4)
|
||||
if (toxic < 0 ) // Make sure it won't go overoboard
|
||||
if (toxic < 0 ) // Make sure it won't go overboard
|
||||
toxic = 0
|
||||
|
||||
else if(waterlevel >= 100)
|
||||
user << "\red The [src] is already full."
|
||||
user << "\red \The [src] is already full."
|
||||
else
|
||||
user << "\red The bucket is not filled with water."
|
||||
user << "\red \The [O] is not filled with water."
|
||||
updateicon()
|
||||
|
||||
else if ( istype(O, /obj/item/nutrient) )
|
||||
@@ -484,7 +484,7 @@ obj/machinery/hydroponics/attackby(var/obj/item/O as obj, var/mob/user as mob)
|
||||
if (planted)
|
||||
if (S.mode == 1)
|
||||
if(!S.reagents.total_volume)
|
||||
user << "\red The syringe is empty."
|
||||
user << "\red \The [O] is empty."
|
||||
return
|
||||
user << "\red You inject the [myseed.plantname] with a chemical solution."
|
||||
|
||||
|
||||
@@ -14,9 +14,19 @@
|
||||
var/global/max_n_of_items = 999 // Sorry but the BYOND infinite loop detector doesn't look things over 1000.
|
||||
var/icon_on = "smartfridge"
|
||||
var/icon_off = "smartfridge-off"
|
||||
var/icon_panel = "smartfridge-panel"
|
||||
var/item_quants = list()
|
||||
var/ispowered = 1 //starts powered
|
||||
var/isbroken = 0
|
||||
var/seconds_electrified = 0;
|
||||
var/shoot_inventory = 0
|
||||
var/locked = 0
|
||||
var/panel_open = 0 //Hacking a smartfridge
|
||||
var/wires = 7
|
||||
var/const/WIRE_SHOCK = 1
|
||||
var/const/WIRE_SHOOTINV = 2
|
||||
var/const/WIRE_SCANID = 3 //Only used by the secure smartfridge, but required by the cut, mend and pulse procs.
|
||||
|
||||
|
||||
/obj/machinery/smartfridge/proc/accept_check(var/obj/item/O as obj)
|
||||
if(istype(O,/obj/item/weapon/reagent_containers/food/snacks/grown/) || istype(O,/obj/item/seeds/))
|
||||
@@ -36,15 +46,76 @@
|
||||
return 1
|
||||
return 0
|
||||
|
||||
/obj/machinery/smartfridge/extract
|
||||
/obj/machinery/smartfridge/secure/extract
|
||||
name = "\improper Slime Extract Storage"
|
||||
desc = "A refrigerated storage unit for slime extracts"
|
||||
req_access_txt = "47"
|
||||
|
||||
/obj/machinery/smartfridge/extract/accept_check(var/obj/item/O as obj)
|
||||
/obj/machinery/smartfridge/secure/extract/accept_check(var/obj/item/O as obj)
|
||||
if(istype(O,/obj/item/slime_extract))
|
||||
return 1
|
||||
return 0
|
||||
|
||||
/obj/machinery/smartfridge/secure/medbay
|
||||
name = "\improper Refrigerated Medicine Storage"
|
||||
desc = "A refrigerated storage unit for storing medicine and chemicals."
|
||||
icon_state = "smartfridge" //To fix the icon in the map editor.
|
||||
icon_on = "smartfridge_chem"
|
||||
req_one_access_txt = "5;33"
|
||||
|
||||
/obj/machinery/smartfridge/secure/medbay/accept_check(var/obj/item/O as obj)
|
||||
if(istype(O,/obj/item/weapon/reagent_containers/glass/))
|
||||
return 1
|
||||
if(istype(O,/obj/item/weapon/storage/pill_bottle/))
|
||||
return 1
|
||||
if(istype(O,/obj/item/weapon/reagent_containers/pill/))
|
||||
return 1
|
||||
return 0
|
||||
|
||||
/obj/machinery/smartfridge/secure/virology
|
||||
name = "\improper Refrigerated Virus Storage"
|
||||
desc = "A refrigerated storage unit for storing viral material."
|
||||
req_access_txt = "39"
|
||||
icon_state = "smartfridge_virology"
|
||||
icon_on = "smartfridge_virology"
|
||||
icon_off = "smartfridge_virology-off"
|
||||
|
||||
/obj/machinery/smartfridge/secure/virology/accept_check(var/obj/item/O as obj)
|
||||
if(istype(O,/obj/item/weapon/reagent_containers/glass/beaker/vial/))
|
||||
return 1
|
||||
if(istype(O,/obj/item/weapon/virusdish/))
|
||||
return 1
|
||||
return 0
|
||||
|
||||
/obj/machinery/smartfridge/chemistry
|
||||
name = "\improper Smart Chemical Storage"
|
||||
desc = "A refrigerated storage unit for medicine and chemical storage."
|
||||
|
||||
/obj/machinery/smartfridge/chemistry/accept_check(var/obj/item/O as obj)
|
||||
if(istype(O,/obj/item/weapon/storage/pill_bottle) || istype(O,/obj/item/weapon/reagent_containers))
|
||||
return 1
|
||||
return 0
|
||||
|
||||
/obj/machinery/smartfridge/chemistry/virology
|
||||
name = "\improper Smart Virus Storage"
|
||||
desc = "A refrigerated storage unit for volatile sample storage."
|
||||
|
||||
|
||||
/obj/machinery/smartfridge/drinks
|
||||
name = "\improper Drink Showcase"
|
||||
desc = "A refrigerated storage unit for tasty tasty alcohol."
|
||||
|
||||
/obj/machinery/smartfridge/drinks/accept_check(var/obj/item/O as obj)
|
||||
if(istype(O,/obj/item/weapon/reagent_containers/glass) || istype(O,/obj/item/weapon/reagent_containers/food/drinks) || istype(O,/obj/item/weapon/reagent_containers/food/condiment))
|
||||
return 1
|
||||
|
||||
/obj/machinery/smartfridge/process()
|
||||
if(!src.ispowered)
|
||||
return
|
||||
if(src.seconds_electrified > 0)
|
||||
src.seconds_electrified--
|
||||
if(src.shoot_inventory && prob(2))
|
||||
src.throw_item()
|
||||
|
||||
/obj/machinery/smartfridge/power_change()
|
||||
if( powered() )
|
||||
@@ -111,6 +182,26 @@
|
||||
|
||||
updateUsrDialog()
|
||||
|
||||
/obj/machinery/smartfridge/secure/attackby(var/obj/item/O as obj, var/mob/user as mob)
|
||||
if (istype(O, /obj/item/weapon/card/emag))
|
||||
src.emagged = 1
|
||||
user << "You short out the product lock on [src]"
|
||||
return
|
||||
else if(istype(O, /obj/item/weapon/screwdriver))
|
||||
src.panel_open = !src.panel_open
|
||||
user << "You [src.panel_open ? "open" : "close"] the maintenance panel."
|
||||
src.overlays.Cut()
|
||||
if(src.panel_open)
|
||||
src.overlays += image(src.icon, src.icon_panel)
|
||||
src.updateUsrDialog()
|
||||
return
|
||||
else if(istype(O, /obj/item/device/multitool)||istype(O, /obj/item/weapon/wirecutters))
|
||||
if(src.panel_open)
|
||||
attack_hand(user)
|
||||
return
|
||||
..()
|
||||
return
|
||||
|
||||
/obj/machinery/smartfridge/attack_paw(mob/user as mob)
|
||||
return src.attack_hand(user)
|
||||
|
||||
@@ -119,6 +210,9 @@
|
||||
|
||||
/obj/machinery/smartfridge/attack_hand(mob/user as mob)
|
||||
user.set_machine(src)
|
||||
if(src.seconds_electrified != 0)
|
||||
if(src.shock(user, 100))
|
||||
return
|
||||
interact(user)
|
||||
|
||||
/*******************
|
||||
@@ -151,8 +245,28 @@
|
||||
dat += "<br>"
|
||||
|
||||
dat += "</TT>"
|
||||
if(panel_open)
|
||||
//One of the wires does absolutely nothing.
|
||||
var/list/vendwires = list(
|
||||
"Blue" = 1,
|
||||
"Red" = 2,
|
||||
"Black" = 3
|
||||
)
|
||||
dat += "<br><hr><br><B>Access Panel</B><br>"
|
||||
for(var/wiredesc in vendwires)
|
||||
var/is_uncut = src.wires & APCWireColorToFlag[vendwires[wiredesc]]
|
||||
dat += "[wiredesc] wire: "
|
||||
if(!is_uncut)
|
||||
dat += "<a href='?src=\ref[src];cutwire=[vendwires[wiredesc]]'>Mend</a>"
|
||||
else
|
||||
dat += "<a href='?src=\ref[src];cutwire=[vendwires[wiredesc]]'>Cut</a> "
|
||||
dat += "<a href='?src=\ref[src];pulsewire=[vendwires[wiredesc]]'>Pulse</a> "
|
||||
dat += "<br>"
|
||||
|
||||
dat += "<br>"
|
||||
dat += "The orange light is [(src.seconds_electrified == 0) ? "off" : "on"].<BR>"
|
||||
dat += "The red light is [src.shoot_inventory ? "off" : "blinking"].<BR>"
|
||||
user << browse("<HEAD><TITLE>[src] Supplies</TITLE></HEAD><TT>[dat]</TT>", "window=smartfridge")
|
||||
onclose(user, "smartfridge")
|
||||
return
|
||||
|
||||
/obj/machinery/smartfridge/Topic(href, href_list)
|
||||
@@ -160,21 +274,191 @@
|
||||
return
|
||||
usr.set_machine(src)
|
||||
|
||||
var/N = href_list["vend"]
|
||||
var/amount = text2num(href_list["amount"])
|
||||
if ((href_list["cutwire"]) && (src.panel_open))
|
||||
var/twire = text2num(href_list["cutwire"])
|
||||
if (!( istype(usr.get_active_hand(), /obj/item/weapon/wirecutters) ))
|
||||
usr << "You need wirecutters!"
|
||||
return
|
||||
if (src.isWireColorCut(twire))
|
||||
src.mend(twire)
|
||||
else
|
||||
src.cut(twire)
|
||||
else if ((href_list["pulsewire"]) && (src.panel_open))
|
||||
var/twire = text2num(href_list["pulsewire"])
|
||||
if (!istype(usr.get_active_hand(), /obj/item/device/multitool))
|
||||
usr << "You need a multitool!"
|
||||
return
|
||||
if (src.isWireColorCut(twire))
|
||||
usr << "You can't pulse a cut wire."
|
||||
return
|
||||
else
|
||||
src.pulse(twire)
|
||||
else if (href_list["vend"])
|
||||
var/N = href_list["vend"]
|
||||
var/amount = text2num(href_list["amount"])
|
||||
|
||||
if(item_quants[N] <= 0) // Sanity check, there are probably ways to press the button when it shouldn't be possible.
|
||||
return
|
||||
if(item_quants[N] <= 0) // Sanity check, there are probably ways to press the button when it shouldn't be possible.
|
||||
return
|
||||
|
||||
item_quants[N] = max(item_quants[N] - amount, 0)
|
||||
|
||||
var/i = amount
|
||||
for(var/obj/O in contents)
|
||||
if(O.name == N)
|
||||
O.loc = src.loc
|
||||
i--
|
||||
if(i <= 0)
|
||||
break
|
||||
item_quants[N] = max(item_quants[N] - amount, 0)
|
||||
|
||||
var/i = amount
|
||||
for(var/obj/O in contents)
|
||||
if(O.name == N)
|
||||
O.loc = src.loc
|
||||
i--
|
||||
if(i <= 0)
|
||||
break
|
||||
src.add_fingerprint(usr)
|
||||
src.updateUsrDialog()
|
||||
return
|
||||
|
||||
/*************
|
||||
* Hacking
|
||||
**************/
|
||||
|
||||
/obj/machinery/smartfridge/proc/cut(var/wireColor)
|
||||
var/wireFlag = APCWireColorToFlag[wireColor]
|
||||
var/wireIndex = APCWireColorToIndex[wireColor]
|
||||
src.wires &= ~wireFlag
|
||||
switch(wireIndex)
|
||||
if(WIRE_SHOCK)
|
||||
src.seconds_electrified = -1
|
||||
if (WIRE_SHOOTINV)
|
||||
if(!src.shoot_inventory)
|
||||
src.shoot_inventory = 1
|
||||
if(WIRE_SCANID)
|
||||
src.locked = 1
|
||||
|
||||
/obj/machinery/smartfridge/proc/mend(var/wireColor)
|
||||
var/wireFlag = APCWireColorToFlag[wireColor]
|
||||
var/wireIndex = APCWireColorToIndex[wireColor] //not used in this function
|
||||
src.wires |= wireFlag
|
||||
switch(wireIndex)
|
||||
if(WIRE_SHOCK)
|
||||
src.seconds_electrified = 0
|
||||
if (WIRE_SHOOTINV)
|
||||
src.shoot_inventory = 0
|
||||
if(WIRE_SCANID)
|
||||
src.locked = 0
|
||||
|
||||
/obj/machinery/smartfridge/proc/pulse(var/wireColor)
|
||||
var/wireIndex = APCWireColorToIndex[wireColor]
|
||||
switch(wireIndex)
|
||||
if(WIRE_SHOCK)
|
||||
src.seconds_electrified = 30
|
||||
if (WIRE_SHOOTINV)
|
||||
src.shoot_inventory = !src.shoot_inventory
|
||||
if(WIRE_SCANID)
|
||||
src.locked = -1
|
||||
|
||||
/obj/machinery/smartfridge/proc/isWireColorCut(var/wireColor)
|
||||
var/wireFlag = APCWireColorToFlag[wireColor]
|
||||
return ((src.wires & wireFlag) == 0)
|
||||
|
||||
/obj/machinery/smartfridge/proc/isWireCut(var/wireIndex)
|
||||
var/wireFlag = APCIndexToFlag[wireIndex]
|
||||
return ((src.wires & wireFlag) == 0)
|
||||
|
||||
/obj/machinery/smartfridge/proc/throw_item()
|
||||
var/obj/throw_item = null
|
||||
var/mob/living/target = locate() in view(7,src)
|
||||
if(!target)
|
||||
return 0
|
||||
|
||||
for (var/O in item_quants)
|
||||
if(item_quants[O] <= 0) //Try to use a record that actually has something to dump.
|
||||
continue
|
||||
|
||||
item_quants[O]--
|
||||
for(var/obj/T in contents)
|
||||
if(T.name == O)
|
||||
T.loc = src.loc
|
||||
throw_item = T
|
||||
break
|
||||
break
|
||||
if(!throw_item)
|
||||
return 0
|
||||
spawn(0)
|
||||
throw_item.throw_at(target,16,3)
|
||||
src.visible_message("\red <b>[src] launches [throw_item.name] at [target.name]!</b>")
|
||||
return 1
|
||||
|
||||
/obj/machinery/smartfridge/proc/shock(mob/user, prb)
|
||||
if(!src.ispowered) // unpowered, no shock
|
||||
return 0
|
||||
if(!prob(prb))
|
||||
return 0
|
||||
var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread
|
||||
s.set_up(5, 1, src)
|
||||
s.start()
|
||||
if (electrocute_mob(user, get_area(src), src, 0.7))
|
||||
return 1
|
||||
else
|
||||
return 0
|
||||
|
||||
/************************
|
||||
* Secure SmartFridges
|
||||
*************************/
|
||||
|
||||
/obj/machinery/smartfridge/secure/Topic(href, href_list)
|
||||
usr.set_machine(src)
|
||||
if ((usr.contents.Find(src) || (in_range(src, usr) && istype(src.loc, /turf))))
|
||||
if ((!src.allowed(usr)) && (!src.emagged) && (src.locked != -1) && href_list["vend"]) //For SECURE VENDING MACHINES YEAH
|
||||
usr << "\red Access denied." //Unless emagged of course
|
||||
return
|
||||
..()
|
||||
return
|
||||
|
||||
/obj/machinery/smartfridge/secure/interact(mob/user as mob)
|
||||
|
||||
if(!src.ispowered)
|
||||
return
|
||||
|
||||
var/dat = "<TT><b>Select an item:</b><br>"
|
||||
|
||||
if (contents.len == 0)
|
||||
dat += "<font color = 'red'>No product loaded!</font>"
|
||||
else
|
||||
for (var/O in item_quants)
|
||||
if(item_quants[O] > 0)
|
||||
var/N = item_quants[O]
|
||||
dat += "<FONT color = 'blue'><B>[capitalize(O)]</B>:"
|
||||
dat += " [N] </font>"
|
||||
dat += "<a href='byond://?src=\ref[src];vend=[O];amount=1'>Vend</A> "
|
||||
if(N > 5)
|
||||
dat += "(<a href='byond://?src=\ref[src];vend=[O];amount=5'>x5</A>)"
|
||||
if(N > 10)
|
||||
dat += "(<a href='byond://?src=\ref[src];vend=[O];amount=10'>x10</A>)"
|
||||
if(N > 25)
|
||||
dat += "(<a href='byond://?src=\ref[src];vend=[O];amount=25'>x25</A>)"
|
||||
if(N > 1)
|
||||
dat += "(<a href='?src=\ref[src];vend=[O];amount=[N]'>All</A>)"
|
||||
dat += "<br>"
|
||||
|
||||
dat += "</TT>"
|
||||
if(panel_open)
|
||||
var/list/vendwires = list(
|
||||
"Violet" = 1,
|
||||
"Orange" = 2,
|
||||
"Green" = 3
|
||||
)
|
||||
dat += "<br><hr><br><B>Access Panel</B><br>"
|
||||
for(var/wiredesc in vendwires)
|
||||
var/is_uncut = src.wires & APCWireColorToFlag[vendwires[wiredesc]]
|
||||
dat += "[wiredesc] wire: "
|
||||
if(!is_uncut)
|
||||
dat += "<a href='?src=\ref[src];cutwire=[vendwires[wiredesc]]'>Mend</a>"
|
||||
else
|
||||
dat += "<a href='?src=\ref[src];cutwire=[vendwires[wiredesc]]'>Cut</a> "
|
||||
dat += "<a href='?src=\ref[src];pulsewire=[vendwires[wiredesc]]'>Pulse</a> "
|
||||
dat += "<br>"
|
||||
|
||||
dat += "<br>"
|
||||
dat += "The orange light is [(src.seconds_electrified == 0) ? "off" : "on"].<BR>"
|
||||
//dat += "The red light is [src.shoot_inventory ? "off" : "blinking"].<BR>"
|
||||
dat += "The green light is [src.locked == 1 ? "off" : "[src.locked == -1 ? "blinking" : "on"]"].<BR>"
|
||||
user << browse("<HEAD><TITLE>[src] Supplies</TITLE></HEAD><TT>[dat]</TT>", "window=smartfridge")
|
||||
return
|
||||
|
||||
//TODO: Make smartfridges hackable. - JoeyJo0
|
||||
|
||||
@@ -75,4 +75,4 @@
|
||||
..(severity)
|
||||
return
|
||||
power_change()
|
||||
..(severity)
|
||||
..(severity)
|
||||
|
||||
@@ -67,8 +67,10 @@ Class Procs:
|
||||
Checks to see if area that contains the object has power available for power
|
||||
channel given in 'chan'.
|
||||
|
||||
use_power(amount, chan=EQUIP) 'modules/power/power.dm'
|
||||
use_power(amount, chan=EQUIP, autocalled) 'modules/power/power.dm'
|
||||
Deducts 'amount' from the power channel 'chan' of the area that contains the object.
|
||||
If it's autocalled then everything is normal, if something else calls use_power we are going to
|
||||
need to recalculate the power two ticks in a row.
|
||||
|
||||
power_change() 'modules/power/power.dm'
|
||||
Called by the area that contains the object when ever that area under goes a
|
||||
@@ -159,9 +161,9 @@ Class Procs:
|
||||
if(!powered(power_channel))
|
||||
return 0
|
||||
if(src.use_power == 1)
|
||||
use_power(idle_power_usage,power_channel)
|
||||
use_power(idle_power_usage,power_channel, 1)
|
||||
else if(src.use_power >= 2)
|
||||
use_power(active_power_usage,power_channel)
|
||||
use_power(active_power_usage,power_channel, 1)
|
||||
return 1
|
||||
|
||||
/obj/machinery/Topic(href, href_list)
|
||||
@@ -189,6 +191,10 @@ Class Procs:
|
||||
return 1
|
||||
|
||||
src.add_fingerprint(usr)
|
||||
|
||||
var/area/A = get_area(src)
|
||||
A.powerupdate = 1
|
||||
|
||||
return 0
|
||||
|
||||
/obj/machinery/attack_ai(mob/user as mob)
|
||||
@@ -228,6 +234,10 @@ Class Procs:
|
||||
return 1
|
||||
|
||||
src.add_fingerprint(user)
|
||||
|
||||
var/area/A = get_area(src)
|
||||
A.powerupdate = 1
|
||||
|
||||
return 0
|
||||
|
||||
/obj/machinery/proc/RefreshParts() //Placeholder proc for machines that are built using frames.
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -0,0 +1,252 @@
|
||||
/////////////////////////////////////////////
|
||||
// Chem smoke
|
||||
/////////////////////////////////////////////
|
||||
/obj/effect/effect/smoke/chem
|
||||
icon = 'icons/effects/chemsmoke.dmi'
|
||||
opacity = 0
|
||||
time_to_live = 300
|
||||
pass_flags = PASSTABLE | PASSGRILLE | PASSGLASS //PASSGLASS is fine here, it's just so the visual effect can "flow" around glass
|
||||
|
||||
/obj/effect/effect/smoke/chem/New()
|
||||
..()
|
||||
var/datum/reagents/R = new/datum/reagents(500)
|
||||
reagents = R
|
||||
R.my_atom = src
|
||||
return
|
||||
|
||||
/datum/effect/effect/system/smoke_spread/chem
|
||||
smoke_type = /obj/effect/effect/smoke/chem
|
||||
var/obj/chemholder
|
||||
var/range
|
||||
var/list/targetTurfs
|
||||
var/list/wallList
|
||||
var/density
|
||||
|
||||
|
||||
/datum/effect/effect/system/smoke_spread/chem/New()
|
||||
..()
|
||||
chemholder = new/obj()
|
||||
var/datum/reagents/R = new/datum/reagents(500)
|
||||
chemholder.reagents = R
|
||||
R.my_atom = chemholder
|
||||
|
||||
//------------------------------------------
|
||||
//Sets up the chem smoke effect
|
||||
//
|
||||
// Calculates the max range smoke can travel, then gets all turfs in that view range.
|
||||
// Culls the selected turfs to a (roughly) circle shape, then calls smokeFlow() to make
|
||||
// sure the smoke can actually path to the turfs. This culls any turfs it can't reach.
|
||||
//------------------------------------------
|
||||
/datum/effect/effect/system/smoke_spread/chem/set_up(var/datum/reagents/carry = null, n = 10, c = 0, loca, direct)
|
||||
range = n * 0.3
|
||||
cardinals = c
|
||||
carry.copy_to(chemholder, carry.total_volume)
|
||||
|
||||
if(istype(loca, /turf/))
|
||||
location = loca
|
||||
else
|
||||
location = get_turf(loca)
|
||||
if(!location)
|
||||
return
|
||||
|
||||
targetTurfs = new()
|
||||
|
||||
//build affected area list
|
||||
for(var/turf/T in view(range, location))
|
||||
//cull turfs to circle
|
||||
if(cheap_pythag(T.x - location.x, T.y - location.y) <= range)
|
||||
targetTurfs += T
|
||||
|
||||
//make secondary list for reagents that affect walls
|
||||
if(chemholder.reagents.has_reagent("thermite") || chemholder.reagents.has_reagent("plantbgone"))
|
||||
wallList = new()
|
||||
|
||||
//pathing check
|
||||
smokeFlow(location, targetTurfs, wallList)
|
||||
|
||||
//set the density of the cloud - for diluting reagents
|
||||
density = max(1, targetTurfs.len / 4) //clamp the cloud density minimum to 1 so it cant multiply the reagents
|
||||
|
||||
//Admin messaging
|
||||
var/contained = ""
|
||||
for(var/reagent in carry.reagent_list)
|
||||
contained += " [reagent] "
|
||||
if(contained)
|
||||
contained = "\[[contained]\]"
|
||||
var/area/A = get_area(location)
|
||||
|
||||
var/where = "[A.name] | [location.x], [location.y]"
|
||||
var/whereLink = "<A HREF='?_src_=holder;adminplayerobservecoodjump=1;X=[location.x];Y=[location.y];Z=[location.z]'>[where]</a>"
|
||||
|
||||
if(carry.my_atom.fingerprintslast)
|
||||
var/mob/M = get_mob_by_key(carry.my_atom.fingerprintslast)
|
||||
var/more = ""
|
||||
if(M)
|
||||
more = "(<A HREF='?_src_=holder;adminmoreinfo=\ref[M]'>?</a>)"
|
||||
message_admins("A chemical smoke reaction has taken place in ([whereLink])[contained]. Last associated key is [carry.my_atom.fingerprintslast][more].", 0, 1)
|
||||
log_game("A chemical smoke reaction has taken place in ([where])[contained]. Last associated key is [carry.my_atom.fingerprintslast].")
|
||||
else
|
||||
message_admins("A chemical smoke reaction has taken place in ([whereLink]). No associated key.", 0, 1)
|
||||
log_game("A chemical smoke reaction has taken place in ([where])[contained]. No associated key.")
|
||||
|
||||
|
||||
//------------------------------------------
|
||||
//Runs the chem smoke effect
|
||||
//
|
||||
// Spawns damage over time loop for each reagent held in the cloud.
|
||||
// Applies reagents to walls that affect walls (only thermite and plant-b-gone at the moment).
|
||||
// Also calculates target locations to spawn the visual smoke effect on, so the whole area
|
||||
// is covered fairly evenly.
|
||||
//------------------------------------------
|
||||
/datum/effect/effect/system/smoke_spread/chem/start()
|
||||
|
||||
if(!location) //kill grenade if it somehow ends up in nullspace
|
||||
return
|
||||
|
||||
//reagent application - only run if there are extra reagents in the smoke
|
||||
if(chemholder.reagents.reagent_list.len)
|
||||
for(var/datum/reagent/R in chemholder.reagents.reagent_list)
|
||||
var/proba = 100
|
||||
var/runs = 5
|
||||
|
||||
//dilute the reagents according to cloud density
|
||||
R.volume /= density
|
||||
chemholder.reagents.update_total()
|
||||
|
||||
//apply wall affecting reagents to walls
|
||||
if(R.id in list("thermite", "plantbgone"))
|
||||
for(var/turf/T in wallList)
|
||||
R.reaction_turf(T, R.volume)
|
||||
|
||||
//reagents that should be applied to turfs in a random pattern
|
||||
if(R.id == "carbon")
|
||||
proba = 75
|
||||
else if(R.id in list("blood", "radium", "uranium"))
|
||||
proba = 25
|
||||
|
||||
spawn(0)
|
||||
for(var/i = 0, i < runs, i++)
|
||||
for(var/turf/T in targetTurfs)
|
||||
if(prob(proba))
|
||||
R.reaction_turf(T, R.volume)
|
||||
for(var/atom/A in T.contents)
|
||||
if(istype(A, /obj/effect/effect/smoke/chem)) //skip the item if it is chem smoke
|
||||
continue
|
||||
else if(istype(A, /mob))
|
||||
var/dist = cheap_pythag(T.x - location.x, T.y - location.y)
|
||||
if(!dist)
|
||||
dist = 1
|
||||
R.reaction_mob(A, volume = R.volume / dist)
|
||||
else if(istype(A, /obj))
|
||||
R.reaction_obj(A, R.volume)
|
||||
sleep(30)
|
||||
|
||||
|
||||
//build smoke icon
|
||||
var/color = mix_color_from_reagents(chemholder.reagents.reagent_list)
|
||||
var/icon/I
|
||||
if(color)
|
||||
I = icon('icons/effects/chemsmoke.dmi')
|
||||
I += color
|
||||
else
|
||||
I = icon('icons/effects/96x96.dmi', "smoke")
|
||||
|
||||
|
||||
//distance between each smoke cloud
|
||||
var/const/arcLength = 2.3559
|
||||
|
||||
|
||||
//calculate positions for smoke coverage - then spawn smoke
|
||||
for(var/i = 0, i < range, i++)
|
||||
var/radius = i * 1.5
|
||||
if(!radius)
|
||||
spawn(0)
|
||||
spawnSmoke(location, I, 1)
|
||||
continue
|
||||
|
||||
var/offset = 0
|
||||
var/points = round((radius * 2 * PI) / arcLength)
|
||||
var/angle = round(ToDegrees(arcLength / radius), 1)
|
||||
|
||||
if(!IsInteger(radius))
|
||||
offset = 45 //degrees
|
||||
|
||||
for(var/j = 0, j < points, j++)
|
||||
var/a = (angle * j) + offset
|
||||
var/x = round(radius * cos(a) + location.x, 1)
|
||||
var/y = round(radius * sin(a) + location.y, 1)
|
||||
var/turf/T = locate(x,y,location.z)
|
||||
if(!T)
|
||||
continue
|
||||
if(T in targetTurfs)
|
||||
spawn(0)
|
||||
spawnSmoke(T, I, range)
|
||||
|
||||
//------------------------------------------
|
||||
// Randomizes and spawns the smoke effect.
|
||||
// Also handles deleting the smoke once the effect is finished.
|
||||
//------------------------------------------
|
||||
/datum/effect/effect/system/smoke_spread/chem/proc/spawnSmoke(var/turf/T, var/icon/I, var/dist = 1)
|
||||
var/obj/effect/effect/smoke/chem/smoke = new(location)
|
||||
if(chemholder.reagents.reagent_list.len)
|
||||
chemholder.reagents.copy_to(smoke, chemholder.reagents.total_volume / dist, safety = 1) //copy reagents to the smoke so mob/breathe() can handle inhaling the reagents
|
||||
smoke.icon = I
|
||||
smoke.layer = 6
|
||||
smoke.dir = pick(cardinal)
|
||||
smoke.pixel_x = -32 + rand(-8,8)
|
||||
smoke.pixel_y = -32 + rand(-8,8)
|
||||
walk_to(smoke, T)
|
||||
smoke.opacity = 1 //switching opacity on after the smoke has spawned, and then
|
||||
sleep(150+rand(0,20)) // turning it off before it is deleted results in cleaner
|
||||
smoke.opacity = 0 // lighting and view range updates
|
||||
fadeOut(smoke)
|
||||
smoke.delete()
|
||||
|
||||
//------------------------------------------
|
||||
// Fades out the smoke smoothly using it's alpha variable.
|
||||
//------------------------------------------
|
||||
/datum/effect/effect/system/smoke_spread/chem/proc/fadeOut(var/atom/A, var/frames = 16)
|
||||
var/step = A.alpha / frames
|
||||
for(var/i = 0, i < frames, i++)
|
||||
A.alpha -= step
|
||||
sleep(world.tick_lag)
|
||||
return
|
||||
|
||||
//------------------------------------------
|
||||
// Smoke pathfinder. Uses a flood fill method based on zones to
|
||||
// quickly check what turfs the smoke (airflow) can actually reach.
|
||||
//------------------------------------------
|
||||
/datum/effect/effect/system/smoke_spread/chem/proc/smokeFlow()
|
||||
|
||||
var/list/pending = new()
|
||||
var/list/complete = new()
|
||||
|
||||
pending += location
|
||||
|
||||
while(pending.len)
|
||||
for(var/turf/simulated/current in pending)
|
||||
for(var/D in cardinal)
|
||||
var/turf/simulated/target = get_step(current, D)
|
||||
|
||||
if(wallList)
|
||||
if(istype(target, /turf/simulated/wall))
|
||||
if(!(target in wallList))
|
||||
wallList += target
|
||||
continue
|
||||
if(!target.zone)
|
||||
continue
|
||||
if(target in pending)
|
||||
continue
|
||||
if(target in complete)
|
||||
continue
|
||||
if(!(target in targetTurfs))
|
||||
continue
|
||||
|
||||
pending += target
|
||||
|
||||
pending -= current
|
||||
complete += current
|
||||
|
||||
targetTurfs = complete
|
||||
|
||||
return
|
||||
@@ -380,121 +380,6 @@ steam.start() -- spawns the effect
|
||||
|
||||
/datum/effect/effect/system/smoke_spread/mustard
|
||||
smoke_type = /obj/effect/effect/smoke/mustard
|
||||
/////////////////////////////////////////////
|
||||
// Chem smoke
|
||||
/////////////////////////////////////////////
|
||||
/obj/effect/effect/smoke/chem
|
||||
icon = 'icons/effects/chemsmoke.dmi'
|
||||
|
||||
/obj/effect/effect/smoke/chem/New()
|
||||
..()
|
||||
var/datum/reagents/R = new/datum/reagents(500)
|
||||
reagents = R
|
||||
R.my_atom = src
|
||||
return
|
||||
|
||||
/obj/effect/effect/smoke/chem/proc/applyReagents()
|
||||
if(reagents.reagent_list.len)
|
||||
for(var/atom/A in view(1, src))
|
||||
if(!istype(A, src.type))
|
||||
if(reagents.has_reagent("radium")||reagents.has_reagent("uranium")||reagents.has_reagent("carbon")||reagents.has_reagent("thermite"))//Prevents unholy radium spam by reducing the number of 'greenglows' down to something reasonable -Sieve
|
||||
if(prob(5))
|
||||
reagents.reaction(A)
|
||||
else
|
||||
reagents.reaction(A)
|
||||
|
||||
/obj/effect/effect/smoke/chem/affect(mob/living/carbon/M as mob )
|
||||
reagents.reaction(M)
|
||||
|
||||
/datum/effect/effect/system/smoke_spread/chem
|
||||
smoke_type = /obj/effect/effect/smoke/chem
|
||||
var/obj/chemholder
|
||||
|
||||
New()
|
||||
..()
|
||||
chemholder = new/obj()
|
||||
var/datum/reagents/R = new/datum/reagents(500)
|
||||
chemholder.reagents = R
|
||||
R.my_atom = chemholder
|
||||
|
||||
set_up(var/datum/reagents/carry = null, n = 5, c = 0, loca, direct)
|
||||
if(n > 20)
|
||||
n = 20
|
||||
number = n
|
||||
cardinals = c
|
||||
carry.copy_to(chemholder, carry.total_volume)
|
||||
|
||||
|
||||
if(istype(loca, /turf/))
|
||||
location = loca
|
||||
else
|
||||
location = get_turf(loca)
|
||||
if(direct)
|
||||
direction = direct
|
||||
|
||||
var/contained = ""
|
||||
for(var/reagent in carry.reagent_list)
|
||||
contained += " [reagent] "
|
||||
if(contained)
|
||||
contained = "\[[contained]\]"
|
||||
var/area/A = get_area(location)
|
||||
|
||||
var/where = "[A.name] | [location.x], [location.y]"
|
||||
var/whereLink = "<A HREF='?_src_=holder;adminplayerobservecoodjump=1;X=[location.x];Y=[location.y];Z=[location.z]'>[where]</a>"
|
||||
|
||||
if(carry.my_atom.fingerprintslast)
|
||||
var/mob/M = get_mob_by_key(carry.my_atom.fingerprintslast)
|
||||
var/more = ""
|
||||
if(M)
|
||||
more = "(<A HREF='?_src_=holder;adminmoreinfo=\ref[M]'>?</a>)"
|
||||
message_admins("A chemical smoke reaction has taken place in ([whereLink])[contained]. Last associated key is [carry.my_atom.fingerprintslast][more].", 0, 1)
|
||||
log_game("A chemical smoke reaction has taken place in ([where])[contained]. Last associated key is [carry.my_atom.fingerprintslast].")
|
||||
else
|
||||
message_admins("A chemical smoke reaction has taken place in ([whereLink]). No associated key.", 0, 1)
|
||||
log_game("A chemical smoke reaction has taken place in ([where])[contained]. No associated key.")
|
||||
|
||||
start()
|
||||
var/i = 0
|
||||
|
||||
var/color = mix_color_from_reagents(chemholder.reagents.reagent_list)
|
||||
|
||||
for(i=0, i<src.number, i++)
|
||||
if(src.total_smoke > 20)
|
||||
return
|
||||
spawn(0)
|
||||
if(holder)
|
||||
src.location = get_turf(holder)
|
||||
var/obj/effect/effect/smoke/chem/smoke = new /obj/effect/effect/smoke/chem(src.location)
|
||||
src.total_smoke++
|
||||
var/direction = src.direction
|
||||
if(!direction)
|
||||
if(src.cardinals)
|
||||
direction = pick(cardinal)
|
||||
else
|
||||
direction = pick(alldirs)
|
||||
|
||||
if(chemholder.reagents.total_volume != 1) // can't split 1 very well
|
||||
chemholder.reagents.copy_to(smoke, chemholder.reagents.total_volume / number, safety = 1) // copy reagents to each smoke, divide evenly
|
||||
|
||||
if(color)
|
||||
smoke.icon += color // give the smoke color, if it has any to begin with
|
||||
else
|
||||
// if no color, just use the old smoke icon
|
||||
smoke.icon = 'icons/effects/96x96.dmi'
|
||||
smoke.icon_state = "smoke"
|
||||
|
||||
for(i=0, i<pick(0,1,1,1,2,2,2,3), i++)
|
||||
sleep(10)
|
||||
step(smoke,direction)
|
||||
|
||||
//apply the reagents to the environment after the smoke has stopped moving - to reduce reagent spam
|
||||
for(i=0, i<2 + pick(0,1,), i++)
|
||||
smoke.applyReagents()
|
||||
sleep(20)
|
||||
|
||||
sleep(60)
|
||||
smoke.delete()
|
||||
src.total_smoke--
|
||||
|
||||
|
||||
/////////////////////////////////////////////
|
||||
@@ -834,7 +719,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
|
||||
|
||||
|
||||
@@ -161,6 +161,10 @@
|
||||
if( A == src ) continue
|
||||
src.reagents.reaction(A, 1, 10)
|
||||
|
||||
if(istype(loc, /mob/living/carbon)) //drop dat grenade if it goes off in your hand
|
||||
var/mob/living/carbon/C = loc
|
||||
C.drop_from_inventory(src)
|
||||
C.throw_mode_off()
|
||||
|
||||
invisibility = INVISIBILITY_MAXIMUM //Why am i doing this?
|
||||
spawn(50) //To make sure all reagents can work
|
||||
|
||||
@@ -92,6 +92,31 @@
|
||||
return
|
||||
return
|
||||
|
||||
var/last_chew = 0
|
||||
/mob/living/carbon/human/RestrainedClickOn(var/atom/A)
|
||||
if (A != src) return ..()
|
||||
if (last_chew + 26 > world.time) return
|
||||
|
||||
var/mob/living/carbon/human/H = A
|
||||
if (!H.handcuffed) return
|
||||
if (H.a_intent != "hurt") return
|
||||
if (H.zone_sel.selecting != "mouth") return
|
||||
if (H.wear_mask) return
|
||||
if (istype(H.wear_suit, /obj/item/clothing/suit/straight_jacket)) return
|
||||
|
||||
var/datum/organ/external/O = H.organs_by_name[H.hand?"l_hand":"r_hand"]
|
||||
if (!O) return
|
||||
|
||||
var/s = "\red [H.name] chews on \his [O.display_name]!"
|
||||
H.visible_message(s, "\red You chew on your [O.display_name]!")
|
||||
H.attack_log += text("\[[time_stamp()]\] <font color='red'>[s] ([H.ckey])</font>")
|
||||
log_attack("[s] ([H.ckey])")
|
||||
|
||||
if(O.take_damage(3,0,1,"teeth marks"))
|
||||
H:UpdateDamageIcon()
|
||||
|
||||
last_chew = world.time
|
||||
|
||||
/obj/item/weapon/handcuffs/cable
|
||||
name = "cable restraints"
|
||||
desc = "Looks like some cables tied together. Could be used to tie something up."
|
||||
@@ -123,4 +148,4 @@
|
||||
color = "#FFFFFF"
|
||||
|
||||
/obj/item/weapon/handcuffs/cyborg
|
||||
dispenser = 1
|
||||
dispenser = 1
|
||||
|
||||
@@ -713,6 +713,52 @@
|
||||
"}
|
||||
|
||||
|
||||
|
||||
/obj/item/weapon/book/manual/medical_diagnostics_manual
|
||||
name = "NT Medical Diagnostics Manual"
|
||||
desc = "First, do no harm. A detailed medical practitioner's guide."
|
||||
icon_state = "bookMedical"
|
||||
author = "Nanotrasen Medicine Department"
|
||||
title = "NT Medical Diagnostics Manual"
|
||||
|
||||
dat = {"
|
||||
|
||||
<html><head>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<b>The Oath</b><br>
|
||||
<i>The Medical Oath sworn by recognised medical practitioners in the employ of Nanotrasen</i><br>
|
||||
<br>
|
||||
Now, as a new doctor, I solemnly promise that I will to the best of my ability serve humanity-caring for the sick, promoting good health, and alleviating pain and suffering.<br>
|
||||
<br>
|
||||
I recognise that the practice of medicine is a privilege with which comes considerable responsibility and I will not abuse my position.<br>
|
||||
<br>
|
||||
I will practise medicine with integrity, humility, honesty, and compassion-working with my fellow doctors and other colleagues to meet the needs of my patients.<br>
|
||||
<br>
|
||||
I shall never intentionally do or administer anything to the overall harm of my patients.<br>
|
||||
<br>
|
||||
I will not permit considerations of gender, race, religion, political affiliation, sexual orientation, nationality, or social standing to influence my duty of care.<br>
|
||||
<br>
|
||||
I will oppose policies in breach of human rights and will not participate in them. I will strive to change laws that are contrary to my profession's ethics and will work towards a fairer distribution of health resources.<br>
|
||||
<br>
|
||||
I will assist my patients to make informed decisions that coincide with their own values and beliefs and will uphold patient confidentiality.<br>
|
||||
<br>
|
||||
I will recognise the limits of my knowledge and seek to maintain and increase my understanding and skills throughout my professional life. I will acknowledge and try to remedy my own mistakes and honestly assess and respond to those of others.<br>
|
||||
<br>
|
||||
I will seek to promote the advancement of medical knowledge through teaching and research.<br>
|
||||
<br>
|
||||
I make this declaration solemnly, freely, and upon my honour.<br>
|
||||
|
||||
<HR COLOR="steelblue" WIDTH="60%" ALIGN="LEFT">
|
||||
|
||||
<iframe width='100%' height='100%' src="http://baystation12.net/wiki/index.php?title=Guide_to_Medicine&printable=yes&removelinks=1" frameborder="0" id="main_frame"></iframe>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
"}
|
||||
|
||||
|
||||
/obj/item/weapon/book/manual/engineering_guide
|
||||
name = "Engineering Textbook"
|
||||
icon_state ="bookEngineering2"
|
||||
@@ -725,7 +771,7 @@
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<iframe width='100%' height='97%' src="http://baystation12.net/wiki/index.php?title=Guide_to_Engineering&printable=yes&remove_links=1" frameborder="0" id="main_frame"></iframe> </body>
|
||||
<iframe width='100%' height='100%' src="http://baystation12.net/wiki/index.php?title=Guide_to_Engineering&printable=yes&remove_links=1" frameborder="0" id="main_frame"></iframe> </body>
|
||||
|
||||
</html>
|
||||
|
||||
|
||||
@@ -163,7 +163,7 @@
|
||||
new /obj/item/weapon/reagent_containers/pill/kelotane( src )
|
||||
|
||||
/obj/item/weapon/storage/pill_bottle/antitox
|
||||
name = "bottle of anti-toxin pills"
|
||||
name = "Dylovene pills"
|
||||
desc = "Contains pills used to counter toxins."
|
||||
|
||||
New()
|
||||
@@ -177,7 +177,7 @@
|
||||
new /obj/item/weapon/reagent_containers/pill/antitox( src )
|
||||
|
||||
/obj/item/weapon/storage/pill_bottle/inaprovaline
|
||||
name = "bottle of inaprovaline pills"
|
||||
name = "Inaprovaline pills"
|
||||
desc = "Contains pills used to stabilize patients."
|
||||
|
||||
New()
|
||||
@@ -190,6 +190,19 @@
|
||||
new /obj/item/weapon/reagent_containers/pill/inaprovaline( src )
|
||||
new /obj/item/weapon/reagent_containers/pill/inaprovaline( src )
|
||||
|
||||
/obj/item/weapon/storage/pill_bottle/tramadol
|
||||
name = "Tramadol Pills"
|
||||
desc = "Contains pills used to relieve pain."
|
||||
|
||||
New()
|
||||
..()
|
||||
new /obj/item/weapon/reagent_containers/pill/tramadol( src )
|
||||
new /obj/item/weapon/reagent_containers/pill/tramadol( src )
|
||||
new /obj/item/weapon/reagent_containers/pill/tramadol( src )
|
||||
new /obj/item/weapon/reagent_containers/pill/tramadol( src )
|
||||
new /obj/item/weapon/reagent_containers/pill/tramadol( src )
|
||||
new /obj/item/weapon/reagent_containers/pill/tramadol( src )
|
||||
new /obj/item/weapon/reagent_containers/pill/tramadol( src )
|
||||
|
||||
/obj/item/weapon/storage/pill_bottle/dice
|
||||
name = "pack of dice"
|
||||
|
||||
@@ -620,6 +620,35 @@ LOOK FOR SURGERY.DM*/
|
||||
return
|
||||
*/
|
||||
|
||||
/*
|
||||
* Researchable Scalpels
|
||||
*/
|
||||
/obj/item/weapon/scalpel/laser1
|
||||
name = "laser scalpel"
|
||||
desc = "A scalpel augmented with a directed laser, for more precise cutting without blood entering the field. This one looks basic and could be improved."
|
||||
icon_state = "scalpel_laser1_on"
|
||||
damtype = "fire"
|
||||
|
||||
/obj/item/weapon/scalpel/laser2
|
||||
name = "laser scalpel"
|
||||
desc = "A scalpel augmented with a directed laser, for more precise cutting without blood entering the field. This one looks somewhat advanced."
|
||||
icon_state = "scalpel_laser2_on"
|
||||
damtype = "fire"
|
||||
force = 12.0
|
||||
|
||||
/obj/item/weapon/scalpel/laser3
|
||||
name = "laser scalpel"
|
||||
desc = "A scalpel augmented with a directed laser, for more precise cutting without blood entering the field. This one looks to be the pinnacle of precision energy cutlery!"
|
||||
icon_state = "scalpel_laser3_on"
|
||||
damtype = "fire"
|
||||
force = 15.0
|
||||
|
||||
/obj/item/weapon/scalpel/manager
|
||||
name = "incision management system"
|
||||
desc = "A true extension of the surgeon's body, this marvel instantly and completely prepares an incision allowing for the immediate commencement of therapeutic steps."
|
||||
icon_state = "scalpel_manager_on"
|
||||
force = 7.5
|
||||
|
||||
/*
|
||||
* Circular Saw
|
||||
*/
|
||||
@@ -819,4 +848,4 @@ LOOK FOR SURGERY.DM*/
|
||||
throw_speed = 3
|
||||
throw_range = 5
|
||||
w_class = 2.0
|
||||
attack_verb = list("attacked", "hit", "bludgeoned")
|
||||
attack_verb = list("attacked", "hit", "bludgeoned")
|
||||
|
||||
@@ -87,6 +87,9 @@
|
||||
new /obj/item/weapon/weldingtool/largetank(src)
|
||||
new /obj/item/weapon/weldingtool/largetank(src)
|
||||
new /obj/item/weapon/weldingtool/largetank(src)
|
||||
new /obj/item/weapon/weldpack(src)
|
||||
new /obj/item/weapon/weldpack(src)
|
||||
new /obj/item/weapon/weldpack(src)
|
||||
return
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
+22
-11
@@ -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
|
||||
@@ -216,13 +216,24 @@
|
||||
|
||||
var/old_lumcount = lighting_lumcount - initial(lighting_lumcount)
|
||||
|
||||
//world << "Replacing [src.type] with [N]"
|
||||
|
||||
if(connections) connections.erase_all()
|
||||
|
||||
if(istype(src,/turf/simulated))
|
||||
//Yeah, we're just going to rebuild the whole thing.
|
||||
//Despite this being called a bunch during explosions,
|
||||
//the zone will only really do heavy lifting once.
|
||||
var/turf/simulated/S = src
|
||||
if(S.zone) S.zone.rebuild()
|
||||
|
||||
if(ispath(N, /turf/simulated/floor))
|
||||
//if the old turf had a zone, connect the new turf to it as well - Cael
|
||||
//Adjusted by SkyMarshal 5/10/13 - The air master will handle the addition of the new turf.
|
||||
if(zone)
|
||||
zone.RemoveTurf(src)
|
||||
if(!zone.CheckStatus())
|
||||
zone.SetStatus(ZONE_ACTIVE)
|
||||
//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 +247,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 +265,7 @@
|
||||
lighting_controller.changed_turfs += W
|
||||
|
||||
if(air_master)
|
||||
air_master.AddTurfToUpdate(src)
|
||||
air_master.mark_for_update(src)
|
||||
|
||||
W.levelupdate()
|
||||
return W
|
||||
|
||||
+3
-1
@@ -6,6 +6,8 @@ var/global/obj/effect/overlay/plmaster = null
|
||||
var/global/obj/effect/overlay/slmaster = null
|
||||
|
||||
|
||||
var/global/list/active_areas = list()
|
||||
var/global/list/all_areas = list()
|
||||
var/global/list/machines = list()
|
||||
var/global/list/processing_objects = list()
|
||||
var/global/list/active_diseases = list()
|
||||
@@ -114,7 +116,7 @@ var/list/reg_dna = list( )
|
||||
var/mouse_respawn_time = 5 //Amount of time that must pass between a player dying as a mouse and repawning as a mouse. In minutes.
|
||||
|
||||
var/CELLRATE = 0.002 // multiplier for watts per tick <> cell storage (eg: .002 means if there is a load of 1000 watts, 20 units will be taken from a cell per second)
|
||||
var/CHARGELEVEL = 0.001 // Cap for how fast cells charge, as a percentage-per-tick (.001 means cellcharge is capped to 1% per second)
|
||||
var/CHARGELEVEL = 0.0005 // Cap for how fast cells charge, as a percentage-per-tick (.001 means cellcharge is capped to 1% per second)
|
||||
|
||||
var/shuttle_z = 2 //default
|
||||
var/airtunnel_start = 68 // default
|
||||
|
||||
@@ -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++
|
||||
|
||||
@@ -125,20 +125,20 @@ var/intercom_range_display_status = 0
|
||||
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/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/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/ticklag
|
||||
,/client/proc/cmd_admin_grantfullaccess
|
||||
,/client/proc/kaboom
|
||||
,/client/proc/splash
|
||||
@@ -157,10 +157,10 @@ var/list/debug_verbs = list (
|
||||
,/client/proc/Zone_Info
|
||||
,/client/proc/Test_ZAS_Connection
|
||||
,/client/proc/ZoneTick
|
||||
,/client/proc/TestZASRebuild
|
||||
,/client/proc/rebootAirMaster
|
||||
,/client/proc/hide_debug_verbs
|
||||
,/client/proc/testZAScolors
|
||||
,/client/proc/testZAScolors_remove
|
||||
,/client/proc/testZAScolors
|
||||
,/client/proc/testZAScolors_remove
|
||||
)
|
||||
|
||||
|
||||
@@ -194,16 +194,17 @@ var/list/debug_verbs = list (
|
||||
testZAScolors_zones += Z
|
||||
if(recurse_level > 10)
|
||||
return
|
||||
var/icon/yellow = new('icons/misc/debug_group.dmi', "yellow")
|
||||
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)
|
||||
for(var/connection_edge/zone/edge in Z.edges)
|
||||
var/zone/connected = edge.get_connected_zone(Z)
|
||||
if(connected in testZAScolors_zones)
|
||||
continue
|
||||
recurse_zone(connected,recurse_level+1)
|
||||
|
||||
|
||||
|
||||
/client/proc/testZAScolors()
|
||||
set category = "ZAS"
|
||||
@@ -212,17 +213,17 @@ var/list/debug_verbs = list (
|
||||
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/turf/simulated/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")
|
||||
var/icon/green = new('icons/misc/debug_group.dmi', "green")
|
||||
var/icon/blue = new('icons/misc/debug_group.dmi', "blue")
|
||||
|
||||
if(!usedZAScolors)
|
||||
if(!usedZAScolors)
|
||||
usr << "ZAS Test Colors"
|
||||
usr << "Green = Zone you are standing in"
|
||||
usr << "Blue = Connected zone to the zone you are standing in"
|
||||
@@ -234,12 +235,14 @@ var/list/debug_verbs = list (
|
||||
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/connection_edge/zone/edge in location.zone.edges)
|
||||
var/zone/Z = edge.get_connected_zone(location.zone)
|
||||
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)
|
||||
for(var/connection_edge/zone/z_edge in Z.edges)
|
||||
var/zone/connected = z_edge.get_connected_zone(Z)
|
||||
if(connected in testZAScolors_zones)
|
||||
continue
|
||||
recurse_zone(connected,1)
|
||||
@@ -264,6 +267,19 @@ var/list/debug_verbs = list (
|
||||
if(i.icon_state == "zasdebug")
|
||||
images.Remove(i)
|
||||
|
||||
/client/proc/rebootAirMaster()
|
||||
set category = "ZAS"
|
||||
set name = "Reboot ZAS"
|
||||
|
||||
if(alert("This will destroy and remake all zone geometry on the whole map.","Reboot ZAS","Reboot ZAS","Nevermind") == "Reboot ZAS")
|
||||
var/datum/controller/air_system/old_air = air_master
|
||||
for(var/zone/zone in old_air.zones)
|
||||
zone.c_invalidate()
|
||||
del old_air
|
||||
air_master = new
|
||||
air_master.Setup()
|
||||
spawn air_master.Start()
|
||||
|
||||
|
||||
/client/proc/count_objects_on_z_level()
|
||||
set category = "Mapping"
|
||||
|
||||
@@ -200,7 +200,7 @@ BLIND // can't see anything
|
||||
armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 100, rad = 50)
|
||||
flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE
|
||||
cold_protection = HEAD
|
||||
min_cold_protection_temperature = SPACE_HELMET_MIN_COLD_PROTECITON_TEMPERATURE
|
||||
min_cold_protection_temperature = SPACE_HELMET_MIN_COLD_PROTECTION_TEMPERATURE
|
||||
siemens_coefficient = 0.9
|
||||
species_restricted = list("exclude","Diona","Vox")
|
||||
|
||||
@@ -219,7 +219,7 @@ BLIND // can't see anything
|
||||
armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 100, rad = 50)
|
||||
flags_inv = HIDEGLOVES|HIDESHOES|HIDEJUMPSUIT
|
||||
cold_protection = UPPER_TORSO | LOWER_TORSO | LEGS | FEET | ARMS | HANDS
|
||||
min_cold_protection_temperature = SPACE_SUIT_MIN_COLD_PROTECITON_TEMPERATURE
|
||||
min_cold_protection_temperature = SPACE_SUIT_MIN_COLD_PROTECTION_TEMPERATURE
|
||||
siemens_coefficient = 0.9
|
||||
species_restricted = list("exclude","Diona","Vox")
|
||||
|
||||
|
||||
@@ -70,6 +70,12 @@
|
||||
icon_state = "hipster_glasses"
|
||||
item_state = "hipster_glasses"
|
||||
|
||||
/obj/item/clothing/glasses/threedglasses
|
||||
desc = "A long time ago, people used these glasses to makes images from screens threedimensional."
|
||||
name = "3D glasses"
|
||||
icon_state = "3d"
|
||||
item_state = "3d"
|
||||
|
||||
/obj/item/clothing/glasses/gglasses
|
||||
name = "Green Glasses"
|
||||
desc = "Forest green glasses, like the kind you'd wear when hatching a nasty scheme."
|
||||
|
||||
@@ -28,9 +28,9 @@
|
||||
item_color="brown"
|
||||
|
||||
cold_protection = HANDS
|
||||
min_cold_protection_temperature = GLOVES_MIN_COLD_PROTECITON_TEMPERATURE
|
||||
min_cold_protection_temperature = GLOVES_MIN_COLD_PROTECTION_TEMPERATURE
|
||||
heat_protection = HANDS
|
||||
max_heat_protection_temperature = GLOVES_MAX_HEAT_PROTECITON_TEMPERATURE
|
||||
max_heat_protection_temperature = GLOVES_MAX_HEAT_PROTECTION_TEMPERATURE
|
||||
|
||||
|
||||
hos
|
||||
|
||||
@@ -5,9 +5,9 @@
|
||||
item_state = "egloves"
|
||||
item_color = "captain"
|
||||
cold_protection = HANDS
|
||||
min_cold_protection_temperature = GLOVES_MIN_COLD_PROTECITON_TEMPERATURE
|
||||
min_cold_protection_temperature = GLOVES_MIN_COLD_PROTECTION_TEMPERATURE
|
||||
heat_protection = HANDS
|
||||
max_heat_protection_temperature = GLOVES_MAX_HEAT_PROTECITON_TEMPERATURE
|
||||
max_heat_protection_temperature = GLOVES_MAX_HEAT_PROTECTION_TEMPERATURE
|
||||
|
||||
/obj/item/clothing/gloves/cyborg
|
||||
desc = "beep boop borp"
|
||||
@@ -25,9 +25,9 @@
|
||||
permeability_coefficient = 0.05
|
||||
|
||||
cold_protection = HANDS
|
||||
min_cold_protection_temperature = GLOVES_MIN_COLD_PROTECITON_TEMPERATURE
|
||||
min_cold_protection_temperature = GLOVES_MIN_COLD_PROTECTION_TEMPERATURE
|
||||
heat_protection = HANDS
|
||||
max_heat_protection_temperature = GLOVES_MAX_HEAT_PROTECITON_TEMPERATURE
|
||||
max_heat_protection_temperature = GLOVES_MAX_HEAT_PROTECTION_TEMPERATURE
|
||||
|
||||
/obj/item/clothing/gloves/combat //Combined effect of SWAT gloves and insulated gloves
|
||||
desc = "These tactical gloves are somewhat fire and impact resistant."
|
||||
@@ -37,9 +37,9 @@
|
||||
siemens_coefficient = 0
|
||||
permeability_coefficient = 0.05
|
||||
cold_protection = HANDS
|
||||
min_cold_protection_temperature = GLOVES_MIN_COLD_PROTECITON_TEMPERATURE
|
||||
min_cold_protection_temperature = GLOVES_MIN_COLD_PROTECTION_TEMPERATURE
|
||||
heat_protection = HANDS
|
||||
max_heat_protection_temperature = GLOVES_MAX_HEAT_PROTECITON_TEMPERATURE
|
||||
max_heat_protection_temperature = GLOVES_MAX_HEAT_PROTECTION_TEMPERATURE
|
||||
|
||||
/obj/item/clothing/gloves/latex
|
||||
name = "latex gloves"
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
name = "firefighter helmet"
|
||||
flags = FPRINT | TABLEPASS | STOPSPRESSUREDMAGE
|
||||
heat_protection = HEAD
|
||||
max_heat_protection_temperature = FIRE_HELMET_MAX_HEAT_PROTECITON_TEMPERATURE
|
||||
max_heat_protection_temperature = FIRE_HELMET_MAX_HEAT_PROTECTION_TEMPERATURE
|
||||
|
||||
/obj/item/clothing/head/hardhat/white
|
||||
icon_state = "hardhat0_white"
|
||||
@@ -56,7 +56,7 @@
|
||||
item_color = "white"
|
||||
flags = FPRINT | TABLEPASS | STOPSPRESSUREDMAGE
|
||||
heat_protection = HEAD
|
||||
max_heat_protection_temperature = FIRE_HELMET_MAX_HEAT_PROTECITON_TEMPERATURE
|
||||
max_heat_protection_temperature = FIRE_HELMET_MAX_HEAT_PROTECTION_TEMPERATURE
|
||||
|
||||
/obj/item/clothing/head/hardhat/dblue
|
||||
icon_state = "hardhat0_dblue"
|
||||
|
||||
@@ -7,9 +7,9 @@
|
||||
armor = list(melee = 50, bullet = 15, laser = 50,energy = 10, bomb = 25, bio = 0, rad = 0)
|
||||
flags_inv = HIDEEARS|HIDEEYES
|
||||
cold_protection = HEAD
|
||||
min_cold_protection_temperature = HELMET_MIN_COLD_PROTECITON_TEMPERATURE
|
||||
min_cold_protection_temperature = HELMET_MIN_COLD_PROTECTION_TEMPERATURE
|
||||
heat_protection = HEAD
|
||||
max_heat_protection_temperature = HELMET_MAX_HEAT_PROTECITON_TEMPERATURE
|
||||
max_heat_protection_temperature = HELMET_MAX_HEAT_PROTECTION_TEMPERATURE
|
||||
siemens_coefficient = 0.7
|
||||
|
||||
/obj/item/clothing/head/helmet/warden
|
||||
@@ -37,7 +37,7 @@
|
||||
armor = list(melee = 80, bullet = 60, laser = 50,energy = 25, bomb = 50, bio = 10, rad = 0)
|
||||
flags_inv = HIDEEARS|HIDEEYES
|
||||
cold_protection = HEAD
|
||||
min_cold_protection_temperature = SPACE_HELMET_MIN_COLD_PROTECITON_TEMPERATURE
|
||||
min_cold_protection_temperature = SPACE_HELMET_MIN_COLD_PROTECTION_TEMPERATURE
|
||||
siemens_coefficient = 0.5
|
||||
|
||||
/obj/item/clothing/head/helmet/thunderdome
|
||||
@@ -48,7 +48,7 @@
|
||||
item_state = "thunderdome"
|
||||
armor = list(melee = 80, bullet = 60, laser = 50,energy = 10, bomb = 25, bio = 10, rad = 0)
|
||||
cold_protection = HEAD
|
||||
min_cold_protection_temperature = SPACE_HELMET_MIN_COLD_PROTECITON_TEMPERATURE
|
||||
min_cold_protection_temperature = SPACE_HELMET_MIN_COLD_PROTECTION_TEMPERATURE
|
||||
siemens_coefficient = 1
|
||||
|
||||
/obj/item/clothing/head/helmet/gladiator
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
flags = FPRINT|TABLEPASS
|
||||
flags_inv = 0
|
||||
cold_protection = HEAD
|
||||
min_cold_protection_temperature = SPACE_HELMET_MIN_COLD_PROTECITON_TEMPERATURE
|
||||
min_cold_protection_temperature = SPACE_HELMET_MIN_COLD_PROTECTION_TEMPERATURE
|
||||
siemens_coefficient = 0.9
|
||||
|
||||
//Chaplain
|
||||
|
||||
@@ -5,9 +5,9 @@
|
||||
desc = "A pair of black shoes."
|
||||
|
||||
cold_protection = FEET
|
||||
min_cold_protection_temperature = SHOE_MIN_COLD_PROTECITON_TEMPERATURE
|
||||
min_cold_protection_temperature = SHOE_MIN_COLD_PROTECTION_TEMPERATURE
|
||||
heat_protection = FEET
|
||||
max_heat_protection_temperature = SHOE_MAX_HEAT_PROTECITON_TEMPERATURE
|
||||
max_heat_protection_temperature = SHOE_MAX_HEAT_PROTECTION_TEMPERATURE
|
||||
|
||||
redcoat
|
||||
item_color = "redcoat" //Exists for washing machines. Is not different from black shoes in any way.
|
||||
|
||||
@@ -32,9 +32,9 @@
|
||||
siemens_coefficient = 0.6
|
||||
|
||||
cold_protection = FEET
|
||||
min_cold_protection_temperature = SHOE_MIN_COLD_PROTECITON_TEMPERATURE
|
||||
min_cold_protection_temperature = SHOE_MIN_COLD_PROTECTION_TEMPERATURE
|
||||
heat_protection = FEET
|
||||
max_heat_protection_temperature = SHOE_MAX_HEAT_PROTECITON_TEMPERATURE
|
||||
max_heat_protection_temperature = SHOE_MAX_HEAT_PROTECTION_TEMPERATURE
|
||||
|
||||
/obj/item/clothing/shoes/space_ninja
|
||||
name = "ninja shoes"
|
||||
@@ -46,9 +46,9 @@
|
||||
siemens_coefficient = 0.2
|
||||
|
||||
cold_protection = FEET
|
||||
min_cold_protection_temperature = SHOE_MIN_COLD_PROTECITON_TEMPERATURE
|
||||
min_cold_protection_temperature = SHOE_MIN_COLD_PROTECTION_TEMPERATURE
|
||||
heat_protection = FEET
|
||||
max_heat_protection_temperature = SHOE_MAX_HEAT_PROTECITON_TEMPERATURE
|
||||
max_heat_protection_temperature = SHOE_MAX_HEAT_PROTECTION_TEMPERATURE
|
||||
species_restricted = null
|
||||
|
||||
/obj/item/clothing/shoes/sandal
|
||||
@@ -98,9 +98,9 @@
|
||||
siemens_coefficient = 0.7
|
||||
|
||||
cold_protection = FEET
|
||||
min_cold_protection_temperature = SHOE_MIN_COLD_PROTECITON_TEMPERATURE
|
||||
min_cold_protection_temperature = SHOE_MIN_COLD_PROTECTION_TEMPERATURE
|
||||
heat_protection = FEET
|
||||
max_heat_protection_temperature = SHOE_MAX_HEAT_PROTECITON_TEMPERATURE
|
||||
max_heat_protection_temperature = SHOE_MAX_HEAT_PROTECTION_TEMPERATURE
|
||||
species_restricted = null
|
||||
|
||||
/obj/item/clothing/shoes/cyborg
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
name = "Skrellian helmet"
|
||||
desc = "Smoothly contoured and polished to a shine. Still looks like a fishbowl."
|
||||
armor = list(melee = 20, bullet = 20, laser = 50,energy = 50, bomb = 50, bio = 100, rad = 100)
|
||||
max_heat_protection_temperature = SPACE_SUIT_MAX_HEAT_PROTECITON_TEMPERATURE
|
||||
max_heat_protection_temperature = SPACE_SUIT_MAX_HEAT_PROTECTION_TEMPERATURE
|
||||
species_restricted = list("Skrell","Human")
|
||||
|
||||
/obj/item/clothing/head/helmet/space/skrell/white
|
||||
@@ -38,7 +38,7 @@
|
||||
armor = list(melee = 20, bullet = 20, laser = 50,energy = 50, bomb = 50, bio = 100, rad = 100)
|
||||
allowed = list(/obj/item/device/flashlight,/obj/item/weapon/tank,/obj/item/weapon/storage/bag/ore,/obj/item/device/t_scanner,/obj/item/weapon/pickaxe, /obj/item/weapon/rcd)
|
||||
heat_protection = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS
|
||||
max_heat_protection_temperature = SPACE_SUIT_MAX_HEAT_PROTECITON_TEMPERATURE
|
||||
max_heat_protection_temperature = SPACE_SUIT_MAX_HEAT_PROTECTION_TEMPERATURE
|
||||
species_restricted = list("Skrell","Human")
|
||||
|
||||
/obj/item/clothing/suit/space/skrell/white
|
||||
@@ -56,7 +56,7 @@
|
||||
/obj/item/clothing/head/helmet/space/unathi
|
||||
armor = list(melee = 40, bullet = 30, laser = 30,energy = 15, bomb = 35, bio = 100, rad = 50)
|
||||
heat_protection = HEAD
|
||||
max_heat_protection_temperature = SPACE_SUIT_MAX_HEAT_PROTECITON_TEMPERATURE
|
||||
max_heat_protection_temperature = SPACE_SUIT_MAX_HEAT_PROTECTION_TEMPERATURE
|
||||
var/up = 0 //So Unathi helmets play nicely with the weldervision check.
|
||||
species_restricted = list("Unathi")
|
||||
|
||||
@@ -71,7 +71,7 @@
|
||||
armor = list(melee = 40, bullet = 30, laser = 30,energy = 15, bomb = 35, bio = 100, rad = 50)
|
||||
allowed = list(/obj/item/device/flashlight,/obj/item/weapon/tank,/obj/item/weapon/storage/bag/ore,/obj/item/device/t_scanner,/obj/item/weapon/pickaxe, /obj/item/weapon/rcd)
|
||||
heat_protection = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS
|
||||
max_heat_protection_temperature = SPACE_SUIT_MAX_HEAT_PROTECITON_TEMPERATURE
|
||||
max_heat_protection_temperature = SPACE_SUIT_MAX_HEAT_PROTECTION_TEMPERATURE
|
||||
species_restricted = list("Unathi")
|
||||
|
||||
/obj/item/clothing/suit/space/unathi/rig_cheap
|
||||
@@ -105,7 +105,7 @@
|
||||
slowdown = 2
|
||||
armor = list(melee = 60, bullet = 50, laser = 30,energy = 15, bomb = 30, bio = 30, rad = 30)
|
||||
heat_protection = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS
|
||||
max_heat_protection_temperature = SPACE_SUIT_MAX_HEAT_PROTECITON_TEMPERATURE
|
||||
max_heat_protection_temperature = SPACE_SUIT_MAX_HEAT_PROTECTION_TEMPERATURE
|
||||
species_restricted = list("Vox")
|
||||
|
||||
/obj/item/clothing/head/helmet/space/vox
|
||||
|
||||
@@ -25,5 +25,5 @@
|
||||
armor = list(melee = 65, bullet = 50, laser = 50, energy = 25, bomb = 50, bio = 100, rad = 50)
|
||||
flags_inv = HIDEGLOVES|HIDESHOES|HIDEJUMPSUIT
|
||||
cold_protection = UPPER_TORSO | LOWER_TORSO | LEGS | FEET | ARMS | HANDS
|
||||
min_cold_protection_temperature = SPACE_SUIT_MIN_COLD_PROTECITON_TEMPERATURE
|
||||
min_cold_protection_temperature = SPACE_SUIT_MIN_COLD_PROTECTION_TEMPERATURE
|
||||
siemens_coefficient = 0.7
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
armor = list(melee = 65, bullet = 50, laser = 50, energy = 25, bomb = 50, bio = 100, rad = 50)
|
||||
flags_inv = HIDEGLOVES|HIDESHOES|HIDEJUMPSUIT
|
||||
cold_protection = UPPER_TORSO | LOWER_TORSO | LEGS | FEET | ARMS | HANDS
|
||||
min_cold_protection_temperature = SPACE_SUIT_MIN_COLD_PROTECITON_TEMPERATURE
|
||||
min_cold_protection_temperature = SPACE_SUIT_MIN_COLD_PROTECTION_TEMPERATURE
|
||||
siemens_coefficient = 0.7
|
||||
|
||||
//Deathsquad suit
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
item_color = "engineering" //Determines used sprites: rig[on]-[color] and rig[on]-[color]2 (lying down sprite)
|
||||
icon_action_button = "action_hardhat"
|
||||
heat_protection = HEAD
|
||||
max_heat_protection_temperature = SPACE_SUIT_MAX_HEAT_PROTECITON_TEMPERATURE
|
||||
max_heat_protection_temperature = SPACE_SUIT_MAX_HEAT_PROTECTION_TEMPERATURE
|
||||
species_restricted = list("exclude","Unathi","Tajaran","Skrell","Diona","Vox")
|
||||
|
||||
attack_self(mob/user)
|
||||
@@ -50,7 +50,7 @@
|
||||
armor = list(melee = 40, bullet = 5, laser = 20,energy = 5, bomb = 35, bio = 100, rad = 80)
|
||||
allowed = list(/obj/item/device/flashlight,/obj/item/weapon/tank,/obj/item/weapon/storage/bag/ore,/obj/item/device/t_scanner,/obj/item/weapon/pickaxe, /obj/item/weapon/rcd)
|
||||
heat_protection = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS
|
||||
max_heat_protection_temperature = SPACE_SUIT_MAX_HEAT_PROTECITON_TEMPERATURE
|
||||
max_heat_protection_temperature = SPACE_SUIT_MAX_HEAT_PROTECTION_TEMPERATURE
|
||||
species_restricted = list("exclude","Unathi","Tajaran","Diona","Vox")
|
||||
|
||||
//Chief Engineer's rig
|
||||
@@ -190,7 +190,7 @@
|
||||
item_state = "atmos_helm"
|
||||
item_color = "atmos"
|
||||
armor = list(melee = 40, bullet = 0, laser = 0, energy = 0, bomb = 25, bio = 100, rad = 0)
|
||||
max_heat_protection_temperature = FIRE_HELMET_MAX_HEAT_PROTECITON_TEMPERATURE
|
||||
max_heat_protection_temperature = FIRE_HELMET_MAX_HEAT_PROTECTION_TEMPERATURE
|
||||
|
||||
/obj/item/clothing/suit/space/rig/atmos
|
||||
desc = "A special suit that protects against hazardous, low pressure environments. Has reduced radiation shielding to allow for greater mobility."
|
||||
@@ -198,4 +198,4 @@
|
||||
name = "atmos hardsuit"
|
||||
item_state = "atmos_hardsuit"
|
||||
armor = list(melee = 40, bullet = 0, laser = 0, energy = 0, bomb = 25, bio = 100, rad = 0)
|
||||
max_heat_protection_temperature = FIRESUIT_MAX_HEAT_PROTECITON_TEMPERATURE
|
||||
max_heat_protection_temperature = FIRESUIT_MAX_HEAT_PROTECTION_TEMPERATURE
|
||||
|
||||
@@ -5,9 +5,9 @@
|
||||
flags = FPRINT | TABLEPASS
|
||||
|
||||
cold_protection = UPPER_TORSO|LOWER_TORSO
|
||||
min_cold_protection_temperature = ARMOR_MIN_COLD_PROTECITON_TEMPERATURE
|
||||
min_cold_protection_temperature = ARMOR_MIN_COLD_PROTECTION_TEMPERATURE
|
||||
heat_protection = UPPER_TORSO|LOWER_TORSO
|
||||
max_heat_protection_temperature = ARMOR_MAX_HEAT_PROTECITON_TEMPERATURE
|
||||
max_heat_protection_temperature = ARMOR_MAX_HEAT_PROTECTION_TEMPERATURE
|
||||
siemens_coefficient = 0.6
|
||||
|
||||
|
||||
@@ -77,7 +77,7 @@
|
||||
armor = list(melee = 80, bullet = 60, laser = 50,energy = 25, bomb = 50, bio = 0, rad = 0)
|
||||
flags_inv = HIDEGLOVES|HIDESHOES|HIDEJUMPSUIT
|
||||
cold_protection = UPPER_TORSO | LOWER_TORSO | LEGS | FEET | ARMS | HANDS
|
||||
min_cold_protection_temperature = SPACE_SUIT_MIN_COLD_PROTECITON_TEMPERATURE
|
||||
min_cold_protection_temperature = SPACE_SUIT_MIN_COLD_PROTECTION_TEMPERATURE
|
||||
siemens_coefficient = 0.5
|
||||
|
||||
|
||||
@@ -152,7 +152,7 @@
|
||||
allowed = list(/obj/item/weapon/gun/energy,/obj/item/weapon/melee/baton,/obj/item/weapon/handcuffs,/obj/item/weapon/tank/emergency_oxygen)
|
||||
flags_inv = HIDEGLOVES|HIDESHOES|HIDEJUMPSUIT
|
||||
cold_protection = UPPER_TORSO | LOWER_TORSO | LEGS | FEET | ARMS | HANDS
|
||||
min_cold_protection_temperature = SPACE_SUIT_MIN_COLD_PROTECITON_TEMPERATURE
|
||||
min_cold_protection_temperature = SPACE_SUIT_MIN_COLD_PROTECTION_TEMPERATURE
|
||||
siemens_coefficient = 0
|
||||
|
||||
/obj/item/clothing/suit/armor/heavy
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
flags_inv = HIDEGLOVES|HIDESHOES|HIDEJUMPSUIT
|
||||
flags = FPRINT | TABLEPASS | STOPSPRESSUREDMAGE
|
||||
heat_protection = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS
|
||||
max_heat_protection_temperature = FIRESUIT_MAX_HEAT_PROTECITON_TEMPERATURE
|
||||
max_heat_protection_temperature = FIRESUIT_MAX_HEAT_PROTECTION_TEMPERATURE
|
||||
cold_protection = UPPER_TORSO | LOWER_TORSO | LEGS | FEET | ARMS | HANDS
|
||||
|
||||
|
||||
@@ -67,7 +67,7 @@
|
||||
armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 100, bio = 0, rad = 0)
|
||||
flags_inv = HIDEJUMPSUIT
|
||||
heat_protection = UPPER_TORSO|LOWER_TORSO
|
||||
max_heat_protection_temperature = ARMOR_MAX_HEAT_PROTECITON_TEMPERATURE
|
||||
max_heat_protection_temperature = ARMOR_MAX_HEAT_PROTECTION_TEMPERATURE
|
||||
siemens_coefficient = 0
|
||||
|
||||
|
||||
|
||||
@@ -88,7 +88,7 @@
|
||||
flags = FPRINT | TABLEPASS
|
||||
body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS
|
||||
cold_protection = UPPER_TORSO | LOWER_TORSO | LEGS | ARMS //Needs gloves and shoes with cold protection to be fully protected.
|
||||
min_cold_protection_temperature = SPACE_SUIT_MIN_COLD_PROTECITON_TEMPERATURE
|
||||
min_cold_protection_temperature = SPACE_SUIT_MIN_COLD_PROTECTION_TEMPERATURE
|
||||
|
||||
/obj/item/clothing/under/acj
|
||||
name = "administrative cybernetic jumpsuit"
|
||||
@@ -102,7 +102,7 @@
|
||||
body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS
|
||||
armor = list(melee = 100, bullet = 100, laser = 100,energy = 100, bomb = 100, bio = 100, rad = 100)
|
||||
cold_protection = UPPER_TORSO | LOWER_TORSO | LEGS | FEET | ARMS | HANDS
|
||||
min_cold_protection_temperature = SPACE_SUIT_MIN_COLD_PROTECITON_TEMPERATURE
|
||||
min_cold_protection_temperature = SPACE_SUIT_MIN_COLD_PROTECTION_TEMPERATURE
|
||||
siemens_coefficient = 0
|
||||
|
||||
/obj/item/clothing/under/owl
|
||||
|
||||
@@ -87,6 +87,9 @@
|
||||
New()
|
||||
..()
|
||||
new /obj/item/weapon/book/manual/medical_cloning(src)
|
||||
new /obj/item/weapon/book/manual/medical_diagnostics_manual(src)
|
||||
new /obj/item/weapon/book/manual/medical_diagnostics_manual(src)
|
||||
new /obj/item/weapon/book/manual/medical_diagnostics_manual(src)
|
||||
update_icon()
|
||||
|
||||
|
||||
|
||||
@@ -119,7 +119,6 @@
|
||||
else
|
||||
machine.selected &= ~ORE_PROC_CLOWN
|
||||
|
||||
|
||||
//On or off
|
||||
dat += text("Machine is currently ")
|
||||
if (machine.on==1)
|
||||
@@ -179,6 +178,7 @@
|
||||
machine.selected |= ORE_PROC_CLOWN
|
||||
else
|
||||
machine.selected &= ~ORE_PROC_CLOWN
|
||||
|
||||
if(href_list["set_on"])
|
||||
if (href_list["set_on"] == "on")
|
||||
machine.on = 1
|
||||
@@ -316,6 +316,7 @@
|
||||
else
|
||||
on = 0
|
||||
continue
|
||||
/*
|
||||
if (selected == ORE_PROC_GLASS + ORE_PROC_PLASMA)
|
||||
if (ore_glass > 0 && ore_plasma > 0)
|
||||
ore_glass--;
|
||||
@@ -333,8 +334,8 @@
|
||||
else
|
||||
on = 0
|
||||
continue
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
if (selected == ORE_PROC_URANIUM + ORE_PROC_DIAMOND)
|
||||
if (ore_uranium >= 2 && ore_diamond >= 1)
|
||||
@@ -353,7 +354,6 @@
|
||||
on = 0
|
||||
continue
|
||||
|
||||
*/
|
||||
|
||||
|
||||
//if a non valid combination is selected
|
||||
|
||||
@@ -386,7 +386,7 @@
|
||||
|
||||
//spread some viruses while we are at it
|
||||
if (virus2.len > 0)
|
||||
if (get_infection_chance(src) && prob(20))
|
||||
if (prob(10) && get_infection_chance(src))
|
||||
// log_debug("[src] : Exhaling some viruses")
|
||||
for(var/mob/living/carbon/M in view(1,src))
|
||||
src.spread_disease_to(M)
|
||||
@@ -1076,7 +1076,7 @@
|
||||
E = get_visible_implants(0)
|
||||
if(!E.len)
|
||||
embedded_flag = 0
|
||||
|
||||
|
||||
|
||||
//Eyes
|
||||
if(sdisabilities & BLIND) //disabled-blind, doesn't get better on its own
|
||||
@@ -1134,6 +1134,10 @@
|
||||
|
||||
if(!client) return 0
|
||||
|
||||
if(hud_updateflag)
|
||||
handle_hud_list()
|
||||
|
||||
|
||||
for(var/image/hud in client.images)
|
||||
if(copytext(hud.icon_state,1,4) == "hud") //ugly, but icon comparison is worse, I believe
|
||||
client.images.Remove(hud)
|
||||
@@ -1704,7 +1708,6 @@
|
||||
holder.icon_state = "hudninja"
|
||||
|
||||
hud_list[SPECIALROLE_HUD] = holder
|
||||
|
||||
hud_updateflag = 0
|
||||
|
||||
|
||||
|
||||
@@ -836,9 +836,9 @@ mob/living/carbon/slime/var/temperature_resistance = T0C+75
|
||||
flags_inv = HIDEGLOVES|HIDESHOES|HIDEJUMPSUIT
|
||||
flags = FPRINT | TABLEPASS | ONESIZEFITSALL | STOPSPRESSUREDMAGE
|
||||
heat_protection = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS | HEAD
|
||||
max_heat_protection_temperature = FIRESUIT_MAX_HEAT_PROTECITON_TEMPERATURE
|
||||
max_heat_protection_temperature = FIRESUIT_MAX_HEAT_PROTECTION_TEMPERATURE
|
||||
cold_protection = UPPER_TORSO | LOWER_TORSO | LEGS | FEET | ARMS | HANDS | HEAD
|
||||
min_cold_protection_temperature = SPACE_SUIT_MIN_COLD_PROTECITON_TEMPERATURE
|
||||
min_cold_protection_temperature = SPACE_SUIT_MIN_COLD_PROTECTION_TEMPERATURE
|
||||
canremove = 0
|
||||
armor = list(melee = 80, bullet = 20, laser = 20, energy = 10, bomb = 0, bio = 0, rad = 0)
|
||||
|
||||
@@ -890,7 +890,7 @@ mob/living/carbon/slime/var/temperature_resistance = T0C+75
|
||||
unacidable = 1
|
||||
flags = FPRINT | TABLEPASS | STOPSPRESSUREDMAGE
|
||||
heat_protection = HEAD
|
||||
max_heat_protection_temperature = FIRE_HELMET_MAX_HEAT_PROTECITON_TEMPERATURE
|
||||
max_heat_protection_temperature = FIRE_HELMET_MAX_HEAT_PROTECTION_TEMPERATURE
|
||||
armor = list(melee = 80, bullet = 20, laser = 20, energy = 10, bomb = 0, bio = 0, rad = 0)
|
||||
|
||||
/obj/effect/golemrune
|
||||
|
||||
@@ -256,6 +256,9 @@
|
||||
buckled = initial(src.buckled)
|
||||
if(iscarbon(src))
|
||||
var/mob/living/carbon/C = src
|
||||
|
||||
if (C.handcuffed && !initial(C.handcuffed))
|
||||
C.drop_from_inventory(C.handcuffed)
|
||||
C.handcuffed = initial(C.handcuffed)
|
||||
|
||||
if (C.legcuffed && !initial(C.legcuffed))
|
||||
@@ -635,8 +638,6 @@
|
||||
O.show_message("\red <B>[CM] manages to remove the handcuffs!</B>", 1)
|
||||
CM << "\blue You successfully remove \the [CM.handcuffed]."
|
||||
CM.drop_from_inventory(CM.handcuffed)
|
||||
CM.handcuffed = null
|
||||
CM.update_inv_handcuffed()
|
||||
|
||||
else if(CM.legcuffed && CM.canmove && (CM.last_special <= world.time))
|
||||
CM.next_move = world.time + 100
|
||||
|
||||
@@ -94,7 +94,7 @@ var/list/ai_list = list()
|
||||
/mob/living/silicon/ai/proc/ai_camera_list, /mob/living/silicon/ai/proc/ai_network_change, \
|
||||
/mob/living/silicon/ai/proc/ai_statuschange, /mob/living/silicon/ai/proc/ai_hologram_change, \
|
||||
/mob/living/silicon/ai/proc/toggle_camera_light)
|
||||
|
||||
|
||||
|
||||
if(!safety)//Only used by AIize() to successfully spawn an AI.
|
||||
if (!B)//If there is no player/brain inside.
|
||||
@@ -116,6 +116,10 @@ var/list/ai_list = list()
|
||||
|
||||
job = "AI"
|
||||
|
||||
spawn(5)
|
||||
new /obj/machinery/ai_powersupply(src)
|
||||
|
||||
|
||||
hud_list[HEALTH_HUD] = image('icons/mob/hud.dmi', src, "hudblank")
|
||||
hud_list[STATUS_HUD] = image('icons/mob/hud.dmi', src, "hudblank")
|
||||
hud_list[ID_HUD] = image('icons/mob/hud.dmi', src, "hudblank")
|
||||
@@ -135,6 +139,37 @@ var/list/ai_list = list()
|
||||
..()
|
||||
|
||||
|
||||
/*
|
||||
The AI Power supply is a dummy object used for powering the AI since only machinery should be using power.
|
||||
The alternative was to rewrite a bunch of AI code instead here we are.
|
||||
*/
|
||||
/obj/machinery/ai_powersupply
|
||||
name="Power Supply"
|
||||
active_power_usage=1000
|
||||
use_power = 2
|
||||
power_channel = EQUIP
|
||||
var/mob/living/silicon/ai/powered_ai = null
|
||||
invisibility = 100
|
||||
|
||||
/obj/machinery/ai_powersupply/New(var/mob/living/silicon/ai/ai=null)
|
||||
powered_ai = ai
|
||||
if(isnull(powered_ai))
|
||||
Del()
|
||||
|
||||
loc = powered_ai.loc
|
||||
use_power(1) // Just incase we need to wake up the power system.
|
||||
|
||||
..()
|
||||
|
||||
/obj/machinery/ai_powersupply/process()
|
||||
if(!powered_ai || powered_ai.stat & DEAD)
|
||||
Del()
|
||||
if(!powered_ai.anchored)
|
||||
loc = powered_ai.loc
|
||||
use_power = 0
|
||||
if(powered_ai.anchored)
|
||||
use_power = 2
|
||||
|
||||
/mob/living/silicon/ai/verb/pick_icon()
|
||||
set category = "AI Commands"
|
||||
set name = "Set AI Core Display"
|
||||
|
||||
@@ -58,10 +58,20 @@
|
||||
src.see_in_dark = 8
|
||||
src.see_invisible = SEE_INVISIBLE_LEVEL_TWO
|
||||
|
||||
|
||||
//Congratulations! You've found a way for AI's to run without using power!
|
||||
//Todo: Without snowflaking up master_controller procs find a way to make AI use_power but only when APC's clear the area usage the tick prior
|
||||
// since mobs are in master_controller before machinery. We also have to do it in a manner where we don't reset the entire area's need to update
|
||||
// the power usage.
|
||||
//
|
||||
// We can probably create a new machine that resides inside of the AI contents that uses power using the idle_usage of 1000 and nothing else and
|
||||
// be fine.
|
||||
/*
|
||||
var/area/home = get_area(src)
|
||||
if(!home) return//something to do with malf fucking things up I guess. <-- aisat is gone. is this still necessary? ~Carn
|
||||
if(home.powered(EQUIP))
|
||||
home.use_power(1000, EQUIP)
|
||||
*/
|
||||
|
||||
if (src:aiRestorePowerRoutine==2)
|
||||
src << "Alert cancelled. Power has been restored without our assistance."
|
||||
@@ -168,4 +178,4 @@
|
||||
if(fire_res_on_core)
|
||||
health = 100 - getOxyLoss() - getToxLoss() - getBruteLoss()
|
||||
else
|
||||
health = 100 - getOxyLoss() - getToxLoss() - getFireLoss() - getBruteLoss()
|
||||
health = 100 - getOxyLoss() - getToxLoss() - getFireLoss() - getBruteLoss()
|
||||
|
||||
@@ -217,7 +217,10 @@ var/const/BLOOD_VOLUME_SURVIVE = 122
|
||||
var/datum/reagent/blood/injected = get_blood(container.reagents)
|
||||
if (!injected)
|
||||
return
|
||||
src.virus2 |= virus_copylist(injected.data["virus2"])
|
||||
var/list/sniffles = virus_copylist(injected.data["virus2"])
|
||||
for(var/ID in sniffles)
|
||||
var/datum/disease2/disease/sniffle = sniffles[ID]
|
||||
infect_virus2(src,sniffle,1)
|
||||
if (injected.data["antibodies"] && prob(5))
|
||||
antibodies |= injected.data["antibodies"]
|
||||
var/list/chems = list()
|
||||
|
||||
@@ -587,11 +587,28 @@ This function completely restores a damaged organ to perfect condition.
|
||||
|
||||
owner.update_body(1)
|
||||
|
||||
// OK so maybe your limb just flew off, but if it was attached to a pair of cuffs then hooray! Freedom!
|
||||
release_restraints()
|
||||
|
||||
/****************************************************
|
||||
HELPERS
|
||||
****************************************************/
|
||||
|
||||
/datum/organ/external/proc/release_restraints()
|
||||
if (owner.handcuffed && body_part in list(ARM_LEFT, ARM_RIGHT, HAND_LEFT, HAND_RIGHT))
|
||||
owner.visible_message(\
|
||||
"\The [owner.handcuffed.name] falls off of [owner.name].",\
|
||||
"\The [owner.handcuffed.name] falls off you.")
|
||||
|
||||
owner.drop_from_inventory(owner.handcuffed)
|
||||
|
||||
if (owner.legcuffed && body_part in list(FOOT_LEFT, FOOT_RIGHT, LEG_LEFT, LEG_RIGHT))
|
||||
owner.visible_message(\
|
||||
"\The [owner.legcuffed.name] falls off of [owner.name].",\
|
||||
"\The [owner.legcuffed.name] falls off you.")
|
||||
|
||||
owner.drop_from_inventory(owner.legcuffed)
|
||||
|
||||
/datum/organ/external/proc/bandage()
|
||||
var/rval = 0
|
||||
src.status &= ~ORGAN_BLEEDING
|
||||
@@ -620,7 +637,10 @@ This function completely restores a damaged organ to perfect condition.
|
||||
/datum/organ/external/proc/fracture()
|
||||
if(status & ORGAN_BROKEN)
|
||||
return
|
||||
owner.visible_message("\red You hear a loud cracking sound coming from \the [owner].","\red <b>Something feels like it shattered in your [display_name]!</b>","You hear a sickening crack.")
|
||||
owner.visible_message(\
|
||||
"\red You hear a loud cracking sound coming from \the [owner].",\
|
||||
"\red <b>Something feels like it shattered in your [display_name]!</b>",\
|
||||
"You hear a sickening crack.")
|
||||
|
||||
if(owner.species && !(owner.species.flags & NO_PAIN))
|
||||
owner.emote("scream")
|
||||
@@ -629,6 +649,10 @@ This function completely restores a damaged organ to perfect condition.
|
||||
broken_description = pick("broken","fracture","hairline fracture")
|
||||
perma_injury = brute_dam
|
||||
|
||||
// Fractures have a chance of getting you out of restraints
|
||||
if (prob(25))
|
||||
release_restraints()
|
||||
|
||||
/datum/organ/external/proc/robotize()
|
||||
src.status &= ~ORGAN_BROKEN
|
||||
src.status &= ~ORGAN_BLEEDING
|
||||
|
||||
+53
-45
@@ -53,7 +53,7 @@
|
||||
var/areastring = null
|
||||
var/obj/item/weapon/cell/cell
|
||||
var/start_charge = 90 // initial cell charge %
|
||||
var/cell_type = 2500 // 0=no cell, 1=regular, 2=high-cap (x5) <- old, now it's just 0=no cell, otherwise dictate cellcapacity by changing this value. 1 used to be 1000, 2 was 2500
|
||||
var/cell_type = 5000 // 0=no cell, 1=regular, 2=high-cap (x5) <- old, now it's just 0=no cell, otherwise dictate cellcapacity by changing this value. 1 used to be 1000, 2 was 2500
|
||||
var/opened = 0 //0=closed, 1=opened, 2=cover removed
|
||||
var/shorted = 0
|
||||
var/lighting = 3
|
||||
@@ -78,6 +78,8 @@
|
||||
powernet = 0 // set so that APCs aren't found as powernet nodes //Hackish, Horrible, was like this before I changed it :(
|
||||
var/malfhack = 0 //New var for my changes to AI malf. --NeoFite
|
||||
var/mob/living/silicon/ai/malfai = null //See above --NeoFite
|
||||
var/debug= 0
|
||||
var/autoflag= 0 // 0 = off, 1= eqp and lights off, 2 = eqp off, 3 = all on.
|
||||
// luminosity = 1
|
||||
var/has_electronics = 0 // 0 - none, 1 - plugged in, 2 - secured by screwdriver
|
||||
var/overload = 1 //used for the Blackout malf module
|
||||
@@ -142,6 +144,7 @@
|
||||
init()
|
||||
else
|
||||
area = src.loc.loc:master
|
||||
area.apc |= src
|
||||
opened = 1
|
||||
operating = 0
|
||||
name = "[area.name] APC"
|
||||
@@ -168,6 +171,7 @@
|
||||
cell.charge = start_charge * cell.maxcharge / 100.0 // (convert percentage to actual value)
|
||||
|
||||
var/area/A = src.loc.loc
|
||||
|
||||
|
||||
//if area isn't specified use current
|
||||
if(isarea(A) && src.areastring == null)
|
||||
@@ -176,6 +180,7 @@
|
||||
else
|
||||
src.area = get_area_name(areastring)
|
||||
name = "\improper [area.name] APC"
|
||||
area.apc |= src
|
||||
update_icon()
|
||||
|
||||
make_terminal()
|
||||
@@ -919,6 +924,7 @@
|
||||
if(user.lying)
|
||||
user << "\red You must stand to use this [src]!"
|
||||
return 0
|
||||
autoflag = 5
|
||||
if (istype(user, /mob/living/silicon))
|
||||
var/mob/living/silicon/ai/AI = user
|
||||
var/mob/living/silicon/robot/robot = user
|
||||
@@ -1154,20 +1160,11 @@
|
||||
return
|
||||
|
||||
|
||||
/*
|
||||
if (equipment > 1) // off=0, off auto=1, on=2, on auto=3
|
||||
use_power(src.equip_consumption, EQUIP)
|
||||
if (lighting > 1) // off=0, off auto=1, on=2, on auto=3
|
||||
use_power(src.light_consumption, LIGHT)
|
||||
if (environ > 1) // off=0, off auto=1, on=2, on auto=3
|
||||
use_power(src.environ_consumption, ENVIRON)
|
||||
|
||||
area.calc_lighting() */
|
||||
|
||||
lastused_light = area.usage(LIGHT)
|
||||
lastused_equip = area.usage(EQUIP)
|
||||
lastused_environ = area.usage(ENVIRON)
|
||||
area.clear_usage()
|
||||
if(area.powerupdate)
|
||||
area.clear_usage()
|
||||
|
||||
lastused_total = lastused_light + lastused_equip + lastused_environ
|
||||
|
||||
@@ -1191,13 +1188,15 @@
|
||||
perapc = terminal.powernet.perapc
|
||||
|
||||
//if(debug)
|
||||
// world.log << "Status: [main_status] - Excess: [excess] - Last Equip: [lastused_equip] - Last Light: [lastused_light] - Longterm: [longtermpower]"
|
||||
//world << "Status: [main_status] - Excess: [excess] - Last Equip: [lastused_equip] - Last Light: [lastused_light]"
|
||||
|
||||
if(cell && !shorted)
|
||||
var/cell_charge = cell.charge
|
||||
var/cell_maxcharge = cell.maxcharge
|
||||
|
||||
// draw power from cell as before
|
||||
|
||||
var/cellused = min(cell.charge, CELLRATE * lastused_total) // clamp deduction to a max, amount left in cell
|
||||
var/cellused = min(cell_charge, CELLRATE * lastused_total) // clamp deduction to a max, amount left in cell
|
||||
cell.use(cellused)
|
||||
|
||||
if(excess > 0 || perapc > lastused_total) // if power excess, or enough anyway, recharge the cell
|
||||
@@ -1208,19 +1207,21 @@
|
||||
|
||||
else // no excess, and not enough per-apc
|
||||
|
||||
if( (cell.charge/CELLRATE+perapc) >= lastused_total) // can we draw enough from cell+grid to cover last usage?
|
||||
if( (cell_charge/CELLRATE+perapc) >= lastused_total) // can we draw enough from cell+grid to cover last usage?
|
||||
|
||||
cell.charge = min(cell.maxcharge, cell.charge + CELLRATE * perapc) //recharge with what we can
|
||||
cell_charge = min(cell_maxcharge, cell_charge + CELLRATE * perapc) //recharge with what we can
|
||||
cell.charge = cell_charge
|
||||
add_load(perapc) // so draw what we can from the grid
|
||||
charging = 0
|
||||
|
||||
else // not enough power available to run the last tick!
|
||||
else if (autoflag != 0) // not enough power available to run the last tick!
|
||||
charging = 0
|
||||
chargecount = 0
|
||||
// This turns everything off in the case that there is still a charge left on the battery, just not enough to run the room.
|
||||
equipment = autoset(equipment, 0)
|
||||
lighting = autoset(lighting, 0)
|
||||
environ = autoset(environ, 0)
|
||||
autoflag = 0
|
||||
|
||||
|
||||
// set channels depending on how much charge we have left
|
||||
@@ -1231,39 +1232,44 @@
|
||||
else if(longtermpower > -10)
|
||||
longtermpower -= 2
|
||||
|
||||
if(cell.charge <= 0) // zero charge, turn all off
|
||||
equipment = autoset(equipment, 0)
|
||||
lighting = autoset(lighting, 0)
|
||||
environ = autoset(environ, 0)
|
||||
area.poweralert(0, src)
|
||||
else if(cell.percent() < 15 && longtermpower < 0) // <15%, turn off lighting & equipment
|
||||
equipment = autoset(equipment, 2)
|
||||
lighting = autoset(lighting, 2)
|
||||
environ = autoset(environ, 1)
|
||||
area.poweralert(0, src)
|
||||
else if(cell.percent() < 30 && longtermpower < 0) // <30%, turn off equipment
|
||||
equipment = autoset(equipment, 2)
|
||||
lighting = autoset(lighting, 1)
|
||||
environ = autoset(environ, 1)
|
||||
area.poweralert(0, src)
|
||||
else // otherwise all can be on
|
||||
equipment = autoset(equipment, 1)
|
||||
lighting = autoset(lighting, 1)
|
||||
environ = autoset(environ, 1)
|
||||
area.poweralert(1, src)
|
||||
if(cell.percent() > 75)
|
||||
|
||||
if(cell_charge >= 1250 || longtermpower > 0) // Put most likely at the top so we don't check it last, effeciency 101
|
||||
if(autoflag != 3)
|
||||
equipment = autoset(equipment, 1)
|
||||
lighting = autoset(lighting, 1)
|
||||
environ = autoset(environ, 1)
|
||||
autoflag = 3
|
||||
area.poweralert(1, src)
|
||||
if(cell_charge >= 4000)
|
||||
area.poweralert(1, src)
|
||||
else if(cell_charge < 1250 && cell_charge > 750 && longtermpower < 0) // <30%, turn off equipment
|
||||
if(autoflag != 2)
|
||||
equipment = autoset(equipment, 2)
|
||||
lighting = autoset(lighting, 1)
|
||||
environ = autoset(environ, 1)
|
||||
area.poweralert(0, src)
|
||||
autoflag = 2
|
||||
else if(cell_charge < 750 && cell_charge > 10 && longtermpower < 0) // <15%, turn off lighting & equipment
|
||||
if(autoflag != 1)
|
||||
equipment = autoset(equipment, 2)
|
||||
lighting = autoset(lighting, 2)
|
||||
environ = autoset(environ, 1)
|
||||
area.poweralert(0, src)
|
||||
autoflag = 1
|
||||
else if(cell_charge <= 0) // zero charge, turn all off
|
||||
if(autoflag != 0)
|
||||
equipment = autoset(equipment, 0)
|
||||
lighting = autoset(lighting, 0)
|
||||
environ = autoset(environ, 0)
|
||||
area.poweralert(0, src)
|
||||
autoflag = 0
|
||||
|
||||
// now trickle-charge the cell
|
||||
|
||||
if(chargemode && charging == 1 && operating)
|
||||
if(excess > 0) // check to make sure we have enough to charge
|
||||
// Max charge is perapc share, capped to cell capacity, or % per second constant (Whichever is smallest)
|
||||
/* var/ch = min(perapc, (cell.maxcharge - cell.charge), (cell.maxcharge*CHARGELEVEL))
|
||||
add_load(ch) // Removes the power we're taking from the grid
|
||||
cell.give(ch) // actually recharge the cell
|
||||
*/
|
||||
var/ch = min(perapc*CELLRATE, (cell.maxcharge - cell.charge), (cell.maxcharge*CHARGELEVEL))
|
||||
var/ch = min(perapc*CELLRATE, (cell_maxcharge - cell_charge), (cell_maxcharge*CHARGELEVEL))
|
||||
add_load(ch/CELLRATE) // Removes the power we're taking from the grid
|
||||
cell.give(ch) // actually recharge the cell
|
||||
|
||||
@@ -1273,12 +1279,12 @@
|
||||
|
||||
// show cell as fully charged if so
|
||||
|
||||
if(cell.charge >= cell.maxcharge)
|
||||
if(cell.charge >= cell_maxcharge)
|
||||
charging = 2
|
||||
|
||||
if(chargemode)
|
||||
if(!charging)
|
||||
if(excess > cell.maxcharge*CHARGELEVEL)
|
||||
if(excess > cell_maxcharge*CHARGELEVEL)
|
||||
chargecount++
|
||||
else
|
||||
chargecount = 0
|
||||
@@ -1300,6 +1306,8 @@
|
||||
lighting = autoset(lighting, 0)
|
||||
environ = autoset(environ, 0)
|
||||
area.poweralert(0, src)
|
||||
autoflag = 0
|
||||
|
||||
|
||||
// update icon & area power if anything changed
|
||||
|
||||
|
||||
@@ -603,9 +603,11 @@
|
||||
|
||||
#define LIGHTING_POWER_FACTOR 20 //20W per unit luminosity
|
||||
|
||||
/*
|
||||
/obj/machinery/light/process()//TODO: remove/add this from machines to save on processing as needed ~Carn PRIORITY
|
||||
if(on)
|
||||
use_power(luminosity * LIGHTING_POWER_FACTOR, LIGHT)
|
||||
*/
|
||||
|
||||
// called when area power state changes
|
||||
/obj/machinery/light/power_change()
|
||||
|
||||
@@ -54,13 +54,15 @@
|
||||
|
||||
// increment the power usage stats for an area
|
||||
|
||||
/obj/machinery/proc/use_power(var/amount, var/chan = -1) // defaults to power_channel
|
||||
/obj/machinery/proc/use_power(var/amount, var/chan = -1, var/autocalled = 0) // defaults to power_channel
|
||||
var/area/A = src.loc.loc // make sure it's in an area
|
||||
if(!A || !isarea(A) || !A.master)
|
||||
return
|
||||
if(chan == -1)
|
||||
chan = power_channel
|
||||
A.master.use_power(amount, chan)
|
||||
if(!autocalled)
|
||||
A.master.powerupdate = 2 // Decremented by 2 each GC tick, since it's not auto power change we're going to update power twice.
|
||||
|
||||
/obj/machinery/proc/power_change() // called whenever the power settings of the containing area change
|
||||
// by default, check equipment channel & set flag
|
||||
|
||||
@@ -370,7 +370,7 @@ var/list/solars_list = list()
|
||||
if(stat & (NOPOWER | BROKEN))
|
||||
return
|
||||
|
||||
use_power(250)
|
||||
//use_power(250)
|
||||
if(track==1 && nexttime < world.time && trackdir*trackrate)
|
||||
// Increments nexttime using itself and not world.time to prevent drift
|
||||
nexttime = nexttime + 6000/trackrate
|
||||
|
||||
@@ -121,6 +121,56 @@ datum
|
||||
src.handle_reactions()
|
||||
return amount
|
||||
|
||||
trans_to_ingest(var/obj/target, var/amount=1, var/multiplier=1, var/preserve_data=1)//For items ingested. A delay is added between ingestion and addition of the reagents
|
||||
if (!target )
|
||||
return
|
||||
if (!target.reagents || src.total_volume<=0)
|
||||
return
|
||||
|
||||
/*var/datum/reagents/R = target.reagents
|
||||
|
||||
var/obj/item/weapon/reagent_containers/glass/beaker/noreact/B = new /obj/item/weapon/reagent_containers/glass/beaker/noreact //temporary holder
|
||||
|
||||
amount = min(min(amount, src.total_volume), R.maximum_volume-R.total_volume)
|
||||
var/part = amount / src.total_volume
|
||||
var/trans_data = null
|
||||
for (var/datum/reagent/current_reagent in src.reagent_list)
|
||||
if (!current_reagent)
|
||||
continue
|
||||
//if (current_reagent.id == "blood" && ishuman(target))
|
||||
// var/mob/living/carbon/human/H = target
|
||||
// H.inject_blood(my_atom, amount)
|
||||
// continue
|
||||
var/current_reagent_transfer = current_reagent.volume * part
|
||||
if(preserve_data)
|
||||
trans_data = current_reagent.data
|
||||
|
||||
B.add_reagent(current_reagent.id, (current_reagent_transfer * multiplier), trans_data, safety = 1) //safety checks on these so all chemicals are transferred
|
||||
src.remove_reagent(current_reagent.id, current_reagent_transfer, safety = 1) // to the target container before handling reactions
|
||||
|
||||
src.update_total()
|
||||
B.update_total()
|
||||
B.handle_reactions()
|
||||
src.handle_reactions()*/
|
||||
|
||||
var/obj/item/weapon/reagent_containers/glass/beaker/noreact/B = new /obj/item/weapon/reagent_containers/glass/beaker/noreact //temporary holder
|
||||
B.volume = 1000
|
||||
|
||||
var/datum/reagents/BR = B.reagents
|
||||
var/datum/reagents/R = target.reagents
|
||||
|
||||
amount = min(min(amount, src.total_volume), R.maximum_volume-R.total_volume)
|
||||
|
||||
src.trans_to(B, amount)
|
||||
|
||||
spawn(95)
|
||||
BR.reaction(target, INGEST)
|
||||
spawn(5)
|
||||
BR.trans_to(target, BR.total_volume)
|
||||
del(B)
|
||||
|
||||
return amount
|
||||
|
||||
copy_to(var/obj/target, var/amount=1, var/multiplier=1, var/preserve_data=1, var/safety = 0)
|
||||
if(!target)
|
||||
return
|
||||
|
||||
@@ -35,31 +35,32 @@ datum
|
||||
var/datum/reagent/self = src
|
||||
src = null //of the reagent to the mob on TOUCHING it.
|
||||
|
||||
if(!istype(self.holder.my_atom, /obj/effect/effect/smoke/chem))
|
||||
// If the chemicals are in a smoke cloud, do not try to let the chemicals "penetrate" into the mob's system (balance station 13) -- Doohl
|
||||
if(self.holder) //for catching rare runtimes
|
||||
if(!istype(self.holder.my_atom, /obj/effect/effect/smoke/chem))
|
||||
// If the chemicals are in a smoke cloud, do not try to let the chemicals "penetrate" into the mob's system (balance station 13) -- Doohl
|
||||
|
||||
if(method == TOUCH)
|
||||
if(method == TOUCH)
|
||||
|
||||
var/chance = 1
|
||||
var/block = 0
|
||||
var/chance = 1
|
||||
var/block = 0
|
||||
|
||||
for(var/obj/item/clothing/C in M.get_equipped_items())
|
||||
if(C.permeability_coefficient < chance) chance = C.permeability_coefficient
|
||||
if(istype(C, /obj/item/clothing/suit/bio_suit))
|
||||
// bio suits are just about completely fool-proof - Doohl
|
||||
// kind of a hacky way of making bio suits more resistant to chemicals but w/e
|
||||
if(prob(75))
|
||||
block = 1
|
||||
for(var/obj/item/clothing/C in M.get_equipped_items())
|
||||
if(C.permeability_coefficient < chance) chance = C.permeability_coefficient
|
||||
if(istype(C, /obj/item/clothing/suit/bio_suit))
|
||||
// bio suits are just about completely fool-proof - Doohl
|
||||
// kind of a hacky way of making bio suits more resistant to chemicals but w/e
|
||||
if(prob(75))
|
||||
block = 1
|
||||
|
||||
if(istype(C, /obj/item/clothing/head/bio_hood))
|
||||
if(prob(75))
|
||||
block = 1
|
||||
if(istype(C, /obj/item/clothing/head/bio_hood))
|
||||
if(prob(75))
|
||||
block = 1
|
||||
|
||||
chance = chance * 100
|
||||
chance = chance * 100
|
||||
|
||||
if(prob(chance) && !block)
|
||||
if(M.reagents)
|
||||
M.reagents.add_reagent(self.id,self.volume/2)
|
||||
if(prob(chance) && !block)
|
||||
if(M.reagents)
|
||||
M.reagents.add_reagent(self.id,self.volume/2)
|
||||
return 1
|
||||
|
||||
reaction_obj(var/obj/O, var/volume) //By default we transfer a small part of the reagent to the object
|
||||
@@ -582,7 +583,12 @@ datum
|
||||
reaction_turf(var/turf/T, var/volume)
|
||||
src = null
|
||||
if(!istype(T, /turf/space))
|
||||
new /obj/effect/decal/cleanable/dirt(T)
|
||||
var/obj/effect/decal/cleanable/dirt/dirtoverlay = locate(/obj/effect/decal/cleanable/dirt, T)
|
||||
if (!dirtoverlay)
|
||||
dirtoverlay = new/obj/effect/decal/cleanable/dirt(T)
|
||||
dirtoverlay.alpha = volume*30
|
||||
else
|
||||
dirtoverlay.alpha = min(dirtoverlay.alpha+volume*30, 255)
|
||||
|
||||
chlorine
|
||||
name = "Chlorine"
|
||||
@@ -708,7 +714,9 @@ datum
|
||||
src = null
|
||||
if(volume >= 3)
|
||||
if(!istype(T, /turf/space))
|
||||
new /obj/effect/decal/cleanable/greenglow(T)
|
||||
var/obj/effect/decal/cleanable/greenglow/glow = locate(/obj/effect/decal/cleanable/greenglow, T)
|
||||
if(!glow)
|
||||
new /obj/effect/decal/cleanable/greenglow(T)
|
||||
return
|
||||
|
||||
|
||||
@@ -748,9 +756,9 @@ datum
|
||||
src = null
|
||||
if(volume >= 5)
|
||||
if(istype(T, /turf/simulated/wall))
|
||||
T:thermite = 1
|
||||
T.overlays.Cut()
|
||||
T.overlays = image('icons/effects/effects.dmi',icon_state = "thermite")
|
||||
var/turf/simulated/wall/W = T
|
||||
W.thermite = 1
|
||||
W.overlays += image('icons/effects/effects.dmi',icon_state = "#673910")
|
||||
return
|
||||
|
||||
on_mob_life(var/mob/living/M as mob)
|
||||
@@ -872,7 +880,10 @@ datum
|
||||
src = null
|
||||
if(volume >= 3)
|
||||
if(!istype(T, /turf/space))
|
||||
new /obj/effect/decal/cleanable/greenglow(T)
|
||||
var/obj/effect/decal/cleanable/greenglow/glow = locate(/obj/effect/decal/cleanable/greenglow, T)
|
||||
if(!glow)
|
||||
new /obj/effect/decal/cleanable/greenglow(T)
|
||||
return
|
||||
|
||||
aluminum
|
||||
name = "Aluminum"
|
||||
@@ -931,7 +942,6 @@ datum
|
||||
if(istype(T, /turf/simulated))
|
||||
var/turf/simulated/S = T
|
||||
S.dirt = 0
|
||||
T.overlays.Cut()
|
||||
T.clean_blood()
|
||||
for(var/obj/effect/decal/cleanable/C in T.contents)
|
||||
src.reaction_obj(C, volume)
|
||||
|
||||
@@ -462,8 +462,6 @@ datum
|
||||
playsound(location, 'sound/effects/smoke.ogg', 50, 1, -3)
|
||||
spawn(0)
|
||||
S.start()
|
||||
sleep(10)
|
||||
S.start()
|
||||
holder.clear_reagents()
|
||||
return
|
||||
|
||||
|
||||
@@ -23,12 +23,10 @@
|
||||
if(M == user)
|
||||
M << "\blue You swallow a gulp of [src]."
|
||||
if(reagents.total_volume)
|
||||
reagents.trans_to_ingest(M, gulp_size)
|
||||
reagents.reaction(M, INGEST)
|
||||
spawn(5)
|
||||
reagents.trans_to(M, gulp_size)
|
||||
else if (canopened == 0)
|
||||
user << "<span class='notice'>You need to open the drink!</span>"
|
||||
return
|
||||
|
||||
playsound(M.loc,'sound/items/drink.ogg', rand(10,50), 1)
|
||||
return 1
|
||||
@@ -49,9 +47,7 @@
|
||||
msg_admin_attack("[key_name(user)] fed [key_name(M)] with [src.name] Reagents: [reagentlist(src)] (INTENT: [uppertext(user.a_intent)]) (<A HREF='?_src_=holder;adminplayerobservecoodjump=1;X=[user.x];Y=[user.y];Z=[user.z]'>JMP</a>)")
|
||||
|
||||
if(reagents.total_volume)
|
||||
reagents.reaction(M, INGEST)
|
||||
spawn(5)
|
||||
reagents.trans_to(M, gulp_size)
|
||||
reagents.trans_to_ingest(M, gulp_size)
|
||||
|
||||
if(isrobot(user)) //Cyborg modules that include drinks automatically refill themselves, but drain the borg's cell
|
||||
var/mob/living/silicon/robot/bro = user
|
||||
|
||||
@@ -29,9 +29,7 @@
|
||||
if(M == user)
|
||||
M << "\blue You swallow some of contents of the [src]."
|
||||
if(reagents.total_volume)
|
||||
reagents.reaction(M, INGEST)
|
||||
spawn(5)
|
||||
reagents.trans_to(M, 10)
|
||||
reagents.trans_to_ingest(M, 10)
|
||||
|
||||
playsound(M.loc,'sound/items/drink.ogg', rand(10,50), 1)
|
||||
return 1
|
||||
@@ -48,9 +46,7 @@
|
||||
msg_admin_attack("[user.name] ([user.ckey]) fed [M.name] ([M.ckey]) with [src.name] (INTENT: [uppertext(user.a_intent)]) (<A HREF='?_src_=holder;adminplayerobservecoodjump=1;X=[user.x];Y=[user.y];Z=[user.z]'>JMP</a>)")
|
||||
|
||||
if(reagents.total_volume)
|
||||
reagents.reaction(M, INGEST)
|
||||
spawn(5)
|
||||
reagents.trans_to(M, 10)
|
||||
reagents.trans_to_ingest(M, 10)
|
||||
|
||||
playsound(M.loc,'sound/items/drink.ogg', rand(10,50), 1)
|
||||
return 1
|
||||
|
||||
@@ -29,9 +29,7 @@
|
||||
if(M == user)
|
||||
M << "\blue You swallow a gulp of [src]."
|
||||
if(reagents.total_volume)
|
||||
reagents.reaction(M, INGEST)
|
||||
spawn(5)
|
||||
reagents.trans_to(M, gulp_size)
|
||||
reagents.trans_to_ingest(M, gulp_size)
|
||||
|
||||
playsound(M.loc,'sound/items/drink.ogg', rand(10,50), 1)
|
||||
return 1
|
||||
@@ -48,9 +46,7 @@
|
||||
msg_admin_attack("[key_name(user)] fed [key_name(M)] with [src.name] Reagents: [reagentlist(src)] (INTENT: [uppertext(user.a_intent)]) (<A HREF='?_src_=holder;adminplayerobservecoodjump=1;X=[user.x];Y=[user.y];Z=[user.z]'>JMP</a>)")
|
||||
|
||||
if(reagents.total_volume)
|
||||
reagents.reaction(M, INGEST)
|
||||
spawn(5)
|
||||
reagents.trans_to(M, gulp_size)
|
||||
reagents.trans_to_ingest(M, gulp_size)
|
||||
|
||||
if(isrobot(user)) //Cyborg modules that include drinks automatically refill themselves, but drain the borg's cell
|
||||
var/mob/living/silicon/robot/bro = user
|
||||
|
||||
@@ -78,20 +78,18 @@
|
||||
if(reagents) //Handle ingestion of the reagent.
|
||||
playsound(M.loc,'sound/items/eatfood.ogg', rand(10,50), 1)
|
||||
if(reagents.total_volume)
|
||||
reagents.reaction(M, INGEST)
|
||||
spawn(5)
|
||||
if(reagents.total_volume > bitesize)
|
||||
/*
|
||||
* I totally cannot understand what this code supposed to do.
|
||||
* Right now every snack consumes in 2 bites, my popcorn does not work right, so I simplify it. -- rastaf0
|
||||
var/temp_bitesize = max(reagents.total_volume /2, bitesize)
|
||||
reagents.trans_to(M, temp_bitesize)
|
||||
*/
|
||||
reagents.trans_to(M, bitesize)
|
||||
else
|
||||
reagents.trans_to(M, reagents.total_volume)
|
||||
bitecount++
|
||||
On_Consume(M)
|
||||
if(reagents.total_volume > bitesize)
|
||||
/*
|
||||
* I totally cannot understand what this code supposed to do.
|
||||
* Right now every snack consumes in 2 bites, my popcorn does not work right, so I simplify it. -- rastaf0
|
||||
var/temp_bitesize = max(reagents.total_volume /2, bitesize)
|
||||
reagents.trans_to(M, temp_bitesize)
|
||||
*/
|
||||
reagents.trans_to_ingest(M, bitesize)
|
||||
else
|
||||
reagents.trans_to_ingest(M, reagents.total_volume)
|
||||
bitecount++
|
||||
On_Consume(M)
|
||||
return 1
|
||||
|
||||
return 0
|
||||
|
||||
@@ -37,7 +37,11 @@
|
||||
/mob/living/simple_animal/cow,
|
||||
/mob/living/simple_animal/hostile/retaliate/goat,
|
||||
/obj/machinery/computer/centrifuge,
|
||||
/obj/machinery/sleeper )
|
||||
/obj/machinery/sleeper,
|
||||
/obj/machinery/smartfridge/,
|
||||
/obj/machinery/biogenerator,
|
||||
/obj/machinery/hydroponics,
|
||||
/obj/machinery/constructable_frame)
|
||||
|
||||
New()
|
||||
..()
|
||||
@@ -122,6 +126,9 @@
|
||||
else if(istype(target, /obj/machinery/bunsen_burner))
|
||||
return
|
||||
|
||||
else if(istype(target, /obj/machinery/smartfridge))
|
||||
return
|
||||
|
||||
else if(istype(target, /obj/machinery/radiocarbon_spectrometer))
|
||||
return
|
||||
|
||||
|
||||
@@ -22,10 +22,8 @@
|
||||
M << "\blue You swallow [src]."
|
||||
M.drop_from_inventory(src) //icon update
|
||||
if(reagents.total_volume)
|
||||
reagents.reaction(M, INGEST)
|
||||
spawn(5)
|
||||
reagents.trans_to(M, reagents.total_volume)
|
||||
del(src)
|
||||
reagents.trans_to_ingest(M, reagents.total_volume)
|
||||
del(src)
|
||||
else
|
||||
del(src)
|
||||
return 1
|
||||
@@ -46,10 +44,8 @@
|
||||
msg_admin_attack("[user.name] ([user.ckey]) fed [M.name] ([M.ckey]) with [src.name] Reagents: [reagentlist(src)] (INTENT: [uppertext(user.a_intent)]) (<A HREF='?_src_=holder;adminplayerobservecoodjump=1;X=[user.x];Y=[user.y];Z=[user.z]'>JMP</a>)")
|
||||
|
||||
if(reagents.total_volume)
|
||||
reagents.reaction(M, INGEST)
|
||||
spawn(5)
|
||||
reagents.trans_to(M, reagents.total_volume)
|
||||
del(src)
|
||||
reagents.trans_to_ingest(M, reagents.total_volume)
|
||||
del(src)
|
||||
else
|
||||
del(src)
|
||||
|
||||
|
||||
@@ -22,6 +22,8 @@
|
||||
var/flush_every_ticks = 30 //Every 30 ticks it will look whether it is ready to flush
|
||||
var/flush_count = 0 //this var adds 1 once per tick. When it reaches flush_every_ticks it resets and tries to flush.
|
||||
var/last_sound = 0
|
||||
active_power_usage = 600
|
||||
idle_power_usage = 100
|
||||
|
||||
// create a new disposal
|
||||
// find the attached trunk (if present) and init gas resvr.
|
||||
@@ -340,6 +342,7 @@
|
||||
// timed process
|
||||
// charge the gas reservoir and perform flush if ready
|
||||
process()
|
||||
use_power = 0
|
||||
if(stat & BROKEN) // nothing can happen if broken
|
||||
return
|
||||
|
||||
@@ -363,13 +366,13 @@
|
||||
if(stat & NOPOWER) // won't charge if no power
|
||||
return
|
||||
|
||||
use_power(100) // base power usage
|
||||
|
||||
use_power = 1
|
||||
|
||||
if(mode != 1) // if off or ready, no need to charge
|
||||
return
|
||||
|
||||
// otherwise charge
|
||||
use_power(500) // charging power usage
|
||||
use_power = 2
|
||||
|
||||
var/atom/L = loc // recharging from loc turf
|
||||
|
||||
|
||||
@@ -218,7 +218,7 @@
|
||||
return
|
||||
|
||||
Bumped(var/atom/movable/AM) //Go straight into the chute
|
||||
if(istype(AM, /obj/item/projectile)) return
|
||||
if(istype(AM, /obj/item/projectile) || istype(AM, /obj/effect)) return
|
||||
switch(dir)
|
||||
if(NORTH)
|
||||
if(AM.loc.y != src.loc.y+1) return
|
||||
|
||||
@@ -1398,6 +1398,43 @@ datum/design/noreactbeaker
|
||||
reliability_base = 76
|
||||
build_path = "/obj/item/weapon/reagent_containers/glass/beaker/noreact"
|
||||
category = "Misc"
|
||||
|
||||
datum/design/scalpel_laser1
|
||||
name = "Basic Laser Scalpel"
|
||||
desc = "A scalpel augmented with a directed laser, for more precise cutting without blood entering the field. This one looks basic and could be improved."
|
||||
id = "scalpel_laser1"
|
||||
req_tech = list("biotech" = 2, "materials" = 2, "magnets" = 2)
|
||||
build_type = PROTOLATHE
|
||||
materials = list("$metal" = 12500, "$glass" = 7500)
|
||||
build_path = "/obj/item/weapon/scalpel/laser1"
|
||||
|
||||
datum/design/scalpel_laser2
|
||||
name = "Improved Laser Scalpel"
|
||||
desc = "A scalpel augmented with a directed laser, for more precise cutting without blood entering the field. This one looks somewhat advanced."
|
||||
id = "scalpel_laser2"
|
||||
req_tech = list("biotech" = 3, "materials" = 4, "magnets" = 4)
|
||||
build_type = PROTOLATHE
|
||||
materials = list("$metal" = 12500, "$glass" = 7500, "$silver" = 2500)
|
||||
build_path = "/obj/item/weapon/scalpel/laser2"
|
||||
|
||||
datum/design/scalpel_laser3
|
||||
name = "Advanced Laser Scalpel"
|
||||
desc = "A scalpel augmented with a directed laser, for more precise cutting without blood entering the field. This one looks to be the pinnacle of precision energy cutlery!"
|
||||
id = "scalpel_laser3"
|
||||
req_tech = list("biotech" = 4, "materials" = 6, "magnets" = 5)
|
||||
build_type = PROTOLATHE
|
||||
materials = list("$metal" = 12500, "$glass" = 7500, "$silver" = 2000, "$gold" = 1500)
|
||||
build_path = "/obj/item/weapon/scalpel/laser3"
|
||||
|
||||
datum/design/scalpel_manager
|
||||
name = "Incision Management System"
|
||||
desc = "A true extension of the surgeon's body, this marvel instantly and completely prepares an incision allowing for the immediate commencement of therapeutic steps."
|
||||
id = "scalpel_manager"
|
||||
req_tech = list("biotech" = 4, "materials" = 7, "magnets" = 5, "programming" = 4)
|
||||
build_type = PROTOLATHE
|
||||
materials = list ("$metal" = 12500, "$glass" = 7500, "$silver" = 1500, "$gold" = 1500, "$diamond" = 750)
|
||||
build_path = "/obj/item/weapon/scalpel/manager"
|
||||
|
||||
/////////////////////////////////////////
|
||||
/////////////////Weapons/////////////////
|
||||
/////////////////////////////////////////
|
||||
|
||||
@@ -23,6 +23,86 @@
|
||||
return 0
|
||||
return 1
|
||||
|
||||
/datum/surgery_step/generic/cut_with_laser
|
||||
allowed_tools = list(
|
||||
/obj/item/weapon/scalpel/laser3 = 95, \
|
||||
/obj/item/weapon/scalpel/laser2 = 85, \
|
||||
/obj/item/weapon/scalpel/laser1 = 75, \
|
||||
/obj/item/weapon/melee/energy/sword = 5
|
||||
)
|
||||
|
||||
min_duration = 90
|
||||
max_duration = 110
|
||||
|
||||
can_use(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool)
|
||||
var/datum/organ/external/affected = target.get_organ(target_zone)
|
||||
return ..() && affected.open == 0 && target_zone != "mouth"
|
||||
|
||||
begin_step(mob/user, mob/living/carbon/human/target, target_zone, obj/item/tool)
|
||||
var/datum/organ/external/affected = target.get_organ(target_zone)
|
||||
user.visible_message("[user] starts the bloodless incision on [target]'s [affected.display_name] with \the [tool].", \
|
||||
"You start the bloodless incision on [target]'s [affected.display_name] with \the [tool].")
|
||||
target.custom_pain("You feel a horrible, searing pain in your [affected.display_name]!",1)
|
||||
..()
|
||||
|
||||
end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool)
|
||||
var/datum/organ/external/affected = target.get_organ(target_zone)
|
||||
user.visible_message("\blue [user] has made a bloodless incision on [target]'s [affected.display_name] with \the [tool].", \
|
||||
"\blue You have made a bloodless incision on [target]'s [affected.display_name] with \the [tool].",)
|
||||
//Could be cleaner ...
|
||||
affected.open = 1
|
||||
affected.status |= ORGAN_BLEEDING
|
||||
affected.createwound(CUT, 1)
|
||||
affected.clamp()
|
||||
spread_germs_to_organ(affected, user)
|
||||
if (target_zone == "head")
|
||||
target.brain_op_stage = 1
|
||||
|
||||
fail_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool)
|
||||
var/datum/organ/external/affected = target.get_organ(target_zone)
|
||||
user.visible_message("\red [user]'s hand slips as the blade sputters, searing a long gash in [target]'s [affected.display_name] with \the [tool]!", \
|
||||
"\red Your hand slips as the blade sputters, searing a long gash in [target]'s [affected.display_name] with \the [tool]!")
|
||||
affected.createwound(CUT, 7.5)
|
||||
affected.createwound(BURN, 12.5)
|
||||
|
||||
/datum/surgery_step/generic/incision_manager
|
||||
allowed_tools = list(
|
||||
/obj/item/weapon/scalpel/manager = 100
|
||||
)
|
||||
|
||||
min_duration = 80
|
||||
max_duration = 120
|
||||
|
||||
can_use(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool)
|
||||
var/datum/organ/external/affected = target.get_organ(target_zone)
|
||||
return ..() && affected.open == 0 && target_zone != "mouth"
|
||||
|
||||
begin_step(mob/user, mob/living/carbon/human/target, target_zone, obj/item/tool)
|
||||
var/datum/organ/external/affected = target.get_organ(target_zone)
|
||||
user.visible_message("[user] starts to construct a prepared incision on and within [target]'s [affected.display_name] with \the [tool].", \
|
||||
"You start to construct a prepared incision on and within [target]'s [affected.display_name] with \the [tool].")
|
||||
target.custom_pain("You feel a horrible, searing pain in your [affected.display_name] as it is pushed apart!",1)
|
||||
..()
|
||||
|
||||
end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool)
|
||||
var/datum/organ/external/affected = target.get_organ(target_zone)
|
||||
user.visible_message("\blue [user] has constructed a prepared incision on and within [target]'s [affected.display_name] with \the [tool].", \
|
||||
"\blue You have constructed a prepared incision on and within [target]'s [affected.display_name] with \the [tool].",)
|
||||
affected.open = 1
|
||||
affected.status |= ORGAN_BLEEDING
|
||||
affected.createwound(CUT, 1)
|
||||
affected.clamp()
|
||||
affected.open = 2
|
||||
if (target_zone == "head")
|
||||
target.brain_op_stage = 1
|
||||
|
||||
fail_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool)
|
||||
var/datum/organ/external/affected = target.get_organ(target_zone)
|
||||
user.visible_message("\red [user]'s hand jolts as the system sparks, ripping a gruesome hole in [target]'s [affected.display_name] with \the [tool]!", \
|
||||
"\red Your hand jolts as the system sparks, ripping a gruesome hole in [target]'s [affected.display_name] with \the [tool]!")
|
||||
affected.createwound(CUT, 20)
|
||||
affected.createwound(BURN, 15)
|
||||
|
||||
/datum/surgery_step/generic/cut_open
|
||||
allowed_tools = list(
|
||||
/obj/item/weapon/scalpel = 100, \
|
||||
|
||||
@@ -28,7 +28,8 @@
|
||||
/obj/machinery/disease2/diseaseanalyser/process()
|
||||
if(stat & (NOPOWER|BROKEN))
|
||||
return
|
||||
use_power(500)
|
||||
|
||||
//use_power(500)
|
||||
|
||||
if(scanning)
|
||||
scanning -= 1
|
||||
@@ -59,4 +60,4 @@
|
||||
dish = null
|
||||
src.state("\The [src.name] buzzes")
|
||||
pause = 0
|
||||
return
|
||||
return
|
||||
|
||||
@@ -72,7 +72,7 @@
|
||||
|
||||
if(stat & (NOPOWER|BROKEN))
|
||||
return
|
||||
use_power(500)
|
||||
//use_power(500)
|
||||
|
||||
if(curing)
|
||||
curing -= 1
|
||||
@@ -151,4 +151,4 @@
|
||||
var/obj/item/weapon/virusdish/dish = new/obj/item/weapon/virusdish(src.loc)
|
||||
dish.virus2 = virus2
|
||||
|
||||
state("\The [src.name] pings", "blue")
|
||||
state("\The [src.name] pings", "blue")
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
var/list/datum/disease2/effectholder/effects = list()
|
||||
var/antigen = 0 // 16 bits describing the antigens, when one bit is set, a cure with that bit can dock here
|
||||
var/max_stage = 4
|
||||
var/list/affected_species = list("Human","Unathi","Skrell","Tajaran")
|
||||
|
||||
/datum/disease2/disease/New()
|
||||
uniqueID = rand(0,10000)
|
||||
@@ -30,6 +31,24 @@
|
||||
antigen |= text2num(pick(ANTIGENS))
|
||||
spreadtype = prob(70) ? "Airborne" : "Contact"
|
||||
|
||||
if(all_species.len)
|
||||
affected_species = get_infectable_species()
|
||||
|
||||
/proc/get_infectable_species()
|
||||
var/list/meat = list()
|
||||
var/list/res = list()
|
||||
for (var/specie in all_species)
|
||||
var/datum/species/S = all_species[specie]
|
||||
if(!(S.flags & IS_SYNTHETIC))
|
||||
meat += S.name
|
||||
if(meat.len)
|
||||
var/num = rand(1,meat.len)
|
||||
for(var/i=0,i<num,i++)
|
||||
var/picked = pick(meat)
|
||||
meat -= picked
|
||||
res += picked
|
||||
return res
|
||||
|
||||
/datum/disease2/disease/proc/activate(var/mob/living/carbon/mob)
|
||||
if(dead)
|
||||
cure(mob)
|
||||
@@ -45,8 +64,10 @@
|
||||
if(prob(1))
|
||||
majormutate()
|
||||
|
||||
//Space antibiotics stop disease completely (temporary)
|
||||
//Space antibiotics stop disease completely
|
||||
if(mob.reagents.has_reagent("spaceacillin"))
|
||||
if(stage == 1 && prob(20))
|
||||
src.cure()
|
||||
return
|
||||
|
||||
//Virus food speeds up disease progress
|
||||
@@ -94,6 +115,8 @@
|
||||
if (prob(5))
|
||||
antigen = text2num(pick(ANTIGENS))
|
||||
antigen |= text2num(pick(ANTIGENS))
|
||||
if (prob(5) && all_species.len)
|
||||
affected_species = get_infectable_species()
|
||||
|
||||
/datum/disease2/disease/proc/getcopy()
|
||||
var/datum/disease2/disease/disease = new /datum/disease2/disease
|
||||
@@ -102,6 +125,7 @@
|
||||
disease.stageprob = stageprob
|
||||
disease.antigen = antigen
|
||||
disease.uniqueID = uniqueID
|
||||
disease.affected_species = affected_species.Copy()
|
||||
for(var/datum/disease2/effectholder/holder in effects)
|
||||
var/datum/disease2/effectholder/newholder = new /datum/disease2/effectholder
|
||||
newholder.effect = new holder.effect.type
|
||||
@@ -152,6 +176,9 @@ var/global/list/virusDB = list()
|
||||
r += "<BR>Infection rate : [infectionchance * 10]"
|
||||
r += "<BR>Spread form : [spreadtype]"
|
||||
r += "<BR>Progress Speed : [stageprob * 10]"
|
||||
r += "<BR>Affected species:"
|
||||
for(var/S in affected_species)
|
||||
r += "<BR>[S]"
|
||||
for(var/datum/disease2/effectholder/E in effects)
|
||||
r += "<BR>Effect:[E.effect.name]. Strength : [E.multiplier * 8]. Verosity : [E.chance * 15]. Type : [5-E.stage]."
|
||||
|
||||
|
||||
@@ -90,7 +90,7 @@
|
||||
/obj/machinery/computer/diseasesplicer/process()
|
||||
if(stat & (NOPOWER|BROKEN))
|
||||
return
|
||||
use_power(500)
|
||||
//use_power(500)
|
||||
|
||||
if(scanning)
|
||||
scanning -= 1
|
||||
@@ -137,6 +137,7 @@
|
||||
if(e.stage == memorybank.stage)
|
||||
e.effect = memorybank.effect
|
||||
splicing = 10
|
||||
dish.virus2.uniqueID = rand(0,10000)
|
||||
// dish.virus2.spreadtype = "Blood"
|
||||
|
||||
else if(href_list["disk"])
|
||||
|
||||
@@ -22,7 +22,8 @@
|
||||
if(f.stage == src.stage)
|
||||
list += f
|
||||
effect = pick(list)
|
||||
chance = rand(1,6)
|
||||
chance = rand(0,effect.chance_maxm)
|
||||
multiplier = rand(1,effect.maxm)
|
||||
|
||||
/datum/disease2/effectholder/proc/minormutate()
|
||||
switch(pick(1,2,3,4,5))
|
||||
@@ -47,23 +48,6 @@
|
||||
proc/activate(var/mob/living/carbon/mob,var/multiplier)
|
||||
proc/deactivate(var/mob/living/carbon/mob)
|
||||
|
||||
////////////////////////SPECIAL/////////////////////////////////
|
||||
/*/datum/disease2/effect/alien
|
||||
name = "Unidentified Foreign Body"
|
||||
stage = 4
|
||||
activate(var/mob/living/carbon/mob,var/multiplier)
|
||||
mob << "\red You feel something tearing its way out of your stomach..."
|
||||
mob.adjustToxLoss(10)
|
||||
mob.updatehealth()
|
||||
if(prob(40))
|
||||
if(mob.client)
|
||||
mob.client.mob = new/mob/living/carbon/alien/larva(mob.loc)
|
||||
else
|
||||
new/mob/living/carbon/alien/larva(mob.loc)
|
||||
var/datum/disease2/disease/D = mob:virus2
|
||||
mob:gib()
|
||||
del D*/
|
||||
|
||||
/datum/disease2/effect/invisible
|
||||
name = "Waiting Syndrome"
|
||||
stage = 1
|
||||
@@ -219,7 +203,8 @@
|
||||
if(istype(mob, /mob/living/carbon/human))
|
||||
var/mob/living/carbon/human/H = mob
|
||||
var/datum/organ/internal/brain/B = H.internal_organs["brain"]
|
||||
B.take_damage(5)
|
||||
if (B.damage < B.min_broken_damage)
|
||||
B.take_damage(5)
|
||||
else
|
||||
mob.setBrainLoss(50)
|
||||
|
||||
@@ -334,7 +319,12 @@
|
||||
name = "Coldingtons Effect"
|
||||
stage = 1
|
||||
activate(var/mob/living/carbon/mob,var/multiplier)
|
||||
if (prob(30))
|
||||
mob << "<span class='warning'>You feel like you are about to sneeze!</span>"
|
||||
sleep(5)
|
||||
mob.say("*sneeze")
|
||||
for(var/mob/living/carbon/M in get_step(mob,mob.dir))
|
||||
mob.spread_disease_to(M)
|
||||
if (prob(50))
|
||||
var/obj/effect/decal/cleanable/mucus/M = new(get_turf(mob))
|
||||
M.virus2 = virus_copylist(mob.virus2)
|
||||
|
||||
@@ -13,14 +13,19 @@ proc/get_infection_chance(var/mob/living/carbon/M, var/vector = "Airborne")
|
||||
score += 5
|
||||
if(istype(M:wear_mask, /obj/item/clothing/mask/surgical) && !M.internal)
|
||||
score += 10
|
||||
if(istype(M:wear_suit, /obj/item/clothing/suit/space) && istype(M:head, /obj/item/clothing/head/helmet/space)) score += 15
|
||||
if(istype(M:wear_suit, /obj/item/clothing/suit/bio_suit) && istype(M:head, /obj/item/clothing/head/bio_hood)) score += 15
|
||||
if(istype(M:wear_suit, /obj/item/clothing/suit/space) && istype(M:head, /obj/item/clothing/head/helmet/space))
|
||||
score += 20
|
||||
if(istype(M:wear_suit, /obj/item/clothing/suit/bio_suit) && istype(M:head, /obj/item/clothing/head/bio_hood))
|
||||
score += 30
|
||||
|
||||
|
||||
if (vector == "Contact")
|
||||
if(M:gloves) score += 15
|
||||
if(istype(M:wear_suit, /obj/item/clothing/suit/space)) score += 10
|
||||
if(istype(M:wear_suit, /obj/item/clothing/suit/bio_suit)) score += 10
|
||||
if(istype(M:wear_suit, /obj/item/clothing/suit/space) && istype(M:head, /obj/item/clothing/head/helmet/space))
|
||||
score += 15
|
||||
if(istype(M:wear_suit, /obj/item/clothing/suit/bio_suit) && istype(M:head, /obj/item/clothing/head/bio_hood))
|
||||
score += 15
|
||||
|
||||
|
||||
// log_debug("[M]'s resistance to [vector] viruses: [score]")
|
||||
|
||||
@@ -47,7 +52,7 @@ proc/airborne_can_reach(turf/source, turf/target)
|
||||
|
||||
for(var/i=0, i<5, i++) if(!step_towards(dummy, target)) break
|
||||
|
||||
var/rval = (dummy.loc in range(1,target))
|
||||
var/rval = dummy.Adjacent(target)
|
||||
dummy.loc = null
|
||||
dummy = null
|
||||
return rval
|
||||
@@ -65,10 +70,18 @@ proc/airborne_can_reach(turf/source, turf/target)
|
||||
// if one of the antibodies in the mob's body matches one of the disease's antigens, don't infect
|
||||
if(M.antibodies & disease.antigen != 0)
|
||||
return
|
||||
if(istype(M,/mob/living/carbon/human))
|
||||
var/mob/living/carbon/human/H = M
|
||||
if (H.species.flags & IS_SYNTHETIC)
|
||||
if(M.reagents.has_reagent("spaceacillin"))
|
||||
return
|
||||
if(istype(M,/mob/living/carbon/monkey))
|
||||
var/mob/living/carbon/monkey/chimp = M
|
||||
if (!(chimp.greaterform in disease.affected_species))
|
||||
return
|
||||
|
||||
if(istype(M,/mob/living/carbon/human))
|
||||
var/mob/living/carbon/human/chump = M
|
||||
if (!(chump.species.name in disease.affected_species))
|
||||
return
|
||||
|
||||
// log_debug("Infecting [M]")
|
||||
|
||||
if(prob(disease.infectionchance) || forced)
|
||||
@@ -87,14 +100,14 @@ proc/airborne_can_reach(turf/source, turf/target)
|
||||
var/datum/disease2/disease/D = new /datum/disease2/disease
|
||||
D.makerandom()
|
||||
D.infectionchance = 1
|
||||
M.virus2["[D.uniqueID]"] = D
|
||||
infect_virus2(M,D,1)
|
||||
M.hud_updateflag |= 1 << STATUS_HUD
|
||||
|
||||
//Infects mob M with random greated disease, if he doesn't have one
|
||||
/proc/infect_mob_random_greater(var/mob/living/carbon/M)
|
||||
var/datum/disease2/disease/D = new /datum/disease2/disease
|
||||
D.makerandom(1)
|
||||
M.virus2["[D.uniqueID]"] = D
|
||||
infect_virus2(M,D,1)
|
||||
M.hud_updateflag |= 1 << STATUS_HUD
|
||||
|
||||
//Fancy prob() function.
|
||||
|
||||
+13
-13
@@ -44,20 +44,20 @@
|
||||
#define BODYTEMP_HEAT_DAMAGE_LIMIT 360.15 // The limit the human body can take before it starts taking damage from heat.
|
||||
#define BODYTEMP_COLD_DAMAGE_LIMIT 260.15 // The limit the human body can take before it starts taking damage from coldness.
|
||||
|
||||
#define SPACE_HELMET_MIN_COLD_PROTECITON_TEMPERATURE 2.0 //what min_cold_protection_temperature is set to for space-helmet quality headwear. MUST NOT BE 0.
|
||||
#define SPACE_SUIT_MIN_COLD_PROTECITON_TEMPERATURE 2.0 //what min_cold_protection_temperature is set to for space-suit quality jumpsuits or suits. MUST NOT BE 0.
|
||||
#define SPACE_SUIT_MAX_HEAT_PROTECITON_TEMPERATURE 5000 //These need better heat protect
|
||||
#define FIRESUIT_MAX_HEAT_PROTECITON_TEMPERATURE 30000 //what max_heat_protection_temperature is set to for firesuit quality headwear. MUST NOT BE 0.
|
||||
#define FIRE_HELMET_MAX_HEAT_PROTECITON_TEMPERATURE 30000 //for fire helmet quality items (red and white hardhats)
|
||||
#define HELMET_MIN_COLD_PROTECITON_TEMPERATURE 160 //For normal helmets
|
||||
#define HELMET_MAX_HEAT_PROTECITON_TEMPERATURE 600 //For normal helmets
|
||||
#define ARMOR_MIN_COLD_PROTECITON_TEMPERATURE 160 //For armor
|
||||
#define ARMOR_MAX_HEAT_PROTECITON_TEMPERATURE 600 //For armor
|
||||
#define SPACE_HELMET_MIN_COLD_PROTECTION_TEMPERATURE 2.0 //what min_cold_protection_temperature is set to for space-helmet quality headwear. MUST NOT BE 0.
|
||||
#define SPACE_SUIT_MIN_COLD_PROTECTION_TEMPERATURE 2.0 //what min_cold_protection_temperature is set to for space-suit quality jumpsuits or suits. MUST NOT BE 0.
|
||||
#define SPACE_SUIT_MAX_HEAT_PROTECTION_TEMPERATURE 5000 //These need better heat protect
|
||||
#define FIRESUIT_MAX_HEAT_PROTECTION_TEMPERATURE 30000 //what max_heat_protection_temperature is set to for firesuit quality headwear. MUST NOT BE 0.
|
||||
#define FIRE_HELMET_MAX_HEAT_PROTECTION_TEMPERATURE 30000 //for fire helmet quality items (red and white hardhats)
|
||||
#define HELMET_MIN_COLD_PROTECTION_TEMPERATURE 160 //For normal helmets
|
||||
#define HELMET_MAX_HEAT_PROTECTION_TEMPERATURE 600 //For normal helmets
|
||||
#define ARMOR_MIN_COLD_PROTECTION_TEMPERATURE 160 //For armor
|
||||
#define ARMOR_MAX_HEAT_PROTECTION_TEMPERATURE 600 //For armor
|
||||
|
||||
#define GLOVES_MIN_COLD_PROTECITON_TEMPERATURE 2.0 //For some gloves (black and)
|
||||
#define GLOVES_MAX_HEAT_PROTECITON_TEMPERATURE 1500 //For some gloves
|
||||
#define SHOE_MIN_COLD_PROTECITON_TEMPERATURE 2.0 //For gloves
|
||||
#define SHOE_MAX_HEAT_PROTECITON_TEMPERATURE 1500 //For gloves
|
||||
#define GLOVES_MIN_COLD_PROTECTION_TEMPERATURE 2.0 //For some gloves (black and)
|
||||
#define GLOVES_MAX_HEAT_PROTECTION_TEMPERATURE 1500 //For some gloves
|
||||
#define SHOE_MIN_COLD_PROTECTION_TEMPERATURE 2.0 //For gloves
|
||||
#define SHOE_MAX_HEAT_PROTECTION_TEMPERATURE 1500 //For gloves
|
||||
|
||||
|
||||
#define PRESSURE_DAMAGE_COEFFICIENT 4 //The amount of pressure damage someone takes is equal to (pressure / HAZARD_HIGH_PRESSURE)*PRESSURE_DAMAGE_COEFFICIENT, with the maximum of MAX_PRESSURE_DAMAGE
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user