Merge pull request #8155 from PsiOmegaDelta/subsystems

Centralized Alarm Handling
This commit is contained in:
Chinsky
2015-02-24 04:39:48 +03:00
42 changed files with 907 additions and 658 deletions

View File

@@ -93,8 +93,10 @@
#include "code\controllers\lighting_controller.dm"
#include "code\controllers\master_controller.dm"
#include "code\controllers\shuttle_controller.dm"
#include "code\controllers\subsystems.dm"
#include "code\controllers\verbs.dm"
#include "code\controllers\voting.dm"
#include "code\controllers\subsystem\alarms.dm"
#include "code\datums\ai_laws.dm"
#include "code\datums\browser.dm"
#include "code\datums\computerfiles.dm"
@@ -811,6 +813,13 @@
#include "code\modules\admin\verbs\ticklag.dm"
#include "code\modules\admin\verbs\tripAI.dm"
#include "code\modules\admin\verbs\vox_raiders.dm"
#include "code\modules\alarm\alarm.dm"
#include "code\modules\alarm\alarm_handler.dm"
#include "code\modules\alarm\atmosphere_alarm.dm"
#include "code\modules\alarm\camera_alarm.dm"
#include "code\modules\alarm\fire_alarm.dm"
#include "code\modules\alarm\motion_alarm.dm"
#include "code\modules\alarm\power_alarm.dm"
#include "code\modules\assembly\assembly.dm"
#include "code\modules\assembly\bomb.dm"
#include "code\modules\assembly\helpers.dm"
@@ -1130,7 +1139,6 @@
#include "code\modules\mob\living\carbon\monkey\login.dm"
#include "code\modules\mob\living\carbon\monkey\monkey.dm"
#include "code\modules\mob\living\carbon\monkey\update_icons.dm"
#include "code\modules\mob\living\silicon\alarm.dm"
#include "code\modules\mob\living\silicon\death.dm"
#include "code\modules\mob\living\silicon\laws.dm"
#include "code\modules\mob\living\silicon\login.dm"
@@ -1144,8 +1152,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 +1186,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 +1245,7 @@
#include "code\modules\nano\nanomanager.dm"
#include "code\modules\nano\nanomapgen.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"

View File

@@ -491,5 +491,8 @@ datum/projectile_data
temps[direction] = rstats
return temps
/proc/MinutesToTicks(var/minutes as num)
return minutes * 60 * 10
/proc/MinutesToTicks(var/minutes)
return SecondsToTicks(60 * minutes)
/proc/SecondsToTicks(var/seconds)
return seconds * 10

View File

@@ -596,3 +596,7 @@ datum/proc/dd_SortValue()
/obj/machinery/camera/dd_SortValue()
return "[c_tag]"
/datum/alarm/dd_SortValue()
return "[sanitize(last_name)]"

View File

@@ -26,6 +26,7 @@ datum/controller/game_controller
var/powernets_cost = 0
var/nano_cost = 0
var/events_cost = 0
var/alarms_cost = 0
var/ticker_cost = 0
var/total_cost = 0
@@ -231,6 +232,11 @@ datum/controller/game_controller/proc/process()
process_events()
events_cost = (world.timeofday - timer) / 10
//ALARMS
timer = world.timeofday
process_alarms()
alarms_cost = (world.timeofday - timer) / 10
//TICKER
timer = world.timeofday
last_thing_processed = ticker.type
@@ -238,7 +244,7 @@ datum/controller/game_controller/proc/process()
ticker_cost = (world.timeofday - timer) / 10
//TIMING
total_cost = air_cost + sun_cost + mobs_cost + diseases_cost + machines_cost + objects_cost + networks_cost + powernets_cost + nano_cost + events_cost + ticker_cost
total_cost = air_cost + sun_cost + mobs_cost + diseases_cost + machines_cost + objects_cost + networks_cost + powernets_cost + nano_cost + events_cost + alarms_cost + ticker_cost
var/end_time = world.timeofday
if(end_time < start_time) //why not just use world.time instead?
@@ -334,9 +340,13 @@ datum/controller/game_controller/proc/process_nano()
nanomanager.processing_uis.Cut(i,i+1)
datum/controller/game_controller/proc/process_events()
last_thing_processed = /datum/event
last_thing_processed = /datum/event_manager
event_manager.process()
datum/controller/game_controller/proc/process_alarms()
last_thing_processed = /datum/subsystem/alarm
alarm_manager.fire()
datum/controller/game_controller/proc/Recover() //Mostly a placeholder for now.
var/msg = "## DEBUG: [time2text(world.timeofday)] MC restarted. Reports:\n"
for(var/varname in master_controller.vars)

View File

@@ -0,0 +1,33 @@
// 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(atmosphere_alarm, camera_alarm, fire_alarm, motion_alarm, power_alarm)
/datum/subsystem/alarm/stat_entry()
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/list/all_alarms = new
for(var/datum/alarm_handler/AH in all_handlers)
var/list/alarms = AH.alarms
all_alarms += alarms
return all_alarms
/datum/subsystem/alarm/proc/number_of_active_alarms()
var/list/alarms = active_alarms()
return alarms.len

View File

