mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-08-22 20:48:56 +01:00
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.
This commit is contained in:
+1
-1
@@ -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
|
||||
|
||||
+27
-18
@@ -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)
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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 (
|
||||
<Window width={350} height={305}>
|
||||
<Window width={350} height={335}>
|
||||
<Window.Content>
|
||||
<Flex direction="column" height="100%">
|
||||
<Flex.Item mb={1}>
|
||||
@@ -150,11 +154,25 @@ export const Canister = (props, context) => {
|
||||
</LabeledControls>
|
||||
</Section>
|
||||
<Section>
|
||||
<Box>
|
||||
{data.has_cell
|
||||
? 'Cell charge at: ' + data.cell_charge + '%'
|
||||
: 'Missing Cell'}
|
||||
</Box>
|
||||
<LabeledList>
|
||||
<LabeledList.Item label="Cell Charge">
|
||||
{hasCell ? cellCharge + '%' : 'Missing Cell'}
|
||||
</LabeledList.Item>
|
||||
{!!hasHypernobCrystal && (
|
||||
<LabeledList.Item label="Reaction Suppression">
|
||||
<Button
|
||||
icon={
|
||||
data.reactionSuppressionEnabled ? 'snowflake' : 'times'
|
||||
}
|
||||
content={
|
||||
data.reactionSuppressionEnabled ? 'Enabled' : 'Disabled'
|
||||
}
|
||||
selected={data.reactionSuppressionEnabled}
|
||||
onClick={() => act('reaction_suppression')}
|
||||
/>
|
||||
</LabeledList.Item>
|
||||
)}
|
||||
</LabeledList>
|
||||
</Section>
|
||||
</Flex.Item>
|
||||
<Flex.Item grow={1}>
|
||||
|
||||
@@ -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 (
|
||||
<Window width={300} height={315}>
|
||||
<Window width={300} height={340}>
|
||||
<Window.Content>
|
||||
<PortableBasicInfo />
|
||||
<Section
|
||||
@@ -36,11 +36,11 @@ export const PortablePump = (props, context) => {
|
||||
<LabeledList>
|
||||
<LabeledList.Item label="Output">
|
||||
<NumberInput
|
||||
value={target_pressure}
|
||||
value={targetPressure}
|
||||
unit="kPa"
|
||||
width="75px"
|
||||
minValue={min_pressure}
|
||||
maxValue={max_pressure}
|
||||
minValue={minPressure}
|
||||
maxValue={maxPressure}
|
||||
step={10}
|
||||
onChange={(e, value) =>
|
||||
act('pressure', {
|
||||
@@ -52,7 +52,7 @@ export const PortablePump = (props, context) => {
|
||||
<LabeledList.Item label="Presets">
|
||||
<Button
|
||||
icon="minus"
|
||||
disabled={target_pressure === min_pressure}
|
||||
disabled={targetPressure === minPressure}
|
||||
onClick={() =>
|
||||
act('pressure', {
|
||||
pressure: 'min',
|
||||
@@ -61,7 +61,7 @@ export const PortablePump = (props, context) => {
|
||||
/>
|
||||
<Button
|
||||
icon="sync"
|
||||
disabled={target_pressure === default_pressure}
|
||||
disabled={targetPressure === defaultPressure}
|
||||
onClick={() =>
|
||||
act('pressure', {
|
||||
pressure: 'reset',
|
||||
@@ -70,7 +70,7 @@ export const PortablePump = (props, context) => {
|
||||
/>
|
||||
<Button
|
||||
icon="plus"
|
||||
disabled={target_pressure === max_pressure}
|
||||
disabled={targetPressure === maxPressure}
|
||||
onClick={() =>
|
||||
act('pressure', {
|
||||
pressure: 'max',
|
||||
|
||||
@@ -6,34 +6,34 @@ import { Window } from '../layouts';
|
||||
import { PortableBasicInfo } from './common/PortableAtmos';
|
||||
|
||||
type Data = {
|
||||
filter_types: Filter[];
|
||||
filterTypes: Filter[];
|
||||
};
|
||||
|
||||
type Filter = {
|
||||
id: string;
|
||||
enabled: BooleanLike;
|
||||
gas_id: string;
|
||||
gas_name: string;
|
||||
gasId: string;
|
||||
gasName: string;
|
||||
};
|
||||
|
||||
export const PortableScrubber = (props, context) => {
|
||||
const { act, data } = useBackend<Data>(context);
|
||||
const { filter_types = [] } = data;
|
||||
const { filterTypes = [] } = data;
|
||||
|
||||
return (
|
||||
<Window width={320} height={396}>
|
||||
<Window width={320} height={420}>
|
||||
<Window.Content>
|
||||
<PortableBasicInfo />
|
||||
<Section title="Filters">
|
||||
{filter_types.map((filter) => (
|
||||
{filterTypes.map((filter) => (
|
||||
<Button
|
||||
key={filter.id}
|
||||
icon={filter.enabled ? 'check-square-o' : 'square-o'}
|
||||
content={getGasLabel(filter.gas_id, filter.gas_name)}
|
||||
content={getGasLabel(filter.gasId, filter.gasName)}
|
||||
selected={filter.enabled}
|
||||
onClick={() =>
|
||||
act('toggle_filter', {
|
||||
val: filter.gas_id,
|
||||
val: filter.gasId,
|
||||
})
|
||||
}
|
||||
/>
|
||||
|
||||
@@ -3,7 +3,14 @@ import { AnimatedNumber, Box, Button, LabeledList, Section } from '../../compone
|
||||
|
||||
export const PortableBasicInfo = (props, context) => {
|
||||
const { act, data } = useBackend(context);
|
||||
const { connected, holding, on, pressure } = data;
|
||||
const {
|
||||
connected,
|
||||
holding,
|
||||
on,
|
||||
pressure,
|
||||
hasHypernobCrystal,
|
||||
reactionSuppressionEnabled,
|
||||
} = data;
|
||||
return (
|
||||
<>
|
||||
<Section
|
||||
@@ -24,6 +31,18 @@ export const PortableBasicInfo = (props, context) => {
|
||||
<LabeledList.Item label="Port" color={connected ? 'good' : 'average'}>
|
||||
{connected ? 'Connected' : 'Not Connected'}
|
||||
</LabeledList.Item>
|
||||
{!!hasHypernobCrystal && (
|
||||
<LabeledList.Item label="Reaction Suppression">
|
||||
<Button
|
||||
icon={data.reactionSuppressionEnabled ? 'snowflake' : 'times'}
|
||||
content={
|
||||
data.reactionSuppressionEnabled ? 'Enabled' : 'Disabled'
|
||||
}
|
||||
selected={data.reactionSuppressionEnabled}
|
||||
onClick={() => act('reaction_suppression')}
|
||||
/>
|
||||
</LabeledList.Item>
|
||||
)}
|
||||
</LabeledList>
|
||||
</Section>
|
||||
<Section
|
||||
|
||||
Reference in New Issue
Block a user