Maptick lag minimization port (#12066)

* extools.dll

* backend

* queued pipenet builds

* oh yeah we don't have that

* wow

* Update air.dm
This commit is contained in:
kevinz000
2020-05-02 18:34:33 -07:00
committed by Archie
parent 16fc2658b8
commit fb613db8d7
17 changed files with 870 additions and 829 deletions
@@ -1,341 +1,342 @@
// Quick overview:
//
// Pipes combine to form pipelines
// Pipelines and other atmospheric objects combine to form pipe_networks
// Note: A single pipe_network represents a completely open space
//
// Pipes -> Pipelines
// Pipelines + Other Objects -> Pipe network
#define PIPE_VISIBLE_LEVEL 2
#define PIPE_HIDDEN_LEVEL 1
/obj/machinery/atmospherics
anchored = TRUE
move_resist = INFINITY //Moving a connected machine without actually doing the normal (dis)connection things will probably cause a LOT of issues.
idle_power_usage = 0
active_power_usage = 0
power_channel = ENVIRON
layer = GAS_PIPE_HIDDEN_LAYER //under wires
resistance_flags = FIRE_PROOF
max_integrity = 200
obj_flags = CAN_BE_HIT | ON_BLUEPRINTS
var/nodealert = 0
var/can_unwrench = 0
var/initialize_directions = 0
var/pipe_color
var/piping_layer = PIPING_LAYER_DEFAULT
var/pipe_flags = NONE
var/global/list/iconsetids = list()
var/global/list/pipeimages = list()
var/image/pipe_vision_img = null
var/device_type = 0
var/list/obj/machinery/atmospherics/nodes
var/construction_type
var/pipe_state //icon_state as a pipe item
var/on = FALSE
/obj/machinery/atmospherics/examine(mob/user)
. = ..()
if(is_type_in_list(src, GLOB.ventcrawl_machinery) && isliving(user))
var/mob/living/L = user
if(L.ventcrawler)
. += "<span class='notice'>Alt-click to crawl through it.</span>"
/obj/machinery/atmospherics/New(loc, process = TRUE, setdir)
if(!isnull(setdir))
setDir(setdir)
if(pipe_flags & PIPING_CARDINAL_AUTONORMALIZE)
normalize_cardinal_directions()
nodes = new(device_type)
if (!armor)
armor = list("melee" = 25, "bullet" = 10, "laser" = 10, "energy" = 100, "bomb" = 0, "bio" = 100, "rad" = 100, "fire" = 100, "acid" = 70)
..()
if(process)
SSair.atmos_machinery += src
SetInitDirections()
/obj/machinery/atmospherics/Destroy()
for(var/i in 1 to device_type)
nullifyNode(i)
SSair.atmos_machinery -= src
dropContents()
if(pipe_vision_img)
qdel(pipe_vision_img)
return ..()
//return QDEL_HINT_FINDREFERENCE
/obj/machinery/atmospherics/proc/destroy_network()
return
/obj/machinery/atmospherics/proc/build_network()
// Called to build a network from this node
return
/obj/machinery/atmospherics/proc/nullifyNode(i)
if(nodes[i])
var/obj/machinery/atmospherics/N = nodes[i]
N.disconnect(src)
nodes[i] = null
/obj/machinery/atmospherics/proc/getNodeConnects()
var/list/node_connects = list()
node_connects.len = device_type
for(var/i in 1 to device_type)
for(var/D in GLOB.cardinals)
if(D & GetInitDirections())
if(D in node_connects)
continue
node_connects[i] = D
break
return node_connects
/obj/machinery/atmospherics/proc/normalize_cardinal_directions()
switch(dir)
if(SOUTH)
setDir(NORTH)
if(WEST)
setDir(EAST)
//this is called just after the air controller sets up turfs
/obj/machinery/atmospherics/proc/atmosinit(list/node_connects)
if(!node_connects) //for pipes where order of nodes doesn't matter
node_connects = getNodeConnects()
for(var/i in 1 to device_type)
for(var/obj/machinery/atmospherics/target in get_step(src,node_connects[i]))
if(can_be_node(target, i))
nodes[i] = target
break
update_icon()
/obj/machinery/atmospherics/proc/setPipingLayer(new_layer)
piping_layer = (pipe_flags & PIPING_DEFAULT_LAYER_ONLY) ? PIPING_LAYER_DEFAULT : new_layer
update_icon()
/obj/machinery/atmospherics/proc/can_be_node(obj/machinery/atmospherics/target, iteration)
return connection_check(target, piping_layer)
//Find a connecting /obj/machinery/atmospherics in specified direction
/obj/machinery/atmospherics/proc/findConnecting(direction, prompted_layer)
for(var/obj/machinery/atmospherics/target in get_step(src, direction))
if(target.initialize_directions & get_dir(target,src))
if(connection_check(target, prompted_layer))
return target
/obj/machinery/atmospherics/proc/connection_check(obj/machinery/atmospherics/target, given_layer)
if(isConnectable(target, given_layer) && target.isConnectable(src, given_layer) && (target.initialize_directions & get_dir(target,src)))
return TRUE
return FALSE
/obj/machinery/atmospherics/proc/isConnectable(obj/machinery/atmospherics/target, given_layer)
if(isnull(given_layer))
given_layer = piping_layer
if((target.piping_layer == given_layer) || (target.pipe_flags & PIPING_ALL_LAYER))
return TRUE
return FALSE
/obj/machinery/atmospherics/proc/pipeline_expansion()
return nodes
/obj/machinery/atmospherics/proc/SetInitDirections()
return
/obj/machinery/atmospherics/proc/GetInitDirections()
return initialize_directions
/obj/machinery/atmospherics/proc/returnPipenet()
return
/obj/machinery/atmospherics/proc/returnPipenetAir()
return
/obj/machinery/atmospherics/proc/setPipenet()
return
/obj/machinery/atmospherics/proc/replacePipenet()
return
/obj/machinery/atmospherics/proc/disconnect(obj/machinery/atmospherics/reference)
if(istype(reference, /obj/machinery/atmospherics/pipe))
var/obj/machinery/atmospherics/pipe/P = reference
P.destroy_network()
nodes[nodes.Find(reference)] = null
update_icon()
/obj/machinery/atmospherics/attackby(obj/item/W, mob/user, params)
if(istype(W, /obj/item/pipe)) //lets you autodrop
var/obj/item/pipe/pipe = W
if(user.dropItemToGround(pipe))
pipe.setPipingLayer(piping_layer) //align it with us
return TRUE
else
return ..()
/obj/machinery/atmospherics/wrench_act(mob/living/user, obj/item/I)
if(!can_unwrench(user))
return ..()
var/turf/T = get_turf(src)
if (level==1 && isturf(T) && T.intact)
to_chat(user, "<span class='warning'>You must remove the plating first!</span>")
return TRUE
var/datum/gas_mixture/int_air = return_air()
var/datum/gas_mixture/env_air = loc.return_air()
add_fingerprint(user)
var/unsafe_wrenching = FALSE
var/internal_pressure = int_air.return_pressure()-env_air.return_pressure()
to_chat(user, "<span class='notice'>You begin to unfasten \the [src]...</span>")
if (internal_pressure > 2*ONE_ATMOSPHERE)
to_chat(user, "<span class='warning'>As you begin unwrenching \the [src] a gush of air blows in your face... maybe you should reconsider?</span>")
unsafe_wrenching = TRUE //Oh dear oh dear
if(I.use_tool(src, user, 20, volume=50))
user.visible_message( \
"[user] unfastens \the [src].", \
"<span class='notice'>You unfasten \the [src].</span>", \
"<span class='italics'>You hear ratchet.</span>")
investigate_log("was <span class='warning'>REMOVED</span> by [key_name(usr)]", INVESTIGATE_ATMOS)
//You unwrenched a pipe full of pressure? Let's splat you into the wall, silly.
if(unsafe_wrenching)
unsafe_pressure_release(user, internal_pressure)
deconstruct(TRUE)
return TRUE
/obj/machinery/atmospherics/proc/can_unwrench(mob/user)
return can_unwrench
// Throws the user when they unwrench a pipe with a major difference between the internal and environmental pressure.
/obj/machinery/atmospherics/proc/unsafe_pressure_release(mob/user, pressures = null)
if(!user)
return
if(!pressures)
var/datum/gas_mixture/int_air = return_air()
var/datum/gas_mixture/env_air = loc.return_air()
pressures = int_air.return_pressure() - env_air.return_pressure()
user.visible_message("<span class='danger'>[user] is sent flying by pressure!</span>","<span class='userdanger'>The pressure sends you flying!</span>")
// if get_dir(src, user) is not 0, target is the edge_target_turf on that dir
// otherwise, edge_target_turf uses a random cardinal direction
// range is pressures / 250
// speed is pressures / 1250
user.throw_at(get_edge_target_turf(user, get_dir(src, user) || pick(GLOB.cardinals)), pressures / 250, pressures / 1250)
/obj/machinery/atmospherics/deconstruct(disassembled = TRUE)
if(!(flags_1 & NODECONSTRUCT_1))
if(can_unwrench)
var/obj/item/pipe/stored = new construction_type(loc, null, dir, src)
stored.setPipingLayer(piping_layer)
if(!disassembled)
stored.obj_integrity = stored.max_integrity * 0.5
transfer_fingerprints_to(stored)
..()
/obj/machinery/atmospherics/proc/getpipeimage(iconset, iconstate, direction, col=rgb(255,255,255), piping_layer=2)
//Add identifiers for the iconset
if(iconsetids[iconset] == null)
iconsetids[iconset] = num2text(iconsetids.len + 1)
//Generate a unique identifier for this image combination
var/identifier = iconsetids[iconset] + "_[iconstate]_[direction]_[col]_[piping_layer]"
if((!(. = pipeimages[identifier])))
var/image/pipe_overlay
pipe_overlay = . = pipeimages[identifier] = image(iconset, iconstate, dir = direction)
pipe_overlay.color = col
PIPING_LAYER_SHIFT(pipe_overlay, piping_layer)
/obj/machinery/atmospherics/on_construction(obj_color, set_layer)
if(can_unwrench)
add_atom_colour(obj_color, FIXED_COLOUR_PRIORITY)
pipe_color = obj_color
setPipingLayer(set_layer)
var/turf/T = get_turf(src)
level = T.intact ? 2 : 1
atmosinit()
var/list/nodes = pipeline_expansion()
for(var/obj/machinery/atmospherics/A in nodes)
A.atmosinit()
A.addMember(src)
build_network()
/obj/machinery/atmospherics/Entered(atom/movable/AM)
if(istype(AM, /mob/living))
var/mob/living/L = AM
L.ventcrawl_layer = piping_layer
return ..()
/obj/machinery/atmospherics/singularity_pull(S, current_size)
if(current_size >= STAGE_FIVE)
deconstruct(FALSE)
return ..()
#define VENT_SOUND_DELAY 30
/obj/machinery/atmospherics/relaymove(mob/living/user, direction)
direction &= initialize_directions
if(!direction || !(direction in GLOB.cardinals)) //cant go this way.
return
if(user in buckled_mobs)// fixes buckle ventcrawl edgecase fuck bug
return
var/obj/machinery/atmospherics/target_move = findConnecting(direction, user.ventcrawl_layer)
if(target_move)
if(target_move.can_crawl_through())
if(is_type_in_typecache(target_move, GLOB.ventcrawl_machinery))
user.forceMove(target_move.loc) //handle entering and so on.
user.visible_message("<span class='notice'>You hear something squeezing through the ducts...</span>", "<span class='notice'>You climb out the ventilation system.")
else
var/list/pipenetdiff = returnPipenets() ^ target_move.returnPipenets()
if(pipenetdiff.len)
user.update_pipe_vision(target_move)
user.forceMove(target_move)
user.client.eye = target_move //Byond only updates the eye every tick, This smooths out the movement
if(world.time - user.last_played_vent > VENT_SOUND_DELAY)
user.last_played_vent = world.time
playsound(src, 'sound/machines/ventcrawl.ogg', 50, 1, -3)
else if(is_type_in_typecache(src, GLOB.ventcrawl_machinery) && can_crawl_through()) //if we move in a way the pipe can connect, but doesn't - or we're in a vent
user.forceMove(loc)
user.visible_message("<span class='notice'>You hear something squeezing through the ducts...</span>", "<span class='notice'>You climb out the ventilation system.")
user.canmove = FALSE
addtimer(VARSET_CALLBACK(user, canmove, TRUE), 1)
/obj/machinery/atmospherics/AltClick(mob/living/L)
if(is_type_in_typecache(src, GLOB.ventcrawl_machinery))
return L.handle_ventcrawl(src)
return ..()
/obj/machinery/atmospherics/proc/can_crawl_through()
return TRUE
/obj/machinery/atmospherics/proc/returnPipenets()
return list()
/obj/machinery/atmospherics/update_remote_sight(mob/user)
user.sight |= (SEE_TURFS|BLIND)
//Used for certain children of obj/machinery/atmospherics to not show pipe vision when mob is inside it.
/obj/machinery/atmospherics/proc/can_see_pipes()
return TRUE
/obj/machinery/atmospherics/proc/update_layer()
// Quick overview:
//
// Pipes combine to form pipelines
// Pipelines and other atmospheric objects combine to form pipe_networks
// Note: A single pipe_network represents a completely open space
//
// Pipes -> Pipelines
// Pipelines + Other Objects -> Pipe network
#define PIPE_VISIBLE_LEVEL 2
#define PIPE_HIDDEN_LEVEL 1
/obj/machinery/atmospherics
anchored = TRUE
move_resist = INFINITY //Moving a connected machine without actually doing the normal (dis)connection things will probably cause a LOT of issues.
idle_power_usage = 0
active_power_usage = 0
power_channel = ENVIRON
layer = GAS_PIPE_HIDDEN_LAYER //under wires
resistance_flags = FIRE_PROOF
max_integrity = 200
obj_flags = CAN_BE_HIT | ON_BLUEPRINTS
var/nodealert = 0
var/can_unwrench = 0
var/initialize_directions = 0
var/pipe_color
var/piping_layer = PIPING_LAYER_DEFAULT
var/pipe_flags = NONE
var/global/list/iconsetids = list()
var/global/list/pipeimages = list()
var/image/pipe_vision_img = null
var/device_type = 0
var/list/obj/machinery/atmospherics/nodes
var/construction_type
var/pipe_state //icon_state as a pipe item
var/on = FALSE
/obj/machinery/atmospherics/examine(mob/user)
. = ..()
if(is_type_in_list(src, GLOB.ventcrawl_machinery) && isliving(user))
var/mob/living/L = user
if(L.ventcrawler)
. += "<span class='notice'>Alt-click to crawl through it.</span>"
/obj/machinery/atmospherics/New(loc, process = TRUE, setdir)
if(!isnull(setdir))
setDir(setdir)
if(pipe_flags & PIPING_CARDINAL_AUTONORMALIZE)
normalize_cardinal_directions()
nodes = new(device_type)
if (!armor)
armor = list("melee" = 25, "bullet" = 10, "laser" = 10, "energy" = 100, "bomb" = 0, "bio" = 100, "rad" = 100, "fire" = 100, "acid" = 70)
..()
if(process)
SSair.atmos_machinery += src
SetInitDirections()
/obj/machinery/atmospherics/Destroy()
for(var/i in 1 to device_type)
nullifyNode(i)
SSair.atmos_machinery -= src
SSair.pipenets_needing_rebuilt -= src
dropContents()
if(pipe_vision_img)
qdel(pipe_vision_img)
return ..()
//return QDEL_HINT_FINDREFERENCE
/obj/machinery/atmospherics/proc/destroy_network()
return
/obj/machinery/atmospherics/proc/build_network()
// Called to build a network from this node
return
/obj/machinery/atmospherics/proc/nullifyNode(i)
if(nodes[i])
var/obj/machinery/atmospherics/N = nodes[i]
N.disconnect(src)
nodes[i] = null
/obj/machinery/atmospherics/proc/getNodeConnects()
var/list/node_connects = list()
node_connects.len = device_type
for(var/i in 1 to device_type)
for(var/D in GLOB.cardinals)
if(D & GetInitDirections())
if(D in node_connects)
continue
node_connects[i] = D
break
return node_connects
/obj/machinery/atmospherics/proc/normalize_cardinal_directions()
switch(dir)
if(SOUTH)
setDir(NORTH)
if(WEST)
setDir(EAST)
//this is called just after the air controller sets up turfs
/obj/machinery/atmospherics/proc/atmosinit(list/node_connects)
if(!node_connects) //for pipes where order of nodes doesn't matter
node_connects = getNodeConnects()
for(var/i in 1 to device_type)
for(var/obj/machinery/atmospherics/target in get_step(src,node_connects[i]))
if(can_be_node(target, i))
nodes[i] = target
break
update_icon()
/obj/machinery/atmospherics/proc/setPipingLayer(new_layer)
piping_layer = (pipe_flags & PIPING_DEFAULT_LAYER_ONLY) ? PIPING_LAYER_DEFAULT : new_layer
update_icon()
/obj/machinery/atmospherics/proc/can_be_node(obj/machinery/atmospherics/target, iteration)
return connection_check(target, piping_layer)
//Find a connecting /obj/machinery/atmospherics in specified direction
/obj/machinery/atmospherics/proc/findConnecting(direction, prompted_layer)
for(var/obj/machinery/atmospherics/target in get_step(src, direction))
if(target.initialize_directions & get_dir(target,src))
if(connection_check(target, prompted_layer))
return target
/obj/machinery/atmospherics/proc/connection_check(obj/machinery/atmospherics/target, given_layer)
if(isConnectable(target, given_layer) && target.isConnectable(src, given_layer) && (target.initialize_directions & get_dir(target,src)))
return TRUE
return FALSE
/obj/machinery/atmospherics/proc/isConnectable(obj/machinery/atmospherics/target, given_layer)
if(isnull(given_layer))
given_layer = piping_layer
if((target.piping_layer == given_layer) || (target.pipe_flags & PIPING_ALL_LAYER))
return TRUE
return FALSE
/obj/machinery/atmospherics/proc/pipeline_expansion()
return nodes
/obj/machinery/atmospherics/proc/SetInitDirections()
return
/obj/machinery/atmospherics/proc/GetInitDirections()
return initialize_directions
/obj/machinery/atmospherics/proc/returnPipenet()
return
/obj/machinery/atmospherics/proc/returnPipenetAir()
return
/obj/machinery/atmospherics/proc/setPipenet()
return
/obj/machinery/atmospherics/proc/replacePipenet()
return
/obj/machinery/atmospherics/proc/disconnect(obj/machinery/atmospherics/reference)
if(istype(reference, /obj/machinery/atmospherics/pipe))
var/obj/machinery/atmospherics/pipe/P = reference
P.destroy_network()
nodes[nodes.Find(reference)] = null
update_icon()
/obj/machinery/atmospherics/attackby(obj/item/W, mob/user, params)
if(istype(W, /obj/item/pipe)) //lets you autodrop
var/obj/item/pipe/pipe = W
if(user.dropItemToGround(pipe))
pipe.setPipingLayer(piping_layer) //align it with us
return TRUE
else
return ..()
/obj/machinery/atmospherics/wrench_act(mob/living/user, obj/item/I)
if(!can_unwrench(user))
return ..()
var/turf/T = get_turf(src)
if (level==1 && isturf(T) && T.intact)
to_chat(user, "<span class='warning'>You must remove the plating first!</span>")
return TRUE
var/datum/gas_mixture/int_air = return_air()
var/datum/gas_mixture/env_air = loc.return_air()
add_fingerprint(user)
var/unsafe_wrenching = FALSE
var/internal_pressure = int_air.return_pressure()-env_air.return_pressure()
to_chat(user, "<span class='notice'>You begin to unfasten \the [src]...</span>")
if (internal_pressure > 2*ONE_ATMOSPHERE)
to_chat(user, "<span class='warning'>As you begin unwrenching \the [src] a gush of air blows in your face... maybe you should reconsider?</span>")
unsafe_wrenching = TRUE //Oh dear oh dear
if(I.use_tool(src, user, 20, volume=50))
user.visible_message( \
"[user] unfastens \the [src].", \
"<span class='notice'>You unfasten \the [src].</span>", \
"<span class='italics'>You hear ratchet.</span>")
investigate_log("was <span class='warning'>REMOVED</span> by [key_name(usr)]", INVESTIGATE_ATMOS)
//You unwrenched a pipe full of pressure? Let's splat you into the wall, silly.
if(unsafe_wrenching)
unsafe_pressure_release(user, internal_pressure)
deconstruct(TRUE)
return TRUE
/obj/machinery/atmospherics/proc/can_unwrench(mob/user)
return can_unwrench
// Throws the user when they unwrench a pipe with a major difference between the internal and environmental pressure.
/obj/machinery/atmospherics/proc/unsafe_pressure_release(mob/user, pressures = null)
if(!user)
return
if(!pressures)
var/datum/gas_mixture/int_air = return_air()
var/datum/gas_mixture/env_air = loc.return_air()
pressures = int_air.return_pressure() - env_air.return_pressure()
user.visible_message("<span class='danger'>[user] is sent flying by pressure!</span>","<span class='userdanger'>The pressure sends you flying!</span>")
// if get_dir(src, user) is not 0, target is the edge_target_turf on that dir
// otherwise, edge_target_turf uses a random cardinal direction
// range is pressures / 250
// speed is pressures / 1250
user.throw_at(get_edge_target_turf(user, get_dir(src, user) || pick(GLOB.cardinals)), pressures / 250, pressures / 1250)
/obj/machinery/atmospherics/deconstruct(disassembled = TRUE)
if(!(flags_1 & NODECONSTRUCT_1))
if(can_unwrench)
var/obj/item/pipe/stored = new construction_type(loc, null, dir, src)
stored.setPipingLayer(piping_layer)
if(!disassembled)
stored.obj_integrity = stored.max_integrity * 0.5
transfer_fingerprints_to(stored)
..()
/obj/machinery/atmospherics/proc/getpipeimage(iconset, iconstate, direction, col=rgb(255,255,255), piping_layer=2)
//Add identifiers for the iconset
if(iconsetids[iconset] == null)
iconsetids[iconset] = num2text(iconsetids.len + 1)
//Generate a unique identifier for this image combination
var/identifier = iconsetids[iconset] + "_[iconstate]_[direction]_[col]_[piping_layer]"
if((!(. = pipeimages[identifier])))
var/image/pipe_overlay
pipe_overlay = . = pipeimages[identifier] = image(iconset, iconstate, dir = direction)
pipe_overlay.color = col
PIPING_LAYER_SHIFT(pipe_overlay, piping_layer)
/obj/machinery/atmospherics/on_construction(obj_color, set_layer)
if(can_unwrench)
add_atom_colour(obj_color, FIXED_COLOUR_PRIORITY)
pipe_color = obj_color
setPipingLayer(set_layer)
var/turf/T = get_turf(src)
level = T.intact ? 2 : 1
atmosinit()
var/list/nodes = pipeline_expansion()
for(var/obj/machinery/atmospherics/A in nodes)
A.atmosinit()
A.addMember(src)
build_network()
/obj/machinery/atmospherics/Entered(atom/movable/AM)
if(istype(AM, /mob/living))
var/mob/living/L = AM
L.ventcrawl_layer = piping_layer
return ..()
/obj/machinery/atmospherics/singularity_pull(S, current_size)
if(current_size >= STAGE_FIVE)
deconstruct(FALSE)
return ..()
#define VENT_SOUND_DELAY 30
/obj/machinery/atmospherics/relaymove(mob/living/user, direction)
direction &= initialize_directions
if(!direction || !(direction in GLOB.cardinals)) //cant go this way.
return
if(user in buckled_mobs)// fixes buckle ventcrawl edgecase fuck bug
return
var/obj/machinery/atmospherics/target_move = findConnecting(direction, user.ventcrawl_layer)
if(target_move)
if(target_move.can_crawl_through())
if(is_type_in_typecache(target_move, GLOB.ventcrawl_machinery))
user.forceMove(target_move.loc) //handle entering and so on.
user.visible_message("<span class='notice'>You hear something squeezing through the ducts...</span>", "<span class='notice'>You climb out the ventilation system.")
else
var/list/pipenetdiff = returnPipenets() ^ target_move.returnPipenets()
if(pipenetdiff.len)
user.update_pipe_vision(target_move)
user.forceMove(target_move)
user.client.eye = target_move //Byond only updates the eye every tick, This smooths out the movement
if(world.time - user.last_played_vent > VENT_SOUND_DELAY)
user.last_played_vent = world.time
playsound(src, 'sound/machines/ventcrawl.ogg', 50, 1, -3)
else if(is_type_in_typecache(src, GLOB.ventcrawl_machinery) && can_crawl_through()) //if we move in a way the pipe can connect, but doesn't - or we're in a vent
user.forceMove(loc)
user.visible_message("<span class='notice'>You hear something squeezing through the ducts...</span>", "<span class='notice'>You climb out the ventilation system.")
user.canmove = FALSE
addtimer(VARSET_CALLBACK(user, canmove, TRUE), 1)
/obj/machinery/atmospherics/AltClick(mob/living/L)
if(is_type_in_typecache(src, GLOB.ventcrawl_machinery))
return L.handle_ventcrawl(src)
return ..()
/obj/machinery/atmospherics/proc/can_crawl_through()
return TRUE
/obj/machinery/atmospherics/proc/returnPipenets()
return list()
/obj/machinery/atmospherics/update_remote_sight(mob/user)
user.sight |= (SEE_TURFS|BLIND)
//Used for certain children of obj/machinery/atmospherics to not show pipe vision when mob is inside it.
/obj/machinery/atmospherics/proc/can_see_pipes()
return TRUE
/obj/machinery/atmospherics/proc/update_layer()
layer = initial(layer) + (piping_layer - PIPING_LAYER_DEFAULT) * PIPING_LAYER_LCHANGE
@@ -119,7 +119,7 @@
if(node2)
node2.atmosinit()
node2.addMember(src)
build_network()
SSair.add_to_rebuild_queue(src)
return TRUE
@@ -144,8 +144,8 @@
for(var/i in 1 to device_type)
var/datum/pipeline/parent = parents[i]
if(!parent)
throw EXCEPTION("Component is missing a pipenet! Rebuilding...")
build_network()
stack_trace("Component is missing a pipenet! Rebuilding...")
SSair.add_to_rebuild_queue(src)
parent.update = 1
/obj/machinery/atmospherics/components/returnPipenets()
@@ -1,446 +1,446 @@
/obj/machinery/atmospherics/components/unary/cryo_cell
name = "cryo cell"
icon = 'icons/obj/cryogenics.dmi'
icon_state = "pod-off"
density = TRUE
max_integrity = 350
armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 100, "bomb" = 0, "bio" = 100, "rad" = 100, "fire" = 30, "acid" = 30)
layer = ABOVE_WINDOW_LAYER
state_open = FALSE
circuit = /obj/item/circuitboard/machine/cryo_tube
pipe_flags = PIPING_ONE_PER_TURF | PIPING_DEFAULT_LAYER_ONLY
occupant_typecache = list(/mob/living/carbon, /mob/living/simple_animal)
var/autoeject = FALSE
var/volume = 100
var/efficiency = 1
var/sleep_factor = 0.00125
var/unconscious_factor = 0.001
var/heat_capacity = 20000
var/conduction_coefficient = 0.3
var/obj/item/reagent_containers/glass/beaker = null
var/reagent_transfer = 0
var/obj/item/radio/radio
var/radio_key = /obj/item/encryptionkey/headset_med
var/radio_channel = RADIO_CHANNEL_MEDICAL
var/running_anim = FALSE
var/escape_in_progress = FALSE
var/message_cooldown
var/breakout_time = 300
/obj/machinery/atmospherics/components/unary/cryo_cell/Initialize()
. = ..()
initialize_directions = dir
radio = new(src)
radio.keyslot = new radio_key
radio.subspace_transmission = TRUE
radio.canhear_range = 0
radio.recalculateChannels()
/obj/machinery/atmospherics/components/unary/cryo_cell/on_construction()
..(dir, dir)
/obj/machinery/atmospherics/components/unary/cryo_cell/RefreshParts()
var/C
for(var/obj/item/stock_parts/matter_bin/M in component_parts)
C += M.rating
efficiency = initial(efficiency) * C
sleep_factor = initial(sleep_factor) * C
unconscious_factor = initial(unconscious_factor) * C
heat_capacity = initial(heat_capacity) / C
conduction_coefficient = initial(conduction_coefficient) * C
/obj/machinery/atmospherics/components/unary/cryo_cell/Destroy()
QDEL_NULL(radio)
QDEL_NULL(beaker)
return ..()
/obj/machinery/atmospherics/components/unary/cryo_cell/contents_explosion(severity, target)
..()
if(beaker)
beaker.ex_act(severity, target)
/obj/machinery/atmospherics/components/unary/cryo_cell/handle_atom_del(atom/A)
..()
if(A == beaker)
beaker = null
updateUsrDialog()
/obj/machinery/atmospherics/components/unary/cryo_cell/on_deconstruction()
if(beaker)
beaker.forceMove(drop_location())
beaker = null
/obj/machinery/atmospherics/components/unary/cryo_cell/update_icon()
cut_overlays()
if(panel_open)
add_overlay("pod-panel")
if(state_open)
icon_state = "pod-open"
return
if(occupant)
var/image/occupant_overlay
if(ismonkey(occupant)) // Monkey
occupant_overlay = image(CRYOMOBS, "monkey")
else if(isalienadult(occupant))
if(isalienroyal(occupant)) // Queen and prae
occupant_overlay = image(CRYOMOBS, "alienq")
else if(isalienhunter(occupant)) // Hunter
occupant_overlay = image(CRYOMOBS, "alienh")
else if(isaliensentinel(occupant)) // Sentinel
occupant_overlay = image(CRYOMOBS, "aliens")
else // Drone or other
occupant_overlay = image(CRYOMOBS, "aliend")
else if(ishuman(occupant) || islarva(occupant) || (isanimal(occupant) && !ismegafauna(occupant))) // Mobs that are smaller than cryotube
occupant_overlay = image(occupant.icon, occupant.icon_state)
occupant_overlay.copy_overlays(occupant)
else
occupant_overlay = image(CRYOMOBS, "generic")
occupant_overlay.dir = SOUTH
occupant_overlay.pixel_y = 22
if(on && !running_anim && is_operational())
icon_state = "pod-on"
running_anim = TRUE
run_anim(TRUE, occupant_overlay)
else
icon_state = "pod-off"
add_overlay(occupant_overlay)
add_overlay("cover-off")
else if(on && is_operational())
icon_state = "pod-on"
add_overlay("cover-on")
else
icon_state = "pod-off"
add_overlay("cover-off")
/obj/machinery/atmospherics/components/unary/cryo_cell/proc/run_anim(anim_up, image/occupant_overlay)
if(!on || !occupant || !is_operational())
running_anim = FALSE
return
cut_overlays()
if(occupant_overlay.pixel_y != 23) // Same effect as occupant_overlay.pixel_y == 22 || occupant_overlay.pixel_y == 24
anim_up = occupant_overlay.pixel_y == 22 // Same effect as if(occupant_overlay.pixel_y == 22) anim_up = TRUE ; if(occupant_overlay.pixel_y == 24) anim_up = FALSE
if(anim_up)
occupant_overlay.pixel_y++
else
occupant_overlay.pixel_y--
add_overlay(occupant_overlay)
add_overlay("cover-on")
addtimer(CALLBACK(src, .proc/run_anim, anim_up, occupant_overlay), 7, TIMER_UNIQUE)
/obj/machinery/atmospherics/components/unary/cryo_cell/process()
..()
if(!on)
return
if(!is_operational())
on = FALSE
update_icon()
return
if(!occupant)
return
var/mob/living/mob_occupant = occupant
if(mob_occupant.stat == DEAD) // We don't bother with dead people.
return
if(mob_occupant.health >= mob_occupant.getMaxHealth()) // Don't bother with fully healed people.
on = FALSE
update_icon()
playsound(src, 'sound/machines/cryo_warning.ogg', volume) // Bug the doctors.
var/msg = "Patient fully restored."
if(autoeject) // Eject if configured.
msg += " Auto ejecting patient now."
open_machine()
radio.talk_into(src, msg, radio_channel)
return
var/datum/gas_mixture/air1 = airs[1]
if(air1.gases.len)
if(mob_occupant.bodytemperature < T0C) // Sleepytime. Why? More cryo magic.
mob_occupant.Sleeping((mob_occupant.bodytemperature * sleep_factor) * 2000)
mob_occupant.Unconscious((mob_occupant.bodytemperature * unconscious_factor) * 2000)
if(beaker)
if(reagent_transfer == 0) // Magically transfer reagents. Because cryo magic.
beaker.reagents.trans_to(occupant, 1, efficiency * 0.25) // Transfer reagents.
beaker.reagents.reaction(occupant, VAPOR)
air1.gases[/datum/gas/oxygen] -= max(0,air1.gases[/datum/gas/oxygen] - 2 / efficiency) //Let's use gas for this
GAS_GARBAGE_COLLECT(air1.gases)
if(++reagent_transfer >= 10 * efficiency) // Throttle reagent transfer (higher efficiency will transfer the same amount but consume less from the beaker).
reagent_transfer = 0
return 1
/obj/machinery/atmospherics/components/unary/cryo_cell/process_atmos()
..()
if(!on)
return
var/datum/gas_mixture/air1 = airs[1]
if(!nodes[1] || !airs[1] || !air1.gases.len || air1.gases[/datum/gas/oxygen] < 5) // Turn off if the machine won't work.
on = FALSE
update_icon()
return
if(occupant)
var/mob/living/mob_occupant = occupant
var/cold_protection = 0
var/temperature_delta = air1.temperature - mob_occupant.bodytemperature // The only semi-realistic thing here: share temperature between the cell and the occupant.
if(ishuman(occupant))
var/mob/living/carbon/human/H = occupant
cold_protection = H.get_thermal_protection(air1.temperature, TRUE)
if(abs(temperature_delta) > 1)
var/air_heat_capacity = air1.heat_capacity()
var/heat = ((1 - cold_protection) * 0.1 + conduction_coefficient) * temperature_delta * (air_heat_capacity * heat_capacity / (air_heat_capacity + heat_capacity))
air1.temperature = max(air1.temperature - heat / air_heat_capacity, TCMB)
mob_occupant.adjust_bodytemperature(heat / heat_capacity, TCMB)
air1.gases[/datum/gas/oxygen] = max(0,air1.gases[/datum/gas/oxygen] - 0.5 / efficiency) // Magically consume gas? Why not, we run on cryo magic.
GAS_GARBAGE_COLLECT(air1.gases)
/obj/machinery/atmospherics/components/unary/cryo_cell/power_change()
..()
update_icon()
/obj/machinery/atmospherics/components/unary/cryo_cell/relaymove(mob/user)
if(message_cooldown <= world.time)
message_cooldown = world.time + 50
to_chat(user, "<span class='warning'>[src]'s door won't budge!</span>")
/obj/machinery/atmospherics/components/unary/cryo_cell/open_machine(drop = 0)
if(!state_open && !panel_open)
on = FALSE
..()
for(var/mob/M in contents) //only drop mobs
M.forceMove(get_turf(src))
if(isliving(M))
var/mob/living/L = M
L.update_canmove()
occupant = null
update_icon()
/obj/machinery/atmospherics/components/unary/cryo_cell/close_machine(mob/living/carbon/user)
if((isnull(user) || istype(user)) && state_open && !panel_open)
..(user)
return occupant
/obj/machinery/atmospherics/components/unary/cryo_cell/container_resist(mob/living/user)
user.changeNext_move(CLICK_CD_BREAKOUT)
user.last_special = world.time + CLICK_CD_BREAKOUT
user.visible_message("<span class='notice'>You see [user] kicking against the glass of [src]!</span>", \
"<span class='notice'>You struggle inside [src], kicking the release with your foot... (this will take about [DisplayTimeText(breakout_time)].)</span>", \
"<span class='italics'>You hear a thump from [src].</span>")
if(do_after(user, breakout_time, target = src))
if(!user || user.stat != CONSCIOUS || user.loc != src )
return
user.visible_message("<span class='warning'>[user] successfully broke out of [src]!</span>", \
"<span class='notice'>You successfully break out of [src]!</span>")
open_machine()
/obj/machinery/atmospherics/components/unary/cryo_cell/examine(mob/user)
. = ..()
if(occupant)
if(on)
. += "Someone's inside [src]!"
else
. += "You can barely make out a form floating in [src]."
else
. += "[src] seems empty."
/obj/machinery/atmospherics/components/unary/cryo_cell/MouseDrop_T(mob/target, mob/user)
if(user.stat || user.lying || !Adjacent(user) || !user.Adjacent(target) || !iscarbon(target) || !user.IsAdvancedToolUser())
return
if (target.IsKnockdown() || target.IsStun() || target.IsSleeping() || target.IsUnconscious())
close_machine(target)
else
user.visible_message("<b>[user]</b> starts shoving [target] inside [src].", "<span class='notice'>You start shoving [target] inside [src].</span>")
if (do_after(user, 25, target=target))
close_machine(target)
/obj/machinery/atmospherics/components/unary/cryo_cell/attackby(obj/item/I, mob/user, params)
if(istype(I, /obj/item/reagent_containers/glass))
. = 1 //no afterattack
if(beaker)
to_chat(user, "<span class='warning'>A beaker is already loaded into [src]!</span>")
return
if(!user.transferItemToLoc(I, src))
return
beaker = I
user.visible_message("[user] places [I] in [src].", \
"<span class='notice'>You place [I] in [src].</span>")
var/reagentlist = pretty_string_from_reagent_list(I.reagents.reagent_list)
log_game("[key_name(user)] added an [I] to cryo containing [reagentlist]")
return
if(!on && !occupant && !state_open && (default_deconstruction_screwdriver(user, "pod-off", "pod-off", I)) \
|| default_change_direction_wrench(user, I) \
|| default_pry_open(I) \
|| default_deconstruction_crowbar(I))
update_icon()
return
else if(istype(I, /obj/item/screwdriver))
to_chat(user, "<span class='notice'>You can't access the maintenance panel while the pod is " \
+ (on ? "active" : (occupant ? "full" : "open")) + ".</span>")
return
return ..()
/obj/machinery/atmospherics/components/unary/cryo_cell/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
datum/tgui/master_ui = null, datum/ui_state/state = GLOB.notcontained_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
ui = new(user, src, ui_key, "cryo", name, 400, 550, master_ui, state)
ui.open()
/obj/machinery/atmospherics/components/unary/cryo_cell/ui_data()
var/list/data = list()
data["isOperating"] = on
data["hasOccupant"] = occupant ? TRUE : FALSE
data["isOpen"] = state_open
data["autoEject"] = autoeject
data["occupant"] = list()
if(occupant)
var/mob/living/mob_occupant = occupant
data["occupant"]["name"] = mob_occupant.name
switch(mob_occupant.stat)
if(CONSCIOUS)
data["occupant"]["stat"] = "Conscious"
data["occupant"]["statstate"] = "good"
if(SOFT_CRIT)
data["occupant"]["stat"] = "Conscious"
data["occupant"]["statstate"] = "average"
if(UNCONSCIOUS)
data["occupant"]["stat"] = "Unconscious"
data["occupant"]["statstate"] = "average"
if(DEAD)
data["occupant"]["stat"] = "Dead"
data["occupant"]["statstate"] = "bad"
data["occupant"]["health"] = round(mob_occupant.health, 1)
data["occupant"]["maxHealth"] = mob_occupant.maxHealth
data["occupant"]["minHealth"] = HEALTH_THRESHOLD_DEAD
data["occupant"]["bruteLoss"] = round(mob_occupant.getBruteLoss(), 1)
data["occupant"]["oxyLoss"] = round(mob_occupant.getOxyLoss(), 1)
data["occupant"]["toxLoss"] = round(mob_occupant.getToxLoss(), 1)
data["occupant"]["fireLoss"] = round(mob_occupant.getFireLoss(), 1)
data["occupant"]["bodyTemperature"] = round(mob_occupant.bodytemperature, 1)
if(mob_occupant.bodytemperature < TCRYO)
data["occupant"]["temperaturestatus"] = "good"
else if(mob_occupant.bodytemperature < T0C)
data["occupant"]["temperaturestatus"] = "average"
else
data["occupant"]["temperaturestatus"] = "bad"
var/datum/gas_mixture/air1 = airs[1]
data["cellTemperature"] = round(air1.temperature, 1)
data["isBeakerLoaded"] = beaker ? TRUE : FALSE
var/beakerContents = list()
if(beaker && beaker.reagents && beaker.reagents.reagent_list.len)
for(var/datum/reagent/R in beaker.reagents.reagent_list)
beakerContents += list(list("name" = R.name, "volume" = R.volume))
data["beakerContents"] = beakerContents
return data
/obj/machinery/atmospherics/components/unary/cryo_cell/ui_act(action, params)
if(..())
return
switch(action)
if("power")
if(on)
on = FALSE
else if(!state_open)
on = TRUE
. = TRUE
if("door")
if(state_open)
close_machine()
else
open_machine()
. = TRUE
if("autoeject")
autoeject = !autoeject
. = TRUE
if("ejectbeaker")
if(beaker)
beaker.forceMove(drop_location())
if(Adjacent(usr) && !issilicon(usr))
usr.put_in_hands(beaker)
beaker = null
. = TRUE
update_icon()
/obj/machinery/atmospherics/components/unary/cryo_cell/CtrlClick(mob/user)
if(user.canUseTopic(src, BE_CLOSE, FALSE, NO_TK) && !state_open)
on = !on
update_icon()
return ..()
/obj/machinery/atmospherics/components/unary/cryo_cell/AltClick(mob/user)
. = ..()
if(user.canUseTopic(src, BE_CLOSE, FALSE, NO_TK))
if(state_open)
close_machine()
else
open_machine()
update_icon()
return TRUE
/obj/machinery/atmospherics/components/unary/cryo_cell/update_remote_sight(mob/living/user)
return // we don't see the pipe network while inside cryo.
/obj/machinery/atmospherics/components/unary/cryo_cell/get_remote_view_fullscreens(mob/user)
user.overlay_fullscreen("remote_view", /obj/screen/fullscreen/impaired, 1)
/obj/machinery/atmospherics/components/unary/cryo_cell/can_crawl_through()
return // can't ventcrawl in or out of cryo.
/obj/machinery/atmospherics/components/unary/cryo_cell/can_see_pipes()
return 0 // you can't see the pipe network when inside a cryo cell.
/obj/machinery/atmospherics/components/unary/cryo_cell/return_temperature()
var/datum/gas_mixture/G = airs[1]
if(G.total_moles() > 10)
return G.temperature
return ..()
/obj/machinery/atmospherics/components/unary/cryo_cell/default_change_direction_wrench(mob/user, obj/item/wrench/W)
. = ..()
if(.)
SetInitDirections()
var/obj/machinery/atmospherics/node = nodes[1]
if(node)
node.disconnect(src)
nodes[1] = null
nullifyPipenet(parents[1])
atmosinit()
node = nodes[1]
if(node)
node.atmosinit()
node.addMember(src)
build_network()
#undef CRYOMOBS
/obj/machinery/atmospherics/components/unary/cryo_cell
name = "cryo cell"
icon = 'icons/obj/cryogenics.dmi'
icon_state = "pod-off"
density = TRUE
max_integrity = 350
armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 100, "bomb" = 0, "bio" = 100, "rad" = 100, "fire" = 30, "acid" = 30)
layer = ABOVE_WINDOW_LAYER
state_open = FALSE
circuit = /obj/item/circuitboard/machine/cryo_tube
pipe_flags = PIPING_ONE_PER_TURF | PIPING_DEFAULT_LAYER_ONLY
occupant_typecache = list(/mob/living/carbon, /mob/living/simple_animal)
var/autoeject = FALSE
var/volume = 100
var/efficiency = 1
var/sleep_factor = 0.00125
var/unconscious_factor = 0.001
var/heat_capacity = 20000
var/conduction_coefficient = 0.3
var/obj/item/reagent_containers/glass/beaker = null
var/reagent_transfer = 0
var/obj/item/radio/radio
var/radio_key = /obj/item/encryptionkey/headset_med
var/radio_channel = RADIO_CHANNEL_MEDICAL
var/running_anim = FALSE
var/escape_in_progress = FALSE
var/message_cooldown
var/breakout_time = 300
/obj/machinery/atmospherics/components/unary/cryo_cell/Initialize()
. = ..()
initialize_directions = dir
radio = new(src)
radio.keyslot = new radio_key
radio.subspace_transmission = TRUE
radio.canhear_range = 0
radio.recalculateChannels()
/obj/machinery/atmospherics/components/unary/cryo_cell/on_construction()
..(dir, dir)
/obj/machinery/atmospherics/components/unary/cryo_cell/RefreshParts()
var/C
for(var/obj/item/stock_parts/matter_bin/M in component_parts)
C += M.rating
efficiency = initial(efficiency) * C
sleep_factor = initial(sleep_factor) * C
unconscious_factor = initial(unconscious_factor) * C
heat_capacity = initial(heat_capacity) / C
conduction_coefficient = initial(conduction_coefficient) * C
/obj/machinery/atmospherics/components/unary/cryo_cell/Destroy()
QDEL_NULL(radio)
QDEL_NULL(beaker)
return ..()
/obj/machinery/atmospherics/components/unary/cryo_cell/contents_explosion(severity, target)
..()
if(beaker)
beaker.ex_act(severity, target)
/obj/machinery/atmospherics/components/unary/cryo_cell/handle_atom_del(atom/A)
..()
if(A == beaker)
beaker = null
updateUsrDialog()
/obj/machinery/atmospherics/components/unary/cryo_cell/on_deconstruction()
if(beaker)
beaker.forceMove(drop_location())
beaker = null
/obj/machinery/atmospherics/components/unary/cryo_cell/update_icon()
cut_overlays()
if(panel_open)
add_overlay("pod-panel")
if(state_open)
icon_state = "pod-open"
return
if(occupant)
var/image/occupant_overlay
if(ismonkey(occupant)) // Monkey
occupant_overlay = image(CRYOMOBS, "monkey")
else if(isalienadult(occupant))
if(isalienroyal(occupant)) // Queen and prae
occupant_overlay = image(CRYOMOBS, "alienq")
else if(isalienhunter(occupant)) // Hunter
occupant_overlay = image(CRYOMOBS, "alienh")
else if(isaliensentinel(occupant)) // Sentinel
occupant_overlay = image(CRYOMOBS, "aliens")
else // Drone or other
occupant_overlay = image(CRYOMOBS, "aliend")
else if(ishuman(occupant) || islarva(occupant) || (isanimal(occupant) && !ismegafauna(occupant))) // Mobs that are smaller than cryotube
occupant_overlay = image(occupant.icon, occupant.icon_state)
occupant_overlay.copy_overlays(occupant)
else
occupant_overlay = image(CRYOMOBS, "generic")
occupant_overlay.dir = SOUTH
occupant_overlay.pixel_y = 22
if(on && !running_anim && is_operational())
icon_state = "pod-on"
running_anim = TRUE
run_anim(TRUE, occupant_overlay)
else
icon_state = "pod-off"
add_overlay(occupant_overlay)
add_overlay("cover-off")
else if(on && is_operational())
icon_state = "pod-on"
add_overlay("cover-on")
else
icon_state = "pod-off"
add_overlay("cover-off")
/obj/machinery/atmospherics/components/unary/cryo_cell/proc/run_anim(anim_up, image/occupant_overlay)
if(!on || !occupant || !is_operational())
running_anim = FALSE
return
cut_overlays()
if(occupant_overlay.pixel_y != 23) // Same effect as occupant_overlay.pixel_y == 22 || occupant_overlay.pixel_y == 24
anim_up = occupant_overlay.pixel_y == 22 // Same effect as if(occupant_overlay.pixel_y == 22) anim_up = TRUE ; if(occupant_overlay.pixel_y == 24) anim_up = FALSE
if(anim_up)
occupant_overlay.pixel_y++
else
occupant_overlay.pixel_y--
add_overlay(occupant_overlay)
add_overlay("cover-on")
addtimer(CALLBACK(src, .proc/run_anim, anim_up, occupant_overlay), 7, TIMER_UNIQUE)
/obj/machinery/atmospherics/components/unary/cryo_cell/process()
..()
if(!on)
return
if(!is_operational())
on = FALSE
update_icon()
return
if(!occupant)
return
var/mob/living/mob_occupant = occupant
if(mob_occupant.stat == DEAD) // We don't bother with dead people.
return
if(mob_occupant.health >= mob_occupant.getMaxHealth()) // Don't bother with fully healed people.
on = FALSE
update_icon()
playsound(src, 'sound/machines/cryo_warning.ogg', volume) // Bug the doctors.
var/msg = "Patient fully restored."
if(autoeject) // Eject if configured.
msg += " Auto ejecting patient now."
open_machine()
radio.talk_into(src, msg, radio_channel)
return
var/datum/gas_mixture/air1 = airs[1]
if(air1.gases.len)
if(mob_occupant.bodytemperature < T0C) // Sleepytime. Why? More cryo magic.
mob_occupant.Sleeping((mob_occupant.bodytemperature * sleep_factor) * 2000)
mob_occupant.Unconscious((mob_occupant.bodytemperature * unconscious_factor) * 2000)
if(beaker)
if(reagent_transfer == 0) // Magically transfer reagents. Because cryo magic.
beaker.reagents.trans_to(occupant, 1, efficiency * 0.25) // Transfer reagents.
beaker.reagents.reaction(occupant, VAPOR)
air1.gases[/datum/gas/oxygen] -= max(0,air1.gases[/datum/gas/oxygen] - 2 / efficiency) //Let's use gas for this
GAS_GARBAGE_COLLECT(air1.gases)
if(++reagent_transfer >= 10 * efficiency) // Throttle reagent transfer (higher efficiency will transfer the same amount but consume less from the beaker).
reagent_transfer = 0
return 1
/obj/machinery/atmospherics/components/unary/cryo_cell/process_atmos()
..()
if(!on)
return
var/datum/gas_mixture/air1 = airs[1]
if(!nodes[1] || !airs[1] || !air1.gases.len || air1.gases[/datum/gas/oxygen] < 5) // Turn off if the machine won't work.
on = FALSE
update_icon()
return
if(occupant)
var/mob/living/mob_occupant = occupant
var/cold_protection = 0
var/temperature_delta = air1.temperature - mob_occupant.bodytemperature // The only semi-realistic thing here: share temperature between the cell and the occupant.
if(ishuman(occupant))
var/mob/living/carbon/human/H = occupant
cold_protection = H.get_thermal_protection(air1.temperature, TRUE)
if(abs(temperature_delta) > 1)
var/air_heat_capacity = air1.heat_capacity()
var/heat = ((1 - cold_protection) * 0.1 + conduction_coefficient) * temperature_delta * (air_heat_capacity * heat_capacity / (air_heat_capacity + heat_capacity))
air1.temperature = max(air1.temperature - heat / air_heat_capacity, TCMB)
mob_occupant.adjust_bodytemperature(heat / heat_capacity, TCMB)
air1.gases[/datum/gas/oxygen] = max(0,air1.gases[/datum/gas/oxygen] - 0.5 / efficiency) // Magically consume gas? Why not, we run on cryo magic.
GAS_GARBAGE_COLLECT(air1.gases)
/obj/machinery/atmospherics/components/unary/cryo_cell/power_change()
..()
update_icon()
/obj/machinery/atmospherics/components/unary/cryo_cell/relaymove(mob/user)
if(message_cooldown <= world.time)
message_cooldown = world.time + 50
to_chat(user, "<span class='warning'>[src]'s door won't budge!</span>")
/obj/machinery/atmospherics/components/unary/cryo_cell/open_machine(drop = 0)
if(!state_open && !panel_open)
on = FALSE
..()
for(var/mob/M in contents) //only drop mobs
M.forceMove(get_turf(src))
if(isliving(M))
var/mob/living/L = M
L.update_canmove()
occupant = null
update_icon()
/obj/machinery/atmospherics/components/unary/cryo_cell/close_machine(mob/living/carbon/user)
if((isnull(user) || istype(user)) && state_open && !panel_open)
..(user)
return occupant
/obj/machinery/atmospherics/components/unary/cryo_cell/container_resist(mob/living/user)
user.changeNext_move(CLICK_CD_BREAKOUT)
user.last_special = world.time + CLICK_CD_BREAKOUT
user.visible_message("<span class='notice'>You see [user] kicking against the glass of [src]!</span>", \
"<span class='notice'>You struggle inside [src], kicking the release with your foot... (this will take about [DisplayTimeText(breakout_time)].)</span>", \
"<span class='italics'>You hear a thump from [src].</span>")
if(do_after(user, breakout_time, target = src))
if(!user || user.stat != CONSCIOUS || user.loc != src )
return
user.visible_message("<span class='warning'>[user] successfully broke out of [src]!</span>", \
"<span class='notice'>You successfully break out of [src]!</span>")
open_machine()
/obj/machinery/atmospherics/components/unary/cryo_cell/examine(mob/user)
. = ..()
if(occupant)
if(on)
. += "Someone's inside [src]!"
else
. += "You can barely make out a form floating in [src]."
else
. += "[src] seems empty."
/obj/machinery/atmospherics/components/unary/cryo_cell/MouseDrop_T(mob/target, mob/user)
if(user.stat || user.lying || !Adjacent(user) || !user.Adjacent(target) || !iscarbon(target) || !user.IsAdvancedToolUser())
return
if (target.IsKnockdown() || target.IsStun() || target.IsSleeping() || target.IsUnconscious())
close_machine(target)
else
user.visible_message("<b>[user]</b> starts shoving [target] inside [src].", "<span class='notice'>You start shoving [target] inside [src].</span>")
if (do_after(user, 25, target=target))
close_machine(target)
/obj/machinery/atmospherics/components/unary/cryo_cell/attackby(obj/item/I, mob/user, params)
if(istype(I, /obj/item/reagent_containers/glass))
. = 1 //no afterattack
if(beaker)
to_chat(user, "<span class='warning'>A beaker is already loaded into [src]!</span>")
return
if(!user.transferItemToLoc(I, src))
return
beaker = I
user.visible_message("[user] places [I] in [src].", \
"<span class='notice'>You place [I] in [src].</span>")
var/reagentlist = pretty_string_from_reagent_list(I.reagents.reagent_list)
log_game("[key_name(user)] added an [I] to cryo containing [reagentlist]")
return
if(!on && !occupant && !state_open && (default_deconstruction_screwdriver(user, "pod-off", "pod-off", I)) \
|| default_change_direction_wrench(user, I) \
|| default_pry_open(I) \
|| default_deconstruction_crowbar(I))
update_icon()
return
else if(istype(I, /obj/item/screwdriver))
to_chat(user, "<span class='notice'>You can't access the maintenance panel while the pod is " \
+ (on ? "active" : (occupant ? "full" : "open")) + ".</span>")
return
return ..()
/obj/machinery/atmospherics/components/unary/cryo_cell/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
datum/tgui/master_ui = null, datum/ui_state/state = GLOB.notcontained_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
ui = new(user, src, ui_key, "cryo", name, 400, 550, master_ui, state)
ui.open()
/obj/machinery/atmospherics/components/unary/cryo_cell/ui_data()
var/list/data = list()
data["isOperating"] = on
data["hasOccupant"] = occupant ? TRUE : FALSE
data["isOpen"] = state_open
data["autoEject"] = autoeject
data["occupant"] = list()
if(occupant)
var/mob/living/mob_occupant = occupant
data["occupant"]["name"] = mob_occupant.name
switch(mob_occupant.stat)
if(CONSCIOUS)
data["occupant"]["stat"] = "Conscious"
data["occupant"]["statstate"] = "good"
if(SOFT_CRIT)
data["occupant"]["stat"] = "Conscious"
data["occupant"]["statstate"] = "average"
if(UNCONSCIOUS)
data["occupant"]["stat"] = "Unconscious"
data["occupant"]["statstate"] = "average"
if(DEAD)
data["occupant"]["stat"] = "Dead"
data["occupant"]["statstate"] = "bad"
data["occupant"]["health"] = round(mob_occupant.health, 1)
data["occupant"]["maxHealth"] = mob_occupant.maxHealth
data["occupant"]["minHealth"] = HEALTH_THRESHOLD_DEAD
data["occupant"]["bruteLoss"] = round(mob_occupant.getBruteLoss(), 1)
data["occupant"]["oxyLoss"] = round(mob_occupant.getOxyLoss(), 1)
data["occupant"]["toxLoss"] = round(mob_occupant.getToxLoss(), 1)
data["occupant"]["fireLoss"] = round(mob_occupant.getFireLoss(), 1)
data["occupant"]["bodyTemperature"] = round(mob_occupant.bodytemperature, 1)
if(mob_occupant.bodytemperature < TCRYO)
data["occupant"]["temperaturestatus"] = "good"
else if(mob_occupant.bodytemperature < T0C)
data["occupant"]["temperaturestatus"] = "average"
else
data["occupant"]["temperaturestatus"] = "bad"
var/datum/gas_mixture/air1 = airs[1]
data["cellTemperature"] = round(air1.temperature, 1)
data["isBeakerLoaded"] = beaker ? TRUE : FALSE
var/beakerContents = list()
if(beaker && beaker.reagents && beaker.reagents.reagent_list.len)
for(var/datum/reagent/R in beaker.reagents.reagent_list)
beakerContents += list(list("name" = R.name, "volume" = R.volume))
data["beakerContents"] = beakerContents
return data
/obj/machinery/atmospherics/components/unary/cryo_cell/ui_act(action, params)
if(..())
return
switch(action)
if("power")
if(on)
on = FALSE
else if(!state_open)
on = TRUE
. = TRUE
if("door")
if(state_open)
close_machine()
else
open_machine()
. = TRUE
if("autoeject")
autoeject = !autoeject
. = TRUE
if("ejectbeaker")
if(beaker)
beaker.forceMove(drop_location())
if(Adjacent(usr) && !issilicon(usr))
usr.put_in_hands(beaker)
beaker = null
. = TRUE
update_icon()
/obj/machinery/atmospherics/components/unary/cryo_cell/CtrlClick(mob/user)
if(user.canUseTopic(src, BE_CLOSE, FALSE, NO_TK) && !state_open)
on = !on
update_icon()
return ..()
/obj/machinery/atmospherics/components/unary/cryo_cell/AltClick(mob/user)
. = ..()
if(user.canUseTopic(src, BE_CLOSE, FALSE, NO_TK))
if(state_open)
close_machine()
else
open_machine()
update_icon()
return TRUE
/obj/machinery/atmospherics/components/unary/cryo_cell/update_remote_sight(mob/living/user)
return // we don't see the pipe network while inside cryo.
/obj/machinery/atmospherics/components/unary/cryo_cell/get_remote_view_fullscreens(mob/user)
user.overlay_fullscreen("remote_view", /obj/screen/fullscreen/impaired, 1)
/obj/machinery/atmospherics/components/unary/cryo_cell/can_crawl_through()
return // can't ventcrawl in or out of cryo.
/obj/machinery/atmospherics/components/unary/cryo_cell/can_see_pipes()
return 0 // you can't see the pipe network when inside a cryo cell.
/obj/machinery/atmospherics/components/unary/cryo_cell/return_temperature()
var/datum/gas_mixture/G = airs[1]
if(G.total_moles() > 10)
return G.temperature
return ..()
/obj/machinery/atmospherics/components/unary/cryo_cell/default_change_direction_wrench(mob/user, obj/item/wrench/W)
. = ..()
if(.)
SetInitDirections()
var/obj/machinery/atmospherics/node = nodes[1]
if(node)
node.disconnect(src)
nodes[1] = null
nullifyPipenet(parents[1])
atmosinit()
node = nodes[1]
if(node)
node.atmosinit()
node.addMember(src)
SSair.add_to_rebuild_queue(src)
#undef CRYOMOBS
@@ -116,7 +116,7 @@
if(node)
node.atmosinit()
node.addMember(src)
build_network()
SSair.add_to_rebuild_queue(src)
return TRUE
/obj/machinery/atmospherics/components/unary/thermomachine/ui_status(mob/user)
@@ -31,7 +31,7 @@
nodes = list()
for(var/obj/machinery/atmospherics/A in needs_nullifying)
A.disconnect(src)
A.build_network()
SSair.add_to_rebuild_queue(A)
/obj/machinery/atmospherics/pipe/layer_manifold/proc/get_all_connected_nodes()
return front_nodes + back_nodes + nodes
@@ -22,7 +22,7 @@
var/obj/machinery/atmospherics/oldN = nodes[i]
..()
if(oldN)
oldN.build_network()
SSair.add_to_rebuild_queue(oldN)
/obj/machinery/atmospherics/pipe/destroy_network()
QDEL_NULL(parent)
@@ -184,7 +184,6 @@
/obj/machinery/portable_atmospherics/canister/proto
name = "prototype canister"
/obj/machinery/portable_atmospherics/canister/proto/default
name = "prototype canister"
desc = "The best way to fix an atmospheric emergency... or the best way to introduce one."
@@ -197,7 +196,6 @@
can_min_release_pressure = (ONE_ATMOSPHERE / 30)
prototype = TRUE
/obj/machinery/portable_atmospherics/canister/proto/default/oxygen
name = "prototype canister"
desc = "A prototype canister for a prototype bike, what could go wrong?"
@@ -206,8 +204,6 @@
filled = 1
release_pressure = ONE_ATMOSPHERE*2
/obj/machinery/portable_atmospherics/canister/New(loc, datum/gas_mixture/existing_mixture)
..()
if(existing_mixture)
@@ -217,7 +213,7 @@
pump = new(src, FALSE)
pump.on = TRUE
pump.stat = 0
pump.build_network()
SSair.add_to_rebuild_queue(pump)
update_icon()
@@ -233,6 +229,7 @@
air_contents.gases[gas_type] = (maximum_pressure * filled) * air_contents.volume / (R_IDEAL_GAS_EQUATION * air_contents.temperature)
if(starter_temp)
air_contents.temperature = starter_temp
/obj/machinery/portable_atmospherics/canister/air/create_gas()
air_contents.gases[/datum/gas/oxygen] = (O2STANDARD * maximum_pressure * filled) * air_contents.volume / (R_IDEAL_GAS_EQUATION * air_contents.temperature)
air_contents.gases[/datum/gas/nitrogen] = (N2STANDARD * maximum_pressure * filled) * air_contents.volume / (R_IDEAL_GAS_EQUATION * air_contents.temperature)
@@ -20,7 +20,7 @@
pump = new(src, FALSE)
pump.on = TRUE
pump.stat = 0
pump.build_network()
SSair.add_to_rebuild_queue(pump)
/obj/machinery/portable_atmospherics/pump/Destroy()
var/turf/T = get_turf(src)
+1 -1
View File
@@ -249,7 +249,7 @@ All ShuttleMove procs go here
A.atmosinit()
if(A.returnPipenet())
A.addMember(src)
build_network()
SSair.add_to_rebuild_queue(src)
else
// atmosinit() calls update_icon(), so we don't need to call it
update_icon()