@@ -0,0 +1,46 @@
#define NEW_SS_GLOBAL(varname) if(varname != src){if(istype(varname)){Recover();qdel(varname);}varname = src;}
/datum/subsystem
//things you will want to define
var/name //name of the subsystem
var/priority = 0 //priority affects order of initialization. Higher priorities are initialized first, lower priorities later. Can be decimal and negative values.
var/wait = 20 //time to wait (in deciseconds) between each call to fire(). Must be a positive integer.
//things you will probably want to leave alone
var/can_fire = 0 //prevent fire() calls
var/last_fire = 0 //last world.time we called fire()
var/next_fire = 0 //scheduled world.time for next fire()
var/cpu = 0 //cpu-usage stats (somewhat vague)
var/cost = 0 //average time to execute
var/times_fired = 0 //number of times we have called fire()
//used to initialize the subsystem BEFORE the map has loaded
/datum/subsystem/New()
//previously, this would have been named 'process()' but that name is used everywhere for different things!
//fire() seems more suitable. This is the procedure that gets called every 'wait' deciseconds.
//fire(), and the procs it calls, SHOULD NOT HAVE ANY SLEEP OPERATIONS in them!
//YE BE WARNED!
/datum/subsystem/proc/fire()
can_fire = 0
//used to initialize the subsystem AFTER the map has loaded
/datum/subsystem/proc/Initialize(start_timeofday)
var/time = (world.timeofday - start_timeofday) / 10
var/msg = "Initialized [name] SubSystem within [time] seconds"
world << "<span class='userdanger'>[msg]</span>"
world.log << msg
//hook for printing stats to the "MC" statuspanel for admins to see performance and related stats etc.
/datum/subsystem/proc/stat_entry()
stat(name, "[round(cost,0.001)]ds\t(CPU:[round(cpu,1)]%)")
//could be used to postpone a costly subsystem for one cycle
//for instance, during cpu intensive operations like explosions
/datum/subsystem/proc/postpone()
if(next_fire - world.time < wait)
next_fire += wait
//usually called via datum/subsystem/New() when replacing a subsystem (i.e. due to a recurring crash)
//should attempt to salvage what it can from the old instance of subsystem
/datum/subsystem/proc/Recover()

View File

@@ -56,7 +56,7 @@
message_admins("Admin [key_name_admin(usr)] has restarted the [controller] controller.")
return
/client/proc/debug_controller(controller in list("Master","Failsafe","Ticker","Lighting","Air","Jobs","Sun","Radio","Supply","Shuttles","Emergency Shuttle","Configuration","pAI", "Cameras", "Transfer Controller", "Gas Data","Event"))
/client/proc/debug_controller(controller in list("Master","Failsafe","Ticker","Lighting","Air","Jobs","Sun","Radio","Supply","Shuttles","Emergency Shuttle","Configuration","pAI", "Cameras", "Transfer Controller", "Gas Data","Event","Alarm"))
set category = "Debug"
set name = "Debug Controller"
set desc = "Debug the various periodic loop controllers for the game (be careful!)"
@@ -114,5 +114,8 @@
if("Event")
debug_variables(event_manager)
feedback_add_details("admin_verb", "DEvent")
if("Alarm")
debug_variables(alarm_manager)
feedback_add_details("admin_verb", "DAlarm")
message_admins("Admin [key_name_admin(usr)] is debugging the [controller] controller.")
return

View File

@@ -46,7 +46,7 @@ var/const/AALARM_WIRE_AALARM = 16
//world << "Syphon Wire Cut"
if(AALARM_WIRE_AALARM)
if (A.alarm_area.atmosalert(2))
if (A.alarm_area.atmosalert(2, A))
A.post_alert(2)
A.update_icon()
@@ -88,6 +88,6 @@ var/const/AALARM_WIRE_AALARM = 16
if(AALARM_WIRE_AALARM)
// world << "Aalarm wire pulsed"
if (A.alarm_area.atmosalert(0))
if (A.alarm_area.atmosalert(0, A))
A.post_alert(0)
A.update_icon()

View File

@@ -110,7 +110,13 @@ var/list/ghostteleportlocs = list()
power_environ = 0
ambience = list('sound/ambience/ambispace.ogg','sound/music/title2.ogg','sound/music/space.ogg','sound/music/main.ogg','sound/music/traitor.ogg')
/area/space/firealert()
area/space/atmosalert()
return
/area/space/fire_alert()
return
/area/space/fire_reset()
return
/area/space/readyalert()
@@ -633,7 +639,7 @@ var/list/ghostteleportlocs = list()
/area/prison/cell_block/C
name = "Prison Cell Block C"
icon_state = "brig"
////////////////////
//SPACE STATION 13//
////////////////////
@@ -1070,7 +1076,7 @@ var/list/ghostteleportlocs = list()
name = "\improper Engineering"
icon_state = "engineering"
ambience = list('sound/ambience/ambisin1.ogg','sound/ambience/ambisin2.ogg','sound/ambience/ambisin3.ogg','sound/ambience/ambisin4.ogg')
/area/engineering/atmos
name = "\improper Atmospherics"
icon_state = "atmos"
@@ -1082,7 +1088,7 @@ var/list/ghostteleportlocs = list()
/area/engineering/atmos/storage
name = "\improper Atmospherics Storage"
icon_state = "atmos_storage"
/area/engineering/drone_fabrication
name = "\improper Drone Fabrication"
icon_state = "drone_fab"
@@ -1130,7 +1136,7 @@ var/list/ghostteleportlocs = list()
/area/engineering/locker_room
name = "\improper Engineering Locker Room"
icon_state = "engineering_locker"
/area/engineering/workshop
name = "\improper Engineering Workshop"
icon_state = "engineering_workshop"

View File

