Files
CHOMPStation2/code/ATMOSPHERICS/atmospherics.dm

311 lines
13 KiB
Plaintext

/*
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
*/
/obj/machinery/atmospherics
anchored = TRUE
idle_power_usage = 0
active_power_usage = 0
power_channel = ENVIRON
var/nodealert = 0
var/power_rating //the maximum amount of power the machine can use to do work, affects how powerful the machine is, in Watts
unacidable = TRUE
layer = ATMOS_LAYER
plane = PLATING_PLANE
var/pipe_flags = PIPING_DEFAULT_LAYER_ONLY // Allow other layers by exception basis.
var/connect_types = CONNECT_TYPE_REGULAR
var/piping_layer = PIPING_LAYER_DEFAULT // This will replace icon_connect_type at some point ~Leshana
var/icon_connect_type = "" //"-supply" or "-scrubbers"
var/construction_type = null // Type path of the pipe item when this is deconstructed.
var/pipe_state // icon_state as a pipe item
var/being_loaded = FALSE //If the atmos machinery is currently being loaded via a map_template
var/initialize_directions = 0
var/pipe_color
var/obj/machinery/atmospherics/node1
var/obj/machinery/atmospherics/node2
/obj/machinery/atmospherics/Initialize(mapload, newdir)
. = ..()
if(!isnull(newdir))
set_dir(newdir)
if(!pipe_color)
pipe_color = color
color = null
if(!pipe_color_check(pipe_color))
pipe_color = null
init_dir()
/obj/machinery/atmospherics/examine_icon()
return icon(icon=initial(icon),icon_state=initial(icon_state))
// This is used to set up what directions pipes will connect to. Should be called inside New() and whenever a dir changes.
/obj/machinery/atmospherics/proc/init_dir()
return
// Get ALL initialize_directions - Some types (HE pipes etc) combine two vars together for this.
/obj/machinery/atmospherics/proc/get_init_dirs()
return initialize_directions
// Get the direction each node is facing to connect.
// It now returns as a list so it can be fetched nicely, each entry corresponds to node of same number.
/obj/machinery/atmospherics/proc/get_node_connect_dirs()
return
// Initializes nodes by looking at neighboring atmospherics machinery to connect to.
// When we're being constructed at runtime, atmos_init() is called by the construction code.
// When dynamically loading a map atmos_init is called by the maploader (initTemplateBounds proc)
// But during initial world creation its called by the master_controller.
// TODO - Consolidate these different ways of being called once SSatoms is created.
/obj/machinery/atmospherics/proc/atmos_init()
return
/** Check if target is an acceptable target to connect as a node from this machine. */
/obj/machinery/atmospherics/proc/can_be_node(obj/machinery/atmospherics/target, node_num)
return (target.initialize_directions & get_dir(target,src)) && check_connectable(target) && target.check_connectable(src)
/** Check if this machine is willing to connect with the target machine. */
/obj/machinery/atmospherics/proc/check_connectable(obj/machinery/atmospherics/target)
return (src.connect_types & target.connect_types) && !check_if_side_in_use(target)
/** Returns true if a pipe is already connected to the same side of the target as ourselves. This is a hack to prevent multi-connection from the same turf. Our atmopipes do not have support for multiple connections, this requires a major refactor or replacement port from TG in the future. */
/obj/machinery/atmospherics/proc/check_if_side_in_use(obj/machinery/atmospherics/target)
if(istype(src, /obj/machinery/atmospherics/pipe/simple/hidden/universal) \
|| istype(src, /obj/machinery/atmospherics/pipe/simple/visible/universal) \
|| istype(target, /obj/machinery/atmospherics/pipe/simple/visible/universal) \
|| istype(target, /obj/machinery/atmospherics/pipe/simple/visible/universal))
return FALSE // Snowflake to allow univerals pipe adaptors to bridge the gap on the same side
var/list/node_list = target.get_neighbor_nodes_for_init()
if(!length(node_list)) // Preserving original behavior
return TRUE
// By default, do not allow connections from the same side.
var/turf/our_turf = get_turf(src)
for(var/obj/machinery/atmospherics/check_node in node_list)
if(QDELETED(check_node) || check_node == src)
continue
if(get_turf(check_node) == our_turf)
return TRUE
return FALSE
/// Prioritize pipes that are already trying to link to us, prevents mismatching when both sides call atmo_init() on their own
/obj/machinery/atmospherics/proc/get_prioritized_nodes(turf/at_loc)
RETURN_TYPE(/list)
var/priority_target = null
var/list/viable_nodes = list()
for(var/obj/machinery/atmospherics/checking in at_loc)
var/list/node_list = checking.get_neighbor_nodes_for_init()
if(!priority_target) // Check for pipes already linking to us from their side. We want them to be priority to link back.
for(var/obj/machinery/atmospherics/node_check in node_list)
if(!priority_target && node_check == src)
priority_target = checking
break
// Otherwise we just want a list of all the viable targets we COULD link to. check_connectable() will be called after to see if they can be linked to properly.
if(checking != priority_target)
viable_nodes += checking
// So the goal of this proc is to organize the list so that the one trying to link to us will be FIRST, but the list of targets is always all available nodes
if(priority_target) // Put the priority target at the head
viable_nodes = list(priority_target) + viable_nodes
return viable_nodes
/obj/machinery/atmospherics/attackby(atom/A, mob/user as mob)
if(istype(A, /obj/item/pipe_painter))
return
..()
/obj/machinery/atmospherics/proc/add_underlay(turf/T, obj/machinery/atmospherics/node, direction, icon_connect_type)
if(node)
if(!T.is_plating() && node.level == 1 && istype(node, /obj/machinery/atmospherics/pipe))
//underlays += icon_manager.get_atmos_icon("underlay_down", direction, color_cache_name(node))
underlays += GLOB.icon_manager.get_atmos_icon("underlay", direction, color_cache_name(node), "down" + icon_connect_type)
else
//underlays += icon_manager.get_atmos_icon("underlay_intact", direction, color_cache_name(node))
underlays += GLOB.icon_manager.get_atmos_icon("underlay", direction, color_cache_name(node), "intact" + icon_connect_type)
else
//underlays += icon_manager.get_atmos_icon("underlay_exposed", direction, pipe_color)
underlays += GLOB.icon_manager.get_atmos_icon("underlay", direction, color_cache_name(node), "exposed" + icon_connect_type)
/obj/machinery/atmospherics/proc/update_underlays()
return TRUE
/obj/machinery/atmospherics/proc/color_cache_name(obj/machinery/atmospherics/node)
//Don't use this for standard pipes
if(!istype(node))
return null
return node.pipe_color
/obj/machinery/atmospherics/process()
if(being_loaded) //If we're being maploaded, don't build the network just yet.
return
last_flow_rate = 0
last_power_draw = 0
build_network()
/obj/machinery/atmospherics/proc/network_expand(datum/pipe_network/new_network, obj/machinery/atmospherics/pipe/reference)
// Check to see if should be added to network. Add self if so and adjust variables appropriately.
// Note don't forget to have neighbors look as well!
return null
/obj/machinery/atmospherics/proc/build_network(new_attachment)
// Called to build a network from this node
return null
/obj/machinery/atmospherics/proc/return_network(obj/machinery/atmospherics/reference)
// Returns pipe_network associated with connection to reference
// Notes: should create network if necessary
// Should never return null
return null
/obj/machinery/atmospherics/proc/reassign_network(datum/pipe_network/old_network, datum/pipe_network/new_network)
// Used when two pipe_networks are combining
/obj/machinery/atmospherics/proc/return_network_air(datum/pipe_network/reference)
// Return a list of gas_mixture(s) in the object
// associated with reference pipe_network for use in rebuilding the networks gases list
// Is permitted to return null
/obj/machinery/atmospherics/proc/disconnect(obj/machinery/atmospherics/reference)
/obj/machinery/atmospherics/update_icon()
return null
/obj/machinery/atmospherics/proc/can_unwrench()
/* //Old version. We now handle unwrenching in the machinery itself.
var/datum/gas_mixture/int_air = return_air()
var/datum/gas_mixture/env_air = loc.return_air()
if((int_air.return_pressure()-env_air.return_pressure()) > 2*ONE_ATMOSPHERE)
return FALSE
*/
return TRUE
// Deconstruct into a pipe item.
/obj/machinery/atmospherics/atom_deconstruct()
if(QDELETED(src))
return
if(construction_type)
var/obj/item/pipe/I = new construction_type(loc, null, null, src)
I.setPipingLayer(piping_layer)
if(istype(I, /obj/item/pipe/trinary/flippable))
var/obj/item/pipe/trinary/flippable/flip = I
flip.icon_state = "[flip.icon_state][flip.mirrored ? "m" : ""]"
transfer_fingerprints_to(I)
qdel(src)
// Return a list of nodes which we should call atmos_init() and build_network() during on_construction()
/obj/machinery/atmospherics/proc/get_neighbor_nodes_for_init()
return null
// Called on construction (i.e from pipe item) but not on initialization
/obj/machinery/atmospherics/proc/on_construction(obj_color, set_layer)
pipe_color = obj_color
setPipingLayer(set_layer)
// TODO - M.connect_types = src.connect_types - Or otherwise copy from item? Or figure it out from piping layer?
var/turf/T = get_turf(src)
level = !T.is_plating() ? 2 : 1
atmos_init()
if(QDELETED(src))
return // TODO - Eventually should get rid of the need for this.
var/list/nodes = get_neighbor_nodes_for_init()
for(var/obj/machinery/atmospherics/A in nodes)
A.atmos_init()
A.build_network(TRUE)
build_network()
// There was a coder comment her from 7 years ago asking 'tg does it this way, should we?' and the answer was yes.
// By building the network BEFORE our nodes build their network, two things happened:
// 1. The network was built and none of the pipes got their temporary air vaiables, resulting in the pipes having no air in them
// 2. The previous network was nulled but never deleted, resulting in a memory leak.
// So now, we build our network AFTER the nodes build their network AND we delete the previous network.
// This sets our piping layer. Hopefully its cool.
/obj/machinery/atmospherics/proc/setPipingLayer(new_layer)
if(pipe_flags & (PIPING_DEFAULT_LAYER_ONLY|PIPING_ALL_LAYER))
new_layer = PIPING_LAYER_DEFAULT
piping_layer = new_layer
// Do it the Polaris way
switch(piping_layer)
if(PIPING_LAYER_SCRUBBER)
icon_state = "[icon_state]-scrubbers"
connect_types = CONNECT_TYPE_SCRUBBER
layer = PIPES_SCRUBBER_LAYER
icon_connect_type = "-scrubbers"
if(PIPING_LAYER_SUPPLY)
icon_state = "[icon_state]-supply"
connect_types = CONNECT_TYPE_SUPPLY
layer = PIPES_SUPPLY_LAYER
icon_connect_type = "-supply"
if(PIPING_LAYER_FUEL)
icon_state = "[icon_state]-fuel"
connect_types = CONNECT_TYPE_FUEL
layer = PIPES_FUEL_LAYER
icon_connect_type = "-fuel"
if(PIPING_LAYER_AUX)
icon_state = "[icon_state]-aux"
connect_types = CONNECT_TYPE_AUX
layer = PIPES_AUX_LAYER
icon_connect_type = "-aux"
if(pipe_flags & PIPING_ALL_LAYER)
connect_types = CONNECT_TYPE_REGULAR|CONNECT_TYPE_SUPPLY|CONNECT_TYPE_SCRUBBER|CONNECT_TYPE_FUEL|CONNECT_TYPE_AUX
// Or if we were to do it the TG way...
// pixel_x = PIPE_PIXEL_OFFSET_X(piping_layer)
// pixel_y = PIPE_PIXEL_OFFSET_Y(piping_layer)
// layer = initial(layer) + PIPE_LAYER_OFFSET(piping_layer)
/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_danger("[user] is sent flying by pressure!"),span_userdanger("The pressure sends you flying!"))
// 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
if(user.buckled)
user.buckled.unbuckle_mob(user, TRUE)
user.throw_at(get_edge_target_turf(user, get_dir(src, user) || pick(GLOB.cardinal)), pressures / 250, pressures / 1250)
/// Blows out a pipe, deconstructing it, breaking the floor and releasing all mobs crawling inside it
/obj/machinery/atmospherics/proc/blowout(mob/user)
// Deconstruct turf
var/turf/our_turf = loc
if(!our_turf.is_plating() && istype(our_turf,/turf/simulated/floor)) //intact floor, pop the tile
var/turf/simulated/floor/our_floor = our_turf
our_floor.make_plating(TRUE)
// Deconstruct pipe
var/datum/gas_mixture/int_air = return_air()
var/datum/gas_mixture/env_air = our_turf.return_air()
var/internal_pressure = int_air.return_pressure()-env_air.return_pressure()
atom_deconstruct()
// Release pressure
playsound(our_turf, 'sound/effects/bang.ogg', 70, 0, 0)
playsound(our_turf, 'sound/effects/clang2.ogg', 70, 0, 0)
if(internal_pressure > 2*ONE_ATMOSPHERE)
unsafe_pressure_release(user, internal_pressure)
playsound(our_turf, 'sound/machines/hiss.ogg', 50, 0, 0)