From 2e9c266a2f47a7f34ef4487354a2bc5de29e085f Mon Sep 17 00:00:00 2001 From: PsiOmega Date: Mon, 9 Feb 2015 08:22:47 +0100 Subject: [PATCH 1/7] Basic main structure for centralized alarm handling. --- baystation12.dme | 10 +- code/__HELPERS/game.dm | 7 +- code/controllers/master_controller.dm | 14 ++- code/controllers/subsystem/alarms.dm | 27 +++++ code/controllers/subsystems.dm | 46 +++++++ code/controllers/verbs.dm | 5 +- code/game/area/areas.dm | 27 ++--- code/game/machinery/camera/camera.dm | 36 +++--- code/game/machinery/camera/motion.dm | 6 +- code/game/machinery/computer/camera.dm | 2 + code/global.dm | 5 +- code/modules/alarm/alarm.dm | 78 ++++++++++++ code/modules/alarm/alarm_handler.dm | 112 ++++++++++++++++++ code/modules/alarm/atmosphere_alarm.dm | 2 + code/modules/alarm/camera_alarm.dm | 2 + code/modules/alarm/fire_alarm.dm | 2 + code/modules/alarm/motion_alarm.dm | 2 + code/modules/alarm/power_alarm.dm | 2 + code/modules/mob/living/silicon/ai/ai.dm | 50 +------- code/modules/mob/living/silicon/alarm.dm | 111 ----------------- .../modules/mob/living/silicon/robot/robot.dm | 40 +------ code/modules/mob/living/silicon/silicon.dm | 1 + code/modules/mob/mob.dm | 4 +- 23 files changed, 345 insertions(+), 246 deletions(-) create mode 100644 code/controllers/subsystem/alarms.dm create mode 100644 code/controllers/subsystems.dm create mode 100644 code/modules/alarm/alarm.dm create mode 100644 code/modules/alarm/alarm_handler.dm create mode 100644 code/modules/alarm/atmosphere_alarm.dm create mode 100644 code/modules/alarm/camera_alarm.dm create mode 100644 code/modules/alarm/fire_alarm.dm create mode 100644 code/modules/alarm/motion_alarm.dm create mode 100644 code/modules/alarm/power_alarm.dm delete mode 100644 code/modules/mob/living/silicon/alarm.dm diff --git a/baystation12.dme b/baystation12.dme index 82506f8bed..89b3383968 100644 --- a/baystation12.dme +++ b/baystation12.dme @@ -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" @@ -806,6 +808,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" @@ -1115,7 +1124,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" diff --git a/code/__HELPERS/game.dm b/code/__HELPERS/game.dm index dcfcb50fcb..2cff3a2338 100644 --- a/code/__HELPERS/game.dm +++ b/code/__HELPERS/game.dm @@ -484,5 +484,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 diff --git a/code/controllers/master_controller.dm b/code/controllers/master_controller.dm index 8675033ef0..a842ac582d 100644 --- a/code/controllers/master_controller.dm +++ b/code/controllers/master_controller.dm @@ -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? @@ -333,9 +339,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) diff --git a/code/controllers/subsystem/alarms.dm b/code/controllers/subsystem/alarms.dm new file mode 100644 index 0000000000..4684737578 --- /dev/null +++ b/code/controllers/subsystem/alarms.dm @@ -0,0 +1,27 @@ +/* /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() */ + +/datum/subsystem/alarm + name = "Alarm" + var/list/datum/alarm/all_handlers + +/datum/subsystem/alarm/New() + all_handlers = list(camera_alarm) + +/datum/subsystem/alarm/stat_entry() + stat(null,"Alarm-[master_controller.alarms_cost]\t# [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 + for(var/datum/alarm_handler/AH in all_handlers) + var/list/alarms = AH.alarms + total += alarms.len + + return total diff --git a/code/controllers/subsystems.dm b/code/controllers/subsystems.dm new file mode 100644 index 0000000000..11025d8d53 --- /dev/null +++ b/code/controllers/subsystems.dm @@ -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 << "[msg]" + 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() diff --git a/code/controllers/verbs.dm b/code/controllers/verbs.dm index 3dcba37bc0..325e2e6472 100644 --- a/code/controllers/verbs.dm +++ b/code/controllers/verbs.dm @@ -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 diff --git a/code/game/area/areas.dm b/code/game/area/areas.dm index 609d1a2a6b..13b7ffa34c 100644 --- a/code/game/area/areas.dm +++ b/code/game/area/areas.dm @@ -30,11 +30,17 @@ 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. + /*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) @@ -54,7 +60,7 @@ if(state == 1) a.cancelAlarm("Power", src, source) else - a.triggerAlarm("Power", src, cameras, source) + a.triggerAlarm("Power", src, cameras, source) */ return /area/proc/atmosalert(danger_level, var/set_firelocks=1) @@ -71,7 +77,7 @@ if (set_firelocks && 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() - + /* if (danger_level < 2 && atmosalm >= 2) for(var/area/RA in related) for(var/obj/machinery/camera/C in RA) @@ -98,7 +104,7 @@ atmosalm = danger_level for(var/area/RA in related) for (var/obj/machinery/alarm/AA in RA) - AA.update_icon() + AA.update_icon() */ return 1 return 0 @@ -141,15 +147,6 @@ 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() if (fire) @@ -164,13 +161,13 @@ else if(D.density) spawn(0) D.open() - for(var/area/RA in related) + /*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) + a.cancelAlarm("Fire", src, src)*/ /area/proc/readyalert() if(!eject) diff --git a/code/game/machinery/camera/camera.dm b/code/game/machinery/camera/camera.dm index d631ca46df..5d91cb6084 100644 --- a/code/game/machinery/camera/camera.dm +++ b/code/game/machinery/camera/camera.dm @@ -57,7 +57,7 @@ /obj/machinery/camera/Del() if(!alarm_on) triggerCameraAlarm() - + cancelCameraAlarm() ..() @@ -70,7 +70,7 @@ kick_viewers() triggerCameraAlarm() update_icon() - + spawn(900) stat &= ~EMPED cancelCameraAlarm() @@ -85,11 +85,11 @@ /obj/machinery/camera/ex_act(severity) if(src.invuln) return - + //camera dies if an explosion touches it! if(severity <= 2 || prob(50)) destroy() - + ..() //and give it the regular chance of being deleted outright @@ -174,7 +174,7 @@ if (S.current == src) O << "[U] holds \a [itemname] up to one of the cameras ..." O << browse(text("[][]", itemname, info), text("window=[]", itemname)) - + else if (istype(W, /obj/item/weapon/camera_bug)) if (!src.can_use()) user << "\blue Camera non-functional" @@ -185,7 +185,7 @@ else user << "\blue Camera bugged." src.bugged = 1 - + else if(W.damtype == BRUTE || W.damtype == BURN) //bashing cameras if (W.force >= src.toughness) visible_message("[src] has been [pick(W.attack_verb)] with [W] by [user]!") @@ -194,7 +194,7 @@ if (I.hitsound) playsound(loc, I.hitsound, 50, 1, -1) take_damage(W.force) - + else ..() @@ -221,14 +221,14 @@ if (force >= toughness && (force > toughness*4 || prob(25))) destroy() -//Used when someone breaks a camera +//Used when someone breaks a camera /obj/machinery/camera/proc/destroy() invalidateCameraCache() stat |= BROKEN kick_viewers() triggerCameraAlarm() update_icon() - + //sparks var/datum/effect/effect/system/spark_spread/spark_system = new /datum/effect/effect/system/spark_spread() spark_system.set_up(5, 0, loc) @@ -264,20 +264,14 @@ /obj/machinery/camera/proc/triggerCameraAlarm() 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) /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.cancelAlarm(loc, src) //if false, then the camera is listed as DEACTIVATED and cannot be used /obj/machinery/camera/proc/can_use() @@ -355,7 +349,7 @@ /obj/machinery/camera/interact(mob/living/user as mob) if(!panel_open || istype(user, /mob/living/silicon/ai)) return - + if(stat & BROKEN) user << "\The [src] is broken." return diff --git a/code/game/machinery/camera/motion.dm b/code/game/machinery/camera/motion.dm index 0c6f7d95a7..f14608b1d4 100644 --- a/code/game/machinery/camera/motion.dm +++ b/code/game/machinery/camera/motion.dm @@ -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.cancelAlarm(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 diff --git a/code/game/machinery/computer/camera.dm b/code/game/machinery/computer/camera.dm index 2067bd43a0..b1100fd77c 100644 --- a/code/game/machinery/computer/camera.dm +++ b/code/game/machinery/computer/camera.dm @@ -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 alarm_manager.active_alarms()) + A.cameras = null /obj/machinery/computer/security name = "security camera monitor" diff --git a/code/global.dm b/code/global.dm index c5fd821f79..13a62b38ce 100644 --- a/code/global.dm +++ b/code/global.dm @@ -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. diff --git a/code/modules/alarm/alarm.dm b/code/modules/alarm/alarm.dm new file mode 100644 index 0000000000..0bbeed527d --- /dev/null +++ b/code/modules/alarm/alarm.dm @@ -0,0 +1,78 @@ +/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/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 + source_name = source.name + start_time = world.time + +/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). + +/datum/alarm/New(var/atom/origin, var/atom/source, var/duration) + src.origin = origin + last_area = alarm_area() + set_duration(source, duration) + +/datum/alarm/proc/set_duration(var/atom/source, var/duration) + var/datum/alarm_source/AS = sources[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) + AS.duration = duration + +/datum/alarm/proc/clear(var/source) + var/datum/alarm_source/AS = sources[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/cameras() + if(!origin) + return list() + + if(!cameras) + cameras = origin.get_alarm_cameras() + + return cameras + + +/atom/proc/get_alarm_area() + return get_area(src) + +/area/get_alarm_area() + return src + +/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() diff --git a/code/modules/alarm/alarm_handler.dm b/code/modules/alarm/alarm_handler.dm new file mode 100644 index 0000000000..3fd21fc4a0 --- /dev/null +++ b/code/modules/alarm/alarm_handler.dm @@ -0,0 +1,112 @@ +#define ALARM_ORIGIN_LOST "Origin Lost" + +/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. + +/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.. + */ + +/datum/alarm_handler/proc/triggerAlarm(var/atom/origin, var/atom/source, var/duration = 0) + //Proper origin and source mandatory + if(!origin || !source) + return + + //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] + if(existing) + existing.set_duration(source, duration) + else + existing = new/datum/alarm(origin, source, duration) + + alarms |= existing + alarms_assoc[alarm_key] = existing + +/datum/alarm_handler/proc/cancelAlarm(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] + if(existing) + existing.clear(source) + if (!existing.sources.len) + alarms -= existing + alarms_assoc -= alarm_key + +/atom/proc/get_alarm_key() + return src + +/turf/get_alarm_key() + return get_area(src) + +/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" + +/obj/item/device/alarm_debug/loc + name = "An alarm debug tool - Loc" + +/obj/item/device/alarm_debug/verb/alarm() + set name = "Alarm" + set category = "Debug" + usr << "Raising alarm" + camera_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]" + +/obj/item/device/alarm_debug/loc/alarm() + set name = "Alarm" + set category = "Debug" + usr << "Raising alarm" + camera_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) diff --git a/code/modules/alarm/atmosphere_alarm.dm b/code/modules/alarm/atmosphere_alarm.dm new file mode 100644 index 0000000000..7aad426421 --- /dev/null +++ b/code/modules/alarm/atmosphere_alarm.dm @@ -0,0 +1,2 @@ +/datum/alarm_handler/atmosphere + category = "Atmosphere" diff --git a/code/modules/alarm/camera_alarm.dm b/code/modules/alarm/camera_alarm.dm new file mode 100644 index 0000000000..4624625043 --- /dev/null +++ b/code/modules/alarm/camera_alarm.dm @@ -0,0 +1,2 @@ +/datum/alarm_handler/camera + category = "Camera" \ No newline at end of file diff --git a/code/modules/alarm/fire_alarm.dm b/code/modules/alarm/fire_alarm.dm new file mode 100644 index 0000000000..e654b966b6 --- /dev/null +++ b/code/modules/alarm/fire_alarm.dm @@ -0,0 +1,2 @@ +/datum/alarm_handler/fire_alarm + category = "Fire" diff --git a/code/modules/alarm/motion_alarm.dm b/code/modules/alarm/motion_alarm.dm new file mode 100644 index 0000000000..f51ef3c27b --- /dev/null +++ b/code/modules/alarm/motion_alarm.dm @@ -0,0 +1,2 @@ +/datum/alarm_handler/motion + category = "Motion" diff --git a/code/modules/alarm/power_alarm.dm b/code/modules/alarm/power_alarm.dm new file mode 100644 index 0000000000..8571dbac61 --- /dev/null +++ b/code/modules/alarm/power_alarm.dm @@ -0,0 +1,2 @@ +/datum/alarm_handler/power + category = "Power" diff --git a/code/modules/mob/living/silicon/ai/ai.dm b/code/modules/mob/living/silicon/ai/ai.dm index 3278198723..376031fe4d 100644 --- a/code/modules/mob/living/silicon/ai/ai.dm +++ b/code/modules/mob/living/silicon/ai/ai.dm @@ -324,31 +324,8 @@ var/list/ai_verbs_default = list( set category = "AI Commands" set name = "Show Alerts" - var/dat = "Current Station Alerts\n" - dat += "Close

