From 5864447d5da8aa3ef16a9d3c299ce854c3ada6e8 Mon Sep 17 00:00:00 2001 From: Mechoid Date: Wed, 24 Jul 2019 01:44:59 -0700 Subject: [PATCH] Pipe Clamps, Shutoff Valves, and Leaking Tubes, oh my. (#6279) * Pipe Clamps, Shutoff Valves, and Leaking Tubes, oh my. * Fix processing fuckery. * Deal with Forever-Seals, and Infini-clamps. * Update clamp.dm --- code/ATMOSPHERICS/components/shutoff.dm | 50 +++ code/ATMOSPHERICS/datum_pipe_network.dm | 109 +++--- code/ATMOSPHERICS/datum_pipeline.dm | 337 +++++++++--------- code/ATMOSPHERICS/pipes/he_pipes.dm | 15 + code/ATMOSPHERICS/pipes/manifold.dm | 8 + code/ATMOSPHERICS/pipes/manifold4w.dm | 8 + code/ATMOSPHERICS/pipes/pipe_base.dm | 27 ++ code/ATMOSPHERICS/pipes/simple.dm | 18 +- code/ATMOSPHERICS/pipes/universal.dm | 2 +- code/game/machinery/atmoalter/clamp.dm | 154 ++++++++ code/game/machinery/pipe/pipe_recipes.dm | 1 + .../closets/secure/engineering.dm | 3 + html/changelogs/Mechoid - Leaky Pipes.yml | 37 ++ icons/atmos/clamp.dmi | Bin 0 -> 2295 bytes vorestation.dme | 2 + 15 files changed, 557 insertions(+), 214 deletions(-) create mode 100644 code/ATMOSPHERICS/components/shutoff.dm create mode 100644 code/game/machinery/atmoalter/clamp.dm create mode 100644 html/changelogs/Mechoid - Leaky Pipes.yml create mode 100644 icons/atmos/clamp.dmi diff --git a/code/ATMOSPHERICS/components/shutoff.dm b/code/ATMOSPHERICS/components/shutoff.dm new file mode 100644 index 0000000000..b1fb43baa1 --- /dev/null +++ b/code/ATMOSPHERICS/components/shutoff.dm @@ -0,0 +1,50 @@ +/obj/machinery/atmospherics/valve/shutoff + icon = 'icons/atmos/clamp.dmi' + icon_state = "map_vclamp0" + + name = "automatic shutoff valve" + desc = "An automatic valve with control circuitry and pipe integrity sensor, capable of automatically isolating damaged segments of the pipe network." + var/close_on_leaks = TRUE // If false it will be always open + level = 1 + connect_types = CONNECT_TYPE_SCRUBBER | CONNECT_TYPE_SUPPLY | CONNECT_TYPE_REGULAR + +/obj/machinery/atmospherics/valve/shutoff/update_icon() + icon_state = "vclamp[open]" + +/obj/machinery/atmospherics/valve/shutoff/examine(var/mob/user) + ..() + to_chat(user, "The automatic shutoff circuit is [close_on_leaks ? "enabled" : "disabled"].") + +/obj/machinery/atmospherics/valve/shutoff/Initialize() + . = ..() + open() + hide(1) + +/obj/machinery/atmospherics/valve/shutoff/attack_ai(mob/user as mob) + return src.attack_hand(user) + +/obj/machinery/atmospherics/valve/shutoff/attack_hand(var/mob/user) + src.add_fingerprint(usr) + update_icon(1) + close_on_leaks = !close_on_leaks + to_chat(user, "You [close_on_leaks ? "enable" : "disable"] the automatic shutoff circuit.") + return TRUE + +/obj/machinery/atmospherics/valve/shutoff/process() + ..() + + if (!network_node1 || !network_node2) + if(open) + close() + return + + if (!close_on_leaks) + if (!open) + open() + return + + if (network_node1.leaks.len || network_node2.leaks.len) + if (open) + close() + else if (!open) + open() diff --git a/code/ATMOSPHERICS/datum_pipe_network.dm b/code/ATMOSPHERICS/datum_pipe_network.dm index eb5e89276e..a611e30a89 100644 --- a/code/ATMOSPHERICS/datum_pipe_network.dm +++ b/code/ATMOSPHERICS/datum_pipe_network.dm @@ -8,76 +8,83 @@ var/global/list/datum/pipe_network/pipe_networks = list() // TODO - Move into SS var/list/datum/pipeline/line_members = list() //membership roster to go through for updates and what not + var/list/leaks = list() + var/update = 1 //var/datum/gas_mixture/air_transient = null - Destroy() - STOP_PROCESSING_PIPENET(src) - for(var/datum/pipeline/line_member in line_members) - line_member.network = null - for(var/obj/machinery/atmospherics/normal_member in normal_members) - normal_member.reassign_network(src, null) - gases.Cut() // Do not qdel the gases, we don't own them - return ..() +/datum/pipe_network/Destroy() + STOP_PROCESSING_PIPENET(src) + for(var/datum/pipeline/line_member in line_members) + line_member.network = null + for(var/obj/machinery/atmospherics/normal_member in normal_members) + normal_member.reassign_network(src, null) + gases.Cut() // Do not qdel the gases, we don't own them + leaks.Cut() + return ..() - process() - //Equalize gases amongst pipe if called for - if(update) - update = 0 - reconcile_air() //equalize_gases(gases) +/datum/pipe_network/process() + //Equalize gases amongst pipe if called for + if(update) + update = 0 + reconcile_air() //equalize_gases(gases) - //Give pipelines their process call for pressure checking and what not. Have to remove pressure checks for the time being as pipes dont radiate heat - Mport - //for(var/datum/pipeline/line_member in line_members) - // line_member.process() + listclearnulls(leaks) // Let's not have forever-seals. - proc/build_network(obj/machinery/atmospherics/start_normal, obj/machinery/atmospherics/reference) - //Purpose: Generate membership roster - //Notes: Assuming that members will add themselves to appropriate roster in network_expand() + //Give pipelines their process call for pressure checking and what not. Have to remove pressure checks for the time being as pipes dont radiate heat - Mport + //for(var/datum/pipeline/line_member in line_members) + // line_member.process() - if(!start_normal) - qdel(src) - return +/datum/pipe_network/proc/build_network(obj/machinery/atmospherics/start_normal, obj/machinery/atmospherics/reference) + //Purpose: Generate membership roster + //Notes: Assuming that members will add themselves to appropriate roster in network_expand() - start_normal.network_expand(src, reference) + if(!start_normal) + qdel(src) + return - update_network_gases() + start_normal.network_expand(src, reference) - if((normal_members.len>0)||(line_members.len>0)) - START_PROCESSING_PIPENET(src) - else - qdel(src) + update_network_gases() - proc/merge(datum/pipe_network/giver) - if(giver==src) return 0 + if((normal_members.len>0)||(line_members.len>0)) + START_PROCESSING_PIPENET(src) + else + qdel(src) - normal_members |= giver.normal_members +/datum/pipe_network/proc/merge(datum/pipe_network/giver) + if(giver==src) return 0 - line_members |= giver.line_members + normal_members |= giver.normal_members - for(var/obj/machinery/atmospherics/normal_member in giver.normal_members) - normal_member.reassign_network(giver, src) + line_members |= giver.line_members - for(var/datum/pipeline/line_member in giver.line_members) - line_member.network = src + leaks |= giver.leaks - update_network_gases() - return 1 + for(var/obj/machinery/atmospherics/normal_member in giver.normal_members) + normal_member.reassign_network(giver, src) - proc/update_network_gases() - //Go through membership roster and make sure gases is up to date + for(var/datum/pipeline/line_member in giver.line_members) + line_member.network = src - gases = list() - volume = 0 + update_network_gases() + return 1 - for(var/obj/machinery/atmospherics/normal_member in normal_members) - var/result = normal_member.return_network_air(src) - if(result) gases += result +/datum/pipe_network/proc/update_network_gases() + //Go through membership roster and make sure gases is up to date - for(var/datum/pipeline/line_member in line_members) - gases += line_member.air + gases = list() + volume = 0 - for(var/datum/gas_mixture/air in gases) - volume += air.volume + for(var/obj/machinery/atmospherics/normal_member in normal_members) + var/result = normal_member.return_network_air(src) + if(result) gases += result - proc/reconcile_air() - equalize_gases(gases) + for(var/datum/pipeline/line_member in line_members) + gases += line_member.air + + for(var/datum/gas_mixture/air in gases) + volume += air.volume + +/datum/pipe_network/proc/reconcile_air() + equalize_gases(gases) diff --git a/code/ATMOSPHERICS/datum_pipeline.dm b/code/ATMOSPHERICS/datum_pipeline.dm index fc47bca938..b53d722458 100644 --- a/code/ATMOSPHERICS/datum_pipeline.dm +++ b/code/ATMOSPHERICS/datum_pipeline.dm @@ -1,219 +1,234 @@ -datum/pipeline +/datum/pipeline var/datum/gas_mixture/air var/list/obj/machinery/atmospherics/pipe/members var/list/obj/machinery/atmospherics/pipe/edges //Used for building networks + // Nodes that are leaking. Used for A.S. Valves. + var/list/leaks = list() + var/datum/pipe_network/network var/alert_pressure = 0 - Destroy() - QDEL_NULL(network) +/datum/pipeline/Destroy() + QDEL_NULL(network) - if(air && air.volume) - temporarily_store_air() - for(var/obj/machinery/atmospherics/pipe/P in members) - P.parent = null - members = null - edges = null - . = ..() + if(air && air.volume) + temporarily_store_air() + for(var/obj/machinery/atmospherics/pipe/P in members) + P.parent = null + members = null + edges = null + leaks = null + . = ..() - process()//This use to be called called from the pipe networks - - //Check to see if pressure is within acceptable limits - var/pressure = air.return_pressure() - if(pressure > alert_pressure) - for(var/obj/machinery/atmospherics/pipe/member in members) - if(!member.check_pressure(pressure)) - break //Only delete 1 pipe per process - - proc/temporarily_store_air() - //Update individual gas_mixtures by volume ratio +/datum/pipeline/process()//This use to be called called from the pipe networks + //Check to see if pressure is within acceptable limits + var/pressure = air.return_pressure() + if(pressure > alert_pressure) for(var/obj/machinery/atmospherics/pipe/member in members) - member.air_temporary = new - member.air_temporary.copy_from(air) - member.air_temporary.volume = member.volume - member.air_temporary.multiply(member.volume / air.volume) + if(!member.check_pressure(pressure)) + break //Only delete 1 pipe per process - proc/build_pipeline(obj/machinery/atmospherics/pipe/base) +/datum/pipeline/proc/temporarily_store_air() + //Update individual gas_mixtures by volume ratio + + for(var/obj/machinery/atmospherics/pipe/member in members) + member.air_temporary = new + member.air_temporary.copy_from(air) + member.air_temporary.volume = member.volume + member.air_temporary.multiply(member.volume / air.volume) + +/datum/pipeline/proc/build_pipeline(obj/machinery/atmospherics/pipe/base) + air = new + + var/list/possible_expansions = list(base) + members = list(base) + edges = list() + + var/volume = base.volume + base.parent = src + alert_pressure = base.alert_pressure + + if(base.air_temporary) + air = base.air_temporary + base.air_temporary = null + else air = new - var/list/possible_expansions = list(base) - members = list(base) - edges = list() + if(base.leaking) + leaks |= base - var/volume = base.volume - base.parent = src - alert_pressure = base.alert_pressure + while(possible_expansions.len>0) + for(var/obj/machinery/atmospherics/pipe/borderline in possible_expansions) - if(base.air_temporary) - air = base.air_temporary - base.air_temporary = null - else - air = new + var/list/result = borderline.pipeline_expansion() + var/edge_check = result.len - while(possible_expansions.len>0) - for(var/obj/machinery/atmospherics/pipe/borderline in possible_expansions) + if(result.len>0) + for(var/obj/machinery/atmospherics/pipe/item in result) - var/list/result = borderline.pipeline_expansion() - var/edge_check = result.len + if(item.in_stasis) + continue - if(result.len>0) - for(var/obj/machinery/atmospherics/pipe/item in result) - if(!members.Find(item)) - members += item - possible_expansions += item + if(!members.Find(item)) + members += item + possible_expansions += item - volume += item.volume - item.parent = src + volume += item.volume + item.parent = src - alert_pressure = min(alert_pressure, item.alert_pressure) + alert_pressure = min(alert_pressure, item.alert_pressure) - if(item.air_temporary) - air.merge(item.air_temporary) + if(item.air_temporary) + air.merge(item.air_temporary) - edge_check-- + if(item.leaking) + leaks |= item - if(edge_check>0) - edges += borderline + edge_check-- - possible_expansions -= borderline + if(edge_check>0) + edges += borderline - air.volume = volume + possible_expansions -= borderline - proc/network_expand(datum/pipe_network/new_network, obj/machinery/atmospherics/pipe/reference) + air.volume = volume - if(new_network.line_members.Find(src)) - return 0 +/datum/pipeline/proc/network_expand(datum/pipe_network/new_network, obj/machinery/atmospherics/pipe/reference) - new_network.line_members += src + if(new_network.line_members.Find(src)) + return 0 - network = new_network + new_network.line_members += src - for(var/obj/machinery/atmospherics/pipe/edge in edges) - for(var/obj/machinery/atmospherics/result in edge.pipeline_expansion()) - if(!istype(result,/obj/machinery/atmospherics/pipe) && (result!=reference)) - result.network_expand(new_network, edge) + network = new_network + network.leaks |= leaks - return 1 + for(var/obj/machinery/atmospherics/pipe/edge in edges) + for(var/obj/machinery/atmospherics/result in edge.pipeline_expansion()) + if(!istype(result,/obj/machinery/atmospherics/pipe) && (result!=reference)) + result.network_expand(new_network, edge) - proc/return_network(obj/machinery/atmospherics/reference) - if(!network) - network = new /datum/pipe_network() - network.build_network(src, null) - //technically passing these parameters should not be allowed - //however pipe_network.build_network(..) and pipeline.network_extend(...) - // were setup to properly handle this case + return 1 - return network +/datum/pipeline/proc/return_network(obj/machinery/atmospherics/reference) + if(!network) + network = new /datum/pipe_network() + network.build_network(src, null) + //technically passing these parameters should not be allowed + //however pipe_network.build_network(..) and pipeline.network_extend(...) + // were setup to properly handle this case - proc/mingle_with_turf(turf/simulated/target, mingle_volume) - var/datum/gas_mixture/air_sample = air.remove_ratio(mingle_volume/air.volume) - air_sample.volume = mingle_volume + return network - if(istype(target) && target.zone) - //Have to consider preservation of group statuses - var/datum/gas_mixture/turf_copy = new - var/datum/gas_mixture/turf_original = new +/datum/pipeline/proc/mingle_with_turf(turf/simulated/target, mingle_volume) + var/datum/gas_mixture/air_sample = air.remove_ratio(mingle_volume/air.volume) + air_sample.volume = mingle_volume - turf_copy.copy_from(target.zone.air) - turf_copy.volume = target.zone.air.volume //Copy a good representation of the turf from parent group - turf_original.copy_from(turf_copy) + if(istype(target) && target.zone) + //Have to consider preservation of group statuses + var/datum/gas_mixture/turf_copy = new + var/datum/gas_mixture/turf_original = new - equalize_gases(list(air_sample, turf_copy)) - air.merge(air_sample) + turf_copy.copy_from(target.zone.air) + turf_copy.volume = target.zone.air.volume //Copy a good representation of the turf from parent group + turf_original.copy_from(turf_copy) + + equalize_gases(list(air_sample, turf_copy)) + air.merge(air_sample) - target.zone.air.remove(turf_original.total_moles) - target.zone.air.merge(turf_copy) + target.zone.air.remove(turf_original.total_moles) + target.zone.air.merge(turf_copy) - else - var/datum/gas_mixture/turf_air = target.return_air() + else + var/datum/gas_mixture/turf_air = target.return_air() - equalize_gases(list(air_sample, turf_air)) - air.merge(air_sample) - //turf_air already modified by equalize_gases() + equalize_gases(list(air_sample, turf_air)) + air.merge(air_sample) + //turf_air already modified by equalize_gases() - if(network) - network.update = 1 + if(network) + network.update = 1 - proc/temperature_interact(turf/target, share_volume, thermal_conductivity) - var/total_heat_capacity = air.heat_capacity() - var/partial_heat_capacity = total_heat_capacity*(share_volume/air.volume) +/datum/pipeline/proc/temperature_interact(turf/target, share_volume, thermal_conductivity) + var/total_heat_capacity = air.heat_capacity() + var/partial_heat_capacity = total_heat_capacity*(share_volume/air.volume) - if(istype(target, /turf/simulated)) - var/turf/simulated/modeled_location = target + if(istype(target, /turf/simulated)) + var/turf/simulated/modeled_location = target - if(modeled_location.blocks_air) + if(modeled_location.blocks_air) - if((modeled_location.heat_capacity>0) && (partial_heat_capacity>0)) - var/delta_temperature = air.temperature - modeled_location.temperature - - var/heat = thermal_conductivity*delta_temperature* \ - (partial_heat_capacity*modeled_location.heat_capacity/(partial_heat_capacity+modeled_location.heat_capacity)) - - air.temperature -= heat/total_heat_capacity - modeled_location.temperature += heat/modeled_location.heat_capacity - - else - var/delta_temperature = 0 - var/sharer_heat_capacity = 0 - - if(modeled_location.zone) - delta_temperature = (air.temperature - modeled_location.zone.air.temperature) - sharer_heat_capacity = modeled_location.zone.air.heat_capacity() - else - delta_temperature = (air.temperature - modeled_location.air.temperature) - sharer_heat_capacity = modeled_location.air.heat_capacity() - - var/self_temperature_delta = 0 - var/sharer_temperature_delta = 0 - - if((sharer_heat_capacity>0) && (partial_heat_capacity>0)) - var/heat = thermal_conductivity*delta_temperature* \ - (partial_heat_capacity*sharer_heat_capacity/(partial_heat_capacity+sharer_heat_capacity)) - - self_temperature_delta = -heat/total_heat_capacity - sharer_temperature_delta = heat/sharer_heat_capacity - else - return 1 - - air.temperature += self_temperature_delta - - if(modeled_location.zone) - modeled_location.zone.air.temperature += sharer_temperature_delta/modeled_location.zone.air.group_multiplier - else - modeled_location.air.temperature += sharer_temperature_delta - - - else - if((target.heat_capacity>0) && (partial_heat_capacity>0)) - var/delta_temperature = air.temperature - target.temperature + if((modeled_location.heat_capacity>0) && (partial_heat_capacity>0)) + var/delta_temperature = air.temperature - modeled_location.temperature var/heat = thermal_conductivity*delta_temperature* \ - (partial_heat_capacity*target.heat_capacity/(partial_heat_capacity+target.heat_capacity)) + (partial_heat_capacity*modeled_location.heat_capacity/(partial_heat_capacity+modeled_location.heat_capacity)) air.temperature -= heat/total_heat_capacity - if(network) - network.update = 1 + modeled_location.temperature += heat/modeled_location.heat_capacity - //surface must be the surface area in m^2 - proc/radiate_heat_to_space(surface, thermal_conductivity) - var/gas_density = air.total_moles/air.volume - thermal_conductivity *= min(gas_density / ( RADIATOR_OPTIMUM_PRESSURE/(R_IDEAL_GAS_EQUATION*GAS_CRITICAL_TEMPERATURE) ), 1) //mult by density ratio + else + var/delta_temperature = 0 + var/sharer_heat_capacity = 0 - // We only get heat from the star on the exposed surface area. - // If the HE pipes gain more energy from AVERAGE_SOLAR_RADIATION than they can radiate, then they have a net heat increase. - var/heat_gain = AVERAGE_SOLAR_RADIATION * (RADIATOR_EXPOSED_SURFACE_AREA_RATIO * surface) * thermal_conductivity + if(modeled_location.zone) + delta_temperature = (air.temperature - modeled_location.zone.air.temperature) + sharer_heat_capacity = modeled_location.zone.air.heat_capacity() + else + delta_temperature = (air.temperature - modeled_location.air.temperature) + sharer_heat_capacity = modeled_location.air.heat_capacity() - // Previously, the temperature would enter equilibrium at 26C or 294K. - // Only would happen if both sides (all 2 square meters of surface area) were exposed to sunlight. We now assume it aligned edge on. - // It currently should stabilise at 129.6K or -143.6C - heat_gain -= surface * STEFAN_BOLTZMANN_CONSTANT * thermal_conductivity * (air.temperature - COSMIC_RADIATION_TEMPERATURE) ** 4 + var/self_temperature_delta = 0 + var/sharer_temperature_delta = 0 - air.add_thermal_energy(heat_gain) - if(network) - network.update = 1 + if((sharer_heat_capacity>0) && (partial_heat_capacity>0)) + var/heat = thermal_conductivity*delta_temperature* \ + (partial_heat_capacity*sharer_heat_capacity/(partial_heat_capacity+sharer_heat_capacity)) + + self_temperature_delta = -heat/total_heat_capacity + sharer_temperature_delta = heat/sharer_heat_capacity + else + return 1 + + air.temperature += self_temperature_delta + + if(modeled_location.zone) + modeled_location.zone.air.temperature += sharer_temperature_delta/modeled_location.zone.air.group_multiplier + else + modeled_location.air.temperature += sharer_temperature_delta + + + else + if((target.heat_capacity>0) && (partial_heat_capacity>0)) + var/delta_temperature = air.temperature - target.temperature + + var/heat = thermal_conductivity*delta_temperature* \ + (partial_heat_capacity*target.heat_capacity/(partial_heat_capacity+target.heat_capacity)) + + air.temperature -= heat/total_heat_capacity + if(network) + network.update = 1 + +//surface must be the surface area in m^2 +/datum/pipeline/proc/radiate_heat_to_space(surface, thermal_conductivity) + var/gas_density = air.total_moles/air.volume + thermal_conductivity *= min(gas_density / ( RADIATOR_OPTIMUM_PRESSURE/(R_IDEAL_GAS_EQUATION*GAS_CRITICAL_TEMPERATURE) ), 1) //mult by density ratio + + // We only get heat from the star on the exposed surface area. + // If the HE pipes gain more energy from AVERAGE_SOLAR_RADIATION than they can radiate, then they have a net heat increase. + var/heat_gain = AVERAGE_SOLAR_RADIATION * (RADIATOR_EXPOSED_SURFACE_AREA_RATIO * surface) * thermal_conductivity + + // Previously, the temperature would enter equilibrium at 26C or 294K. + // Only would happen if both sides (all 2 square meters of surface area) were exposed to sunlight. We now assume it aligned edge on. + // It currently should stabilise at 129.6K or -143.6C + heat_gain -= surface * STEFAN_BOLTZMANN_CONSTANT * thermal_conductivity * (air.temperature - COSMIC_RADIATION_TEMPERATURE) ** 4 + + air.add_thermal_energy(heat_gain) + if(network) + network.update = 1 diff --git a/code/ATMOSPHERICS/pipes/he_pipes.dm b/code/ATMOSPHERICS/pipes/he_pipes.dm index 2d77e3a4ca..1b283e3ba4 100644 --- a/code/ATMOSPHERICS/pipes/he_pipes.dm +++ b/code/ATMOSPHERICS/pipes/he_pipes.dm @@ -67,8 +67,22 @@ return update_icon() + handle_leaking() return +/obj/machinery/atmospherics/pipe/simple/heat_exchanging/set_leaking(var/new_leaking) // They already process, no need for manual processing toggles. + if(new_leaking && !leaking) + leaking = TRUE + if(parent) + parent.leaks |= src + if(parent.network) + parent.network.leaks |= src + else if (!new_leaking && leaking) + leaking = FALSE + if(parent) + parent.leaks -= src + if(parent.network) + parent.network.leaks -= src /obj/machinery/atmospherics/pipe/simple/heat_exchanging/process() if(!parent) @@ -180,4 +194,5 @@ return update_icon() + handle_leaking() return diff --git a/code/ATMOSPHERICS/pipes/manifold.dm b/code/ATMOSPHERICS/pipes/manifold.dm index 524d420d39..1af1eaa767 100644 --- a/code/ATMOSPHERICS/pipes/manifold.dm +++ b/code/ATMOSPHERICS/pipes/manifold.dm @@ -68,9 +68,16 @@ node3 = null update_icon() + handle_leaking() ..() +/obj/machinery/atmospherics/pipe/manifold/handle_leaking() + if(node1 && node2 && node3) + set_leaking(FALSE) + else + set_leaking(TRUE) + /obj/machinery/atmospherics/pipe/manifold/change_color(var/new_color) ..() //for updating connected atmos device pipes (i.e. vents, manifolds, etc) @@ -154,6 +161,7 @@ var/turf/T = get_turf(src) if(level == 1 && !T.is_plating()) hide(1) update_icon() + handle_leaking() /obj/machinery/atmospherics/pipe/manifold/visible icon_state = "map" diff --git a/code/ATMOSPHERICS/pipes/manifold4w.dm b/code/ATMOSPHERICS/pipes/manifold4w.dm index 0cc022423b..500088fec6 100644 --- a/code/ATMOSPHERICS/pipes/manifold4w.dm +++ b/code/ATMOSPHERICS/pipes/manifold4w.dm @@ -66,9 +66,16 @@ node4 = null update_icon() + handle_leaking() ..() +/obj/machinery/atmospherics/pipe/manifold4w/handle_leaking() + if(node1 && node2 && node3 && node4) + set_leaking(FALSE) + else + set_leaking(TRUE) + /obj/machinery/atmospherics/pipe/manifold4w/change_color(var/new_color) ..() //for updating connected atmos device pipes (i.e. vents, manifolds, etc) @@ -156,6 +163,7 @@ var/turf/T = get_turf(src) if(level == 1 && !T.is_plating()) hide(1) update_icon() + handle_leaking() /obj/machinery/atmospherics/pipe/manifold4w/visible icon_state = "map_4way" diff --git a/code/ATMOSPHERICS/pipes/pipe_base.dm b/code/ATMOSPHERICS/pipes/pipe_base.dm index a035857e54..d1bb92fac7 100644 --- a/code/ATMOSPHERICS/pipes/pipe_base.dm +++ b/code/ATMOSPHERICS/pipes/pipe_base.dm @@ -6,6 +6,7 @@ var/datum/gas_mixture/air_temporary // used when reconstructing a pipeline that broke var/datum/pipeline/parent var/volume = 0 + var/leaking = FALSE // Do not set directly, use set_leaking(TRUE/FALSE) layer = PIPES_LAYER use_power = 0 @@ -13,6 +14,7 @@ pipe_flags = 0 // Does not have PIPING_DEFAULT_LAYER_ONLY flag. var/alert_pressure = 80*ONE_ATMOSPHERE + var/in_stasis = FALSE //minimum pressure before check_pressure(...) should be called can_buckle = 1 @@ -30,6 +32,31 @@ /obj/machinery/atmospherics/pipe/hides_under_flooring() return level != 2 +/obj/machinery/atmospherics/pipe/proc/set_leaking(var/new_leaking) + if(new_leaking && !leaking) + if(!speed_process) + START_MACHINE_PROCESSING(src) + else + START_PROCESSING(SSfastprocess, src) + leaking = TRUE + if(parent) + parent.leaks |= src + if(parent.network) + parent.network.leaks |= src + else if (!new_leaking && leaking) + if(!speed_process) + STOP_MACHINE_PROCESSING(src) + else + STOP_PROCESSING(SSfastprocess, src) + leaking = FALSE + if(parent) + parent.leaks -= src + if(parent.network) + parent.network.leaks -= src + +/obj/machinery/atmospherics/pipe/proc/handle_leaking() // Used specifically to update leaking status on different pipes. + return + /obj/machinery/atmospherics/pipe/proc/pipeline_expansion() return null diff --git a/code/ATMOSPHERICS/pipes/simple.dm b/code/ATMOSPHERICS/pipes/simple.dm index 6aee81807e..eb980c3b47 100644 --- a/code/ATMOSPHERICS/pipes/simple.dm +++ b/code/ATMOSPHERICS/pipes/simple.dm @@ -1,6 +1,6 @@ // // Simple Pipes - Just a tube, maybe bent -// +// /obj/machinery/atmospherics/pipe/simple icon = 'icons/atmos/pipes.dmi' icon_state = "" @@ -34,6 +34,14 @@ icon = null alpha = 255 +/obj/machinery/atmospherics/pipe/simple/process() + if(!parent) + ..() + else if(leaking) + parent.mingle_with_turf(loc, volume) + else + . = PROCESS_KILL + /obj/machinery/atmospherics/pipe/simple/check_pressure(pressure) var/datum/gas_mixture/environment = loc.return_air() @@ -147,6 +155,7 @@ var/turf/T = loc if(level == 1 && !T.is_plating()) hide(1) update_icon() + handle_leaking() /obj/machinery/atmospherics/pipe/simple/disconnect(obj/machinery/atmospherics/reference) if(reference == node1) @@ -160,9 +169,16 @@ node2 = null update_icon() + handle_leaking() return null +/obj/machinery/atmospherics/pipe/simple/handle_leaking() + if(node1 && node2) + set_leaking(FALSE) + else + set_leaking(TRUE) + /obj/machinery/atmospherics/pipe/simple/visible icon_state = "intact" level = 2 diff --git a/code/ATMOSPHERICS/pipes/universal.dm b/code/ATMOSPHERICS/pipes/universal.dm index 2d8bd09dff..00d2746947 100644 --- a/code/ATMOSPHERICS/pipes/universal.dm +++ b/code/ATMOSPHERICS/pipes/universal.dm @@ -48,7 +48,7 @@ construction_type = /obj/item/pipe/binary pipe_state = "universal" -/obj/machinery/atmospherics/pipe/simple/hidden/universal/update_icon(var/safety = 0) +/obj/machinery/atmospherics/pipe/simple/hidden/universal/update_icon(var/safety = 0) // Doesn't leak. It's a special pipe. if(!check_icon_cache()) return diff --git a/code/game/machinery/atmoalter/clamp.dm b/code/game/machinery/atmoalter/clamp.dm new file mode 100644 index 0000000000..318145c229 --- /dev/null +++ b/code/game/machinery/atmoalter/clamp.dm @@ -0,0 +1,154 @@ +//Good luck. --BlueNexus + +//Static version of the clamp +/obj/machinery/clamp + name = "stasis clamp" + desc = "A magnetic clamp which can halt the flow of gas in a pipe, via a localised stasis field." + description_info = "Click-dragging this to yourself while adjacent will attempt to remove it from the pipe." + icon = 'icons/atmos/clamp.dmi' + icon_state = "pclamp0" + anchored = 1.0 + var/obj/machinery/atmospherics/pipe/simple/target = null + var/open = 1 + + var/datum/pipe_network/network_node1 + var/datum/pipe_network/network_node2 + +/obj/machinery/clamp/New(loc, var/obj/machinery/atmospherics/pipe/simple/to_attach = null) + ..() + if(istype(to_attach)) + target = to_attach + else + target = locate(/obj/machinery/atmospherics/pipe/simple) in loc + if(target) + update_networks() + dir = target.dir + return 1 + +/obj/machinery/clamp/proc/update_networks() + if(!target) + return + else + var/obj/machinery/atmospherics/pipe/node1 = target.node1 + var/obj/machinery/atmospherics/pipe/node2 = target.node2 + if(istype(node1)) + var/datum/pipeline/P1 = node1.parent + network_node1 = P1.network + if(istype(node2)) + var/datum/pipeline/P2 = node2.parent + network_node2 = P2.network + +/obj/machinery/clamp/attack_hand(var/mob/user) + if(!target) + return FALSE + if(!open) + open() + else + close() + to_chat(user, "You turn [open ? "off" : "on"] \the [src]") + return TRUE + +/obj/machinery/clamp/Destroy() + if(!open) + spawn(-1) open() + . = ..() + +/obj/machinery/clamp/proc/open() + if(open || !target) + return 0 + + target.build_network() + + + if(network_node1&&network_node2) + network_node1.merge(network_node2) + network_node2 = network_node1 + + if(network_node1) + network_node1.update = 1 + else if(network_node2) + network_node2.update = 1 + + update_networks() + + open = 1 + icon_state = "pclamp0" + target.in_stasis = 0 + return 1 + +/obj/machinery/clamp/proc/close() + if(!open) + return 0 + + qdel(target.parent) + + if(network_node1) + qdel(network_node1) + if(network_node2) + qdel(network_node2) + + var/obj/machinery/atmospherics/pipe/node1 = null + var/obj/machinery/atmospherics/pipe/node2 = null + + if(target.node1) + target.node1.build_network() + node1 = target.node1 + if(target.node2) + target.node2.build_network() + node2 = target.node2 + if(istype(node1) && node1.parent) + var/datum/pipeline/P1 = node1.parent + P1.build_pipeline(node1) + qdel(P1) + if(istype(node2) && node2.parent) + var/datum/pipeline/P2 = node2.parent + P2.build_pipeline(node2) + qdel(P2) +// P1.build_network() +// P2.build_network() + + open = 0 + icon_state = "pclamp1" + target.in_stasis = 1 + + return 1 + +/obj/machinery/clamp/MouseDrop(obj/over_object as obj) + if(!usr) + return + + if(open && over_object == usr && Adjacent(usr)) + to_chat(usr, "You begin to remove \the [src]...") + if (do_after(usr, 30, src)) + to_chat(usr, "You have removed \the [src].") + var/obj/item/clamp/C = new/obj/item/clamp(src.loc) + C.forceMove(usr.loc) + if(ishuman(usr)) + usr.put_in_hands(C) + qdel(src) + return + else + to_chat(usr, "You can't remove \the [src] while it's active!") + +/obj/item/clamp + name = "stasis clamp" + desc = "A magnetic clamp which can halt the flow of gas in a pipe, via a localised stasis field." + icon = 'icons/atmos/clamp.dmi' + icon_state = "pclamp0" + origin_tech = list(TECH_ENGINEERING = 4, TECH_MAGNET = 4) + +/obj/item/clamp/afterattack(var/atom/A, mob/user as mob, proximity) + if(!proximity) + return + + if (istype(A, /obj/machinery/atmospherics/pipe/simple)) + to_chat(user, "You begin to attach \the [src] to \the [A]...") + var/C = locate(/obj/machinery/clamp) in get_turf(A) + if (do_after(user, 30, src) && !C) + if(!user.unEquip(src)) + return + to_chat(user, "You have attached \the [src] to \the [A].") + new/obj/machinery/clamp(A.loc, A) + qdel(src) + if(C) + to_chat(user, "\The [C] is already attached to the pipe at this location!") diff --git a/code/game/machinery/pipe/pipe_recipes.dm b/code/game/machinery/pipe/pipe_recipes.dm index f0ae483055..24661951e9 100644 --- a/code/game/machinery/pipe/pipe_recipes.dm +++ b/code/game/machinery/pipe/pipe_recipes.dm @@ -27,6 +27,7 @@ var/global/list/atmos_pipe_recipes = null new /datum/pipe_recipe/pipe("Gas Pump", /obj/machinery/atmospherics/binary/pump), new /datum/pipe_recipe/pipe("Pressure Regulator", /obj/machinery/atmospherics/binary/passive_gate), new /datum/pipe_recipe/pipe("High Power Gas Pump",/obj/machinery/atmospherics/binary/pump/high_power), + new /datum/pipe_recipe/pipe("Automatic Shutoff Valve",/obj/machinery/atmospherics/valve/shutoff), new /datum/pipe_recipe/pipe("Scrubber", /obj/machinery/atmospherics/unary/vent_scrubber), new /datum/pipe_recipe/meter("Meter"), new /datum/pipe_recipe/pipe("Gas Filter", /obj/machinery/atmospherics/trinary/atmos_filter), diff --git a/code/game/objects/structures/crates_lockers/closets/secure/engineering.dm b/code/game/objects/structures/crates_lockers/closets/secure/engineering.dm index 8380c059ec..5be0f76db7 100644 --- a/code/game/objects/structures/crates_lockers/closets/secure/engineering.dm +++ b/code/game/objects/structures/crates_lockers/closets/secure/engineering.dm @@ -11,6 +11,8 @@ starts_with = list( /obj/item/clothing/accessory/storage/brown_vest, /obj/item/blueprints, + /obj/item/clamp, + /obj/item/clamp, /obj/item/clothing/under/rank/chief_engineer, /obj/item/clothing/under/rank/chief_engineer/skirt, /obj/item/clothing/head/hardhat/white, @@ -125,6 +127,7 @@ /obj/item/clothing/suit/fire/firefighter, /obj/item/device/flashlight, /obj/item/weapon/extinguisher, + /obj/item/clamp, /obj/item/device/radio/headset/headset_eng, /obj/item/device/radio/headset/headset_eng/alt, /obj/item/clothing/suit/storage/hazardvest, diff --git a/html/changelogs/Mechoid - Leaky Pipes.yml b/html/changelogs/Mechoid - Leaky Pipes.yml new file mode 100644 index 0000000000..62c477edb7 --- /dev/null +++ b/html/changelogs/Mechoid - Leaky Pipes.yml @@ -0,0 +1,37 @@ +################################ +# Example Changelog File +# +# Note: This file, and files beginning with ".", and files that don't end in ".yml" will not be read. If you change this file, you will look really dumb. +# +# Your changelog will be merged with a master changelog. (New stuff added only, and only on the date entry for the day it was merged.) +# When it is, any changes listed below will disappear. +# +# Valid Prefixes: +# bugfix +# wip (For works in progress) +# tweak +# soundadd +# sounddel +# rscadd (general adding of nice things) +# rscdel (general deleting of nice things) +# imageadd +# imagedel +# maptweak +# spellcheck (typo fixes) +# experiment +################################# + +# Your name. +author: Mechoid + +# Optional: Remove this file after generating master changelog. Useful for PR changelogs that won't get used again. +delete-after: True + +# Any changes you've made. See valid prefix list above. +# INDENT WITH TWO SPACES. NOT TABS. SPACES. +# SCREW THIS UP AND IT WON'T WORK. +# Also, all entries are changed into a single [] after a master changelog generation. Just remove the brackets when you add new entries. +# Please surround your changes in double quotes ("), as certain characters otherwise screws up compiling. The quotes will not show up in the changelog. +changes: + - rscadd: "Pipes will now leak if their ends are unsealed." + - rscadd: "Pipe clamps now exist." diff --git a/icons/atmos/clamp.dmi b/icons/atmos/clamp.dmi new file mode 100644 index 0000000000000000000000000000000000000000..ed22b930594323fbece5de2722b13b2132080cc6 GIT binary patch literal 2295 zcmai0dpuO@8lKo8L}j-lx0xeJi8QB1iZQ6sCWE0&HshLIWJ=kDS{eOxWMh6(w%luW zh+P@PjLXu+r5iSylB8%%W@0joF|1kZEY9il=lNrO>-)an^M2p^KF{;6-%9uLB(K)o zsENT~Rv&b8^}%3PV3!^ZRSX7W78t}qUnyQ5zWdwS+Vu4Fs8niJR#sqOpplUgjYi{e zIO*x>rlzL#_4TQ#sdaUAX=!O2H*RcbXy9_W1_lN}K|zg;jlRCVJRZ-++PVjW0eUcL zOy-Lh7-M7O(9qERPEL7wd9AIj&CShTUS5WVhBzD!1VQvV{0ju1kKm6X7(9Y5J>$@t<+gB>g zgU#ZcG=^8$Ib7Ob#bH*JXxaH-GUj3e#`n>BF&Oo82VF_N35AnI0R64Ke*4fNd)Mu% z!{hTGry4i($`Lh&yZ6Vig+glz&!S8y28wU!^*Oo3dT_qvLjA(3eNpNYN2`^%TcgkC zkCv5B=^CuRYRF}Bih0Cv-*@kyuFsVXC~*zj7;QRSC&+mCpWXp~{oodwV1H6|(- zU5>T4%$txCMKEjdF@ei;>WTW1R}Xr?S$RQpsj@S^hM8mX1X(+;Wp7196hFJKjBx0Q zP-l}R^uYW6cMn$yZq_qzzRv}ZzP8(@G5c9rrFA_+#BArFyFqyXbiA^(nk)g{GN&9x z1Cg)EB55{J3@uDHxKTQhx|KyG;W0|l_|(+x%?LL!qUZ={l-(Im7~94!A1e`5g=yWD zns#g2%gTo)lxJg!%DBTRKT{QwWP zT*pO@Yb|_UIPLZcezo#mouAi$%xthr>5{y`XKfSk5^M)X3vK%9{TE#h%+RImJ-sNU zFUF}gDsIb^j-+ZnTVgWS`rclgLmbc*YaSoMMn$m{O4)Wx{6~KSbF5+n);hDOHVTQw zo(n4IY!&X(8t1MZmWIhPoim(Yo%NBLm~NV&fyX0PgXa$G{5k7PbeuJos$CvnZSgA0DB#j=;3|*E4qE>v)lc=H z?ngu$GH;0&C1*CDz>0B@yp7)HJsk;g?*_(ej!83{hFVk0b2?uJ_uNx!Qr6yr zI0^P;^ZumL$hUj-xxjF9ft*mb6n$J0{0Y7sGQKJc)%A93DhX=oH*ADHcjAg5C0W{- zyh~=BL*{Ty9w7$v?uinAA){JpTah$3cs}f_#kcx2aH@|Jou9om4JiU^<}MFZsn`U+ za_V!LkAkDP-wUK)1lduOnXij@ibadAI@oxI0B_3+krLmFV@?2K=nxr@TnnfN-k3FU-YxF*}B&g@(Zk=^Pdbvw+j|23W^G8RLb^+dHe;vu=m!;=4Mg9@aL%2WAS zsa}kv2m*wRPUyzR;ofHep>4^5OM8 zcg7FTkObm>RfW3*E1+&d3rIw8b~;zi8HG+Viox{Zk$r$(6QI(+x&!{sad@yl#jSmc zQ3R&HAF&7YT2P|K8S_=$M0W_f=Lz4@o3&0tOe5e*ZYcs~BX0qH;FU=<7EKaBlN_8P z4*Y^0et5FBo>Kdi;tn6^#h*+J8;YLnkCu+j<;_}kQLE9oQ2BK%e5ZxHbe148_zdZh z5RkPX-e(>PgaFhTl7D_l9#ky|uAvavQ3$p;Q$&XFi>&A0Wj=p{kWVN6xP|BicPv0P@1d77gqp?IiW|qGN?h7H^1-z- zk0kYmL(`cl%Qo&D)m1YQW|+sq{tUQGO5iUd^K$H@B6PY0`7`;-BkAMcU99Csd`!i< z@D#{z6{qze5$PxVAD>nrl6@D!6Ozo<%9&kK-qq^MUkUGkiXq-r8PYPOK&^07-q1bb zDp{u7bUbW8QSf>+O7#d#Mq7)~enib0pb>h$0T7jLL^KEv*@brr?>i4HuG#7>A@K=( zg<%w+ExK@HDxXRG-zP!CK%cMY!-qANJ-JCS11@Y%n*ptqIKJ4E53@pqfc=gy5Jf$$ z2${IJ6R6!&CV*^7*=?pfzwNfQ%@Fm)r~g$?Tn_KB2f`+^Yl>s;&;6${C6I2?Z$G8| U&@bbo{|lId`#oLnItMfV4MT*uzW@LL literal 0 HcmV?d00001 diff --git a/vorestation.dme b/vorestation.dme index 9452c0963d..179bbd6868 100644 --- a/vorestation.dme +++ b/vorestation.dme @@ -157,6 +157,7 @@ #include "code\ATMOSPHERICS\datum_pipeline.dm" #include "code\ATMOSPHERICS\mainspipe.dm" #include "code\ATMOSPHERICS\components\portables_connector.dm" +#include "code\ATMOSPHERICS\components\shutoff.dm" #include "code\ATMOSPHERICS\components\tvalve.dm" #include "code\ATMOSPHERICS\components\valve.dm" #include "code\ATMOSPHERICS\components\binary_devices\algae_generator_vr.dm" @@ -761,6 +762,7 @@ #include "code\game\machinery\atmoalter\area_atmos_computer.dm" #include "code\game\machinery\atmoalter\area_atmos_computer_vr.dm" #include "code\game\machinery\atmoalter\canister.dm" +#include "code\game\machinery\atmoalter\clamp.dm" #include "code\game\machinery\atmoalter\meter.dm" #include "code\game\machinery\atmoalter\portable_atmospherics.dm" #include "code\game\machinery\atmoalter\pump.dm"