diff --git a/code/game/machinery/alarm.dm b/code/game/machinery/alarm.dm
index cad86d1325d..360d877aeea 100644
--- a/code/game/machinery/alarm.dm
+++ b/code/game/machinery/alarm.dm
@@ -14,12 +14,6 @@
/// Shuts it all down.
#define AALARM_MODE_OFF 6
-#define AALARM_SCREEN_MAIN 1
-#define AALARM_SCREEN_VENT 2
-#define AALARM_SCREEN_SCRUB 3
-#define AALARM_SCREEN_MODE 4
-#define AALARM_SCREEN_SENSORS 5
-
#define AALARM_REPORT_TIMEOUT 100
#define RCON_NO 1
@@ -134,7 +128,6 @@ pixel_x = 10;
var/datum/wires/alarm/wires
var/mode = AALARM_MODE_SCRUBBING
- var/screen = AALARM_SCREEN_MAIN
var/area_uid
var/area/alarm_area
/// Display name
@@ -717,204 +710,178 @@ pixel_x = 10;
if (panel_open)
wires.interact(user)
-/obj/machinery/alarm/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 1, var/master_ui = null, var/datum/ui_state/state = GLOB.default_state)
- var/data = list()
- var/remote_connection = 0
- var/remote_access = 0
- if(state)
- var/list/href = state.href_list(user)
- /// Remote connection means we're non-adjacent/connecting from another computer
- remote_connection = href["remote_connection"]
- /// Remote access means we also have the privilege to alter the air alarm.
- remote_access = href["remote_access"]
-
- data["locked"] = locked && !issilicon(user)
- data["remote_connection"] = remote_connection
- data["remote_access"] = remote_access
- data["rcon"] = rcon_setting
- data["screen"] = screen
-
- populate_status(data)
-
- if(!(locked && !remote_connection) || remote_access || issilicon(user))
- populate_controls(data)
-
- ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open)
- if(!ui)
- ui = new(user, src, ui_key, "air_alarm.tmpl", src.name, 325, 625, master_ui = master_ui, state = state)
- ui.set_initial_data(data)
- ui.open()
- ui.set_auto_update(1)
-
-/obj/machinery/alarm/proc/populate_status(var/data)
- var/turf/location = get_turf(src)
- if(!istype(location)) return
- var/datum/gas_mixture/environment = location.return_air()
- var/total = environment.total_moles
-
- var/list/environment_data = new
- data["has_environment"] = total
- if(total)
- var/pressure = XGM_PRESSURE(environment)
- environment_data[++environment_data.len] = list("name" = "Pressure", "value" = pressure, "unit" = "kPa", "danger_level" = pressure_dangerlevel)
- environment_data[++environment_data.len] = list("name" = "Oxygen", "value" = environment.gas[GAS_OXYGEN] / total * 100, "unit" = "%", "danger_level" = oxygen_dangerlevel)
- environment_data[++environment_data.len] = list("name" = "Carbon Dioxide", "value" = environment.gas[GAS_CO2] / total * 100, "unit" = "%", "danger_level" = co2_dangerlevel)
- environment_data[++environment_data.len] = list("name" = "Phoron", "value" = environment.gas[GAS_PHORON] / total * 100, "unit" = "%", "danger_level" = phoron_dangerlevel)
- environment_data[++environment_data.len] = list("name" = "Hydrogen", "value" = environment.gas[GAS_HYDROGEN] / total * 100, "unit" = "%", "danger_level" = hydrogen_dangerlevel)
- environment_data[++environment_data.len] = list("name" = "Temperature", "value" = environment.temperature, "unit" = "K ([round(environment.temperature - T0C, 0.1)]C)", "danger_level" = temperature_dangerlevel)
- data["total_danger"] = danger_level
- data["environment"] = environment_data
- data["atmos_alarm"] = alarm_area.atmosalm
- data["target_temperature"] = "[target_temperature - T0C]C"
-
-/obj/machinery/alarm/proc/populate_controls(var/list/data)
- switch(screen)
- if(AALARM_SCREEN_MAIN)
- data["mode"] = mode
- if(AALARM_SCREEN_VENT)
- var/vents[0]
- for(var/id_tag in alarm_area.air_vent_names)
- var/long_name = alarm_area.air_vent_names[id_tag]
- var/list/info = alarm_area.air_vent_info[id_tag]
- if(!info)
- continue
- vents[++vents.len] = list(
- "id_tag" = id_tag,
- "long_name" = sanitize(long_name),
- "power" = info["power"],
- "checks" = info["checks"],
- "direction" = info["direction"],
- "external" = info["external"]
- )
- data["vents"] = vents
- if(AALARM_SCREEN_SCRUB)
- var/scrubbers[0]
- for(var/id_tag in alarm_area.air_scrub_names)
- var/long_name = alarm_area.air_scrub_names[id_tag]
- var/list/info = alarm_area.air_scrub_info[id_tag]
- if(!info)
- continue
- scrubbers[++scrubbers.len] = list(
- "id_tag" = id_tag,
- "long_name" = sanitize(long_name),
- "power" = info["power"],
- "scrubbing" = info["scrubbing"],
- "panic" = info["panic"],
- "filters" = list()
- )
- scrubbers[scrubbers.len]["filters"] += list(list("name" = "Oxygen", "command" = "o2_scrub", "val" = info["filter_o2"]))
- scrubbers[scrubbers.len]["filters"] += list(list("name" = "Nitrogen", "command" = "n2_scrub", "val" = info["filter_n2"]))
- scrubbers[scrubbers.len]["filters"] += list(list("name" = "Carbon Dioxide", "command" = "co2_scrub","val" = info["filter_co2"]))
- scrubbers[scrubbers.len]["filters"] += list(list("name" = "Phoron", "command" = "tox_scrub","val" = info["filter_phoron"]))
- scrubbers[scrubbers.len]["filters"] += list(list("name" = "Hydrogen", "command" = "h2_scrub","val" = info["filter_h2"]))
- scrubbers[scrubbers.len]["filters"] += list(list("name" = "Nitrous Oxide", "command" = "n2o_scrub","val" = info["filter_n2o"]))
- data["scrubbers"] = scrubbers
- if(AALARM_SCREEN_MODE)
- var/modes[0]
- modes[++modes.len] = list("name" = "Filtering - Scrubs out contaminants", "mode" = AALARM_MODE_SCRUBBING, "selected" = mode == AALARM_MODE_SCRUBBING, "danger" = 0)
- modes[++modes.len] = list("name" = "Replace Air - Siphons out air while replacing", "mode" = AALARM_MODE_REPLACEMENT, "selected" = mode == AALARM_MODE_REPLACEMENT, "danger" = 0)
- modes[++modes.len] = list("name" = "Panic - Siphons air out of the compartment", "mode" = AALARM_MODE_PANIC, "selected" = mode == AALARM_MODE_PANIC, "danger" = 1)
- modes[++modes.len] = list("name" = "Cycle - Siphons air before replacing", "mode" = AALARM_MODE_CYCLE, "selected" = mode == AALARM_MODE_CYCLE, "danger" = 1)
- modes[++modes.len] = list("name" = "Fill - Shuts off scrubbers and opens vents", "mode" = AALARM_MODE_FILL, "selected" = mode == AALARM_MODE_FILL, "danger" = 0)
- modes[++modes.len] = list("name" = "Off - Shuts off vents and scrubbers", "mode" = AALARM_MODE_OFF, "selected" = mode == AALARM_MODE_OFF, "danger" = 0)
- data["modes"] = modes
- data["mode"] = mode
- if(AALARM_SCREEN_SENSORS)
- var/list/selected
- var/thresholds[0]
-
- var/list/gas_names = list(
- GAS_OXYGEN = "O2",
- GAS_CO2 = "CO2",
- GAS_PHORON = "Phoron",
- GAS_HYDROGEN = "Hydrogen",
- "other" = "Other")
- for (var/g in gas_names)
- thresholds[++thresholds.len] = list("name" = gas_names[g], "settings" = list())
- selected = TLV[g]
- for(var/i = 1, i <= 4, i++)
- thresholds[thresholds.len]["settings"] += list(list("env" = g, "val" = i, "selected" = selected[i]))
-
- selected = TLV["pressure"]
- thresholds[++thresholds.len] = list("name" = "Pressure", "settings" = list())
- for(var/i = 1, i <= 4, i++)
- thresholds[thresholds.len]["settings"] += list(list("env" = "pressure", "val" = i, "selected" = selected[i]))
-
- selected = TLV["temperature"]
- thresholds[++thresholds.len] = list("name" = "Temperature", "settings" = list())
- for(var/i = 1, i <= 4, i++)
- thresholds[thresholds.len]["settings"] += list(list("env" = "temperature", "val" = i, "selected" = selected[i]))
-
-
- data["thresholds"] = thresholds
-
-/obj/machinery/alarm/CanUseTopic(var/mob/user, var/datum/ui_state/state, var/href_list = list())
+/obj/machinery/alarm/ui_interact(mob/user, datum/tgui/ui)
if(buildstage != 2)
- return STATUS_CLOSE
-
+ return
if(aidisabled && isAI(user))
to_chat(user, SPAN_WARNING("AI control for \the [src] interface has been disabled."))
- return STATUS_CLOSE
+ return
+ ui = SStgui.try_update_ui(user, src, ui)
+ if(!ui)
+ ui = new(user, src, "AirAlarm")
+ ui.open()
- . = shorted ? STATUS_DISABLED : STATUS_INTERACTIVE
+/obj/machinery/alarm/ui_data(mob/user)
+ var/list/data = list()
- if(. == STATUS_INTERACTIVE)
- var/extra_href = QDELETED(state) ? list() : state.href_list(user ? user : usr)
- // Prevent remote users from altering RCON settings unless they already have access
- if(href_list["rcon"] && extra_href["remote_connection"] && !extra_href["remote_access"])
- . = STATUS_UPDATE
+ // Status
+ var/turf/location = get_turf(src)
+ var/list/environment_data = list()
+ data["has_environment"] = FALSE
+ if(istype(location))
+ var/datum/gas_mixture/environment = location.return_air()
+ var/total = environment.total_moles
+ data["has_environment"] = !!total
+ if(total)
+ var/pressure = XGM_PRESSURE(environment)
+ environment_data += list(list("name" = "Pressure", "value" = pressure, "unit" = "kPa", "danger_level" = pressure_dangerlevel))
+ environment_data += list(list("name" = "Oxygen", "value" = environment.gas[GAS_OXYGEN] / total * 100, "unit" = "%", "danger_level" = oxygen_dangerlevel))
+ environment_data += list(list("name" = "Carbon Dioxide", "value" = environment.gas[GAS_CO2] / total * 100, "unit" = "%", "danger_level" = co2_dangerlevel))
+ environment_data += list(list("name" = "Phoron", "value" = environment.gas[GAS_PHORON] / total * 100, "unit" = "%", "danger_level" = phoron_dangerlevel))
+ environment_data += list(list("name" = "Hydrogen", "value" = environment.gas[GAS_HYDROGEN] / total * 100,"unit" = "%", "danger_level" = hydrogen_dangerlevel))
+ environment_data += list(list("name" = "Temperature", "value" = environment.temperature, "unit" = "K", "danger_level" = temperature_dangerlevel))
+ data["environment"] = environment_data
+ data["total_danger"] = danger_level
+ data["atmos_alarm"] = alarm_area.atmosalm
+ data["target_temperature"] = round(target_temperature - T0C, 0.1)
- return min(..(), .)
+ // Access
+ data["locked"] = locked && !issilicon(user)
+ data["shorted"] = shorted
+ data["rcon"] = rcon_setting
-/obj/machinery/alarm/Topic(href, href_list, var/datum/ui_state/state)
- if(..(href, href_list, state))
- return 1
+ // Mode
+ data["mode"] = mode
+ data["modes"] = list(
+ list("name" = "Filtering", "description" = "Scrubs out contaminants", "mode" = AALARM_MODE_SCRUBBING, "danger" = FALSE),
+ list("name" = "Replace Air", "description" = "Siphons out air while replacing", "mode" = AALARM_MODE_REPLACEMENT, "danger" = FALSE),
+ list("name" = "Panic Siphon", "description" = "Siphons air out of the compartment", "mode" = AALARM_MODE_PANIC, "danger" = TRUE),
+ list("name" = "Cycle", "description" = "Siphons air before replacing", "mode" = AALARM_MODE_CYCLE, "danger" = TRUE),
+ list("name" = "Fill", "description" = "Shuts off scrubbers and opens vents", "mode" = AALARM_MODE_FILL, "danger" = FALSE),
+ list("name" = "Off", "description" = "Shuts off vents and scrubbers", "mode" = AALARM_MODE_OFF, "danger" = FALSE)
+ )
- // hrefs that can always be called -walter0o
- if(href_list["rcon"])
- var/attempted_rcon_setting = text2num(href_list["rcon"])
+ // Vents
+ var/list/vents = list()
+ for(var/id_tag in alarm_area.air_vent_names)
+ var/list/info = alarm_area.air_vent_info[id_tag]
+ if(!info)
+ continue
+ vents += list(list(
+ "id_tag" = id_tag,
+ "long_name" = sanitize(alarm_area.air_vent_names[id_tag]),
+ "power" = info["power"],
+ "checks" = info["checks"],
+ "direction" = info["direction"],
+ "external" = info["external"]
+ ))
+ data["vents"] = vents
- switch(attempted_rcon_setting)
- if(RCON_NO)
- rcon_setting = RCON_NO
- if(RCON_AUTO)
- rcon_setting = RCON_AUTO
- if(RCON_YES)
- rcon_setting = RCON_YES
- return 1
+ // Scrubbers
+ var/list/scrubbers = list()
+ for(var/id_tag in alarm_area.air_scrub_names)
+ var/list/info = alarm_area.air_scrub_info[id_tag]
+ if(!info)
+ continue
+ var/list/scrubber = list(
+ "id_tag" = id_tag,
+ "long_name" = sanitize(alarm_area.air_scrub_names[id_tag]),
+ "power" = info["power"],
+ "scrubbing" = info["scrubbing"],
+ "panic" = info["panic"],
+ "filters" = list(
+ list("name" = "Oxygen", "command" = "o2_scrub", "val" = info["filter_o2"]),
+ list("name" = "Nitrogen", "command" = "n2_scrub", "val" = info["filter_n2"]),
+ list("name" = "Carbon Dioxide", "command" = "co2_scrub", "val" = info["filter_co2"]),
+ list("name" = "Phoron", "command" = "tox_scrub", "val" = info["filter_phoron"]),
+ list("name" = "Hydrogen", "command" = "h2_scrub", "val" = info["filter_h"]),
+ list("name" = "Nitrous Oxide", "command" = "n2o_scrub", "val" = info["filter_n2o"])
+ )
+ )
+ scrubbers += list(scrubber)
+ data["scrubbers"] = scrubbers
- if(href_list["temperature"])
- var/list/selected = TLV["temperature"]
- var/max_temperature = min(selected[3] - T0C, MAX_TEMPERATURE)
- var/min_temperature = max(selected[2] - T0C, MIN_TEMPERATURE)
- var/input_temperature = tgui_input_number(usr, "What temperature would you like the system to mantain?", "Thermostat Controls", target_temperature - T0C, max_temperature, min_temperature)
- if(isnum(input_temperature))
- var/temp = clamp(input_temperature, min_temperature, max_temperature)
- if(input_temperature > max_temperature || input_temperature < min_temperature)
- to_chat(usr, "Temperature must be between [min_temperature]C and [max_temperature]C. Target temperature clamped to [temp]C.")
- target_temperature = clamp(input_temperature + T0C, selected[2], selected[3])
- else
- to_chat(usr, "Error, input not recognised. Temperature unchanged.")
+ // Thresholds
+ var/list/thresholds = list()
+ var/list/gas_names = list(
+ GAS_OXYGEN = "O\u2082",
+ GAS_CO2 = "CO\u2082",
+ GAS_PHORON = "Phoron",
+ GAS_HYDROGEN = "Hydrogen",
+ "other" = "Other"
+ )
+ for(var/g in gas_names)
+ var/list/tlv = TLV[g]
+ var/list/settings = list()
+ for(var/i = 1, i <= 4, i++)
+ settings += list(list("env" = g, "val" = i, "selected" = tlv[i]))
+ thresholds += list(list("name" = gas_names[g], "settings" = settings))
- return 1
+ var/list/pressure_settings = list()
+ for(var/i = 1, i <= 4, i++)
+ pressure_settings += list(list("env" = "pressure", "val" = i, "selected" = TLV["pressure"][i]))
+ thresholds += list(list("name" = "Pressure", "settings" = pressure_settings))
- // hrefs that need the AA unlocked -walter0o
- var/extra_href = QDELETED(state) ? list() : state.href_list(usr)
- if(!(locked && !extra_href["remote_connection"]) || extra_href["remote_access"] || issilicon(usr))
- if(href_list["command"])
- var/device_id = href_list["id_tag"]
- switch(href_list["command"])
+ var/list/temp_settings = list()
+ for(var/i = 1, i <= 4, i++)
+ temp_settings += list(list("env" = "temperature", "val" = i, "selected" = TLV["temperature"][i]))
+ thresholds += list(list("name" = "Temperature", "settings" = temp_settings))
+
+ data["thresholds"] = thresholds
+
+ return data
+
+/obj/machinery/alarm/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
+ . = ..()
+ if(.)
+ return
+
+ if(shorted)
+ return
+
+ // Actions available without unlocking
+ switch(action)
+ if("rcon")
+ var/new_rcon = text2num(params["value"])
+ switch(new_rcon)
+ if(RCON_NO) rcon_setting = RCON_NO
+ if(RCON_AUTO) rcon_setting = RCON_AUTO
+ if(RCON_YES) rcon_setting = RCON_YES
+ return TRUE
+
+ if("temperature")
+ var/list/tlv = TLV["temperature"]
+ var/max_temperature = min(tlv[3] - T0C, MAX_TEMPERATURE)
+ var/min_temperature = max(tlv[2] - T0C, MIN_TEMPERATURE)
+ var/input = tgui_input_number(usr, "What temperature would you like the system to maintain?", "Thermostat Controls", target_temperature - T0C, max_temperature, min_temperature)
+ if(isnum(input))
+ var/temp = clamp(input, min_temperature, max_temperature)
+ if(input > max_temperature || input < min_temperature)
+ to_chat(usr, "Temperature must be between [min_temperature]C and [max_temperature]C. Target temperature clamped to [temp]C.")
+ target_temperature = clamp(input + T0C, tlv[2], tlv[3])
+ else
+ to_chat(usr, "Error, input not recognised. Temperature unchanged.")
+ return TRUE
+
+ // Actions that require the alarm to be unlocked
+ if(locked && !issilicon(usr))
+ return
+
+ switch(action)
+ if("command")
+ var/device_id = params["id_tag"]
+ var/command = params["command"]
+ var/list/signal = list()
+ switch(command)
if("set_external_pressure")
- var/input_pressure = tgui_input_number(usr, "What pressure you like the system to mantain?", "Pressure Controls")
+ var/input_pressure = tgui_input_number(usr, "What pressure would you like the system to maintain?", "Pressure Controls")
if(isnum(input_pressure))
- send_signal(device_id, list(href_list["command"] = input_pressure))
- return 1
-
+ signal[command] = input_pressure
+ send_signal(device_id, signal)
if("reset_external_pressure")
- send_signal(device_id, list(href_list["command"] = ONE_ATMOSPHERE))
- return 1
-
- if( "power",
+ signal[command] = ONE_ATMOSPHERE
+ send_signal(device_id, signal)
+ if("power",
"adjust_external_pressure",
"checks",
"o2_scrub",
@@ -925,91 +892,75 @@ pixel_x = 10;
"n2o_scrub",
"panic_siphon",
"scrubbing")
-
- send_signal(device_id, list(href_list["command"] = text2num(href_list["val"]) ) )
- return 1
-
+ signal[command] = text2num(params["val"])
+ send_signal(device_id, signal)
if("set_threshold")
- var/env = href_list["env"]
- var/threshold = text2num(href_list["var"])
+ var/env = params["env"]
+ var/threshold = text2num(params["threshold"])
var/list/selected = TLV[env]
- var/list/thresholds = list("lower bound", "low warning", "high warning", "upper bound")
- var/newval = tgui_input_number(usr, "Enter [thresholds[threshold]] for [env].", "Alarm Triggers", selected[threshold])
- if (isnull(newval))
- return 1
- if (newval<0)
+ var/list/threshold_names = list("lower bound", "low warning", "high warning", "upper bound")
+ var/newval = tgui_input_number(usr, "Enter [threshold_names[threshold]] for [env]. Use -1 to disable.", "Alarm Triggers", selected[threshold], min_value = -1)
+ if(isnull(newval))
+ return TRUE
+ if(newval < 0)
selected[threshold] = -1.0
- else if (env=="temperature" && newval>5000)
+ else if(env == "temperature" && newval > 5000)
selected[threshold] = 5000
- else if (env=="pressure" && newval>50*ONE_ATMOSPHERE)
- selected[threshold] = 50*ONE_ATMOSPHERE
- else if (env!="temperature" && env!="pressure" && newval>200)
+ else if(env == "pressure" && newval > 50 * ONE_ATMOSPHERE)
+ selected[threshold] = 50 * ONE_ATMOSPHERE
+ else if(env != "temperature" && env != "pressure" && newval > 200)
selected[threshold] = 200
else
- newval = round(newval,0.01)
- selected[threshold] = newval
-
- switch (threshold)
- if (1)
- if(selected[1] > selected[2])
- selected[2] = selected[1]
- if(selected[1] > selected[3])
- selected[3] = selected[1]
- if(selected[1] > selected[4])
- selected[4] = selected[1]
- if (2)
- if(selected[1] > selected[2])
- selected[1] = selected[2]
- if(selected[2] > selected[3])
- selected[3] = selected[2]
- if(selected[2] > selected[4])
- selected[4] = selected[2]
- if (3)
- if(selected[1] > selected[3])
- selected[1] = selected[3]
- if(selected[2] > selected[3])
- selected[2] = selected[3]
- if(selected[3] > selected[4])
- selected[4] = selected[3]
- if (4)
- if(selected[1] > selected[4])
- selected[1] = selected[4]
- if(selected[2] > selected[4])
- selected[2] = selected[4]
- if(selected[3] > selected[4])
- selected[3] = selected[4]
-
+ selected[threshold] = round(newval, 0.01)
+ // Clamp adjacent thresholds to keep ordering valid
+ switch(threshold)
+ if(1)
+ if(selected[1] > selected[2]) selected[2] = selected[1]
+ if(selected[1] > selected[3]) selected[3] = selected[1]
+ if(selected[1] > selected[4]) selected[4] = selected[1]
+ if(2)
+ if(selected[1] > selected[2]) selected[1] = selected[2]
+ if(selected[2] > selected[3]) selected[3] = selected[2]
+ if(selected[2] > selected[4]) selected[4] = selected[2]
+ if(3)
+ if(selected[1] > selected[3]) selected[1] = selected[3]
+ if(selected[2] > selected[3]) selected[2] = selected[3]
+ if(selected[3] > selected[4]) selected[4] = selected[3]
+ if(4)
+ if(selected[1] > selected[4]) selected[1] = selected[4]
+ if(selected[2] > selected[4]) selected[2] = selected[4]
+ if(selected[3] > selected[4]) selected[3] = selected[4]
apply_mode()
- return 1
+ return TRUE
- if(href_list["screen"])
- screen = text2num(href_list["screen"])
- return 1
+ if("atmos_unlock")
+ switch(params["value"])
+ if("0") alarm_area.air_doors_close()
+ if("1") alarm_area.air_doors_open()
+ return TRUE
- if(href_list["atmos_unlock"])
- switch(href_list["atmos_unlock"])
- if("0")
- alarm_area.air_doors_close()
- if("1")
- alarm_area.air_doors_open()
- return 1
-
- if(href_list["atmos_alarm"])
- if (alarm_area.atmosalert(2, src))
+ if("atmos_alarm")
+ if(alarm_area.atmosalert(2, src))
apply_danger_level(2)
update_icon()
- return 1
+ return TRUE
- if(href_list["atmos_reset"])
- if (alarm_area.atmosalert(0, src))
- apply_danger_level(0)
- update_icon()
- return 1
+ if("atmos_reset")
+ alarm_reset()
+ return TRUE
- if(href_list["mode"])
- mode = text2num(href_list["mode"])
+ if("mode")
+ mode = text2num(params["mode"])
apply_mode()
- return 1
+ return TRUE
+
+/**
+ * Resets the area atmospheric alarm. Called by ui_act and directly by atmos_alert computers.
+ */
+/obj/machinery/alarm/proc/alarm_reset()
+ if(alarm_area.atmosalert(0, src))
+ apply_danger_level(0)
+ update_icon()
/obj/machinery/alarm/attackby(obj/item/attacking_item, mob/user)
if(!istype(attacking_item, /obj/item/forensics))
@@ -1120,11 +1071,6 @@ Just a object used in constructing air alarms
#undef AALARM_MODE_CYCLE
#undef AALARM_MODE_FILL
#undef AALARM_MODE_OFF
-#undef AALARM_SCREEN_MAIN
-#undef AALARM_SCREEN_VENT
-#undef AALARM_SCREEN_SCRUB
-#undef AALARM_SCREEN_MODE
-#undef AALARM_SCREEN_SENSORS
#undef AALARM_REPORT_TIMEOUT
#undef MAX_TEMPERATURE
#undef MIN_TEMPERATURE
diff --git a/code/game/machinery/computer/atmos_alert.dm b/code/game/machinery/computer/atmos_alert.dm
index 188610d84c3..d1c3caedb9a 100644
--- a/code/game/machinery/computer/atmos_alert.dm
+++ b/code/game/machinery/computer/atmos_alert.dm
@@ -25,26 +25,26 @@ GLOBAL_LIST_EMPTY(minor_air_alarms)
/obj/machinery/computer/atmos_alert/attack_hand(mob/user)
ui_interact(user)
-/obj/machinery/computer/atmos_alert/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
- var/data[0]
- var/major_alarms[0]
- var/minor_alarms[0]
+/obj/machinery/computer/atmos_alert/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
+ if(!ui)
+ ui = new(user, src, "AtmosAlertComputer")
+ ui.open()
+
+/obj/machinery/computer/atmos_alert/ui_data(mob/user)
+ var/list/data = list()
+ var/list/major_alarms = list()
+ var/list/minor_alarms = list()
for(var/datum/alarm/alarm in GLOB.atmosphere_alarm.major_alarms())
- major_alarms[++major_alarms.len] = list("name" = sanitize(alarm.alarm_name()), "ref" = "[REF(alarm)]")
+ major_alarms += list(list("name" = sanitize(alarm.alarm_name()), "ref" = REF(alarm)))
for(var/datum/alarm/alarm in GLOB.atmosphere_alarm.minor_alarms())
- minor_alarms[++minor_alarms.len] = list("name" = sanitize(alarm.alarm_name()), "ref" = "[REF(alarm)]")
+ minor_alarms += list(list("name" = sanitize(alarm.alarm_name()), "ref" = REF(alarm)))
data["priority_alarms"] = major_alarms
data["minor_alarms"] = minor_alarms
-
- ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open)
- if(!ui)
- ui = new(user, src, ui_key, "atmos_alert.tmpl", src.name, 500, 500)
- ui.set_initial_data(data)
- ui.open()
- ui.set_auto_update(1)
+ return data
/obj/machinery/computer/atmos_alert/update_icon()
if(!(stat & (NOPOWER|BROKEN)))
@@ -59,26 +59,16 @@ GLOBAL_LIST_EMPTY(minor_air_alarms)
icon_screen = initial(icon_screen)
..()
-/obj/machinery/computer/atmos_alert/Topic(href, href_list)
- if(..())
- return 1
+/obj/machinery/computer/atmos_alert/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
+ . = ..()
+ if(.)
+ return
- if(href_list["clear_alarm"])
- var/datum/alarm/alarm = locate(href_list["clear_alarm"]) in GLOB.atmosphere_alarm.alarms
+ if(action == "clear_alarm")
+ var/datum/alarm/alarm = locate(params["ref"]) in GLOB.atmosphere_alarm.alarms
if(alarm)
for(var/datum/alarm_source/alarm_source in alarm.sources)
var/obj/machinery/alarm/air_alarm = alarm_source.source
if(istype(air_alarm))
- var/list/new_ref = list("atmos_reset" = 1)
- air_alarm.Topic(href, new_ref, state = GLOB.air_alarm_topic)
- return 1
-
-
-GLOBAL_DATUM_INIT(air_alarm_topic, /datum/ui_state/air_alarm_topic, new())
-
-/datum/ui_state/air_alarm_topic/href_list(var/mob/user)
- var/list/extra_href = list()
- extra_href["remote_connection"] = 1
- extra_href["remote_access"] = 1
-
- return extra_href
+ air_alarm.alarm_reset()
+ return TRUE
diff --git a/code/modules/atmospherics/components/binary_devices/oxyregenerator.dm b/code/modules/atmospherics/components/binary_devices/oxyregenerator.dm
index 28e30599c21..df274963386 100644
--- a/code/modules/atmospherics/components/binary_devices/oxyregenerator.dm
+++ b/code/modules/atmospherics/components/binary_devices/oxyregenerator.dm
@@ -157,8 +157,14 @@
else
icon_state = "[use_power ? "on" : "off"]"
-/obj/machinery/atmospherics/binary/oxyregenerator/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 1)
- var/data[0]
+/obj/machinery/atmospherics/binary/oxyregenerator/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
+ if(!ui)
+ ui = new(user, src, "OxyRegenerator")
+ ui.open()
+
+/obj/machinery/atmospherics/binary/oxyregenerator/ui_data(mob/user)
+ var/list/data = list()
data["on"] = use_power ? 1 : 0
data["powerSetting"] = power_setting
data["gasProcessed"] = last_flow_rate
@@ -167,27 +173,23 @@
data["tankPressure"] = round(XGM_PRESSURE(inner_tank))
data["targetPressure"] = round(target_pressure)
data["phase"] = phase
- if (inner_tank.total_moles > 0)
- data["co2"] = round(100 * inner_tank.gas[GAS_CO2]/inner_tank.total_moles)
- data["o2"] = round(100 * inner_tank.gas[GAS_OXYGEN]/inner_tank.total_moles)
+ if(inner_tank.total_moles > 0)
+ data["co2"] = round(100 * inner_tank.gas[GAS_CO2] / inner_tank.total_moles)
+ data["o2"] = round(100 * inner_tank.gas[GAS_OXYGEN] / inner_tank.total_moles)
else
data["co2"] = 0
data["o2"] = 0
- // update the ui if it exists, returns null if no ui is passed/found
- ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open)
- if(!ui)
- ui = new(user, src, ui_key, "oxyregenerator.tmpl", "Oxygen Regeneration System", 440, 300)
- ui.set_initial_data(data)
- ui.open()
- ui.set_auto_update(1)
+ return data
-/obj/machinery/atmospherics/binary/oxyregenerator/Topic(href, href_list)
- if(..())
- return 1
- if(href_list["power"])
- update_use_power(!use_power)
- update_icon()
- return 1
- if(href_list["setPower"]) //setting power to 0 is redundant anyways
- power_setting = clamp(text2num(href_list["setPower"]), 1, 5)
- return 1
+/obj/machinery/atmospherics/binary/oxyregenerator/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
+ . = ..()
+ if(.)
+ return
+ switch(action)
+ if("power")
+ update_use_power(!use_power)
+ update_icon()
+ return TRUE
+ if("set_power")
+ power_setting = clamp(text2num(params["value"]), 1, 5)
+ return TRUE
diff --git a/code/modules/atmospherics/components/binary_devices/passive_gate.dm b/code/modules/atmospherics/components/binary_devices/passive_gate.dm
index e52394bf0c1..8319749435b 100644
--- a/code/modules/atmospherics/components/binary_devices/passive_gate.dm
+++ b/code/modules/atmospherics/components/binary_devices/passive_gate.dm
@@ -289,40 +289,6 @@
update_icon()
-/obj/machinery/atmospherics/binary/passive_gate/Topic(href,href_list)
- if(..()) return 1
-
- if(href_list["toggle_valve"])
- unlocked = !unlocked
-
- if(href_list["regulate_mode"])
- switch(href_list["regulate_mode"])
- if ("off") regulate_mode = REGULATE_NONE
- if ("input") regulate_mode = REGULATE_INPUT
- if ("output") regulate_mode = REGULATE_OUTPUT
-
- switch(href_list["set_press"])
- if ("min")
- target_pressure = 0
- if ("max")
- target_pressure = max_pressure_setting
- if ("set")
- var/new_pressure = input(usr,"Enter new output pressure (0-[max_pressure_setting]kPa)","Pressure Control",src.target_pressure) as num
- src.target_pressure = between(0, new_pressure, max_pressure_setting)
-
- switch(href_list["set_flow_rate"])
- if ("min")
- set_flow_rate = 0
- if ("max")
- set_flow_rate = air1.volume
- if ("set")
- var/new_flow_rate = input(usr,"Enter new flow rate limit (0-[air1.volume]kPa)","Flow Rate Control",src.set_flow_rate) as num
- src.set_flow_rate = between(0, new_flow_rate, air1.volume)
-
- usr.set_machine(src) //Is this even needed with NanoUI?
- src.update_icon()
- src.add_fingerprint(usr)
- return
/obj/machinery/atmospherics/binary/passive_gate/attackby(obj/item/attacking_item, mob/user)
if (attacking_item.tool_behaviour != TOOL_WRENCH)
diff --git a/code/modules/atmospherics/components/binary_devices/pump.dm b/code/modules/atmospherics/components/binary_devices/pump.dm
index 36b184da4db..1fcc52be4d2 100644
--- a/code/modules/atmospherics/components/binary_devices/pump.dm
+++ b/code/modules/atmospherics/components/binary_devices/pump.dm
@@ -265,26 +265,6 @@ Thus, the two variables affect pump operation are set in New():
ui_interact(user)
return
-/obj/machinery/atmospherics/binary/pump/Topic(href,href_list)
- if(..()) return 1
-
- if(href_list["power"])
- update_use_power(!use_power)
-
- switch(href_list["set_press"])
- if ("min")
- target_pressure = 0
- if ("max")
- target_pressure = max_pressure_setting
- if ("set")
- var/new_pressure = input(usr,"Enter new output pressure (0-[max_pressure_setting]kPa)","Pressure control",src.target_pressure) as num
- src.target_pressure = between(0, new_pressure, max_pressure_setting)
-
- usr.set_machine(src)
- src.add_fingerprint(usr)
-
- src.update_icon()
-
/obj/machinery/atmospherics/binary/pump/power_change()
var/old_stat = stat
..()
diff --git a/code/modules/atmospherics/components/omni_devices/filter.dm b/code/modules/atmospherics/components/omni_devices/filter.dm
index 04795c08c60..2e641fa39b3 100644
--- a/code/modules/atmospherics/components/omni_devices/filter.dm
+++ b/code/modules/atmospherics/components/omni_devices/filter.dm
@@ -91,59 +91,40 @@
return 1
-/obj/machinery/atmospherics/omni/filter/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
- user.set_machine(src)
-
- var/list/data = new()
-
- data = build_uidata()
-
- ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open)
-
- if (!ui)
- ui = new(user, src, ui_key, "omni_filter.tmpl", "Omni Filter Control", 470, 330)
- ui.set_initial_data(data)
-
+/obj/machinery/atmospherics/omni/filter/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
+ if(!ui)
+ ui = new(user, src, "OmniFilter")
ui.open()
-/obj/machinery/atmospherics/omni/filter/proc/build_uidata()
- var/list/data = new()
-
+/obj/machinery/atmospherics/omni/filter/ui_data(mob/user)
+ var/list/data = list()
data["power"] = use_power
data["config"] = configuring
data["last_power_draw"] = last_power_draw
data["max_power_draw"] = power_rating
- var/portData[0]
+ var/list/port_data = list()
for(var/datum/omni_port/P in ports)
- if(!configuring && P.mode == 0)
+ if(!configuring && P.mode == ATM_NONE)
continue
+ var/is_input = P.mode == ATM_INPUT
+ var/is_output = P.mode == ATM_OUTPUT
+ var/is_filter = !is_input && !is_output
+ port_data += list(list(
+ "dir" = dir_name(P.dir, capitalize = 1),
+ "input" = is_input,
+ "output" = is_output,
+ "filter" = is_filter,
+ "f_type" = is_filter ? mode_send_switch(P.mode) : null
+ ))
+ data["ports"] = port_data
- var/input = 0
- var/output = 0
- var/filter = 1
- var/f_type = null
- switch(P.mode)
- if(ATM_INPUT)
- input = 1
- filter = 0
- if(ATM_OUTPUT)
- output = 1
- filter = 0
- if(ATM_O2 to ATM_H2O)
- f_type = mode_send_switch(P.mode)
+ data["set_flow_rate"] = set_flow_rate
+ data["max_flow_rate"] = max_flow_rate
+ data["last_flow_rate"] = last_flow_rate
- portData[++portData.len] = list("dir" = dir_name(P.dir, capitalize = 1), \
- "input" = input, \
- "output" = output, \
- "filter" = filter, \
- "f_type" = f_type)
-
- if(portData.len)
- data["ports"] = portData
- if(output)
- data["set_flow_rate"] = round(set_flow_rate*10) //because nanoui can't handle rounded decimals.
- data["last_flow_rate"] = round(last_flow_rate*10)
+ data["filter_types"] = list("None", "Oxygen", "Nitrogen", "Carbon Dioxide", "Phoron", "Nitrous Oxide", "Hydrogen", "Deuterium", "Tritium", "Helium", "Helium-3", "Sulfur Dioxide", "Nitrogen Dioxide", "Chlorine", "Water Vapor")
return data
@@ -180,34 +161,49 @@
else
return null
-/obj/machinery/atmospherics/omni/filter/Topic(href, href_list)
- if(..()) return 1
- switch(href_list["command"])
+/obj/machinery/atmospherics/omni/filter/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
+ . = ..()
+ if(.)
+ return
+
+ switch(action)
if("power")
if(!configuring)
update_use_power(!use_power)
else
update_use_power(POWER_USE_OFF)
+ update_icon()
+ return TRUE
+
if("configure")
configuring = !configuring
if(configuring)
update_use_power(POWER_USE_OFF)
+ update_icon()
+ return TRUE
- //only allows config changes when in configuring mode ~otherwise you'll get weird pressure stuff going on
- if(configuring && !use_power)
- switch(href_list["command"])
- if("set_flow_rate")
- var/new_flow_rate = input(usr,"Enter new flow rate limit (0-[max_flow_rate]L/s)","Flow Rate Control",set_flow_rate) as num
- set_flow_rate = between(0, new_flow_rate, max_flow_rate)
- if("switch_mode")
- switch_mode(dir_flag(href_list["dir"]), mode_return_switch(href_list["mode"]))
- if("switch_filter")
- var/new_filter = input(usr,"Select filter mode:","Change filter",href_list["mode"]) in list("None", "Oxygen", "Nitrogen", "Carbon Dioxide", "Phoron", "Nitrous Oxide", "Hydrogen", "Deuterium", "Tritium", "Helium", "Helium-3", "Sulfur Dioxide", "Nitrogen Dioxide", "Chlorine", "Water Vapor")
- switch_filter(dir_flag(href_list["dir"]), mode_return_switch(new_filter))
+ if(!configuring || use_power)
+ return
- update_icon()
- SSnanoui.update_uis(src)
- return
+ switch(action)
+ if("set_flow_rate")
+ var/new_rate = tgui_input_number(usr, "Enter new flow rate limit (0-[max_flow_rate] L/s)", "Flow Rate Control", set_flow_rate, max_flow_rate, 0)
+ if(isnum(new_rate))
+ set_flow_rate = between(0, new_rate, max_flow_rate)
+ return TRUE
+
+ if("switch_mode")
+ switch_mode(dir_flag(params["dir"]), mode_return_switch(params["mode"]))
+ update_icon()
+ return TRUE
+
+ if("switch_filter")
+ var/list/filter_types = list("None", "Oxygen", "Nitrogen", "Carbon Dioxide", "Phoron", "Nitrous Oxide", "Hydrogen", "Deuterium", "Tritium", "Helium", "Helium-3", "Sulfur Dioxide", "Nitrogen Dioxide", "Chlorine", "Water Vapor")
+ var/new_filter = tgui_input_list(usr, "Select filter gas:", "Change Filter", filter_types)
+ if(new_filter)
+ switch_filter(dir_flag(params["dir"]), mode_return_switch(new_filter))
+ update_icon()
+ return TRUE
/obj/machinery/atmospherics/omni/filter/proc/mode_return_switch(var/mode)
switch(mode)
diff --git a/code/modules/atmospherics/components/omni_devices/mixer.dm b/code/modules/atmospherics/components/omni_devices/mixer.dm
index 215c8ad3971..16dcd6db096 100644
--- a/code/modules/atmospherics/components/omni_devices/mixer.dm
+++ b/code/modules/atmospherics/components/omni_devices/mixer.dm
@@ -129,86 +129,103 @@
return 1
-/obj/machinery/atmospherics/omni/mixer/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
- user.set_machine(src)
-
- var/list/data = new()
-
- data = build_uidata()
-
- ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open)
-
- if (!ui)
- ui = new(user, src, ui_key, "omni_mixer.tmpl", "Omni Mixer Control", 470, 330)
- ui.set_initial_data(data)
-
+/obj/machinery/atmospherics/omni/mixer/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
+ if(!ui)
+ ui = new(user, src, "OmniMixer")
ui.open()
-/obj/machinery/atmospherics/omni/mixer/proc/build_uidata()
- var/list/data = new()
-
+/obj/machinery/atmospherics/omni/mixer/ui_data(mob/user)
+ var/list/data = list()
data["power"] = use_power
data["config"] = configuring
data["last_power_draw"] = last_power_draw
data["max_power_draw"] = power_rating
- var/portData[0]
+ var/list/port_data = list()
for(var/datum/omni_port/P in ports)
- if(!configuring && P.mode == 0)
+ if(!configuring && P.mode == ATM_NONE)
continue
+ port_data += list(list(
+ "dir" = dir_name(P.dir, capitalize = 1),
+ "concentration" = P.concentration,
+ "input" = P.mode == ATM_INPUT,
+ "output" = P.mode == ATM_OUTPUT,
+ "con_lock" = P.con_lock
+ ))
+ data["ports"] = port_data
- var/input = 0
- var/output = 0
- switch(P.mode)
- if(ATM_INPUT)
- input = 1
- if(ATM_OUTPUT)
- output = 1
-
- portData[++portData.len] = list("dir" = dir_name(P.dir, capitalize = 1), \
- "concentration" = P.concentration, \
- "input" = input, \
- "output" = output, \
- "con_lock" = P.con_lock)
-
- if(portData.len)
- data["ports"] = portData
- if(output)
- data["set_flow_rate"] = round(set_flow_rate*10) //because nanoui can't handle rounded decimals.
- data["last_flow_rate"] = round(last_flow_rate*10)
+ data["set_flow_rate"] = set_flow_rate
+ data["max_flow_rate"] = max_flow_rate
+ data["last_flow_rate"] = last_flow_rate
return data
-/obj/machinery/atmospherics/omni/mixer/Topic(href, href_list)
- if(..()) return 1
+/obj/machinery/atmospherics/omni/mixer/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
+ . = ..()
+ if(.)
+ return
- switch(href_list["command"])
+ switch(action)
if("power")
if(!configuring)
update_use_power(!use_power)
else
update_use_power(POWER_USE_OFF)
+ update_icon()
+ return TRUE
+
if("configure")
configuring = !configuring
if(configuring)
update_use_power(POWER_USE_OFF)
+ update_icon()
+ return TRUE
- //only allows config changes when in configuring mode ~otherwise you'll get weird pressure stuff going on
- if(configuring && !use_power)
- switch(href_list["command"])
- if("set_flow_rate")
- var/new_flow_rate = input(usr,"Enter new flow rate limit (0-[max_flow_rate]L/s)","Flow Rate Control",set_flow_rate) as num
- set_flow_rate = between(0, new_flow_rate, max_flow_rate)
- if("switch_mode")
- switch_mode(dir_flag(href_list["dir"]), href_list["mode"])
- if("switch_con")
- change_concentration(dir_flag(href_list["dir"]))
- if("switch_conlock")
- con_lock(dir_flag(href_list["dir"]))
+ if(!configuring || use_power)
+ return
- update_icon()
- SSnanoui.update_uis(src)
- return
+ switch(action)
+ if("set_flow_rate")
+ var/new_rate = tgui_input_number(usr, "Enter new flow rate limit (0-[max_flow_rate] L/s)", "Flow Rate Control", set_flow_rate, max_flow_rate, 0)
+ if(isnum(new_rate))
+ set_flow_rate = between(0, new_rate, max_flow_rate)
+ return TRUE
+
+ if("switch_mode")
+ switch_mode(dir_flag(params["dir"]), params["mode"])
+ return TRUE
+
+ if("set_concentration")
+ var/port_dir = dir_flag(params["dir"])
+ var/old_con = 0
+ var/non_locked = 0
+ var/remain_con = 1
+ for(var/datum/omni_port/P in inputs)
+ if(P.dir == port_dir)
+ old_con = P.concentration
+ else if(!P.con_lock)
+ non_locked++
+ else
+ remain_con -= P.concentration
+ if(non_locked < 1)
+ return TRUE
+ var/new_con = tgui_input_number(usr, "Enter a new concentration (0-[round(remain_con * 100, 0.5)])%", "Concentration Control", min(remain_con, old_con) * 100, remain_con * 100, 0) / 100
+ if(isnull(new_con))
+ return TRUE
+ new_con = between(0, new_con, remain_con)
+ remain_con = max(0, remain_con - new_con) / max(1, non_locked)
+ for(var/datum/omni_port/P in inputs)
+ if(P.dir == port_dir)
+ P.concentration = new_con
+ else if(!P.con_lock)
+ P.concentration = remain_con
+ rebuild_mixing_inputs()
+ return TRUE
+
+ if("toggle_con_lock")
+ con_lock(dir_flag(params["dir"]))
+ return TRUE
/obj/machinery/atmospherics/omni/mixer/proc/switch_mode(var/port = NORTH, var/mode = ATM_NONE)
if(mode != ATM_INPUT && mode != ATM_OUTPUT)
diff --git a/code/modules/modular_computers/file_system/programs/engineering/atmoscontrol.dm b/code/modules/modular_computers/file_system/programs/engineering/atmoscontrol.dm
index 24ed341eb75..1f4e7d009f3 100644
--- a/code/modules/modular_computers/file_system/programs/engineering/atmoscontrol.dm
+++ b/code/modules/modular_computers/file_system/programs/engineering/atmoscontrol.dm
@@ -49,8 +49,7 @@
if("alarm")
var/obj/machinery/alarm/alarm = locate(params["alarm"]) in (monitored_alarms.len ? monitored_alarms : SSmachinery.processing)
if(alarm)
- var/datum/ui_state/TS = generate_state(alarm)
- alarm.ui_interact(usr, state = TS) //what the fuck?
+ alarm.ui_interact(usr)
return TRUE
// Manually clear and repopulate the alarm list.
if("refresh")
@@ -79,29 +78,3 @@
return data
-/datum/computer_file/program/atmos_control/proc/generate_state(air_alarm)
- var/datum/ui_state/air_alarm/state = new()
- state.atmos_control = src
- state.air_alarm = air_alarm
- return state
-
-/datum/ui_state/air_alarm
- var/datum/computer_file/program/atmos_control/atmos_control
- var/obj/machinery/alarm/air_alarm
-
-/datum/ui_state/air_alarm/can_use_topic(var/src_object, var/mob/user)
- var/obj/item/card/id/I = user.GetIdCard()
- if(has_access(req_one_access = atmos_control.required_access_run, accesses = I.GetAccess()))
- return STATUS_INTERACTIVE
- return STATUS_UPDATE
-
-/datum/ui_state/air_alarm/href_list(var/mob/user)
- var/list/extra_href = list()
- extra_href["remote_connection"] = TRUE
- extra_href["remote_access"] = can_access(user)
-
- return extra_href
-
-/datum/ui_state/air_alarm/proc/can_access(var/mob/user)
- var/obj/item/card/id/I = user.GetIdCard()
- return user && (isAI(user) || has_access(req_one_access = atmos_control.required_access_run, accesses = I.GetAccess()) || atmos_control.computer_emagged || air_alarm.rcon_setting == RCON_YES || (air_alarm.alarm_area.atmosalm && air_alarm.rcon_setting == RCON_AUTO))
diff --git a/html/changelogs/arrow768-2026-03-26-air-alarm-tgui.yml b/html/changelogs/arrow768-2026-03-26-air-alarm-tgui.yml
new file mode 100644
index 00000000000..d0e56721b87
--- /dev/null
+++ b/html/changelogs/arrow768-2026-03-26-air-alarm-tgui.yml
@@ -0,0 +1,4 @@
+author: arrow768
+delete-after: True
+changes:
+ - refactor: "Refactored the air alarm, atmospheric alert computer, oxygen regenerator, omni gas mixer, and omni gas filter from NanoUI to TGUI."
diff --git a/tgui/packages/tgui/interfaces/AirAlarm.tsx b/tgui/packages/tgui/interfaces/AirAlarm.tsx
new file mode 100644
index 00000000000..5e467c148e3
--- /dev/null
+++ b/tgui/packages/tgui/interfaces/AirAlarm.tsx
@@ -0,0 +1,481 @@
+import { BooleanLike } from '../../common/react';
+import { useBackend, useLocalState } from '../backend';
+import {
+ Box,
+ Button,
+ LabeledList,
+ NoticeBox,
+ Section,
+ Table,
+ Tabs,
+} from '../components';
+import { Window } from '../layouts';
+
+// ---- Types ----
+
+type AirAlarmData = {
+ // Status
+ has_environment: BooleanLike;
+ environment: EnvironmentEntry[];
+ total_danger: number;
+ atmos_alarm: BooleanLike;
+ target_temperature: number;
+ // Access
+ locked: BooleanLike;
+ shorted: BooleanLike;
+ rcon: number;
+ // Mode
+ mode: number;
+ modes: ModeEntry[];
+ // Devices
+ vents: VentEntry[];
+ scrubbers: ScrubberEntry[];
+ // Thresholds
+ thresholds: ThresholdEntry[];
+};
+
+type EnvironmentEntry = {
+ name: string;
+ value: number;
+ unit: string;
+ danger_level: number;
+};
+
+type ModeEntry = {
+ name: string;
+ description: string;
+ mode: number;
+ danger: BooleanLike;
+};
+
+type VentEntry = {
+ id_tag: string;
+ long_name: string;
+ power: BooleanLike;
+ checks: number;
+ direction: string;
+ external: number;
+};
+
+type ScrubberEntry = {
+ id_tag: string;
+ long_name: string;
+ power: BooleanLike;
+ scrubbing: BooleanLike;
+ panic: BooleanLike;
+ filters: FilterEntry[];
+};
+
+type FilterEntry = {
+ name: string;
+ command: string;
+ val: BooleanLike;
+};
+
+type ThresholdEntry = {
+ name: string;
+ settings: ThresholdSetting[];
+};
+
+type ThresholdSetting = {
+ env: string;
+ val: number;
+ selected: number;
+};
+
+// ---- Helpers ----
+
+const DANGER_COLOR: Record = {
+ 0: 'good',
+ 1: 'average',
+ 2: 'bad',
+};
+
+const DANGER_LABEL: Record = {
+ 0: 'Optimal',
+ 1: 'Caution',
+ 2: 'DANGER: Internals Required',
+};
+
+const RCON_NO = 1;
+const RCON_AUTO = 2;
+const RCON_YES = 3;
+
+// ---- Root component ----
+
+export const AirAlarm = (props, context) => {
+ const { act, data } = useBackend(context);
+ const [tab, setTab] = useLocalState(context, 'tab', 'status');
+
+ const tabs = [
+ { id: 'status', label: 'Status' },
+ { id: 'vents', label: 'Vents' },
+ { id: 'scrubbers', label: 'Scrubbers' },
+ { id: 'mode', label: 'Mode' },
+ { id: 'sensors', label: 'Sensors' },
+ ];
+
+ return (
+
+
+
+ {data.locked ? (
+ Swipe ID card to unlock interface.
+ ) : (
+
+ {tabs.map((t) => (
+ setTab(t.id)}
+ >
+ {t.label}
+
+ ))}
+
+ }
+ >
+ {tab === 'status' && }
+ {tab === 'vents' && }
+ {tab === 'scrubbers' && }
+ {tab === 'mode' && }
+ {tab === 'sensors' && }
+
+ )}
+
+
+ );
+};
+
+// ---- Status section (always visible) ----
+
+const StatusSection = (props, context) => {
+ const { act, data } = useBackend(context);
+
+ return (
+
+ {data.has_environment ? (
+
+ {data.environment.map((entry) => (
+
+
+ {entry.value.toFixed(1)} {entry.unit}
+
+
+ ))}
+
+
+ {DANGER_LABEL[data.total_danger]}
+
+
+
+ {data.atmos_alarm ? (
+ Atmosphere alert in area
+ ) : (
+ 'No alerts'
+ )}
+
+
+ ) : (
+
+ Warning: Cannot obtain air sample for analysis.
+
+ )}
+
+
+
+
+
+ act('temperature')}
+ />
+
+
+
+
+ );
+};
+
+// ---- Main tab ----
+
+const MainSection = (props, context) => {
+ const { act, data } = useBackend(context);
+
+ return (
+
+ act(data.atmos_alarm ? 'atmos_reset' : 'atmos_alarm')}
+ >
+ {data.atmos_alarm
+ ? 'Reset Area Atmospheric Alarm'
+ : 'Activate Area Atmospheric Alarm'}
+
+
+ act('mode', { mode: data.mode === 3 ? 1 : 3 })}
+ >
+ {data.mode === 3
+ ? 'PANIC SIPHON ACTIVE — Turn off'
+ : 'Activate Panic Siphon'}
+
+
+
+ );
+};
+
+// ---- Vents tab ----
+
+const VentsSection = (props, context) => {
+ const { act, data } = useBackend(context);
+
+ if (!data.vents.length) {
+ return No vents connected.;
+ }
+
+ return (
+ <>
+ {data.vents.map((vent) => (
+
+
+
+
+ act('command', {
+ id_tag: vent.id_tag,
+ command: 'power',
+ val: vent.power ? 0 : 1,
+ })
+ }
+ />
+
+
+ {vent.direction === 'siphon' ? 'Siphoning' : 'Pressurizing'}
+
+
+
+ act('command', {
+ id_tag: vent.id_tag,
+ command: 'checks',
+ val: vent.checks ^ 1,
+ })
+ }
+ />
+
+ act('command', {
+ id_tag: vent.id_tag,
+ command: 'checks',
+ val: vent.checks ^ 2,
+ })
+ }
+ />
+
+
+
+ act('command', {
+ id_tag: vent.id_tag,
+ command: 'set_external_pressure',
+ })
+ }
+ />
+
+ act('command', {
+ id_tag: vent.id_tag,
+ command: 'reset_external_pressure',
+ })
+ }
+ />
+
+
+
+ ))}
+ >
+ );
+};
+
+// ---- Scrubbers tab ----
+
+const ScrubbersSection = (props, context) => {
+ const { act, data } = useBackend(context);
+
+ if (!data.scrubbers.length) {
+ return No scrubbers connected.;
+ }
+
+ return (
+ <>
+ {data.scrubbers.map((scrubber) => (
+
+
+
+
+ act('command', {
+ id_tag: scrubber.id_tag,
+ command: 'power',
+ val: scrubber.power ? 0 : 1,
+ })
+ }
+ />
+
+
+
+ act('command', {
+ id_tag: scrubber.id_tag,
+ command: 'scrubbing',
+ val: scrubber.scrubbing ? 0 : 1,
+ })
+ }
+ />
+
+
+ {scrubber.filters.map((filter) => (
+
+ act('command', {
+ id_tag: scrubber.id_tag,
+ command: filter.command,
+ val: filter.val ? 0 : 1,
+ })
+ }
+ />
+ ))}
+
+
+
+ ))}
+ >
+ );
+};
+
+// ---- Mode tab ----
+
+const ModeSection = (props, context) => {
+ const { act, data } = useBackend(context);
+
+ return (
+
+ {data.modes.map((m) => (
+
+ act('mode', { mode: m.mode })}
+ >
+
+ {m.name}
+ {' '}
+ — {m.description}
+
+
+ ))}
+
+ );
+};
+
+// ---- Sensors tab ----
+
+const THRESHOLD_LABELS = ['Min²', 'Min¹', 'Max¹', 'Max²'];
+
+const SensorsSection = (props, context) => {
+ const { act, data } = useBackend(context);
+
+ return (
+
+
+ Partial pressure for gases (kPa). Negative values disable the threshold.
+
+
+
+ Gas
+ {THRESHOLD_LABELS.map((label, i) => (
+
+ {label}
+
+ ))}
+
+ {data.thresholds.map((threshold) => (
+
+ {threshold.name}
+ {threshold.settings.map((setting) => (
+
+ = 0 ? setting.selected.toFixed(2) : 'Off'
+ }
+ disabled={!!data.shorted || !!data.locked}
+ onClick={() =>
+ act('command', {
+ command: 'set_threshold',
+ env: setting.env,
+ threshold: setting.val,
+ })
+ }
+ />
+
+ ))}
+
+ ))}
+
+
+ );
+};
diff --git a/tgui/packages/tgui/interfaces/AtmosAlertComputer.tsx b/tgui/packages/tgui/interfaces/AtmosAlertComputer.tsx
new file mode 100644
index 00000000000..8022c06bc5f
--- /dev/null
+++ b/tgui/packages/tgui/interfaces/AtmosAlertComputer.tsx
@@ -0,0 +1,61 @@
+import { useBackend } from '../backend';
+import { Button, NoticeBox, Section } from '../components';
+import { Window } from '../layouts';
+
+type AtmosAlertComputerData = {
+ priority_alarms: AlarmEntry[];
+ minor_alarms: AlarmEntry[];
+};
+
+type AlarmEntry = {
+ name: string;
+ ref: string;
+};
+
+export const AtmosAlertComputer = (props, context) => {
+ const { act, data } = useBackend(context);
+
+ return (
+
+
+
+ {data.priority_alarms.length ? (
+ data.priority_alarms.map((alarm) => (
+ act('clear_alarm', { ref: alarm.ref })}
+ />
+ ))
+ ) : (
+ No priority alerts detected.
+ )}
+
+
+ {data.minor_alarms.length ? (
+ data.minor_alarms.map((alarm) => (
+ act('clear_alarm', { ref: alarm.ref })}
+ />
+ ))
+ ) : (
+ No minor alerts detected.
+ )}
+
+
+
+ );
+};
diff --git a/tgui/packages/tgui/interfaces/OmniFilter.tsx b/tgui/packages/tgui/interfaces/OmniFilter.tsx
new file mode 100644
index 00000000000..32094e6ff5d
--- /dev/null
+++ b/tgui/packages/tgui/interfaces/OmniFilter.tsx
@@ -0,0 +1,160 @@
+import { BooleanLike } from '../../common/react';
+import { useBackend } from '../backend';
+import {
+ Button,
+ LabeledList,
+ ProgressBar,
+ Section,
+ Table,
+} from '../components';
+import { Window } from '../layouts';
+
+type OmniFilterData = {
+ power: BooleanLike;
+ config: BooleanLike;
+ last_power_draw: number;
+ max_power_draw: number;
+ set_flow_rate: number;
+ max_flow_rate: number;
+ last_flow_rate: number;
+ ports: PortEntry[];
+ filter_types: string[];
+};
+
+type PortEntry = {
+ dir: string;
+ input: BooleanLike;
+ output: BooleanLike;
+ filter: BooleanLike;
+ f_type: string | null;
+};
+
+export const OmniFilter = (props, context) => {
+ const { act, data } = useBackend(context);
+
+ return (
+
+
+
+
+
+ act('power')}
+ />
+
+
+ act('configure')}
+ />
+
+
+ {data.config ? (
+ act('set_flow_rate')}
+ />
+ ) : (
+ `${data.set_flow_rate} L/s`
+ )}
+
+
+ {data.last_flow_rate} L/s
+
+
+
+ {data.last_power_draw} W
+
+
+
+
+ {data.ports?.length > 0 && (
+
+
+
+ Port
+ Input
+ Output
+ Filter
+
+ {data.ports.map((port) => (
+
+ {port.dir} Port
+
+ {data.config ? (
+
+ act('switch_mode', {
+ dir: port.dir,
+ mode: port.input ? 'none' : 'in',
+ })
+ }
+ />
+ ) : port.input ? (
+ 'Input'
+ ) : (
+ ''
+ )}
+
+
+ {data.config ? (
+
+ act('switch_mode', {
+ dir: port.dir,
+ mode: 'out',
+ })
+ }
+ />
+ ) : port.output ? (
+ 'Output'
+ ) : (
+ ''
+ )}
+
+
+ {data.config ? (
+ act('switch_filter', { dir: port.dir })}
+ />
+ ) : port.filter ? (
+ (port.f_type ?? 'None')
+ ) : (
+ ''
+ )}
+
+
+ ))}
+
+
+ )}
+
+
+ );
+};
diff --git a/tgui/packages/tgui/interfaces/OmniMixer.tsx b/tgui/packages/tgui/interfaces/OmniMixer.tsx
new file mode 100644
index 00000000000..1006fa08d23
--- /dev/null
+++ b/tgui/packages/tgui/interfaces/OmniMixer.tsx
@@ -0,0 +1,189 @@
+import { BooleanLike } from '../../common/react';
+import { useBackend } from '../backend';
+import {
+ Button,
+ LabeledList,
+ ProgressBar,
+ Section,
+ Table,
+} from '../components';
+import { Window } from '../layouts';
+
+type OmniMixerData = {
+ power: BooleanLike;
+ config: BooleanLike;
+ last_power_draw: number;
+ max_power_draw: number;
+ set_flow_rate: number;
+ max_flow_rate: number;
+ last_flow_rate: number;
+ ports: PortEntry[];
+};
+
+type PortEntry = {
+ dir: string;
+ concentration: number;
+ input: BooleanLike;
+ output: BooleanLike;
+ con_lock: BooleanLike;
+};
+
+export const OmniMixer = (props, context) => {
+ const { act, data } = useBackend(context);
+
+ return (
+
+
+
+
+
+ act('power')}
+ />
+
+
+ act('configure')}
+ />
+
+
+ {data.config ? (
+ act('set_flow_rate')}
+ />
+ ) : (
+ `${data.set_flow_rate} L/s`
+ )}
+
+
+ {data.last_flow_rate} L/s
+
+
+
+ {data.last_power_draw} W
+
+
+
+
+ {data.ports?.length > 0 && (
+
+
+
+ Port
+ Input
+ Output
+ {data.config && (
+ <>
+ Concentration
+ Lock
+ >
+ )}
+ {!data.config && (
+ Concentration
+ )}
+
+ {data.ports.map((port) => (
+
+ {port.dir} Port
+
+ {data.config ? (
+
+ act('switch_mode', {
+ dir: port.dir,
+ mode: port.input ? 'none' : 'in',
+ })
+ }
+ />
+ ) : port.input ? (
+ 'Input'
+ ) : (
+ ''
+ )}
+
+
+ {data.config ? (
+
+ act('switch_mode', {
+ dir: port.dir,
+ mode: 'out',
+ })
+ }
+ />
+ ) : port.output ? (
+ 'Output'
+ ) : (
+ ''
+ )}
+
+ {data.config && (
+ <>
+
+
+ act('set_concentration', { dir: port.dir })
+ }
+ />
+
+
+
+ act('toggle_con_lock', { dir: port.dir })
+ }
+ />
+
+ >
+ )}
+ {!data.config && (
+
+ {port.input
+ ? `${Math.round(port.concentration * 100)}%`
+ : '-'}
+
+ )}
+
+ ))}
+
+
+ )}
+
+
+ );
+};
diff --git a/tgui/packages/tgui/interfaces/OxyRegenerator.tsx b/tgui/packages/tgui/interfaces/OxyRegenerator.tsx
new file mode 100644
index 00000000000..5f468d1fc1f
--- /dev/null
+++ b/tgui/packages/tgui/interfaces/OxyRegenerator.tsx
@@ -0,0 +1,101 @@
+import { BooleanLike } from '../../common/react';
+import { useBackend } from '../backend';
+import { Button, LabeledList, Section } from '../components';
+import { Window } from '../layouts';
+
+type OxyRegeneratorData = {
+ on: BooleanLike;
+ powerSetting: number;
+ gasProcessed: number;
+ air1Pressure: number;
+ air2Pressure: number;
+ tankPressure: number;
+ targetPressure: number;
+ phase: 'filling' | 'processing' | 'releasing';
+ co2: number;
+ o2: number;
+};
+
+export const OxyRegenerator = (props, context) => {
+ const { act, data } = useBackend(context);
+
+ return (
+
+
+
+
+
+ act('power')}
+ />
+
+
+ {[1, 2, 3, 4, 5].map((level) => (
+ act('set_power', { value: level })}
+ />
+ ))}
+
+
+
+
+
+ {data.phase === 'filling' && (
+ <>
+
+ {data.air1Pressure} kPa
+
+
+ {data.tankPressure} kPa
+
+
+ {data.targetPressure} kPa
+
+ >
+ )}
+ {data.phase === 'processing' && (
+ <>
+
+ {data.gasProcessed} mol
+
+
+ {data.tankPressure} kPa
+
+
+ {data.co2}%
+
+
+ {data.o2}%
+
+ >
+ )}
+ {data.phase === 'releasing' && (
+ <>
+
+ {data.tankPressure} kPa
+
+
+ {data.air2Pressure} kPa
+
+
+ {data.targetPressure} kPa
+
+ >
+ )}
+
+
+
+
+ );
+};