Connect air alarm to air sensor (#75187)

## About The Pull Request

To control vents and scrubbers in ordinance, burn and engine chambers,
mappers extend the area outside of the walls towards a tile where they
place the air alarm.

With this PR, they can now assign `chamber_id` to an air alarm and
connect to the chamber with an air sensor. There is a new map helper for
this.

Also, this connection can be done manually. You need to click on a
sensor with a multi-tool, then unlock certain air alarm, and click with
a multi-tool on it. This action will link sensor to an air alarm,
reporting gas mixture from the sensor tile and giving control over the
vents and scrubbers of the sensor's area.

### TLDR

Before:
<img width="718" alt="ordnance_before"
src="https://user-images.githubusercontent.com/3625094/236577769-5d79871f-2dce-43be-a20a-e6669bfbc1c6.PNG">

After: 
<img width="638" alt="ordnance_after"
src="https://user-images.githubusercontent.com/3625094/236577786-3c7e9c9f-1501-4747-bbe1-292fc4947b0d.PNG">

This is how the area is setup on meta station right now vs if it was
setup with a link

![image](https://user-images.githubusercontent.com/3625094/236578327-531cfb6f-e0ac-4c03-927f-f5b05b9c74ab.png)

This is also true for the supermatter chamber - you can make the air
alarm display the gas mix in the actual chamber and avoid using mapping
area hack there too.
<img width="954" alt="supermatter_after"
src="https://user-images.githubusercontent.com/3625094/236578528-4650b426-6bf0-4634-a5b0-cad7a50d5b01.PNG">

## Why It's Good For The Game

The area hack is no longer needed and you can place air alarm to control
certain remote area wherever you want when you design a map. Even 3 air
alarms next to each other controlling 3 different burn chambers.

The air alarm will also report the gas mix on the actual tile of a
sensor, instead of the gas mix before the air alarm, which is usually a
normal habitable environment.

Also, now you can build such chambers manually because there are no
precise area editing tools available in-game to repeat the area hack.

## Changelog

🆑
add: Air alarms can be connected to an area remotely via air sensor with
multi-tool and corresponding access
qol: Mapping: Added air alarm helper to link air alarm with certain
chamber_id on map load
/🆑
This commit is contained in:
Andrew
2023-05-15 20:10:08 -06:00
committed by GitHub
parent dc0a1e65e4
commit 4b01dbe5d4
7 changed files with 123 additions and 17 deletions
@@ -30,6 +30,7 @@
#define ATMOS_GAS_MONITOR_ORDNANCE_FREEZER "ordnancefreezer"
#define ATMOS_GAS_MONITOR_DISTRO "distro"
#define ATMOS_GAS_MONITOR_WASTE "waste"
#define ATMOS_GAS_MONITOR_ENGINE "engine"
///maps a chamber id to its air sensor
#define CHAMBER_SENSOR_FROM_ID(chamber_id) ((chamber_id) + "_sensor")
@@ -67,4 +68,5 @@ GLOBAL_LIST_INIT(station_gas_chambers, list(
ATMOS_GAS_MONITOR_ORDNANCE_FREEZER = "Ordnance Freezer Chamber",
ATMOS_GAS_MONITOR_DISTRO = "Distribution Loop",
ATMOS_GAS_MONITOR_WASTE = "Waste Loop",
ATMOS_GAS_MONITOR_ENGINE = "Supermatter Engine Chamber",
))
@@ -60,10 +60,8 @@
input.chamber_id = chamber_id
GLOB.objects_by_id_tag[CHAMBER_INPUT_FROM_ID(chamber_id)] = input
balloon_alert(user, "connected to input")
else if(istype(multi_tool.buffer, /obj/machinery/atmospherics/components/unary/vent_pump))
var/obj/machinery/atmospherics/components/unary/vent_pump/output = multi_tool.buffer
//so its no longer controlled by air alarm
output.disconnect_from_area()
//configuration copied from /obj/machinery/atmospherics/components/unary/vent_pump/siphon
@@ -71,9 +69,11 @@
output.pressure_checks = ATMOS_INTERNAL_BOUND
output.internal_pressure_bound = 4000
output.external_pressure_bound = 0
output.chamber_id = chamber_id
GLOB.objects_by_id_tag[CHAMBER_OUTPUT_FROM_ID(chamber_id)] = output
balloon_alert(user, "connected to output")
else
multi_tool.buffer = src
balloon_alert(user, "added to multitool buffer")
return TRUE
@@ -85,7 +85,7 @@
/obj/machinery/air_sensor/antinoblium_tank
name = "antinoblium tank gas sensor"
chamber_id = ATMOS_GAS_MONITOR_ANTINOBLIUM
/obj/machinery/air_sensor/incinerator_tank
name = "incinerator chamber gas sensor"
chamber_id = ATMOS_GAS_MONITOR_INCINERATOR
@@ -97,3 +97,7 @@
/obj/machinery/air_sensor/ordnance_freezer_chamber
name = "ordnance freezer chamber gas sensor"
chamber_id = ATMOS_GAS_MONITOR_ORDNANCE_FREEZER
/obj/machinery/air_sensor/engine_chamber
name = "supermatter engine chamber gas sensor"
chamber_id = ATMOS_GAS_MONITOR_ENGINE
@@ -57,6 +57,13 @@
/// Used for air alarm helper called tlv_no_ckecks to remove alarm thresholds.
var/tlv_no_checks = FALSE
/// Used for connecting air alarm to a remote tile/zone via air sensor instead of the tile/zone of the air alarm
var/obj/machinery/air_sensor/connected_sensor
/// Used to link air alarm to air sensor via map helpers
var/air_sensor_chamber_id = ""
/// Whether it is possible to link/unlink this air alarm from a sensor
var/allow_link_change = TRUE
GLOBAL_LIST_EMPTY_TYPED(air_alarms, /obj/machinery/airalarm)
@@ -92,7 +99,7 @@ GLOBAL_LIST_EMPTY_TYPED(air_alarms, /obj/machinery/airalarm)
else
tlv_collection[gas_path] = new /datum/tlv/no_checks
my_area = get_area(src)
my_area = connected_sensor ? get_area(connected_sensor) : get_area(src)
alarm_manager = new(src)
select_mode(src, /datum/air_alarm_mode/filtering)
@@ -121,7 +128,7 @@ GLOBAL_LIST_EMPTY_TYPED(air_alarms, /obj/machinery/airalarm)
return
. = ..()
my_area = area_to_register
my_area = connected_sensor ? get_area(connected_sensor) : area_to_register
update_appearance()
/obj/machinery/airalarm/update_name(updates)
@@ -134,7 +141,7 @@ GLOBAL_LIST_EMPTY_TYPED(air_alarms, /obj/machinery/airalarm)
return
. = ..()
my_area = null
my_area = connected_sensor ? get_area(connected_sensor) : null
/obj/machinery/airalarm/examine(mob/user)
. = ..()
@@ -153,6 +160,19 @@ GLOBAL_LIST_EMPTY_TYPED(air_alarms, /obj/machinery/airalarm)
return ..()
return UI_CLOSE
/obj/machinery/airalarm/multitool_act(mob/living/user, obj/item/multitool/multi_tool)
.= ..()
if (!istype(multi_tool) || locked)
return .
if(istype(multi_tool.buffer, /obj/machinery/air_sensor))
if(!allow_link_change)
balloon_alert(user, "linking disabled")
return TOOL_ACT_SIGNAL_BLOCKING
connect_sensor(multi_tool.buffer)
balloon_alert(user, "connected sensor")
return TOOL_ACT_TOOLTYPE_SUCCESS
/obj/machinery/airalarm/ui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
@@ -180,14 +200,21 @@ GLOBAL_LIST_EMPTY_TYPED(air_alarms, /obj/machinery/airalarm)
data["dangerLevel"] = danger_level
data["atmosAlarm"] = !!my_area.active_alarms[ALARM_ATMOS]
data["fireAlarm"] = my_area.fire
data["sensor"] = !!connected_sensor
data["allowLinkChange"] = allow_link_change
var/turf/turf = get_turf(src)
var/turf/turf = connected_sensor ? get_turf(connected_sensor) : get_turf(src)
var/datum/gas_mixture/environment = turf.return_air()
var/total_moles = environment.total_moles()
var/temp = environment.temperature
var/pressure = environment.return_pressure()
data["envData"] = list()
if(connected_sensor)
data["envData"] += list(list(
"name" = "Linked area",
"value" = my_area.name
))
data["envData"] += list(list(
"name" = "Pressure",
"value" = "[round(pressure, 0.01)] kPa",
@@ -287,7 +314,8 @@ GLOBAL_LIST_EMPTY_TYPED(air_alarms, /obj/machinery/airalarm)
return
var/mob/user = usr
var/area/area = get_area(src)
var/area/area = connected_sensor ? get_area(connected_sensor) : get_area(src)
ASSERT(!isnull(area))
var/ref = params["ref"]
@@ -394,7 +422,7 @@ GLOBAL_LIST_EMPTY_TYPED(air_alarms, /obj/machinery/airalarm)
tlv.set_value(threshold_type, value)
investigate_log("threshold value for [threshold]:[threshold_type] was set to [value] by [key_name(usr)]", INVESTIGATE_ATMOS)
var/turf/our_turf = get_turf(src)
var/turf/our_turf = connected_sensor ? get_turf(connected_sensor) : get_turf(src)
var/datum/gas_mixture/environment = our_turf.return_air()
check_danger(our_turf, environment, environment.temperature)
@@ -407,7 +435,7 @@ GLOBAL_LIST_EMPTY_TYPED(air_alarms, /obj/machinery/airalarm)
tlv.reset_value(threshold_type)
investigate_log("threshold value for [threshold]:[threshold_type] was reset by [key_name(usr)]", INVESTIGATE_ATMOS)
var/turf/our_turf = get_turf(src)
var/turf/our_turf = connected_sensor ? get_turf(connected_sensor) : get_turf(src)
var/datum/gas_mixture/environment = our_turf.return_air()
check_danger(our_turf, environment, environment.temperature)
@@ -419,6 +447,10 @@ GLOBAL_LIST_EMPTY_TYPED(air_alarms, /obj/machinery/airalarm)
if (alarm_manager.clear_alarm(ALARM_ATMOS))
danger_level = AIR_ALARM_ALERT_NONE
if ("disconnect_sensor")
if(allow_link_change)
disconnect_sensor()
update_appearance()
return TRUE
@@ -557,3 +589,29 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/airalarm, 24)
/obj/machinery/airalarm/proc/set_tlv_no_checks()
tlv_collection["temperature"] = new /datum/tlv/no_checks
tlv_collection["pressure"] = new /datum/tlv/no_checks
///Used for air alarm link helper, which connects air alarm to a sensor with corresponding chamber_id
/obj/machinery/airalarm/proc/setup_chamber_link()
var/obj/machinery/air_sensor/sensor = GLOB.objects_by_id_tag[CHAMBER_SENSOR_FROM_ID(air_sensor_chamber_id)]
if(isnull(sensor))
log_mapping("[src] at [AREACOORD(src)] tried to connect to a sensor, but no sensor with chamber_id:[air_sensor_chamber_id] found!")
return
connect_sensor(sensor)
///Used to connect air alarm with a sensor
/obj/machinery/airalarm/proc/connect_sensor(obj/machinery/air_sensor/sensor)
if(!isnull(connected_sensor))
UnregisterSignal(connected_sensor, COMSIG_PARENT_QDELETING)
connected_sensor = sensor
RegisterSignal(connected_sensor, COMSIG_PARENT_QDELETING, PROC_REF(disconnect_sensor))
my_area = get_area(connected_sensor)
update_name()
check_danger()
///Used to reset the air alarm to default configuration after disconnecting from air sensor
/obj/machinery/airalarm/proc/disconnect_sensor()
UnregisterSignal(connected_sensor, COMSIG_PARENT_QDELETING)
connected_sensor = null
my_area = get_area(src)
update_name()
check_danger()
+24 -1
View File
@@ -253,7 +253,7 @@
//air alarm helpers
/obj/effect/mapping_helpers/airalarm
desc = "You shouldn't see this. Report it please."
layer = DOOR_HELPER_LAYER
layer = ABOVE_OBJ_LAYER
late = TRUE
/obj/effect/mapping_helpers/airalarm/Initialize(mapload)
@@ -301,6 +301,9 @@
if(target.syndicate_access + target.away_general_access + target.engine_access + target.mixingchamber_access + target.all_access > 1)
CRASH("Tried to combine incompatible air alarm access helpers!")
if(target.air_sensor_chamber_id)
target.setup_chamber_link()
target.update_appearance()
qdel(src)
@@ -387,6 +390,26 @@
log_mapping("[src] at [AREACOORD(src)] [(area.type)] tried to adjust [target]'s tlv to no_checks but it's already changed!")
target.tlv_no_checks = TRUE
/obj/effect/mapping_helpers/airalarm/link
name = "airalarm link helper"
icon_state = "airalarm_link_helper"
var/chamber_id = ""
var/allow_link_change = FALSE
/obj/effect/mapping_helpers/airalarm/link/Initialize(mapload)
. = ..()
if(!mapload)
log_mapping("[src] spawned outside of mapload!")
return INITIALIZE_HINT_QDEL
var/obj/machinery/airalarm/alarm = locate(/obj/machinery/airalarm) in loc
if(!isnull(alarm))
alarm.air_sensor_chamber_id = chamber_id
alarm.allow_link_change = allow_link_change
else
log_mapping("[src] failed to find air alarm at [AREACOORD(src)].")
return INITIALIZE_HINT_QDEL
//apc helpers
/obj/effect/mapping_helpers/apc
desc = "You shouldn't see this. Report it please."
Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 24 KiB

+24 -5
View File
@@ -13,6 +13,8 @@ type AirAlarmData = {
dangerLevel: 0 | 1 | 2;
atmosAlarm: BooleanLike; // fix this
fireAlarm: BooleanLike;
sensor: BooleanLike;
allowLinkChange: BooleanLike;
envData: {
name: string;
value: string; // preformatted in backend, shorter code that way.
@@ -167,7 +169,14 @@ const AirAlarmControl = (props, context) => {
const AirAlarmControlHome = (props, context) => {
const { act, data } = useBackend<AirAlarmData>(context);
const [screen, setScreen] = useLocalState<Screen>(context, 'screen', 'home');
const { selectedModePath, panicSiphonPath, filteringPath, atmosAlarm } = data;
const {
selectedModePath,
panicSiphonPath,
filteringPath,
atmosAlarm,
sensor,
allowLinkChange,
} = data;
const isPanicSiphoning = selectedModePath === panicSiphonPath;
return (
<>
@@ -212,6 +221,16 @@ const AirAlarmControlHome = (props, context) => {
content="Alarm Thresholds"
onClick={() => setScreen('thresholds')}
/>
{!!sensor && !!allowLinkChange && (
<Box mt={1}>
<Button.Confirm
icon="link-slash"
content="Disconnect Sensor"
color="danger"
onClick={() => act('disconnect_sensor')}
/>
</Box>
)}
</>
);
};
@@ -364,16 +383,16 @@ const AirAlarmControlThresholds = (props, context) => {
<Table.Row>
<Table.Cell bold>Threshold</Table.Cell>
<Table.Cell bold color="bad">
Minimum Hazard
Danger Below
</Table.Cell>
<Table.Cell bold color="average">
Minimum Warning
Warning Below
</Table.Cell>
<Table.Cell bold color="average">
Maximum Warning
Warning Above
</Table.Cell>
<Table.Cell bold color="bad">
Maximum Hazard
Danger Above
</Table.Cell>
<Table.Cell bold>Actions</Table.Cell>
</Table.Row>