From 9f65a2334aefd7bb5b498c1ebf31f6202ce4e2c0 Mon Sep 17 00:00:00 2001 From: RandomGamer123 <31096837+RandomGamer123@users.noreply.github.com> Date: Thu, 15 Sep 2022 18:12:05 -0400 Subject: [PATCH] Allows adding a hypernoblium crystal to portable atmospheric devices to suppress reactions (#68447) The main functionality is allowing for reaction suppression in canisters to still be a thing by allowing people to add a hypernoblium crystal to a can/pump/scrubber to add a toggleable option to stop it from reacting. The crystal can only be retrieved by deconstructing the canister to make transferring 1 crystal between 5 different cans less of an option. Some other changes included in this: Hypernoblium crystals are now single use, and their cost has been halved to reflect this. (So players dont have to waste 2 uses of a crystal on a can) Canister UI's cell charge display style has been slightly changed. Removed unused id_tag variable from scrubber UI code that was from 5 years ago Some various variable name changes (snake_case to camelCase) in relevant UI code for consistency with other JS UI code. Allows some preservation of the ability to stop canisters from reacting when wrenched in #68420, while also making it more intuitive and giving an incentive for people to make hypernob crystals You can now insert a hypernoblium crystal into portable atmospheric devices (ie. canisters, pumps, and scrubbers) to allow you to toggle reactions inside the can on and off, similar to how wrenching and unwrenching it on a connector did previously. The crystal once inserted cannot be removed without deconstructing the canister. However, hypernoblium crystals are now single-use items, and their cost has been halved to account for it. Variable names in canister, pump, and scrubber UI code have been changed to camel case for consistency. The canister UI has been slightly changed in terms of how it displays cell charge. --- .../atmos_machines_recipes.dm | 2 +- .../gas_recipe_machines/crystallizer_items.dm | 45 +++++++++++-------- .../machinery/portable/canister.dm | 17 +++++-- .../portable/portable_atmospherics.dm | 17 ++++++- .../atmospherics/machinery/portable/pump.dm | 19 ++++++-- .../machinery/portable/scrubber.dm | 17 +++++-- tgui/packages/tgui/interfaces/Canister.js | 30 ++++++++++--- tgui/packages/tgui/interfaces/PortablePump.js | 22 ++++----- .../tgui/interfaces/PortableScrubber.tsx | 16 +++---- .../tgui/interfaces/common/PortableAtmos.js | 21 ++++++++- 10 files changed, 150 insertions(+), 56 deletions(-) diff --git a/code/modules/atmospherics/machinery/components/gas_recipe_machines/atmos_machines_recipes.dm b/code/modules/atmospherics/machinery/components/gas_recipe_machines/atmos_machines_recipes.dm index 11521031303..7634f2d6519 100644 --- a/code/modules/atmospherics/machinery/components/gas_recipe_machines/atmos_machines_recipes.dm +++ b/code/modules/atmospherics/machinery/components/gas_recipe_machines/atmos_machines_recipes.dm @@ -42,7 +42,7 @@ GLOBAL_LIST_INIT(gas_recipe_meta, gas_recipes_list()) min_temp = 3 max_temp = 250 energy_release = -250000 - requirements = list(/datum/gas/oxygen = 2000, /datum/gas/hypernoblium = 175) + requirements = list(/datum/gas/oxygen = 1000, /datum/gas/hypernoblium = 85) products = list(/obj/item/hypernoblium_crystal = 1) /datum/gas_recipe/crystallizer/metallic_hydrogen diff --git a/code/modules/atmospherics/machinery/components/gas_recipe_machines/crystallizer_items.dm b/code/modules/atmospherics/machinery/components/gas_recipe_machines/crystallizer_items.dm index c35b77f866d..0551551dd03 100644 --- a/code/modules/atmospherics/machinery/components/gas_recipe_machines/crystallizer_items.dm +++ b/code/modules/atmospherics/machinery/components/gas_recipe_machines/crystallizer_items.dm @@ -1,30 +1,39 @@ /obj/item/hypernoblium_crystal name = "Hypernoblium Crystal" - desc = "Crystalized oxygen and hypernoblium to pressureproof your clothes." + desc = "Crystalized oxygen and hypernoblium stored in a bottle to pressureproof your clothes or stop reactions occuring in portable atmospheric devices." icon = 'icons/obj/atmospherics/atmos.dmi' icon_state = "hypernoblium_crystal" - var/uses = 2 + var/uses = 1 -/obj/item/hypernoblium_crystal/afterattack(obj/item/clothing/worn_item, mob/user, proximity) +/obj/item/hypernoblium_crystal/afterattack(obj/target_object, mob/user, proximity) . = ..() if(!proximity) return - if(!istype(worn_item)) - to_chat(user, span_warning("The crystal can only be used on clothing!")) + var/obj/machinery/portable_atmospherics/atmos_device = target_object + if(istype(atmos_device)) + if(atmos_device.nob_crystal_inserted) + to_chat(user, span_warning("[atmos_device] already has a hypernoblium crystal inserted in it!")) + return + atmos_device.nob_crystal_inserted = TRUE + to_chat(user, span_notice("You insert the [src] into [atmos_device].")) + var/obj/item/clothing/worn_item = target_object + if(!istype(worn_item) && !istype(atmos_device)) + to_chat(user, span_warning("The crystal can only be used on clothing and portable atmospheric devices!")) return - if(istype(worn_item, /obj/item/clothing/suit/space)) - to_chat(user, span_warning("The [worn_item] is already pressure-resistant!")) - return - if(worn_item.min_cold_protection_temperature == SPACE_SUIT_MIN_TEMP_PROTECT && worn_item.clothing_flags & STOPSPRESSUREDAMAGE) - to_chat(user, span_warning("[worn_item] is already pressure-resistant!")) - return - to_chat(user, span_notice("You see how the [worn_item] changes color, it's now pressure proof.")) - worn_item.name = "pressure-resistant [worn_item.name]" - worn_item.remove_atom_colour(WASHABLE_COLOUR_PRIORITY) - worn_item.add_atom_colour("#00fff7", FIXED_COLOUR_PRIORITY) - worn_item.min_cold_protection_temperature = SPACE_SUIT_MIN_TEMP_PROTECT - worn_item.cold_protection = worn_item.body_parts_covered - worn_item.clothing_flags |= STOPSPRESSUREDAMAGE + if(istype(worn_item)) + if(istype(worn_item, /obj/item/clothing/suit/space)) + to_chat(user, span_warning("The [worn_item] is already pressure-resistant!")) + return + if(worn_item.min_cold_protection_temperature == SPACE_SUIT_MIN_TEMP_PROTECT && worn_item.clothing_flags & STOPSPRESSUREDAMAGE) + to_chat(user, span_warning("[worn_item] is already pressure-resistant!")) + return + to_chat(user, span_notice("You see how the [worn_item] changes color, it's now pressure proof.")) + worn_item.name = "pressure-resistant [worn_item.name]" + worn_item.remove_atom_colour(WASHABLE_COLOUR_PRIORITY) + worn_item.add_atom_colour("#00fff7", FIXED_COLOUR_PRIORITY) + worn_item.min_cold_protection_temperature = SPACE_SUIT_MIN_TEMP_PROTECT + worn_item.cold_protection = worn_item.body_parts_covered + worn_item.clothing_flags |= STOPSPRESSUREDAMAGE uses-- if(!uses) qdel(src) diff --git a/code/modules/atmospherics/machinery/portable/canister.dm b/code/modules/atmospherics/machinery/portable/canister.dm index b61f119a49e..dbac4ca250e 100644 --- a/code/modules/atmospherics/machinery/portable/canister.dm +++ b/code/modules/atmospherics/machinery/portable/canister.dm @@ -620,7 +620,9 @@ GLOBAL_LIST_INIT(gas_id_to_canister, init_gas_id_to_canister()) "releasePressure" = round(release_pressure), "valveOpen" = !!valve_open, "isPrototype" = !!prototype, - "hasHoldingTank" = !!holding + "hasHoldingTank" = !!holding, + "hasHypernobCrystal" = !!nob_crystal_inserted, + "reactionSuppressionEnabled" = !!suppress_reactions ) if (prototype) @@ -644,8 +646,8 @@ GLOBAL_LIST_INIT(gas_id_to_canister, init_gas_id_to_canister()) ) . += list( "shielding" = shielding_powered, - "has_cell" = (internal_cell ? TRUE : FALSE), - "cell_charge" = internal_cell?.percent() + "hasCell" = (internal_cell ? TRUE : FALSE), + "cellCharge" = internal_cell?.percent() ) /obj/machinery/portable_atmospherics/canister/ui_act(action, params) @@ -761,6 +763,15 @@ GLOBAL_LIST_INIT(gas_id_to_canister, init_gas_id_to_canister()) message_admins("[ADMIN_LOOKUPFLW(usr)] turned [shielding_powered ? "on" : "off"] the [src] powered shielding.") investigate_log("[key_name(usr)] turned [shielding_powered ? "on" : "off"] the [src] powered shielding.") . = TRUE + if("reaction_suppression") + if(!nob_crystal_inserted) + stack_trace("[usr] tried to toggle reaction suppression on a canister without a noblium crystal inside, possible href exploit attempt.") + return + suppress_reactions = !suppress_reactions + SSair.start_processing_machine(src) + message_admins("[ADMIN_LOOKUPFLW(usr)] turned [suppress_reactions ? "on" : "off"] the [src] reaction suppression.") + investigate_log("[key_name(usr)] turned [suppress_reactions ? "on" : "off"] the [src] reaction suppression.") + . = TRUE update_appearance() diff --git a/code/modules/atmospherics/machinery/portable/portable_atmospherics.dm b/code/modules/atmospherics/machinery/portable/portable_atmospherics.dm index fa82fb26574..1197cadd29d 100644 --- a/code/modules/atmospherics/machinery/portable/portable_atmospherics.dm +++ b/code/modules/atmospherics/machinery/portable/portable_atmospherics.dm @@ -26,6 +26,11 @@ /// Max amount of pressure allowed inside of the canister before it starts to break. [PORTABLE_ATMOS_IGNORE_ATMOS_LIMIT] is special value meaning we are immune. var/pressure_limit = 500000 + /// Should reactions inside the object be suppressed + var/suppress_reactions = FALSE + /// Is there a hypernoblium crystal inserted into this + var/nob_crystal_inserted = FALSE + /obj/machinery/portable_atmospherics/Initialize(mapload) . = ..() air_contents = new @@ -38,8 +43,18 @@ air_contents = null SSair.stop_processing_machine(src) + if(nob_crystal_inserted) + new /obj/item/hypernoblium_crystal(src) + return ..() +/obj/machinery/portable_atmospherics/examine(mob/user) + . = ..() + if(nob_crystal_inserted) + . += "There is a hypernoblium crystal inside it that allows for reactions inside to be suppressed." + if(suppress_reactions) + . += "The hypernoblium crystal inside is glowing with a faint blue colour, indicating reactions inside are currently being suppressed." + /obj/machinery/portable_atmospherics/ex_act(severity, target) if(resistance_flags & INDESTRUCTIBLE) return FALSE //Indestructible cans shouldn't release air @@ -52,7 +67,7 @@ return ..() /obj/machinery/portable_atmospherics/process_atmos() - excited = (excited | air_contents.react(src)) + excited = (!suppress_reactions && (excited || air_contents.react(src))) if(!excited) return PROCESS_KILL excited = FALSE diff --git a/code/modules/atmospherics/machinery/portable/pump.dm b/code/modules/atmospherics/machinery/portable/pump.dm index 4d1cfb6b912..f446ec8f149 100644 --- a/code/modules/atmospherics/machinery/portable/pump.dm +++ b/code/modules/atmospherics/machinery/portable/pump.dm @@ -103,10 +103,12 @@ data["direction"] = direction data["connected"] = !!connected_port data["pressure"] = round(air_contents.return_pressure() ? air_contents.return_pressure() : 0) - data["target_pressure"] = round(target_pressure ? target_pressure : 0) - data["default_pressure"] = round(PUMP_DEFAULT_PRESSURE) - data["min_pressure"] = round(PUMP_MIN_PRESSURE) - data["max_pressure"] = round(PUMP_MAX_PRESSURE) + data["targetPressure"] = round(target_pressure ? target_pressure : 0) + data["defaultPressure"] = round(PUMP_DEFAULT_PRESSURE) + data["minPressure"] = round(PUMP_MIN_PRESSURE) + data["maxPressure"] = round(PUMP_MAX_PRESSURE) + data["hasHypernobCrystal"] = !!nob_crystal_inserted + data["reactionSuppressionEnabled"] = !!suppress_reactions if(holding) data["holding"] = list() @@ -164,6 +166,15 @@ if(holding) replace_tank(usr, FALSE) . = TRUE + if("reaction_suppression") + if(!nob_crystal_inserted) + stack_trace("[usr] tried to toggle reaction suppression on a pump without a noblium crystal inside, possible href exploit attempt.") + return + suppress_reactions = !suppress_reactions + SSair.start_processing_machine(src) + message_admins("[ADMIN_LOOKUPFLW(usr)] turned [suppress_reactions ? "on" : "off"] the [src] reaction suppression.") + investigate_log("[key_name(usr)] turned [suppress_reactions ? "on" : "off"] the [src] reaction suppression.") + . = TRUE update_appearance() /obj/machinery/portable_atmospherics/pump/unregister_holding() diff --git a/code/modules/atmospherics/machinery/portable/scrubber.dm b/code/modules/atmospherics/machinery/portable/scrubber.dm index e906db259d3..6298887b3d9 100644 --- a/code/modules/atmospherics/machinery/portable/scrubber.dm +++ b/code/modules/atmospherics/machinery/portable/scrubber.dm @@ -114,11 +114,13 @@ data["connected"] = connected_port ? 1 : 0 data["pressure"] = round(air_contents.return_pressure() ? air_contents.return_pressure() : 0) - data["id_tag"] = -1 //must be defined in order to reuse code between portable and vent scrubbers - data["filter_types"] = list() + data["hasHypernobCrystal"] = !!nob_crystal_inserted + data["reactionSuppressionEnabled"] = !!suppress_reactions + + data["filterTypes"] = list() for(var/path in GLOB.meta_gas_info) var/list/gas = GLOB.meta_gas_info[path] - data["filter_types"] += list(list("gas_id" = gas[META_GAS_ID], "gas_name" = gas[META_GAS_NAME], "enabled" = (path in scrubbing))) + data["filterTypes"] += list(list("gasId" = gas[META_GAS_ID], "gasName" = gas[META_GAS_NAME], "enabled" = (path in scrubbing))) if(holding) data["holding"] = list() @@ -157,6 +159,15 @@ if("toggle_filter") scrubbing ^= gas_id2path(params["val"]) . = TRUE + if("reaction_suppression") + if(!nob_crystal_inserted) + message_admins("[ADMIN_LOOKUPFLW(usr)] tried to toggle reaction suppression on a scrubber without a noblium crystal inside, possible href exploit attempt.") + return + suppress_reactions = !suppress_reactions + SSair.start_processing_machine(src) + message_admins("[ADMIN_LOOKUPFLW(usr)] turned [suppress_reactions ? "on" : "off"] the [src] reaction suppression.") + investigate_log("[key_name(usr)] turned [suppress_reactions ? "on" : "off"] the [src] reaction suppression.") + . = TRUE update_appearance() /obj/machinery/portable_atmospherics/scrubber/unregister_holding() diff --git a/tgui/packages/tgui/interfaces/Canister.js b/tgui/packages/tgui/interfaces/Canister.js index 14f2315a71d..40ef30a8829 100644 --- a/tgui/packages/tgui/interfaces/Canister.js +++ b/tgui/packages/tgui/interfaces/Canister.js @@ -20,6 +20,10 @@ export const Canister = (props, context) => { defaultReleasePressure, minReleasePressure, maxReleasePressure, + hasHypernobCrystal, + reactionSuppressionEnabled, + hasCell, + cellCharge, pressureLimit, valveOpen, isPrototype, @@ -30,7 +34,7 @@ export const Canister = (props, context) => { restricted, } = data; return ( - + @@ -150,11 +154,25 @@ export const Canister = (props, context) => {
- - {data.has_cell - ? 'Cell charge at: ' + data.cell_charge + '%' - : 'Missing Cell'} - + + + {hasCell ? cellCharge + '%' : 'Missing Cell'} + + {!!hasHypernobCrystal && ( + +
diff --git a/tgui/packages/tgui/interfaces/PortablePump.js b/tgui/packages/tgui/interfaces/PortablePump.js index 709a126e638..13b254c830e 100644 --- a/tgui/packages/tgui/interfaces/PortablePump.js +++ b/tgui/packages/tgui/interfaces/PortablePump.js @@ -9,15 +9,15 @@ export const PortablePump = (props, context) => { direction, connected, holding, - target_pressure, - default_pressure, - min_pressure, - max_pressure, + targetPressure, + defaultPressure, + minPressure, + maxPressure, } = data; const pump_or_port = connected ? 'Port' : 'Pump'; const area_or_tank = holding ? 'Tank' : 'Area'; return ( - +
{ act('pressure', { @@ -52,7 +52,7 @@ export const PortablePump = (props, context) => {