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 += "


" - if(screen == 1) + if(src.active_event == INACTIVE) dat += "Select an event to trigger:" show_browser(user, dat, "window=keycard_auth;size=500x250") - if(screen == 2) + else + var/event = "Standby" + switch (src.active_event) + if (CALL_ERT) + event = "Call Emergency Response Team" + if (GRANT_MAINT) + event = "Grant Emergency Maintenance Access" + if (REVOKE_MAINT) + event = "Revoke Emergency Maintenance Access" + if (GRANT_NUCLEAR_CODE) + event = "Grant Nuclear Authorization Code" + dat += "Please swipe your card to authorize the following event: [event]" - dat += "[event_additional_info ? "

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