" - for (var/cat in alarms) - dat += text("[]
\n", cat) - var/list/alarmlist = alarms[cat] - if (alarmlist.len) - for (var/area_name in alarmlist) - var/datum/alarm/alarm = alarmlist[area_name] - dat += "" - - var/cameratext = "" - if (alarm.cameras) - for (var/obj/machinery/camera/I in alarm.cameras) - cameratext += text("[][]", (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 += "
\n" - else - dat += "-- All Systems Nominal
\n" - dat += "
\n" - - viewalerts = 1 - src << browse(dat, "window=aialerts&can_close=0") + //PsiFix + //nano_alarm.ui_interact(usr) // this verb lets the ai see the stations manifest /mob/living/silicon/ai/proc/ai_roster() @@ -521,29 +498,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 == "")? "" : "|"][C.c_tag]" - - 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" diff --git a/code/modules/mob/living/silicon/alarm.dm b/code/modules/mob/living/silicon/alarm.dm deleted file mode 100644 index ee505fae5c..0000000000 --- a/code/modules/mob/living/silicon/alarm.dm +++ /dev/null @@ -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 += "\[Show Alerts\]" - 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 += "\[Show Alerts\]" - 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 \ No newline at end of file diff --git a/code/modules/mob/living/silicon/robot/robot.dm b/code/modules/mob/living/silicon/robot/robot.dm index 6b0509eea1..025f1ef0c2 100644 --- a/code/modules/mob/living/silicon/robot/robot.dm +++ b/code/modules/mob/living/silicon/robot/robot.dm @@ -450,25 +450,8 @@ var/list/robot_verbs_default = list( /mob/living/silicon/robot/proc/robot_alerts() - var/dat = "Current Station Alerts\n" - dat += "Close

