Merge pull request #13751 from Fox-McCloud/alarm-overhaul

Overhauls and Refactors Alarm System
This commit is contained in:
AffectedArc07
2020-09-14 13:54:06 +01:00
committed by GitHub
53 changed files with 1033 additions and 906 deletions
-136
View File
@@ -1,136 +0,0 @@
#define ALARM_RESET_DELAY 100 // How long will the alarm/trigger remain active once origin/source has been found to be gone?
/datum/alarm_source
var/source = null // The source trigger
var/source_name = "" // The name of the source should it be lost (for example a destroyed camera)
var/duration = 0 // How long this source will be alarming, 0 for indefinetely.
var/severity = 1 // How severe the alarm from this source is.
var/start_time = 0 // When this source began alarming.
var/end_time = 0 // Use to set when this trigger should clear, in case the source is lost.
/datum/alarm_source/New(var/atom/source)
src.source = source
start_time = world.time
source_name = source.get_source_name()
/datum/alarm
var/atom/origin //Used to identify the alarm area.
var/list/sources = new() //List of sources triggering the alarm. Used to determine when the alarm should be cleared.
var/list/sources_assoc = new() //Associative list of source triggers. Used to efficiently acquire the alarm source.
var/list/cameras //List of cameras that can be switched to, if the player has that capability.
var/area/last_area //The last acquired area, used should origin be lost (for example a destroyed borg containing an alarming camera).
var/area/last_name //The last acquired name, used should origin be lost
var/area/last_camera_area //The last area in which cameras where fetched, used to see if the camera list should be updated.
var/end_time //Used to set when this alarm should clear, in case the origin is lost.
/datum/alarm/New(var/atom/origin, var/atom/source, var/duration, var/severity)
src.origin = origin
cameras() // Sets up both cameras and last alarm area.
set_source_data(source, duration, severity)
/datum/alarm/process()
// Has origin gone missing?
if(!origin && !end_time)
end_time = world.time + ALARM_RESET_DELAY
for(var/datum/alarm_source/AS in sources)
// Has the alarm passed its best before date?
if((AS.end_time && world.time > AS.end_time) || (AS.duration && world.time > (AS.start_time + AS.duration)))
sources -= AS
// Has the source gone missing? Then reset the normal duration and set end_time
if(!AS.source && !AS.end_time) // end_time is used instead of duration to ensure the reset doesn't remain in the future indefinetely.
AS.duration = 0
AS.end_time = world.time + ALARM_RESET_DELAY
/datum/alarm/proc/set_source_data(var/atom/source, var/duration, var/severity)
var/datum/alarm_source/AS = sources_assoc[source]
if(!AS)
AS = new/datum/alarm_source(source)
sources += AS
sources_assoc[source] = AS
// Currently only non-0 durations can be altered (normal alarms VS EMP blasts)
if(AS.duration)
duration = duration SECONDS
AS.duration = duration
AS.severity = severity
/datum/alarm/proc/clear(var/source)
var/datum/alarm_source/AS = sources_assoc[source]
sources -= AS
sources_assoc -= source
/datum/alarm/proc/alarm_area()
if(!origin)
return last_area
last_area = origin.get_alarm_area()
return last_area
/datum/alarm/proc/alarm_name()
if(!origin)
return last_name
last_name = origin.get_alarm_name()
return last_name
/datum/alarm/proc/cameras()
// If the alarm origin has changed area, for example a borg containing an alarming camera, reset the list of cameras
if(cameras && (last_camera_area != alarm_area()))
cameras = null
// The list of cameras is also reset by /proc/invalidateCameraCache()
if(!cameras)
cameras = origin ? origin.get_alarm_cameras() : last_area.get_alarm_cameras()
last_camera_area = last_area
return cameras
/datum/alarm/proc/max_severity()
var/max_severity = 0
for(var/datum/alarm_source/AS in sources)
max_severity = max(AS.severity, max_severity)
return max_severity
/******************
* Assisting procs *
******************/
/atom/proc/get_alarm_area()
var/area/A = get_area(src)
return A
/area/get_alarm_area()
return src
/atom/proc/get_alarm_name()
var/area/A = get_area(src)
return A.name
/area/get_alarm_name()
return name
/mob/get_alarm_name()
return name
/atom/proc/get_source_name()
return name
/obj/machinery/camera/get_source_name()
return c_tag
/atom/proc/get_alarm_cameras()
var/area/A = get_area(src)
return A.get_cameras()
/area/get_alarm_cameras()
return get_cameras()
/mob/living/silicon/robot/get_alarm_cameras()
var/list/cameras = ..()
if(camera)
cameras += camera
return cameras
/mob/living/silicon/robot/syndicate/get_alarm_cameras()
return list()
-103
View File
@@ -1,103 +0,0 @@
#define ALARM_RAISED 1
#define ALARM_CLEARED 0
/datum/alarm_handler
var/category = ""
var/list/datum/alarm/alarms = new // All alarms, to handle cases when an origin has been deleted with one or more active alarms
var/list/datum/alarm/alarms_assoc = new // Associative list of alarms, to efficiently acquire them based on origin.
var/list/listeners = new // A list of all objects interested in alarm changes.
/datum/alarm_handler/process()
for(var/datum/alarm/A in alarms)
A.process()
check_alarm_cleared(A)
/datum/alarm_handler/proc/triggerAlarm(var/atom/origin, var/atom/source, var/duration = 0, var/severity = 1)
var/new_alarm
//Proper origin and source mandatory
if(!(origin && source))
return
origin = origin.get_alarm_origin()
new_alarm = 0
//see if there is already an alarm of this origin
var/datum/alarm/existing = alarms_assoc[origin]
if(existing)
existing.set_source_data(source, duration, severity)
else
existing = new/datum/alarm(origin, source, duration, severity)
new_alarm = 1
alarms |= existing
alarms_assoc[origin] = existing
if(new_alarm)
alarms = dd_sortedObjectList(alarms)
on_alarm_change(existing, ALARM_RAISED)
return new_alarm
/datum/alarm_handler/proc/clearAlarm(var/atom/origin, var/source)
//Proper origin and source mandatory
if(!(origin && source))
return
origin = origin.get_alarm_origin()
var/datum/alarm/existing = alarms_assoc[origin]
if(existing)
existing.clear(source)
return check_alarm_cleared(existing)
/datum/alarm_handler/proc/has_major_alarms()
if(alarms && alarms.len)
return 1
return 0
/datum/alarm_handler/proc/major_alarms()
return alarms
/datum/alarm_handler/proc/minor_alarms()
return alarms
/datum/alarm_handler/proc/check_alarm_cleared(var/datum/alarm/alarm)
if((alarm.end_time && world.time > alarm.end_time) || !alarm.sources.len)
alarms -= alarm
alarms_assoc -= alarm.origin
on_alarm_change(alarm, ALARM_CLEARED)
return 1
return 0
/datum/alarm_handler/proc/on_alarm_change(var/datum/alarm/alarm, var/was_raised)
for(var/obj/machinery/camera/C in alarm.cameras())
if(was_raised)
C.network.Add(category)
else
C.network.Remove(category)
notify_listeners(alarm, was_raised)
/datum/alarm_handler/proc/get_alarm_severity_for_origin(var/atom/origin)
if(!origin)
return
origin = origin.get_alarm_origin()
var/datum/alarm/existing = alarms_assoc[origin]
if(!existing)
return
return existing.max_severity()
/atom/proc/get_alarm_origin()
return src
/turf/get_alarm_origin()
var/area/area = get_area(src)
return area // Very important to get area.master, as dynamic lightning can and will split areas.
/datum/alarm_handler/proc/register(var/object, var/procName)
listeners[object] = procName
/datum/alarm_handler/proc/unregister(var/object)
listeners -= object
/datum/alarm_handler/proc/notify_listeners(var/alarm, var/was_raised)
for(var/listener in listeners)
call(listener, listeners[listener])(src, alarm, was_raised)
-19
View File
@@ -1,19 +0,0 @@
/datum/alarm_handler/atmosphere
category = "Atmosphere Alarms"
/datum/alarm_handler/atmosphere/triggerAlarm(var/atom/origin, var/atom/source, var/duration = 0, var/severity = 1)
..()
/datum/alarm_handler/atmosphere/major_alarms()
var/list/major_alarms = new()
for(var/datum/alarm/A in alarms)
if(A.max_severity() > 1)
major_alarms.Add(A)
return major_alarms
/datum/alarm_handler/atmosphere/minor_alarms()
var/list/minor_alarms = new()
for(var/datum/alarm/A in alarms)
if(A.max_severity() == 1)
minor_alarms.Add(A)
return minor_alarms
-2
View File
@@ -1,2 +0,0 @@
/datum/alarm_handler/burglar
category = "Burglar Alarms"
-2
View File
@@ -1,2 +0,0 @@
/datum/alarm_handler/camera
category = "Camera Alarms"
-11
View File
@@ -1,11 +0,0 @@
/datum/alarm_handler/fire
category = "Fire Alarms"
/datum/alarm_handler/fire/on_alarm_change(var/datum/alarm/alarm, var/was_raised)
var/area/A = alarm.origin
if(istype(A))
if(was_raised)
A.fire_alert()
else
A.fire_reset()
..()
-2
View File
@@ -1,2 +0,0 @@
/datum/alarm_handler/motion
category = "Motion Alarms"
-10
View File
@@ -1,10 +0,0 @@
/datum/alarm_handler/power
category = "Power Alarms"
/datum/alarm_handler/power/on_alarm_change(var/datum/alarm/alarm, var/was_raised)
var/area/A = alarm.origin
if(istype(A))
A.power_alert(was_raised)
..()
/area/proc/power_alert(var/alarming)
+80 -3
View File
@@ -50,7 +50,7 @@ GLOBAL_LIST_INIT(ai_verbs_default, list(
var/list/connected_robots = list()
var/aiRestorePowerRoutine = 0
//var/list/laws = list()
var/alarms = list("Motion" = list(), "Fire" = list(), "Atmosphere" = list(), "Power" = list(), "Camera" = list())
alarms_listend_for = list("Motion", "Fire", "Atmosphere", "Power", "Camera", "Burglar")
var/viewalerts = 0
var/icon/holo_icon//Default is assigned when AI is created.
var/obj/mecha/controlled_mech //For controlled_mech a mech, to determine whether to relaymove or use the AI eye.
@@ -242,6 +242,46 @@ GLOBAL_LIST_INIT(ai_verbs_default, list(
return
show_borg_info()
/mob/living/silicon/ai/proc/ai_alerts()
var/list/dat = list("<HEAD><TITLE>Current Station Alerts</TITLE><META HTTP-EQUIV='Refresh' CONTENT='10'></HEAD><BODY>\n")
dat += "<A HREF='?src=[UID()];mach_close=aialerts'>Close</A><BR><BR>"
var/list/list/temp_alarm_list = SSalarm.alarms.Copy()
for(var/cat in temp_alarm_list)
if(!(cat in alarms_listend_for))
continue
dat += text("<B>[]</B><BR>\n", cat)
var/list/list/L = temp_alarm_list[cat].Copy()
for(var/alarm in L)
var/list/list/alm = L[alarm].Copy()
var/area_name = alm[1]
var/C = alm[2]
var/list/list/sources = alm[3].Copy()
for(var/thing in sources)
var/atom/A = locateUID(thing)
if(A && A.z != z)
L -= alarm
continue
dat += "<NOBR>"
if(C && islist(C))
var/dat2 = ""
for(var/cam in C)
var/obj/machinery/camera/I = locateUID(cam)
if(!QDELETED(I))
dat2 += text("[]<A HREF=?src=[UID()];switchcamera=[cam]>[]</A>", (dat2 == "") ? "" : " | ", I.c_tag)
dat += text("-- [] ([])", area_name, (dat2 != "") ? dat2 : "No Camera")
else
dat += text("-- [] (No Camera)", area_name)
if(sources.len > 1)
dat += text("- [] sources", sources.len)
dat += "</NOBR><BR>\n"
if(!L.len)
dat += "-- All Systems Nominal<BR>\n"
dat += "<BR>\n"
viewalerts = TRUE
var/dat_text = dat.Join("")
src << browse(dat_text, "window=aialerts&can_close=0")
/mob/living/silicon/ai/proc/show_borg_info()
stat(null, text("Connected cyborgs: [connected_robots.len]"))
for(var/thing in connected_robots)
@@ -612,7 +652,7 @@ GLOBAL_LIST_INIT(ai_verbs_default, list(
if(href_list["switchcamera"])
switchCamera(locate(href_list["switchcamera"])) in GLOB.cameranet.cameras
if(href_list["showalerts"])
subsystem_alarm_monitor()
ai_alerts()
if(href_list["show_paper"])
if(last_paper_seen)
src << browse(last_paper_seen, "window=show_paper")
@@ -787,12 +827,49 @@ GLOBAL_LIST_INIT(ai_verbs_default, list(
Bot.call_bot(src, waypoint)
/mob/living/silicon/ai/alarm_triggered(src, class, area/A, list/O, obj/alarmsource)
if(!(class in alarms_listend_for))
return
if(alarmsource.z != z)
return
if(stat == DEAD)
return TRUE
if(O)
var/obj/machinery/camera/C = locateUID(O[1])
if(O.len == 1 && !QDELETED(C) && C.can_use())
queueAlarm("--- [class] alarm detected in [A.name]! (<A HREF=?src=[UID()];switchcamera=[O[1]]>[C.c_tag]</A>)", class)
else if(O && O.len)
var/foo = 0
var/dat2 = ""
for(var/thing in O)
var/obj/machinery/camera/I = locateUID(thing)
if(!QDELETED(I))
dat2 += text("[]<A HREF=?src=[UID()];switchcamera=[thing]>[]</A>", (!foo) ? "" : " | ", I.c_tag) //I'm not fixing this shit...
foo = 1
queueAlarm(text ("--- [] alarm detected in []! ([])", class, A.name, dat2), class)
else
queueAlarm(text("--- [] alarm detected in []! (No Camera)", class, A.name), class)
else
queueAlarm(text("--- [] alarm detected in []! (No Camera)", class, A.name), class)
if(viewalerts)
ai_alerts()
/mob/living/silicon/ai/alarm_cancelled(src, class, area/A, obj/origin, cleared)
if(cleared)
if(!(class in alarms_listend_for))
return
if(origin.z != z)
return
queueAlarm("--- [class] alarm in [A.name] has been cleared.", class, 0)
if(viewalerts)
ai_alerts()
/mob/living/silicon/ai/proc/switchCamera(obj/machinery/camera/C)
if(!tracking)
cameraFollow = null
if(!C || stat == DEAD) //C.can_use())
if(QDELETED(C) || stat == DEAD) //C.can_use())
return FALSE
if(!eyeobj)
@@ -132,8 +132,6 @@
sleep(50)
theAPC = null
process_queued_alarms()
/mob/living/silicon/ai/updatehealth(reason = "none given")
if(status_flags & GODMODE)
health = 100
@@ -13,7 +13,6 @@
handle_robot_cell()
process_locks()
update_items()
process_queued_alarms()
/mob/living/silicon/robot/proc/handle_robot_cell()
+59 -5
View File
@@ -71,7 +71,6 @@ GLOBAL_LIST_INIT(robot_verbs_default, list(
var/list/req_access
var/ident = 0
//var/list/laws = list()
var/alarms = list("Motion"=list(), "Fire"=list(), "Atmosphere"=list(), "Power"=list(), "Camera"=list())
var/viewalerts = 0
var/modtype = "Default"
var/lower_mod = 0
@@ -544,6 +543,43 @@ GLOBAL_LIST_INIT(robot_verbs_default, list(
src.verbs -= GLOB.robot_verbs_default
src.verbs -= silicon_subsystems
/mob/living/silicon/robot/verb/cmd_robot_alerts()
set category = "Robot Commands"
set name = "Show Alerts"
if(usr.stat == DEAD)
to_chat(src, "<span class='userdanger'>Alert: You are dead.</span>")
return //won't work if dead
robot_alerts()
/mob/living/silicon/robot/proc/robot_alerts()
var/list/dat = list()
var/list/list/temp_alarm_list = SSalarm.alarms.Copy()
for(var/cat in temp_alarm_list)
if(!(cat in alarms_listend_for))
continue
dat += text("<B>[cat]</B><BR>\n")
var/list/list/L = temp_alarm_list[cat].Copy()
for(var/alarm in L)
var/list/list/alm = L[alarm].Copy()
var/list/list/sources = alm[3].Copy()
var/area_name = alm[1]
for(var/thing in sources)
var/atom/A = locateUID(thing)
if(A && A.z != z)
L -= alarm
continue
dat += "<NOBR>"
dat += text("-- [area_name]")
dat += "</NOBR><BR>\n"
if(!L.len)
dat += "-- All Systems Nominal<BR>\n"
dat += "<BR>\n"
var/datum/browser/alerts = new(usr, "robotalerts", "Current Station Alerts", 400, 410)
var/dat_text = dat.Join("")
alerts.set_content(dat_text)
alerts.open()
/mob/living/silicon/robot/proc/ionpulse()
if(!ionpulse_on)
return
@@ -605,6 +641,23 @@ GLOBAL_LIST_INIT(robot_verbs_default, list(
/mob/living/silicon/robot/InCritical()
return low_power_mode
/mob/living/silicon/robot/alarm_triggered(src, class, area/A, list/O, obj/alarmsource)
if(!(class in alarms_listend_for))
return
if(alarmsource.z != z)
return
if(stat == DEAD)
return
queueAlarm(text("--- [class] alarm detected in [A.name]!"), class)
/mob/living/silicon/robot/alarm_cancelled(src, class, area/A, obj/origin, cleared)
if(cleared)
if(!(class in alarms_listend_for))
return
if(origin.z != z)
return
queueAlarm("--- [class] alarm in [A.name] has been cleared.", class, 0)
/mob/living/silicon/robot/ex_act(severity)
switch(severity)
if(1.0)
@@ -1024,10 +1077,6 @@ GLOBAL_LIST_INIT(robot_verbs_default, list(
src << browse(null, t1)
return 1
if(href_list["showalerts"])
subsystem_alarm_monitor()
return 1
if(href_list["mod"])
var/obj/item/O = locate(href_list["mod"])
if(istype(O) && (O.loc == src))
@@ -1042,6 +1091,11 @@ GLOBAL_LIST_INIT(robot_verbs_default, list(
activate_module(O)
installed_modules()
//Show alerts window if user clicked on "Show alerts" in chat
if(href_list["showalerts"])
robot_alerts()
return TRUE
if(href_list["deact"])
var/obj/item/O = locate(href_list["deact"])
if(activated(O))
+90 -63
View File
@@ -11,9 +11,11 @@
var/list/stating_laws = list()// Channels laws are currently being stated on
var/list/alarms_to_show = list()
var/list/alarms_to_clear = list()
var/list/alarm_types_show = list("Motion" = 0, "Fire" = 0, "Atmosphere" = 0, "Power" = 0, "Camera" = 0)
var/list/alarm_types_clear = list("Motion" = 0, "Fire" = 0, "Atmosphere" = 0, "Power" = 0, "Camera" = 0)
var/list/alarms_listend_for = list("Motion", "Fire", "Atmosphere", "Power", "Camera")
//var/list/hud_list[10]
var/list/speech_synthesizer_langs = list() //which languages can be vocalized by the speech synthesizer
var/list/alarm_handlers = list() // List of alarm handlers this silicon is registered to
var/designation = ""
var/obj/item/camera/siliconcam/aiCamera = null //photography
//Used in say.dm, allows for pAIs to have different say flavor text, as well as silicons, although the latter is not implemented.
@@ -25,9 +27,6 @@
//var/sensor_mode = 0 //Determines the current HUD.
var/next_alarm_notice
var/list/datum/alarm/queued_alarms = new()
hud_possible = list(SPECIALROLE_HUD, DIAG_STAT_HUD, DIAG_HUD)
@@ -46,6 +45,8 @@
diag_hud_set_health()
add_language("Galactic Common")
init_subsystems()
RegisterSignal(SSalarm, COMSIG_TRIGGERED_ALARM, .proc/alarm_triggered)
RegisterSignal(SSalarm, COMSIG_CANCELLED_ALARM, .proc/alarm_cancelled)
/mob/living/silicon/med_hud_set_health()
return //we use a different hud
@@ -55,10 +56,93 @@
/mob/living/silicon/Destroy()
GLOB.silicon_mob_list -= src
for(var/datum/alarm_handler/AH in alarm_handlers)
AH.unregister(src)
return ..()
/mob/living/silicon/proc/alarm_triggered(src, class, area/A, list/O, obj/alarmsource)
return
/mob/living/silicon/proc/alarm_cancelled(src, class, area/A, obj/origin, cleared)
return
/mob/living/silicon/proc/queueAlarm(message, type, incoming = TRUE)
var/in_cooldown = (alarms_to_show.len > 0 || alarms_to_clear.len > 0)
if(incoming)
alarms_to_show += message
alarm_types_show[type] += 1
else
alarms_to_clear += message
alarm_types_clear[type] += 1
if(in_cooldown)
return
addtimer(CALLBACK(src, .proc/show_alarms), 3 SECONDS)
/mob/living/silicon/proc/show_alarms()
if(alarms_to_show.len < 5)
for(var/msg in alarms_to_show)
to_chat(src, msg)
else if(length(alarms_to_show))
var/list/msg = list("--- ")
if(alarm_types_show["Burglar"])
msg += "BURGLAR: [alarm_types_show["Burglar"]] alarms detected. - "
if(alarm_types_show["Motion"])
msg += "MOTION: [alarm_types_show["Motion"]] alarms detected. - "
if(alarm_types_show["Fire"])
msg += "FIRE: [alarm_types_show["Fire"]] alarms detected. - "
if(alarm_types_show["Atmosphere"])
msg += "ATMOSPHERE: [alarm_types_show["Atmosphere"]] alarms detected. - "
if(alarm_types_show["Power"])
msg += "POWER: [alarm_types_show["Power"]] alarms detected. - "
if(alarm_types_show["Camera"])
msg += "CAMERA: [alarm_types_show["Camera"]] alarms detected. - "
msg += "<A href=?src=[UID()];showalerts=1'>\[Show Alerts\]</a>"
var/msg_text = msg.Join("")
to_chat(src, msg_text)
if(alarms_to_clear.len < 3)
for(var/msg in alarms_to_clear)
to_chat(src, msg)
else if(alarms_to_clear.len)
var/list/msg = list("--- ")
if(alarm_types_clear["Motion"])
msg += "MOTION: [alarm_types_clear["Motion"]] alarms cleared. - "
if(alarm_types_clear["Fire"])
msg += "FIRE: [alarm_types_clear["Fire"]] alarms cleared. - "
if(alarm_types_clear["Atmosphere"])
msg += "ATMOSPHERE: [alarm_types_clear["Atmosphere"]] alarms cleared. - "
if(alarm_types_clear["Power"])
msg += "POWER: [alarm_types_clear["Power"]] alarms cleared. - "
if(alarm_types_show["Camera"])
msg += "CAMERA: [alarm_types_clear["Camera"]] alarms cleared. - "
msg += "<A href=?src=[UID()];showalerts=1'>\[Show Alerts\]</a>"
var/msg_text = msg.Join("")
to_chat(src, msg_text)
alarms_to_show.Cut()
alarms_to_clear.Cut()
for(var/key in alarm_types_show)
alarm_types_show[key] = 0
for(var/key in alarm_types_clear)
alarm_types_clear[key] = 0
/mob/living/silicon/rename_character(oldname, newname)
// we actually don't want it changing minds and stuff
if(!newname)
@@ -283,63 +367,6 @@
if("Disable")
to_chat(src, "Sensor augmentations disabled.")
/mob/living/silicon/proc/receive_alarm(var/datum/alarm_handler/alarm_handler, var/datum/alarm/alarm, was_raised)
if(!next_alarm_notice)
next_alarm_notice = world.time + 10 SECONDS
var/list/alarms = queued_alarms[alarm_handler]
if(was_raised)
// Raised alarms are always set
alarms[alarm] = 1
else
// Alarms that were raised but then cleared before the next notice are instead removed
if(alarm in alarms)
alarms -= alarm
// And alarms that have only been cleared thus far are set as such
else
alarms[alarm] = -1
/mob/living/silicon/proc/process_queued_alarms()
if(next_alarm_notice && (world.time > next_alarm_notice))
next_alarm_notice = 0
var/alarm_raised = 0
for(var/datum/alarm_handler/AH in queued_alarms)
var/list/alarms = queued_alarms[AH]
var/reported = 0
for(var/datum/alarm/A in alarms)
if(alarms[A] == 1)
if(!reported)
reported = 1
to_chat(src, "<span class='warning'>--- [AH.category] Detected ---</span>")
raised_alarm(A)
for(var/datum/alarm_handler/AH in queued_alarms)
var/list/alarms = queued_alarms[AH]
var/reported = 0
for(var/datum/alarm/A in alarms)
if(alarms[A] == -1)
if(!reported)
reported = 1
to_chat(src, "<span class='notice'>--- [AH.category] Cleared ---</span>")
to_chat(src, "\The [A.alarm_name()].")
if(alarm_raised)
to_chat(src, "<A HREF=?src=[UID()];showalerts=1>\[Show Alerts\]</A>")
for(var/datum/alarm_handler/AH in queued_alarms)
var/list/alarms = queued_alarms[AH]
alarms.Cut()
/mob/living/silicon/proc/raised_alarm(var/datum/alarm/A)
to_chat(src, "[A.alarm_name()]!")
/mob/living/silicon/ai/raised_alarm(var/datum/alarm/A)
var/cameratext = ""
for(var/obj/machinery/camera/C in A.cameras())
cameratext += "[(cameratext == "")? "" : "|"]<A HREF=?src=[UID()];switchcamera=\ref[C]>[C.c_tag]</A>"
to_chat(src, "[A.alarm_name()]! ([(cameratext)? cameratext : "No Camera"])")
/mob/living/silicon/adjustToxLoss(var/amount)
return STATUS_UPDATE_NONE
@@ -8,13 +8,11 @@
/mob/living/silicon
var/list/silicon_subsystems = list(
/mob/living/silicon/proc/subsystem_alarm_monitor,
/mob/living/silicon/proc/subsystem_law_manager
)
/mob/living/silicon/ai
silicon_subsystems = list(
/mob/living/silicon/proc/subsystem_alarm_monitor,
/mob/living/silicon/proc/subsystem_atmos_control,
/mob/living/silicon/proc/subsystem_crew_monitor,
/mob/living/silicon/proc/subsystem_law_manager,
@@ -23,7 +21,6 @@
/mob/living/silicon/robot/drone
silicon_subsystems = list(
/mob/living/silicon/proc/subsystem_alarm_monitor,
/mob/living/silicon/proc/subsystem_law_manager,
/mob/living/silicon/proc/subsystem_power_monitor
)
@@ -32,30 +29,11 @@
register_alarms = 0
/mob/living/silicon/proc/init_subsystems()
alarm_monitor = new(src)
atmos_control = new(src)
crew_monitor = new(src)
law_manager = new(src)
power_monitor = new(src)
if(!register_alarms)
return
var/list/register_to = list(SSalarms.atmosphere_alarm, SSalarms.burglar_alarm, SSalarms.camera_alarm, SSalarms.fire_alarm, SSalarms.motion_alarm, SSalarms.power_alarm)
for(var/datum/alarm_handler/AH in register_to)
AH.register(src, /mob/living/silicon/proc/receive_alarm)
queued_alarms[AH] = list() // Makes sure alarms remain listed in consistent order
alarm_handlers |= AH
/********************
* Alarm Monitor *
********************/
/mob/living/silicon/proc/subsystem_alarm_monitor()
set name = "Alarm Monitor"
set category = "Subsystems"
alarm_monitor.ui_interact(usr, state = GLOB.self_state)
/********************
* Atmos Control *
********************/
@@ -35,7 +35,7 @@
/obj/machinery/modular_computer/console/preset/engineering/install_programs()
var/obj/item/computer_hardware/hard_drive/hard_drive = cpu.all_components[MC_HDD]
hard_drive.store_file(new/datum/computer_file/program/power_monitor())
hard_drive.store_file(new/datum/computer_file/program/alarm_monitor())
// hard_drive.store_file(new/datum/computer_file/program/alarm_monitor()) //TO-DO:TGUI--Uncomment Modular computers
hard_drive.store_file(new/datum/computer_file/program/supermatter_monitor())
// ===== RESEARCH CONSOLE =====
@@ -7,65 +7,71 @@
requires_ntnet = 1
network_destination = "alarm monitoring network"
size = 5
var/list/datum/alarm_handler/alarm_handlers
var/tgui_id = "NtosStationAlertConsole"
var/ui_x = 315
var/ui_y = 500
var/has_alert = 0
var/list/alarms_listend_for = list("Fire", "Atmosphere", "Power")
/datum/computer_file/program/alarm_monitor/New()
/datum/computer_file/program/alarm_monitor/process_tick()
..()
alarm_handlers = list(SSalarms.atmosphere_alarm, SSalarms.fire_alarm, SSalarms.power_alarm)
for(var/datum/alarm_handler/AH in alarm_handlers)
AH.register(src, /datum/computer_file/program/alarm_monitor/proc/update_icon)
/datum/computer_file/program/alarm_monitor/Destroy()
for(var/datum/alarm_handler/AH in alarm_handlers)
AH.unregister(src)
QDEL_NULL(alarm_handlers)
return ..()
if(has_alert)
program_icon_state = "alert-red"
ui_header = "alarm_red.gif"
update_computer_icon()
else
program_icon_state = "alert-green"
ui_header = "alarm_green.gif"
update_computer_icon()
return TRUE
/datum/computer_file/program/alarm_monitor/proc/update_icon()
for(var/datum/alarm_handler/AH in alarm_handlers)
if(AH.has_major_alarms())
program_icon_state = "alert-red"
ui_header = "alarm_red.gif"
update_computer_icon()
return 1
program_icon_state = "alert-green"
ui_header = "alarm_green.gif"
update_computer_icon()
return 0
/datum/computer_file/program/alarm_monitor/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
var/datum/asset/assets = get_asset_datum(/datum/asset/simple/headers)
assets.send(user)
ui = new(user, src, ui_key, "alarm_monitor.tmpl", "Alarm Monitoring", 575, 700)
ui.set_auto_update(1)
ui.set_layout_key("program")
ui.open()
/datum/computer_file/program/alarm_monitor/ui_data(mob/user)
/datum/computer_file/program/alarm_monitor/tgui_data(mob/user)
var/list/data = get_header_data()
var/categories[0]
for(var/datum/alarm_handler/AH in alarm_handlers)
categories[++categories.len] = list("category" = AH.category, "alarms" = list())
for(var/datum/alarm/A in AH.major_alarms())
var/cameras[0]
var/lost_sources[0]
if(isAI(user))
for(var/obj/machinery/camera/C in A.cameras())
cameras[++cameras.len] = C.nano_structure()
for(var/datum/alarm_source/AS in A.sources)
if(!AS.source)
lost_sources[++lost_sources.len] = AS.source_name
categories[categories.len]["alarms"] += list(list(
"name" = sanitize(A.alarm_name()),
"origin_lost" = A.origin == null,
"has_cameras" = cameras.len,
"cameras" = cameras,
"lost_sources" = lost_sources.len ? sanitize(english_list(lost_sources, nothing_text = "", and_text = ", ")) : ""))
data["categories"] = categories
data["alarms"] = list()
for(var/class in SSalarm.alarms)
if(!(class in alarms_listend_for))
continue
data["alarms"][class] = list()
for(var/area in alarms[class])
data["alarms"][class] += area
return data
/datum/computer_file/program/alarm_monitor/proc/alarm_triggered(src, class, area/A, list/O, obj/alarmsource)
if(is_station_level(alarmsource.z))
if(!(A.type in GLOB.the_station_areas))
return
else if(!is_mining_level(alarmsource.z) || istype(A, /area/ruin))
return
update_alarm_display()
/datum/computer_file/program/alarm_monitor/proc/alarm_cancelled(src, class, area/A, obj/origin, cleared)
if(is_station_level(origin.z))
if(!(A.type in GLOB.the_station_areas))
return
else if(!is_mining_level(origin.z) || istype(A, /area/ruin))
return
update_alarm_display()
/datum/computer_file/program/alarm_monitor/proc/update_alarm_display()
has_alert = FALSE
for(var/cat in alarms)
if(!(cat in alarms_listend_for))
continue
var/list/L = alarms[cat]
if(length(L))
has_alert = TRUE
/datum/computer_file/program/alarm_monitor/run_program(mob/user)
. = ..(user)
GLOB.alarmdisplay += src
RegisterSignal(SSalarm, COMSIG_TRIGGERED_ALARM, .proc/alarm_triggered)
RegisterSignal(SSalarm, COMSIG_CANCELLED_ALARM, .proc/alarm_cancelled)
/datum/computer_file/program/alarm_monitor/kill_program(forced = FALSE)
GLOB.alarmdisplay -= src
UnregisterSignal(SSalarm, COMSIG_TRIGGERED_ALARM)
UnregisterSignal(SSalarm, COMSIG_CANCELLED_ALARM)
..()
@@ -1,90 +0,0 @@
/datum/nano_module/alarm_monitor
name = "Alarm monitor"
var/list_cameras = 0 // Whether or not to list camera references. A future goal would be to merge this with the enginering/security camera console. Currently really only for AI-use.
var/list/datum/alarm_handler/alarm_handlers // The particular list of alarm handlers this alarm monitor should present to the user.
/datum/nano_module/alarm_monitor/all/New()
..()
alarm_handlers = list(SSalarms.atmosphere_alarm, SSalarms.burglar_alarm, SSalarms.camera_alarm, SSalarms.fire_alarm, SSalarms.motion_alarm, SSalarms.power_alarm)
/datum/nano_module/alarm_monitor/engineering/New()
..()
alarm_handlers = list(SSalarms.atmosphere_alarm, SSalarms.fire_alarm, SSalarms.power_alarm)
/datum/nano_module/alarm_monitor/security/New()
..()
alarm_handlers = list(SSalarms.burglar_alarm, SSalarms.camera_alarm, SSalarms.motion_alarm)
/datum/nano_module/alarm_monitor/proc/register(var/object, var/procName)
for(var/datum/alarm_handler/AH in alarm_handlers)
AH.register(object, procName)
/datum/nano_module/alarm_monitor/proc/unregister(var/object)
for(var/datum/alarm_handler/AH in alarm_handlers)
AH.unregister(object)
/datum/nano_module/alarm_monitor/proc/all_alarms()
var/list/all_alarms = new()
for(var/datum/alarm_handler/AH in alarm_handlers)
all_alarms += AH.alarms
return all_alarms
/datum/nano_module/alarm_monitor/proc/major_alarms()
var/list/all_alarms = new()
for(var/datum/alarm_handler/AH in alarm_handlers)
all_alarms += AH.major_alarms()
return all_alarms
/datum/nano_module/alarm_monitor/proc/minor_alarms()
var/list/all_alarms = new()
for(var/datum/alarm_handler/AH in alarm_handlers)
all_alarms += AH.minor_alarms()
return all_alarms
/datum/nano_module/alarm_monitor/Topic(ref, href_list)
if(..())
return 1
if(href_list["switchTo"])
var/obj/machinery/camera/C = locate(href_list["switchTo"]) in GLOB.cameranet.cameras
if(!C || !isAI(usr))
return
usr.switch_to_camera(C)
return 1
/datum/nano_module/alarm_monitor/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = GLOB.default_state)
ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
ui = new(user, src, ui_key, "alarm_monitor.tmpl", "Alarm Monitoring Console", 800, 800, state = state)
ui.open()
ui.set_auto_update(1)
/datum/nano_module/alarm_monitor/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state)
var/data[0]
var/categories[0]
for(var/datum/alarm_handler/AH in alarm_handlers)
categories[++categories.len] = list("category" = AH.category, "alarms" = list())
for(var/datum/alarm/A in AH.major_alarms())
var/cameras[0]
var/lost_sources[0]
if(isAI(user))
for(var/obj/machinery/camera/C in A.cameras())
cameras[++cameras.len] = C.nano_structure()
for(var/datum/alarm_source/AS in A.sources)
if(!AS.source)
lost_sources[++lost_sources.len] = AS.source_name
categories[categories.len]["alarms"] += list(list(
"name" = sanitize(A.alarm_name()),
"origin_lost" = A.origin == null,
"has_cameras" = cameras.len,
"cameras" = cameras,
"lost_sources" = lost_sources.len ? sanitize(english_list(lost_sources, nothing_text = "", and_text = ", ")) : ""))
data["categories"] = categories
return data
+11 -12
View File
@@ -216,8 +216,7 @@
area = A
name = "\improper [area.name] APC"
else
area = get_area_name(areastring)
name = "\improper [area.name] APC"
name = "\improper [get_area_name(area, TRUE)] APC"
area.apc |= src
update_icon()
@@ -1192,31 +1191,31 @@
lighting = autoset(lighting, 1)
environ = autoset(environ, 1)
autoflag = 3
if(report_power_alarm && is_station_contact(z))
SSalarms.power_alarm.clearAlarm(loc, src)
if(report_power_alarm)
area.poweralert(TRUE, src)
else if(cell.charge < 1250 && cell.charge > 750 && longtermpower < 0) // <30%, turn off equipment
if(autoflag != 2)
equipment = autoset(equipment, 2)
lighting = autoset(lighting, 1)
environ = autoset(environ, 1)
if(report_power_alarm && is_station_contact(z))
SSalarms.power_alarm.triggerAlarm(loc, src)
if(report_power_alarm)
area.poweralert(FALSE, src)
autoflag = 2
else if(cell.charge < 750 && cell.charge > 10) // <15%, turn off lighting & equipment
if((autoflag > 1 && longtermpower < 0) || (autoflag > 1 && longtermpower >= 0))
equipment = autoset(equipment, 2)
lighting = autoset(lighting, 2)
environ = autoset(environ, 1)
if(report_power_alarm && is_station_contact(z))
SSalarms.power_alarm.triggerAlarm(loc, src)
if(report_power_alarm)
area.poweralert(FALSE, src)
autoflag = 1
else if(cell.charge <= 0) // zero charge, turn all off
if(autoflag != 0)
equipment = autoset(equipment, 0)
lighting = autoset(lighting, 0)
environ = autoset(environ, 0)
if(report_power_alarm && is_station_contact(z))
SSalarms.power_alarm.triggerAlarm(loc, src)
if(report_power_alarm)
area.poweralert(FALSE, src)
autoflag = 0
// now trickle-charge the cell
@@ -1271,8 +1270,8 @@
equipment = autoset(equipment, 0)
lighting = autoset(lighting, 0)
environ = autoset(environ, 0)
if(report_power_alarm && is_station_contact(z))
SSalarms.power_alarm.triggerAlarm(loc, src)
if(report_power_alarm)
area.poweralert(FALSE, src)
autoflag = 0
// update icon & area power if anything changed
+21 -5
View File
@@ -177,6 +177,8 @@
var/nightshift_light_power = 0.45
var/nightshift_light_color = "#FFDDCC"
var/bulb_emergency_colour = "#FF3232" // determines the colour of the light while it's in emergency mode
// the smaller bulb light fixture
/obj/machinery/light/small
@@ -238,7 +240,11 @@
switch(status) // set icon_states
if(LIGHT_OK)
icon_state = "[base_state][on]"
var/area/A = get_area(src)
if(A && A.fire)
icon_state = "[base_state]_emergency"
else
icon_state = "[base_state][on]"
if(LIGHT_EMPTY)
icon_state = "[base_state]-empty"
on = FALSE
@@ -260,10 +266,20 @@
on = FALSE
update_icon()
if(on)
var/BR = nightshift_enabled ? nightshift_light_range : brightness_range
var/PO = nightshift_enabled ? nightshift_light_power : brightness_power
var/CO = nightshift_enabled ? nightshift_light_color : brightness_color
var/matching = light_range == BR && light_power == PO && light_color == CO
var/BR = brightness_range
var/PO = brightness_power
var/CO = brightness_color
if(color)
CO = color
var/area/A = get_area(src)
if(A && A.fire)
CO = bulb_emergency_colour
else if(nightshift_enabled)
BR = nightshift_light_range
PO = nightshift_light_power
if(!color)
CO = nightshift_light_color
var/matching = light && BR == light.light_range && PO == light.light_power && CO == light.light_color
if(!matching)
switchcount++
if(rigged)
@@ -1,4 +1,4 @@
/area/engine/engineering/power_alert(var/alarming)
if(alarming)
investigate_log("has a power alarm!","singulo")
/area/engine/engineering/poweralert(state, source)
if(state != poweralm)
investigate_log("has a power alarm!", "singulo")
..()