diff --git a/code/game/objects/items/weapons/storage/secure.dm b/code/game/objects/items/weapons/storage/secure.dm index e069f12a6c0..3d530f35751 100644 --- a/code/game/objects/items/weapons/storage/secure.dm +++ b/code/game/objects/items/weapons/storage/secure.dm @@ -12,24 +12,28 @@ // ----------------------------- /obj/item/storage/secure name = "secstorage" - var/icon_locking = "secureb" - var/icon_sparking = "securespark" - var/icon_opened = "secure0" - var/locked = TRUE - var/code = "" - var/l_code = null - var/l_set = FALSE - var/l_setshort = FALSE - var/l_hacking = FALSE - var/open = FALSE w_class = WEIGHT_CLASS_NORMAL max_w_class = WEIGHT_CLASS_SMALL max_combined_w_class = 14 + var/icon_locking = "secureb" + var/icon_sparking = "securespark" + var/icon_opened = "secure0" + + /// Are we locked? + var/locked = TRUE + /// What is our code to open? + var/code + /// Is our hacking panel open? + var/panel_open = FALSE + /// What has the user entered to guess the code + var/user_entered_code + /// Stops people from spamming enter like an idiot + COOLDOWN_DECLARE(enter_spam) /obj/item/storage/secure/examine(mob/user) . = ..() if(in_range(user, src)) - . += "The service panel is [open ? "open" : "closed"]." + . += "The service panel is [panel_open ? "open" : "closed"]." /obj/item/storage/secure/populate_contents() new /obj/item/paper(src) @@ -40,23 +44,6 @@ if((istype(W, /obj/item/melee/energy/blade)) && (!emagged)) emag_act(user, W) - if(istype(W, /obj/item/multitool) && open && !l_hacking) - user.show_message("Now attempting to reset internal memory, please hold.", 1) - l_hacking = TRUE - if(do_after(usr, 100 * W.toolspeed, target = src)) - if(prob(40)) - l_setshort = TRUE - l_set = FALSE - user.show_message("Internal memory reset. Please give it a few seconds to reinitialize.", 1) - sleep(80) - l_setshort = FALSE - l_hacking = FALSE - else - user.show_message("Unable to reset internal memory.", 1) - l_hacking = FALSE - else - l_hacking = FALSE - return //At this point you have exhausted all the special things to do when locked // ... but it's still locked. return @@ -64,19 +51,32 @@ return ..() /obj/item/storage/secure/screwdriver_act(mob/living/user, obj/item/I) - if(do_after(user, 20 * I.toolspeed, target = src)) - open = !open - user.visible_message("[user] [open ? "opens" : "closes"] the service panel on [src].", "You [open ? "open" : "close"] the service panel.") + if(I.use_tool(src, user, 2 SECONDS * I.toolspeed, volume = 10)) + panel_open = !panel_open + user.visible_message("[user] [panel_open ? "opens" : "closes"] the service panel on [src].", "You [panel_open ? "open" : "close"] the service panel.") return TRUE -/obj/item/storage/secure/emag_act(user as mob, weapon as obj) +/obj/item/storage/secure/multitool_act(mob/living/user, obj/item/I) + if(!panel_open) + return + if(!I.use_tool(src, user, 0, volume = 0)) + return + . = TRUE + to_chat(user, "You start fiddling with the internal memory mechanisms.") + if(do_after_once(user, 10 SECONDS * I.toolspeed, target = src)) + if(prob(40)) + to_chat(user, "The screen dims, the internal memory seems to be reset.") + code = null + else + to_chat(user, "The screen flashes, and then goes back to normal.") + + +/obj/item/storage/secure/emag_act(user, weapon) if(!emagged) emagged = TRUE - overlays += image('icons/obj/storage.dmi', icon_sparking) - sleep(6) - overlays = null - overlays += image('icons/obj/storage.dmi', icon_locking) + flick_overlay_view(image(icon, src, icon_sparking), src, 0.9 SECONDS) locked = FALSE + update_icon(UPDATE_OVERLAYS) if(istype(weapon, /obj/item/melee/energy/blade)) do_sparks(5, 0, loc) playsound(loc, 'sound/weapons/blade1.ogg', 50, 1) @@ -84,7 +84,6 @@ to_chat(user, "You slice through the lock on [src].") else to_chat(user, "You short out the lock on [src].") - return /obj/item/storage/secure/AltClick(mob/user) if(!try_to_open()) @@ -103,74 +102,22 @@ return FALSE return TRUE -/obj/item/storage/secure/attack_self(mob/user as mob) - user.set_machine(src) - var/dat = text("[]
\n\nLock Status: []", src, (locked ? "LOCKED" : "UNLOCKED")) - var/message = "Code" - if(!l_set && !emagged && !l_setshort) - dat += text("

