diff --git a/baystation12.dme b/baystation12.dme
index 7e8f14d39e..fcfb95c66e 100644
--- a/baystation12.dme
+++ b/baystation12.dme
@@ -1144,8 +1144,8 @@
#include "code\modules\mob\living\silicon\ai\life.dm"
#include "code\modules\mob\living\silicon\ai\login.dm"
#include "code\modules\mob\living\silicon\ai\logout.dm"
-#include "code\modules\mob\living\silicon\ai\nano.dm"
#include "code\modules\mob\living\silicon\ai\say.dm"
+#include "code\modules\mob\living\silicon\ai\subsystems.dm"
#include "code\modules\mob\living\silicon\ai\freelook\cameranet.dm"
#include "code\modules\mob\living\silicon\ai\freelook\chunk.dm"
#include "code\modules\mob\living\silicon\ai\freelook\eye.dm"
@@ -1178,6 +1178,7 @@
#include "code\modules\mob\living\silicon\robot\robot_items.dm"
#include "code\modules\mob\living\silicon\robot\robot_modules.dm"
#include "code\modules\mob\living\silicon\robot\robot_movement.dm"
+#include "code\modules\mob\living\silicon\robot\subsystems.dm"
#include "code\modules\mob\living\silicon\robot\drone\drone.dm"
#include "code\modules\mob\living\silicon\robot\drone\drone_abilities.dm"
#include "code\modules\mob\living\silicon\robot\drone\drone_console.dm"
@@ -1236,6 +1237,7 @@
#include "code\modules\nano\nanomapgen.dm"
#include "code\modules\nano\nanoprocs.dm"
#include "code\modules\nano\nanoui.dm"
+#include "code\modules\nano\modules\alarm_monitor.dm"
#include "code\modules\nano\modules\crew_monitor.dm"
#include "code\modules\nano\modules\power_monitor.dm"
#include "code\modules\nano\modules\rcon.dm"
diff --git a/code/__HELPERS/lists.dm b/code/__HELPERS/lists.dm
index 1e8ccb6a4d..9fd6a8c0e9 100644
--- a/code/__HELPERS/lists.dm
+++ b/code/__HELPERS/lists.dm
@@ -596,3 +596,7 @@ datum/proc/dd_SortValue()
/obj/machinery/camera/dd_SortValue()
return "[c_tag]"
+
+/datum/alarm/dd_SortValue()
+ return "[sanitize(last_name)]"
+
diff --git a/code/controllers/subsystem/alarms.dm b/code/controllers/subsystem/alarms.dm
index 4684737578..a13e60782d 100644
--- a/code/controllers/subsystem/alarms.dm
+++ b/code/controllers/subsystem/alarms.dm
@@ -1,27 +1,33 @@
-/* /var/global/datum/alarm_handler/atmosphere_alarm = new()*/
-/var/global/datum/alarm_handler/camera_alarm = new()
-/* /var/global/datum/alarm_handler/fire_alarm = new()*/
-/var/global/datum/alarm_handler/motion_alarm = new()
-/* /var/global/datum/alarm_handler/power_alarm = new() */
+// We manually initialize the alarm handlers instead of looping over all existing types
+// to make it possible to write: camera.triggerAlarm() rather than alarm_manager.managers[datum/alarm_handler/camera].triggerAlarm() or a variant thereof.
+/var/global/datum/alarm_handler/atmosphere/atmosphere_alarm = new()
+/var/global/datum/alarm_handler/camera/camera_alarm = new()
+/var/global/datum/alarm_handler/fire/fire_alarm = new()
+/var/global/datum/alarm_handler/motion/motion_alarm = new()
+/var/global/datum/alarm_handler/power/power_alarm = new()
/datum/subsystem/alarm
name = "Alarm"
var/list/datum/alarm/all_handlers
/datum/subsystem/alarm/New()
- all_handlers = list(camera_alarm)
+ all_handlers = list(atmosphere_alarm, camera_alarm, fire_alarm, motion_alarm, power_alarm)
/datum/subsystem/alarm/stat_entry()
- stat(null,"Alarm-[master_controller.alarms_cost]\t# [active_alarms()]")
+ stat(null,"Alarm-[master_controller.alarms_cost]\t#[number_of_active_alarms()]")
/datum/subsystem/alarm/fire()
for(var/datum/alarm_handler/AH in all_handlers)
AH.process()
/datum/subsystem/alarm/proc/active_alarms()
- var/total = 0
+ var/list/all_alarms = new
for(var/datum/alarm_handler/AH in all_handlers)
var/list/alarms = AH.alarms
- total += alarms.len
+ all_alarms += alarms
- return total
+ return all_alarms
+
+/datum/subsystem/alarm/proc/number_of_active_alarms()
+ var/list/alarms = active_alarms()
+ return alarms.len
diff --git a/code/game/machinery/alarm.dm b/code/game/machinery/alarm.dm
index 2ffdcc6e2f..c9a10bc728 100644
--- a/code/game/machinery/alarm.dm
+++ b/code/game/machinery/alarm.dm
@@ -460,10 +460,12 @@
if(alert_level==2)
alert_signal.data["alert"] = "severe"
+ atmosphere_alarm.triggerAlarm(src.loc, src)
else if (alert_level==1)
alert_signal.data["alert"] = "minor"
else if (alert_level==0)
alert_signal.data["alert"] = "clear"
+ atmosphere_alarm.clearAlarm(src.loc, src)
frequency.post_signal(src, alert_signal)
diff --git a/code/game/machinery/camera/camera.dm b/code/game/machinery/camera/camera.dm
index d6640f6534..77f41cb2eb 100644
--- a/code/game/machinery/camera/camera.dm
+++ b/code/game/machinery/camera/camera.dm
@@ -53,13 +53,6 @@
ASSERT(src.network.len > 0)
..()
-/obj/machinery/camera/Del()
- if(!alarm_on)
- triggerCameraAlarm()
-
- cancelCameraAlarm()
- ..()
-
/obj/machinery/camera/emp_act(severity)
if(!isEmpProof())
if(prob(100/severity))
@@ -67,7 +60,7 @@
stat |= EMPED
SetLuminosity(0)
kick_viewers()
- triggerCameraAlarm()
+ triggerCameraAlarm(10 * severity)
update_icon()
spawn(900)
@@ -261,16 +254,16 @@
else
icon_state = initial(icon_state)
-/obj/machinery/camera/proc/triggerCameraAlarm()
+/obj/machinery/camera/proc/triggerCameraAlarm(var/duration = 0)
alarm_on = 1
- camera_alarm.triggerAlarm(loc, src)
+ camera_alarm.triggerAlarm(loc, src, duration)
/obj/machinery/camera/proc/cancelCameraAlarm()
if(wires.IsIndexCut(CAMERA_WIRE_ALARM))
return
alarm_on = 0
- camera_alarm.cancelAlarm(loc, src)
+ camera_alarm.clearAlarm(loc, src)
//if false, then the camera is listed as DEACTIVATED and cannot be used
/obj/machinery/camera/proc/can_use()
@@ -355,3 +348,13 @@
user.set_machine(src)
wires.Interact(user)
+
+/obj/machinery/camera/proc/nano_structure()
+ var/cam[0]
+ cam["name"] = sanitize(c_tag)
+ cam["deact"] = !can_use()
+ cam["camera"] = "\ref[src]"
+ cam["x"] = x
+ cam["y"] = y
+ cam["z"] = z
+ return cam
diff --git a/code/game/machinery/camera/motion.dm b/code/game/machinery/camera/motion.dm
index f14608b1d4..636f114de7 100644
--- a/code/game/machinery/camera/motion.dm
+++ b/code/game/machinery/camera/motion.dm
@@ -45,7 +45,7 @@
if (!status || (stat & NOPOWER))
return 0
if (detectTime == -1)
- motion_alarm.cancelAlarm(loc, src)
+ motion_alarm.clearAlarm(loc, src)
detectTime = 0
return 1
diff --git a/code/game/machinery/computer/camera.dm b/code/game/machinery/computer/camera.dm
index b1100fd77c..8cb58a14cb 100644
--- a/code/game/machinery/computer/camera.dm
+++ b/code/game/machinery/computer/camera.dm
@@ -3,7 +3,7 @@
/proc/invalidateCameraCache()
for(var/obj/machinery/computer/security/s in world)
s.camera_cache = null
- for(var/datum/alarm/A in alarm_manager.active_alarms())
+ for(var/datum/alarm/A in world)
A.cameras = null
/obj/machinery/computer/security
@@ -45,33 +45,17 @@
if(!can_access_camera(C))
continue
- var/cam[0]
- cam["name"] = sanitize(C.c_tag)
- cam["deact"] = !C.can_use()
- cam["camera"] = "\ref[C]"
- cam["x"] = C.x
- cam["y"] = C.y
- cam["z"] = C.z
-
+ var/cam = C.nano_structure()
cameras[++cameras.len] = cam
if(C == current)
data["current"] = cam
- var/list/camera_list = list("cameras" = cameras)
- camera_cache=list2json(camera_list)
-
+ var/list/camera_list = list("cameras" = cameras)
+ camera_cache=list2json(camera_list)
else
if(current)
- var/cam[0]
- cam["name"] = current.c_tag
- cam["deact"] = !current.can_use()
- cam["camera"] = "\ref[current]"
- cam["x"] = current.x
- cam["y"] = current.y
- cam["z"] = current.z
-
- data["current"] = cam
+ data["current"] = current.nano_structure()
if(ui)
diff --git a/code/game/machinery/computer/station_alert.dm b/code/game/machinery/computer/station_alert.dm
index 028be6e3a3..f585162ae9 100644
--- a/code/game/machinery/computer/station_alert.dm
+++ b/code/game/machinery/computer/station_alert.dm
@@ -5,106 +5,42 @@
icon_state = "alert:0"
circuit = "/obj/item/weapon/circuitboard/stationalert"
var/alarms = list("Fire"=list(), "Atmosphere"=list(), "Power"=list())
+ var/obj/nano_module/alarm_monitor/engineering/alarm_monitor
+/obj/machinery/computer/station_alert/New()
+ ..()
+ alarm_monitor = new(src)
+ alarm_monitor.register(src, /obj/machinery/computer/station_alert/update_icon)
- attack_ai(mob/user)
- add_fingerprint(user)
- if(stat & (BROKEN|NOPOWER))
- return
- interact(user)
+/obj/machinery/computer/station_alert/Del()
+ alarm_monitor.unregister(src)
+ ..()
+
+/obj/machinery/computer/station_alert/attack_ai(mob/user)
+ add_fingerprint(user)
+ if(stat & (BROKEN|NOPOWER))
+ return
+ interact(user)
+ return
+
+/obj/machinery/computer/station_alert/attack_hand(mob/user)
+ add_fingerprint(user)
+ if(stat & (BROKEN|NOPOWER))
+ return
+ interact(user)
+ return
+
+/obj/machinery/computer/station_alert/interact(mob/user)
+ alarm_monitor.ui_interact(user)
+
+/obj/machinery/computer/station_alert/update_icon()
+ ..()
+ if(stat & (BROKEN|NOPOWER))
return
-
- attack_hand(mob/user)
- add_fingerprint(user)
- if(stat & (BROKEN|NOPOWER))
- return
- interact(user)
- return
-
-
- interact(mob/user)
- usr.set_machine(src)
- var/dat = "
Current Station Alerts\n"
- dat += "Close
"
- for (var/cat in src.alarms)
- dat += text("[]
\n", cat)
- var/list/L = src.alarms[cat]
- if (L.len)
- for (var/alarm in L)
- var/list/alm = L[alarm]
- var/area/A = alm[1]
- var/list/sources = alm[3]
- dat += ""
- dat += "• "
- dat += "[A.name]"
- if (sources.len > 1)
- dat += text(" - [] sources", sources.len)
- dat += "
\n"
- else
- dat += "-- All Systems Nominal
\n"
- dat += "
\n"
- user << browse(dat, "window=alerts")
- onclose(user, "alerts")
-
-
- Topic(href, href_list)
- if(..())
- return
- return
-
-
- proc/triggerAlarm(var/class, area/A, var/O, var/alarmsource)
- if(stat & (BROKEN))
- return
- var/list/L = src.alarms[class]
- for (var/I in L)
- if (I == A.name)
- var/list/alarm = L[I]
- var/list/sources = alarm[3]
- if (!(alarmsource in sources))
- sources += alarmsource
- 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(alarmsource))
- return 1
-
-
- proc/cancelAlarm(var/class, area/A as area, obj/origin)
- if(stat & (BROKEN))
- return
- var/list/L = src.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
- return !cleared
-
-
- process()
- if(stat & (BROKEN|NOPOWER))
- icon_state = "atmos0"
- return
- var/active_alarms = 0
- for (var/cat in src.alarms)
- var/list/L = src.alarms[cat]
- if(L.len) active_alarms = 1
- if(active_alarms)
- icon_state = "alert:2"
- else
- icon_state = "alert:0"
- ..()
- return
+ var/list/alarms = alarm_monitor.active_alarms()
+ if(alarms.len)
+ icon_state = "alert:2"
+ else
+ icon_state = "alert:0"
+ return
diff --git a/code/modules/alarm/alarm.dm b/code/modules/alarm/alarm.dm
index 0bbeed527d..623c38c503 100644
--- a/code/modules/alarm/alarm.dm
+++ b/code/modules/alarm/alarm.dm
@@ -1,3 +1,5 @@
+#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)
@@ -7,8 +9,8 @@
/datum/alarm_source/New(var/atom/source)
src.source = source
- source_name = source.name
start_time = world.time
+ source_name = source.get_source_name()
/datum/alarm
var/atom/origin //Used to identify the alarm area.
@@ -16,24 +18,42 @@
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)
src.origin = origin
- last_area = alarm_area()
+
+ cameras() // Sets up both cameras and last alarm area.
set_duration(source, duration)
+/datum/alarm/proc/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_duration(var/atom/source, var/duration)
- var/datum/alarm_source/AS = sources[source]
+ 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 = SecondsToTicks(duration)
AS.duration = duration
/datum/alarm/proc/clear(var/source)
- var/datum/alarm_source/AS = sources[source]
+ var/datum/alarm_source/AS = sources_assoc[source]
sources -= AS
sources_assoc -= source
@@ -44,22 +64,51 @@
last_area = origin.get_alarm_area()
return last_area
-/datum/alarm/proc/cameras()
+/datum/alarm/proc/alarm_name()
if(!origin)
- return list()
+ 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.get_alarm_cameras()
+ cameras = origin ? origin.get_alarm_cameras() : last_area.get_alarm_cameras()
+ last_camera_area = last_area
return cameras
+/******************
+* Assisting procs *
+******************/
/atom/proc/get_alarm_area()
return get_area(src)
/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()
@@ -76,3 +125,5 @@
/mob/living/silicon/robot/syndicate/get_alarm_cameras()
return list()
+
+#undef ALARM_LOSS_DELAY
diff --git a/code/modules/alarm/alarm_handler.dm b/code/modules/alarm/alarm_handler.dm
index 3fd21fc4a0..9c92a7793a 100644
--- a/code/modules/alarm/alarm_handler.dm
+++ b/code/modules/alarm/alarm_handler.dm
@@ -1,64 +1,82 @@
-#define ALARM_ORIGIN_LOST "Origin Lost"
+#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 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/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/proc/process()
- /*
for(var/datum/alarm/A in alarms)
- var/datum/alarm_source/AS = A.source
- // Alarm owner has been deleted. Clean up in at most 15 seconds
- if(!AS.owner && !AS.end_time)
- AS.end_time = world.time + SecondsToTicks(15)
- if(AS.duration || AS.end_time)
- if(world.time > (AS.start_time + AS.duration) || world.time > AS.end_time)
- //Somethingthing..
- */
+ A.process()
+ check_alarm_cleared(A)
/datum/alarm_handler/proc/triggerAlarm(var/atom/origin, var/atom/source, var/duration = 0)
+ var/new_alarm
//Proper origin and source mandatory
if(!origin || !source)
return
+ new_alarm = 0
//see if there is already an alarm of this origin
- var/alarm_key = origin.get_alarm_key()
- var/datum/alarm/existing = alarms_assoc[alarm_key]
+ var/datum/alarm/existing = alarms_assoc[origin]
if(existing)
existing.set_duration(source, duration)
else
existing = new/datum/alarm(origin, source, duration)
+ new_alarm = 1
alarms |= existing
- alarms_assoc[alarm_key] = existing
+ alarms_assoc[origin] = existing
+ if(new_alarm)
+ alarms = dd_sortedObjectList(alarms)
+ notify_listeners(existing, ALARM_RAISED)
-/datum/alarm_handler/proc/cancelAlarm(var/atom/origin, var/source)
+ return new_alarm
+
+/datum/alarm_handler/proc/clearAlarm(var/atom/origin, var/source)
//Proper origin and source mandatory
if(!origin || !source)
return
- var/alarm_key = origin.get_alarm_key()
-
- var/datum/alarm/existing = alarms_assoc[alarm_key]
+ var/datum/alarm/existing = alarms_assoc[origin]
if(existing)
existing.clear(source)
- if (!existing.sources.len)
- alarms -= existing
- alarms_assoc -= alarm_key
+ return check_alarm_cleared(existing)
-/atom/proc/get_alarm_key()
- return src
+/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
+ notify_listeners(alarm, ALARM_CLEARED)
+ return 1
+ return 0
-/turf/get_alarm_key()
- return get_area(src)
+/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)
+
+/********
+* DEBUG *
+********/
/obj/item/device/alarm_debug
name = "An alarm debug tool - Self"
desc = "Alarm Up. Alarm Reset."
icon = 'icons/obj/radio.dmi'
icon_state = "beacon"
item_state = "signaler"
+ var/obj/nano_module/alarm_monitor/ai/alarm_monitor
+
+/obj/item/device/alarm_debug/New()
+ ..()
+ alarm_monitor = new(src)
/obj/item/device/alarm_debug/loc
name = "An alarm debug tool - Loc"
@@ -67,46 +85,33 @@
set name = "Alarm"
set category = "Debug"
usr << "Raising alarm"
- camera_alarm.triggerAlarm(src, src)
+ fire_alarm.triggerAlarm(src, src)
/obj/item/device/alarm_debug/verb/reset()
set name = "Reset"
set category = "Debug"
- usr << "Raising alarm"
- camera_alarm.triggerAlarm(src, src)
-
-/obj/item/device/alarm_debug/verb/tell_me()
- set name = "Tell"
- set category = "Debug"
- usr << "Telling about alarms"
-
- var/list/datum/alarm/alarms = camera_alarm.alarms
- var/list/datum/alarm/alarms_assoc = camera_alarm.alarms_assoc
-
- world << "List"
- for(var/datum/alarm/A in alarms)
- world << "Origin: [A.origin ? A.origin : ALARM_ORIGIN_LOST]"
- world << "Alarm area: [A.alarm_area()]"
- for(var/source in A.sources)
- world << "Source: [source]"
-
- world << "Assoc"
-
- for(var/atom/origin in alarms_assoc)
- world << "Origin: [origin ? origin : ALARM_ORIGIN_LOST]"
- var/datum/alarm/A = alarms_assoc[origin]
- world << "Alarm area: [A.alarm_area()]"
- for(var/source in A.sources)
- world << "Source: [source]"
+ usr << "Clearing alarm"
+ fire_alarm.clearAlarm(src, src)
/obj/item/device/alarm_debug/loc/alarm()
set name = "Alarm"
set category = "Debug"
usr << "Raising alarm"
- camera_alarm.triggerAlarm(src.loc, src)
+ fire_alarm.triggerAlarm(src.loc, src)
/obj/item/device/alarm_debug/loc/reset()
set name = "Reset"
set category = "Debug"
usr << "Clearing alarm"
- camera_alarm.cancelAlarm(src.loc, src)
+ fire_alarm.clearAlarm(src.loc, src)
+
+/obj/item/device/alarm_debug/verb/nano()
+ set name = "Nano"
+ set category = "Debug"
+ alarm_monitor.ui_interact(usr)
+
+/obj/item/device/alarm_debug/attack_self(var/mob/user)
+ alarm_monitor.ui_interact(user)
+
+#undef ALARM_RAISED
+#undef ALARM_CLEARED
diff --git a/code/modules/alarm/fire_alarm.dm b/code/modules/alarm/fire_alarm.dm
index e654b966b6..1b7256517e 100644
--- a/code/modules/alarm/fire_alarm.dm
+++ b/code/modules/alarm/fire_alarm.dm
@@ -1,2 +1,2 @@
-/datum/alarm_handler/fire_alarm
+/datum/alarm_handler/fire
category = "Fire"
diff --git a/code/modules/mob/living/silicon/ai/ai.dm b/code/modules/mob/living/silicon/ai/ai.dm
index 306be81c88..a0781d7073 100644
--- a/code/modules/mob/living/silicon/ai/ai.dm
+++ b/code/modules/mob/living/silicon/ai/ai.dm
@@ -3,7 +3,6 @@
var/list/ai_list = list()
var/list/ai_verbs_default = list(
- /mob/living/silicon/ai/proc/ai_alerts,
/mob/living/silicon/ai/proc/ai_announcement,
/mob/living/silicon/ai/proc/ai_call_shuttle,
// /mob/living/silicon/ai/proc/ai_recall_shuttle,
@@ -23,10 +22,7 @@ var/list/ai_verbs_default = list(
/mob/living/silicon/ai/proc/sensor_mode,
/mob/living/silicon/ai/proc/show_laws_verb,
/mob/living/silicon/ai/proc/toggle_acceleration,
- /mob/living/silicon/ai/proc/toggle_camera_light,
- /mob/living/silicon/ai/proc/nano_rcon,
- /mob/living/silicon/ai/proc/nano_crew_monitor,
- /mob/living/silicon/ai/proc/nano_power_monitor
+ /mob/living/silicon/ai/proc/toggle_camera_light
)
//Not sure why this is necessary...
@@ -83,9 +79,11 @@ var/list/ai_verbs_default = list(
/mob/living/silicon/ai/proc/add_ai_verbs()
src.verbs |= ai_verbs_default
+ src.verbs |= ai_verbs_subsystems
/mob/living/silicon/ai/proc/remove_ai_verbs()
src.verbs -= ai_verbs_default
+ src.verbs -= ai_verbs_subsystems
/mob/living/silicon/ai/New(loc, var/datum/ai_laws/L, var/obj/item/device/mmi/B, var/safety = 0)
announcement = new()
@@ -166,8 +164,6 @@ var/list/ai_verbs_default = list(
hud_list[IMPTRACK_HUD] = image('icons/mob/hud.dmi', src, "hudblank")
hud_list[SPECIALROLE_HUD] = image('icons/mob/hud.dmi', src, "hudblank")
- init_subsystems()
-
ai_list += src
..()
return
@@ -325,13 +321,6 @@ var/list/ai_verbs_default = list(
if(malf && malf.apcs >= 3)
stat(null, "Time until station control secured: [max(malf.AI_win_timeleft/(malf.apcs/3), 0)] seconds")
-/mob/living/silicon/ai/proc/ai_alerts()
- set category = "AI Commands"
- set name = "Show Alerts"
-
- //PsiFix
- //nano_alarm.ui_interact(usr)
-
// this verb lets the ai see the stations manifest
/mob/living/silicon/ai/proc/ai_roster()
set category = "AI Commands"
@@ -428,7 +417,7 @@ var/list/ai_verbs_default = list(
if (href_list["switchcamera"])
switchCamera(locate(href_list["switchcamera"])) in cameranet.cameras
if (href_list["showalerts"])
- ai_alerts()
+ subsystem_alarm_monitor()
//Carn: holopad requests
if (href_list["jumptoholopad"])
var/obj/machinery/hologram/holopad/H = locate(href_list["jumptoholopad"])
diff --git a/code/modules/mob/living/silicon/ai/nano.dm b/code/modules/mob/living/silicon/ai/nano.dm
deleted file mode 100644
index 8572c8ec59..0000000000
--- a/code/modules/mob/living/silicon/ai/nano.dm
+++ /dev/null
@@ -1,27 +0,0 @@
-var/obj/nano_module/crew_monitor/crew_monitor
-var/obj/nano_module/rcon/rcon
-var/obj/nano_module/power_monitor/power_monitor
-
-/mob/living/silicon/ai/proc/init_subsystems()
- crew_monitor = new(src)
- rcon = new(src)
- power_monitor = new(src)
-
-/mob/living/silicon/ai/proc/nano_crew_monitor()
- set category = "AI Subystems"
- set name = "Crew Monitor"
-
- crew_monitor.ui_interact(usr)
-
-/mob/living/silicon/ai/proc/nano_power_monitor()
- set category = "AI Subystems"
- set name = "Power Monitor"
-
- power_monitor.ui_interact(usr)
-
-
-/mob/living/silicon/ai/proc/nano_rcon()
- set category = "AI Subystems"
- set name = "RCON"
-
- rcon.ui_interact(usr)
diff --git a/code/modules/mob/living/silicon/ai/subsystems.dm b/code/modules/mob/living/silicon/ai/subsystems.dm
new file mode 100644
index 0000000000..3ef229712e
--- /dev/null
+++ b/code/modules/mob/living/silicon/ai/subsystems.dm
@@ -0,0 +1,44 @@
+var/list/ai_verbs_subsystems = list(
+ /mob/living/silicon/ai/proc/subsystem_alarm_monitor,
+ /mob/living/silicon/ai/proc/subsystem_crew_monitor,
+ /mob/living/silicon/ai/proc/subsystem_power_monitor,
+ /mob/living/silicon/ai/proc/subsystem_rcon
+)
+
+/mob/living/silicon/ai
+ var/
+ var/obj/nano_module/crew_monitor/crew_monitor
+ var/obj/nano_module/rcon/rcon
+ var/obj/nano_module/power_monitor/power_monitor
+
+/mob/living/silicon/ai/init_subsystems()
+ ..()
+ del(alarm_monitor)
+ alarm_monitor = new/obj/nano_module/alarm_monitor/ai(src)
+ crew_monitor = new(src)
+ rcon = new(src)
+ power_monitor = new(src)
+
+/mob/living/silicon/ai/proc/subsystem_alarm_monitor()
+ set name = "Alarm Monitor"
+ set category = "AI Subystems"
+
+ alarm_monitor.ui_interact(usr)
+
+/mob/living/silicon/ai/proc/subsystem_crew_monitor()
+ set category = "AI Subystems"
+ set name = "Crew Monitor"
+
+ crew_monitor.ui_interact(usr)
+
+/mob/living/silicon/ai/proc/subsystem_power_monitor()
+ set category = "AI Subystems"
+ set name = "Power Monitor"
+
+ power_monitor.ui_interact(usr)
+
+/mob/living/silicon/ai/proc/subsystem_rcon()
+ set category = "AI Subystems"
+ set name = "RCON"
+
+ rcon.ui_interact(usr)
diff --git a/code/modules/mob/living/silicon/robot/robot.dm b/code/modules/mob/living/silicon/robot/robot.dm
index 025f1ef0c2..0f04f741c8 100644
--- a/code/modules/mob/living/silicon/robot/robot.dm
+++ b/code/modules/mob/living/silicon/robot/robot.dm
@@ -437,22 +437,12 @@ var/list/robot_verbs_default = list(
updatename()
updateicon()
-/mob/living/silicon/robot/verb/cmd_robot_alerts()
- set category = "Robot Commands"
- set name = "Show Alerts"
- robot_alerts()
-
// this verb lets cyborgs see the stations manifest
/mob/living/silicon/robot/verb/cmd_station_manifest()
set category = "Robot Commands"
set name = "Show Crew Manifest"
show_station_manifest()
-
-/mob/living/silicon/robot/proc/robot_alerts()
- //PsiFix
- //nano_alarm.ui_interact(usr)
-
/mob/living/silicon/robot/proc/self_diagnosis()
if(!is_component_functioning("diagnosis unit"))
return null
@@ -1004,30 +994,31 @@ var/list/robot_verbs_default = list(
/mob/living/silicon/robot/Topic(href, href_list)
if(..())
- return
+ return 1
if(usr != src)
- return
+ return 1
if (href_list["showalerts"])
- robot_alerts()
- return
+ subsystem_alarm_monitor()
+ return 1
if (href_list["mod"])
var/obj/item/O = locate(href_list["mod"])
if (istype(O) && (O.loc == src))
O.attack_self(src)
+ return 1
if (href_list["act"])
var/obj/item/O = locate(href_list["act"])
if (!istype(O))
- return
+ return 1
if(!((O in src.module.modules) || (O == src.module.emag)))
- return
+ return 1
if(activated(O))
src << "Already activated"
- return
+ return 1
if(!module_state_1)
module_state_1 = O
O.layer = 20
@@ -1049,6 +1040,7 @@ var/list/robot_verbs_default = list(
else
src << "You need to disable a module first!"
installed_modules()
+ return 1
if (href_list["deact"])
var/obj/item/O = locate(href_list["deact"])
@@ -1067,6 +1059,7 @@ var/list/robot_verbs_default = list(
else
src << "Module isn't activated"
installed_modules()
+ return 1
if (href_list["lawc"]) // Toggling whether or not a law gets stated by the State Laws verb --NeoFite
var/L = text2num(href_list["lawc"])
@@ -1075,6 +1068,7 @@ var/list/robot_verbs_default = list(
if ("No") lawcheck[L+1] = "Yes"
// src << text ("Switching Law [L]'s report status to []", lawcheck[L+1])
checklaws()
+ return 1
if (href_list["lawi"]) // Toggling whether or not a law gets stated by the State Laws verb --NeoFite
var/L = text2num(href_list["lawi"])
@@ -1083,9 +1077,11 @@ var/list/robot_verbs_default = list(
if ("No") ioncheck[L] = "Yes"
// src << text ("Switching Law [L]'s report status to []", lawcheck[L+1])
checklaws()
+ return 1
if (href_list["laws"]) // With how my law selection code works, I changed statelaws from a verb to a proc, and call it through my law selection panel. --NeoFite
statelaws()
+ return 1
return
/mob/living/silicon/robot/proc/radio_menu()
@@ -1218,9 +1214,11 @@ var/list/robot_verbs_default = list(
/mob/living/silicon/robot/proc/add_robot_verbs()
src.verbs |= robot_verbs_default
+ src.verbs |= robot_verbs_subsystems
/mob/living/silicon/robot/proc/remove_robot_verbs()
src.verbs -= robot_verbs_default
+ src.verbs -= robot_verbs_subsystems
// Uses power from cyborg's cell. Returns 1 on success or 0 on failure.
// Properly converts using CELLRATE now! Amount is in Joules.
diff --git a/code/modules/mob/living/silicon/robot/subsystems.dm b/code/modules/mob/living/silicon/robot/subsystems.dm
new file mode 100644
index 0000000000..4750828544
--- /dev/null
+++ b/code/modules/mob/living/silicon/robot/subsystems.dm
@@ -0,0 +1,9 @@
+var/list/robot_verbs_subsystems = list(
+ /mob/living/silicon/robot/proc/subsystem_alarm_monitor
+)
+
+/mob/living/silicon/robot/proc/subsystem_alarm_monitor()
+ set name = "Alarm Monitor"
+ set category = "Robot Subystems"
+
+ alarm_monitor.ui_interact(usr)
\ No newline at end of file
diff --git a/code/modules/mob/living/silicon/silicon.dm b/code/modules/mob/living/silicon/silicon.dm
index af7881986b..a4ee540c2b 100644
--- a/code/modules/mob/living/silicon/silicon.dm
+++ b/code/modules/mob/living/silicon/silicon.dm
@@ -22,6 +22,9 @@
var/obj/item/device/camera/siliconcam/aiCamera = null //photography
var/local_transmit //If set, can only speak to others of the same type within a short range.
+ // Subsystems
+ var/obj/nano_module/alarm_monitor = null
+
var/sensor_mode = 0 //Determines the current HUD.
#define SEC_HUD 1 //Security HUD mode
@@ -30,6 +33,7 @@
/mob/living/silicon/New()
..()
add_language("Galactic Common")
+ init_subsystems()
/mob/living/silicon/proc/SetName(pickedName as text)
real_name = pickedName
@@ -244,7 +248,8 @@
return 1
/mob/living/silicon/Topic(href, href_list)
- ..()
+ if(..())
+ return 1
if (href_list["lawr"]) // Selects on which channel to state laws
var/list/channels = list(MAIN_CHANNEL)
@@ -283,3 +288,6 @@
adjustBruteLoss(30)
updatehealth()
+
+/mob/living/silicon/proc/init_subsystems()
+ alarm_monitor = new/obj/nano_module/alarm_monitor/borg(src)
diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm
index a6ba0bed0a..323cdfaf8b 100644
--- a/code/modules/mob/mob.dm
+++ b/code/modules/mob/mob.dm
@@ -792,27 +792,25 @@ note dizziness decrements automatically in the mob's Life() proc.
/mob/Stat()
..()
- if(statpanel("MC")) //not looking at that panel
-
- if(client && client.holder)
+ if(client && client.holder)
+ if(statpanel("Status"))
stat(null,"Location:\t([x], [y], [z])")
stat(null,"CPU:\t[world.cpu]")
stat(null,"Instances:\t[world.contents.len]")
-
- if(master_controller)
- stat(null,"MasterController-[last_tick_duration] ([master_controller.processing?"On":"Off"]-[controller_iteration])")
- stat(null,"Air-[master_controller.air_cost]\tSun-[master_controller.sun_cost]")
- stat(null,"Mob-[master_controller.mobs_cost]\t#[mob_list.len]")
- stat(null,"Dis-[master_controller.diseases_cost]\t#[active_diseases.len]")
- stat(null,"Mch-[master_controller.machines_cost]\t#[machines.len]")
- stat(null,"Obj-[master_controller.objects_cost]\t#[processing_objects.len]")
- stat(null,"Net-[master_controller.networks_cost]\tPnet-[master_controller.powernets_cost]")
- stat(null,"NanoUI-[master_controller.nano_cost]\t#[nanomanager.processing_uis.len]")
- stat(null,"Event-[master_controller.events_cost]\t# [event_manager.active_events.len]")
- alarm_manager.stat_entry()
- stat(null,"Tick-[master_controller.ticker_cost]\tALL-[master_controller.total_cost]")
- else
- stat(null,"MasterController-ERROR")
+ if(statpanel("MC") && master_controller)
+ stat(null,"MasterController-[last_tick_duration] ([master_controller.processing?"On":"Off"]-[controller_iteration])")
+ stat(null,"Air-[master_controller.air_cost]\tSun-[master_controller.sun_cost]")
+ stat(null,"Mob-[master_controller.mobs_cost]\t#[mob_list.len]")
+ stat(null,"Dis-[master_controller.diseases_cost]\t#[active_diseases.len]")
+ stat(null,"Mch-[master_controller.machines_cost]\t#[machines.len]")
+ stat(null,"Obj-[master_controller.objects_cost]\t#[processing_objects.len]")
+ stat(null,"Net-[master_controller.networks_cost]\tPnet-[master_controller.powernets_cost]")
+ stat(null,"NanoUI-[master_controller.nano_cost]\t#[nanomanager.processing_uis.len]")
+ stat(null,"Event-[master_controller.events_cost]\t#[event_manager.active_events.len]")
+ alarm_manager.stat_entry()
+ stat(null,"Tick-[master_controller.ticker_cost]\tALL-[master_controller.total_cost]")
+ else
+ stat(null,"MasterController-ERROR")
if(listed_turf && client)
if(!TurfAdjacent(listed_turf))
diff --git a/code/modules/mob/mob_helpers.dm b/code/modules/mob/mob_helpers.dm
index 3c983bcc22..d064ee67b4 100644
--- a/code/modules/mob/mob_helpers.dm
+++ b/code/modules/mob/mob_helpers.dm
@@ -547,3 +547,16 @@ proc/is_blind(A)
say_dead_direct("The ghost of [name] now [pick("skulks","lurks","prowls","creeps","stalks")] among the dead. [message]")
else
say_dead_direct("[name] no longer [pick("skulks","lurks","prowls","creeps","stalks")] in the realm of the dead. [message]")
+
+/mob/proc/switch_to_camera(var/obj/machinery/camera/C)
+ if (!C.can_use() || stat || (get_dist(C, src) > 1 || machine != src || blinded || !canmove))
+ return 0
+ check_eye(src)
+ return 1
+
+/mob/living/silicon/ai/switch_to_camera(var/obj/machinery/camera/C)
+ if(!C.can_use() || !is_in_chassis())
+ return 0
+
+ eyeobj.setLoc(C)
+ return 1
diff --git a/code/modules/nano/modules/alarm_monitor.dm b/code/modules/nano/modules/alarm_monitor.dm
new file mode 100644
index 0000000000..f0044506c8
--- /dev/null
+++ b/code/modules/nano/modules/alarm_monitor.dm
@@ -0,0 +1,86 @@
+/obj/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.
+ var/list/datum/alarm_handler/alarm_handlers // The particular list of alarm handlers this alarm monitor should present to the user.
+
+/obj/nano_module/alarm_monitor/ai
+ list_cameras = 1
+
+/obj/nano_module/alarm_monitor/ai/New()
+ ..()
+ alarm_handlers = alarm_manager.all_handlers
+
+/obj/nano_module/alarm_monitor/borg/New()
+ ..()
+ alarm_handlers = alarm_manager.all_handlers
+
+/obj/nano_module/alarm_monitor/engineering/New()
+ ..()
+ alarm_handlers = list(atmosphere_alarm, fire_alarm, power_alarm)
+
+/obj/nano_module/alarm_monitor/security/New()
+ ..()
+ alarm_handlers = list(camera_alarm, motion_alarm)
+
+/obj/nano_module/alarm_monitor/proc/register(var/object, var/procName)
+ for(var/datum/alarm_handler/AH in alarm_handlers)
+ AH.register(object, procName)
+
+/obj/nano_module/alarm_monitor/proc/unregister(var/object)
+ for(var/datum/alarm_handler/AH in alarm_handlers)
+ AH.unregister(object)
+
+/obj/nano_module/alarm_monitor/proc/active_alarms()
+ var/list/all_alarms = new()
+ for(var/datum/alarm_handler/AH in alarm_handlers)
+ var/list/alarms = AH.alarms
+ all_alarms += alarms
+
+ return all_alarms
+
+/obj/nano_module/alarm_monitor/Topic(href, href_list)
+ if(..()) return 1
+
+/obj/nano_module/alarm_monitor/ai/Topic(ref, href_list)
+ if(..())
+ return 1
+ if(href_list["switchTo"])
+ var/obj/machinery/camera/C = locate(href_list["switchTo"]) in cameranet.cameras
+ if(!C)
+
+ return
+
+ usr.switch_to_camera(C)
+ return 1
+
+/obj/nano_module/alarm_monitor/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
+ 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.alarms)
+ var/cameras[0]
+ var/lost_sources[0]
+
+ if(list_cameras)
+ 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" = sanitize(english_list(lost_sources, nothing_text = "", and_text = ", "))))
+ data["categories"] = categories
+
+ ui = nanomanager.try_update_ui(user, src, ui_key, ui, data, force_open)
+ if (!ui)
+ ui = new(user, src, ui_key, "alarm_monitor.tmpl", "Alarm Monitoring Console", 800, 800)
+ ui.set_initial_data(data)
+ ui.open()
+ ui.set_auto_update(1)
diff --git a/code/modules/nano/modules/rcon.dm b/code/modules/nano/modules/rcon.dm
index 6224c56de8..abdae311d0 100644
--- a/code/modules/nano/modules/rcon.dm
+++ b/code/modules/nano/modules/rcon.dm
@@ -1,5 +1,5 @@
/obj/nano_module/rcon
- name = "RCON interface"
+ name = "Power RCON"
var/list/known_SMESs = null
var/list/known_breakers = null
diff --git a/nano/css/shared.css b/nano/css/shared.css
index b81990b7a3..97b0604ad1 100644
--- a/nano/css/shared.css
+++ b/nano/css/shared.css
@@ -271,6 +271,12 @@ div.notice {
color: #e9c183;
}
+.itemLabelWidest {
+ float: left;
+ width: 100%;
+ color: #e9c183;
+}
+
.itemContentWide {
float: left;
width: 79%;
diff --git a/nano/templates/alarm_monitor.tmpl b/nano/templates/alarm_monitor.tmpl
new file mode 100644
index 0000000000..0089467e43
--- /dev/null
+++ b/nano/templates/alarm_monitor.tmpl
@@ -0,0 +1,37 @@
+
+
+{{for data.categories}}
+ {{:value.category}}
+ {{for value.alarms :alarmValue:alarmIndex}}
+ {{if alarmValue.origin_lost}}
+ {{:alarmValue.name}} Alarm Origin Lost
+ {{else}}
+ {{:alarmValue.name}}
+ {{/if}}
+ {{if alarmValue.has_cameras || alarmValue.lost_sources != ""}}
+
+ {{if alarmValue.has_cameras}}
+
+ {{for alarmValue.cameras :cameraValue:cameraIndex}}
+ {{if cameraValue.deact}}
+ {{:helper.link(cameraValue.name + " (deactivated)", '', {}, 'inactive')}}
+ {{else}}
+ {{:helper.link(cameraValue.name, '', {'switchTo' : cameraValue.camera})}}
+ {{/if}}
+ {{/for}}
+
+ {{/if}}
+ {{if alarmValue.lost_sources != ""}}
+
+
Lost Alarm Sources: {{:alarmValue.lost_sources}}
+
+ {{/if}}
+
+ {{/if}}
+ {{empty}}
+ --All Systems Nominal
+ {{/for}}
+{{/for}}
\ No newline at end of file