Engineering stuff tweaks and bugfixes (#20709)

Atmos alert console can clear air alarm atmos alert
During an active fire nearby a firealarm, it will show you the current temperature when examined
Firealarm now notify on engineering channel when theres a fire
Fix station alert console and program not updating overlays
Fix fire alerts not clearing when there are multiple fire alarms in one area
fix destroyed airalarm clearing atmos alert if theres an actual atmos alert in an area with multiple air alarms

Co-authored-by: John Willard <53777086+JohnFulpWillard@users.noreply.github.com>
Co-authored-by: SapphicOverload <93578146+SapphicOverload@users.noreply.github.com>
This commit is contained in:
Bop
2023-11-19 12:27:46 +07:00
committed by GitHub
parent 6c9fac09a2
commit d6bc9beec6
9 changed files with 185 additions and 137 deletions

View File

@@ -495,3 +495,8 @@ GLOBAL_LIST_INIT(pipe_paint_colors, list(
#define MIASMA_GIBS_MOLES 0.005
#define TURF_SHARES(T) (LAZYLEN(T.atmos_adjacent_turfs))
//Defines for air alarm severities in areas.
#define ATMOS_ALARM_SEVERE "severe"
#define ATMOS_ALARM_MINOR "minor"
#define ATMOS_ALARM_CLEAR "clear"

View File

@@ -32,6 +32,7 @@
var/clockwork_warp_allowed = TRUE // Can servants warp into this area from Reebe?
var/clockwork_warp_fail = "The structure there is too dense for warping to pierce. (This is normal in high-security areas.)"
///If true, that means one of any fire alarms in the area is active
var/fire = FALSE
var/atmos = TRUE
var/atmosalm = FALSE
@@ -113,6 +114,7 @@
var/list/firedoors
var/list/cameras
var/list/firealarms
var/list/airalarms
var/firedoors_last_closed_on = 0
/// Can the Xenobio management console transverse this area by default?
var/xenobiology_compatible = FALSE
@@ -344,10 +346,7 @@ GLOBAL_LIST_EMPTY(teleportlocs)
D.triggerAlarm("Power", src, cameras, source)
for(var/item in GLOB.alarmdisplay)
var/datum/computer_file/program/alarm_monitor/p = item
if(state == 1)
p.cancelAlarm("Power", src, source)
else
p.triggerAlarm("Power", src, cameras, source)
p.update_alarm_display()
/**
* Generate an atmospheric alert for this area
@@ -368,7 +367,7 @@ GLOBAL_LIST_EMPTY(teleportlocs)
D.triggerAlarm("Atmosphere", src, cameras, source)
for(var/item in GLOB.alarmdisplay)
var/datum/computer_file/program/alarm_monitor/p = item
p.triggerAlarm("Atmosphere", src, cameras, source)
p.update_alarm_display()
else
for (var/item in GLOB.silicon_mobs)
@@ -382,7 +381,7 @@ GLOBAL_LIST_EMPTY(teleportlocs)
D.cancelAlarm("Atmosphere", src, source)
for(var/item in GLOB.alarmdisplay)
var/datum/computer_file/program/alarm_monitor/p = item
p.cancelAlarm("Atmosphere", src, source)
p.update_alarm_display()
/**
* Try to close all the firedoors in the area
*/
@@ -433,7 +432,7 @@ GLOBAL_LIST_EMPTY(teleportlocs)
D.triggerAlarm("Fire", src, cameras, source)
for(var/item in GLOB.alarmdisplay)
var/datum/computer_file/program/alarm_monitor/p = item
p.triggerAlarm("Fire", src, cameras, source)
p.update_alarm_display()
START_PROCESSING(SSobj, src)
@@ -445,8 +444,8 @@ GLOBAL_LIST_EMPTY(teleportlocs)
*
* Also cycles the icons of all firealarms and deregisters the area from processing on SSOBJ
*/
/area/proc/firereset(obj/source)
if (fire)
/area/proc/firereset(obj/source, alert_only=FALSE)
if (fire && !alert_only)
unset_fire_alarm_effects()
ModifyFiredoors(TRUE)
for(var/item in firealarms)
@@ -464,7 +463,7 @@ GLOBAL_LIST_EMPTY(teleportlocs)
D.cancelAlarm("Fire", src, source)
for(var/item in GLOB.alarmdisplay)
var/datum/computer_file/program/alarm_monitor/p = item
p.cancelAlarm("Fire", src, source)
p.update_alarm_display()
STOP_PROCESSING(SSobj, src)

View File

@@ -4,13 +4,18 @@
circuit = /obj/item/circuitboard/computer/atmos_alert
icon_screen = "alert:0"
icon_keyboard = "atmos_key"
var/list/priority_alarms = list()
var/list/minor_alarms = list()
var/receive_frequency = FREQ_ATMOS_ALARMS
var/datum/radio_frequency/radio_connection
light_color = LIGHT_COLOR_CYAN
///List of areas with a priority alarm ongoing.
var/list/area/priority_alarms = list()
///List of areas with a minor alarm ongoing.
var/list/area/minor_alarms = list()
///The radio connection the computer uses to receive signals.
var/datum/radio_frequency/radio_connection
///The frequency that radio_connection listens and retrieves signals from.
var/receive_frequency = FREQ_ATMOS_ALARMS
/obj/machinery/computer/atmos_alert/Initialize(mapload)
. = ..()
set_frequency(receive_frequency)
@@ -27,31 +32,43 @@
/obj/machinery/computer/atmos_alert/ui_data(mob/user)
var/list/data = list()
data["priority_alerts"] = list()
data["minor_alerts"] = list()
data["priority"] = list()
for(var/zone in priority_alarms)
data["priority"] += zone
data["minor"] = list()
for(var/zone in minor_alarms)
data["minor"] += zone
for(var/area/priority_alarm as anything in priority_alarms)
data["priority_alerts"] += list(list(
"name" = priority_alarm.name,
"ref" = REF(priority_alarm),
))
for(var/area/minor_alarm as anything in minor_alarms)
data["minor_alerts"] += list(list(
"name" = minor_alarm.name,
"ref" = REF(minor_alarm),
))
return data
/obj/machinery/computer/atmos_alert/ui_act(action, params)
if(..())
return
switch(action)
if("clear")
var/zone = params["zone"]
if(zone in priority_alarms)
to_chat(usr, "Priority alarm for [zone] cleared.")
priority_alarms -= zone
. = TRUE
if(zone in minor_alarms)
to_chat(usr, "Minor alarm for [zone] cleared.")
minor_alarms -= zone
. = TRUE
update_appearance(UPDATE_ICON)
. = ..()
if(.)
return TRUE
if(action != "clear")
return TRUE
var/area/zone_from_tgui = locate(params["zone_ref"]) in priority_alarms + minor_alarms
if(!zone_from_tgui || !istype(zone_from_tgui))
return TRUE
var/obj/machinery/airalarm/found_air_alarm = locate() in zone_from_tgui
if(found_air_alarm)
found_air_alarm.atmos_manualOverride(TRUE)
found_air_alarm.post_alert(0)
priority_alarms -= zone_from_tgui
minor_alarms -= zone_from_tgui
balloon_alert(usr, "alarm cleared.")
update_appearance(UPDATE_ICON)
return TRUE
/obj/machinery/computer/atmos_alert/proc/set_frequency(new_frequency)
SSradio.remove_object(src, receive_frequency)
@@ -62,25 +79,27 @@
if(!signal)
return
var/zone = signal.data["zone"]
var/area/new_zone = signal.data["zone"]
var/severity = signal.data["alert"]
if(!zone || !severity)
if(!new_zone || !istype(new_zone) || !severity)
return
minor_alarms -= zone
priority_alarms -= zone
if(severity == "severe")
priority_alarms += zone
else if (severity == "minor")
minor_alarms += zone
//clear current references.
minor_alarms -= new_zone
priority_alarms -= new_zone
switch(severity)
if(ATMOS_ALARM_SEVERE)
priority_alarms += new_zone
if(ATMOS_ALARM_MINOR)
minor_alarms += new_zone
update_appearance(UPDATE_ICON)
return
/obj/machinery/computer/atmos_alert/update_overlays()
. = ..()
if(stat & (NOPOWER|BROKEN))
return
if(priority_alarms.len)
. += "alert:2"
else if(minor_alarms.len)

View File

@@ -45,6 +45,7 @@
var/list/sources = alarm[3]
if (!(source in sources))
sources += source
update_appearance(UPDATE_ICON)
return 1
var/obj/machinery/camera/C = null
var/list/CL = null
@@ -55,6 +56,7 @@
else if(O && istype(O, /obj/machinery/camera))
C = O
L[A.name] = list(A, (C ? C : O), list(source))
update_appearance(UPDATE_ICON)
return 1
@@ -72,6 +74,7 @@
if (srcs.len == 0)
cleared = 1
L -= I
update_appearance(UPDATE_ICON)
return !cleared
/obj/machinery/computer/station_alert/update_overlays()

View File

@@ -35,9 +35,9 @@
if(alarm_area == get_area_name(src))
switch(alert)
if("severe")
if(ATMOS_ALARM_SEVERE)
autoclose = TRUE
close()
if("minor", "clear")
if(ATMOS_ALARM_MINOR, ATMOS_ALARM_CLEAR)
autoclose = FALSE
open()
open()

View File

@@ -30,10 +30,20 @@
light_range = 7
light_color = "#ff3232"
/// 1 = will auto detect fire, 0 = no auto
var/detecting = 1
var/buildstage = 2 // 2 = complete, 1 = no wires, 0 = circuit gone
/// 2 = complete, 1 = no wires, 0 = circuit gone
var/buildstage = 2
/// Cooldown for next alarm trigger, so it doesnt spam much
var/last_alarm = 0
/// The area of the current fire alarm
var/area/myarea = null
/// If true, then this area has a real fire and not by someone triggering it manually
var/real_fire = FALSE
/// If real_fire is true then it will show you the current hot temperature
var/bad_temp = null
/// The radio to alert engineers, atmos techs
var/obj/item/radio/radio
/obj/machinery/firealarm/Initialize(mapload, dir, building)
. = ..()
@@ -47,8 +57,16 @@
update_appearance(UPDATE_ICON)
myarea = get_area(src)
LAZYADD(myarea.firealarms, src)
radio = new(src)
radio.keyslot = new /obj/item/encryptionkey/headset_eng()
radio.subspace_transmission = TRUE
radio.canhear_range = 0
radio.recalculateChannels()
STOP_PROCESSING(SSmachines, src) // I will do this
/obj/machinery/firealarm/Destroy()
myarea.firereset(src, TRUE)
QDEL_NULL(radio)
LAZYREMOVE(myarea.firealarms, src)
return ..()
@@ -115,18 +133,42 @@
playsound(src, "sparks", 50, 1)
return TRUE
/obj/machinery/firealarm/examine(mob/user)
. = ..()
if(areafire_check())
. += span_danger("Fire detected in this area, current fire alarm temperature: [bad_temp-T0C]C")
else
. += span_notice("There's no fire detected.")
/obj/machinery/firealarm/temperature_expose(datum/gas_mixture/air, temperature, volume)
var/turf/open/T = get_turf(src)
if((temperature >= FIRE_MINIMUM_TEMPERATURE_TO_EXIST || temperature < BODYTEMP_COLD_DAMAGE_LIMIT || (istype(T) && T.turf_fire)) && (last_alarm+FIREALARM_COOLDOWN < world.time) && !(obj_flags & EMAGGED) && detecting && !stat)
if(!real_fire)
radio.talk_into(src, "Fire detected in [myarea].", RADIO_CHANNEL_ENGINEERING)
real_fire = TRUE
bad_temp = temperature
alarm()
START_PROCESSING(SSmachines, src)
..()
/obj/machinery/firealarm/process() //Fire alarm only start processing when its triggered by temperature_expose()
var/turf/open/T = get_turf(src)
var/datum/gas_mixture/env = T.return_air()
if(env.return_temperature() < FIRE_MINIMUM_TEMPERATURE_TO_EXIST && env.return_temperature() > BODYTEMP_COLD_DAMAGE_LIMIT && (istype(T) && !T.turf_fire))
real_fire = FALSE
STOP_PROCESSING(SSmachines, src)
/obj/machinery/firealarm/proc/areafire_check()
for(var/obj/machinery/firealarm/FA in myarea.firealarms)
if(FA.real_fire)
return TRUE
return FALSE
/obj/machinery/firealarm/proc/alarm(mob/user)
if(!is_operational() || (last_alarm+FIREALARM_COOLDOWN > world.time))
return
last_alarm = world.time
var/area/A = get_area(src)
A.firealert(src)
myarea.firealert(src)
playsound(loc, 'goon/sound/machinery/FireAlarm.ogg', 75)
if(user)
log_game("[user] triggered a fire alarm at [COORD(src)]")
@@ -134,8 +176,9 @@
/obj/machinery/firealarm/proc/reset(mob/user)
if(!is_operational())
return
var/area/A = get_area(src)
A.firereset(src)
for(var/obj/machinery/firealarm/F in myarea.firealarms)
F.myarea.firereset(F)
F.bad_temp = null
if(user)
log_game("[user] reset a fire alarm at [COORD(src)]")
@@ -144,8 +187,7 @@
return ..()
add_fingerprint(user)
play_click_sound("button")
var/area/A = get_area(src)
if(A.fire || A.party)
if(myarea.fire || myarea.party)
reset(user)
else
alarm(user)
@@ -201,8 +243,7 @@
else if(W.force) //hit and turn it on
..()
var/area/A = get_area(src)
if(!A.fire)
if(!myarea.fire)
alarm()
return
@@ -295,6 +336,7 @@
. = ..()
if(.)
myarea.firereset(src, TRUE)
LAZYREMOVE(myarea.firealarms, src)
/obj/machinery/firealarm/deconstruct(disassembled = TRUE)
@@ -332,19 +374,17 @@
/obj/machinery/firealarm/partyalarm/reset()
if (stat & (NOPOWER|BROKEN))
return
var/area/A = get_area(src)
if (!A || !A.party)
if (!myarea || !myarea.party)
return
A.party = FALSE
A.cut_overlay(party_overlay)
myarea.party = FALSE
myarea.cut_overlay(party_overlay)
/obj/machinery/firealarm/partyalarm/alarm()
if (stat & (NOPOWER|BROKEN))
return
var/area/A = get_area(src)
if (!A || A.party || A.name == "Space")
if (!myarea || myarea.party || istype(myarea, /area/space))
return
A.party = TRUE
myarea.party = TRUE
if (!party_overlay)
party_overlay = iconstate2appearance('icons/turf/areas.dmi', "party")
A.add_overlay(party_overlay)
myarea.add_overlay(party_overlay)

View File

@@ -231,6 +231,7 @@
..()
wires = new /datum/wires/airalarm(src)
A = get_area(src)
LAZYADD(A.airalarms, src)
if(ndir)
setDir(ndir)
@@ -248,8 +249,13 @@
/obj/machinery/airalarm/Destroy()
SSradio.remove_object(src, frequency)
QDEL_NULL(wires)
atmos_manualOverride(TRUE)
post_alert(0)
if(length(A.airalarms)<2 || A.manual_atmosalm)
atmos_manualOverride(TRUE)
post_alert(0)
else
atmos_manualOverride(TRUE)
A.atmosalert(0, src)
LAZYREMOVE(A.airalarms, src)
return ..()
/obj/machinery/airalarm/Initialize(mapload)
@@ -708,9 +714,13 @@
else
AA.manual_override = TRUE
#define ALARM_LEVEL_CLEAR 0
#define ALARM_LEVEL_MINOR 1
#define ALARM_LEVEL_SEVERE 2
/obj/machinery/airalarm/proc/post_alert(alert_level)
var/datum/radio_frequency/frequency = SSradio.return_frequency(alarm_frequency)
if(alert_level>0 && !manual_override)
if(alert_level > 0 && !manual_override)
trigger_reset = TRUE
else
trigger_reset = FALSE
@@ -722,23 +732,28 @@
return
var/datum/signal/alert_signal = new(list(
"zone" = get_area_name(src),
"type" = "Atmospheric"
"zone" = A,
"type" = "Atmospheric",
))
if(alert_level==2)
alert_signal.data["alert"] = "severe"
A.set_vacuum_alarm_effect()
else if (alert_level==1)
alert_signal.data["alert"] = "minor"
else if (alert_level==0)
alert_signal.data["alert"] = "clear"
A.unset_vacuum_alarm_effect()
switch(alert_level)
if(ALARM_LEVEL_CLEAR)
alert_signal.data["alert"] = ATMOS_ALARM_CLEAR
A.unset_vacuum_alarm_effect()
if(ALARM_LEVEL_MINOR)
alert_signal.data["alert"] = ATMOS_ALARM_MINOR
if(ALARM_LEVEL_SEVERE)
alert_signal.data["alert"] = ATMOS_ALARM_SEVERE
A.set_vacuum_alarm_effect()
frequency.post_signal(src, alert_signal, range = -1)
for(var/obj/machinery/airalarm/AA in A)
AA.update_appearance(UPDATE_ICON)
#undef ALARM_LEVEL_CLEAR
#undef ALARM_LEVEL_MINOR
#undef ALARM_LEVEL_SEVERE
/obj/machinery/airalarm/proc/apply_danger_level()
var/new_area_danger_level = 0

View File

@@ -38,52 +38,6 @@
return data
/datum/computer_file/program/alarm_monitor/proc/triggerAlarm(class, area/A, O, obj/source)
if(is_station_level(source.z))
if(!(A.type in GLOB.the_station_areas))
return
else if(!is_mining_level(source.z) || istype(A, /area/ruin))
return
var/list/L = GLOB.alarms[class]
for(var/I in L)
if (I == A.name)
var/list/alarm = L[I]
var/list/sources = alarm[3]
if (!(source in sources))
sources += source
return 1
var/obj/machinery/camera/C = null
var/list/CL = null
if(O && istype(O, /list))
CL = O
if (CL.len == 1)
C = CL[1]
else if(O && istype(O, /obj/machinery/camera))
C = O
L[A.name] = list(A, (C ? C : O), list(source))
update_alarm_display()
return 1
/datum/computer_file/program/alarm_monitor/proc/cancelAlarm(class, area/A, obj/origin)
var/list/L = GLOB.alarms[class]
var/cleared = 0
for (var/I in L)
if (I == A.name)
var/list/alarm = L[I]
var/list/srcs = alarm[3]
if (origin in srcs)
srcs -= origin
if (srcs.len == 0)
cleared = 1
L -= I
update_alarm_display()
return !cleared
/datum/computer_file/program/alarm_monitor/proc/update_alarm_display()
has_alert = FALSE
for(var/cat in GLOB.alarms)
@@ -91,6 +45,10 @@
if(L.len)
has_alert = TRUE
/datum/computer_file/program/alarm_monitor/New()
..()
GLOB.alarmdisplay += src
/datum/computer_file/program/alarm_monitor/run_program(mob/user)
. = ..(user)
GLOB.alarmdisplay += src

View File

@@ -2,10 +2,19 @@ import { useBackend } from '../backend';
import { Button, Section } from '../components';
import { Window } from '../layouts';
type Data = {
priority_alerts: AlertData[];
minor_alerts: AlertData[];
};
type AlertData = {
name: string;
ref: string;
};
export const AtmosAlertConsole = (props, context) => {
const { act, data } = useBackend(context);
const priorityAlerts = data.priority || [];
const minorAlerts = data.minor || [];
const { act, data } = useBackend<Data>(context);
const { priority_alerts = [], minor_alerts = [] } = data;
return (
<Window
width={350}
@@ -14,32 +23,32 @@ export const AtmosAlertConsole = (props, context) => {
<Window.Content scrollable>
<Section title="Alarms">
<ul>
{priorityAlerts.length === 0 && (
{priority_alerts.length === 0 && (
<li className="color-good">
No Priority Alerts
</li>
)}
{priorityAlerts.map(alert => (
<li key={alert}>
{priority_alerts.map((alert) => (
<li key={alert.name}>
<Button
icon="times"
content={alert}
content={alert.name}
color="bad"
onClick={() => act('clear', { zone: alert })} />
onClick={() => act('clear', { zone_ref: alert.ref })} />
</li>
))}
{minorAlerts.length === 0 && (
{minor_alerts.length === 0 && (
<li className="color-good">
No Minor Alerts
</li>
)}
{minorAlerts.map(alert => (
<li key={alert}>
{minor_alerts.map((alert) => (
<li key={alert.name}>
<Button
icon="times"
content={alert}
content={alert.name}
color="average"
onClick={() => act('clear', { zone: alert })} />
onClick={() => act('clear', { zone_ref: alert.ref })} />
</li>
))}
</ul>