Files
Bloop 966d6547e8 Big tooltype decargo-culting (continued) (#95814)
## About The Pull Request

Gets some instances that I caught missed by
https://github.com/tgstation/tgstation/pull/95408
2026-04-29 12:19:00 -05:00

384 lines
13 KiB
Plaintext

// So much of atmospherics.dm was used solely by components, so separating this makes things all a lot cleaner.
// On top of that, now people can add component-speciic procs/vars if they want!
/obj/machinery/atmospherics/components
hide = FALSE
layer = GAS_PUMP_LAYER
///Is the component welded?
var/welded = FALSE
///Current underfloor_accessibility state, determines if the component should show the pipe underneath it and what plane it renders on.
var/underfloor_state = UNDERFLOOR_INTERACTABLE
///When the component is on a non default layer should we shift everything? Or just the underlay pipe
var/shift_underlay_only = TRUE
///Stores the parent pipeline, used in components
var/list/datum/pipeline/parents
///If this is queued for a rebuild this var signifies whether parents should be updated after it's done
var/update_parents_after_rebuild = FALSE
///Stores the gasmix for each node, used in components
var/list/datum/gas_mixture/airs
///Handles whether the custom reconcilation handling should be used
var/custom_reconcilation = FALSE
/obj/machinery/atmospherics/components/get_save_vars()
. = ..()
if(!override_naming)
// Prevents saving the dynamic name with \proper due to it converting to "???"
. -= NAMEOF(src, name)
. += NAMEOF(src, welded)
return .
/obj/machinery/atmospherics/components/Initialize(mapload)
parents = new(device_type)
airs = new(device_type)
. = ..()
for(var/i in 1 to device_type)
if(airs[i])
continue
var/datum/gas_mixture/component_mixture = new
component_mixture.volume = 200
airs[i] = component_mixture
update_appearance()
// Iconnery
/**
* Called by update_icon(), used individually by each component to determine the icon state without the pipe in consideration
*/
/obj/machinery/atmospherics/components/proc/update_icon_nopipes()
return
/obj/machinery/atmospherics/components/on_hide(datum/source, underfloor_accessibility)
hide_pipe(underfloor_accessibility)
return ..()
/**
* Called in on_hide(), set the underfloor_state var to true or false depending on the situation, calls update_icon()
*/
/obj/machinery/atmospherics/components/proc/hide_pipe(underfloor_accessibility)
underfloor_state = underfloor_accessibility
if(underfloor_state)
REMOVE_TRAIT(src, TRAIT_UNDERFLOOR, REF(src))
else
ADD_TRAIT(src, TRAIT_UNDERFLOOR, REF(src))
update_appearance(UPDATE_ICON)
/obj/machinery/atmospherics/components/update_icon()
update_icon_nopipes()
underlays.Cut()
color = null
var/uncovered_turf = loc && HAS_TRAIT(loc, TRAIT_UNCOVERED_TURF)
SET_PLANE_IMPLICIT(src, (underfloor_state == UNDERFLOOR_INTERACTABLE && !uncovered_turf) ? GAME_PLANE : FLOOR_PLANE)
// Layer is handled in update_layer()
if(!underfloor_state)
return ..()
if(pipe_flags & PIPING_DISTRO_AND_WASTE_LAYERS)
return ..()
var/connected = 0 //Direction bitset
var/underlay_pipe_layer = shift_underlay_only ? piping_layer : 3
for(var/i in 1 to device_type) //adds intact pieces
if(!nodes[i])
continue
var/obj/machinery/atmospherics/node = nodes[i]
var/node_dir = get_dir(src, node)
var/mutable_appearance/pipe_appearance = mutable_appearance('icons/obj/pipes_n_cables/pipe_underlays.dmi', "intact_[node_dir]_[underlay_pipe_layer]", appearance_flags = RESET_COLOR|KEEP_APART)
pipe_appearance.color = (node.pipe_color == ATMOS_COLOR_OMNI || istype(node, /obj/machinery/atmospherics/pipe/color_adapter)) ? pipe_color : node.pipe_color
if (underfloor_state == UNDERFLOOR_VISIBLE || uncovered_turf)
pipe_appearance.layer = BELOW_CATWALK_LAYER + get_pipe_layer_offset()
SET_PLANE_EXPLICIT(pipe_appearance, FLOOR_PLANE, src)
underlays += pipe_appearance
connected |= node_dir
for(var/direction in GLOB.cardinals)
if((initialize_directions & direction) && !(connected & direction))
var/mutable_appearance/pipe_appearance = mutable_appearance('icons/obj/pipes_n_cables/pipe_underlays.dmi', "exposed_[direction]_[underlay_pipe_layer]", appearance_flags = RESET_COLOR|KEEP_APART)
pipe_appearance.color = pipe_color
if (underfloor_state == UNDERFLOOR_VISIBLE || uncovered_turf)
pipe_appearance.layer = BELOW_CATWALK_LAYER + get_pipe_layer_offset()
SET_PLANE_EXPLICIT(pipe_appearance, FLOOR_PLANE, src)
underlays += pipe_appearance
if(!shift_underlay_only)
PIPING_LAYER_SHIFT(src, piping_layer)
return ..()
/obj/machinery/atmospherics/components/get_pipe_image(iconfile, iconstate, direction, color, piping_layer, trinary)
var/mutable_appearance/pipe_appearance = ..()
if (underfloor_state == UNDERFLOOR_VISIBLE || (loc && HAS_TRAIT(loc, TRAIT_UNCOVERED_TURF)))
pipe_appearance.layer = BELOW_CATWALK_LAYER + get_pipe_layer_offset()
SET_PLANE_EXPLICIT(pipe_appearance, FLOOR_PLANE, src)
return pipe_appearance
// Pipenet stuff; housekeeping
/obj/machinery/atmospherics/components/nullify_node(i)
if(parents[i])
nullify_pipenet(parents[i])
airs[i] = null
return ..()
/obj/machinery/atmospherics/components/on_construction(mob/user)
. = ..()
update_parents()
/obj/machinery/atmospherics/components/on_deconstruction(disassembled)
relocate_airs()
return ..()
/obj/machinery/atmospherics/components/rebuild_pipes()
. = ..()
if(update_parents_after_rebuild)
update_parents()
/obj/machinery/atmospherics/components/get_rebuild_targets()
var/list/to_return = list()
for(var/i in 1 to device_type)
if(parents[i])
continue
parents[i] = new /datum/pipeline()
to_return += parents[i]
return to_return
/**
* Called by nullify_node(), used to remove the pipeline the component is attached to
* Arguments:
* * -reference: the pipeline the component is attached to
*/
/obj/machinery/atmospherics/components/proc/nullify_pipenet(datum/pipeline/reference)
if(!reference)
CRASH("nullify_pipenet(null) called by [type] on [COORD(src)]")
for (var/i in 1 to parents.len)
if (parents[i] == reference)
reference.other_airs -= airs[i] // Disconnects from the pipeline side
parents[i] = null // Disconnects from the machinery side.
reference.other_atmos_machines -= src
if(custom_reconcilation)
reference.require_custom_reconcilation -= src
/**
* We explicitly qdel pipeline when this particular pipeline
* is projected to have no member and cause GC problems.
* We have to do this because components don't qdel pipelines
* while pipes must and will happily wreck and rebuild everything
* again every time they are qdeleted.
*/
if(!length(reference.other_atmos_machines) && !length(reference.members))
if(QDESTROYING(reference))
CRASH("nullify_pipenet() called on qdeleting [reference]")
qdel(reference)
/obj/machinery/atmospherics/components/return_pipenet_airs(datum/pipeline/reference)
var/list/returned_air = list()
for (var/i in 1 to parents.len)
if (parents[i] == reference)
returned_air += airs[i]
return returned_air
/obj/machinery/atmospherics/components/pipeline_expansion(datum/pipeline/reference)
if(reference)
return list(nodes[parents.Find(reference)])
return ..()
/obj/machinery/atmospherics/components/set_pipenet(datum/pipeline/reference, obj/machinery/atmospherics/target_component)
parents[nodes.Find(target_component)] = reference
/obj/machinery/atmospherics/components/return_pipenet(obj/machinery/atmospherics/target_component = nodes[1]) //returns parents[1] if called without argument
return parents[nodes.Find(target_component)]
/obj/machinery/atmospherics/components/replace_pipenet(datum/pipeline/Old, datum/pipeline/New)
parents[parents.Find(Old)] = New
// Helpers
/**
* Called in most atmos processes and gas handling situations, update the parents pipelines of the devices connected to the source component
* This way gases won't get stuck
*/
/obj/machinery/atmospherics/components/proc/update_parents()
if(!SSair.initialized)
return
if(rebuilding)
update_parents_after_rebuild = TRUE
return
for(var/i in 1 to device_type)
var/datum/pipeline/parent = parents[i]
if(!parent)
WARNING("Component is missing a pipenet! Rebuilding...")
SSair.add_to_rebuild_queue(src)
else
parent.update = TRUE
/obj/machinery/atmospherics/components/return_pipenets()
. = list()
for(var/i in 1 to device_type)
. += return_pipenet(nodes[i])
/// When this machine is in a pipenet that is reconciling airs, this proc can add pipelines to the calculation.
/// Can be either a list of pipenets or a single pipenet.
/obj/machinery/atmospherics/components/proc/return_pipenets_for_reconcilation(datum/pipeline/requester)
return list()
/// When this machine is in a pipenet that is reconciling airs, this proc can add airs to the calculation.
/// Can be either a list of airs or a single air mix.
/obj/machinery/atmospherics/components/proc/return_airs_for_reconcilation(datum/pipeline/requester)
return list()
// UI Stuff
/obj/machinery/atmospherics/components/ui_status(mob/user, datum/ui_state/state)
if(allowed(user))
return ..()
to_chat(user, span_danger("Access denied."))
return UI_CLOSE
// Tool acts
/obj/machinery/atmospherics/components/return_analyzable_air()
return airs
/**
* Handles machinery deconstruction and unsafe pressure release
*/
/obj/machinery/atmospherics/components/proc/crowbar_deconstruction_act(mob/living/user, obj/item/tool, internal_pressure = 0)
if(!panel_open)
balloon_alert(user, "open panel!")
return ITEM_INTERACT_SUCCESS
var/unsafe_wrenching = FALSE
var/filled_pipe = FALSE
var/datum/gas_mixture/environment_air = loc.return_air()
for(var/i in 1 to device_type)
var/datum/gas_mixture/inside_air = airs[i]
if(inside_air.total_moles() > 0 || internal_pressure)
filled_pipe = TRUE
if(!nodes[i] || (istype(nodes[i], /obj/machinery/atmospherics/components/unary/portables_connector) && !portable_device_connected(i)))
internal_pressure = internal_pressure > airs[i].return_pressure() ? internal_pressure : airs[i].return_pressure()
if(!filled_pipe)
return default_deconstruction_crowbar(user, tool)
to_chat(user, span_notice("You begin to unfasten \the [src]..."))
internal_pressure -= environment_air.return_pressure()
if(internal_pressure > 2 * ONE_ATMOSPHERE)
to_chat(user, span_warning("As you begin deconstructing \the [src] a gush of air blows in your face... maybe you should reconsider?"))
unsafe_wrenching = TRUE
if(!do_after(user, 2 SECONDS, src))
return
if(unsafe_wrenching)
unsafe_pressure_release(user, internal_pressure)
tool.play_tool_sound(src, 50)
deconstruct(TRUE)
return ITEM_INTERACT_SUCCESS
/obj/machinery/atmospherics/components/default_change_direction_wrench(mob/user, obj/item/I)
. = ..()
if(!.)
return FALSE
set_init_directions()
reconnect_nodes()
return TRUE
/obj/machinery/atmospherics/components/proc/reconnect_nodes()
for(var/i in 1 to device_type)
var/obj/machinery/atmospherics/node = nodes[i]
if(node)
if(src in node.nodes)
node.disconnect(src)
nodes[i] = null
if(parents[i])
nullify_pipenet(parents[i])
for(var/i in 1 to device_type)
var/obj/machinery/atmospherics/node = nodes[i]
atmos_init()
node = nodes[i]
if(node)
node.atmos_init()
node.add_member(src)
update_parents()
SSair.add_to_rebuild_queue(src)
/**
* Disconnects all nodes from ourselves, remove us from the node's nodes.
* Nullify our parent pipenet
*/
/obj/machinery/atmospherics/components/proc/disconnect_nodes()
for(var/i in 1 to device_type)
var/obj/machinery/atmospherics/node = nodes[i]
if(node)
if(src in node.nodes) //Only if it's actually connected. On-pipe version would is one-sided.
node.disconnect(src)
nodes[i] = null
if(parents[i])
nullify_pipenet(parents[i])
/**
* Connects all nodes to ourselves, add us to the node's nodes.
* Calls atmos_init() on the node and on us.
*/
/obj/machinery/atmospherics/components/proc/connect_nodes()
atmos_init()
for(var/i in 1 to device_type)
var/obj/machinery/atmospherics/node = nodes[i]
if(node)
node.atmos_init()
node.add_member(src)
SSair.add_to_rebuild_queue(src)
/**
* Easy way to toggle nodes connection and disconnection.
*
* Arguments:
* * disconnect - if TRUE, disconnects all nodes. If FALSE, connects all nodes.
*/
/obj/machinery/atmospherics/components/proc/change_nodes_connection(disconnect)
if(disconnect)
disconnect_nodes()
return
connect_nodes()
/obj/machinery/atmospherics/components/update_layer()
if (!underfloor_state)
layer = BELOW_CATWALK_LAYER
else if (PLANE_TO_TRUE(plane) == FLOOR_PLANE)
layer = ABOVE_OPEN_TURF_LAYER
else
layer = initial(layer)
layer += get_pipe_layer_offset()
/obj/machinery/atmospherics/components/proc/get_pipe_layer_offset()
return (piping_layer - PIPING_LAYER_DEFAULT) * PIPING_LAYER_LCHANGE + (GLOB.pipe_colors_ordered[pipe_color] * 0.001)
/**
* Handles air relocation to the pipenet/environment
*/
/obj/machinery/atmospherics/components/proc/relocate_airs(datum/gas_mixture/to_release)
var/turf/local_turf = get_turf(src)
for(var/i in 1 to device_type)
var/datum/gas_mixture/air = airs[i]
if(!nodes[i] || (istype(nodes[i], /obj/machinery/atmospherics/components/unary/portables_connector) && !portable_device_connected(i)))
if(!to_release)
to_release = air
continue
to_release.merge(air)
continue
var/datum/gas_mixture/parents_air = parents[i].air
parents_air.merge(air)
if(to_release)
local_turf.assume_air(to_release)