\n5-DIGIT PASSCODE NOT SET.
ENTER NEW PASSCODE.
") - if(emagged) - dat += text("

\nLOCKING SYSTEM ERROR - 1701") - if(l_setshort) - dat += text("

\nALERT: MEMORY SYSTEM ERROR - 6040 201") - message = text("[]", code) - if(!locked) - message = "*****" - dat += {"


\n>[message]
\n - 1- - 2- - 3
\n - 4- - 5- - 6
\n - 7- - 8- - 9
\n - R- - 0- - E
\n
"} - user << browse(dat, "window=caselock;size=300x280") - -/obj/item/storage/secure/Topic(href, href_list) - ..() - if(usr.incapacitated() || (get_dist(src, usr) > 1)) - return - if(href_list["type"]) - if(href_list["type"] == "E") - if(!l_set && length(code) == 5 && !l_setshort && code != "ERROR") - l_code = code - l_set = TRUE - else if(code == l_code && !emagged && l_set) - locked = FALSE - overlays = null - overlays += image('icons/obj/storage.dmi', icon_opened) - code = null - else - code = "ERROR" - else - if(href_list["type"] == "R" && !emagged && !l_setshort) - locked = TRUE - overlays = null - code = null - if(usr.s_active == src) - close(usr) - else - code += text("[]", href_list["type"]) - if(length(code) > 5) - code = "ERROR" - add_fingerprint(usr) - for(var/mob/M in viewers(1, loc)) - if((M.client && M.machine == src)) - attack_self(M) - return - return /obj/item/storage/secure/can_be_inserted(obj/item/W as obj, stop_messages = 0) if(!locked) return ..() if(!stop_messages) to_chat(usr, "[src] is locked!") - return 0 + return FALSE + +/obj/item/storage/secure/update_overlays() + . = ..() + if(isnull(code)) + return + if(locked) + . += icon_locking + else + . += icon_opened /obj/item/storage/secure/hear_talk(mob/living/M as mob, list/message_pieces) return @@ -178,6 +125,74 @@ /obj/item/storage/secure/hear_message(mob/living/M as mob, msg) return +/obj/item/storage/secure/attack_self(mob/user) + ui_interact(user) + +/obj/item/storage/secure/ui_interact(mob/user, ui_key, datum/tgui/ui, force_open, datum/tgui/master_ui, datum/ui_state/state) + ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) + if(!ui) + ui = new(user, src, ui_key, "SecureStorage", name, 275, 500, master_ui, state) + ui.open() + +/obj/item/storage/secure/ui_data(mob/user) + var/list/data = list() + data["locked"] = locked + data["user_entered_code"] = user_entered_code + data["no_passcode"] = isnull(code) + data["emagged"] = emagged + return data + +/obj/item/storage/secure/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state) + if(..()) + return + + add_fingerprint(usr) + + if(!COOLDOWN_FINISHED(src, enter_spam)) + return + + . = TRUE + + switch(action) + if("keypad") + if(emagged) + return FALSE + + var/digit = params["digit"] + switch(digit) + if("E") + if(isnull(code)) + if(length(user_entered_code) != 5) + return FALSE + code = user_entered_code + to_chat(ui.user, "You set the code to [code].") + locked = FALSE + else if(!locked) + locked = TRUE + to_chat(ui.user, "You lock [src].") + else if(user_entered_code == code) // correct code! + locked = FALSE + to_chat(ui.user, "You unlock [src].") + update_icon(UPDATE_OVERLAYS) + COOLDOWN_START(src, enter_spam, 0.1 SECONDS) + if("C") + user_entered_code = null + COOLDOWN_START(src, enter_spam, 0.1 SECONDS) + else + if(!isnum(text2num(digit))) + return FALSE + if(length(user_entered_code) >= 5) + return FALSE + user_entered_code = copytext("[user_entered_code][digit]", 1, 6) + + if("backspace") + if(emagged) + return FALSE + user_entered_code = copytext(user_entered_code, 1, length(user_entered_code)) + COOLDOWN_START(src, enter_spam, 0.1 SECONDS) + + playsound(src, "terminal_type", 10, 1) + // ----------------------------- // Secure Briefcase // ----------------------------- @@ -233,7 +248,7 @@ icon = 'icons/obj/storage.dmi' icon_state = "safe" icon_opened = "safe0" - icon_locking = "safeb" + icon_locking = null icon_sparking = "safespark" force = 8 w_class = WEIGHT_CLASS_HUGE @@ -243,4 +258,4 @@ cant_hold = list(/obj/item/storage/secure/briefcase) /obj/item/storage/secure/safe/attack_hand(mob/user as mob) - return attack_self(user) + ui_interact(user) diff --git a/tgui/packages/tgui/hotkeys.js b/tgui/packages/tgui/hotkeys.js index 36a21bc8a89..186905f41fb 100644 --- a/tgui/packages/tgui/hotkeys.js +++ b/tgui/packages/tgui/hotkeys.js @@ -48,6 +48,16 @@ export const KEY_W = 87; export const KEY_X = 88; export const KEY_Y = 89; export const KEY_Z = 90; +export const KEY_NUMPAD_0 = 96; +export const KEY_NUMPAD_1 = 97; +export const KEY_NUMPAD_2 = 98; +export const KEY_NUMPAD_3 = 99; +export const KEY_NUMPAD_4 = 100; +export const KEY_NUMPAD_5 = 101; +export const KEY_NUMPAD_6 = 102; +export const KEY_NUMPAD_7 = 103; +export const KEY_NUMPAD_8 = 104; +export const KEY_NUMPAD_9 = 105; export const KEY_EQUAL = 187; export const KEY_MINUS = 189; diff --git a/tgui/packages/tgui/index.js b/tgui/packages/tgui/index.js index a05d3907226..886247b7fbd 100644 --- a/tgui/packages/tgui/index.js +++ b/tgui/packages/tgui/index.js @@ -27,6 +27,7 @@ import './styles/themes/malfunction.scss'; import './styles/themes/ntos.scss'; import './styles/themes/retro.scss'; import './styles/themes/safe.scss'; +import './styles/themes/securestorage.scss'; import './styles/themes/security.scss'; import './styles/themes/syndicate.scss'; import './styles/themes/nologo.scss'; diff --git a/tgui/packages/tgui/interfaces/SecureStorage.js b/tgui/packages/tgui/interfaces/SecureStorage.js new file mode 100644 index 00000000000..350946413ae --- /dev/null +++ b/tgui/packages/tgui/interfaces/SecureStorage.js @@ -0,0 +1,124 @@ +import { classes } from 'common/react'; +import { useBackend } from '../backend'; +import { Box, Button, Flex, Section, Table } from '../components'; +import { Window } from '../layouts'; +import { TableCell, TableRow } from '../components/Table'; +import { + KEY_BACKSPACE, + KEY_ENTER, + KEY_ESCAPE, + KEY_0, + KEY_9, + KEY_NUMPAD_0, + KEY_NUMPAD_9, +} from '../hotkeys'; + +export const SecureStorage = (props, context) => { + return ( + + + + + + ); +}; + +const handleKeyCodeEvent = (e, context) => { + const { act } = useBackend(context); + const keyCode = window.event ? e.which : e.keyCode; + + if (keyCode === KEY_ENTER) { + e.preventDefault(); + act('keypad', { digit: 'E' }); + return; + } + if (keyCode === KEY_ESCAPE) { + e.preventDefault(); + act('keypad', { digit: 'C' }); + return; + } + if (keyCode === KEY_BACKSPACE) { + e.preventDefault(); + act('backspace'); + return; + } + if (keyCode >= KEY_0 && keyCode <= KEY_9) { + e.preventDefault(); + act('keypad', { digit: keyCode - KEY_0 }); + return; + } + if (keyCode >= KEY_NUMPAD_0 && keyCode <= KEY_NUMPAD_9) { + e.preventDefault(); + act('keypad', { digit: keyCode - KEY_NUMPAD_0 }); + return; + } +}; + +const MainPage = (props, context) => { + const { act, data } = useBackend(context); + const { locked, no_passcode, emagged, user_entered_code } = data; + + const keypadKeys = [ + ['1', '2', '3'], + ['4', '5', '6'], + ['7', '8', '9'], + ['C', '0', 'E'], + ]; + + const status = no_passcode ? '' : locked ? 'bad' : 'good'; + + return ( +
handleKeyCodeEvent(e, context)} + > + + + {emagged ? 'ERROR' : user_entered_code} + + + + {keypadKeys.map((keyColumn) => ( + + {keyColumn.map((key) => ( + + + + ))} + + ))} +
+
+ ); +}; + +const NumberButton = (props, context) => { + const { act, data } = useBackend(context); + const { number } = props; + + return ( +