From 54f74233aa9324cd3d30912f2a825e97a1aab1b7 Mon Sep 17 00:00:00 2001 From: SkyratBot <59378654+SkyratBot@users.noreply.github.com> Date: Wed, 26 May 2021 02:00:43 +0200 Subject: [PATCH] [MIRROR] Makes canister leaking and blowing up use a component and element respectively (#5958) * Makes canister leaking and blowing up use a component and element respectively (#59075) I want to use this behavior on some other things so into a component and element it goes. Gas leaking is handled by a component so it can process whereas the object breaking and causing an explosion is handled by an element. Some minor changes were made so canisters were more consistent in leaking. * Makes canister leaking and blowing up use a component and element respectively Co-authored-by: Emmett Gaines --- code/__DEFINES/dcs/signals.dm | 6 ++ code/datums/components/gas_leaker.dm | 79 +++++++++++++++++++ code/datums/elements/volatile_gas_storage.dm | 50 ++++++++++++ code/game/machinery/_machinery.dm | 1 - code/game/objects/obj_defense.dm | 3 +- code/game/objects/structures/aliens.dm | 1 + code/game/objects/structures/barsigns.dm | 1 + .../structures/crates_lockers/closets.dm | 1 + code/game/objects/structures/displaycase.dm | 2 + code/game/objects/structures/extinguisher.dm | 1 + code/game/objects/structures/fireaxe.dm | 1 + code/game/objects/structures/grille.dm | 1 + code/game/objects/structures/mirror.dm | 1 + .../atmospherics/gasmixtures/gas_mixture.dm | 4 +- .../machinery/portable/canister.dm | 36 ++------- code/modules/clothing/clothing.dm | 1 + tgstation.dme | 2 + 17 files changed, 157 insertions(+), 34 deletions(-) create mode 100644 code/datums/components/gas_leaker.dm create mode 100644 code/datums/elements/volatile_gas_storage.dm diff --git a/code/__DEFINES/dcs/signals.dm b/code/__DEFINES/dcs/signals.dm index 1b7b0a414ba..0b141f27550 100644 --- a/code/__DEFINES/dcs/signals.dm +++ b/code/__DEFINES/dcs/signals.dm @@ -703,6 +703,8 @@ #define COMSIG_OBJ_HIDE "obj_hide" /// from /obj/item/toy/crayon/spraycan/afterattack: (color_is_dark) #define COMSIG_OBJ_PAINTED "obj_painted" +/// from /obj/proc/obj_break: () +#define COMSIG_OBJ_BREAK "obj_break" // /obj/machinery signals @@ -718,6 +720,10 @@ #define COMSIG_MACHINERY_DESTRUCTIVE_SCAN "machinery_destructive_scan" ///from /obj/machinery/computer/arcade/prizevend(mob/user, prizes = 1) #define COMSIG_ARCADE_PRIZEVEND "arcade_prizevend" +///from /datum/controller/subsystem/air/proc/start_processing_machine: () +#define COMSIG_MACHINERY_START_PROCESSING_AIR "start_processing_air" +///from /datum/controller/subsystem/air/proc/stop_processing_machine: () +#define COMSIG_MACHINERY_STOP_PROCESSING_AIR "stop_processing_air" ///from /obj/machinery/can_interact(mob/user): Called on user when attempting to interact with a machine (obj/machinery/machine) #define COMSIG_TRY_USE_MACHINE "try_use_machine" diff --git a/code/datums/components/gas_leaker.dm b/code/datums/components/gas_leaker.dm new file mode 100644 index 00000000000..6a54fbca30a --- /dev/null +++ b/code/datums/components/gas_leaker.dm @@ -0,0 +1,79 @@ +#define PROCESS_COMPONENT "component" +#define PROCESS_MACHINE "machine" +#define PROCESS_OBJ "obj" + +/// A component to leak gas over time from damaged objects with gas storage +/datum/component/gas_leaker + /// Keeps track of what type we were attached to so we don't need to istype every process + var/process_type + + /// The percent of max integrity that we start leaking. From 0 to 1 + var/integrity_leak_percent + + /// The rate at which gas leaks, you probably want this *very* low. From 0 to 1 + var/leak_rate + +/datum/component/gas_leaker/Initialize(integrity_leak_percent=0.9, leak_rate=1) + . = ..() + if(istype(parent, /obj/machinery/atmospherics/components)) + process_type = PROCESS_COMPONENT + else if(istype(parent, /obj/machinery)) + process_type = PROCESS_MACHINE + else if(isobj(parent)) + process_type = PROCESS_OBJ + else + return COMPONENT_INCOMPATIBLE + + src.integrity_leak_percent = integrity_leak_percent + src.leak_rate = leak_rate + +/datum/component/gas_leaker/RegisterWithParent() + . = ..() + RegisterSignal(parent, COMSIG_OBJ_TAKE_DAMAGE, .proc/start_processing) + +/datum/component/gas_leaker/UnregisterFromParent() + . = ..() + UnregisterSignal(parent, COMSIG_OBJ_TAKE_DAMAGE) + +/datum/component/gas_leaker/proc/process_atmos() + . = PROCESS_KILL + switch(process_type) + if(PROCESS_OBJ) + . = process_obj(parent) + if(PROCESS_MACHINE) + . = process_machine(parent) + if(PROCESS_COMPONENT) + . = process_component(parent) + +/datum/component/gas_leaker/proc/start_processing() + SIGNAL_HANDLER + // Hello fellow atmospherics machines, I too am definitely an atmos machine like you! + // This component needs to tick at the same rate as the atmos system + SSair.atmos_machinery += src + +/datum/component/gas_leaker/proc/process_obj(obj/master, list/airs=list()) + airs += master.return_air() + return process_leak(master, airs) + +/datum/component/gas_leaker/proc/process_machine(obj/machinery/master, list/airs=list()) + if(master.machine_stat & BROKEN) + return PROCESS_KILL + return process_obj(master, airs) + +/datum/component/gas_leaker/proc/process_component(obj/machinery/atmospherics/components/master, list/airs=list()) + airs += master.airs + return process_machine(master, airs) + +/datum/component/gas_leaker/proc/process_leak(obj/master, list/airs) + if(master.obj_integrity > master.max_integrity * integrity_leak_percent) + return PROCESS_KILL + var/turf/location = get_turf(master) + var/true_rate = (1 - (master.obj_integrity / master.max_integrity)) * leak_rate + for(var/datum/gas_mixture/mix as anything in airs) + var/pressure = mix.return_pressure() + if(mix.release_gas_to(location.return_air(), pressure, true_rate)) + location.air_update_turf(FALSE, FALSE) + +#undef PROCESS_OBJ +#undef PROCESS_MACHINE +#undef PROCESS_COMPONENT diff --git a/code/datums/elements/volatile_gas_storage.dm b/code/datums/elements/volatile_gas_storage.dm new file mode 100644 index 00000000000..ef5b3b87504 --- /dev/null +++ b/code/datums/elements/volatile_gas_storage.dm @@ -0,0 +1,50 @@ +/// An element to make an /obj explode based on gas pressure when broken +/datum/element/volatile_gas_storage + element_flags = ELEMENT_BESPOKE + id_arg_index = 2 + + /// The minimum pressure of the gas storage to consider an explosion when broken + var/minimum_explosive_pressure + /// The max pressure to stop scaling the explosion at, you can go higher but the explosion range will stay at max + var/max_explosive_pressure + /// The max explsion range at the max pressure + var/max_explosive_force + +/datum/element/volatile_gas_storage/Attach(datum/target, minimum_explosive_pressure=5000, max_explosive_pressure=100000, max_explosive_force=9) + . = ..() + if(istype(target, /obj/machinery/atmospherics/components)) + RegisterSignal(target, COMSIG_OBJ_BREAK, .proc/AtmosComponentBreak) + else if(isobj(target)) + RegisterSignal(target, COMSIG_OBJ_BREAK, .proc/ObjBreak) + else + return ELEMENT_INCOMPATIBLE + + src.minimum_explosive_pressure = minimum_explosive_pressure + src.max_explosive_pressure = max_explosive_pressure + src.max_explosive_force = max_explosive_force + +/datum/element/volatile_gas_storage/Detach(datum/source, ...) + . = ..() + UnregisterSignal(source, COMSIG_OBJ_BREAK) + +/datum/element/volatile_gas_storage/proc/Break(atom/origin, datum/gas_mixture/released_gas) + var/expelled_pressure = min(released_gas?.return_pressure(), max_explosive_pressure) + + if(expelled_pressure < minimum_explosive_pressure) + return + + var/explosive_force = CEILING((expelled_pressure / max_explosive_pressure) * max_explosive_force , 1) + // This is supposed to represent only shrapnel and no fire + // Maybe one day we'll get something a bit better + explosion(get_turf(origin), light_impact_range=explosive_force, smoke=FALSE) + +/datum/element/volatile_gas_storage/proc/AtmosComponentBreak(obj/machinery/atmospherics/components/owner) + SIGNAL_HANDLER + for(var/datum/gas_mixture/gas_contents as anything in owner.airs) + if(!gas_contents) + continue + Break(owner, gas_contents) + +/datum/element/volatile_gas_storage/proc/ObjBreak(obj/owner) + SIGNAL_HANDLER + Break(owner, owner.return_air()) diff --git a/code/game/machinery/_machinery.dm b/code/game/machinery/_machinery.dm index f5c16b40763..26ceb988acd 100644 --- a/code/game/machinery/_machinery.dm +++ b/code/game/machinery/_machinery.dm @@ -573,7 +573,6 @@ /obj/machinery/obj_break(damage_flag) - SHOULD_CALL_PARENT(TRUE) . = ..() if(!(machine_stat & BROKEN) && !(flags_1 & NODECONSTRUCT_1)) set_machine_stat(machine_stat | BROKEN) diff --git a/code/game/objects/obj_defense.dm b/code/game/objects/obj_defense.dm index eb53491ab78..4f051850269 100644 --- a/code/game/objects/obj_defense.dm +++ b/code/game/objects/obj_defense.dm @@ -225,7 +225,8 @@ GLOBAL_DATUM_INIT(acid_overlay, /mutable_appearance, mutable_appearance('icons/e ///called after the obj takes damage and integrity is below integrity_failure level /obj/proc/obj_break(damage_flag) - return + SHOULD_CALL_PARENT(TRUE) + SEND_SIGNAL(src, COMSIG_OBJ_BREAK) ///what happens when the obj's integrity reaches zero. /obj/proc/obj_destruction(damage_flag) diff --git a/code/game/objects/structures/aliens.dm b/code/game/objects/structures/aliens.dm index 49bd02ff905..b21c0a2f0a1 100644 --- a/code/game/objects/structures/aliens.dm +++ b/code/game/objects/structures/aliens.dm @@ -362,6 +362,7 @@ take_damage(5, BURN, 0, 0) /obj/structure/alien/egg/obj_break(damage_flag) + . = ..() if(!(flags_1 & NODECONSTRUCT_1)) if(status != BURST) Burst(kill=TRUE) diff --git a/code/game/objects/structures/barsigns.dm b/code/game/objects/structures/barsigns.dm index 73f2b074f8a..f02166431ec 100644 --- a/code/game/objects/structures/barsigns.dm +++ b/code/game/objects/structures/barsigns.dm @@ -46,6 +46,7 @@ return set_sign(new_sign) /obj/structure/sign/barsign/obj_break(damage_flag) + . = ..() if(!broken && !(flags_1 & NODECONSTRUCT_1)) broken = TRUE diff --git a/code/game/objects/structures/crates_lockers/closets.dm b/code/game/objects/structures/crates_lockers/closets.dm index 06605f33ed4..3d09124aaa0 100644 --- a/code/game/objects/structures/crates_lockers/closets.dm +++ b/code/game/objects/structures/crates_lockers/closets.dm @@ -248,6 +248,7 @@ qdel(src) /obj/structure/closet/obj_break(damage_flag) + . = ..() if(!broken && !(flags_1 & NODECONSTRUCT_1)) bust_open() diff --git a/code/game/objects/structures/displaycase.dm b/code/game/objects/structures/displaycase.dm index d4c84d77b6d..9abf0a8c5aa 100644 --- a/code/game/objects/structures/displaycase.dm +++ b/code/game/objects/structures/displaycase.dm @@ -83,6 +83,7 @@ qdel(src) /obj/structure/displaycase/obj_break(damage_flag) + . = ..() if(!broken && !(flags_1 & NODECONSTRUCT_1)) density = FALSE broken = TRUE @@ -576,6 +577,7 @@ . += "[src] is sparking and the hover field generator seems to be overloaded. Use a multitool to fix it." /obj/structure/displaycase/forsale/obj_break(damage_flag) + . = ..() if(!broken && !(flags_1 & NODECONSTRUCT_1)) broken = TRUE playsound(src, "shatter", 70, TRUE) diff --git a/code/game/objects/structures/extinguisher.dm b/code/game/objects/structures/extinguisher.dm index fb2a091fb66..907c6abd83a 100644 --- a/code/game/objects/structures/extinguisher.dm +++ b/code/game/objects/structures/extinguisher.dm @@ -156,6 +156,7 @@ */ /obj/structure/extinguisher_cabinet/obj_break(damage_flag) + . = ..() if(!broken && !(flags_1 & NODECONSTRUCT_1)) broken = 1 opened = 1 diff --git a/code/game/objects/structures/fireaxe.dm b/code/game/objects/structures/fireaxe.dm index d92c182145d..454692a6bd6 100644 --- a/code/game/objects/structures/fireaxe.dm +++ b/code/game/objects/structures/fireaxe.dm @@ -99,6 +99,7 @@ update_appearance() /obj/structure/fireaxecabinet/obj_break(damage_flag) + . = ..() if(!broken && !(flags_1 & NODECONSTRUCT_1)) update_appearance() broken = TRUE diff --git a/code/game/objects/structures/grille.dm b/code/game/objects/structures/grille.dm index 920a60e89f9..afd3a0b6c73 100644 --- a/code/game/objects/structures/grille.dm +++ b/code/game/objects/structures/grille.dm @@ -265,6 +265,7 @@ ..() /obj/structure/grille/obj_break() + . = ..() if(!broken && !(flags_1 & NODECONSTRUCT_1)) icon_state = "brokengrille" density = FALSE diff --git a/code/game/objects/structures/mirror.dm b/code/game/objects/structures/mirror.dm index bb70ae55e9b..ec895f50ddf 100644 --- a/code/game/objects/structures/mirror.dm +++ b/code/game/objects/structures/mirror.dm @@ -89,6 +89,7 @@ unlucky_dude.AddComponent(/datum/component/omen, silent=TRUE) // we have our own message /obj/structure/mirror/obj_break(damage_flag, mapload) + . = ..() if(broken || (flags_1 & NODECONSTRUCT_1)) return icon_state = "mirror_broke" diff --git a/code/modules/atmospherics/gasmixtures/gas_mixture.dm b/code/modules/atmospherics/gasmixtures/gas_mixture.dm index 1165c636734..37507bbf61c 100644 --- a/code/modules/atmospherics/gasmixtures/gas_mixture.dm +++ b/code/modules/atmospherics/gasmixtures/gas_mixture.dm @@ -548,7 +548,7 @@ get_true_breath_pressure(pp) --> gas_pp = pp/breath_pp*total_moles() return FALSE /// Releases gas from src to output air. This means that it can not transfer air to gas mixture with higher pressure. -/datum/gas_mixture/proc/release_gas_to(datum/gas_mixture/output_air, target_pressure) +/datum/gas_mixture/proc/release_gas_to(datum/gas_mixture/output_air, target_pressure, rate=1) var/output_starting_pressure = output_air.return_pressure() var/input_starting_pressure = return_pressure() @@ -565,7 +565,7 @@ get_true_breath_pressure(pp) --> gas_pp = pp/breath_pp*total_moles() var/transfer_moles = (pressure_delta*output_air.volume)/(temperature * R_IDEAL_GAS_EQUATION) //Actually transfer the gas - var/datum/gas_mixture/removed = remove(transfer_moles) + var/datum/gas_mixture/removed = remove(transfer_moles * rate) output_air.merge(removed) return TRUE diff --git a/code/modules/atmospherics/machinery/portable/canister.dm b/code/modules/atmospherics/machinery/portable/canister.dm index 2b64edef211..36095846225 100644 --- a/code/modules/atmospherics/machinery/portable/canister.dm +++ b/code/modules/atmospherics/machinery/portable/canister.dm @@ -108,6 +108,8 @@ GLOBAL_LIST_INIT(gas_id_to_canister, init_gas_id_to_canister()) update_appearance() AddElement(/datum/element/atmos_sensitive, mapload) + AddElement(/datum/element/volatile_gas_storage) + AddComponent(/datum/component/gas_leaker, leak_rate=0.01) /obj/machinery/portable_atmospherics/canister/interact(mob/user) . = ..() @@ -518,18 +520,11 @@ GLOBAL_LIST_INIT(gas_id_to_canister, init_gas_id_to_canister()) /obj/machinery/portable_atmospherics/canister/proc/canister_break() disconnect() var/datum/gas_mixture/expelled_gas = air_contents.remove(air_contents.total_moles()) - var/expelled_pressure = expelled_gas?.return_pressure() var/turf/T = get_turf(src) T.assume_air(expelled_gas) obj_break() - if(expelled_pressure > pressure_limit) - var/pressure_dif = expelled_pressure - pressure_limit - var/max_pressure_difference = 20000 - var/explosion_range = CEILING(min(pressure_dif, max_pressure_difference) / 1000, 1) - explosion(T, light_impact_range = explosion_range, smoke = FALSE) - density = FALSE playsound(src.loc, 'sound/effects/spray.ogg', 10, TRUE, -3) investigate_log("was destroyed.", INVESTIGATE_ATMOS) @@ -558,32 +553,13 @@ GLOBAL_LIST_INIT(gas_id_to_canister, init_gas_id_to_canister()) valve_open = !valve_open timing = FALSE - var/turf/location = get_turf(src) - - var/mix_air = FALSE - var/pressure = release_pressure - var/gas_mix = holding?.return_air() - var/air_update = FALSE - - if(valve_open) - mix_air = TRUE - - // When at least 10% of integrity is lost it starts checking for leaking - if(obj_integrity < max_integrity * 0.9) - var/leak_chance = (1 - obj_integrity / max_integrity) * 100 - excited = TRUE - if(prob(leak_chance)) - mix_air = TRUE - pressure = air_contents.return_pressure() / 10 - gas_mix = location.return_air() - air_update = TRUE - // Handle gas transfer. - if(mix_air) - var/datum/gas_mixture/target_air = gas_mix || location.return_air() + if(valve_open) + var/turf/location = get_turf(src) + var/datum/gas_mixture/target_air = holding?.return_air() || location.return_air() excited = TRUE - if(air_contents.release_gas_to(target_air, pressure) && (!holding || air_update)) + if(air_contents.release_gas_to(target_air, release_pressure) && !holding) air_update_turf(FALSE, FALSE) var/our_pressure = air_contents.return_pressure() diff --git a/code/modules/clothing/clothing.dm b/code/modules/clothing/clothing.dm index f8f71a9d12f..de1642f3db4 100644 --- a/code/modules/clothing/clothing.dm +++ b/code/modules/clothing/clothing.dm @@ -397,6 +397,7 @@ return . /obj/item/clothing/obj_break(damage_flag) + . = ..() update_clothes_damaged_state(CLOTHING_DAMAGED) if(isliving(loc)) //It's not important enough to warrant a message if it's not on someone diff --git a/tgstation.dme b/tgstation.dme index 6f82be92eb5..a970196e685 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -545,6 +545,7 @@ #include "code\datums\components\footstep.dm" #include "code\datums\components\forensics.dm" #include "code\datums\components\fullauto.dm" +#include "code\datums\components\gas_leaker.dm" #include "code\datums\components\gps.dm" #include "code\datums\components\grillable.dm" #include "code\datums\components\gunpoint.dm" @@ -761,6 +762,7 @@ #include "code\datums\elements\update_icon_blocker.dm" #include "code\datums\elements\update_icon_updates_onmob.dm" #include "code\datums\elements\venomous.dm" +#include "code\datums\elements\volatile_gas_storage.dm" #include "code\datums\elements\waddling.dm" #include "code\datums\elements\weapon_description.dm" #include "code\datums\elements\decals\blood.dm"