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. + + )} + + + +
+ ); +}; + +// ---- Main tab ---- + +const MainSection = (props, context) => { + const { act, data } = useBackend(context); + + return ( +
+ + + + +
+ ); +}; + +// ---- Vents tab ---- + +const VentsSection = (props, context) => { + const { act, data } = useBackend(context); + + if (!data.vents.length) { + return No vents connected.; + } + + return ( + <> + {data.vents.map((vent) => ( +
+ + +
+ ))} + + ); +}; + +// ---- Scrubbers tab ---- + +const ScrubbersSection = (props, context) => { + const { act, data } = useBackend(context); + + if (!data.scrubbers.length) { + return No scrubbers connected.; + } + + return ( + <> + {data.scrubbers.map((scrubber) => ( +
+ + +
+ ))} + + ); +}; + +// ---- Mode tab ---- + +const ModeSection = (props, context) => { + const { act, data } = useBackend(context); + + return ( +
+ {data.modes.map((m) => ( + + + + ))} +
+ ); +}; + +// ---- 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) => ( + +
+
+ ); +}; 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 ( + + +
+ + +
+ {data.ports?.length > 0 && ( +
+ + + Port + Input + Output + Filter + + {data.ports.map((port) => ( + + {port.dir} Port + + {data.config ? ( +
+
+ )} +
+
+ ); +}; 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 ( + + +
+ + +
+ {data.ports?.length > 0 && ( +
+ + + Port + Input + Output + {data.config && ( + <> + Concentration + Lock + + )} + {!data.config && ( + Concentration + )} + + {data.ports.map((port) => ( + + {port.dir} Port + + {data.config ? ( +
+
+ )} +
+
+ ); +}; 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 ( + + +
+ + +
+
+ + {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 + + + )} + +
+
+
+ ); +};