@@ -30,37 +30,14 @@
power_change() // all machines set to current power level, also updates lighting icon
InitializeLighting()
/area/proc/get_cameras()
var/list/cameras = list()
for (var/area/RA in related)
for (var/obj/machinery/camera/C in RA)
cameras += C
return cameras
/area/proc/poweralert(var/state, var/obj/source as obj)
if (state != poweralm)
poweralm = state
if(istype(source)) //Only report power alarms on the z-level where the source is located.
var/list/cameras = list()
for (var/area/RA in related)
for (var/obj/machinery/camera/C in RA)
cameras += C
if(state == 1)
C.network.Remove("Power Alarms")
else
C.network.Add("Power Alarms")
for (var/mob/living/silicon/aiPlayer in player_list)
if(aiPlayer.z == source.z)
if (state == 1)
aiPlayer.cancelAlarm("Power", src, source)
else
aiPlayer.triggerAlarm("Power", src, cameras, source)
for(var/obj/machinery/computer/station_alert/a in machines)
if(a.z == source.z)
if(state == 1)
a.cancelAlarm("Power", src, source)
else
a.triggerAlarm("Power", src, cameras, source)
return
/area/proc/atmosalert(danger_level, var/set_firelocks=1)
// if(type==/area) //No atmos alarms in space
// return 0 //redudant
/area/proc/atmosalert(danger_level, var/alarm_source)
//Check all the alarms before lowering atmosalm. Raising is perfectly fine.
for (var/area/RA in related)
for (var/obj/machinery/alarm/AA in RA)
@@ -68,32 +45,16 @@
danger_level = max(danger_level, AA.danger_level)
if(danger_level != atmosalm)
if (set_firelocks && danger_level < 1 && atmosalm >= 1)
if (danger_level < 1 && atmosalm >= 1)
//closing the doors on red and opening on green provides a bit of hysteresis that will hopefully prevent fire doors from opening and closing repeatedly due to noise
air_doors_open()
else if (danger_level >= 2 && atmosalm < 2)
air_doors_close()
if (danger_level < 2 && atmosalm >= 2)
for(var/area/RA in related)
for(var/obj/machinery/camera/C in RA)
C.network.Remove("Atmosphere Alarms")
for(var/mob/living/silicon/aiPlayer in player_list)
aiPlayer.cancelAlarm("Atmosphere", src, src)
for(var/obj/machinery/computer/station_alert/a in machines)
a.cancelAlarm("Atmosphere", src, src)
if (danger_level >= 2 && atmosalm < 2)
var/list/cameras = list()
for(var/area/RA in related)
//updateicon()
for(var/obj/machinery/camera/C in RA)
cameras += C
C.network.Add("Atmosphere Alarms")
for(var/mob/living/silicon/aiPlayer in player_list)
aiPlayer.triggerAlarm("Atmosphere", src, cameras, src)
for(var/obj/machinery/computer/station_alert/a in machines)
a.triggerAlarm("Atmosphere", src, cameras, src)
if (set_firelocks)
air_doors_close()
if (danger_level == 0)
atmosphere_alarm.clearAlarm(master, alarm_source)
else
atmosphere_alarm.triggerAlarm(master, alarm_source, severity = danger_level)
atmosalm = danger_level
for(var/area/RA in related)
@@ -107,9 +68,9 @@
if(!src.master.air_doors_activated)
src.master.air_doors_activated = 1
for(var/obj/machinery/door/firedoor/E in src.master.all_doors)
if(!E:blocked)
if(!E.blocked)
if(E.operating)
E:nextstate = CLOSED
E.nextstate = CLOSED
else if(!E.density)
spawn(0)
E.close()
@@ -118,21 +79,21 @@
if(src.master.air_doors_activated)
src.master.air_doors_activated = 0
for(var/obj/machinery/door/firedoor/E in src.master.all_doors)
if(!E:blocked)
if(!E.blocked)
if(E.operating)
E:nextstate = OPEN
E.nextstate = OPEN
else if(E.density)
spawn(0)
E.open()
/area/proc/firealert()
if(name == "Space") //no fire alarms in space
return
if( !fire )
fire = 1
master.fire = 1 //used for firedoor checks
updateicon()
/area/proc/fire_alert()
if(!fire)
master.fire = 1 //used for firedoor checks
master.updateicon()
for(var/area/A in related)
A.fire = 1
A.updateicon()
mouse_opacity = 0
for(var/obj/machinery/door/firedoor/D in all_doors)
if(!D.blocked)
@@ -141,22 +102,15 @@
else if(!D.density)
spawn()
D.close()
var/list/cameras = list()
for(var/area/RA in related)
for (var/obj/machinery/camera/C in RA)
cameras.Add(C)
C.network.Add("Fire Alarms")
for (var/mob/living/silicon/ai/aiPlayer in player_list)
aiPlayer.triggerAlarm("Fire", src, cameras, src)
for (var/obj/machinery/computer/station_alert/a in machines)
a.triggerAlarm("Fire", src, cameras, src)
/area/proc/firereset()
/area/proc/fire_reset()
if (fire)
fire = 0
master.fire = 0 //used for firedoor checks
master.fire = 0 //used for firedoor checks
master.updateicon()
for(var/area/A in related)
A.fire = 0
A.updateicon()
mouse_opacity = 0
updateicon()
for(var/obj/machinery/door/firedoor/D in all_doors)
if(!D.blocked)
if(D.operating)
@@ -164,13 +118,6 @@
else if(D.density)
spawn(0)
D.open()
for(var/area/RA in related)
for (var/obj/machinery/camera/C in RA)
C.network.Remove("Fire Alarms")
for (var/mob/living/silicon/ai/aiPlayer in player_list)
aiPlayer.cancelAlarm("Fire", src, src)
for (var/obj/machinery/computer/station_alert/a in machines)
a.cancelAlarm("Fire", src, src)
/area/proc/readyalert()
if(!eject)

