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) => {