diff --git a/beestation.dme b/beestation.dme
index eff34a4864dbb..c8594a02e927b 100644
--- a/beestation.dme
+++ b/beestation.dme
@@ -457,6 +457,7 @@
#include "code\controllers\subsystem\radio.dm"
#include "code\controllers\subsystem\research.dm"
#include "code\controllers\subsystem\runechat.dm"
+#include "code\controllers\subsystem\security_level.dm"
#include "code\controllers\subsystem\server_maint.dm"
#include "code\controllers\subsystem\shuttle.dm"
#include "code\controllers\subsystem\sound.dm"
@@ -3834,7 +3835,7 @@
#include "code\modules\security\prison_scanner.dm"
#include "code\modules\security\workshop.dm"
#include "code\modules\security_levels\keycard_authentication.dm"
-#include "code\modules\security_levels\security_levels.dm"
+#include "code\modules\security_levels\security_level_datums.dm"
#include "code\modules\shuttle\arrivals.dm"
#include "code\modules\shuttle\assault_pod.dm"
#include "code\modules\shuttle\custom_shuttle.dm"
diff --git a/code/__DEFINES/dcs/signals/signals_datum/signals_datum.dm b/code/__DEFINES/dcs/signals/signals_datum/signals_datum.dm
index 55808c4f5d436..e9fc41ba61575 100644
--- a/code/__DEFINES/dcs/signals/signals_datum/signals_datum.dm
+++ b/code/__DEFINES/dcs/signals/signals_datum/signals_datum.dm
@@ -91,6 +91,9 @@
///From base of datum/controller/subsystem/Initialize
#define COMSIG_SUBSYSTEM_POST_INITIALIZE "subsystem_post_initialize"
+///from SSsecurity_level when the security level changes : (new_level)
+#define COMSIG_SECURITY_LEVEL_CHANGED "security_level_changed"
+
/// a weather event of some kind occured
#define COMSIG_WEATHER_TELEGRAPH(event_type) "!weather_telegraph [event_type]"
#define COMSIG_WEATHER_START(event_type) "!weather_start [event_type]"
diff --git a/code/__DEFINES/dcs/signals/signals_global.dm b/code/__DEFINES/dcs/signals/signals_global.dm
index 132dee7e28ea9..1d10a44148fb3 100644
--- a/code/__DEFINES/dcs/signals/signals_global.dm
+++ b/code/__DEFINES/dcs/signals/signals_global.dm
@@ -12,7 +12,6 @@
#define COMSIG_GLOB_MOB_DEATH "!mob_death" //! mob died somewhere : (mob , gibbed)
#define COMSIG_GLOB_LIVING_SAY_SPECIAL "!say_special" //! global living say plug - use sparingly: (mob/speaker , message)
#define COMSIG_GLOB_CARBON_THROW_THING "!throw_thing" //! a person somewhere has thrown something : (mob/living/carbon/carbon_thrower, target)
-#define COMSIG_GLOB_SECURITY_ALERT_CHANGE "!alert_change" //! security level was changed : (new_alert)
#define COMSIG_GLOB_SOUND_PLAYED "!sound_played" //! a sound was played : (sound_player, sound_file)
/// called by datum/cinematic/play() : (datum/cinematic/new_cinematic)
#define COMSIG_GLOB_PLAY_CINEMATIC "!play_cinematic"
diff --git a/code/__DEFINES/shuttles.dm b/code/__DEFINES/shuttles.dm
index 4e4c82190daf1..a716c5eab7d22 100644
--- a/code/__DEFINES/shuttles.dm
+++ b/code/__DEFINES/shuttles.dm
@@ -53,6 +53,14 @@
#define ENGINE_COEFF_MAX 2
#define ENGINE_DEFAULT_MAXSPEED_ENGINES 5
+// Alert level related
+#define ALERT_COEFF_AUTOEVAC_NORMAL 2.5
+#define ALERT_COEFF_GREEN 2
+#define ALERT_COEFF_BLUE 1
+#define ALERT_COEFF_RED 0.5
+#define ALERT_COEFF_AUTOEVAC_CRITICAL 0.4
+#define ALERT_COEFF_DELTA 0.25
+
//Docking error flags
#define DOCKING_SUCCESS 0
#define DOCKING_BLOCKED (1<<0)
diff --git a/code/__DEFINES/subsystems.dm b/code/__DEFINES/subsystems.dm
index 71f4fbb4b7606..b024f21c04a24 100644
--- a/code/__DEFINES/subsystems.dm
+++ b/code/__DEFINES/subsystems.dm
@@ -136,6 +136,7 @@
#define INIT_ORDER_INSTRUMENTS 82
#define INIT_ORDER_GREYSCALE 81
#define INIT_ORDER_VIS 80
+#define INIT_ORDER_SECURITY_LEVEL 79 // We need to load before events so that it has a security level to choose from.
#define INIT_ORDER_ACHIEVEMENTS 77
#define INIT_ORDER_RESEARCH 75
#define INIT_ORDER_ORBITS 74 //Other things use the orbital map, so it needs to be made early on.
diff --git a/code/__HELPERS/priority_announce.dm b/code/__HELPERS/priority_announce.dm
index c7a494b4e82e2..5025198209d57 100644
--- a/code/__HELPERS/priority_announce.dm
+++ b/code/__HELPERS/priority_announce.dm
@@ -75,7 +75,20 @@
SScommunications.send_message(M)
-/proc/minor_announce(message, title = "Attention:", alert, from, html_encode = TRUE)
+/**
+ * Sends a minor annoucement to players.
+ * Minor announcements are large text, with the title in red and message in white.
+ * Only mobs that can hear can see the announcements.
+ *
+ * message - the message contents of the announcement.
+ * title - the title of the announcement, which is often "who sent it".
+ * alert - whether this announcement is an alert, or just a notice. Only changes the sound that is played by default.
+ * from - who sent the announcement, in the case of communications consoles or the like.
+ * html_encode - if TRUE, we will html encode our title and message before sending it, to prevent player input abuse.
+ * players - optional, a list mobs to send the announcement to. If unset, sends to all players.
+ * sound_override - optional, use the passed sound file instead of the default notice sounds.
+ */
+/proc/minor_announce(message, title = "Attention:", alert, from, html_encode = TRUE, list/players, sound_override)
if(!message)
return
@@ -83,16 +96,21 @@
title = html_encode(title)
message = html_encode(message)
- for(var/mob/M in GLOB.player_list)
- if(!isnewplayer(M) && M.can_hear())
- var/complete_msg = "[title]
[message]
"
- if(from)
- complete_msg += "-[from]"
- to_chat(M, complete_msg)
- if(M.client.prefs.read_player_preference(/datum/preference/toggle/sound_announcements))
- if(alert)
- SEND_SOUND(M, sound('sound/misc/notice1.ogg'))
- else
- SEND_SOUND(M, sound('sound/misc/notice2.ogg'))
+ if(!players)
+ players = GLOB.player_list
+
+ for(var/mob/target in players)
+ if(isnewplayer(target))
+ continue
+ if(!target.can_hear())
+ continue
+
+ var/complete_msg = "[title]
[message]
"
+ if(from)
+ complete_msg += "-[from]"
+ to_chat(target, complete_msg)
+ if(target.client.prefs.read_player_preference(/datum/preference/toggle/sound_announcements))
+ var/sound_to_play = sound_override || (alert ? 'sound/misc/notice1.ogg' : 'sound/misc/notice2.ogg')
+ SEND_SOUND(target, sound(sound_to_play))
#undef DEFAULT_ALERT
diff --git a/code/controllers/subsystem/nightshift.dm b/code/controllers/subsystem/nightshift.dm
index e30561dca3d3d..e5cb73146c141 100644
--- a/code/controllers/subsystem/nightshift.dm
+++ b/code/controllers/subsystem/nightshift.dm
@@ -27,7 +27,7 @@ SUBSYSTEM_DEF(nightshift)
priority_announce(message, sound='sound/misc/notice2.ogg', sender_override="Automated Lighting System Announcement")
/datum/controller/subsystem/nightshift/proc/check_nightshift()
- var/emergency = GLOB.security_level >= SEC_LEVEL_RED
+ var/emergency = SSsecurity_level.get_current_level_as_number() >= SEC_LEVEL_RED
var/announcing = TRUE
var/time = station_time()
var/night_time = (time < nightshift_end_time) || (time > nightshift_start_time)
diff --git a/code/controllers/subsystem/security_level.dm b/code/controllers/subsystem/security_level.dm
new file mode 100644
index 0000000000000..2dfd91eaccbfb
--- /dev/null
+++ b/code/controllers/subsystem/security_level.dm
@@ -0,0 +1,105 @@
+SUBSYSTEM_DEF(security_level)
+ name = "Security Level"
+ can_fire = FALSE // We will control when we fire in this subsystem
+ init_order = INIT_ORDER_SECURITY_LEVEL
+ /// Currently set security level
+ var/datum/security_level/current_security_level
+ /// A list of initialised security level datums.
+ var/list/available_levels = list()
+
+/datum/controller/subsystem/security_level/Initialize()
+ for(var/iterating_security_level_type in subtypesof(/datum/security_level))
+ var/datum/security_level/new_security_level = new iterating_security_level_type
+ available_levels[new_security_level.name] = new_security_level
+ current_security_level = available_levels[number_level_to_text(SEC_LEVEL_GREEN)]
+ return SS_INIT_SUCCESS
+
+/datum/controller/subsystem/security_level/fire(resumed)
+ if(!current_security_level.looping_sound) // No sound? No play.
+ can_fire = FALSE
+ return
+ sound_to_playing_players(current_security_level.looping_sound)
+
+
+/**
+ * Sets a new security level as our current level
+ *
+ * This is how everything should change the security level.
+ *
+ * Arguments:
+ * * new_level - The new security level that will become our current level
+ */
+/datum/controller/subsystem/security_level/proc/set_level(new_level)
+ new_level = istext(new_level) ? new_level : number_level_to_text(new_level)
+ if(new_level == current_security_level.name) // If we are already at the desired level, do nothing
+ return
+
+ var/datum/security_level/selected_level = available_levels[new_level]
+
+ if(!selected_level)
+ CRASH("set_level was called with an invalid security level([new_level])")
+
+ if(SSnightshift.can_fire && (selected_level.number_level >= SEC_LEVEL_RED || current_security_level.number_level >= SEC_LEVEL_RED))
+ SSnightshift.next_fire = world.time + 7 SECONDS // Fire nightshift after the security level announcement is complete
+
+ announce_security_level(selected_level) // We want to announce BEFORE updating to the new level
+
+ SSsecurity_level.current_security_level = selected_level
+
+ if(selected_level.looping_sound)
+ wait = selected_level.looping_sound_interval
+ can_fire = TRUE
+ else
+ can_fire = FALSE
+
+ if(SSshuttle.emergency.mode == SHUTTLE_CALL || SSshuttle.emergency.mode == SHUTTLE_RECALL) // By god this is absolutely shit
+ SSshuttle.emergency.alert_coeff_change(selected_level.shuttle_call_time_mod)
+
+ SEND_SIGNAL(src, COMSIG_SECURITY_LEVEL_CHANGED, selected_level.number_level)
+ SSblackbox.record_feedback("tally", "security_level_changes", 1, selected_level.name)
+
+/**
+ * Handles announcements of the newly set security level
+ *
+ * Arguments:
+ * * selected_level - The new security level that has been set
+ */
+/datum/controller/subsystem/security_level/proc/announce_security_level(datum/security_level/selected_level)
+ if(selected_level.number_level > current_security_level.number_level) // We are elevating to this level.
+ minor_announce(selected_level.elevating_to_announcemnt, "Attention! Security level elevated to [selected_level.name]:", sound_override = selected_level.sound)
+ else // Going down
+ minor_announce(selected_level.lowering_to_announcement, "Attention! Security level lowered to [selected_level.name]:", sound_override = selected_level.sound)
+
+/**
+ * Returns the current security level as a number
+ */
+/datum/controller/subsystem/security_level/proc/get_current_level_as_number()
+ return ((!initialized || !current_security_level) ? SEC_LEVEL_GREEN : current_security_level.number_level) //Send the default security level in case the subsystem hasn't finished initializing yet
+
+/**
+ * Returns the current security level as text
+ */
+/datum/controller/subsystem/security_level/proc/get_current_level_as_text()
+ return ((!initialized || !current_security_level) ? "green" : current_security_level.name)
+
+/**
+ * Converts a text security level to a number
+ *
+ * Arguments:
+ * * level - The text security level to convert
+ */
+/datum/controller/subsystem/security_level/proc/text_level_to_number(text_level)
+ var/datum/security_level/selected_level = available_levels[text_level]
+ return selected_level?.number_level
+
+/**
+ * Converts a number security level to a text
+ *
+ * Arguments:
+ * * level - The number security level to convert
+ */
+/datum/controller/subsystem/security_level/proc/number_level_to_text(number_level)
+ for(var/iterating_level_text in available_levels)
+ var/datum/security_level/iterating_security_level = available_levels[iterating_level_text]
+ if(iterating_security_level.number_level == number_level)
+ return iterating_security_level.name
diff --git a/code/controllers/subsystem/shuttle.dm b/code/controllers/subsystem/shuttle.dm
index 52529054d20e9..ec8fd7615daf2 100644
--- a/code/controllers/subsystem/shuttle.dm
+++ b/code/controllers/subsystem/shuttle.dm
@@ -141,8 +141,8 @@ SUBSYSTEM_DEF(shuttle)
log_game("[msg] Alive: [alive], Roundstart: [total], Threshold: [threshold]")
emergencyNoRecall = TRUE
priority_announce("Catastrophic casualties detected: crisis shuttle protocols activated - jamming recall signals across all frequencies.", sound = SSstation.announcer.get_rand_alert_sound())
- if(emergency.timeLeft(1) > emergencyCallTime * 0.4)
- emergency.request(null, set_coefficient = 0.4)
+ if(emergency.timeLeft(1) > emergencyCallTime * ALERT_COEFF_AUTOEVAC_CRITICAL)
+ emergency.request(null, set_coefficient = ALERT_COEFF_AUTOEVAC_CRITICAL)
/datum/controller/subsystem/shuttle/proc/block_recall(lockout_timer)
emergencyNoRecall = TRUE
@@ -207,13 +207,13 @@ SUBSYSTEM_DEF(shuttle)
call_reason = trim(html_encode(call_reason))
- if(length(call_reason) < CALL_SHUTTLE_REASON_LENGTH && seclevel2num(get_security_level()) > SEC_LEVEL_GREEN)
+ if(length(call_reason) < CALL_SHUTTLE_REASON_LENGTH && SSsecurity_level.get_current_level_as_number() > SEC_LEVEL_GREEN)
to_chat(user, "You must provide a reason.")
return
var/area/signal_origin = get_area(user)
var/emergency_reason = "\nNature of emergency:\n\n[call_reason]"
- var/security_num = seclevel2num(get_security_level())
+ var/security_num = SSsecurity_level.get_current_level_as_number()
switch(security_num)
if(SEC_LEVEL_RED,SEC_LEVEL_DELTA)
emergency.request(null, signal_origin, html_decode(emergency_reason), 1) //There is a serious threat we gotta move no time to give them five minutes.
@@ -275,7 +275,7 @@ SUBSYSTEM_DEF(shuttle)
/datum/controller/subsystem/shuttle/proc/canRecall()
if(!emergency || emergency.mode != SHUTTLE_CALL || emergencyNoRecall || SSticker.mode.name == "meteor")
return
- var/security_num = seclevel2num(get_security_level())
+ var/security_num = SSsecurity_level.get_current_level_as_number()
switch(security_num)
if(SEC_LEVEL_GREEN)
if(emergency.timeLeft(1) < emergencyCallTime)
@@ -313,7 +313,7 @@ SUBSYSTEM_DEF(shuttle)
if(callShuttle)
if(EMERGENCY_IDLE_OR_RECALLED)
- emergency.request(null, set_coefficient = 2.5)
+ emergency.request(null, set_coefficient = ALERT_COEFF_AUTOEVAC_NORMAL)
log_game("There is no means of calling the shuttle anymore. Shuttle automatically called.")
message_admins("All the communications consoles were destroyed and all AIs are inactive. Shuttle called.")
diff --git a/code/datums/world_topic.dm b/code/datums/world_topic.dm
index d60ff575e9dc5..211054172d68c 100644
--- a/code/datums/world_topic.dm
+++ b/code/datums/world_topic.dm
@@ -242,7 +242,7 @@
data["map_name"] = SSmapping.config?.map_name || "Loading..."
- data["security_level"] = get_security_level()
+ data["security_level"] = SSsecurity_level.get_current_level_as_text()
data["round_duration"] = SSticker?.round_start_timeofday ? round((world.timeofday - SSticker.round_start_timeofday)/10) : 0
// Amount of world's ticks in seconds, useful for calculating round duration
diff --git a/code/game/gamemodes/dynamic/dynamic.dm b/code/game/gamemodes/dynamic/dynamic.dm
index 4472654c9fb37..d49cf56acb87d 100644
--- a/code/game/gamemodes/dynamic/dynamic.dm
+++ b/code/game/gamemodes/dynamic/dynamic.dm
@@ -365,8 +365,8 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1)
print_command_report(., "Central Command Status Summary", announce=FALSE)
priority_announce("A summary has been copied and printed to all communications consoles.", "Security level elevated.", ANNOUNCER_INTERCEPT)
- if(GLOB.security_level < SEC_LEVEL_BLUE)
- set_security_level(SEC_LEVEL_BLUE)
+ if(SSsecurity_level.get_current_level_as_number() < SEC_LEVEL_BLUE)
+ SSsecurity_level.set_level(SEC_LEVEL_BLUE)
// Yes, this is copy pasted from game_mode
/datum/game_mode/dynamic/check_finished(force_ending)
diff --git a/code/game/gamemodes/game_mode.dm b/code/game/gamemodes/game_mode.dm
index dcf555e3bb7a8..7f03d165af976 100644
--- a/code/game/gamemodes/game_mode.dm
+++ b/code/game/gamemodes/game_mode.dm
@@ -396,8 +396,8 @@
print_command_report(intercepttext, "Central Command Status Summary", announce=FALSE)
priority_announce("A summary has been copied and printed to all communications consoles.", "Enemy communication intercepted. Security level elevated.", ANNOUNCER_INTERCEPT)
- if(GLOB.security_level < SEC_LEVEL_BLUE)
- set_security_level(SEC_LEVEL_BLUE)
+ if(SSsecurity_level.get_current_level_as_number() < SEC_LEVEL_BLUE)
+ SSsecurity_level.set_level(SEC_LEVEL_BLUE)
/*
diff --git a/code/game/gamemodes/gangs/dominator.dm b/code/game/gamemodes/gangs/dominator.dm
index 4af2519204b88..b1ecf4b1a2cc7 100644
--- a/code/game/gamemodes/gangs/dominator.dm
+++ b/code/game/gamemodes/gangs/dominator.dm
@@ -224,7 +224,7 @@
priority_announce("All hostile activity within station systems has ceased.","Network Alert", SSstation.announcer.get_rand_alert_sound())
if(get_security_level() == "delta")
- set_security_level("red")
+ SSsecurity_level.set_level(SEC_LEVEL_RED)
SSshuttle.clearHostileEnvironment(src)
gang.message_gangtools("Hostile takeover cancelled: Dominator is no longer operational.[gang.dom_attempts ? " You have [gang.dom_attempts] attempt remaining." : " The station network will have likely blocked any more attempts by us."]",1,1)
diff --git a/code/game/machinery/computer/communications.dm b/code/game/machinery/computer/communications.dm
index 987db2e5a8f9e..1cf6eafd07f8c 100755
--- a/code/game/machinery/computer/communications.dm
+++ b/code/game/machinery/computer/communications.dm
@@ -124,13 +124,13 @@
playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, FALSE)
return
- var/new_sec_level = seclevel2num(params["newSecurityLevel"])
+ var/new_sec_level = SSsecurity_level.text_level_to_number(params["newSecurityLevel"])
if (new_sec_level != SEC_LEVEL_GREEN && new_sec_level != SEC_LEVEL_BLUE)
return
- if (GLOB.security_level == new_sec_level)
+ if (SSsecurity_level.get_current_level_as_number() == new_sec_level)
return
- set_security_level(new_sec_level)
+ SSsecurity_level.set_level(new_sec_level)
to_chat(usr, "Authorization confirmed. Modifying security level.")
playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, FALSE)
@@ -156,7 +156,7 @@
make_announcement(usr)
. = TRUE
if ("messageAssociates")
- if (!authenticated(usr) || issilicon(usr) || (GLOB.security_level < SEC_LEVEL_RED && !authenticated_as_non_silicon_captain(usr)))
+ if (!authenticated(usr) || issilicon(usr) || (SSsecurity_level.get_current_level_as_number() < SEC_LEVEL_RED && !authenticated_as_non_silicon_captain(usr)))
return
if (!COOLDOWN_FINISHED(src, important_action_cooldown))
return
@@ -351,7 +351,7 @@
//Main section is always visible when authenticated
data["canBuyShuttles"] = can_buy_shuttles(user)
data["canMakeAnnouncement"] = FALSE
- data["canMessageAssociates"] = !issilicon(user) && GLOB.security_level >= SEC_LEVEL_RED
+ data["canMessageAssociates"] = !issilicon(user) && SSsecurity_level.get_current_level_as_number() >= SEC_LEVEL_RED
data["canRecallShuttles"] = !issilicon(user)
data["canRequestNuke"] = FALSE
data["canSendToSectors"] = FALSE
@@ -361,7 +361,7 @@
data["shuttleCalled"] = FALSE
data["shuttleLastCalled"] = FALSE
- data["alertLevel"] = get_security_level()
+ data["alertLevel"] = SSsecurity_level.get_current_level_as_text()
data["authorizeName"] = authorize_name
data["canLogOut"] = !issilicon(user)
data["shuttleCanEvacOrFailReason"] = SSshuttle.canEvac(user)
diff --git a/code/game/machinery/defibrillator_mount.dm b/code/game/machinery/defibrillator_mount.dm
index 9870cc9615b5b..abfb524aa0587 100644
--- a/code/game/machinery/defibrillator_mount.dm
+++ b/code/game/machinery/defibrillator_mount.dm
@@ -44,7 +44,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/defibrillator_mount/loaded, 28)
. = ..()
if(defib)
. += "There is a defib unit hooked up. Alt-click to remove it."
- if(GLOB.security_level >= SEC_LEVEL_RED)
+ if(SSsecurity_level.get_current_level_as_number() >= SEC_LEVEL_RED)
. += "Due to a security situation, its locking clamps can be toggled by swiping any ID."
else
. += "Its locking clamps can be [clamps_locked ? "dis" : ""]engaged by swiping an ID with access."
@@ -103,7 +103,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/defibrillator_mount/loaded, 28)
return
var/obj/item/card/id = I.GetID()
if(id)
- if(check_access(id) || GLOB.security_level >= SEC_LEVEL_RED) //anyone can toggle the clamps in red alert!
+ if(check_access(id) || SSsecurity_level.get_current_level_as_number() >= SEC_LEVEL_RED) //anyone can toggle the clamps in red alert!
if(!defib)
to_chat(user, "You can't engage the clamps on a defibrillator that isn't there.")
return
diff --git a/code/game/machinery/doors/door.dm b/code/game/machinery/doors/door.dm
index 624edd84c1168..b303eb31cd972 100644
--- a/code/game/machinery/doors/door.dm
+++ b/code/game/machinery/doors/door.dm
@@ -42,20 +42,6 @@
var/unres_sides = 0 //Unrestricted sides. A bitflag for which direction (if any) can open the door with no access
var/open_speed = 5
-/obj/machinery/door/examine(mob/user)
- . = ..()
- if(red_alert_access)
- if(GLOB.security_level >= SEC_LEVEL_RED)
- . += "Due to a security threat, its access requirements have been lifted!"
- else
- . += "In the event of a red alert, its access requirements will automatically lift."
- . += "Its maintenance panel is screwed in place."
-
-/obj/machinery/door/check_access_list(list/access_list)
- if(red_alert_access && GLOB.security_level >= SEC_LEVEL_RED)
- return TRUE
- return ..()
-
/obj/machinery/door/Initialize(mapload)
. = ..()
set_init_door_layer()
@@ -68,15 +54,21 @@
//doors only block while dense though so we have to use the proc
real_explosion_block = explosion_block
explosion_block = EXPLOSION_BLOCK_PROC
- if(red_alert_access)
- RegisterSignal(SSdcs, COMSIG_GLOB_SECURITY_ALERT_CHANGE, PROC_REF(handle_alert))
+ RegisterSignal(SSsecurity_level, COMSIG_SECURITY_LEVEL_CHANGED, PROC_REF(check_security_level))
-/obj/machinery/door/proc/handle_alert(datum/source, new_alert)
- SIGNAL_HANDLER
- if(new_alert >= SEC_LEVEL_RED)
- visible_message("[src] whirs as it automatically lifts access requirements!")
- playsound(src, 'sound/machines/boltsup.ogg', 50, TRUE)
+/obj/machinery/door/examine(mob/user)
+ . = ..()
+ if(red_alert_access)
+ if(SSsecurity_level.get_current_level_as_number() >= SEC_LEVEL_RED)
+ . += "Due to a security threat, its access requirements have been lifted!"
+ else
+ . += "In the event of a red alert, its access requirements will automatically lift."
+ . += "Its maintenance panel is screwed in place."
+/obj/machinery/door/check_access_list(list/access_list)
+ if(red_alert_access && SSsecurity_level.get_current_level_as_number() >= SEC_LEVEL_RED)
+ return TRUE
+ return ..()
/obj/machinery/door/proc/set_init_door_layer()
if(density)
@@ -426,3 +418,20 @@
/obj/machinery/door/GetExplosionBlock()
return density ? real_explosion_block : 0
+
+/**
+ * Signal handler for checking if we notify our surrounding that access requirements are lifted accordingly to a newly set security level
+ *
+ * Arguments:
+ * * source The datum source of the signal
+ * * new_level The new security level that is in effect
+ */
+/obj/machinery/door/proc/check_security_level(datum/source, new_level)
+ SIGNAL_HANDLER
+
+ if(new_level <= SEC_LEVEL_BLUE)
+ return
+ if(!red_alert_access)
+ return
+ audible_message("[src] whirr[p_s()] as [p_they()] automatically lift[p_s()] access requirements!")
+ playsound(src, 'sound/machines/boltsup.ogg', 50, TRUE)
diff --git a/code/game/machinery/firealarm.dm b/code/game/machinery/firealarm.dm
index a8f5d048da866..70d46cfa00089 100644
--- a/code/game/machinery/firealarm.dm
+++ b/code/game/machinery/firealarm.dm
@@ -46,12 +46,7 @@
update_appearance()
myarea = get_area(src)
LAZYADD(myarea.firealarms, src)
- RegisterSignal(SSdcs, COMSIG_GLOB_SECURITY_ALERT_CHANGE, PROC_REF(handle_alert))
-
-/obj/machinery/firealarm/proc/handle_alert(datum/source, new_alert)
- SIGNAL_HANDLER
- if(is_station_level(z))
- update_appearance()
+ RegisterSignal(SSsecurity_level, COMSIG_SECURITY_LEVEL_CHANGED, PROC_REF(check_security_level))
/obj/machinery/firealarm/Destroy()
myarea.firereset(src)
@@ -67,9 +62,9 @@
. += "fire_overlay"
if(is_station_level(z))
- . += "fire_[GLOB.security_level]"
- . += mutable_appearance(icon, "fire_[GLOB.security_level]")
- . += emissive_appearance(icon, "fire_[GLOB.security_level]", layer, alpha = 255)
+ . += "fire_[SSsecurity_level.get_current_level_as_number()]"
+ . += mutable_appearance(icon, "fire_[SSsecurity_level.get_current_level_as_number()]")
+ . += emissive_appearance(icon, "fire_[SSsecurity_level.get_current_level_as_number()]", layer, alpha = 255)
ADD_LUM_SOURCE(src, LUM_SOURCE_MANAGED_OVERLAY)
else
. += "fire_[SEC_LEVEL_GREEN]"
@@ -120,6 +115,19 @@
alarm()
..()
+/**
+ * Signal handler for checking if we should update fire alarm appearance accordingly to a newly set security level
+ *
+ * Arguments:
+ * * source The datum source of the signal
+ * * new_level The new security level that is in effect
+ */
+/obj/machinery/firealarm/proc/check_security_level(datum/source, new_level)
+ SIGNAL_HANDLER
+
+ if(is_station_level(z))
+ update_appearance()
+
/obj/machinery/firealarm/proc/alarm(mob/user)
if(!is_operational || (last_alarm+FIREALARM_COOLDOWN > world.time))
return
diff --git a/code/game/objects/structures/displaycase.dm b/code/game/objects/structures/displaycase.dm
index 6678f8a7a8d52..a2d071256fd45 100644
--- a/code/game/objects/structures/displaycase.dm
+++ b/code/game/objects/structures/displaycase.dm
@@ -143,7 +143,7 @@
if(open) //You do not require access to close a case, only to open it.
to_chat(user, "You close [src].")
toggle_lock(user)
- else if(security_level_locked > GLOB.security_level || !allowed(user))
+ else if(security_level_locked > SSsecurity_level.get_current_level_as_number() || !allowed(user))
to_chat(user, "Access denied.")
else
to_chat(user, "You open [src].")
diff --git a/code/modules/admin/verbs/randomverbs.dm b/code/modules/admin/verbs/randomverbs.dm
index af25474593553..c77f6d58ce261 100644
--- a/code/modules/admin/verbs/randomverbs.dm
+++ b/code/modules/admin/verbs/randomverbs.dm
@@ -840,13 +840,16 @@ Traitors and the like can also be revived with the previous role mostly intact.
if(!check_rights(R_ADMIN))
return
- var/level = input("Select security level to change to","Set Security Level") as null|anything in list("green","blue","red","delta")
- if(level)
- set_security_level(level)
+ var/level = tgui_input_list(usr, "Select Security Level:", "Set Security Level", SSsecurity_level.available_levels)
- log_admin("[key_name(usr)] changed the security level to [level]")
- message_admins("[key_name_admin(usr)] changed the security level to [level]")
- SSblackbox.record_feedback("tally", "admin_verb", 1, "Set Security Level [capitalize(level)]") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
+ if(!level)
+ return
+
+ SSsecurity_level.set_level(level)
+
+ log_admin("[key_name(usr)] changed the security level to [level]")
+ message_admins("[key_name_admin(usr)] changed the security level to [level]")
+ SSblackbox.record_feedback("tally", "admin_verb", 1, "Set Security Level [capitalize(level)]") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/client/proc/toggle_nuke(obj/machinery/nuclearbomb/N in GLOB.nuke_list)
set name = "Toggle Nuke"
diff --git a/code/modules/antagonists/blob/overmind.dm b/code/modules/antagonists/blob/overmind.dm
index 792640428c5af..d5215bf517976 100644
--- a/code/modules/antagonists/blob/overmind.dm
+++ b/code/modules/antagonists/blob/overmind.dm
@@ -115,7 +115,7 @@ GLOBAL_LIST_EMPTY(blob_nodes)
else if(!victory_in_progress && (blobs_legit.len >= blobwincount))
victory_in_progress = TRUE
priority_announce("Biohazard has reached critical mass. Station loss is imminent.", "Biohazard Alert", SSstation.announcer.get_rand_alert_sound())
- set_security_level("delta")
+ SSsecurity_level.set_level(SEC_LEVEL_DELTA)
max_blob_points = INFINITY
blob_points = INFINITY
addtimer(CALLBACK(src, PROC_REF(victory)), 450)
diff --git a/code/modules/antagonists/clock_cult/clockwork_massive.dm b/code/modules/antagonists/clock_cult/clockwork_massive.dm
index 46c74b77669fa..aa19c1480fbce 100644
--- a/code/modules/antagonists/clock_cult/clockwork_massive.dm
+++ b/code/modules/antagonists/clock_cult/clockwork_massive.dm
@@ -88,7 +88,7 @@ GLOBAL_LIST_INIT(clockwork_portals, list())
for(var/obj/effect/portal/wormhole/clockcult/CC in GLOB.all_wormholes)
qdel(CC)
SSshuttle.clearHostileEnvironment(src)
- set_security_level(SEC_LEVEL_RED)
+ SSsecurity_level.set_level(SEC_LEVEL_RED)
addtimer(CALLBACK(src, PROC_REF(clockies_win)), 300)
/obj/structure/destructible/clockwork/massive/celestial_gateway/proc/clockies_win()
@@ -134,7 +134,7 @@ GLOBAL_LIST_INIT(clockwork_portals, list())
/obj/structure/destructible/clockwork/massive/celestial_gateway/proc/announce_gateway()
set_dynamic_high_impact_event("clockwork ark has opened")
activated = TRUE
- set_security_level(SEC_LEVEL_DELTA)
+ SSsecurity_level.set_level(SEC_LEVEL_DELTA)
mass_recall(TRUE)
var/grace_time = GLOB.narsie_breaching ? 0 : 1800
addtimer(CALLBACK(src, PROC_REF(begin_assault)), grace_time)
diff --git a/code/modules/antagonists/clock_cult/helpers/clockcult_ending.dm b/code/modules/antagonists/clock_cult/helpers/clockcult_ending.dm
index de51ca0ba2f06..6085f97b93802 100644
--- a/code/modules/antagonists/clock_cult/helpers/clockcult_ending.dm
+++ b/code/modules/antagonists/clock_cult/helpers/clockcult_ending.dm
@@ -1,7 +1,7 @@
/proc/trigger_clockcult_victory(hostile)
addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(clockcult_gg)), 700)
sleep(50)
- set_security_level("delta")
+ SSsecurity_level.set_level(SEC_LEVEL_DELTA)
priority_announce("Huge gravitational-energy spike detected emminating from a neutron star near your sector. Event has been determined to be survivable by 0% of life. ESTIMATED TIME UNTIL ENERGY PULSE REACHES [GLOB.station_name]: 56 SECONDS. Godspeed crew, glory to Nanotrasen. -Admiral Telvig.", "Central Command Anomolous Materials Division", 'sound/misc/bloblarm.ogg')
for(var/client/C in GLOB.clients)
SEND_SOUND(C, sound('sound/misc/airraid.ogg', 1))
diff --git a/code/modules/antagonists/cult/cult_items.dm b/code/modules/antagonists/cult/cult_items.dm
index ad574f51bf3d4..b4e2230551530 100644
--- a/code/modules/antagonists/cult/cult_items.dm
+++ b/code/modules/antagonists/cult/cult_items.dm
@@ -324,7 +324,7 @@ Striking a noncultist, however, will tear their flesh."}
if(SSshuttle.emergency.mode == SHUTTLE_CALL)
var/cursetime = 1800
var/timer = SSshuttle.emergency.timeLeft(1) + cursetime
- var/security_num = seclevel2num(get_security_level())
+ var/security_num = SSsecurity_level.get_current_level_as_number()
var/set_coefficient = 1
switch(security_num)
if(SEC_LEVEL_GREEN)
diff --git a/code/modules/antagonists/cult/narsie.dm b/code/modules/antagonists/cult/narsie.dm
index 3fd0470a5fd2d..73a3102022e85 100644
--- a/code/modules/antagonists/cult/narsie.dm
+++ b/code/modules/antagonists/cult/narsie.dm
@@ -206,7 +206,7 @@
sleep(500)
priority_announce("Simulations on acausal dimensional event complete. Deploying solution package now. Deployment ETA: ONE MINUTE. ", "Central Command Higher Dimensional Affairs", SSstation.announcer.get_rand_alert_sound())
sleep(50)
- set_security_level("delta")
+ SSsecurity_level.set_level(SEC_LEVEL_DELTA)
SSshuttle.registerHostileEnvironment(src)
sleep(600)
if(resolved == FALSE)
diff --git a/code/modules/antagonists/nukeop/equipment/nuclearbomb.dm b/code/modules/antagonists/nukeop/equipment/nuclearbomb.dm
index 4f65c750f2b65..1da124706b09f 100644
--- a/code/modules/antagonists/nukeop/equipment/nuclearbomb.dm
+++ b/code/modules/antagonists/nukeop/equipment/nuclearbomb.dm
@@ -46,7 +46,7 @@
STOP_PROCESSING(SSobj, core)
update_icon()
AddElement(/datum/element/point_of_interest)
- previous_level = get_security_level()
+ previous_level = SSsecurity_level.get_current_level_as_text()
/obj/machinery/nuclearbomb/Destroy()
safety = FALSE
@@ -427,7 +427,7 @@
safety = !safety
if(safety)
if(timing)
- set_security_level(previous_level)
+ SSsecurity_level.set_level(previous_level)
stop_soundtrack_music(stop_playing = TRUE)
for(var/obj/item/pinpointer/nuke/syndicate/S in GLOB.pinpointer_list)
S.switch_mode_to(initial(S.mode))
@@ -443,12 +443,12 @@
return
timing = !timing
if(timing)
- previous_level = get_security_level()
+ previous_level = SSsecurity_level.get_current_level_as_number()
detonation_timer = world.time + (timer_set * 10)
for(var/obj/item/pinpointer/nuke/syndicate/S in GLOB.pinpointer_list)
S.switch_mode_to(TRACK_INFILTRATOR)
countdown.start()
- set_security_level(SEC_LEVEL_DELTA)
+ SSsecurity_level.set_level(SEC_LEVEL_DELTA)
if (proper_bomb) // Why does this exist
set_dynamic_high_impact_event("nuclear bomb has been armed")
@@ -456,7 +456,7 @@
else
detonation_timer = null
- set_security_level(previous_level)
+ SSsecurity_level.set_level(previous_level)
stop_soundtrack_music(stop_playing = TRUE)
for(var/obj/item/pinpointer/nuke/syndicate/S in GLOB.pinpointer_list)
@@ -585,7 +585,7 @@
detonation_timer = null
exploding = FALSE
exploded = TRUE
- set_security_level(previous_level)
+ SSsecurity_level.set_level(previous_level)
for(var/obj/item/pinpointer/nuke/syndicate/S in GLOB.pinpointer_list)
S.switch_mode_to(initial(S.mode))
S.alert = FALSE
diff --git a/code/modules/antagonists/traitor/equipment/Malf_Modules.dm b/code/modules/antagonists/traitor/equipment/Malf_Modules.dm
index bbc744eae9834..c9a15ce60686e 100644
--- a/code/modules/antagonists/traitor/equipment/Malf_Modules.dm
+++ b/code/modules/antagonists/traitor/equipment/Malf_Modules.dm
@@ -326,7 +326,7 @@ GLOBAL_LIST_INIT(blacklisted_malf_machines, typecacheof(list(
if(!owner || QDELETED(owner))
return
priority_announce("Hostile runtimes detected in all station systems, please deactivate your AI to prevent possible damage to its morality core.", "Anomaly Alert", ANNOUNCER_AIMALF)
- set_security_level("delta")
+ SSsecurity_level.set_level(SEC_LEVEL_DELTA)
owner.log_message("activated malf module [name]", LOG_GAME)
var/obj/machinery/doomsday_device/DOOM = new(owner_AI)
owner_AI.nuking = TRUE
diff --git a/code/modules/events/asteroid_impact.dm b/code/modules/events/asteroid_impact.dm
index 0db2eb2892264..a303576af65c3 100644
--- a/code/modules/events/asteroid_impact.dm
+++ b/code/modules/events/asteroid_impact.dm
@@ -13,7 +13,7 @@
/datum/round_event/asteroid_impact/announce(fake)
priority_announce("A class-A asteroid has been detected on a collision course with the station. Destruction of the station is innevitable.", SSstation.announcer.get_rand_alert_sound())
if(!fake)
- set_security_level(SEC_LEVEL_DELTA)
+ SSsecurity_level.set_level(SEC_LEVEL_DELTA)
var/area/A = GLOB.areas_by_type[/area/centcom]
if(EMERGENCY_IDLE_OR_RECALLED)
SSshuttle.emergency.request(null, A, "Automatic Shuttle Call: Station destruction imminent.", TRUE)
diff --git a/code/modules/mob/living/silicon/ai/death.dm b/code/modules/mob/living/silicon/ai/death.dm
index fa4346abb58f0..a9b65598d65e3 100644
--- a/code/modules/mob/living/silicon/ai/death.dm
+++ b/code/modules/mob/living/silicon/ai/death.dm
@@ -46,7 +46,7 @@
/mob/living/silicon/ai/proc/ShutOffDoomsdayDevice()
if(nuking)
- set_security_level("red")
+ SSsecurity_level.set_level(SEC_LEVEL_RED)
nuking = FALSE
for(var/obj/item/pinpointer/nuke/P in GLOB.pinpointer_list)
P.switch_mode_to(TRACK_NUKE_DISK) //Party's over, back to work, everyone
diff --git a/code/modules/mob/mob_stat.dm b/code/modules/mob/mob_stat.dm
index c9200140700a5..73e365a2620fd 100644
--- a/code/modules/mob/mob_stat.dm
+++ b/code/modules/mob/mob_stat.dm
@@ -238,7 +238,7 @@
else
tab_data["Players Playing/Connected"] = GENERATE_STAT_TEXT("[get_active_player_count()]/[GLOB.clients.len]")
if(SSticker.round_start_time)
- tab_data["Security Level"] = GENERATE_STAT_TEXT("[capitalize(get_security_level())]")
+ tab_data["Security Level"] = GENERATE_STAT_TEXT("[capitalize(SSsecurity_level.get_current_level_as_text())]")
tab_data["divider_3"] = GENERATE_STAT_DIVIDER
if(SSshuttle.emergency)
diff --git a/code/modules/security_levels/keycard_authentication.dm b/code/modules/security_levels/keycard_authentication.dm
index a0627309447f7..89c92e3f140e6 100644
--- a/code/modules/security_levels/keycard_authentication.dm
+++ b/code/modules/security_levels/keycard_authentication.dm
@@ -51,7 +51,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/keycard_auth, 26)
var/list/data = list()
data["waiting"] = waiting
data["auth_required"] = event_source ? event_source.event : 0
- data["red_alert"] = (seclevel2num(get_security_level()) >= SEC_LEVEL_RED) ? 1 : 0
+ data["red_alert"] = (SSsecurity_level.get_current_level_as_number() >= SEC_LEVEL_RED) ? 1 : 0
data["emergency_maint"] = GLOB.emergency_access
data["bsa_unlock"] = GLOB.bsa_unlock
return data
@@ -134,7 +134,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/keycard_auth, 26)
deadchat_broadcast("[confirmer] confirmed [event] at [A2.name].", confirmer)
switch(event)
if(KEYCARD_RED_ALERT)
- set_security_level(SEC_LEVEL_RED)
+ SSsecurity_level.set_level(SEC_LEVEL_RED)
if(KEYCARD_EMERGENCY_MAINTENANCE_ACCESS)
make_maint_all_access()
if(KEYCARD_BSA_UNLOCK)
diff --git a/code/modules/security_levels/security_level_datums.dm b/code/modules/security_levels/security_level_datums.dm
new file mode 100644
index 0000000000000..4737e21e41d0d
--- /dev/null
+++ b/code/modules/security_levels/security_level_datums.dm
@@ -0,0 +1,86 @@
+/**
+ * Security levels
+ *
+ * These are used by the security level subsystem. Each one of these represents a security level that a player can set.
+ *
+ * Base type is abstract
+ */
+
+/datum/security_level
+ /// The name of this security level.
+ var/name = "not set"
+ /// The numerical level of this security level, see defines for more information.
+ var/number_level = -1
+ /// The sound that we will play when this security level is set
+ var/sound
+ /// The looping sound that will be played while the security level is set
+ var/looping_sound
+ /// The looping sound interval
+ var/looping_sound_interval
+ /// The shuttle call time modification of this security level
+ var/shuttle_call_time_mod = 0
+ /// Our announcement when lowering to this level
+ var/lowering_to_announcement
+ /// Our announcement when elevating to this level
+ var/elevating_to_announcemnt
+ /// Our configuration key for lowering to text, if set, will override the default lowering to announcement.
+ var/lowering_to_configuration_key
+ /// Our configuration key for elevating to text, if set, will override the default elevating to announcement.
+ var/elevating_to_configuration_key
+
+/datum/security_level/New()
+ . = ..()
+ if(lowering_to_configuration_key) // I'm not sure about you, but isn't there an easier way to do this?
+ lowering_to_announcement = global.config.Get(lowering_to_configuration_key)
+ if(elevating_to_configuration_key)
+ elevating_to_announcemnt = global.config.Get(elevating_to_configuration_key)
+
+/**
+ * GREEN
+ *
+ * No threats
+ */
+/datum/security_level/green
+ name = "green"
+ sound = 'sound/misc/notice2.ogg' // Friendly beep
+ number_level = SEC_LEVEL_GREEN
+ lowering_to_configuration_key = /datum/config_entry/string/alert_green
+ shuttle_call_time_mod = ALERT_COEFF_GREEN
+
+/**
+ * BLUE
+ *
+ * Caution advised
+ */
+/datum/security_level/blue
+ name = "blue"
+ sound = 'sound/misc/notice1.ogg' // Angry alarm
+ number_level = SEC_LEVEL_BLUE
+ lowering_to_configuration_key = /datum/config_entry/string/alert_blue_downto
+ elevating_to_configuration_key = /datum/config_entry/string/alert_blue_upto
+ shuttle_call_time_mod = ALERT_COEFF_BLUE
+
+/**
+ * RED
+ *
+ * Hostile threats
+ */
+/datum/security_level/red
+ name = "red"
+ sound = 'sound/misc/notice1.ogg' // The same angry alarm
+ number_level = SEC_LEVEL_RED
+ lowering_to_configuration_key = /datum/config_entry/string/alert_red_downto
+ elevating_to_configuration_key = /datum/config_entry/string/alert_red_upto
+ shuttle_call_time_mod = ALERT_COEFF_RED
+
+/**
+ * DELTA
+ *
+ * Station destruction is imminent
+ */
+/datum/security_level/delta
+ name = "delta"
+ sound = 'sound/misc/notice1.ogg' // The same angry alarm, again
+ number_level = SEC_LEVEL_DELTA
+ elevating_to_configuration_key = /datum/config_entry/string/alert_delta
+ shuttle_call_time_mod = ALERT_COEFF_DELTA
diff --git a/code/modules/security_levels/security_levels.dm b/code/modules/security_levels/security_levels.dm
deleted file mode 100644
index 95398906c4da7..0000000000000
--- a/code/modules/security_levels/security_levels.dm
+++ /dev/null
@@ -1,97 +0,0 @@
-GLOBAL_VAR_INIT(security_level, SEC_LEVEL_GREEN)
-//SEC_LEVEL_GREEN = code green
-//SEC_LEVEL_BLUE = code blue
-//SEC_LEVEL_RED = code red
-//SEC_LEVEL_DELTA = code delta
-
-//config.alert_desc_blue_downto
-
-/proc/set_security_level(level)
- switch(level)
- if("green")
- level = SEC_LEVEL_GREEN
- if("blue")
- level = SEC_LEVEL_BLUE
- if("red")
- level = SEC_LEVEL_RED
- if("delta")
- level = SEC_LEVEL_DELTA
-
- //Will not be announced if you try to set to the same level as it already is
- if(level < SEC_LEVEL_GREEN || level > SEC_LEVEL_DELTA || level == GLOB.security_level)
- return
- switch(level)
- if(SEC_LEVEL_GREEN)
- minor_announce(CONFIG_GET(string/alert_green), "Attention! Security level lowered to green:")
- if(SSshuttle.emergency.mode == SHUTTLE_CALL || SSshuttle.emergency.mode == SHUTTLE_RECALL)
- if(GLOB.security_level >= SEC_LEVEL_RED)
- SSshuttle.emergency.modTimer(4)
- else
- SSshuttle.emergency.modTimer(2)
-
- if(SEC_LEVEL_BLUE)
- if(GLOB.security_level < SEC_LEVEL_BLUE)
- minor_announce(CONFIG_GET(string/alert_blue_upto), "Attention! Security level elevated to blue:",1)
- if(SSshuttle.emergency.mode == SHUTTLE_CALL || SSshuttle.emergency.mode == SHUTTLE_RECALL)
- SSshuttle.emergency.modTimer(0.5)
- else
- minor_announce(CONFIG_GET(string/alert_blue_downto), "Attention! Security level lowered to blue:")
- if(SSshuttle.emergency.mode == SHUTTLE_CALL || SSshuttle.emergency.mode == SHUTTLE_RECALL)
- SSshuttle.emergency.modTimer(2)
-
- if(SEC_LEVEL_RED)
- if(GLOB.security_level < SEC_LEVEL_RED)
- minor_announce(CONFIG_GET(string/alert_red_upto), "Attention! Code red!",1)
- if(SSshuttle.emergency.mode == SHUTTLE_CALL || SSshuttle.emergency.mode == SHUTTLE_RECALL)
- if(GLOB.security_level == SEC_LEVEL_GREEN)
- SSshuttle.emergency.modTimer(0.25)
- else
- SSshuttle.emergency.modTimer(0.5)
- else
- minor_announce(CONFIG_GET(string/alert_red_downto), "Attention! Code red!")
-
- if(SEC_LEVEL_DELTA)
- minor_announce(CONFIG_GET(string/alert_delta), "Attention! Delta security level reached!",1)
- if(SSshuttle.emergency.mode == SHUTTLE_CALL || SSshuttle.emergency.mode == SHUTTLE_RECALL)
- if(GLOB.security_level == SEC_LEVEL_GREEN)
- SSshuttle.emergency.modTimer(0.25)
- else if(GLOB.security_level == SEC_LEVEL_BLUE)
- SSshuttle.emergency.modTimer(0.5)
-
- GLOB.security_level = level
- SEND_GLOBAL_SIGNAL(COMSIG_GLOB_SECURITY_ALERT_CHANGE, level)
- SSblackbox.record_feedback("tally", "security_level_changes", 1, get_security_level())
- SSnightshift.check_nightshift()
-
-/proc/get_security_level()
- switch(GLOB.security_level)
- if(SEC_LEVEL_GREEN)
- return "green"
- if(SEC_LEVEL_BLUE)
- return "blue"
- if(SEC_LEVEL_RED)
- return "red"
- if(SEC_LEVEL_DELTA)
- return "delta"
-
-/proc/num2seclevel(num)
- switch(num)
- if(SEC_LEVEL_GREEN)
- return "green"
- if(SEC_LEVEL_BLUE)
- return "blue"
- if(SEC_LEVEL_RED)
- return "red"
- if(SEC_LEVEL_DELTA)
- return "delta"
-
-/proc/seclevel2num(seclevel)
- switch( lowertext(seclevel) )
- if("green")
- return SEC_LEVEL_GREEN
- if("blue")
- return SEC_LEVEL_BLUE
- if("red")
- return SEC_LEVEL_RED
- if("delta")
- return SEC_LEVEL_DELTA
diff --git a/code/modules/shuttle/emergency.dm b/code/modules/shuttle/emergency.dm
index 495a6bd7bc033..780b3787047fc 100644
--- a/code/modules/shuttle/emergency.dm
+++ b/code/modules/shuttle/emergency.dm
@@ -320,16 +320,10 @@
. = ..()
/obj/docking_port/mobile/emergency/request(obj/docking_port/stationary/S, area/signalOrigin, reason, redAlert, set_coefficient=null)
- if(!isnum_safe(set_coefficient))
- var/security_num = seclevel2num(get_security_level())
- switch(security_num)
- if(SEC_LEVEL_GREEN)
- set_coefficient = 2
- if(SEC_LEVEL_BLUE)
- set_coefficient = 1
- else
- set_coefficient = 0.5
- var/call_time = SSshuttle.emergencyCallTime * set_coefficient * engine_coeff
+ if(!isnum(set_coefficient))
+ set_coefficient = SSsecurity_level.current_security_level.shuttle_call_time_mod
+ alert_coeff = set_coefficient
+ var/call_time = SSshuttle.emergencyCallTime * alert_coeff * engine_coeff
switch(mode)
// The shuttle can not normally be called while "recalling", so
// if this proc is called, it's via admin fiat
@@ -587,7 +581,7 @@
var/obj/machinery/computer/shuttle_flight/C = getControlConsole()
if(!istype(C, /obj/machinery/computer/shuttle_flight/pod))
return ..()
- if(GLOB.security_level >= SEC_LEVEL_RED || (C && (C.obj_flags & EMAGGED)))
+ if(SSsecurity_level.get_current_level_as_number() >= SEC_LEVEL_RED || (C && (C.obj_flags & EMAGGED)))
if(launch_status == UNLAUNCHED)
launch_status = EARLY_LAUNCHED
return ..()
@@ -619,11 +613,7 @@
/obj/machinery/computer/shuttle_flight/pod/Initialize()
. = ..()
- RegisterSignal(SSdcs, COMSIG_GLOB_SECURITY_ALERT_CHANGE, PROC_REF(handle_alert))
-
-/obj/machinery/computer/shuttle_flight/pod/proc/handle_alert(datum/source, new_alert)
- SIGNAL_HANDLER
- admin_controlled = (new_alert < SEC_LEVEL_RED) // admin_controlled is FALSE if its red or delta
+ RegisterSignal(SSsecurity_level, COMSIG_SECURITY_LEVEL_CHANGED, PROC_REF(check_lock))
/obj/machinery/computer/shuttle_flight/pod/update_icon()
return
@@ -637,6 +627,20 @@
if(recall_docking_port_id == initial(recall_docking_port_id) || override)
recall_docking_port_id = "pod_lavaland[idnum]"
+/**
+ * Signal handler for checking if we should lock or unlock escape pods accordingly to a newly set security level
+ *
+ * Arguments:
+ * * source The datum source of the signal
+ * * new_level The new security level that is in effect
+ */
+/obj/machinery/computer/shuttle_flight/pod/proc/check_lock(datum/source, new_level)
+ SIGNAL_HANDLER
+
+ if(obj_flags & EMAGGED)
+ return
+ admin_controlled = (new_level < SEC_LEVEL_RED)
+
/obj/docking_port/stationary/random
name = "escape pod"
id = "pod"
@@ -728,7 +732,7 @@
/obj/item/storage/pod/can_interact(mob/user)
if(!..())
return FALSE
- if(GLOB.security_level >= SEC_LEVEL_RED || unlocked)
+ if(SSsecurity_level.get_current_level_as_number() >= SEC_LEVEL_RED || unlocked)
return TRUE
to_chat(user, "The storage unit will only unlock during a Red or Delta security alert.")
diff --git a/code/modules/shuttle/shuttle.dm b/code/modules/shuttle/shuttle.dm
index 1bbfc0f1b3025..a8461d69d042d 100644
--- a/code/modules/shuttle/shuttle.dm
+++ b/code/modules/shuttle/shuttle.dm
@@ -309,6 +309,8 @@ GLOBAL_LIST_INIT(shuttle_turf_blacklist, typecacheof(list(
var/list/shuttle_areas
+ ///Speed multiplier based on station alert level
+ var/alert_coeff = ALERT_COEFF_BLUE
///used as a timer (if you want time left to complete move, use timeLeft proc)
var/timer
var/last_timer_length
@@ -939,6 +941,20 @@ GLOBAL_LIST_INIT(shuttle_turf_blacklist, typecacheof(list(
last_timer_length *= multiple
setTimer(time_remaining)
+/obj/docking_port/mobile/proc/alert_coeff_change(new_coeff)
+ if(isnull(new_coeff))
+ return
+
+ var/time_multiplier = new_coeff / alert_coeff
+ var/time_remaining = timer - world.time
+ if(time_remaining < 0 || !last_timer_length)
+ return
+
+ time_remaining *= time_multiplier
+ last_timer_length *= time_multiplier
+ alert_coeff = new_coeff
+ setTimer(time_remaining)
+
/obj/docking_port/mobile/proc/invertTimer()
if(!last_timer_length)
return
diff --git a/code/modules/unit_tests/security_levels.dm b/code/modules/unit_tests/security_levels.dm
new file mode 100644
index 0000000000000..eecef51ff99c9
--- /dev/null
+++ b/code/modules/unit_tests/security_levels.dm
@@ -0,0 +1,16 @@
+/**
+ * Security Level Unit Test
+ *
+ * This test is here to ensure there are no security levels with the same name or number level. Having the same name or number level will cause problems.
+ */
+/datum/unit_test/security_levels
+
+/datum/unit_test/security_levels/Run()
+ var/list/comparison = subtypesof(/datum/security_level)
+
+ for(var/datum/security_level/iterating_level in comparison)
+ for(var/datum/security_level/iterating_level_check in comparison)
+ if(iterating_level == iterating_level_check) // If they are the same type, don't check
+ continue
+ TEST_ASSERT_NOTEQUAL(iterating_level.name, iterating_level_check.name, "Security level [iterating_level] has the same name as [iterating_level_check]!")
+ TEST_ASSERT_NOTEQUAL(iterating_level.number_level, iterating_level_check.number_level, "Security level [iterating_level] has the same level number as [iterating_level_check]!")