[MIRROR] Redoes how alarms are handled, moves their behavior to datums (#7547)

* Redoes how alarms are handled, moves their behavior to datums

* a

Co-authored-by: LemonInTheDark <58055496+LemonInTheDark@users.noreply.github.com>
Co-authored-by: Gandalf <jzo123@hotmail.com>
This commit is contained in:
SkyratBot
2021-08-16 00:38:01 +02:00
committed by GitHub
parent 61317efd99
commit d4e11d2080
19 changed files with 430 additions and 588 deletions

13
code/__DEFINES/alarm.dm Normal file
View File

@@ -0,0 +1,13 @@
//A set of defines to be used by the alarm datums
///Sent by air alarms, indecates something wrong with thier attached atmosphere
#define ALARM_ATMOS "Atmosphere"
///Sent by fire alarms when they are toggled
#define ALARM_FIRE "Fire"
///Sent by apcs when their power starts to fail
#define ALARM_POWER "Power"
///Sent by cameras when they're disabled in some manner
#define ALARM_CAMERA "Camera"
///Sent by display cases when they're broken into
#define ALARM_BURGLAR "Burglar"
///Sent by motion detecting cameras when they well, detect motion
#define ALARM_MOTION "Motion"

View File

@@ -49,6 +49,10 @@
#define COMSIG_WEATHER_START(event_type) "!weather_start [event_type]" #define COMSIG_WEATHER_START(event_type) "!weather_start [event_type]"
#define COMSIG_WEATHER_WINDDOWN(event_type) "!weather_winddown [event_type]" #define COMSIG_WEATHER_WINDDOWN(event_type) "!weather_winddown [event_type]"
#define COMSIG_WEATHER_END(event_type) "!weather_end [event_type]" #define COMSIG_WEATHER_END(event_type) "!weather_end [event_type]"
/// An alarm of some form was sent (datum/alarm_handler/source, alarm_type, area/source_area)
#define COMSIG_ALARM_FIRE(alarm_type) "!alarm_fire [alarm_type]"
/// An alarm of some form was cleared (datum/alarm_handler/source, alarm_type, area/source_area)
#define COMSIG_ALARM_CLEAR(alarm_type) "!alarm_clear [alarm_type]"
/// signals from globally accessible objects /// signals from globally accessible objects
@@ -1378,6 +1382,11 @@
/// Called on the merger after finishing a refresh: (list/leaving_members, list/joining_members) /// Called on the merger after finishing a refresh: (list/leaving_members, list/joining_members)
#define COMSIG_MERGER_REFRESH_COMPLETE "comsig_merger_refresh_complete" #define COMSIG_MERGER_REFRESH_COMPLETE "comsig_merger_refresh_complete"
// Alarm listener datum signals
///Sent when an alarm is fired (alarm, area/source_area)
#define COMSIG_ALARM_TRIGGERED "comsig_alarm_triggered"
///Send when an alarm source is cleared (alarm_type, area/source_area)
#define COMSIG_ALARM_CLEARED "comsig_alarm_clear"
// Vacuum signals // Vacuum signals
/// Called on a bag being attached to a vacuum parent /// Called on a bag being attached to a vacuum parent
#define COMSIG_VACUUM_BAG_ATTACH "comsig_vacuum_bag_attach" #define COMSIG_VACUUM_BAG_ATTACH "comsig_vacuum_bag_attach"

199
code/datums/alarm.dm Normal file
View File

@@ -0,0 +1,199 @@
//This files deals with the generic sending and receiving of "alarms"
//This is a somewhat blanket term, it covers things like fire/power/atmos alarms, along with some oddballs
//Side effect of how the system is used, these are mostly things that are of interest to ais and borgs
//Though it could easily be expanded to cover other senders/revievers
//The system as a whole differs from reading off a global list in a few ways.
//In that A, it allows us to send cameras for ais/borgs/potentially others to jump to
//And B, it's not like we're giving you all the alarms that have been sent, because of the seperate listing for each reviever
//You only recieve alarms sent after you start to listen
//Also of note, due to an optimzation done on areas, one alarm handler will only ever send one "on" or "off" alarm
//So the whole only receving stuff sent post creation thing actually matters
//Honestly I'm not sure how much of this is a feature, and how much is just old code
//But I'm leaving it how I found it
///Represents a single source of alarms, one alarm handler will only ever count for one alarm per listener
/datum/alarm_handler
///A list of alarm type -> list of areas we currently have alarms in
var/list/sent_alarms = list()
///Our source atom
var/atom/source_atom
/datum/alarm_handler/New(atom/source_atom)
if(istype(source_atom))
src.source_atom = source_atom
else
var/source_type = (istype(source_atom, /datum)) ? source_atom.type : ""
stack_trace("a non atom was passed into alarm_handler! [source_atom] [source_type]")
return ..()
/datum/alarm_handler/Destroy()
for(var/alarm_type in sent_alarms)
for(var/area/area_to_clear as anything in sent_alarms[alarm_type])
//Yeet all connected alarms
clear_alarm_from_area(alarm_type, area_to_clear)
source_atom = null
return ..()
///Sends an alarm to any interested things, does some checks to prevent unneeded work
///Important to note is that source_atom is not held as a ref, we're used as a proxy to prevent hard deletes
///optional_camera should only be used when you have one camera you want to pass along to alarm listeners, most of the time you should have no use for it
/datum/alarm_handler/proc/send_alarm(alarm_type, atom/use_as_source_atom, optional_camera)
if(!use_as_source_atom)
use_as_source_atom = source_atom
if(!use_as_source_atom)
return
var/area/our_area = get_area(use_as_source_atom)
var/our_z_level = use_as_source_atom.z
if (our_area.area_flags & NO_ALERTS)
return FALSE
var/list/existing_alarms = sent_alarms[alarm_type]
if(existing_alarms)
if(our_area in existing_alarms)
return FALSE
else
sent_alarms[alarm_type] = list()
existing_alarms = sent_alarms[alarm_type]
existing_alarms += our_area
our_area.active_alarms[alarm_type] += 1
SEND_GLOBAL_SIGNAL(COMSIG_ALARM_FIRE(alarm_type), src, alarm_type, our_area, our_z_level, optional_camera)
return TRUE
///Clears an alarm from any interested listeners
/datum/alarm_handler/proc/clear_alarm(alarm_type, use_as_source_atom)
SIGNAL_HANDLER
if(!use_as_source_atom)
use_as_source_atom = source_atom
if(!use_as_source_atom)
return
return clear_alarm_from_area(alarm_type, get_area(use_as_source_atom))
///Exists so we can request that the alarms from an area are cleared, even if our source atom is no longer in that area
/datum/alarm_handler/proc/clear_alarm_from_area(alarm_type, area/our_area)
if (our_area.area_flags & NO_ALERTS)
return FALSE
var/list/existing_alarms = sent_alarms[alarm_type]
if(!existing_alarms)
return FALSE
if(!(our_area in existing_alarms))
return FALSE
existing_alarms -= our_area
if(!length(existing_alarms))
sent_alarms -= alarm_type
our_area.active_alarms[alarm_type] -= 1
if(!length(our_area.active_alarms))
our_area.active_alarms -= alarm_type
SEND_GLOBAL_SIGNAL(COMSIG_ALARM_CLEAR(alarm_type), src, alarm_type, our_area)
return TRUE
/datum/alarm_listener
///List of valid source z levels, ignored if null
var/list/allowed_z_levels
///List of allowed areas. if this is null it's ignored
var/list/allowed_areas
///List of alarm type -> list of area name -> list(area, ref to area's cameras, list(sources))
var/list/alarms = list()
///Should we allow alarm changes to go through or not
var/accepting_alarm_changes = TRUE
///Accepts a list of alarm types to pay attention to, a list of valid z levels, and a list of valid areas. areas and zlevels are ignored if null
/datum/alarm_listener/New(alarms_to_listen_for, allowed_z_levels, allowed_areas)
src.allowed_z_levels = allowed_z_levels
src.allowed_areas = allowed_areas
for(var/alarm_type in alarms_to_listen_for)
RegisterSignal(SSdcs, COMSIG_ALARM_FIRE(alarm_type), .proc/add_alarm)
RegisterSignal(SSdcs, COMSIG_ALARM_CLEAR(alarm_type), .proc/clear_alarm)
return ..()
///Adds an alarm to our alarms list, you shouldn't be calling this manually
///It should all be handled by the signal listening we do, unless you want to only send an alarm to one listener
/datum/alarm_listener/proc/add_alarm(datum/source, datum/alarm_handler/handler, alarm_type, area/source_area, source_z, optional_camera)
if (!accepting_alarm_changes)
return
if(allowed_z_levels && !(source_z in allowed_z_levels))
return
if(allowed_areas && !(source_area.type in allowed_areas))
return
var/list/alarms_of_our_type = alarms[alarm_type]
if(!alarms_of_our_type)
alarms[alarm_type] = list()
alarms_of_our_type = alarms[alarm_type]
if(alarms_of_our_type[source_area.name])
var/list/alarm = alarms_of_our_type[source_area.name]
var/list/sources = alarm[3]
sources |= handler
//Return if a source already exists, we don't want to send a signal or add a new entry
return
//We normally directly pass in a ref to the area's camera's list to prevent hanging refs
var/list/cameras = source_area.cameras
if(optional_camera)
cameras = list(optional_camera) // This will cause harddels, so we need to clear manually
RegisterSignal(optional_camera, COMSIG_PARENT_QDELETING, .proc/clear_camera_ref, override = TRUE) //It's just fine to override, cause we clear all refs in the proc
//This does mean that only the first alarm of that camera type in the area will send a ping, but jesus what else can ya do
alarms_of_our_type[source_area.name] = list(source_area, cameras, list(handler))
SEND_SIGNAL(src, COMSIG_ALARM_TRIGGERED, alarm_type, source_area)
///Removes an alarm to our alarms list, you probably shouldn't be calling this manually
///It should all be handled by the signal listening we do, unless you want to only remove an alarm to one listener
/datum/alarm_listener/proc/clear_alarm(datum/source, datum/alarm_handler/handler, alarm_type, area/source_area)
if(!accepting_alarm_changes)
return
var/list/alarms_of_our_type = alarms[alarm_type]
if(!alarms_of_our_type)
return
if(!alarms_of_our_type[source_area.name])
return
var/list/alarm = alarms_of_our_type[source_area.name]
var/list/sources = alarm[3]
sources -= handler
if (length(sources))
return //Return if there's still sources left, no sense clearing the list or bothering anyone about it
alarms_of_our_type -= source_area.name
SEND_SIGNAL(src, COMSIG_ALARM_CLEARED, alarm_type, source_area)
///Does what it says on the tin, exists for signal hooking
/datum/alarm_listener/proc/prevent_alarm_changes()
SIGNAL_HANDLER
accepting_alarm_changes = FALSE
///Does what it says on the tin, exists for signal hooking
/datum/alarm_listener/proc/allow_alarm_changes()
SIGNAL_HANDLER
accepting_alarm_changes = TRUE
///Used to manually clear camera refs if one is ref'd directly
/datum/alarm_listener/proc/clear_camera_ref(obj/machinery/camera/source)
SIGNAL_HANDLER
var/list/alarms_cache = alarms //Cache for sonic speec
for(var/alarm_type in alarms_cache)
var/list/alarms_of_type = alarms_cache[alarm_type] //Sonic cache speed forads
for(var/area_name as anything in alarms_of_type)
var/list/alarm_packet = alarms_of_type[area_name]
var/list/cameras = alarm_packet[2]
cameras -= source // REF FOUND AND CLEARED BOYSSSS

View File

@@ -48,8 +48,7 @@
A.mode = 1 // AALARM_MODE_SCRUB A.mode = 1 // AALARM_MODE_SCRUB
A.apply_mode(usr) A.apply_mode(usr)
if(WIRE_ALARM) // Clear alarms. if(WIRE_ALARM) // Clear alarms.
var/area/AA = get_area(A) if(A.alarm_manager.clear_alarm(ALARM_ATMOS))
if(AA.atmosalert(FALSE, holder))
A.post_alert(0) A.post_alert(0)
A.update_appearance() A.update_appearance()
@@ -70,7 +69,6 @@
A.mode = 3 // AALARM_MODE_PANIC A.mode = 3 // AALARM_MODE_PANIC
A.apply_mode(usr) A.apply_mode(usr)
if(WIRE_ALARM) // Post alarm. if(WIRE_ALARM) // Post alarm.
var/area/AA = get_area(A) if(A.alarm_manager.send_alarm(ALARM_ATMOS))
if(AA.atmosalert(TRUE, holder))
A.post_alert(2) A.post_alert(2)
A.update_appearance() A.update_appearance()

View File

@@ -17,13 +17,13 @@
///Do we have an active fire alarm? ///Do we have an active fire alarm?
var/fire = FALSE var/fire = FALSE
/* SKYRAT EDIT REMOVAL
///How many fire alarm sources do we have? ///How many fire alarm sources do we have?
var/triggered_firealarms = 0 var/triggered_firealarms = 0
*/ ///Alarm type to count of sources. Not usable for ^ because we handle fires differently
///Whether there is an atmos alarm in this area var/list/active_alarms = list()
var/atmosalm = FALSE ///We use this just for fire alarms, because they're area based right now so one alarm going poof shouldn't prevent you from clearing your alarms listing
var/poweralm = FALSE var/datum/alarm_handler/alarm_manager
var/lightswitch = TRUE var/lightswitch = TRUE
/// All beauty in this area combined, only includes indoor area. /// All beauty in this area combined, only includes indoor area.
@@ -135,6 +135,7 @@ GLOBAL_LIST_EMPTY(teleportlocs)
if (area_flags & UNIQUE_AREA) if (area_flags & UNIQUE_AREA)
GLOB.areas_by_type[type] = src GLOB.areas_by_type[type] = src
power_usage = new /list(AREA_USAGE_LEN) // Some atoms would like to use power in Initialize() power_usage = new /list(AREA_USAGE_LEN) // Some atoms would like to use power in Initialize()
alarm_manager = new(src) // just in case
return ..() return ..()
/* /*
@@ -233,87 +234,8 @@ GLOBAL_LIST_EMPTY(teleportlocs)
GLOB.areas_by_type[type] = null GLOB.areas_by_type[type] = null
GLOB.sortedAreas -= src GLOB.sortedAreas -= src
STOP_PROCESSING(SSobj, src) STOP_PROCESSING(SSobj, src)
QDEL_NULL(alarm_manager)
return ..() return ..()
/**
* Generate a power alert for this area
*
* Sends to all ai players, alert consoles, drones and alarm monitor programs in the world
*/
/area/proc/poweralert(state, obj/source)
if (area_flags & NO_ALERTS)
return
if (state != poweralm)
poweralm = state
if(istype(source)) //Only report power alarms on the z-level where the source is located.
for (var/item in GLOB.silicon_mobs)
var/mob/living/silicon/aiPlayer = item
if (!state)
aiPlayer.cancelAlarm("Power", src, source)
else
aiPlayer.triggerAlarm("Power", src, cameras, source)
for (var/item in GLOB.alert_consoles)
var/obj/machinery/computer/station_alert/a = item
if(!state)
a.cancelAlarm("Power", src, source)
else
a.triggerAlarm("Power", src, cameras, source)
for (var/item in GLOB.drones_list)
var/mob/living/simple_animal/drone/D = item
if(!state)
D.cancelAlarm("Power", src, source)
else
D.triggerAlarm("Power", src, cameras, source)
for(var/item in GLOB.alarmdisplay)
var/datum/computer_file/program/alarm_monitor/p = item
if(!state)
p.cancelAlarm("Power", src, source)
else
p.triggerAlarm("Power", src, cameras, source)
/**
* Generate an atmospheric alert for this area
*
* Sends to all ai players, alert consoles, drones and alarm monitor programs in the world
*/
/area/proc/atmosalert(isdangerous, obj/source)
if (area_flags & NO_ALERTS)
return
if(isdangerous != atmosalm)
if(isdangerous)
for (var/item in GLOB.silicon_mobs)
var/mob/living/silicon/aiPlayer = item
aiPlayer.triggerAlarm("Atmosphere", src, cameras, source)
for (var/item in GLOB.alert_consoles)
var/obj/machinery/computer/station_alert/a = item
a.triggerAlarm("Atmosphere", src, cameras, source)
for (var/item in GLOB.drones_list)
var/mob/living/simple_animal/drone/D = item
D.triggerAlarm("Atmosphere", src, cameras, source)
for(var/item in GLOB.alarmdisplay)
var/datum/computer_file/program/alarm_monitor/p = item
p.triggerAlarm("Atmosphere", src, cameras, source)
else
for (var/item in GLOB.silicon_mobs)
var/mob/living/silicon/aiPlayer = item
aiPlayer.cancelAlarm("Atmosphere", src, source)
for (var/item in GLOB.alert_consoles)
var/obj/machinery/computer/station_alert/a = item
a.cancelAlarm("Atmosphere", src, source)
for (var/item in GLOB.drones_list)
var/mob/living/simple_animal/drone/D = item
D.cancelAlarm("Atmosphere", src, source)
for(var/item in GLOB.alarmdisplay)
var/datum/computer_file/program/alarm_monitor/p = item
p.cancelAlarm("Atmosphere", src, source)
atmosalm = isdangerous
return TRUE
return FALSE
/* SKYRAT EDIT REMOVAL /* SKYRAT EDIT REMOVAL
/** /**
* Try to close all the firedoors in the area * Try to close all the firedoors in the area
@@ -352,19 +274,7 @@ GLOBAL_LIST_EMPTY(teleportlocs)
for(var/item in firealarms) for(var/item in firealarms)
var/obj/machinery/firealarm/F = item var/obj/machinery/firealarm/F = item
F.update_appearance() F.update_appearance()
if (!(area_flags & NO_ALERTS)) //Check here instead at the start of the proc so that fire alarms can still work locally even in areas that don't send alerts alarm_manager.send_alarm(ALARM_FIRE, source)
for (var/item in GLOB.alert_consoles)
var/obj/machinery/computer/station_alert/a = item
a.triggerAlarm("Fire", src, cameras, source)
for (var/item in GLOB.silicon_mobs)
var/mob/living/silicon/aiPlayer = item
aiPlayer.triggerAlarm("Fire", src, cameras, source)
for (var/item in GLOB.drones_list)
var/mob/living/simple_animal/drone/D = item
D.triggerAlarm("Fire", src, cameras, source)
for(var/item in GLOB.alarmdisplay)
var/datum/computer_file/program/alarm_monitor/p = item
p.triggerAlarm("Fire", src, cameras, source)
START_PROCESSING(SSobj, src) START_PROCESSING(SSobj, src)
/** /**
@@ -389,37 +299,13 @@ GLOBAL_LIST_EMPTY(teleportlocs)
if (should_reset_alarms) // if there's a source, make sure there's no fire alarms left if (should_reset_alarms) // if there's a source, make sure there's no fire alarms left
unset_fire_alarm_effects() unset_fire_alarm_effects()
//ModifyFiredoors(TRUE) SKYRAT EDIT CHANGE ModifyFiredoors(TRUE)
for(var/item in firealarms) for(var/item in firealarms)
var/obj/machinery/firealarm/F = item var/obj/machinery/firealarm/F = item
F.update_appearance() F.update_appearance()
if (!(area_flags & NO_ALERTS)) //Check here instead at the start of the proc so that fire alarms can still work locally even in areas that don't send alerts alarm_manager.clear_alarm(ALARM_FIRE, source)
for (var/item in GLOB.silicon_mobs)
var/mob/living/silicon/aiPlayer = item
aiPlayer.cancelAlarm("Fire", src, source)
for (var/item in GLOB.alert_consoles)
var/obj/machinery/computer/station_alert/a = item
a.cancelAlarm("Fire", src, source)
for (var/item in GLOB.drones_list)
var/mob/living/simple_animal/drone/D = item
D.cancelAlarm("Fire", src, source)
for(var/item in GLOB.alarmdisplay)
var/datum/computer_file/program/alarm_monitor/p = item
p.cancelAlarm("Fire", src, source)
STOP_PROCESSING(SSobj, src) STOP_PROCESSING(SSobj, src)
*/ //SKYRAT EDIT END
///Get rid of any dangling camera refs
/area/proc/clear_camera(obj/machinery/camera/cam)
LAZYREMOVE(cameras, cam)
for (var/mob/living/silicon/aiPlayer as anything in GLOB.silicon_mobs)
aiPlayer.freeCamera(src, cam)
for (var/obj/machinery/computer/station_alert/comp as anything in GLOB.alert_consoles)
comp.freeCamera(src, cam)
for (var/mob/living/simple_animal/drone/drone_on as anything in GLOB.drones_list)
drone_on.freeCamera(src, cam)
for(var/datum/computer_file/program/alarm_monitor/monitor as anything in GLOB.alarmdisplay)
monitor.freeCamera(src, cam)
/* SKYRAT EDIT REMOVAL
/** /**
* If 100 ticks has elapsed, toggle all the firedoors closed again * If 100 ticks has elapsed, toggle all the firedoors closed again
*/ */
@@ -428,7 +314,7 @@ GLOBAL_LIST_EMPTY(teleportlocs)
firereset() //If there are no breaches or fires, and this alert was caused by a breach or fire, die firereset() //If there are no breaches or fires, and this alert was caused by a breach or fire, die
if(firedoors_last_closed_on + 100 < world.time) //every 10 seconds if(firedoors_last_closed_on + 100 < world.time) //every 10 seconds
ModifyFiredoors(FALSE) ModifyFiredoors(FALSE)
*/ //SKYRAT EDIT END
/** /**
* Close and lock a door passed into this proc * Close and lock a door passed into this proc
* *
@@ -451,17 +337,11 @@ GLOBAL_LIST_EMPTY(teleportlocs)
if (area_flags & NO_ALERTS) if (area_flags & NO_ALERTS)
return return
//Trigger alarm effect //Trigger alarm effect
//set_fire_alarm_effect() SKYRAT EDIT REMOVAL set_fire_alarm_effect()
//Lockdown airlocks //Lockdown airlocks
for(var/obj/machinery/door/DOOR in src) for(var/obj/machinery/door/door in src)
close_and_lock_door(DOOR) close_and_lock_door(door)
for (var/i in GLOB.silicon_mobs)
var/mob/living/silicon/SILICON = i
if(SILICON.triggerAlarm("Burglar", src, cameras, trigger))
//Cancel silicon alert after 1 minute
addtimer(CALLBACK(SILICON, /mob/living/silicon.proc/cancelAlarm,"Burglar",src,trigger), 600)
/* SKYRAT EDIT REMOVAL
/** /**
* Trigger the fire alarm visual affects in an area * Trigger the fire alarm visual affects in an area
* *
@@ -494,7 +374,6 @@ GLOBAL_LIST_EMPTY(teleportlocs)
for(var/obj/machinery/light/L in src) for(var/obj/machinery/light/L in src)
L.update() L.update()
*/ */
/** /**
* Update the icon state of the area * Update the icon state of the area
* *

View File

@@ -40,6 +40,8 @@
var/upgrades = 0 var/upgrades = 0
var/internal_light = TRUE //Whether it can light up when an AI views it var/internal_light = TRUE //Whether it can light up when an AI views it
///Represents a signel source of camera alarms about movement or camera tampering
var/datum/alarm_handler/alarm_manager
/obj/machinery/camera/preset/toxins //Bomb test site in space /obj/machinery/camera/preset/toxins //Bomb test site in space
name = "Hardened Bomb-Test Camera" name = "Hardened Bomb-Test Camera"
@@ -86,6 +88,8 @@
else //this is handled by toggle_camera, so no need to update it twice. else //this is handled by toggle_camera, so no need to update it twice.
update_appearance() update_appearance()
alarm_manager = new(src)
/obj/machinery/camera/connect_to_shuttle(obj/docking_port/mobile/port, obj/docking_port/stationary/dock) /obj/machinery/camera/connect_to_shuttle(obj/docking_port/mobile/port, obj/docking_port/stationary/dock)
for(var/i in network) for(var/i in network)
network -= i network -= i
@@ -106,7 +110,8 @@
GLOB.cameranet.cameras -= src GLOB.cameranet.cameras -= src
cancelCameraAlarm() cancelCameraAlarm()
if(isarea(myarea)) if(isarea(myarea))
myarea.clear_camera(src) LAZYREMOVE(myarea.cameras, src)
QDEL_NULL(alarm_manager)
QDEL_NULL(assembly_ref) QDEL_NULL(assembly_ref)
if(bug) if(bug)
bug.bugged_cameras -= c_tag bug.bugged_cameras -= c_tag
@@ -443,13 +448,11 @@
/obj/machinery/camera/proc/triggerCameraAlarm() /obj/machinery/camera/proc/triggerCameraAlarm()
alarm_on = TRUE alarm_on = TRUE
for(var/mob/living/silicon/S in GLOB.silicon_mobs) alarm_manager.send_alarm(ALARM_CAMERA, src, src)
S.triggerAlarm("Camera", get_area(src), list(src), src)
/obj/machinery/camera/proc/cancelCameraAlarm() /obj/machinery/camera/proc/cancelCameraAlarm()
alarm_on = FALSE alarm_on = FALSE
for(var/mob/living/silicon/S in GLOB.silicon_mobs) alarm_manager.clear_alarm(ALARM_CAMERA)
S.cancelAlarm("Camera", get_area(src), src)
/obj/machinery/camera/proc/can_use() /obj/machinery/camera/proc/can_use()
if(!status) if(!status)

View File

@@ -51,20 +51,16 @@
cancelAlarm() cancelAlarm()
/obj/machinery/camera/proc/cancelAlarm() /obj/machinery/camera/proc/cancelAlarm()
if (detectTime == -1) if (detectTime == -1 && status)
for (var/i in GLOB.silicon_mobs) alarm_manager.clear_alarm(ALARM_MOTION)
var/mob/living/silicon/aiPlayer = i
if (status)
aiPlayer.cancelAlarm("Motion", get_area(src), src)
detectTime = 0 detectTime = 0
return TRUE return TRUE
/obj/machinery/camera/proc/triggerAlarm() /obj/machinery/camera/proc/triggerAlarm()
if (!detectTime) if (!detectTime)
return FALSE return FALSE
for (var/mob/living/silicon/aiPlayer in GLOB.player_list) if(status)
if (status) if(alarm_manager.send_alarm(ALARM_MOTION, src, src))
aiPlayer.triggerAlarm("Motion", get_area(src), list(src), src)
visible_message(span_warning("A red light flashes on the [src]!")) visible_message(span_warning("A red light flashes on the [src]!"))
detectTime = -1 detectTime = -1
return TRUE return TRUE

View File

@@ -4,16 +4,17 @@
icon_screen = "alert:0" icon_screen = "alert:0"
icon_keyboard = "atmos_key" icon_keyboard = "atmos_key"
circuit = /obj/item/circuitboard/computer/stationalert circuit = /obj/item/circuitboard/computer/stationalert
var/alarms = list("Fire" = list(), "Atmosphere" = list(), "Power" = list()) ///Listens for alarms, provides the alarms list for our ui
var/datum/alarm_listener/listener
light_color = LIGHT_COLOR_CYAN light_color = LIGHT_COLOR_CYAN
/obj/machinery/computer/station_alert/Initialize() /obj/machinery/computer/station_alert/Initialize()
. = ..() listener = new(list(ALARM_ATMOS, ALARM_FIRE, ALARM_POWER), list(z))
GLOB.alert_consoles += src return ..()
/obj/machinery/computer/station_alert/Destroy() /obj/machinery/computer/station_alert/Destroy()
GLOB.alert_consoles -= src QDEL_NULL(listener)
return ..() return ..()
/obj/machinery/computer/station_alert/ui_interact(mob/user, datum/tgui/ui) /obj/machinery/computer/station_alert/ui_interact(mob/user, datum/tgui/ui)
@@ -31,78 +32,23 @@
var/list/data = list() var/list/data = list()
data["alarms"] = list() data["alarms"] = list()
for(var/class in alarms) var/list/alarms = listener.alarms
data["alarms"][class] = list() for(var/alarm_type in alarms)
for(var/area in alarms[class]) data["alarms"][alarm_type] = list()
data["alarms"][class] += area for(var/area_name in alarms[alarm_type])
data["alarms"][alarm_type] += area_name
return data return data
/obj/machinery/computer/station_alert/proc/triggerAlarm(class, area/home, cameras, obj/source) /obj/machinery/computer/station_alert/on_set_machine_stat(old_value)
if(source.z != z) if(machine_stat & BROKEN)
return listener.prevent_alarm_changes()
if(machine_stat & (BROKEN))
return
var/list/our_sort = alarms[class]
for(var/areaname in our_sort)
if (areaname == home.name)
var/list/alarm = our_sort[areaname]
var/list/sources = alarm[3]
if (!(source in sources))
sources += source
return TRUE
var/obj/machinery/camera/cam = null
var/list/our_cams = null
if(cameras && islist(cameras))
our_cams = cameras
if (our_cams.len == 1)
cam = our_cams[1]
else if(cameras && istype(cameras, /obj/machinery/camera))
cam = cameras
our_sort[home.name] = list(home, (cam ? cam : cameras), list(source))
return TRUE
/obj/machinery/computer/station_alert/proc/freeCamera(area/home, obj/machinery/camera/cam)
for(var/class in alarms)
var/our_area = alarms[class][home.name]
if(!our_area)
continue
var/cams = our_area[2] //Get the cameras
if(!cams)
continue
if(islist(cams))
cams -= cam
if(length(cams) == 1)
our_area[2] = cams[1]
else else
our_area[2] = null listener.allow_alarm_changes()
/obj/machinery/computer/station_alert/proc/cancelAlarm(class, area/A, obj/origin)
if(machine_stat & (BROKEN))
return
var/list/L = 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
/obj/machinery/computer/station_alert/update_overlays() /obj/machinery/computer/station_alert/update_overlays()
. = ..() . = ..()
if(machine_stat & (NOPOWER|BROKEN)) if(machine_stat & (NOPOWER|BROKEN))
return return
var/active_alarms = FALSE if(length(listener.alarms))
for(var/cat in alarms)
var/list/L = alarms[cat]
if(L.len)
active_alarms = TRUE
if(active_alarms)
. += "alert:2" . += "alert:2"

View File

@@ -20,6 +20,8 @@
var/list/start_showpieces = list() //Takes sublists in the form of list("type" = /obj/item/bikehorn, "trophy_message" = "henk") var/list/start_showpieces = list() //Takes sublists in the form of list("type" = /obj/item/bikehorn, "trophy_message" = "henk")
var/trophy_message = "" var/trophy_message = ""
var/glass_fix = TRUE var/glass_fix = TRUE
///Represents a signel source of screaming when broken
var/datum/alarm_handler/alarm_manager
/obj/structure/displaycase/Initialize() /obj/structure/displaycase/Initialize()
. = ..() . = ..()
@@ -32,6 +34,7 @@
if(start_showpiece_type) if(start_showpiece_type)
showpiece = new start_showpiece_type (src) showpiece = new start_showpiece_type (src)
update_appearance() update_appearance()
alarm_manager = new(src)
/obj/structure/displaycase/vv_edit_var(vname, vval) /obj/structure/displaycase/vv_edit_var(vname, vval)
. = ..() . = ..()
@@ -49,6 +52,7 @@
/obj/structure/displaycase/Destroy() /obj/structure/displaycase/Destroy()
QDEL_NULL(electronics) QDEL_NULL(electronics)
QDEL_NULL(showpiece) QDEL_NULL(showpiece)
QDEL_NULL(alarm_manager)
return ..() return ..()
/obj/structure/displaycase/examine(mob/user) /obj/structure/displaycase/examine(mob/user)
@@ -96,8 +100,12 @@
/obj/structure/displaycase/proc/trigger_alarm() /obj/structure/displaycase/proc/trigger_alarm()
if(!alert) if(!alert)
return return
var/area/alarmed = get_area(src) //var/area/alarmed = get_area(src) SKYRAT EDIT REMOVAL
alarmed.burglaralert(src) //alarmed.burglaralert(src) SKYRAT EDIT REMOVAL
alarm_manager.send_alarm(ALARM_BURGLAR)
addtimer(CALLBACK(alarm_manager, /datum/alarm_handler/proc/clear_alarm, ALARM_BURGLAR), 1 MINUTES)
playsound(src, 'sound/effects/alert.ogg', 50, TRUE) playsound(src, 'sound/effects/alert.ogg', 50, TRUE)
/obj/structure/displaycase/update_overlays() /obj/structure/displaycase/update_overlays()

View File

@@ -82,6 +82,8 @@
var/frequency = FREQ_ATMOS_CONTROL var/frequency = FREQ_ATMOS_CONTROL
var/alarm_frequency = FREQ_ATMOS_ALARMS var/alarm_frequency = FREQ_ATMOS_ALARMS
var/datum/radio_frequency/radio_connection var/datum/radio_frequency/radio_connection
///Represents a signel source of atmos alarms, complains to all the listeners if one of our thresholds is violated
var/datum/alarm_handler/alarm_manager
var/list/TLV = list( // Breathable air. var/list/TLV = list( // Breathable air.
"pressure" = new/datum/tlv(HAZARD_LOW_PRESSURE, WARNING_LOW_PRESSURE, WARNING_HIGH_PRESSURE, HAZARD_HIGH_PRESSURE), // kPa. Values are min2, min1, max1, max2 "pressure" = new/datum/tlv(HAZARD_LOW_PRESSURE, WARNING_LOW_PRESSURE, WARNING_HIGH_PRESSURE, HAZARD_HIGH_PRESSURE), // kPa. Values are min2, min1, max1, max2
@@ -227,14 +229,13 @@
if(name == initial(name)) if(name == initial(name))
name = "[get_area_name(src)] Air Alarm" name = "[get_area_name(src)] Air Alarm"
alarm_manager = new(src)
update_appearance() update_appearance()
/obj/machinery/airalarm/Destroy() /obj/machinery/airalarm/Destroy()
SSradio.remove_object(src, frequency) SSradio.remove_object(src, frequency)
qdel(wires) QDEL_NULL(wires)
wires = null QDEL_NULL(alarm_manager)
var/area/ourarea = get_area(src)
ourarea.atmosalert(FALSE, src)
return ..() return ..()
/obj/machinery/airalarm/Initialize(mapload) /obj/machinery/airalarm/Initialize(mapload)
@@ -277,7 +278,7 @@
) )
var/area/A = get_area(src) var/area/A = get_area(src)
data["atmos_alarm"] = A.atmosalm data["atmos_alarm"] = !!A.active_alarms[ALARM_ATMOS]
data["fire_alarm"] = A.fire data["fire_alarm"] = A.fire
var/turf/T = get_turf(src) var/turf/T = get_turf(src)
@@ -447,13 +448,11 @@
apply_mode(usr) apply_mode(usr)
. = TRUE . = TRUE
if("alarm") if("alarm")
var/area/A = get_area(src) if(alarm_manager.send_alarm(ALARM_ATMOS))
if(A.atmosalert(TRUE, src))
post_alert(2) post_alert(2)
. = TRUE . = TRUE
if("reset") if("reset")
var/area/A = get_area(src) if(alarm_manager.clear_alarm(ALARM_ATMOS))
if(A.atmosalert(FALSE, src))
post_alert(0) post_alert(0)
. = TRUE . = TRUE
update_appearance() update_appearance()
@@ -659,8 +658,8 @@
icon_state = "alarmp" icon_state = "alarmp"
return ..() return ..()
var/area/A = get_area(src) var/area/our_area = get_area(src)
switch(max(danger_level, A.atmosalm)) switch(max(danger_level, !!our_area.active_alarms[ALARM_ATMOS]))
if(0) if(0)
icon_state = "alarm0" icon_state = "alarm0"
if(1) if(1)
@@ -734,8 +733,14 @@
var/new_area_danger_level = 0 var/new_area_danger_level = 0
for(var/obj/machinery/airalarm/AA in A) for(var/obj/machinery/airalarm/AA in A)
if (!(AA.machine_stat & (NOPOWER|BROKEN)) && !AA.shorted) if (!(AA.machine_stat & (NOPOWER|BROKEN)) && !AA.shorted)
new_area_danger_level = clamp(max(new_area_danger_level, AA.danger_level), 0,1) new_area_danger_level = clamp(max(new_area_danger_level, AA.danger_level), 0, 1)
if(A.atmosalert(new_area_danger_level,src)) //if area was in normal state or if area was in alert state
var/did_anything_happen
if(new_area_danger_level)
did_anything_happen = alarm_manager.send_alarm(ALARM_ATMOS)
else
did_anything_happen = alarm_manager.clear_alarm(ALARM_ATMOS)
if(did_anything_happen) //if something actually changed
post_alert(new_area_danger_level) post_alert(new_area_danger_level)
update_appearance() update_appearance()

View File

@@ -1,4 +1,4 @@
// Look up levels[z].traits[trait] /// Look up levels[z].traits[trait]
/datum/controller/subsystem/mapping/proc/level_trait(z, trait) /datum/controller/subsystem/mapping/proc/level_trait(z, trait)
if (!isnum(z) || z < 1) if (!isnum(z) || z < 1)
return null return null
@@ -15,21 +15,21 @@
return list() return list()
return default[z][DL_TRAITS][trait] return default[z][DL_TRAITS][trait]
// Check if levels[z] has any of the specified traits /// Check if levels[z] has any of the specified traits
/datum/controller/subsystem/mapping/proc/level_has_any_trait(z, list/traits) /datum/controller/subsystem/mapping/proc/level_has_any_trait(z, list/traits)
for (var/I in traits) for (var/I in traits)
if (level_trait(z, I)) if (level_trait(z, I))
return TRUE return TRUE
return FALSE return FALSE
// Check if levels[z] has all of the specified traits /// Check if levels[z] has all of the specified traits
/datum/controller/subsystem/mapping/proc/level_has_all_traits(z, list/traits) /datum/controller/subsystem/mapping/proc/level_has_all_traits(z, list/traits)
for (var/I in traits) for (var/I in traits)
if (!level_trait(z, I)) if (!level_trait(z, I))
return FALSE return FALSE
return TRUE return TRUE
// Get a list of all z which have the specified trait /// Get a list of all z which have the specified trait
/datum/controller/subsystem/mapping/proc/levels_by_trait(trait) /datum/controller/subsystem/mapping/proc/levels_by_trait(trait)
. = list() . = list()
var/list/_z_list = z_list var/list/_z_list = z_list
@@ -38,7 +38,7 @@
if (S.traits[trait]) if (S.traits[trait])
. += S.z_value . += S.z_value
// Get a list of all z which have any of the specified traits /// Get a list of all z which have any of the specified traits
/datum/controller/subsystem/mapping/proc/levels_by_any_trait(list/traits) /datum/controller/subsystem/mapping/proc/levels_by_any_trait(list/traits)
. = list() . = list()
var/list/_z_list = z_list var/list/_z_list = z_list
@@ -49,7 +49,7 @@
. += S.z_value . += S.z_value
break break
// Attempt to get the turf below the provided one according to Z traits /// Attempt to get the turf below the provided one according to Z traits
/datum/controller/subsystem/mapping/proc/get_turf_below(turf/T) /datum/controller/subsystem/mapping/proc/get_turf_below(turf/T)
if (!T) if (!T)
return return
@@ -58,7 +58,7 @@
return return
return locate(T.x, T.y, T.z + offset) return locate(T.x, T.y, T.z + offset)
// Attempt to get the turf above the provided one according to Z traits /// Attempt to get the turf above the provided one according to Z traits
/datum/controller/subsystem/mapping/proc/get_turf_above(turf/T) /datum/controller/subsystem/mapping/proc/get_turf_above(turf/T)
if (!T) if (!T)
return return
@@ -67,7 +67,7 @@
return return
return locate(T.x, T.y, T.z + offset) return locate(T.x, T.y, T.z + offset)
// Prefer not to use this one too often /// Prefer not to use this one too often
/datum/controller/subsystem/mapping/proc/get_station_center() /datum/controller/subsystem/mapping/proc/get_station_center()
var/station_z = levels_by_trait(ZTRAIT_STATION)[1] var/station_z = levels_by_trait(ZTRAIT_STATION)[1]
return locate(round(world.maxx * 0.5, 1), round(world.maxy * 0.5, 1), station_z) return locate(round(world.maxx * 0.5, 1), round(world.maxy * 0.5, 1), station_z)

View File

@@ -37,7 +37,6 @@
var/aiRestorePowerRoutine = POWER_RESTORATION_OFF var/aiRestorePowerRoutine = POWER_RESTORATION_OFF
var/requires_power = POWER_REQ_ALL var/requires_power = POWER_REQ_ALL
var/can_be_carded = TRUE var/can_be_carded = TRUE
var/alarms = list("Motion"=list(), "Fire"=list(), "Atmosphere"=list(), "Power"=list(), "Camera"=list(), "Burglar"=list())
var/viewalerts = 0 var/viewalerts = 0
var/icon/holo_icon //Default is assigned when AI is created. var/icon/holo_icon //Default is assigned when AI is created.
var/obj/vehicle/sealed/mecha/controlled_mech //For controlled_mech a mech, to determine whether to relaymove or use the AI eye. var/obj/vehicle/sealed/mecha/controlled_mech //For controlled_mech a mech, to determine whether to relaymove or use the AI eye.
@@ -101,6 +100,8 @@
var/atom/cam_prev var/atom/cam_prev
var/datum/robot_control/robot_control var/datum/robot_control/robot_control
///Alarm listener datum, handes caring about alarm events and such
var/datum/alarm_listener/listener
///remember AI's last location ///remember AI's last location
var/atom/lastloc var/atom/lastloc
@@ -178,6 +179,9 @@
ADD_TRAIT(src, TRAIT_PULL_BLOCKED, ROUNDSTART_TRAIT) ADD_TRAIT(src, TRAIT_PULL_BLOCKED, ROUNDSTART_TRAIT)
ADD_TRAIT(src, TRAIT_HANDS_BLOCKED, ROUNDSTART_TRAIT) ADD_TRAIT(src, TRAIT_HANDS_BLOCKED, ROUNDSTART_TRAIT)
listener = new(list(ALARM_ATMOS, ALARM_FIRE, ALARM_POWER, ALARM_CAMERA, ALARM_BURGLAR, ALARM_MOTION), list(z))
RegisterSignal(listener, COMSIG_ALARM_TRIGGERED, .proc/alarm_triggered)
RegisterSignal(listener, COMSIG_ALARM_CLEARED, .proc/alarm_cleared)
/mob/living/silicon/ai/key_down(_key, client/user) /mob/living/silicon/ai/key_down(_key, client/user)
if(findtext(_key, "numpad")) //if it's a numpad number, we can convert it to just the number if(findtext(_key, "numpad")) //if it's a numpad number, we can convert it to just the number
@@ -211,6 +215,7 @@
QDEL_NULL(doomsday_device) QDEL_NULL(doomsday_device)
QDEL_NULL(robot_control) QDEL_NULL(robot_control)
QDEL_NULL(aiMulti) QDEL_NULL(aiMulti)
QDEL_NULL(listener)
malfhack = null malfhack = null
current = null current = null
Bot = null Bot = null
@@ -270,12 +275,12 @@
/mob/living/silicon/ai/get_status_tab_items() /mob/living/silicon/ai/get_status_tab_items()
. = ..() . = ..()
if(stat != CONSCIOUS) if(stat != CONSCIOUS)
. += text("Systems nonfunctional") . += "Systems nonfunctional"
return return
. += text("System integrity: [(health + 100) * 0.5]%") . += "System integrity: [(health + 100) * 0.5]%"
if(isturf(loc)) //only show if we're "in" a core if(isturf(loc)) //only show if we're "in" a core
. += text("Backup Power: [battery * 0.5]%") . += "Backup Power: [battery * 0.5]%"
. += text("Connected cyborgs: [length(connected_robots)]") . += "Connected cyborgs: [length(connected_robots)]"
for(var/r in connected_robots) for(var/r in connected_robots)
var/mob/living/silicon/robot/connected_robot = r var/mob/living/silicon/robot/connected_robot = r
var/robot_status = "Nominal" var/robot_status = "Nominal"
@@ -286,19 +291,20 @@
else if(!connected_robot.cell || connected_robot.cell.charge <= 0) else if(!connected_robot.cell || connected_robot.cell.charge <= 0)
robot_status = "DEPOWERED" robot_status = "DEPOWERED"
//Name, Health, Battery, Model, Area, and Status! Everything an AI wants to know about its borgies! //Name, Health, Battery, Model, Area, and Status! Everything an AI wants to know about its borgies!
. += text("[connected_robot.name] | S.Integrity: [connected_robot.health]% | Cell: [connected_robot.cell ? "[connected_robot.cell.charge]/[connected_robot.cell.maxcharge]" : "Empty"] | \ . += "[connected_robot.name] | S.Integrity: [connected_robot.health]% | Cell: [connected_robot.cell ? "[connected_robot.cell.charge]/[connected_robot.cell.maxcharge]" : "Empty"] | \
Model: [connected_robot.designation] | Loc: [get_area_name(connected_robot, TRUE)] | Status: [robot_status]") Model: [connected_robot.designation] | Loc: [get_area_name(connected_robot, TRUE)] | Status: [robot_status]"
. += text("AI shell beacons detected: [LAZYLEN(GLOB.available_ai_shells)]") //Count of total AI shells . += "AI shell beacons detected: [LAZYLEN(GLOB.available_ai_shells)]" //Count of total AI shells
/mob/living/silicon/ai/proc/ai_alerts() /mob/living/silicon/ai/proc/ai_alerts()
var/dat = "<HEAD><TITLE>Current Station Alerts</TITLE><META HTTP-EQUIV='Refresh' CONTENT='10'></HEAD><BODY>\n" var/dat = "<HEAD><TITLE>Current Station Alerts</TITLE><META HTTP-EQUIV='Refresh' CONTENT='10'></HEAD><BODY>\n"
dat += "<A HREF='?src=[REF(src)];mach_close=aialerts'>Close</A><BR><BR>" dat += "<A HREF='?src=[REF(src)];mach_close=aialerts'>Close</A><BR><BR>"
for (var/cat in alarms) var/list/alarms = listener.alarms
dat += text("<B>[]</B><BR>\n", cat) for (var/alarm_type in alarms)
var/list/L = alarms[cat] dat += "<B>[alarm_type]</B><BR>\n"
if (L.len) var/list/alerts = alarms[alarm_type]
for (var/alarm in L) if (length(alerts))
var/list/alm = L[alarm] for (var/alarm in alerts)
var/list/alm = alerts[alarm]
var/area/A = alm[1] var/area/A = alm[1]
var/C = alm[2] var/C = alm[2]
var/list/sources = alm[3] var/list/sources = alm[3]
@@ -306,15 +312,15 @@
if (C && istype(C, /list)) if (C && istype(C, /list))
var/dat2 = "" var/dat2 = ""
for (var/obj/machinery/camera/I in C) for (var/obj/machinery/camera/I in C)
dat2 += text("[]<A HREF=?src=[REF(src)];switchcamera=[REF(I)]>[]</A>", (dat2=="") ? "" : " | ", I.c_tag) dat2 += "[(dat2=="") ? "" : " | "]<A HREF=?src=[REF(src)];switchcamera=[REF(I)]>[I.c_tag]</A>"
dat += text("-- [] ([])", A.name, (dat2!="") ? dat2 : "No Camera") dat += "-- [A.name] ([(dat2!="") ? dat2 : "No Camera"])"
else if (C && istype(C, /obj/machinery/camera)) else if (C && istype(C, /obj/machinery/camera))
var/obj/machinery/camera/Ctmp = C var/obj/machinery/camera/Ctmp = C
dat += text("-- [] (<A HREF=?src=[REF(src)];switchcamera=[REF(C)]>[]</A>)", A.name, Ctmp.c_tag) dat += "-- [A.name] (<A HREF=?src=[REF(src)];switchcamera=[REF(C)]>[Ctmp.c_tag]</A>)"
else else
dat += text("-- [] (No Camera)", A.name) dat += "-- [A.name] (No Camera)"
if (sources.len > 1) if (sources.len > 1)
dat += text("- [] sources", sources.len) dat += "- [sources.len] sources"
dat += "</NOBR><BR>\n" dat += "</NOBR><BR>\n"
else else
dat += "-- All Systems Nominal<BR>\n" dat += "-- All Systems Nominal<BR>\n"
@@ -416,7 +422,7 @@
if (href_list["mach_close"]) if (href_list["mach_close"])
if (href_list["mach_close"] == "aialerts") if (href_list["mach_close"] == "aialerts")
viewalerts = 0 viewalerts = 0
var/t1 = text("window=[]", href_list["mach_close"]) var/t1 = "window=[href_list["mach_close"]]"
unset_machine() unset_machine()
src << browse(null, t1) src << browse(null, t1)
if (href_list["switchcamera"]) if (href_list["switchcamera"])
@@ -541,77 +547,33 @@
Bot.call_bot(src, waypoint) Bot.call_bot(src, waypoint)
call_bot_cooldown = 0 call_bot_cooldown = 0
/mob/living/silicon/ai/triggerAlarm(class, area/home, cameras, obj/source) /mob/living/silicon/ai/proc/alarm_triggered(datum/source, alarm_type, area/source_area)
if(source.z != z) SIGNAL_HANDLER
return var/list/cameras = source_area.cameras
var/list/our_sort = alarms[class] var/home_name = source_area.name
for(var/areaname in our_sort)
if (areaname == home.name)
var/list/alarm = our_sort[areaname]
var/list/sources = alarm[3]
if (!(source in sources))
sources += source
return TRUE
var/obj/machinery/camera/cam = null if (length(cameras))
var/list/our_cams = null var/obj/machinery/camera/cam = cameras[1]
if(cameras && islist(cameras)) if (cam.can_use())
our_cams = cameras queueAlarm("--- [alarm_type] alarm detected in [home_name]! (<A HREF=?src=[REF(src)];switchcamera=[REF(cam)]>[cam.c_tag]</A>)", alarm_type)
if (our_cams.len == 1) else
cam = our_cams[1] var/first_run = FALSE
else if(cameras && istype(cameras, /obj/machinery/camera))
cam = cameras
our_sort[home.name] = list(home, (cam ? cam : cameras), list(source))
if (cameras)
if (cam?.can_use())
queueAlarm("--- [class] alarm detected in [home.name]! (<A HREF=?src=[REF(src)];switchcamera=[REF(cam)]>[cam.c_tag]</A>)", class)
else if (our_cams?.len)
var/foo = 0
var/dat2 = "" var/dat2 = ""
for (var/obj/machinery/camera/I in our_cams) for (var/obj/machinery/camera/camera as anything in cameras)
dat2 += text("[]<A HREF=?src=[REF(src)];switchcamera=[REF(I)]>[]</A>", (!foo) ? "" : " | ", I.c_tag) //I'm not fixing this shit... dat2 += "[(!first_run) ? "" : " | "]<A HREF=?src=[REF(src)];switchcamera=[REF(camera)]>[camera.c_tag]</A>"
foo = 1 first_run = TRUE
queueAlarm(text ("--- [] alarm detected in []! ([])", class, home.name, dat2), class) queueAlarm("--- [alarm_type] alarm detected in [home_name]! ([dat2])", alarm_type)
else else
queueAlarm(text("--- [] alarm detected in []! (No Camera)", class, home.name), class) queueAlarm("--- [alarm_type] alarm detected in [home_name]! (No Camera)", alarm_type)
else
queueAlarm(text("--- [] alarm detected in []! (No Camera)", class, home.name), class)
if (viewalerts) if (viewalerts)
ai_alerts() ai_alerts()
return 1 return 1
/mob/living/silicon/ai/freeCamera(area/home, obj/machinery/camera/cam) /mob/living/silicon/ai/proc/alarm_cleared(datum/source, alarm_type, area/source_area)
for(var/class in alarms) SIGNAL_HANDLER
var/our_area = alarms[class][home.name] queueAlarm("--- [alarm_type] alarm in [source_area.name] has been cleared.", alarm_type, 0)
if(!our_area) if(viewalerts)
continue ai_alerts()
var/cams = our_area[2] //Get the cameras
if(!cams)
continue
if(islist(cams))
cams -= cam
if(length(cams) == 1)
our_area[2] = cams[1]
else
our_area[2] = null
/mob/living/silicon/ai/cancelAlarm(class, area/A, obj/origin)
var/list/L = 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
if (cleared)
queueAlarm("--- [class] alarm in [A.name] has been cleared.", class, 0)
if (viewalerts) ai_alerts()
return !cleared
//Replaces /mob/living/silicon/ai/verb/change_network() in ai.dm & camera.dm //Replaces /mob/living/silicon/ai/verb/change_network() in ai.dm & camera.dm
//Adds in /mob/living/silicon/ai/proc/ai_network_change() instead //Adds in /mob/living/silicon/ai/proc/ai_network_change() instead

View File

@@ -76,6 +76,11 @@
aiPDA.ownjob = "Cyborg" aiPDA.ownjob = "Cyborg"
aiPDA.name = real_name + " (" + aiPDA.ownjob + ")" aiPDA.name = real_name + " (" + aiPDA.ownjob + ")"
//SKYRAT EDIT ADDITION END //SKYRAT EDIT ADDITION END
listener = new(list(ALARM_ATMOS, ALARM_FIRE, ALARM_POWER, ALARM_CAMERA, ALARM_BURGLAR, ALARM_MOTION), list(z))
RegisterSignal(listener, COMSIG_ALARM_TRIGGERED, .proc/alarm_triggered)
RegisterSignal(listener, COMSIG_ALARM_CLEARED, .proc/alarm_cleared)
listener.RegisterSignal(src, COMSIG_LIVING_DEATH, /datum/alarm_listener/proc/prevent_alarm_changes)
listener.RegisterSignal(src, COMSIG_LIVING_REVIVE, /datum/alarm_listener/proc/allow_alarm_changes)
/mob/living/silicon/robot/model/syndicate/Initialize() /mob/living/silicon/robot/model/syndicate/Initialize()
. = ..() . = ..()
@@ -126,6 +131,7 @@
QDEL_NULL(inv2) QDEL_NULL(inv2)
QDEL_NULL(inv3) QDEL_NULL(inv3)
QDEL_NULL(spark_system) QDEL_NULL(spark_system)
QDEL_NULL(listener)
cell = null cell = null
return ..() return ..()
@@ -208,15 +214,16 @@
/mob/living/silicon/robot/proc/robot_alerts() /mob/living/silicon/robot/proc/robot_alerts()
var/dat = "" var/dat = ""
for (var/cat in alarms) var/list/alarms = listener.alarms
dat += text("<B>[cat]</B><BR>\n") for (var/alarm_type in alarms)
var/list/L = alarms[cat] dat += "<B>[alarm_type]</B><BR>\n"
if (L.len) var/list/alerts = alarms[alarm_type]
for (var/alarm in L) if (length(alerts))
var/list/alm = L[alarm] for (var/alarm in alerts)
var/list/alm = alerts[alarm]
var/area/A = alm[1] var/area/A = alm[1]
dat += "<NOBR>" dat += "<NOBR>"
dat += text("-- [A.name]") dat += "-- [A.name]"
dat += "</NOBR><BR>\n" dat += "</NOBR><BR>\n"
else else
dat += "-- All Systems Nominal<BR>\n" dat += "-- All Systems Nominal<BR>\n"
@@ -259,7 +266,7 @@
if(cell) if(cell)
. += "Charge Left: [cell.charge]/[cell.maxcharge]" . += "Charge Left: [cell.charge]/[cell.maxcharge]"
else else
. += text("No Cell Inserted!") . += "No Cell Inserted!"
if(model) if(model)
for(var/datum/robot_energy_storage/st in model.storages) for(var/datum/robot_energy_storage/st in model.storages)
@@ -267,63 +274,13 @@
if(connected_ai) if(connected_ai)
. += "Master AI: [connected_ai.name]" . += "Master AI: [connected_ai.name]"
/mob/living/silicon/robot/proc/alarm_triggered(datum/source, alarm_type, area/source_area)
SIGNAL_HANDLER
queueAlarm("--- [alarm_type] alarm detected in [source_area.name]!", alarm_type)
/mob/living/silicon/robot/triggerAlarm(class, area/home, cameras, obj/source) /mob/living/silicon/robot/proc/alarm_cleared(datum/source, alarm_type, area/source_area)
if(source.z != z) SIGNAL_HANDLER
return queueAlarm("--- [alarm_type] alarm in [source_area.name] has been cleared.", alarm_type, FALSE)
if(stat == DEAD)
return TRUE
var/list/our_sort = alarms[class]
for(var/areaname in our_sort)
if (areaname == home.name)
var/list/alarm = our_sort[areaname]
var/list/sources = alarm[3]
if (!(source in sources))
sources += source
return TRUE
var/obj/machinery/camera/cam = null
var/list/our_cams = null
if(cameras && islist(cameras))
our_cams = cameras
if (our_cams.len == 1)
cam = our_cams[1]
else if(cameras && istype(cameras, /obj/machinery/camera))
cam = cameras
our_sort[home.name] = list(home, (cam ? cam : cameras), list(source))
queueAlarm(text("--- [class] alarm detected in [home.name]!"), class)
return TRUE
/mob/living/silicon/robot/freeCamera(area/home, obj/machinery/camera/cam)
for(var/class in alarms)
var/our_area = alarms[class][home.name]
if(!our_area)
continue
var/cams = our_area[2] //Get the cameras
if(!cams)
continue
if(islist(cams))
cams -= cam
if(length(cams) == 1)
our_area[2] = cams[1]
else
our_area[2] = null
/mob/living/silicon/robot/cancelAlarm(class, area/A, obj/origin)
var/list/L = alarms[class]
var/cleared = FALSE
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 = TRUE
L -= I
if (cleared)
queueAlarm("--- [class] alarm in [A.name] has been cleared.", class, 0)
return !cleared
/mob/living/silicon/robot/can_interact_with(atom/A) /mob/living/silicon/robot/can_interact_with(atom/A)
if (A == modularInterface) if (A == modularInterface)

View File

@@ -114,14 +114,6 @@
///Ionpulse effect. ///Ionpulse effect.
var/datum/effect_system/trail_follow/ion/ion_trail var/datum/effect_system/trail_follow/ion/ion_trail
var/alarms = list(
"Motion" = list(),
"Fire" = list(),
"Atmosphere" = list(),
"Power" = list(),
"Camera" = list(),
"Burglar" = list())
// ------------------------------------------ Misc // ------------------------------------------ Misc
var/toner = 0 var/toner = 0
var/tonermax = 40 var/tonermax = 40
@@ -139,6 +131,8 @@
/// the last health before updating - to check net change in health /// the last health before updating - to check net change in health
var/previous_health var/previous_health
///Alarm listener datum, handes caring about alarm events and such
var/datum/alarm_listener/listener
/*************************************************************************************** /***************************************************************************************

View File

@@ -25,8 +25,8 @@
var/obj/item/radio/borg/radio = null ///If this is a path, this gets created as an object in Initialize. var/obj/item/radio/borg/radio = null ///If this is a path, this gets created as an object in Initialize.
var/list/alarm_types_show = list("Motion" = 0, "Fire" = 0, "Atmosphere" = 0, "Power" = 0, "Camera" = 0) var/list/alarm_types_show = list(ALARM_ATMOS = 0, ALARM_FIRE = 0, ALARM_POWER = 0, ALARM_CAMERA = 0, ALARM_MOTION = 0)
var/list/alarm_types_clear = list("Motion" = 0, "Fire" = 0, "Atmosphere" = 0, "Power" = 0, "Camera" = 0) var/list/alarm_types_clear = list(ALARM_ATMOS = 0, ALARM_FIRE = 0, ALARM_POWER = 0, ALARM_CAMERA = 0, ALARM_MOTION = 0)
var/lawcheck[1] var/lawcheck[1]
var/ioncheck[1] var/ioncheck[1]
@@ -79,16 +79,7 @@
/mob/living/silicon/contents_explosion(severity, target) /mob/living/silicon/contents_explosion(severity, target)
return return
/mob/living/silicon/proc/cancelAlarm() /mob/living/silicon/proc/queueAlarm(message, type, incoming = FALSE)
return
/mob/living/silicon/proc/freeCamera()
return
/mob/living/silicon/proc/triggerAlarm()
return
/mob/living/silicon/proc/queueAlarm(message, type, incoming = 1)
var/in_cooldown = (alarms_to_show.len > 0 || alarms_to_clear.len > 0) var/in_cooldown = (alarms_to_show.len > 0 || alarms_to_clear.len > 0)
if(incoming) if(incoming)
alarms_to_show += message alarms_to_show += message
@@ -109,24 +100,8 @@
else if(alarms_to_show.len) else if(alarms_to_show.len)
var/msg = "--- " var/msg = "--- "
for(var/alarm_type in alarm_types_show)
if(alarm_types_show["Burglar"]) msg += "[uppertext(alarm_type)]: [alarm_types_show[alarm_type]] alarms detected. - "
msg += "BURGLAR: [alarm_types_show["Burglar"]] alarms detected. - "
if(alarm_types_show["Motion"])
msg += "MOTION: [alarm_types_show["Motion"]] alarms detected. - "
if(alarm_types_show["Fire"])
msg += "FIRE: [alarm_types_show["Fire"]] alarms detected. - "
if(alarm_types_show["Atmosphere"])
msg += "ATMOSPHERE: [alarm_types_show["Atmosphere"]] alarms detected. - "
if(alarm_types_show["Power"])
msg += "POWER: [alarm_types_show["Power"]] alarms detected. - "
if(alarm_types_show["Camera"])
msg += "CAMERA: [alarm_types_show["Camera"]] alarms detected. - "
msg += "<A href=?src=[REF(src)];showalerts=1'>\[Show Alerts\]</a>" msg += "<A href=?src=[REF(src)];showalerts=1'>\[Show Alerts\]</a>"
to_chat(src, msg) to_chat(src, msg)
@@ -138,20 +113,8 @@
else if(alarms_to_clear.len) else if(alarms_to_clear.len)
var/msg = "--- " var/msg = "--- "
if(alarm_types_clear["Motion"]) for(var/alarm_type in alarm_types_clear)
msg += "MOTION: [alarm_types_clear["Motion"]] alarms cleared. - " msg += "[uppertext(alarm_type)]: [alarm_types_clear[alarm_type]] alarms cleared. - "
if(alarm_types_clear["Fire"])
msg += "FIRE: [alarm_types_clear["Fire"]] alarms cleared. - "
if(alarm_types_clear["Atmosphere"])
msg += "ATMOSPHERE: [alarm_types_clear["Atmosphere"]] alarms cleared. - "
if(alarm_types_clear["Power"])
msg += "POWER: [alarm_types_clear["Power"]] alarms cleared. - "
if(alarm_types_show["Camera"])
msg += "CAMERA: [alarm_types_clear["Camera"]] alarms cleared. - "
msg += "<A href=?src=[REF(src)];showalerts=1'>\[Show Alerts\]</a>" msg += "<A href=?src=[REF(src)];showalerts=1'>\[Show Alerts\]</a>"
to_chat(src, msg) to_chat(src, msg)

View File

@@ -63,8 +63,8 @@
"3. Your goals are to actively build, maintain, repair, improve, and provide power to the best of your abilities within the facility that housed your activation." //for derelict drones so they don't go to station. "3. Your goals are to actively build, maintain, repair, improve, and provide power to the best of your abilities within the facility that housed your activation." //for derelict drones so they don't go to station.
/// Amount of damage sustained if hit by a heavy EMP pulse /// Amount of damage sustained if hit by a heavy EMP pulse
var/heavy_emp_damage = 25 var/heavy_emp_damage = 25
/// List of active alarms. See [/mob/living/simple_animal/drone/proc/triggerAlarm] and [/mob/living/simple_animal/drone/proc/cancelAlarm] ///Alarm listener datum, handes caring about alarm events and such
var/alarms = list("Atmosphere" = list(), "Fire" = list(), "Power" = list()) var/datum/alarm_listener/listener
/// Internal storage slot. Fits any item /// Internal storage slot. Fits any item
var/obj/item/internal_storage var/obj/item/internal_storage
/// Headwear slot /// Headwear slot
@@ -181,6 +181,12 @@
ADD_TRAIT(src, TRAIT_VENTCRAWLER_ALWAYS, INNATE_TRAIT) ADD_TRAIT(src, TRAIT_VENTCRAWLER_ALWAYS, INNATE_TRAIT)
listener = new(list(ALARM_ATMOS, ALARM_FIRE, ALARM_POWER), list(z))
RegisterSignal(listener, COMSIG_ALARM_TRIGGERED, .proc/alarm_triggered)
RegisterSignal(listener, COMSIG_ALARM_CLEARED, .proc/alarm_cleared)
listener.RegisterSignal(src, COMSIG_LIVING_DEATH, /datum/alarm_listener/proc/prevent_alarm_changes)
listener.RegisterSignal(src, COMSIG_LIVING_REVIVE, /datum/alarm_listener/proc/allow_alarm_changes)
/mob/living/simple_animal/drone/med_hud_set_health() /mob/living/simple_animal/drone/med_hud_set_health()
var/image/holder = hud_list[DIAG_HUD] var/image/holder = hud_list[DIAG_HUD]
var/icon/I = icon(icon, icon_state, dir) var/icon/I = icon(icon, icon_state, dir)
@@ -200,7 +206,8 @@
/mob/living/simple_animal/drone/Destroy() /mob/living/simple_animal/drone/Destroy()
GLOB.drones_list -= src GLOB.drones_list -= src
qdel(access_card) //Otherwise it ends up on the floor! QDEL_NULL(access_card) //Otherwise it ends up on the floor!
QDEL_NULL(listener)
return ..() return ..()
/mob/living/simple_animal/drone/Login() /mob/living/simple_animal/drone/Login()
@@ -289,60 +296,13 @@
adjustBruteLoss(heavy_emp_damage) adjustBruteLoss(heavy_emp_damage)
to_chat(src, span_userdanger("HeAV% DA%^MMA+G TO I/O CIR!%UUT!")) to_chat(src, span_userdanger("HeAV% DA%^MMA+G TO I/O CIR!%UUT!"))
/mob/living/simple_animal/drone/proc/alarm_triggered(datum/source, alarm_type, area/source_area)
SIGNAL_HANDLER
to_chat(src, "--- [alarm_type] alarm detected in [source_area.name]!")
/** /mob/living/simple_animal/drone/proc/alarm_cleared(datum/source, alarm_type, area/source_area)
* Alerts drones about different priorities of alarms SIGNAL_HANDLER
* to_chat(src, "--- [alarm_type] alarm in [source_area.name] has been cleared.")
* Arguments:
* * class - One of the keys listed in [/mob/living/simple_animal/drone/var/alarms]
* * A - [/area] the alarm occurs
* * O - unused argument, see [/mob/living/silicon/robot/triggerAlarm]
* * alarmsource - [/atom] source of the alarm
*/
/mob/living/simple_animal/drone/proc/triggerAlarm(class, area/home, cameras, obj/source)
if(source.z != z)
return
if(stat == DEAD)
return
var/list/our_sort = alarms[class]
for(var/areaname in our_sort)
if (areaname == home.name)
var/list/alarm = our_sort[areaname]
var/list/sources = alarm[2]
if (!(source in sources))
sources += source
return TRUE
our_sort[home.name] = list(home, list(source))
to_chat(src, "--- [class] alarm detected in [home.name]!")
///This isn't currently needed since drones do jack shit with cameras. I hate this code so much
/mob/living/simple_animal/drone/proc/freeCamera(area/home, obj/machinery/camera/cam)
return
/**
* Clears alarm and alerts drones
*
* Arguments:
* * class - One of the keys listed in [/mob/living/simple_animal/drone/var/alarms]
* * A - [/area] the alarm occurs
* * alarmsource - [/atom] source of the alarm
*/
/mob/living/simple_animal/drone/proc/cancelAlarm(class, area/A, obj/origin)
if(stat != DEAD)
var/list/L = alarms[class]
var/cleared = 0
for (var/I in L)
if (I == A.name)
var/list/alarm = L[I]
var/list/srcs = alarm[2]
if (origin in srcs)
srcs -= origin
if (srcs.len == 0)
cleared = 1
L -= I
if(cleared)
to_chat(src, "--- [class] alarm in [A.name] has been cleared.")
/mob/living/simple_animal/drone/proc/blacklist_on_try_use_machine(datum/source, obj/machinery/machine) /mob/living/simple_animal/drone/proc/blacklist_on_try_use_machine(datum/source, obj/machinery/machine)
SIGNAL_HANDLER SIGNAL_HANDLER

View File

@@ -10,7 +10,20 @@
tgui_id = "NtosStationAlertConsole" tgui_id = "NtosStationAlertConsole"
program_icon = "bell" program_icon = "bell"
var/has_alert = 0 var/has_alert = 0
var/alarms = list("Fire" = list(), "Atmosphere" = list(), "Power" = list()) ///Listens for alarms, manages our listing of alarms
var/datum/alarm_listener/listener
/datum/computer_file/program/alarm_monitor/New()
//We want to send an alarm if we're in one of the mining home areas
//Or if we're on station. Otherwise, die.
var/list/allowed_areas = GLOB.the_station_areas + typesof(/area/mine)
listener = new(list(ALARM_ATMOS, ALARM_FIRE, ALARM_POWER), null, allowed_areas)
RegisterSignal(listener, list(COMSIG_ALARM_TRIGGERED, COMSIG_ALARM_CLEARED), .proc/update_alarm_display)
return ..()
/datum/computer_file/program/alarm_monitor/Destroy()
QDEL_NULL(listener)
return ..()
/datum/computer_file/program/alarm_monitor/process_tick() /datum/computer_file/program/alarm_monitor/process_tick()
..() ..()
@@ -30,86 +43,18 @@
var/list/data = get_header_data() var/list/data = get_header_data()
data["alarms"] = list() data["alarms"] = list()
for(var/class in alarms) var/list/alarms = listener.alarms
data["alarms"][class] = list() for(var/alarm_type in alarms)
for(var/area in alarms[class]) data["alarms"][alarm_type] = list()
data["alarms"][class] += area for(var/area in alarms[alarm_type])
data["alarms"][alarm_type] += area
return data return data
/datum/computer_file/program/alarm_monitor/proc/triggerAlarm(class, area/home, cameras, obj/source)
if(is_station_level(source.z))
if(!(home.type in GLOB.the_station_areas))
return
else if(!is_mining_level(source.z) || istype(home, /area/ruin))
return
var/list/our_sort = alarms[class]
for(var/areaname in our_sort)
if (areaname == home.name)
var/list/alarm = our_sort[areaname]
var/list/sources = alarm[3]
if (!(source in sources))
sources += source
return TRUE
var/obj/machinery/camera/cam = null
var/list/our_cams = null
if(cameras && islist(cameras))
our_cams = cameras
if (our_cams.len == 1)
cam = our_cams[1]
else if(cameras && istype(cameras, /obj/machinery/camera))
cam = cameras
our_sort[home.name] = list(home, (cam ? cam : cameras), list(source))
update_alarm_display()
return TRUE
/datum/computer_file/program/alarm_monitor/proc/freeCamera(area/home, obj/machinery/camera/cam)
for(var/class in alarms)
var/our_area = alarms[class][home.name]
if(!our_area)
continue
var/cams = our_area[2] //Get the cameras
if(!cams)
continue
if(islist(cams))
cams -= cam
if(length(cams) == 1)
our_area[2] = cams[1]
else
our_area[2] = null
/datum/computer_file/program/alarm_monitor/proc/cancelAlarm(class, area/A, obj/origin)
var/list/L = alarms[class]
var/cleared = 0
var/arealevelalarm = FALSE // set to TRUE for alarms that set/clear whole areas
if (class=="Fire")
arealevelalarm = TRUE
for (var/I in L)
if (I == A.name)
if (!arealevelalarm) // the traditional behaviour
var/list/alarm = L[I]
var/list/srcs = alarm[3]
if (origin in srcs)
srcs -= origin
if (srcs.len == 0)
cleared = 1
L -= I
else
L -= I // wipe the instances entirely
cleared = 1
update_alarm_display()
return !cleared
/datum/computer_file/program/alarm_monitor/proc/update_alarm_display() /datum/computer_file/program/alarm_monitor/proc/update_alarm_display()
SIGNAL_HANDLER
has_alert = FALSE has_alert = FALSE
for(var/cat in alarms) if(length(listener.alarms))
var/list/L = alarms[cat]
if(L.len)
has_alert = TRUE has_alert = TRUE
/datum/computer_file/program/alarm_monitor/run_program(mob/user) /datum/computer_file/program/alarm_monitor/run_program(mob/user)
@@ -118,4 +63,4 @@
/datum/computer_file/program/alarm_monitor/kill_program(forced = FALSE) /datum/computer_file/program/alarm_monitor/kill_program(forced = FALSE)
GLOB.alarmdisplay -= src GLOB.alarmdisplay -= src
..() return ..()

View File

@@ -169,6 +169,8 @@
var/update_overlay = -1 var/update_overlay = -1
var/icon_update_needed = FALSE var/icon_update_needed = FALSE
var/obj/machinery/computer/apc_control/remote_control = null var/obj/machinery/computer/apc_control/remote_control = null
///Represents a signel source of power alarms for this apc
var/datum/alarm_handler/alarm_manager
var/shock_proof = FALSE // SKYRAT EDIT ADD - APC Arcing. If TRUE, APCs will not arc. var/shock_proof = FALSE // SKYRAT EDIT ADD - APC Arcing. If TRUE, APCs will not arc.
@@ -269,7 +271,7 @@
area.power_equip = FALSE area.power_equip = FALSE
area.power_environ = FALSE area.power_environ = FALSE
area.power_change() area.power_change()
area.poweralert(FALSE, src) QDEL_NULL(alarm_manager)
if(occupier) if(occupier)
malfvacate(1) malfvacate(1)
qdel(wires) qdel(wires)
@@ -296,6 +298,7 @@
/obj/machinery/power/apc/Initialize(mapload) /obj/machinery/power/apc/Initialize(mapload)
. = ..() . = ..()
AddElement(/datum/element/atmos_sensitive, mapload) AddElement(/datum/element/atmos_sensitive, mapload)
alarm_manager = new(src)
if(!mapload) if(!mapload)
return return
@@ -1335,24 +1338,24 @@
equipment = autoset(equipment, AUTOSET_FORCE_OFF) equipment = autoset(equipment, AUTOSET_FORCE_OFF)
lighting = autoset(lighting, AUTOSET_FORCE_OFF) lighting = autoset(lighting, AUTOSET_FORCE_OFF)
environ = autoset(environ, AUTOSET_FORCE_OFF) environ = autoset(environ, AUTOSET_FORCE_OFF)
area.poweralert(TRUE, src) alarm_manager.send_alarm(ALARM_POWER)
else if(cell.percent() < 15 && longtermpower < 0) // <15%, turn off lighting & equipment else if(cell.percent() < 15 && longtermpower < 0) // <15%, turn off lighting & equipment
equipment = autoset(equipment, AUTOSET_OFF) equipment = autoset(equipment, AUTOSET_OFF)
lighting = autoset(lighting, AUTOSET_OFF) lighting = autoset(lighting, AUTOSET_OFF)
environ = autoset(environ, AUTOSET_ON) environ = autoset(environ, AUTOSET_ON)
area.poweralert(TRUE, src) alarm_manager.send_alarm(ALARM_POWER)
else if(cell.percent() < 30 && longtermpower < 0) // <30%, turn off equipment else if(cell.percent() < 30 && longtermpower < 0) // <30%, turn off equipment
equipment = autoset(equipment, AUTOSET_OFF) equipment = autoset(equipment, AUTOSET_OFF)
lighting = autoset(lighting, AUTOSET_ON) lighting = autoset(lighting, AUTOSET_ON)
environ = autoset(environ, AUTOSET_ON) environ = autoset(environ, AUTOSET_ON)
area.poweralert(TRUE, src) alarm_manager.send_alarm(ALARM_POWER)
else // otherwise all can be on else // otherwise all can be on
equipment = autoset(equipment, AUTOSET_ON) equipment = autoset(equipment, AUTOSET_ON)
lighting = autoset(lighting, AUTOSET_ON) lighting = autoset(lighting, AUTOSET_ON)
environ = autoset(environ, AUTOSET_ON) environ = autoset(environ, AUTOSET_ON)
area.poweralert(FALSE, src)
if(cell.percent() > 75) if(cell.percent() > 75)
area.poweralert(FALSE, src) alarm_manager.clear_alarm(ALARM_POWER)
// now trickle-charge the cell // now trickle-charge the cell
if(chargemode && charging == APC_CHARGING && operating) if(chargemode && charging == APC_CHARGING && operating)
@@ -1411,7 +1414,7 @@
equipment = autoset(equipment, AUTOSET_FORCE_OFF) equipment = autoset(equipment, AUTOSET_FORCE_OFF)
lighting = autoset(lighting, AUTOSET_FORCE_OFF) lighting = autoset(lighting, AUTOSET_FORCE_OFF)
environ = autoset(environ, AUTOSET_FORCE_OFF) environ = autoset(environ, AUTOSET_FORCE_OFF)
area.poweralert(TRUE, src) alarm_manager.send_alarm(ALARM_POWER)
// update icon & area power if anything changed // update icon & area power if anything changed

View File

@@ -29,6 +29,7 @@
#include "code\__DEFINES\admin.dm" #include "code\__DEFINES\admin.dm"
#include "code\__DEFINES\adventure.dm" #include "code\__DEFINES\adventure.dm"
#include "code\__DEFINES\ai.dm" #include "code\__DEFINES\ai.dm"
#include "code\__DEFINES\alarm.dm"
#include "code\__DEFINES\antagonists.dm" #include "code\__DEFINES\antagonists.dm"
#include "code\__DEFINES\aquarium.dm" #include "code\__DEFINES\aquarium.dm"
#include "code\__DEFINES\art.dm" #include "code\__DEFINES\art.dm"
@@ -433,6 +434,7 @@
#include "code\controllers\subsystem\processing\wet_floors.dm" #include "code\controllers\subsystem\processing\wet_floors.dm"
#include "code\datums\action.dm" #include "code\datums\action.dm"
#include "code\datums\ai_laws.dm" #include "code\datums\ai_laws.dm"
#include "code\datums\alarm.dm"
#include "code\datums\armor.dm" #include "code\datums\armor.dm"
#include "code\datums\beam.dm" #include "code\datums\beam.dm"
#include "code\datums\browser.dm" #include "code\datums\browser.dm"