diff --git a/baystation12.dme b/baystation12.dme
index d2903348526..703c1a526b4 100644
--- a/baystation12.dme
+++ b/baystation12.dme
@@ -2847,7 +2847,7 @@
#include "code\modules\research\designs\protolathe\dp-stock_parts.dm"
#include "code\modules\research\designs\protolathe\dp-surgery.dm"
#include "code\modules\research\designs\protolathe\dp-weapons.dm"
-#include "code\modules\security levels\keycard authentication.dm"
+#include "code\modules\security_levels\keycard_auth.dm"
#include "code\modules\shield_generators\floor_diffuser.dm"
#include "code\modules\shield_generators\handheld_diffuser.dm"
#include "code\modules\shield_generators\modes.dm"
diff --git a/code/modules/security levels/keycard authentication.dm b/code/modules/security_levels/keycard_auth.dm
similarity index 57%
rename from code/modules/security levels/keycard authentication.dm
rename to code/modules/security_levels/keycard_auth.dm
index 1776f8dc1a7..c62f554c39d 100644
--- a/code/modules/security levels/keycard authentication.dm
+++ b/code/modules/security_levels/keycard_auth.dm
@@ -1,25 +1,37 @@
+#define INACTIVE 0
+#define CALL_ERT 1
+#define GRANT_MAINT 2
+#define REVOKE_MAINT 3
+#define GRANT_NUCLEAR_CODE 4
+
/obj/machinery/keycard_auth
name = "Keycard Authentication Device"
desc = "This device is used to trigger functions which require more than one ID card to authenticate."
icon = 'icons/obj/monitors.dmi'
icon_state = "auth_off"
- var/active = 0 //This gets set to 1 on all devices except the one where the initial request was made.
- var/event = ""
- var/event_additional_info = ""
- var/screen = 1
- var/confirmed = 0 //This variable is set by the device that confirms the request.
+
+ // 3 vars here as booleans produce 4 relevant state:
+ // (!!auth_proxy)<<2 | (!!trigger_id)<<1 | (active_event != INACTIVE)
+ // 000 -- Standby, waiting for an event to be selected
+ // 001 -- Event selected, waiting for causer to swipe ID
+ // 011 -- Causer waits for confirmation
+ // 100 -- Wait for confirmer's ID
+ var/active_event = INACTIVE
+ var/event_reason = ""
+
+ var/obj/machinery/keycard_auth/auth_proxy
+ var/obj/item/card/id/trigger_id
+
var/confirm_delay = 3 SECONDS
- var/busy = 0 //Busy when waiting for authentication or an event request has been sent from this device.
- var/obj/machinery/keycard_auth/event_source
- var/obj/item/card/id/initial_card
- var/mob/event_triggered_by
- var/mob/event_confirmed_by
- //1 = select event
- //2 = authenticate
+
+ var/mob/triggered_by = null
+ var/mob/confirmed_by = null
+
anchored = 1.0
idle_power_usage = 2 WATTS
active_power_usage = 6 WATTS
power_channel = STATIC_ENVIRON
+
/// Whether we have active thinking to call ert or not
var/ert_context_thinking = FALSE
@@ -35,33 +47,81 @@
if(stat & (NOPOWER|BROKEN))
to_chat(user, SPAN_WARNING("This device is not powered."))
return
- if(istype(W,/obj/item/card/id))
- visible_message(SPAN_NOTICE("\The [user] swipes \the [W] through \the [src]."))
- var/obj/item/card/id/ID = W
- if(access_keycard_auth in ID.access)
- if(active)
- if(event_source && initial_card != ID)
- event_source.confirmed = 1
- event_source.event_confirmed_by = user
- else
- visible_message(SPAN_WARNING("\The [src] blinks and displays a message: Unable to confirm the event with the same card."), range=2)
- else if(screen == 2)
- event_triggered_by = user
- initial_card = ID
- broadcast_request() //This is the device making the initial event request. It needs to broadcast to other devices
+
+ if(istype(W, /obj/item/card/id))
+ var/obj/item/card/id/id = W
+
+ if (src.auth_proxy)
+ if (src.auth_proxy.try_confirm(id, user))
+ visible_message(SPAN_INFO("\The [src] blinks and displays a message: Confirmed."), range=2)
+ src.reset()
+ else
+ visible_message(SPAN_WARNING("\The [src] blinks and displays a message: Unable to confirm the event with the same card."), range=2)
+ else if (src.active_event == INACTIVE || src.trigger_id)
+ visible_message(SPAN_NOTICE("\The [user] swipes \the [id] through \the [src], but nothing happens."))
+ else if (!(access_keycard_auth in id.access))
+ visible_message(SPAN_NOTICE("\The [user] swipes \the [id] through \the [src], but it's declined."))
+ else
+ visible_message(SPAN_NOTICE("\The [user] swipes \the [id] through \the [src] and it's accepted."))
+ src.do_proxy(id, user)
+
+/obj/machinery/keycard_auth/proc/do_proxy(obj/item/card/id/trigger_id, mob/triggerer)
+ src.icon_state = "auth_on"
+ src.trigger_id = trigger_id
+ src.triggered_by = triggerer
+
+ for(var/obj/machinery/keycard_auth/KA in world)
+ if(KA == src)
+ continue
+
+ if (!KA.auth_proxy)
+ KA.auth_proxy = src
+ KA.icon_state = "auth_on"
+ spawn(src.confirm_delay)
+ if (KA.auth_proxy == src)
+ KA.reset()
+
+ sleep(src.confirm_delay)
+
+ if (src.trigger_id)
+ visible_message(SPAN_INFO("\The [src] blinks and displays a message: Confirmation failure."), range=2)
+
+ src.reset()
+
+/obj/machinery/keycard_auth/proc/try_confirm(obj/item/card/id/second_id, mob/confirmer)
+ if (!src.trigger_id || src.trigger_id == second_id)
+ return FALSE
+
+ visible_message(SPAN_INFO("\The [src] blinks and displays a message: Confirmed."), range=2)
+ log_game("[key_name(src.triggered_by)] triggered and [key_name(confirmer)] confirmed event [src.active_event]")
+ message_admins("[key_name(src.triggered_by)] triggered and [key_name(confirmer)] confirmed event [src.active_event]", 1)
+
+ src.cause_event()
+ src.reset()
+
+ return TRUE
//icon_state gets set everwhere besides here, that needs to be fixed sometime
/obj/machinery/keycard_auth/on_update_icon()
- if(stat &NOPOWER)
+ if(stat & NOPOWER)
icon_state = "auth_off"
+/obj/machinery/keycard_auth/proc/reset()
+ src.active_event = INACTIVE
+ src.event_reason = ""
+ src.icon_state = "auth_off"
+ src.auth_proxy = null
+ src.trigger_id = null
+ src.triggered_by = null
+ src.confirmed_by = null
+
/obj/machinery/keycard_auth/attack_hand(mob/user as mob)
if(stat & (NOPOWER|BROKEN))
to_chat(user, "This device is not powered.")
return
if(!user.IsAdvancedToolUser())
return 0
- if(busy)
+ if(src.trigger_id)
to_chat(user, "This device is busy.")
return
@@ -72,28 +132,38 @@
dat += "This device is used to trigger some high security events. It requires the simultaneous swipe of two high-level ID cards."
dat += "
Additional info: [event_additional_info]" : ""]"
+ if (src.event_reason)
+ dat += "
Reason: [src.event_reason]
"
dat += "
Back" show_browser(user, dat, "window=keycard_auth;size=500x250") return /obj/machinery/keycard_auth/CanUseTopic(mob/user, href_list) - if(busy) + if(src.auth_proxy) to_chat(user, "This device is busy.") return STATUS_CLOSE if(!user.IsAdvancedToolUser()) @@ -103,21 +173,25 @@ /obj/machinery/keycard_auth/OnTopic(user, href_list) if(href_list["triggerevent"]) - event = href_list["triggerevent"] - event_additional_info = "" - if(event == "Emergency Response Team") - event_additional_info = sanitize(input(user, "Enter call reason", "Call reason") as message, extra=FALSE) - event = "Emergency Response Team" - screen = 2 + src.active_event = text2num(href_list["triggerevent"]) + src.event_reason = "" + + if(src.active_event == CALL_ERT) + event_reason = sanitize(input(user, "Enter call reason", "Call reason") as message, extra=FALSE) + . = TOPIC_REFRESH + if(href_list["reset"]) - reset() + src.active_event = INACTIVE + src.event_reason = "" + . = TOPIC_REFRESH if(is_admin(user) && href_list["approve_ert"]) set_next_think_ctx("call_ert_context", 0) ert_context_thinking = FALSE call_ert() + if(is_admin(user) && href_list["prohibit_ert"]) set_next_think_ctx("call_ert_context", 0) ert_context_thinking = FALSE @@ -127,74 +201,26 @@ if(. == TOPIC_REFRESH) attack_hand(user) -/obj/machinery/keycard_auth/proc/reset() - active = 0 - event = "" - screen = 1 - confirmed = 0 - event_source = null - icon_state = "auth_off" - event_triggered_by = null - event_confirmed_by = null - initial_card = null - -/obj/machinery/keycard_auth/proc/broadcast_request() - icon_state = "auth_on" - for(var/obj/machinery/keycard_auth/KA in world) - if(KA == src) - continue - KA.reset() - KA.receive_request(src, initial_card) - - if(confirm_delay) - set_next_think(world.time + confirm_delay) - -/obj/machinery/keycard_auth/think() - if(confirmed) - confirmed = 0 - trigger_event(event) - log_game("[key_name(event_triggered_by)] triggered and [key_name(event_confirmed_by)] confirmed event [event]") - message_admins("[key_name(event_triggered_by)] triggered and [key_name(event_confirmed_by)] confirmed event [event]", 1) - reset() - -/obj/machinery/keycard_auth/proc/receive_request(obj/machinery/keycard_auth/source, obj/item/card/id/ID) if(stat & (BROKEN|NOPOWER)) return - event_source = source - initial_card = ID - busy = 1 - active = 1 - icon_state = "auth_on" - - sleep(confirm_delay) - event_source = null - initial_card = null - icon_state = "auth_off" - active = 0 - busy = 0 - -/obj/machinery/keycard_auth/proc/trigger_event() - switch(event) - if("Red alert") - var/decl/security_state/security_state = decls_repository.get_decl(GLOB.using_map.security_state) - security_state.set_security_level(security_state.high_security_level) - feedback_inc("alert_keycard_auth_red",1) - if("Grant Emergency Maintenance Access") +/obj/machinery/keycard_auth/proc/cause_event() + switch(src.active_event) + if(GRANT_MAINT) make_maint_all_access() feedback_inc("alert_keycard_auth_maintGrant",1) - if("Revoke Emergency Maintenance Access") + if(REVOKE_MAINT) revoke_maint_all_access() feedback_inc("alert_keycard_auth_maintRevoke",1) - if("Emergency Response Team") + if(CALL_ERT) if(ert_call_failure()) return if(!ert_context_thinking) visible_message(SPAN_NOTICE("\The [src] displays the message: The request has been created and the process of transferring the request to the emergency response service has been started, the approximate waiting time for processing is 2 minutes."), range=2) set_next_think_ctx("call_ert_context", world.time + 2 MINUTES) ert_context_thinking = TRUE - message_admins("An ERT call request was created with the reason:\n[event_additional_info].\nThis call will automatically be approved after 2 minutes. Approve. Reject.") - if("Grant Nuclear Authorization Code") + message_admins("An ERT call request was created with the reason:\n[src.event_reason].\nThis call will automatically be approved after 2 minutes. Approve. Reject.") + if(GRANT_NUCLEAR_CODE) var/obj/machinery/nuclearbomb/nuke = locate(/obj/machinery/nuclearbomb/station) in world if(nuke) visible_message(SPAN_WARNING("\The [src] blinks and displays a message: The nuclear authorization code is [nuke.r_code]"), range=2) @@ -218,7 +244,7 @@ return visible_message(SPAN_NOTICE("\The [src] displays the message: The request has been approved, the response team will be on facility shortly."), range=2) - trigger_armed_response_team(TRUE, event_additional_info) + trigger_armed_response_team(TRUE, src.event_reason) feedback_inc("alert_keycard_auth_ert",1) /obj/machinery/keycard_auth/proc/is_ert_blocked() @@ -241,3 +267,9 @@ var/global/maint_all_access = 0 if(maint_all_access && src.check_access_list(list(access_maint_tunnels))) return 1 return ..(M) + +#undef INACTIVE +#undef CALL_ERT +#undef GRANT_MAINT +#undef REVOKE_MAINT +#undef GRANT_NUCLEAR_CODE