View File

@@ -117,19 +117,6 @@
first_run()
/obj/machinery/alarm/Del()
//If there's an active alarm, clear it after minute so that alarms don't keep going forver
delayed_reset()
..()
//needed to cancel the alarm after it is deleted
/obj/machinery/alarm/proc/delayed_reset()
var/area/A = alarm_area
src = null
spawn(600)
//It makes sense not to touch firelocks here. The alarm itself is gone, we have no idea what the atmos is like.
A.atmosalert(0, set_firelocks=0)
/obj/machinery/alarm/proc/first_run()
alarm_area = get_area(src)
if (alarm_area.master)
@@ -441,7 +428,7 @@
send_signal(device_id, list("power"= 0) )
/obj/machinery/alarm/proc/apply_danger_level(var/new_danger_level)
if (report_danger_level && alarm_area.atmosalert(new_danger_level))
if (report_danger_level && alarm_area.atmosalert(new_danger_level, src))
post_alert(new_danger_level)
update_icon()
@@ -769,13 +756,13 @@
return 1
if(href_list["atmos_alarm"])
if (alarm_area.atmosalert(2))
if (alarm_area.atmosalert(2, src))
apply_danger_level(2)
update_icon()
return 1
if(href_list["atmos_reset"])
if (alarm_area.atmosalert(0))
if (alarm_area.atmosalert(0, src))
apply_danger_level(0)
update_icon()
return 1
@@ -947,7 +934,6 @@ FIRE ALARM
var/buildstage = 2 // 2 = complete, 1 = no wires, 0 = circuit gone
/obj/machinery/firealarm/update_icon()
if(wiresexposed)
switch(buildstage)
if(2)
@@ -981,7 +967,8 @@ FIRE ALARM
return src.alarm()
/obj/machinery/firealarm/emp_act(severity)
if(prob(50/severity)) alarm()
if(prob(50/severity))
alarm(rand(30/severity, 60/severity))
..()
/obj/machinery/firealarm/attackby(obj/item/W as obj, mob/user as mob)
@@ -1080,6 +1067,7 @@ FIRE ALARM
var/d2
if (istype(user, /mob/living/carbon/human) || istype(user, /mob/living/silicon))
A = A.loc
A = A.master
if (A.fire)
d1 = text("<A href='?src=\ref[];reset=1'>Reset - Lockdown</A>", src)
@@ -1145,26 +1133,26 @@ FIRE ALARM
/obj/machinery/firealarm/proc/reset()
if (!( src.working ))
return
var/area/A = src.loc
A = A.loc
if (!( istype(A, /area) ))
return
A.firereset()
var/area/area = get_area(src)
for(var/area/A in area.related)
for(var/obj/machinery/firealarm/FA in A)
fire_alarm.clearAlarm(loc, FA)
update_icon()
return
/obj/machinery/firealarm/proc/alarm()
if (!( src.working ))
/obj/machinery/firealarm/proc/alarm(var/duration = 0)
if (!( src.working))
return
var/area/A = src.loc
A = A.loc
if (!( istype(A, /area) ))
return
A.firealert()
var/area/area = get_area(src)
for(var/area/A in area.related)
for(var/obj/machinery/firealarm/FA in A)
fire_alarm.triggerAlarm(loc, FA, duration)
update_icon()
//playsound(src.loc, 'sound/ambience/signal.ogg', 75, 0)
return
/obj/machinery/firealarm/New(loc, dir, building)
..()
@@ -1180,20 +1168,6 @@ FIRE ALARM
pixel_x = (dir & 3)? 0 : (dir == 4 ? -24 : 24)
pixel_y = (dir & 3)? (dir ==1 ? -24 : 24) : 0
/obj/machinery/firealarm/Del()
//so fire alarms don't keep going forever
delayed_reset()
..()
//needed to cancel the alarm after it is deleted
/obj/machinery/firealarm/proc/delayed_reset()
var/area/A = get_area(src)
if (!A) return
src = null
spawn(600)
A.firereset()
/obj/machinery/firealarm/initialize()
if(z in config.contact_levels)
if(security_level)

View File

@@ -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,22 +254,16 @@
else
icon_state = initial(icon_state)
/obj/machinery/camera/proc/triggerCameraAlarm()
/obj/machinery/camera/proc/triggerCameraAlarm(var/duration = 0)
alarm_on = 1
if(!get_area(src))
return
for(var/mob/living/silicon/S in mob_list)
S.triggerAlarm("Camera", get_area(src), list(src), src)
camera_alarm.triggerAlarm(loc, src, duration)
/obj/machinery/camera/proc/cancelCameraAlarm()
alarm_on = 0
if(!get_area(src))
if(wires.IsIndexCut(CAMERA_WIRE_ALARM))
return
for(var/mob/living/silicon/S in mob_list)
S.cancelAlarm("Camera", get_area(src), src)
alarm_on = 0
camera_alarm.clearAlarm(loc, src)
//if false, then the camera is listed as DEACTIVATED and cannot be used
/obj/machinery/camera/proc/can_use()
@@ -361,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

View File