" - for (var/cat in alarms) - dat += text("[cat]
\n") - var/list/alarmlist = alarms[cat] - if (alarmlist.len) - for (var/area_name in alarmlist) - var/datum/alarm/alarm = alarmlist[area_name] - dat += "" - dat += text("-- [area_name]") - if (alarm.sources.len > 1) - dat += text("- [alarm.sources.len] sources") - dat += "
\n" - else - dat += "-- All Systems Nominal
\n" - dat += "
\n" - - viewalerts = 1 - src << browse(dat, "window=robotalerts&can_close=0") + //PsiFix + //nano_alarm.ui_interact(usr) /mob/living/silicon/robot/proc/self_diagnosis() if(!is_component_functioning("diagnosis unit")) @@ -636,25 +619,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 diff --git a/code/modules/mob/living/silicon/silicon.dm b/code/modules/mob/living/silicon/silicon.dm index bda7d984e2..af7881986b 100644 --- a/code/modules/mob/living/silicon/silicon.dm +++ b/code/modules/mob/living/silicon/silicon.dm @@ -23,6 +23,7 @@ var/local_transmit //If set, can only speak to others of the same type within a short range. var/sensor_mode = 0 //Determines the current HUD. + #define SEC_HUD 1 //Security HUD mode #define MED_HUD 2 //Medical HUD mode diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index 15347c2580..12c9f314e4 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -808,6 +808,8 @@ note dizziness decrements automatically in the mob's Life() proc. 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") @@ -1210,4 +1212,4 @@ mob/proc/yank_out_object() /mob/verb/westfaceperm() set hidden = 1 facing_dir = null - set_face_dir(WEST) + set_face_dir(WEST) From 5578afa07c23454d662a057c6f2a203da9b5ccd9 Mon Sep 17 00:00:00 2001 From: PsiOmega Date: Tue, 17 Feb 2015 16:53:08 +0100 Subject: [PATCH 2/7] General update. NanoUI interface sorted. Accessible by AI, borgs, and through consoles. Automatic alarm resets in case of lost sources. --- baystation12.dme | 4 +- code/__HELPERS/lists.dm | 4 + code/controllers/subsystem/alarms.dm | 26 ++-- code/game/machinery/alarm.dm | 2 + code/game/machinery/camera/camera.dm | 25 ++-- code/game/machinery/camera/motion.dm | 2 +- code/game/machinery/computer/camera.dm | 26 +--- code/game/machinery/computer/station_alert.dm | 134 +++++------------- code/modules/alarm/alarm.dm | 65 ++++++++- code/modules/alarm/alarm_handler.dm | 115 ++++++++------- code/modules/alarm/fire_alarm.dm | 2 +- code/modules/mob/living/silicon/ai/ai.dm | 19 +-- code/modules/mob/living/silicon/ai/nano.dm | 27 ---- .../mob/living/silicon/ai/subsystems.dm | 44 ++++++ .../modules/mob/living/silicon/robot/robot.dm | 32 ++--- .../mob/living/silicon/robot/subsystems.dm | 9 ++ code/modules/mob/living/silicon/silicon.dm | 10 +- code/modules/mob/mob.dm | 34 +++-- code/modules/mob/mob_helpers.dm | 13 ++ code/modules/nano/modules/alarm_monitor.dm | 86 +++++++++++ code/modules/nano/modules/rcon.dm | 2 +- nano/css/shared.css | 6 + nano/templates/alarm_monitor.tmpl | 37 +++++ 23 files changed, 439 insertions(+), 285 deletions(-) delete mode 100644 code/modules/mob/living/silicon/ai/nano.dm create mode 100644 code/modules/mob/living/silicon/ai/subsystems.dm create mode 100644 code/modules/mob/living/silicon/robot/subsystems.dm create mode 100644 code/modules/nano/modules/alarm_monitor.dm create mode 100644 nano/templates/alarm_monitor.tmpl 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 From 93b049e7a666320cd00b0aca9ef64b8a57ea6309 Mon Sep 17 00:00:00 2001 From: PsiOmega Date: Mon, 23 Feb 2015 10:47:47 +0100 Subject: [PATCH 3/7] Alarm centralization completion Ensures the master area is always used, to avoid issues caused by dynamic lightning which splits areas into sub-areas. Power alarms now handled centrally. --- code/game/area/areas.dm | 26 ------------------- code/game/machinery/computer/station_alert.dm | 2 +- code/modules/alarm/alarm.dm | 2 +- code/modules/alarm/alarm_handler.dm | 13 ++++++++-- code/modules/alarm/power_alarm.dm | 8 ++++++ code/modules/nano/modules/alarm_monitor.dm | 5 +--- code/modules/power/apc.dm | 17 +++++------- code/modules/power/singularity/investigate.dm | 4 +-- 8 files changed, 30 insertions(+), 47 deletions(-) diff --git a/code/game/area/areas.dm b/code/game/area/areas.dm index 13b7ffa34c..6e530d35d5 100644 --- a/code/game/area/areas.dm +++ b/code/game/area/areas.dm @@ -37,32 +37,6 @@ 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 diff --git a/code/game/machinery/computer/station_alert.dm b/code/game/machinery/computer/station_alert.dm index f585162ae9..84ce5cc0d1 100644 --- a/code/game/machinery/computer/station_alert.dm +++ b/code/game/machinery/computer/station_alert.dm @@ -42,5 +42,5 @@ if(alarms.len) icon_state = "alert:2" else - icon_state = "alert:0" + icon_state = initial(icon_state) return diff --git a/code/modules/alarm/alarm.dm b/code/modules/alarm/alarm.dm index 623c38c503..d53b54063b 100644 --- a/code/modules/alarm/alarm.dm +++ b/code/modules/alarm/alarm.dm @@ -95,7 +95,7 @@ /atom/proc/get_alarm_name() var/area/A = get_area(src) - return A.name + return A.master.name /area/get_alarm_name() return name diff --git a/code/modules/alarm/alarm_handler.dm b/code/modules/alarm/alarm_handler.dm index 9c92a7793a..a87202e453 100644 --- a/code/modules/alarm/alarm_handler.dm +++ b/code/modules/alarm/alarm_handler.dm @@ -15,8 +15,9 @@ /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) + if(!(origin && source)) return + origin = origin.get_alarm_origin() new_alarm = 0 //see if there is already an alarm of this origin @@ -37,8 +38,9 @@ /datum/alarm_handler/proc/clearAlarm(var/atom/origin, var/source) //Proper origin and source mandatory - if(!origin || !source) + if(!(origin && source)) return + origin = origin.get_alarm_origin() var/datum/alarm/existing = alarms_assoc[origin] if(existing) @@ -53,6 +55,13 @@ return 1 return 0 +/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 diff --git a/code/modules/alarm/power_alarm.dm b/code/modules/alarm/power_alarm.dm index 8571dbac61..2e5c6182d8 100644 --- a/code/modules/alarm/power_alarm.dm +++ b/code/modules/alarm/power_alarm.dm @@ -1,2 +1,10 @@ /datum/alarm_handler/power category = "Power" + +/datum/alarm_handler/power/notify_listeners(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) diff --git a/code/modules/nano/modules/alarm_monitor.dm b/code/modules/nano/modules/alarm_monitor.dm index f0044506c8..f828d9d776 100644 --- a/code/modules/nano/modules/alarm_monitor.dm +++ b/code/modules/nano/modules/alarm_monitor.dm @@ -1,6 +1,6 @@ /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_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 @@ -38,9 +38,6 @@ 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 diff --git a/code/modules/power/apc.dm b/code/modules/power/apc.dm index ea16adbe96..90b5500ac7 100644 --- a/code/modules/power/apc.dm +++ b/code/modules/power/apc.dm @@ -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 diff --git a/code/modules/power/singularity/investigate.dm b/code/modules/power/singularity/investigate.dm index cc2b7016b5..63c54c0e54 100644 --- a/code/modules/power/singularity/investigate.dm +++ b/code/modules/power/singularity/investigate.dm @@ -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") ..() \ No newline at end of file From a2cd104cc9169518896aab5246bd020834bc44f8 Mon Sep 17 00:00:00 2001 From: PsiOmega Date: Mon, 23 Feb 2015 13:19:23 +0100 Subject: [PATCH 4/7] Alarm centralization completion Fire alarms now handled centrally. --- code/game/area/Space Station 13 areas.dm | 13 ++++--- code/game/area/areas.dm | 39 +++++++++---------- code/game/machinery/alarm.dm | 48 ++++++++---------------- code/modules/alarm/alarm.dm | 21 ++++++++--- code/modules/alarm/alarm_handler.dm | 30 ++++++++++++--- code/modules/alarm/atmosphere_alarm.dm | 2 +- code/modules/alarm/camera_alarm.dm | 2 +- code/modules/alarm/fire_alarm.dm | 11 +++++- code/modules/alarm/motion_alarm.dm | 2 +- code/modules/alarm/power_alarm.dm | 6 +-- 10 files changed, 96 insertions(+), 78 deletions(-) diff --git a/code/game/area/Space Station 13 areas.dm b/code/game/area/Space Station 13 areas.dm index 0f19430b31..bb0d5787ab 100755 --- a/code/game/area/Space Station 13 areas.dm +++ b/code/game/area/Space Station 13 areas.dm @@ -110,7 +110,10 @@ 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/fire_alert() + return + +/area/space/fire_reset() return /area/space/readyalert() @@ -633,7 +636,7 @@ var/list/ghostteleportlocs = list() /area/prison/cell_block/C name = "Prison Cell Block C" icon_state = "brig" - + //////////////////// //SPACE STATION 13// //////////////////// @@ -1070,7 +1073,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 +1085,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 +1133,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" diff --git a/code/game/area/areas.dm b/code/game/area/areas.dm index 6e530d35d5..6e0005fa6e 100644 --- a/code/game/area/areas.dm +++ b/code/game/area/areas.dm @@ -87,9 +87,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() @@ -98,21 +98,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) @@ -122,12 +122,14 @@ spawn() D.close() -/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) @@ -135,13 +137,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) diff --git a/code/game/machinery/alarm.dm b/code/game/machinery/alarm.dm index 168f139ae3..ccd88a10fe 100644 --- a/code/game/machinery/alarm.dm +++ b/code/game/machinery/alarm.dm @@ -117,11 +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 @@ -949,7 +944,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) @@ -983,7 +977,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) @@ -1082,6 +1077,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("Reset - Lockdown", src) @@ -1147,26 +1143,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) ..() @@ -1182,20 +1178,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) diff --git a/code/modules/alarm/alarm.dm b/code/modules/alarm/alarm.dm index d53b54063b..525a5b3d21 100644 --- a/code/modules/alarm/alarm.dm +++ b/code/modules/alarm/alarm.dm @@ -4,6 +4,7 @@ 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. @@ -22,11 +23,11 @@ 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) +/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_duration(source, duration) + set_source_data(source, duration, severity) /datum/alarm/proc/process() // Has origin gone missing? @@ -41,7 +42,7 @@ AS.duration = 0 AS.end_time = world.time + ALARM_RESET_DELAY -/datum/alarm/proc/set_duration(var/atom/source, var/duration) +/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) @@ -51,6 +52,7 @@ 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] @@ -83,22 +85,29 @@ 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() - return get_area(src) + var/area/A = get_area(src) + return A.master /area/get_alarm_area() - return src + return src.master /atom/proc/get_alarm_name() var/area/A = get_area(src) return A.master.name /area/get_alarm_name() - return name + return master.name /mob/get_alarm_name() return name diff --git a/code/modules/alarm/alarm_handler.dm b/code/modules/alarm/alarm_handler.dm index a87202e453..6ecc60eea2 100644 --- a/code/modules/alarm/alarm_handler.dm +++ b/code/modules/alarm/alarm_handler.dm @@ -12,7 +12,7 @@ A.process() check_alarm_cleared(A) -/datum/alarm_handler/proc/triggerAlarm(var/atom/origin, var/atom/source, var/duration = 0) +/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)) @@ -23,16 +23,16 @@ //see if there is already an alarm of this origin var/datum/alarm/existing = alarms_assoc[origin] if(existing) - existing.set_duration(source, duration) + existing.set_source_data(source, duration, severity) else - existing = new/datum/alarm(origin, source, duration) + existing = new/datum/alarm(origin, source, duration, severity) new_alarm = 1 alarms |= existing alarms_assoc[origin] = existing if(new_alarm) alarms = dd_sortedObjectList(alarms) - notify_listeners(existing, ALARM_RAISED) + on_alarm_change(existing, ALARM_RAISED) return new_alarm @@ -51,10 +51,30 @@ if ((alarm.end_time && world.time > alarm.end_time) || !alarm.sources.len) alarms -= alarm alarms_assoc -= alarm.origin - notify_listeners(alarm, ALARM_CLEARED) + 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 diff --git a/code/modules/alarm/atmosphere_alarm.dm b/code/modules/alarm/atmosphere_alarm.dm index 7aad426421..153b9b9f04 100644 --- a/code/modules/alarm/atmosphere_alarm.dm +++ b/code/modules/alarm/atmosphere_alarm.dm @@ -1,2 +1,2 @@ /datum/alarm_handler/atmosphere - category = "Atmosphere" + category = "Atmosphere Alarms" diff --git a/code/modules/alarm/camera_alarm.dm b/code/modules/alarm/camera_alarm.dm index 4624625043..9594a1c8a0 100644 --- a/code/modules/alarm/camera_alarm.dm +++ b/code/modules/alarm/camera_alarm.dm @@ -1,2 +1,2 @@ /datum/alarm_handler/camera - category = "Camera" \ No newline at end of file + category = "Camera Alarms" diff --git a/code/modules/alarm/fire_alarm.dm b/code/modules/alarm/fire_alarm.dm index 1b7256517e..9c7a9874c7 100644 --- a/code/modules/alarm/fire_alarm.dm +++ b/code/modules/alarm/fire_alarm.dm @@ -1,2 +1,11 @@ /datum/alarm_handler/fire - category = "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() + ..() diff --git a/code/modules/alarm/motion_alarm.dm b/code/modules/alarm/motion_alarm.dm index f51ef3c27b..cafc7c128d 100644 --- a/code/modules/alarm/motion_alarm.dm +++ b/code/modules/alarm/motion_alarm.dm @@ -1,2 +1,2 @@ /datum/alarm_handler/motion - category = "Motion" + category = "Motion Alarms" diff --git a/code/modules/alarm/power_alarm.dm b/code/modules/alarm/power_alarm.dm index 2e5c6182d8..2df6d1eab3 100644 --- a/code/modules/alarm/power_alarm.dm +++ b/code/modules/alarm/power_alarm.dm @@ -1,10 +1,10 @@ /datum/alarm_handler/power - category = "Power" + category = "Power Alarms" -/datum/alarm_handler/power/notify_listeners(var/datum/alarm/alarm, var/was_raised) - ..() +/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) From f510c38d22eea024b384cc90d9423040b2b4b976 Mon Sep 17 00:00:00 2001 From: PsiOmega Date: Mon, 23 Feb 2015 15:11:13 +0100 Subject: [PATCH 5/7] Alarm centralization completion Atmosphere alarms now handled centrally. --- code/datums/wires/alarm.dm | 4 +-- code/game/area/Space Station 13 areas.dm | 3 ++ code/game/area/areas.dm | 37 ++++++---------------- code/game/machinery/alarm.dm | 16 ++-------- code/modules/alarm/alarm_handler.dm | 6 ++++ code/modules/alarm/atmosphere_alarm.dm | 17 ++++++++++ code/modules/nano/modules/alarm_monitor.dm | 2 +- 7 files changed, 41 insertions(+), 44 deletions(-) diff --git a/code/datums/wires/alarm.dm b/code/datums/wires/alarm.dm index 6ebc937265..c34b0291fb 100644 --- a/code/datums/wires/alarm.dm +++ b/code/datums/wires/alarm.dm @@ -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() diff --git a/code/game/area/Space Station 13 areas.dm b/code/game/area/Space Station 13 areas.dm index bb0d5787ab..55cfeaccfb 100755 --- a/code/game/area/Space Station 13 areas.dm +++ b/code/game/area/Space Station 13 areas.dm @@ -110,6 +110,9 @@ 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/atmosalert() + return + /area/space/fire_alert() return diff --git a/code/game/area/areas.dm b/code/game/area/areas.dm index 6e0005fa6e..ae11b5aa48 100644 --- a/code/game/area/areas.dm +++ b/code/game/area/areas.dm @@ -37,10 +37,7 @@ cameras += C return cameras -/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) @@ -48,37 +45,21 @@ 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() - /* - 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) + else if (danger_level >= 2 && atmosalm < 2) + air_doors_close() - 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) for (var/obj/machinery/alarm/AA in RA) - AA.update_icon() */ + AA.update_icon() return 1 return 0 diff --git a/code/game/machinery/alarm.dm b/code/game/machinery/alarm.dm index ccd88a10fe..0f2af7485f 100644 --- a/code/game/machinery/alarm.dm +++ b/code/game/machinery/alarm.dm @@ -117,14 +117,6 @@ first_run() -//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) @@ -436,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() @@ -454,12 +446,10 @@ 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) @@ -766,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 diff --git a/code/modules/alarm/alarm_handler.dm b/code/modules/alarm/alarm_handler.dm index 6ecc60eea2..ef9bfa8bd6 100644 --- a/code/modules/alarm/alarm_handler.dm +++ b/code/modules/alarm/alarm_handler.dm @@ -47,6 +47,12 @@ 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 diff --git a/code/modules/alarm/atmosphere_alarm.dm b/code/modules/alarm/atmosphere_alarm.dm index 153b9b9f04..9751319111 100644 --- a/code/modules/alarm/atmosphere_alarm.dm +++ b/code/modules/alarm/atmosphere_alarm.dm @@ -1,2 +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 diff --git a/code/modules/nano/modules/alarm_monitor.dm b/code/modules/nano/modules/alarm_monitor.dm index f828d9d776..e4de543498 100644 --- a/code/modules/nano/modules/alarm_monitor.dm +++ b/code/modules/nano/modules/alarm_monitor.dm @@ -56,7 +56,7 @@ 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) + for(var/datum/alarm/A in AH.major_alarms()) var/cameras[0] var/lost_sources[0] From 4659415af2a971f3950e71d7f51556a5b2d399b7 Mon Sep 17 00:00:00 2001 From: PsiOmega Date: Mon, 23 Feb 2015 17:14:20 +0100 Subject: [PATCH 6/7] Atmospheric computer NanoUI interface. --- code/game/machinery/computer/atmos_alert.dm | 141 ++++++++------------ nano/templates/atmos_alert.tmpl | 17 +++ 2 files changed, 70 insertions(+), 88 deletions(-) create mode 100644 nano/templates/atmos_alert.tmpl diff --git a/code/game/machinery/computer/atmos_alert.dm b/code/game/machinery/computer/atmos_alert.dm index 8e978853c4..62b00dab0f 100644 --- a/code/game/machinery/computer/atmos_alert.dm +++ b/code/game/machinery/computer/atmos_alert.dm @@ -1,116 +1,81 @@ //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/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 += "[zone] X
" - else - priority_text = "No priority alerts detected.
" - - if(minor_air_alarms.len) - for(var/zone in minor_air_alarms) - minor_text += "[zone] X
" - else - minor_text = "No minor alerts detected.
" - - var/output = {"[name]
-Priority Alerts:
-[priority_text] -
-
-Minor Alerts:
-[minor_text] -
"} - - 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 diff --git a/nano/templates/atmos_alert.tmpl b/nano/templates/atmos_alert.tmpl new file mode 100644 index 0000000000..dc6f3b9da8 --- /dev/null +++ b/nano/templates/atmos_alert.tmpl @@ -0,0 +1,17 @@ +

