Skip to content

Commit

Permalink
Refactor: refactor security levels (ParadiseSS13#21899)
Browse files Browse the repository at this point in the history
* refactor: datumize security levels

* refactor: implement `SSsecurity_level` for handling security level related stuff

* feat: returns back old `delta_alarm` sound

* refactor: adjust existing code to use `SSsecurity_level`

* fix: remove redundunt new init order

* fix: fix type in var

* refactor: apply reviewer changes

* fix: replace `can_fire=FALSE` with `ss_flags = SS_NO_FIRE`, as subsystem will never fire

* fix: use `flags` instead of `ss_flags` for subsystem

Co-authored-by: SteelSlayer <[email protected]>

* fix: replace old security level interactions

* feat: implement `Recover` proc for `SSsecurity_level`

* refactor: add clearer doc for `security_level_set_timer_id`  propery of `SSsecurirt_level`

* refactor: swap `security_level` datum properties to make it clearer to read

* refactor: move initialization code from `New` to `Initialize` for `/obj/machinery/firealarm`

* fix: revert back `delta_alarm` annoing sound, use `delta_claxon` on change to delta security level

---------

Co-authored-by: SteelSlayer <[email protected]>
  • Loading branch information
Gaxeer and SteelSlayer authored Oct 14, 2023
1 parent 6a2704a commit eda2102
Show file tree
Hide file tree
Showing 29 changed files with 450 additions and 321 deletions.
5 changes: 5 additions & 0 deletions code/__DEFINES/dcs/signals.dm
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,11 @@
///from SSsun when the sun changes position : (azimuth)
#define COMSIG_SUN_MOVED "sun_moved"

///from SSsecurity_level on planning security level change : (previous_level_number, new_level_number)
#define COMSIG_SECURITY_LEVEL_CHANGE_PLANNED "security_level_change_planned"
///from SSsecurity_level when the security level changes : (previous_level_number, new_level_number)
#define COMSIG_SECURITY_LEVEL_CHANGED "security_level_changed"

//////////////////////////////////////////////////////////////////

// /datum signals
Expand Down
2 changes: 1 addition & 1 deletion code/controllers/subsystem/SSnightshift.dm
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ SUBSYSTEM_DEF(nightshift)
/datum/controller/subsystem/nightshift/proc/check_nightshift(check_canfire=FALSE)
if(check_canfire && !can_fire)
return
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)
Expand Down
158 changes: 158 additions & 0 deletions code/controllers/subsystem/SSsecurity_level.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
#define DEFAULT_SECURITY_LEVEL_NUMBER SEC_LEVEL_GREEN
#define DEFAULT_SECURITY_LEVEL_NAME "green"

GLOBAL_DATUM_INIT(security_announcement, /datum/announcer, new(config_type = /datum/announcement_configuration/security))

SUBSYSTEM_DEF(security_level)
name = "Security Level"
flags = SS_NO_FIRE
/// Option reference of a timer id of the latest set security level. Only set when security level is changed to one with `set_delay` > 0
var/security_level_set_timer_id
/// 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()
if (!length(available_levels))
for(var/security_level_type in subtypesof(/datum/security_level))
var/datum/security_level/new_security_level = new security_level_type
available_levels[new_security_level.name] = new_security_level

if (!current_security_level)
current_security_level = available_levels[number_level_to_text(DEFAULT_SECURITY_LEVEL_NUMBER)]

/datum/controller/subsystem/security_level/Recover()
security_level_set_timer_id = SSsecurity_level.security_level_set_timer_id
current_security_level = SSsecurity_level.current_security_level
available_levels = SSsecurity_level.available_levels

/**
* 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, could be number or name of security level
*/
/datum/controller/subsystem/security_level/proc/set_level(new_level)
var/new_level_name = istext(new_level) ? new_level : number_level_to_text(new_level)
if(new_level_name == current_security_level.name)
return

var/datum/security_level/selected_level = available_levels[new_level_name]

if(!selected_level)
CRASH("set_level was called with an invalid security level([new_level_name])")

if(security_level_set_timer_id)
deltimer(security_level_set_timer_id)
security_level_set_timer_id = null

pre_set_level(selected_level)

if(selected_level.set_delay > 0)
SEND_SIGNAL(src, COMSIG_SECURITY_LEVEL_CHANGE_PLANNED, current_security_level.number_level, selected_level.number_level)
security_level_set_timer_id = addtimer(CALLBACK(src, PROC_REF(do_set_level), selected_level), selected_level.set_delay, TIMER_UNIQUE | TIMER_STOPPABLE)
else
do_set_level(selected_level)