@@ -45,8 +45,7 @@
if (!status || (stat & NOPOWER))
return 0
if (detectTime == -1)
for (var/mob/living/silicon/aiPlayer in player_list)
aiPlayer.cancelAlarm("Motion", get_area(src), src)
motion_alarm.clearAlarm(loc, src)
detectTime = 0
return 1
@@ -54,8 +53,7 @@
if (!status || (stat & NOPOWER))
return 0
if (!detectTime) return 0
for (var/mob/living/silicon/aiPlayer in player_list)
aiPlayer.triggerAlarm("Motion", get_area(src), list(src), src)
motion_alarm.triggerAlarm(loc, src)
detectTime = -1
return 1

View File

@@ -1,116 +1,85 @@
//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:31
// Converting these to global lists may be a bit laggy when removal procs are called. Consider
// rewriting this properly to fix the update bug, rather than unifying all monitors. ~Z
var/global/list/priority_air_alarms = list()
var/global/list/minor_air_alarms = list()
/obj/machinery/computer/atmos_alert
name = "atmospheric alert computer"
desc = "Used to access the station's atmospheric sensors."
circuit = "/obj/item/weapon/circuitboard/atmos_alert"
icon_state = "alert:0"
var/receive_frequency = 1437
var/datum/radio_frequency/radio_connection
/obj/machinery/computer/atmos_alert/initialize()
/obj/machinery/computer/atmos_alert/New()
..()
set_frequency(receive_frequency)
/obj/machinery/computer/atmos_alert/receive_signal(datum/signal/signal)
if(!signal || signal.encryption) return
var/zone = signal.data["zone"]
var/severity = signal.data["alert"]
if(!zone || !severity) return
minor_air_alarms -= zone
priority_air_alarms -= zone
if(severity=="severe")
priority_air_alarms |= zone
else if (severity=="minor")
minor_air_alarms |= zone
update_icon()
return
/obj/machinery/computer/atmos_alert/proc/set_frequency(new_frequency)
radio_controller.remove_object(src, receive_frequency)
receive_frequency = new_frequency
radio_connection = radio_controller.add_object(src, receive_frequency, RADIO_ATMOSIA)
atmosphere_alarm.register(src, /obj/machinery/computer/station_alert/update_icon)
/obj/machinery/computer/atmos_alert/Del()
atmosphere_alarm.unregister(src)
..()
/obj/machinery/computer/atmos_alert/attack_hand(mob/user)
if(..(user))
return
user << browse(return_text(),"window=computer")
user.set_machine(src)
onclose(user, "computer")
ui_interact(user)
/obj/machinery/computer/atmos_alert/process()
if(..())
src.updateDialog()
/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]
for(var/datum/alarm/alarm in atmosphere_alarm.major_alarms())
major_alarms[++major_alarms.len] = list("name" = sanitize(alarm.alarm_name()), "ref" = "\ref[alarm]")
for(var/datum/alarm/alarm in atmosphere_alarm.minor_alarms())
minor_alarms[++minor_alarms.len] = list("name" = sanitize(alarm.alarm_name()), "ref" = "\ref[alarm]")
data["priority_alarms"] = major_alarms
data["minor_alarms"] = minor_alarms
ui = nanomanager.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)
/obj/machinery/computer/atmos_alert/update_icon()
..()
if(stat & (NOPOWER|BROKEN))
return
if(priority_air_alarms.len)
var/list/alarms = atmosphere_alarm.major_alarms()
if(alarms.len)
icon_state = "alert:2"
else if(minor_air_alarms.len)
icon_state = "alert:1"
else
icon_state = "alert:0"
alarms = atmosphere_alarm.minor_alarms()
if(alarms.len)
icon_state = "alert:1"
else
icon_state = initial(icon_state)
return
/obj/machinery/computer/atmos_alert/proc/return_text()
var/priority_text
var/minor_text
if(priority_air_alarms.len)
for(var/zone in priority_air_alarms)
priority_text += "<FONT color='red'><B>[zone]</B></FONT> <A href='?src=\ref[src];priority_clear=[ckey(zone)]'>X</A><BR>"
else
priority_text = "No priority alerts detected.<BR>"
if(minor_air_alarms.len)
for(var/zone in minor_air_alarms)
minor_text += "<B>[zone]</B> <A href='?src=\ref[src];minor_clear=[ckey(zone)]'>X</A><BR>"
else
minor_text = "No minor alerts detected.<BR>"
var/output = {"<B>[name]</B><HR>
<B>Priority Alerts:</B><BR>
[priority_text]
<BR>
<HR>
<B>Minor Alerts:</B><BR>
[minor_text]
<BR>"}
return output
/obj/machinery/computer/atmos_alert/Topic(href, href_list)
if(..())
return
return 1
if(href_list["priority_clear"])
var/removing_zone = href_list["priority_clear"]
for(var/zone in priority_air_alarms)
if(ckey(zone) == removing_zone)
priority_air_alarms -= zone
if(href_list["clear_alarm"])
var/datum/alarm/alarm = locate(href_list["clear_alarm"]) in 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, custom_state = atmos_alert_topic)
return 1
if(href_list["minor_clear"])
var/removing_zone = href_list["minor_clear"]
for(var/zone in minor_air_alarms)
if(ckey(zone) == removing_zone)
minor_air_alarms -= zone
update_icon()
return
var/datum/topic_state/atmos_alert/atmos_alert_topic = new()
/datum/topic_state/atmos_alert
flags = NANO_IGNORE_DISTANCE
/datum/topic_state/air_alarm/href_list(var/mob/user)
var/list/extra_href = list()
extra_href["remote_connection"] = 1
extra_href["remote_access"] = 1
return extra_href

View File