Priority Alerts

+{{for data.priority_alarms}} +
+ {{:value.name}} {{:helper.link('Reset', null, {'clear_alarm' : value.ref})}} +
+{{empty}} + No priority alerts detected. +{{/for}} + +

Minor Alerts

+
+ {{for data.minor_alarms}} + {{:value.name}} {{:helper.link('Reset', null, {'clear_alarm' : value.ref})}} + {{empty}} + No minor alerts detected. + {{/for}} +
From d8245540efe07c6d0930e35ee0c4b1fd4de07797 Mon Sep 17 00:00:00 2001 From: PsiOmega Date: Mon, 23 Feb 2015 18:51:50 +0100 Subject: [PATCH 7/7] Restores silicon alarm notices. --- code/game/machinery/computer/atmos_alert.dm | 4 ++ code/modules/alarm/alarm_handler.dm | 53 --------------- code/modules/mob/living/silicon/ai/life.dm | 1 + code/modules/mob/living/silicon/robot/life.dm | 1 + code/modules/mob/living/silicon/silicon.dm | 64 +++++++++++++++++++ 5 files changed, 70 insertions(+), 53 deletions(-) diff --git a/code/game/machinery/computer/atmos_alert.dm b/code/game/machinery/computer/atmos_alert.dm index 62b00dab0f..8306b3a87e 100644 --- a/code/game/machinery/computer/atmos_alert.dm +++ b/code/game/machinery/computer/atmos_alert.dm @@ -13,6 +13,10 @@ var/global/list/minor_air_alarms = list() /obj/machinery/computer/atmos_alert/New() ..() 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) ui_interact(user) diff --git a/code/modules/alarm/alarm_handler.dm b/code/modules/alarm/alarm_handler.dm index ef9bfa8bd6..73bd2348aa 100644 --- a/code/modules/alarm/alarm_handler.dm +++ b/code/modules/alarm/alarm_handler.dm @@ -97,56 +97,3 @@ /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" - -/obj/item/device/alarm_debug/verb/alarm() - set name = "Alarm" - set category = "Debug" - usr << "Raising alarm" - fire_alarm.triggerAlarm(src, src) - -/obj/item/device/alarm_debug/verb/reset() - set name = "Reset" - set category = "Debug" - usr << "Clearing alarm" - fire_alarm.clearAlarm(src, src) - -/obj/item/device/alarm_debug/loc/alarm() - set name = "Alarm" - set category = "Debug" - usr << "Raising alarm" - fire_alarm.triggerAlarm(src.loc, src) - -/obj/item/device/alarm_debug/loc/reset() - set name = "Reset" - set category = "Debug" - usr << "Clearing alarm" - 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/mob/living/silicon/ai/life.dm b/code/modules/mob/living/silicon/ai/life.dm index a1b9f20605..602e427f95 100644 --- a/code/modules/mob/living/silicon/ai/life.dm +++ b/code/modules/mob/living/silicon/ai/life.dm @@ -171,6 +171,7 @@ sleep(50) theAPC = null + process_queued_alarms() regular_hud_updates() switch(src.sensor_mode) if (SEC_HUD) diff --git a/code/modules/mob/living/silicon/robot/life.dm b/code/modules/mob/living/silicon/robot/life.dm index 8295687de9..c5d74c54dd 100644 --- a/code/modules/mob/living/silicon/robot/life.dm +++ b/code/modules/mob/living/silicon/robot/life.dm @@ -18,6 +18,7 @@ use_power() process_killswitch() process_locks() + process_queued_alarms() update_canmove() /mob/living/silicon/robot/proc/clamp_values() diff --git a/code/modules/mob/living/silicon/silicon.dm b/code/modules/mob/living/silicon/silicon.dm index a4ee540c2b..fc647dcbeb 100644 --- a/code/modules/mob/living/silicon/silicon.dm +++ b/code/modules/mob/living/silicon/silicon.dm @@ -27,6 +27,9 @@ 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 @@ -35,6 +38,11 @@ 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 name = real_name @@ -291,3 +299,59 @@ /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 << "--- [AH.category] Detected ---" + 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 << "--- [AH.category] Cleared ---" + 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 == "")? "" : "|"][C.c_tag]" + src << "[A.alarm_name()]! ([(cameratext)? cameratext : "No Camera"])"