/**
* Do things before the actual security level set, like executing security level specific pre change behavior
*
* Arguments:
* * selected_level - The datum of security level selected to be changed to
*/
/datum/controller/subsystem/security_level/proc/pre_set_level(datum/security_level/selected_level)
PRIVATE_PROC(TRUE)

selected_level.pre_change()

/**
* Actually sets the security level after the announcement
*
* Sends `COMSIG_SECURITY_LEVEL_CHANGED` in the end
*
* Arguments:
* * selected_level - The datum of security level selected to be changed to
*/
/datum/controller/subsystem/security_level/proc/do_set_level(datum/security_level/selected_level)
PRIVATE_PROC(TRUE)

var/datum/security_level/previous_security_level = current_security_level
if(previous_security_level.number_level < SEC_LEVEL_RED && selected_level.number_level >= SEC_LEVEL_RED)
// Mark down this time to prevent shuttle cheese
SSshuttle.emergency_sec_level_time = world.time

announce_security_level(selected_level)
current_security_level = selected_level

post_status(current_security_level.status_display_mode, current_security_level.status_display_data)
SSnightshift.check_nightshift()
SSblackbox.record_feedback("tally", "security_level_changes", 1, selected_level.name)

SEND_SIGNAL(src, COMSIG_SECURITY_LEVEL_CHANGED, previous_security_level.number_level, selected_level.number_level)

/**
* 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)
GLOB.security_announcement.Announce(
selected_level.elevating_to_announcement_text,
selected_level.elevating_to_announcement_title,
new_sound = selected_level.elevating_to_sound,
new_sound2 = selected_level.ai_announcement_sound)
else
GLOB.security_announcement.Announce(
selected_level.lowering_to_announcement_text,
selected_level.lowering_to_announcement_title,
new_sound = selected_level.lowering_to_sound,
new_sound2 = selected_level.ai_announcement_sound)

/**
* Returns the current security level as a number
* In case the subsystem hasn't finished initializing yet, returns default security level
*/
/datum/controller/subsystem/security_level/proc/get_current_level_as_number()
return ((!initialized || !current_security_level) ? DEFAULT_SECURITY_LEVEL_NUMBER : current_security_level.number_level)

/**
* Returns the current security level as text
*/
/datum/controller/subsystem/security_level/proc/get_current_level_as_text()
return ((!initialized || !current_security_level) ? DEFAULT_SECURITY_LEVEL_NAME : 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/level_text in available_levels)
var/datum/security_level/security_level = available_levels[level_text]
if(security_level.number_level == number_level)
return security_level.name

/**
* Returns security level name formatted with it's color
*/
/datum/controller/subsystem/security_level/proc/get_colored_current_security_level_name()
return "<font color='[current_security_level.color]'>[current_security_level.name]</font>"

#undef DEFAULT_SECURITY_LEVEL_NUMBER
#undef DEFAULT_SECURITY_LEVEL_NAME
4 changes: 2 additions & 2 deletions code/controllers/subsystem/SSshuttles.dm
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ SUBSYSTEM_DEF(shuttle)

var/area/signal_origin = get_area(user)
var/emergency_reason = "\nNature of emergency:\n\n[call_reason]"
if(seclevel2num(get_security_level()) >= SEC_LEVEL_RED) // There is a serious threat we gotta move no time to give them five minutes.
if(SSsecurity_level.get_current_level_as_number() >= SEC_LEVEL_RED) // There is a serious threat we gotta move no time to give them five minutes.
var/extra_minutes = 0
var/priority_time = emergencyCallTime * 0.5
if(world.time - emergency_sec_level_time < priority_time)
Expand Down Expand Up @@ -152,7 +152,7 @@ SUBSYSTEM_DEF(shuttle)
return
if(!emergency.canRecall)
return
if(seclevel2num(get_security_level()) >= SEC_LEVEL_RED)
if(SSsecurity_level.get_current_level_as_number() >= SEC_LEVEL_RED)
if(emergency.timeLeft(1) < emergencyCallTime * 0.25)
return
else
Expand Down
8 changes: 8 additions & 0 deletions code/game/area/areas.dm
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,9 @@


/area/Initialize(mapload)
if(is_station_level(z))
RegisterSignal(SSsecurity_level, COMSIG_SECURITY_LEVEL_CHANGED, PROC_REF(on_security_level_update))

GLOB.all_areas += src
icon_state = ""
layer = AREA_LAYER
Expand Down Expand Up @@ -116,6 +119,11 @@

return INITIALIZE_HINT_LATELOAD

/area/proc/on_security_level_update(datum/source, previous_level_number, new_level_number)
SIGNAL_HANDLER

area_emergency_mode = (new_level_number >= SEC_LEVEL_EPSILON)