@@ -3,6 +3,8 @@
/proc/invalidateCameraCache()
for(var/obj/machinery/computer/security/s in world)
s.camera_cache = null
for(var/datum/alarm/A in world)
A.cameras = null
/obj/machinery/computer/security
name = "security camera monitor"
@@ -43,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)

View File

@@ -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 = "<HEAD><TITLE>Current Station Alerts</TITLE><META HTTP-EQUIV='Refresh' CONTENT='10'></HEAD><BODY>\n"
dat += "<A HREF='?src=\ref[user];mach_close=alerts'>Close</A><br><br>"
for (var/cat in src.alarms)
dat += text("<B>[]</B><BR>\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 += "<NOBR>"
dat += "&bull; "
dat += "[A.name]"
if (sources.len > 1)
dat += text(" - [] sources", sources.len)
dat += "</NOBR><BR>\n"
else
dat += "-- All Systems Nominal<BR>\n"
dat += "<BR>\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 = initial(icon_state)
return

View File

@@ -174,8 +174,9 @@ var/gravity_is_on = 1
var/join_motd = null
var/forceblob = 0
var/datum/nanomanager/nanomanager = new() // NanoManager, the manager for Nano UIs.
var/datum/event_manager/event_manager = new() // Event Manager, the manager for events.
var/datum/nanomanager/nanomanager = new() // NanoManager, the manager for Nano UIs.
var/datum/event_manager/event_manager = new() // Event Manager, the manager for events.
var/datum/subsystem/alarm/alarm_manager = new() // Alarm Manager, the manager for alarms.
var/list/awaydestinations = list() // Away missions. A list of landmarks that the warpgate can take you to.

138
code/modules/alarm/alarm.dm Normal file
View File

@@ -0,0 +1,138 @@
#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/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_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 = SecondsToTicks(duration)
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.master
/area/get_alarm_area()
return src.master
/atom/proc/get_alarm_name()
var/area/A = get_area(src)
return A.master.name
/area/get_alarm_name()
return master.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()
#undef ALARM_LOSS_DELAY

View File

@@ -0,0 +1,99 @@
#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/proc/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/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)
invalidateCameraCache()
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.master // 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)

View File

@@ -0,0 +1,19 @@
/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

View File

@@ -0,0 +1,2 @@
/datum/alarm_handler/camera
category = "Camera Alarms"

View File

@@ -0,0 +1,11 @@
/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()
..()

View File

@@ -0,0 +1,2 @@
/datum/alarm_handler/motion
category = "Motion Alarms"

View File

@@ -0,0 +1,10 @@
/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)

View File

@@ -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,36 +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"
var/dat = "<HEAD><TITLE>Current Station Alerts</TITLE><META HTTP-EQUIV='Refresh' CONTENT='10'></HEAD><BODY>\n"
dat += "<A HREF='?src=\ref[src];mach_close=aialerts'>Close</A><BR><BR>"
for (var/cat in alarms)
dat += text("<B>[]</B><BR>\n", cat)
var/list/alarmlist = alarms[cat]
if (alarmlist.len)
for (var/area_name in alarmlist)
var/datum/alarm/alarm = alarmlist[area_name]
dat += "<NOBR>"
var/cameratext = ""
if (alarm.cameras)
for (var/obj/machinery/camera/I in alarm.cameras)
cameratext += text("[]<A HREF=?src=\ref[];switchcamera=\ref[]>[]</A>", (cameratext=="") ? "" : " | ", src, I, I.c_tag)
dat += text("-- [] ([])", alarm.area.name, (cameratext)? cameratext : "No Camera")
if (alarm.sources.len > 1)
dat += text(" - [] sources", alarm.sources.len)
dat += "</NOBR><BR>\n"
else
dat += "-- All Systems Nominal<BR>\n"
dat += "<BR>\n"
viewalerts = 1
src << browse(dat, "window=aialerts&can_close=0")
// this verb lets the ai see the stations manifest
/mob/living/silicon/ai/proc/ai_roster()
set category = "AI Commands"
@@ -451,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"])
@@ -526,29 +492,6 @@ var/list/ai_verbs_default = list(
return 1
/mob/living/silicon/ai/triggerAlarm(var/class, area/A, list/cameralist, var/source)
if (stat == 2)
return 1
..()
var/cameratext = ""
for (var/obj/machinery/camera/C in cameralist)
cameratext += "[(cameratext == "")? "" : "|"]<A HREF=?src=\ref[src];switchcamera=\ref[C]>[C.c_tag]</A>"
queueAlarm("--- [class] alarm detected in [A.name]! ([(cameratext)? cameratext : "No Camera"])", class)
if (viewalerts) ai_alerts()
/mob/living/silicon/ai/cancelAlarm(var/class, area/A as area, var/source)
var/has_alarm = ..()
if (!has_alarm)
queueAlarm(text("--- [] alarm in [] has been cleared.", class, A.name), class, 0)
if (viewalerts) ai_alerts()
return has_alarm
/mob/living/silicon/ai/cancel_camera()
set category = "AI Commands"
set name = "Cancel Camera View"

View File

@@ -171,6 +171,7 @@
sleep(50)
theAPC = null
process_queued_alarms()
regular_hud_updates()
switch(src.sensor_mode)
if (SEC_HUD)

View File

@@ -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)

View File

@@ -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)

View File

