Alarm system overhaul

This commit is contained in:
Markolie
2015-08-02 07:13:03 +02:00
parent 4028c2af12
commit 73e94b5dc4
66 changed files with 1452 additions and 1887 deletions
+138
View File
@@ -0,0 +1,138 @@
#define ALARM_RESET_DELAY 100 // How long will the alarm/trigger remain active once origin/source has been found to be gone?
/datum/alarm_source
var/source = null // The source trigger
var/source_name = "" // The name of the source should it be lost (for example a destroyed camera)
var/duration = 0 // How long this source will be alarming, 0 for indefinetely.
var/severity = 1 // How severe the alarm from this source is.
var/start_time = 0 // When this source began alarming.
var/end_time = 0 // Use to set when this trigger should clear, in case the source is lost.
/datum/alarm_source/New(var/atom/source)
src.source = source
start_time = world.time
source_name = source.get_source_name()
/datum/alarm
var/atom/origin //Used to identify the alarm area.
var/list/sources = new() //List of sources triggering the alarm. Used to determine when the alarm should be cleared.
var/list/sources_assoc = new() //Associative list of source triggers. Used to efficiently acquire the alarm source.
var/list/cameras //List of cameras that can be switched to, if the player has that capability.
var/area/last_area //The last acquired area, used should origin be lost (for example a destroyed borg containing an alarming camera).
var/area/last_name //The last acquired name, used should origin be lost
var/area/last_camera_area //The last area in which cameras where fetched, used to see if the camera list should be updated.
var/end_time //Used to set when this alarm should clear, in case the origin is lost.
/datum/alarm/New(var/atom/origin, var/atom/source, var/duration, var/severity)
src.origin = origin
cameras() // Sets up both cameras and last alarm area.
set_source_data(source, duration, severity)
/datum/alarm/proc/process()
// Has origin gone missing?
if(!origin && !end_time)
end_time = world.time + ALARM_RESET_DELAY
for(var/datum/alarm_source/AS in sources)
// Has the alarm passed its best before date?
if((AS.end_time && world.time > AS.end_time) || (AS.duration && world.time > (AS.start_time + AS.duration)))
sources -= AS
// Has the source gone missing? Then reset the normal duration and set end_time
if(!AS.source && !AS.end_time) // end_time is used instead of duration to ensure the reset doesn't remain in the future indefinetely.
AS.duration = 0
AS.end_time = world.time + ALARM_RESET_DELAY
/datum/alarm/proc/set_source_data(var/atom/source, var/duration, var/severity)
var/datum/alarm_source/AS = sources_assoc[source]
if(!AS)
AS = new/datum/alarm_source(source)
sources += AS
sources_assoc[source] = AS
// Currently only non-0 durations can be altered (normal alarms VS EMP blasts)
if(AS.duration)
duration = SecondsToTicks(duration)
AS.duration = duration
AS.severity = severity
/datum/alarm/proc/clear(var/source)
var/datum/alarm_source/AS = sources_assoc[source]
sources -= AS
sources_assoc -= source
/datum/alarm/proc/alarm_area()
if(!origin)
return last_area
last_area = origin.get_alarm_area()
return last_area
/datum/alarm/proc/alarm_name()
if(!origin)
return last_name
last_name = origin.get_alarm_name()
return last_name
/datum/alarm/proc/cameras()
// If the alarm origin has changed area, for example a borg containing an alarming camera, reset the list of cameras
if(cameras && (last_camera_area != alarm_area()))
cameras = null
// The list of cameras is also reset by /proc/invalidateCameraCache()
if(!cameras)
cameras = origin ? origin.get_alarm_cameras() : last_area.get_alarm_cameras()
last_camera_area = last_area
return cameras
/datum/alarm/proc/max_severity()
var/max_severity = 0
for(var/datum/alarm_source/AS in sources)
max_severity = max(AS.severity, max_severity)
return max_severity
/******************
* Assisting procs *
******************/
/atom/proc/get_alarm_area()
var/area/A = get_area(src)
return A
/area/get_alarm_area()
return src
/atom/proc/get_alarm_name()
var/area/A = get_area(src)
return A.name
/area/get_alarm_name()
return name
/mob/get_alarm_name()
return name
/atom/proc/get_source_name()
return name
/obj/machinery/camera/get_source_name()
return c_tag
/atom/proc/get_alarm_cameras()
var/area/A = get_area(src)
return A.get_cameras()
/area/get_alarm_cameras()
return get_cameras()
/mob/living/silicon/robot/get_alarm_cameras()
var/list/cameras = ..()
if(camera)
cameras += camera
return cameras
/mob/living/silicon/robot/syndicate/get_alarm_cameras()
return list()
#undef ALARM_LOSS_DELAY
+99
View File
@@ -0,0 +1,99 @@
#define ALARM_RAISED 1
#define ALARM_CLEARED 0
/datum/alarm_handler
var/category = ""
var/list/datum/alarm/alarms = new // All alarms, to handle cases when an origin has been deleted with one or more active alarms
var/list/datum/alarm/alarms_assoc = new // Associative list of alarms, to efficiently acquire them based on origin.
var/list/listeners = new // A list of all objects interested in alarm changes.
/datum/alarm_handler/proc/process()
for(var/datum/alarm/A in alarms)
A.process()
check_alarm_cleared(A)
/datum/alarm_handler/proc/triggerAlarm(var/atom/origin, var/atom/source, var/duration = 0, var/severity = 1)
var/new_alarm
//Proper origin and source mandatory
if(!(origin && source))
return
origin = origin.get_alarm_origin()
new_alarm = 0
//see if there is already an alarm of this origin
var/datum/alarm/existing = alarms_assoc[origin]
if(existing)
existing.set_source_data(source, duration, severity)
else
existing = new/datum/alarm(origin, source, duration, severity)
new_alarm = 1
alarms |= existing
alarms_assoc[origin] = existing
if(new_alarm)
alarms = dd_sortedObjectList(alarms)
on_alarm_change(existing, ALARM_RAISED)
return new_alarm
/datum/alarm_handler/proc/clearAlarm(var/atom/origin, var/source)
//Proper origin and source mandatory
if(!(origin && source))
return
origin = origin.get_alarm_origin()
var/datum/alarm/existing = alarms_assoc[origin]
if(existing)
existing.clear(source)
return check_alarm_cleared(existing)
/datum/alarm_handler/proc/major_alarms()
return alarms
/datum/alarm_handler/proc/minor_alarms()
return alarms
/datum/alarm_handler/proc/check_alarm_cleared(var/datum/alarm/alarm)
if ((alarm.end_time && world.time > alarm.end_time) || !alarm.sources.len)
alarms -= alarm
alarms_assoc -= alarm.origin
on_alarm_change(alarm, ALARM_CLEARED)
return 1
return 0
/datum/alarm_handler/proc/on_alarm_change(var/datum/alarm/alarm, var/was_raised)
for(var/obj/machinery/camera/C in alarm.cameras())
if(was_raised)
C.network.Add(category)
invalidateCameraCache()
else
C.network.Remove(category)
notify_listeners(alarm, was_raised)
/datum/alarm_handler/proc/get_alarm_severity_for_origin(var/atom/origin)
if(!origin)
return
origin = origin.get_alarm_origin()
var/datum/alarm/existing = alarms_assoc[origin]
if(!existing)
return
return existing.max_severity()
/atom/proc/get_alarm_origin()
return src
/turf/get_alarm_origin()
var/area/area = get_area(src)
return area // Very important to get area.master, as dynamic lightning can and will split areas.
/datum/alarm_handler/proc/register(var/object, var/procName)
listeners[object] = procName
/datum/alarm_handler/proc/unregister(var/object)
listeners -= object
/datum/alarm_handler/proc/notify_listeners(var/alarm, var/was_raised)
for(var/listener in listeners)
call(listener, listeners[listener])(src, alarm, was_raised)
+19
View File
@@ -0,0 +1,19 @@
/datum/alarm_handler/atmosphere
category = "Atmosphere Alarms"
/datum/alarm_handler/atmosphere/triggerAlarm(var/atom/origin, var/atom/source, var/duration = 0, var/severity = 1)
..()
/datum/alarm_handler/atmosphere/major_alarms()
var/list/major_alarms = new()
for(var/datum/alarm/A in alarms)
if(A.max_severity() > 1)
major_alarms.Add(A)
return major_alarms
/datum/alarm_handler/atmosphere/minor_alarms()
var/list/minor_alarms = new()
for(var/datum/alarm/A in alarms)
if(A.max_severity() == 1)
minor_alarms.Add(A)
return minor_alarms
+2
View File
@@ -0,0 +1,2 @@
/datum/alarm_handler/camera
category = "Camera Alarms"
+11
View File
@@ -0,0 +1,11 @@
/datum/alarm_handler/fire
category = "Fire Alarms"
/datum/alarm_handler/fire/on_alarm_change(var/datum/alarm/alarm, var/was_raised)
var/area/A = alarm.origin
if(istype(A))
if(was_raised)
A.fire_alert()
else
A.fire_reset()
..()
+2
View File
@@ -0,0 +1,2 @@
/datum/alarm_handler/motion
category = "Motion Alarms"
+10
View File
@@ -0,0 +1,10 @@
/datum/alarm_handler/power
category = "Power Alarms"
/datum/alarm_handler/power/on_alarm_change(var/datum/alarm/alarm, var/was_raised)
var/area/A = alarm.origin
if(istype(A))
A.power_alert(was_raised)
..()
/area/proc/power_alert(var/alarming)
+1 -1
View File
@@ -182,7 +182,7 @@
if(!E.event_meta)
continue
var/datum/event_meta/EM = E.event_meta
var/ends_at = E.startedAt + (E.lastProcessAt() * master_controller.minimum_ticks) // A best estimate
var/ends_at = E.startedAt + (E.lastProcessAt() * 20) // A best estimate, based on how often the manager processes
var/ends_in = max(0, round((ends_at - world.time) / 600, 0.1))
var/no_end = E.noAutoEnd
html += "<tr>"
+4 -97
View File
@@ -3,7 +3,6 @@
var/list/ai_list = list()
var/list/ai_verbs_default = list(
/mob/living/silicon/ai/proc/ai_alerts,
/mob/living/silicon/ai/proc/announcement,
/mob/living/silicon/ai/proc/ai_call_shuttle,
/mob/living/silicon/ai/proc/ai_camera_track,
@@ -25,7 +24,6 @@ var/list/ai_verbs_default = list(
/mob/living/silicon/ai/proc/toggle_camera_light,
/mob/living/silicon/ai/proc/botcall,
/mob/living/silicon/ai/proc/change_arrival_message,
/mob/living/silicon/ai/proc/nano_crew_monitor
)
//Not sure why this is necessary...
@@ -95,9 +93,11 @@ var/list/ai_verbs_default = list(
/mob/living/silicon/ai/proc/add_ai_verbs()
src.verbs |= ai_verbs_default
src.verbs |= silicon_subsystems
/mob/living/silicon/ai/proc/remove_ai_verbs()
src.verbs -= ai_verbs_default
src.verbs -= silicon_subsystems
/mob/living/silicon/ai/New(loc, var/datum/ai_laws/L, var/obj/item/device/mmi/B, var/safety = 0)
var/list/possibleNames = ai_names
@@ -181,8 +181,6 @@ var/list/ai_verbs_default = list(
hud_list[SPECIALROLE_HUD] = image('icons/mob/hud.dmi', src, "hudblank")
hud_list[NATIONS_HUD] = image('icons/mob/hud.dmi', src, "hudblank")
init_subsystems()
ai_list += src
..()
return
@@ -321,43 +319,6 @@ var/list/ai_verbs_default = list(
if (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 name = "Show Alerts"
set category = "AI Commands"
var/dat = "<HEAD><TITLE>Current Station Alerts</TITLE><META HTTP-EQUIV='Refresh' CONTENT='10'></HEAD><BODY>\n"
dat += "<A HREF='?src=\ref[src];mach_close=aialerts'>Close</A><BR><BR>"
for (var/cat in alarms)
dat += text("<B>[]</B><BR>\n", cat)
var/list/L = alarms[cat]
if (L.len)
for (var/alarm in L)
var/list/alm = L[alarm]
var/area/A = alm[1]
var/C = alm[2]
var/list/sources = alm[3]
dat += "<NOBR>"
if (C && istype(C, /list))
var/dat2 = ""
for (var/obj/machinery/camera/I in C)
dat2 += text("[]<A HREF=?src=\ref[];switchcamera=\ref[]>[]</A>", (dat2=="") ? "" : " | ", src, I, I.c_tag)
dat += text("-- [] ([])", A.name, (dat2!="") ? dat2 : "No Camera")
else if (C && istype(C, /obj/machinery/camera))
var/obj/machinery/camera/Ctmp = C
dat += text("-- [] (<A HREF=?src=\ref[];switchcamera=\ref[]>[]</A>)", A.name, src, C, Ctmp.c_tag)
else
dat += text("-- [] (No Camera)", A.name)
if (sources.len > 1)
dat += text("- [] sources", sources.len)
dat += "</NOBR><BR>\n"
else
dat += "-- All Systems Nominal<BR>\n"
dat += "<BR>\n"
viewalerts = 1
src << browse(dat, "window=aialerts&can_close=0")
// this verb lets the ai see the stations manifest
/mob/living/silicon/ai/proc/ai_roster()
set name = "Show Crew Manifest"
@@ -495,7 +456,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()
if(href_list["show_paper"])
if(last_paper_seen)
src << browse(last_paper_seen, "window=show_paper")
@@ -721,60 +682,6 @@ var/list/ai_verbs_default = list(
return 1
/mob/living/silicon/ai/triggerAlarm(var/class, area/A, var/O, var/alarmsource)
if (stat == 2)
return 1
var/list/L = 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))
if (O)
if (C && C.can_use())
queueAlarm("--- [class] alarm detected in [A.name]! (<A HREF=?src=\ref[src];switchcamera=\ref[C]>[C.c_tag]</A>)", class)
else if (CL && CL.len)
var/foo = 0
var/dat2 = ""
for (var/obj/machinery/camera/I in CL)
dat2 += text("[]<A HREF=?src=\ref[];switchcamera=\ref[]>[]</A>", (!foo) ? "" : " | ", src, I, I.c_tag) //I'm not fixing this shit...
foo = 1
queueAlarm(text ("--- [] alarm detected in []! ([])", class, A.name, dat2), class)
else
queueAlarm(text("--- [] alarm detected in []! (No Camera)", class, A.name), class)
else
queueAlarm(text("--- [] alarm detected in []! (No Camera)", class, A.name), class)
if (viewalerts) ai_alerts()
return 1
/mob/living/silicon/ai/cancelAlarm(var/class, area/A as area, 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(text("--- [] alarm in [] has been cleared.", class, A.name), class, 0)
if (viewalerts) ai_alerts()
return !cleared
//Replaces /mob/living/silicon/ai/verb/change_network() in ai.dm & camera.dm
//Adds in /mob/living/silicon/ai/proc/ai_network_change() instead
//Addition by Mord_Sith to define AI's network change ability
+11 -1
View File
@@ -173,6 +173,7 @@
sleep(50)
theAPC = null
process_queued_alarms()
regular_hud_updates()
switch(src.sensor_mode)
if (SEC_HUD)
@@ -188,4 +189,13 @@
if(fire_res_on_core)
health = 100 - getOxyLoss() - getToxLoss() - getBruteLoss()
else
health = 100 - getOxyLoss() - getToxLoss() - getFireLoss() - getBruteLoss()
health = 100 - getOxyLoss() - getToxLoss() - getFireLoss() - getBruteLoss()
/mob/living/silicon/ai/proc/lacks_power()
var/turf/T = get_turf(src)
var/area/A = get_area(src)
return ((!A.power_equip) && A.requires_power == 1 || istype(T, /turf/space)) && !istype(src.loc,/obj/item)
/mob/living/silicon/ai/rejuvenate()
..()
add_ai_verbs(src)
@@ -1,11 +0,0 @@
/mob/living/silicon/ai
var/datum/nano_module/crew_monitor/crew_monitor
/mob/living/silicon/ai/proc/init_subsystems()
crew_monitor = new(src)
/mob/living/silicon/ai/proc/nano_crew_monitor()
set category = "AI Commands"
set name = "Crew Monitor"
crew_monitor.ui_interact(usr)
@@ -318,3 +318,9 @@
else
src << "<span class='warning'>You are too small to pull that.</span>"
return
/mob/living/silicon/robot/drone/add_robot_verbs()
src.verbs |= silicon_subsystems
/mob/living/silicon/robot/drone/remove_robot_verbs()
src.verbs -= silicon_subsystems
@@ -19,6 +19,7 @@
if (src.stat != DEAD) //still using power
use_power()
process_locks()
process_queued_alarms()
update_canmove()
+18 -83
View File
@@ -300,6 +300,8 @@ var/list/robot_verbs_default = list(
//languages
module.add_languages(src)
//subsystems
module.add_subsystems(src)
//Custom_sprite check and entry
if (custom_sprite == 1)
@@ -374,41 +376,12 @@ var/list/robot_verbs_default = list(
updatename()
update_icons()
/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 Station Manifest"
show_station_manifest()
/mob/living/silicon/robot/proc/robot_alerts()
var/dat = "<HEAD><TITLE>Current Station Alerts</TITLE><META HTTP-EQUIV='Refresh' CONTENT='10'></HEAD><BODY>\n"
dat += "<A HREF='?src=\ref[src];mach_close=robotalerts'>Close</A><BR><BR>"
for (var/cat in alarms)
dat += text("<B>[cat]</B><BR>\n")
var/list/L = alarms[cat]
if (L.len)
for (var/alarm in L)
var/list/alm = L[alarm]
var/area/A = alm[1]
var/list/sources = alm[3]
dat += "<NOBR>" // wat
dat += text("-- [A.name]")
if (sources.len > 1)
dat += text("- [sources.len] sources")
dat += "</NOBR><BR>\n"
else
dat += "-- All Systems Nominal<BR>\n"
dat += "<BR>\n"
viewalerts = 1
src << browse(dat, "window=robotalerts&can_close=0")
/mob/living/silicon/robot/proc/self_diagnosis()
if(!is_component_functioning("diagnosis unit"))
return null
@@ -420,7 +393,6 @@ var/list/robot_verbs_default = list(
return dat
/mob/living/silicon/robot/verb/self_diagnosis_verb()
set category = "Robot Commands"
set name = "Self Diagnosis"
@@ -464,9 +436,11 @@ var/list/robot_verbs_default = list(
/mob/living/silicon/robot/proc/add_robot_verbs()
src.verbs |= robot_verbs_default
src.verbs |= silicon_subsystems
/mob/living/silicon/robot/proc/remove_robot_verbs()
src.verbs -= robot_verbs_default
src.verbs -= silicon_subsystems
/mob/living/silicon/robot/blob_act()
if (stat != 2)
@@ -590,50 +564,6 @@ var/list/robot_verbs_default = list(
return
return
/mob/living/silicon/robot/triggerAlarm(var/class, area/A, var/O, var/alarmsource)
if (stat == 2)
return 1
var/list/L = 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))
queueAlarm(text("--- [class] alarm detected in [A.name]!"), class)
// if (viewalerts) robot_alerts()
return 1
/mob/living/silicon/robot/cancelAlarm(var/class, area/A as area, 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(text("--- [class] alarm in [A.name] has been cleared."), class, 0)
// if (viewalerts) robot_alerts()
return !cleared
/mob/living/silicon/robot/attackby(obj/item/weapon/W as obj, mob/user as mob, params)
if (istype(W, /obj/item/weapon/restraints/handcuffs)) // fuck i don't even know why isrobot() in handcuff code isn't working so this will have to do
return
@@ -1124,32 +1054,32 @@ var/list/robot_verbs_default = list(
/mob/living/silicon/robot/Topic(href, href_list)
..()
if(..())
return 1
if(usr != src)
return
return 1
if (href_list["mach_close"])
var/t1 = text("window=[href_list["mach_close"]]")
unset_machine()
src << browse(null, t1)
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) || !(O.loc == src || O.loc == src.module))
return
return 1
activate_module(O)
installed_modules()
@@ -1171,6 +1101,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"])
@@ -1179,6 +1110,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"])
@@ -1187,10 +1119,12 @@ 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
return 1
return 1
/mob/living/silicon/robot/proc/radio_menu()
radio.interact(src)//Just use the radio's Topic() instead of bullshit special-snowflake code
@@ -1347,6 +1281,7 @@ var/list/robot_verbs_default = list(
var/choice = input("Look at your icon - is this what you want?") in list("Yes","No")
if(choice=="No")
choose_icon(triesleft, module_sprites)
return
else
triesleft = 0
return
@@ -9,32 +9,30 @@
var/list/modules = list()
var/obj/item/emag = null
var/obj/item/borg/upgrade/jetpack = null
var/list/subsystems = list()
var/list/stacktypes
var/channels = list()
emp_act(severity)
if(modules)
for(var/obj/O in modules)
O.emp_act(severity)
if(emag)
emag.emp_act(severity)
..()
return
/obj/item/weapon/robot_module/emp_act(severity)
if(modules)
for(var/obj/O in modules)
O.emp_act(severity)
if(emag)
emag.emp_act(severity)
..()
return
New()
src.modules += new /obj/item/device/flashlight(src)
src.modules += new /obj/item/device/flash/cyborg(src)
src.emag = new /obj/item/toy/sword(src)
src.emag.name = "Placeholder Emag Item"
// src.jetpack = new /obj/item/toy/sword(src)
// src.jetpack.name = "Placeholder Upgrade Item"
return
/obj/item/weapon/robot_module/New()
src.modules += new /obj/item/device/flashlight(src)
src.modules += new /obj/item/device/flash/cyborg(src)
src.emag = new /obj/item/toy/sword(src)
src.emag.name = "Placeholder Emag Item"
return
/obj/item/weapon/robot_module/proc/respawn_consumable(var/mob/living/silicon/robot/R)
if(!stacktypes || !stacktypes.len) return
var/stack_respawned = 0
@@ -77,6 +75,12 @@
R.add_language("Chittin", 0)
R.add_language("Bubblish", 0)
R.add_language("Clownish",0)
/obj/item/weapon/robot_module/proc/add_subsystems(var/mob/living/silicon/robot/R)
R.verbs |= subsystems
/obj/item/weapon/robot_module/proc/remove_subsystems(var/mob/living/silicon/robot/R)
R.verbs -= subsystems
/obj/item/weapon/robot_module/standard
name = "standard robot module"
@@ -94,13 +98,14 @@
/obj/item/weapon/robot_module/medical
name = "medical robot module"
subsystems = list(/mob/living/silicon/proc/subsystem_crew_monitor)
stacktypes = list(
/obj/item/stack/medical/advanced/bruise_pack = 5,
/obj/item/stack/medical/advanced/ointment = 5,
/obj/item/stack/medical/splint = 5,
/obj/item/stack/nanopaste = 5
)
New()
src.modules += new /obj/item/device/flash/cyborg(src)
src.modules += new /obj/item/device/healthanalyzer/advanced(src)
@@ -197,6 +202,7 @@
/obj/item/weapon/robot_module/security
name = "security robot module"
subsystems = list(/mob/living/silicon/proc/subsystem_crew_monitor)
New()
+73 -77
View File
@@ -17,91 +17,29 @@
var/speak_exclamation = "declares"
var/speak_query = "queries"
var/pose //Yes, now AIs can pose too.
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
var/local_transmit //If set, can only speak to others of the same type within a short range.
var/obj/item/device/radio/common_radio
/mob/living/silicon/proc/cancelAlarm()
return
/mob/living/silicon/proc/triggerAlarm()
return
/mob/living/silicon/New()
..()
init_subsystems()
/mob/living/silicon/Destroy()
for(var/datum/alarm_handler/AH in alarm_manager.all_handlers)
AH.unregister(src)
..()
/mob/living/silicon/proc/show_laws()
return
/mob/living/silicon/proc/queueAlarm(var/message, var/type, var/incoming = 1)
var/in_cooldown = (alarms_to_show.len > 0 || alarms_to_clear.len > 0)
if(incoming)
alarms_to_show += message
alarm_types_show[type] += 1
else
alarms_to_clear += message
alarm_types_clear[type] += 1
if(!in_cooldown)
spawn(10 * 10) // 10 seconds
if(alarms_to_show.len < 5)
for(var/msg in alarms_to_show)
src << msg
else if(alarms_to_show.len)
var/msg = "--- "
if(alarm_types_show["Motion"])
msg += "MOTION: [alarm_types_show["Motion"]] alarms detected. - "
if(alarm_types_show["Fire"])
msg += "FIRE: [alarm_types_show["Fire"]] alarms detected. - "
if(alarm_types_show["Atmosphere"])
msg += "ATMOSPHERE: [alarm_types_show["Atmosphere"]] alarms detected. - "
if(alarm_types_show["Power"])
msg += "POWER: [alarm_types_show["Power"]] alarms detected. - "
if(alarm_types_show["Camera"])
msg += "CAMERA: [alarm_types_show["Power"]] alarms detected. - "
msg += "<A href=?src=\ref[src];showalerts=1'>\[Show Alerts\]</a>"
src << msg
if(alarms_to_clear.len < 3)
for(var/msg in alarms_to_clear)
src << msg
else if(alarms_to_clear.len)
var/msg = "--- "
if(alarm_types_clear["Motion"])
msg += "MOTION: [alarm_types_clear["Motion"]] alarms cleared. - "
if(alarm_types_clear["Fire"])
msg += "FIRE: [alarm_types_clear["Fire"]] alarms cleared. - "
if(alarm_types_clear["Atmosphere"])
msg += "ATMOSPHERE: [alarm_types_clear["Atmosphere"]] alarms cleared. - "
if(alarm_types_clear["Power"])
msg += "POWER: [alarm_types_clear["Power"]] alarms cleared. - "
if(alarm_types_show["Camera"])
msg += "CAMERA: [alarm_types_show["Power"]] alarms detected. - "
msg += "<A href=?src=\ref[src];showalerts=1'>\[Show Alerts\]</a>"
src << msg
alarms_to_show = list()
alarms_to_clear = list()
for(var/i = 1; i < alarm_types_show.len; i++)
alarm_types_show[i] = 0
for(var/i = 1; i < alarm_types_clear.len; i++)
alarm_types_clear[i] = 0
/mob/living/silicon/drop_item()
return
@@ -323,4 +261,62 @@
src << "Sensor augmentations disabled."
/mob/living/silicon/IsAdvancedToolUser()
return 1
return 1
/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
var/alarm_raised = 0
for(var/datum/alarm_handler/AH in queued_alarms)
var/list/alarms = queued_alarms[AH]
var/reported = 0
for(var/datum/alarm/A in alarms)
if(alarms[A] == 1)
if(!reported)
reported = 1
src << "<span class='warning'>--- [AH.category] Detected ---</span>"
raised_alarm(A)
for(var/datum/alarm_handler/AH in queued_alarms)
var/list/alarms = queued_alarms[AH]
var/reported = 0
for(var/datum/alarm/A in alarms)
if(alarms[A] == -1)
if(!reported)
reported = 1
src << "<span class='notice'>--- [AH.category] Cleared ---</span>"
src << "\The [A.alarm_name()]."
if(alarm_raised)
src << "<A HREF=?src=\ref[src];showalerts=1>\[Show Alerts\]</A>"
for(var/datum/alarm_handler/AH in queued_alarms)
var/list/alarms = queued_alarms[AH]
alarms.Cut()
/mob/living/silicon/proc/raised_alarm(var/datum/alarm/A)
src << "[A.alarm_name()]!"
/mob/living/silicon/ai/raised_alarm(var/datum/alarm/A)
var/cameratext = ""
for(var/obj/machinery/camera/C in A.cameras())
cameratext += "[(cameratext == "")? "" : "|"]<A HREF=?src=\ref[src];switchcamera=\ref[C]>[C.c_tag]</A>"
src << "[A.alarm_name()]! ([(cameratext)? cameratext : "No Camera"])"
@@ -0,0 +1,60 @@
/mob/living/silicon
var/register_alarms = 1
var/datum/nano_module/alarm_monitor/all/alarm_monitor
var/datum/nano_module/atmos_control/atmos_control
var/datum/nano_module/crew_monitor/crew_monitor
/mob/living/silicon
var/list/silicon_subsystems = list(
/mob/living/silicon/proc/subsystem_alarm_monitor
)
/mob/living/silicon/ai
silicon_subsystems = list(
/mob/living/silicon/proc/subsystem_alarm_monitor,
/mob/living/silicon/proc/subsystem_atmos_control,
/mob/living/silicon/proc/subsystem_crew_monitor
)
/mob/living/silicon/robot/syndicate
register_alarms = 0
/mob/living/silicon/proc/init_subsystems()
alarm_monitor = new(src)
atmos_control = new(src)
crew_monitor = new(src)
if(!register_alarms)
return
var/list/register_to = list(atmosphere_alarm, camera_alarm, fire_alarm, motion_alarm, power_alarm)
for(var/datum/alarm_handler/AH in register_to)
AH.register(src, /mob/living/silicon/proc/receive_alarm)
queued_alarms[AH] = list() // Makes sure alarms remain listed in consistent order
/********************
* Alarm Monitor *
********************/
/mob/living/silicon/proc/subsystem_alarm_monitor()
set name = "Alarm Monitor"
set category = "Subsystems"
alarm_monitor.ui_interact(usr, state = self_state)
/********************
* Atmos Control *
********************/
/mob/living/silicon/proc/subsystem_atmos_control()
set category = "Subsystems"
set name = "Atmospherics Control"
atmos_control.ui_interact(usr, state = self_state)
/********************
* Crew Monitor *
********************/
/mob/living/silicon/proc/subsystem_crew_monitor()
set category = "Subsystems"
set name = "Crew Monitor"
crew_monitor.ui_interact(usr, state = self_state)
+16 -1
View File
@@ -498,4 +498,19 @@ var/list/intents = list("help","disarm","grab","harm")
if(O.client)
O << "<span class='ghostalert'>[message]<span>"
if(ghost_sound)
O << sound(ghost_sound)
O << sound(ghost_sound)
/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(get_turf(C))
client.eye = eyeobj
return 1
+15
View File
@@ -17,3 +17,18 @@
else if (restrained() || lying || stat || stunned || weakened)
return STATUS_UPDATE // update only (orange visibility)
return STATUS_INTERACTIVE
/mob/living/silicon/ai/shared_nano_interaction()
if(lacks_power())
return STATUS_CLOSE
if (check_unable(1, 0))
return STATUS_CLOSE
return ..()
/mob/living/silicon/robot/shared_nano_interaction()
. = STATUS_INTERACTIVE
if(cell.charge <= 0)
return STATUS_CLOSE
if(lockcharge)
. = STATUS_DISABLED
return min(., ..())
+9
View File
@@ -0,0 +1,9 @@
/*
This state checks that the src_object is the same the as user
*/
/var/global/datum/topic_state/self_state/self_state = new()
/datum/topic_state/self_state/can_use_topic(var/src_object, var/mob/user)
if(src_object != user)
return STATUS_CLOSE
return user.shared_nano_interaction()
@@ -0,0 +1,88 @@
/datum/nano_module/alarm_monitor
name = "Alarm monitor"
var/list_cameras = 0 // Whether or not to list camera references. A future goal would be to merge this with the enginering/security camera console. Currently really only for AI-use.
var/list/datum/alarm_handler/alarm_handlers // The particular list of alarm handlers this alarm monitor should present to the user.
/datum/nano_module/alarm_monitor/all/New()
..()
alarm_handlers = list(atmosphere_alarm, camera_alarm, fire_alarm, motion_alarm, power_alarm)
/datum/nano_module/alarm_monitor/engineering/New()
..()
alarm_handlers = list(atmosphere_alarm, fire_alarm, power_alarm)
/datum/nano_module/alarm_monitor/security/New()
..()
alarm_handlers = list(camera_alarm, motion_alarm)
/datum/nano_module/alarm_monitor/proc/register(var/object, var/procName)
for(var/datum/alarm_handler/AH in alarm_handlers)
AH.register(object, procName)
/datum/nano_module/alarm_monitor/proc/unregister(var/object)
for(var/datum/alarm_handler/AH in alarm_handlers)
AH.unregister(object)
/datum/nano_module/alarm_monitor/proc/all_alarms()
var/list/all_alarms = new()
for(var/datum/alarm_handler/AH in alarm_handlers)
all_alarms += AH.alarms
return all_alarms
/datum/nano_module/alarm_monitor/proc/major_alarms()
var/list/all_alarms = new()
for(var/datum/alarm_handler/AH in alarm_handlers)
all_alarms += AH.major_alarms()
return all_alarms
/datum/nano_module/alarm_monitor/proc/minor_alarms()
var/list/all_alarms = new()
for(var/datum/alarm_handler/AH in alarm_handlers)
all_alarms += AH.minor_alarms()
return all_alarms
/datum/nano_module/alarm_monitor/Topic(ref, href_list)
if(..())
return 1
if(href_list["switchTo"])
var/obj/machinery/camera/C = locate(href_list["switchTo"]) in cameranet.cameras
if(!C || !isAI(usr))
return
usr.switch_to_camera(C)
return 1
/datum/nano_module/alarm_monitor/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = default_state)
var/data[0]
var/categories[0]
for(var/datum/alarm_handler/AH in alarm_handlers)
categories[++categories.len] = list("category" = AH.category, "alarms" = list())
for(var/datum/alarm/A in AH.major_alarms())
var/cameras[0]
var/lost_sources[0]
if(isAI(user))
for(var/obj/machinery/camera/C in A.cameras())
cameras[++cameras.len] = C.nano_structure()
for(var/datum/alarm_source/AS in A.sources)
if(!AS.source)
lost_sources[++lost_sources.len] = AS.source_name
categories[categories.len]["alarms"] += list(list(
"name" = sanitize(A.alarm_name()),
"origin_lost" = A.origin == null,
"has_cameras" = cameras.len,
"cameras" = cameras,
"lost_sources" = lost_sources.len ? sanitize(english_list(lost_sources, nothing_text = "", and_text = ", ")) : ""))
data["categories"] = categories
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, state = state)
ui.set_initial_data(data)
ui.open()
ui.set_auto_update(1)
@@ -0,0 +1,79 @@
/datum/nano_module/atmos_control
name = "Atmospherics Control"
var/obj/access = new()
var/emagged = 0
var/ui_ref
var/list/monitored_alarms = list()
/datum/nano_module/atmos_control/New(atmos_computer, req_access, req_one_access, monitored_alarm_ids)
..()
access.req_access = req_access
access.req_one_access = req_one_access
if(monitored_alarm_ids)
for(var/obj/machinery/alarm/alarm in machines)
if(alarm.alarm_id && alarm.alarm_id in monitored_alarm_ids)
monitored_alarms += alarm
// machines may not yet be ordered at this point
monitored_alarms = dd_sortedObjectList(monitored_alarms)
/datum/nano_module/atmos_control/Topic(href, href_list)
if(..())
return 1
if(href_list["alarm"])
if(ui_ref)
var/obj/machinery/alarm/alarm = locate(href_list["alarm"]) in (monitored_alarms.len ? monitored_alarms : machines)
if(alarm)
var/datum/topic_state/TS = generate_state(alarm)
alarm.ui_interact(usr, master_ui = ui_ref, state = TS)
return 1
/datum/nano_module/atmos_control/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/master_ui = null, var/datum/topic_state/state = default_state)
var/data[0]
var/alarms[0]
// TODO: Move these to a cache, similar to cameras
for(var/obj/machinery/alarm/alarm in sortAtom((monitored_alarms.len ? monitored_alarms : machines)))
if(!monitored_alarms.len && alarm.z != ZLEVEL_STATION && alarm.z != ZLEVEL_ASTEROID)
continue
alarms[++alarms.len] = alarm.get_nano_data_console()
data["alarms"] = alarms
ui = nanomanager.try_update_ui(user, src, ui_key, ui, data, force_open)
if(!ui)
ui = new(user, src, ui_key, "atmos_control.tmpl", src.name, 900, 800, state = state)
ui.add_template("mapContent", "atmos_control_map_content.tmpl")
ui.add_template("mapHeader", "atmos_control_map_header.tmpl")
ui.set_show_map(1)
ui.set_initial_data(data)
ui.open()
ui.set_auto_update(1)
ui_ref = ui
/datum/nano_module/atmos_control/proc/generate_state(air_alarm)
var/datum/topic_state/air_alarm/state = new()
state.atmos_control = src
state.air_alarm = air_alarm
return state
/datum/topic_state/air_alarm
var/datum/nano_module/atmos_control/atmos_control = null
var/obj/machinery/alarm/air_alarm = null
/datum/topic_state/air_alarm/can_use_topic(var/src_object, var/mob/user)
if(!isAI(user) && !in_range(atmos_control.nano_host(), user))
return STATUS_CLOSE
if(has_access(user))
return STATUS_INTERACTIVE
return STATUS_UPDATE
/datum/topic_state/air_alarm/href_list(var/mob/user)
var/list/extra_href = list()
extra_href["remote_connection"] = 1
extra_href["remote_access"] = has_access(user)
return extra_href
/datum/topic_state/air_alarm/proc/has_access(var/mob/user)
return user && (isAI(user) || atmos_control.access.allowed(user) || atmos_control.emagged || air_alarm.rcon_setting == RCON_YES || (air_alarm.alarm_area.atmosalm && air_alarm.rcon_setting == RCON_AUTO))
+1 -1
View File
@@ -16,7 +16,7 @@
AI.ai_actual_track(H)
return 1
/datum/nano_module/crew_monitor/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
/datum/nano_module/crew_monitor/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = default_state)
src.scan()
var/data[0]
+13 -7
View File
@@ -98,6 +98,10 @@
var/global/list/status_overlays_lighting
var/global/list/status_overlays_environ
var/indestructible = 0 // If set, prevents aliens from destroying it
var/report_power_alarm = 1
/obj/machinery/power/apc/noalarm
report_power_alarm = 0
/obj/machinery/power/apc/updateDialog()
if (stat & (BROKEN|MAINT))
@@ -1153,29 +1157,30 @@
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_charge < 1250 && cell_charge > 750 && longtermpower < 0) // <30%, turn off equipment
if(autoflag != 2)
equipment = autoset(equipment, 2)
lighting = autoset(lighting, 1)
environ = autoset(environ, 1)
area.poweralert(0, src)
if(report_power_alarm)
power_alarm.triggerAlarm(loc, src)
autoflag = 2
else if(cell_charge < 750 && cell_charge > 10 && longtermpower < 0) // <15%, turn off lighting & equipment
if(autoflag != 1)
equipment = autoset(equipment, 2)
lighting = autoset(lighting, 2)
environ = autoset(environ, 1)
area.poweralert(0, src)
if(report_power_alarm)
power_alarm.triggerAlarm(loc, src)
autoflag = 1
else if(cell_charge <= 0) // zero charge, turn all off
if(autoflag != 0)
equipment = autoset(equipment, 0)
lighting = autoset(lighting, 0)
environ = autoset(environ, 0)
area.poweralert(0, src)
if(report_power_alarm)
power_alarm.triggerAlarm(loc, src)
autoflag = 0
// now trickle-charge the cell
@@ -1221,7 +1226,8 @@
equipment = autoset(equipment, 0)
lighting = autoset(lighting, 0)
environ = autoset(environ, 0)
area.poweralert(0, src)
if(report_power_alarm)
power_alarm.triggerAlarm(loc, src)
autoflag = 0
@@ -1,4 +1,4 @@
/area/engine/engineering/poweralert(var/state, var/source)
if (state != poweralm)
/area/engine/engineering/power_alert(var/alarming)
if (alarming)
investigate_log("has a power alarm!","singulo")
..()