/area/proc/create_powernet()
powernet = new()
powernet.powernet_area = src
Expand Down
2 changes: 1 addition & 1 deletion code/game/gamemodes/malfunction/Malf_Modules.dm
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,7 @@
/datum/action/innate/ai/nuke_station/proc/set_us_up_the_bomb()
to_chat(owner_AI, "<span class='notice'>Nuclear device armed.</span>")
GLOB.major_announcement.Announce("Hostile runtimes detected in all station systems, please deactivate your AI to prevent possible damage to its morality core.", "Anomaly Alert", 'sound/AI/aimalf.ogg')
set_security_level("delta")
SSsecurity_level.set_level(SEC_LEVEL_DELTA)
owner_AI.nuking = TRUE
var/obj/machinery/doomsday_device/DOOM = new /obj/machinery/doomsday_device(owner_AI)
owner_AI.doomsday_device = DOOM
Expand Down
2 changes: 1 addition & 1 deletion code/game/gamemodes/nuclear/nuclear_challenge.dm
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@
return

GLOB.major_announcement.Announce(war_declaration, "Declaration of War", 'sound/effects/siren.ogg', msg_sanitized = TRUE)
addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(set_security_level), SEC_LEVEL_GAMMA), 30 SECONDS)
addtimer(CALLBACK(SSsecurity_level, TYPE_PROC_REF(/datum/controller/subsystem/security_level, set_level), SEC_LEVEL_GAMMA), 30 SECONDS)

to_chat(user, "You've attracted the attention of powerful forces within the syndicate. A bonus bundle of telecrystals has been granted to your team. Great things await you if you complete the mission.")
to_chat(user, "<b>Your bonus telecrystals have been split between your team's uplinks.</b>")
Expand Down
10 changes: 5 additions & 5 deletions code/game/gamemodes/nuclear/nuclearbomb.dm
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ GLOBAL_VAR(bomb_set)
r_code = rand(10000, 99999) // Creates a random code upon object spawn.
wires = new/datum/wires/nuclearbomb(src)
ADD_TRAIT(src, TRAIT_OBSCURED_WIRES, ROUNDSTART_TRAIT)
previous_level = get_security_level()
previous_level = SSsecurity_level.get_current_level_as_text()
GLOB.poi_list |= src
core = new /obj/item/nuke_core/plutonium(src)
STOP_PROCESSING(SSobj, core) //Let us not irradiate the vault by default.
Expand Down Expand Up @@ -480,7 +480,7 @@ GLOBAL_VAR(bomb_set)
safety = !(safety)
if(safety)
if(!is_syndicate)
set_security_level(previous_level)
SSsecurity_level.set_level(previous_level)
timing = FALSE
GLOB.bomb_set = FALSE
if("toggle_armed")
Expand All @@ -498,13 +498,13 @@ GLOBAL_VAR(bomb_set)
if(!safety)
message_admins("[key_name_admin(usr)] engaged a nuclear bomb [ADMIN_JMP(src)]")
if(!is_syndicate)
set_security_level("delta")
SSsecurity_level.set_level(SEC_LEVEL_DELTA)
GLOB.bomb_set = TRUE // There can still be issues with this resetting when there are multiple bombs. Not a big deal though for Nuke
else
GLOB.bomb_set = TRUE
else
if(!is_syndicate)
set_security_level(previous_level)
SSsecurity_level.set_level(previous_level)
GLOB.bomb_set = FALSE
if(!lighthack)
icon_state = "nuclearbomb1"
Expand Down Expand Up @@ -608,7 +608,7 @@ GLOBAL_VAR(bomb_set)
safety = !safety
if(safety == 1)
if(!is_syndicate)
set_security_level(previous_level)
SSsecurity_level.set_level(previous_level)
visible_message("<span class='notice'>[src] quiets down.</span>")
if(!lighthack)
if(icon_state == "nuclearbomb2")
Expand Down
20 changes: 10 additions & 10 deletions code/game/machinery/computer/communications.dm
Original file line number Diff line number Diff line change
Expand Up @@ -61,15 +61,15 @@