@@ -1,111 +0,0 @@
/datum/alarm
var/area/area //the area associated with the alarm. Used to identify the alarm
var/list/sources //list of things triggering the alarm. Used to determine when the alarm should be cleared.
var/list/cameras //list of cameras that can be switched to, if the player has that capability.
/datum/alarm/New(area/A, list/sourcelist=list(), list/cameralist=list())
area = A
sources = sourcelist
cameras = cameralist
/mob/living/silicon
var/alarms = list("Motion"=list(), "Fire"=list(), "Atmosphere"=list(), "Power"=list(), "Camera"=list()) //each sublist stores alarms keyed by the area name
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)
/mob/living/silicon/proc/triggerAlarm(var/class, area/A, list/cameralist, var/source)
var/list/alarmlist = alarms[class]
//see if there is already an alarm of this class for this area
if (A.name in alarmlist)
var/datum/alarm/existing = alarmlist[A.name]
existing.sources += source
existing.cameras |= cameralist
else
alarmlist[A.name] = new /datum/alarm(A, list(source), cameralist)
/mob/living/silicon/proc/cancelAlarm(var/class, area/A as area, var/source)
var/cleared = 0
var/list/alarmlist = alarms[class]
if (A.name in alarmlist)
var/datum/alarm/alarm = alarmlist[A.name]
alarm.sources -= source
if (!(alarm.sources.len))
cleared = 1
alarmlist -= A.name
return !cleared
/mob/living/silicon/proc/queueAlarm(var/message, var/type, var/incoming = 1)
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)
spawn(10 * 10) // 10 seconds
if(alarms_to_show.len < 5)
for(var/msg in alarms_to_show)
src << msg
else if(alarms_to_show.len)
var/msg = "--- "
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["Power"]] alarms detected. - "
msg += "<A href=?src=\ref[src];showalerts=1'>\[Show Alerts\]</a>"
src << msg
if(alarms_to_clear.len < 3)
for(var/msg in alarms_to_clear)
src << msg
else if(alarms_to_clear.len)
var/msg = "--- "
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_show["Power"]] alarms detected. - "
msg += "<A href=?src=\ref[src];showalerts=1'>\[Show Alerts\]</a>"
src << msg
alarms_to_show = list()
alarms_to_clear = list()
for(var/i = 1; i < alarm_types_show.len; i++)
alarm_types_show[i] = 0
for(var/i = 1; i < alarm_types_clear.len; i++)
alarm_types_clear[i] = 0

View File

@@ -18,6 +18,7 @@
use_power()
process_killswitch()
process_locks()
process_queued_alarms()
update_canmove()
/mob/living/silicon/robot/proc/clamp_values()

View File

@@ -437,39 +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()
var/dat = "<HEAD><TITLE>Current Station Alerts</TITLE><META HTTP-EQUIV='Refresh' CONTENT='10'></HEAD><BODY>\n"
dat += "<A HREF='?src=\ref[src];mach_close=robotalerts'>Close</A><BR><BR>"
for (var/cat in alarms)
dat += text("<B>[cat]</B><BR>\n")
var/list/alarmlist = alarms[cat]
if (alarmlist.len)
for (var/area_name in alarmlist)
var/datum/alarm/alarm = alarmlist[area_name]
dat += "<NOBR>"
dat += text("-- [area_name]")
if (alarm.sources.len > 1)
dat += text("- [alarm.sources.len] sources")
dat += "</NOBR><BR>\n"
else
dat += "-- All Systems Nominal<BR>\n"
dat += "<BR>\n"
viewalerts = 1
src << browse(dat, "window=robotalerts&can_close=0")
/mob/living/silicon/robot/proc/self_diagnosis()
if(!is_component_functioning("diagnosis unit"))
return null
@@ -639,25 +612,6 @@ var/list/robot_verbs_default = list(
return
return
/mob/living/silicon/robot/triggerAlarm(var/class, area/A, list/cameralist, var/source)
if (stat == 2)
return 1
..()
queueAlarm(text("--- [class] alarm detected in [A.name]!"), class)
/mob/living/silicon/robot/cancelAlarm(var/class, area/A as area, obj/origin)
var/has_alarm = ..()
if (!has_alarm)
queueAlarm(text("--- [class] alarm in [A.name] has been cleared."), class, 0)
// if (viewalerts) robot_alerts()
return has_alarm
/mob/living/silicon/robot/attackby(obj/item/weapon/W as obj, mob/user as mob)
if (istype(W, /obj/item/weapon/handcuffs)) // fuck i don't even know why isrobot() in handcuff code isn't working so this will have to do
return
@@ -1043,30 +997,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
@@ -1088,6 +1043,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"])
@@ -1106,6 +1062,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"])
@@ -1114,6 +1071,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"])
@@ -1122,9 +1080,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()
@@ -1257,9 +1217,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.

View File

@@ -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)

View File

@@ -22,13 +22,26 @@
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.
var/next_alarm_notice
var/list/datum/alarm/queued_alarms = new()
#define SEC_HUD 1 //Security HUD mode
#define MED_HUD 2 //Medical HUD mode
/mob/living/silicon/New()
..()
add_language("Galactic Common")
init_subsystems()
/mob/living/silicon/Del()
for(var/datum/alarm_handler/AH in alarm_manager.all_handlers)
AH.unregister(src)
..()
/mob/living/silicon/proc/SetName(pickedName as text)
real_name = pickedName
@@ -243,7 +256,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)
@@ -282,3 +296,62 @@
adjustBruteLoss(30)
updatehealth()
/mob/living/silicon/proc/init_subsystems()
alarm_monitor = new/obj/nano_module/alarm_monitor/borg(src)
for(var/datum/alarm_handler/AH in alarm_manager.all_handlers)
AH.register(src, /mob/living/silicon/proc/receive_alarm)
queued_alarms[AH] = list() // Makes sure alarms remain listed in consistent order
/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 + SecondsToTicks(10)
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
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
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
src << "<span class='notice'>--- [AH.category] Cleared ---</span>"
src << "\The [A.alarm_name()]."
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)
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=\ref[src];switchcamera=\ref[C]>[C.c_tag]</A>"
src << "[A.alarm_name()]! ([(cameratext)? cameratext : "No Camera"])"