/obj/machinery/computer/communications/proc/change_security_level(new_level)
tmp_alertlevel = new_level
var/old_level = GLOB.security_level
var/old_level = SSsecurity_level.get_current_level_as_number()
if(!tmp_alertlevel) tmp_alertlevel = SEC_LEVEL_GREEN
if(tmp_alertlevel < SEC_LEVEL_GREEN) tmp_alertlevel = SEC_LEVEL_GREEN
if(tmp_alertlevel > SEC_LEVEL_BLUE) tmp_alertlevel = SEC_LEVEL_BLUE //Cannot engage delta with this
set_security_level(tmp_alertlevel)
if(GLOB.security_level != old_level)
SSsecurity_level.set_level(tmp_alertlevel)
if(SSsecurity_level.get_current_level_as_number() != old_level)
//Only notify the admins if an actual change happened
log_game("[key_name(usr)] has changed the security level to [get_security_level()].")
message_admins("[key_name_admin(usr)] has changed the security level to [get_security_level()].")
log_game("[key_name(usr)] has changed the security level to [SSsecurity_level.get_current_level_as_text()].")
message_admins("[key_name_admin(usr)] has changed the security level to [SSsecurity_level.get_current_level_as_text()].")
tmp_alertlevel = 0

/obj/machinery/computer/communications/ui_act(action, params)
Expand Down Expand Up @@ -132,7 +132,7 @@
var/obj/item/card/id/I = H.get_idcard(TRUE)
if(istype(I))
// You must have captain access and it must be red alert or lower (no getting off delta/epsilon)
if((ACCESS_CAPTAIN in I.access) && GLOB.security_level <= SEC_LEVEL_RED)
if((ACCESS_CAPTAIN in I.access) && SSsecurity_level.get_current_level_as_number() <= SEC_LEVEL_RED)
change_security_level(text2num(params["level"]))
else
to_chat(usr, "<span class='warning'>You are not authorized to do this.</span>")
Expand Down Expand Up @@ -355,8 +355,8 @@
)
)

data["security_level"] = GLOB.security_level
switch(GLOB.security_level)
data["security_level"] = SSsecurity_level.get_current_level_as_number()
switch(SSsecurity_level.get_current_level_as_number())
if(SEC_LEVEL_GREEN)
data["security_level_color"] = "green";
if(SEC_LEVEL_BLUE)
Expand All @@ -365,7 +365,7 @@
data["security_level_color"] = "red";
else
data["security_level_color"] = "purple";
data["str_security_level"] = capitalize(get_security_level())
data["str_security_level"] = capitalize(SSsecurity_level.get_current_level_as_text())
data["levels"] = list(
list("id" = SEC_LEVEL_GREEN, "name" = "Green", "icon" = "dove"),
list("id" = SEC_LEVEL_BLUE, "name" = "Blue", "icon" = "eye"),
Expand Down Expand Up @@ -464,7 +464,7 @@
to_chat(user, "Under directive 7-10, [station_name()] is quarantined until further notice.")
return

if(seclevel2num(get_security_level()) >= SEC_LEVEL_RED) // There is a serious threat we gotta move no time to give them five minutes.
if(SSsecurity_level.get_current_level_as_number() >= SEC_LEVEL_RED) // There is a serious threat we gotta move no time to give them five minutes.
SSshuttle.emergency.canRecall = FALSE
SSshuttle.emergency.request(null, 0.5, null, " Automatic Crew Transfer", 1)
else
Expand Down
4 changes: 2 additions & 2 deletions code/game/machinery/defib_mount.dm
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
. = ..()
if(defib)
. += "<span class='notice'>There is a defib unit hooked up. Alt-click to remove it.<span>"
if(GLOB.security_level >= SEC_LEVEL_RED)
if(SSsecurity_level.get_current_level_as_number() >= SEC_LEVEL_RED)
. += "<span class='notice'>Due to a security situation, its locking clamps can be toggled by swiping any ID.</span>"
else
. += "<span class='notice'>Its locking clamps can be [clamps_locked ? "dis" : ""]engaged by swiping an ID with access.</span>"
Expand Down Expand Up @@ -101,7 +101,7 @@
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, "<span class='warning'>You can't engage the clamps on a defibrillator that isn't there.</span>")
return
Expand Down
13 changes: 13 additions & 0 deletions code/game/machinery/doors/airlock_types.dm
Original file line number Diff line number Diff line change
Expand Up @@ -419,6 +419,19 @@
hackProof = TRUE
aiControlDisabled = AICONTROLDISABLED_ON

/obj/machinery/door/airlock/highsecurity/red/Initialize(mapload)
. = ..()
if(is_station_level(z))
RegisterSignal(SSsecurity_level, COMSIG_SECURITY_LEVEL_CHANGED, PROC_REF(on_security_level_update))

/obj/machinery/door/airlock/highsecurity/red/proc/on_security_level_update(datum/source, previous_level_number, new_level_number)
SIGNAL_HANDLER

if(new_level_number >= SEC_LEVEL_RED)
unlock(TRUE)
else
lock(TRUE)

/obj/machinery/door/airlock/highsecurity/red/attackby(obj/C, mob/user, params)
if(!issilicon(user))
if(isElectrified())
Expand Down
Loading

0 comments on commit eda2102

Please sign in to comment.