View File

@@ -792,26 +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,"Events-[master_controller.events_cost]\t#[event_manager.active_events.len]")
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))

View File

@@ -559,3 +559,16 @@ proc/is_blind(A)
say_dead_direct("The ghost of <span class='name'>[name]</span> now [pick("skulks","lurks","prowls","creeps","stalks")] among the dead. [message]")
else
say_dead_direct("<span class='name'>[name]</span> 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

View File

@@ -0,0 +1,83 @@
/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. 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.
/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/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.major_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)

View File

@@ -1,5 +1,5 @@
/obj/nano_module/rcon
name = "RCON interface"
name = "Power RCON"
var/list/known_SMESs = null
var/list/known_breakers = null

View File

@@ -183,9 +183,6 @@
if(terminal)
disconnect_terminal()
//If there's no more APC then there shouldn't be a cause for alarm I guess
area.poweralert(1, src) //so that alarms don't go on forever
..()
/obj/machinery/power/apc/proc/make_terminal()
@@ -725,7 +722,7 @@
src.interact(user)
/obj/machinery/power/apc/attack_ghost(user as mob)
if(stat & (BROKEN|MAINT))
if(stat & (BROKEN|MAINT))
return
return ui_interact(user)
@@ -1164,29 +1161,27 @@
lighting = autoset(lighting, 1)
environ = autoset(environ, 1)
autoflag = 3
area.poweralert(1, src)
if(cell.charge >= 4000)
area.poweralert(1, src)
power_alarm.clearAlarm(loc, src)
else if((cell.percent() <= 30) && (cell.percent() > 15) && longtermpower < 0) // <30%, turn off equipment
if(autoflag != 2)
equipment = autoset(equipment, 2)
lighting = autoset(lighting, 1)
environ = autoset(environ, 1)
area.poweralert(0, src)
power_alarm.triggerAlarm(loc, src)
autoflag = 2
else if(cell.percent() <= 15) // <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)
area.poweralert(0, src)
power_alarm.triggerAlarm(loc, src)
autoflag = 1
else // zero charge, turn all off
if(autoflag != 0)
equipment = autoset(equipment, 0)
lighting = autoset(lighting, 0)
environ = autoset(environ, 0)
area.poweralert(0, src)
power_alarm.triggerAlarm(loc, src)
autoflag = 0
// now trickle-charge the cell
@@ -1233,7 +1228,7 @@
equipment = autoset(equipment, 0)
lighting = autoset(lighting, 0)
environ = autoset(environ, 0)
area.poweralert(0, src)
power_alarm.triggerAlarm(loc, src)
autoflag = 0
// update icon & area power if anything changed

View File

@@ -1,4 +1,4 @@
/area/engineering/poweralert(var/state, var/source)
if (state != poweralm)
/area/engineering/power_alert(var/alarming)
if (alarming)
investigate_log("has a power alarm!","singulo")
..()

View File

@@ -271,6 +271,12 @@ div.notice {
color: #e9c183;
}
.itemLabelWidest {
float: left;
width: 100%;
color: #e9c183;
}
.itemContentWide {
float: left;
width: 79%;

View File

@@ -0,0 +1,37 @@
<!--
Title: Alarm Monitor Console (Main content)
Used In File(s): \code\modules\nano\modules\alarm_monitor.dm
-->
{{for data.categories}}
<H2><span class="itemLabelWidest">{{:value.category}}</span></H2>
{{for value.alarms :alarmValue:alarmIndex}}
{{if alarmValue.origin_lost}}
{{:alarmValue.name}} <span class='notice'>Alarm Origin Lost</span><br>
{{else}}
{{:alarmValue.name}}<br>
{{/if}}
{{if alarmValue.has_cameras || alarmValue.lost_sources != ""}}
<div class="item">
{{if alarmValue.has_cameras}}
<div class="itemContent" style="width: 100%;">
{{for alarmValue.cameras :cameraValue:cameraIndex}}
{{if cameraValue.deact}}
{{:helper.link(cameraValue.name + " (deactivated)", '', {}, 'inactive')}}
{{else}}
{{:helper.link(cameraValue.name, '', {'switchTo' : cameraValue.camera})}}
{{/if}}
{{/for}}
</div>
{{/if}}
{{if alarmValue.lost_sources != ""}}
<div class="itemContent" style="width: 100%;">
<H4><span class='notice'>Lost Alarm Sources: {{:alarmValue.lost_sources}}</span></H4>
</div>
{{/if}}
</div>
{{/if}}
{{empty}}
<span class="white">--All Systems Nominal</span>
{{/for}}
{{/for}}

View File

@@ -0,0 +1,17 @@
<H1>Priority Alerts</H1>
{{for data.priority_alarms}}
<div class="item">
{{:value.name}} {{:helper.link('Reset', null, {'clear_alarm' : value.ref})}}
</div>
{{empty}}
No priority alerts detected.
{{/for}}
<H3>Minor Alerts</H3>
<div class="item">
{{for data.minor_alarms}}
{{:value.name}} {{:helper.link('Reset', null, {'clear_alarm' : value.ref})}}
{{empty}}
No minor alerts detected.
{{/for}}
</div>