From eed4d09cba91b135534fba202cee5c1edecec4ad Mon Sep 17 00:00:00 2001
From: Wallem <66052067+Wallemations@users.noreply.github.com>
Date: Wed, 26 Nov 2025 15:01:00 -0500
Subject: [PATCH] Abandoned crate UI (#94090)
## About The Pull Request
Gives abandoned crates a ui when you use a multitool on them
Each `Button` component has a tooltip explaining how the minigame works
& what the column it's in means
## Why It's Good For The Game
Now you don't need to scroll through chat to find all your previous
attempts
## Changelog
:cl: Wallem
qol: Replaces abandoned crate multitool interaction with a TGUI window
that shows ALL your previous attempts
/:cl:
---
.../abandoned_crates/abandoned_crates.dm | 37 +++++--
.../tgui/interfaces/AbandonedCrate.tsx | 101 ++++++++++++++++++
2 files changed, 127 insertions(+), 11 deletions(-)
create mode 100644 tgui/packages/tgui/interfaces/AbandonedCrate.tsx
diff --git a/code/game/objects/structures/crates_lockers/crates/abandoned_crates/abandoned_crates.dm b/code/game/objects/structures/crates_lockers/crates/abandoned_crates/abandoned_crates.dm
index 9301a93a3f5..009a8c6a843 100644
--- a/code/game/objects/structures/crates_lockers/crates/abandoned_crates/abandoned_crates.dm
+++ b/code/game/objects/structures/crates_lockers/crates/abandoned_crates/abandoned_crates.dm
@@ -7,12 +7,14 @@
base_icon_state = "securecrate"
integrity_failure = 0 //no breaking open the crate
var/code = null
- var/last_attempt = null
+ /// Associated list of previous attempts w/ bulls & cows
+ var/list/previous_attempts = list()
var/attempts = 10
var/code_length = 4
var/qdel_on_open = FALSE
var/spawned_loot = FALSE
tamperproof = 90
+ interaction_flags_atom = INTERACT_ATOM_ATTACK_HAND
divable = FALSE // Stop people from "diving into" the crate accidentally, and then detonating it.
@@ -127,6 +129,7 @@
spawn_loot()
tamperproof = 0 // set explosion chance to zero, so we dont accidently hit it with a multitool and instantly die
togglelock(user)
+ SStgui.close_user_uis(user, src)
return
if(!validate_input(input))
@@ -134,7 +137,7 @@
return
to_chat(user, span_warning("A red light flashes."))
- last_attempt = input
+ previous_attempts += list(bulls_and_cows(input))
attempts--
if(attempts <= 0)
@@ -160,16 +163,28 @@
attack_hand(user) //this helps you not blow up so easily by overriding unlocking which results in an immediate boom.
return CLICK_ACTION_SUCCESS
+/obj/structure/closet/crate/secure/loot/ui_interact(mob/user, datum/tgui/ui)
+ . = ..()
+
+ // Attempt to update tgui ui, open and update if needed.
+ ui = SStgui.try_update_ui(user, src, ui)
+ if(!ui)
+ ui = new(user, src, "AbandonedCrate", name)
+ ui.open()
+
+/obj/structure/closet/crate/secure/loot/ui_data(mob/user)
+ var/list/data = list()
+
+ data["previous_attempts"] = previous_attempts
+ data["attempts_left"] = attempts
+
+ return data
+
/obj/structure/closet/crate/secure/loot/multitool_act(mob/living/user, obj/item/tool)
if(!locked)
return
-
- to_chat(user, span_boldnotice("DECA-CODE LOCK REPORT:"))
- to_chat(user, span_warning("* Anti-Tamper Bomb will activate [attempts == 1 ? span_boldwarning("on next failed access attempt") : "after [span_boldwarning("[attempts]")] failed access attempts"]."))
-
- if(last_attempt)
- var/list/bulls_cows = bulls_and_cows(last_attempt)
- to_chat(user, span_notice("Last code attempt, [last_attempt], had [bulls_cows[1]] correct digits at correct positions and [bulls_cows[2]] correct digits at incorrect positions."))
+ if(Adjacent(user))
+ ui_interact(user)
return ITEM_INTERACT_SUCCESS
@@ -187,7 +202,7 @@
else if(findtext(code, guess_char))
cows++
- return list(bulls, cows)
+ return list("attempt" = guess, "bulls" = bulls, "cows" = cows)
/obj/structure/closet/crate/secure/loot/emag_act(mob/user, obj/item/card/emag/emag_card)
. = ..()
@@ -204,7 +219,7 @@
//reset the anti-tampering, number of attempts and last attempt when the lock is re-enabled.
tamperproof = initial(tamperproof)
attempts = initial(attempts)
- last_attempt = null
+ previous_attempts = list()
return
if(tamperproof)
return
diff --git a/tgui/packages/tgui/interfaces/AbandonedCrate.tsx b/tgui/packages/tgui/interfaces/AbandonedCrate.tsx
new file mode 100644
index 00000000000..3498eb5cfd1
--- /dev/null
+++ b/tgui/packages/tgui/interfaces/AbandonedCrate.tsx
@@ -0,0 +1,101 @@
+import { Box, Button, NoticeBox, Section, Table } from 'tgui-core/components';
+
+import { useBackend } from '../backend';
+import { Window } from '../layouts';
+
+type Data = {
+ previous_attempts: Attempts[];
+ attempts_left: number;
+};
+
+type Attempts = {
+ attempt: string;
+ bulls: number;
+ cows: number;
+};
+
+const check_attempts = (attempts_to_check: number) => {
+ return attempts_to_check === 1
+ ? 'on next failed access attempt.'
+ : `after ${attempts_to_check} failed access attempts.`;
+};
+
+const BULLS_COWS_INFO = `Codes are made of a series of non-repeating digits.
+Each wrong guess will return the number of correct digits in correct locations,
+and the number of correct digits in incorrect locations.`;
+
+export const AbandonedCrate = (props) => {
+ const { data } = useBackend();
+ const { previous_attempts, attempts_left } = data;
+
+ return (
+
+
+
+ }
+ >
+
+ Anti-Tamper Bomb will activate {check_attempts(attempts_left)}
+
+
+ {!!previous_attempts.length && (
+
+
+ Attempt
+
+
+
+
+
+
+
+
+ )}
+ {previous_attempts.map((previous_attempts) => (
+
+
+
+ {previous_attempts.attempt}
+
+
+
+
+ {previous_attempts.bulls}
+
+
+
+
+ {previous_attempts.cows}
+
+
+
+ ))}
+
+
+
+
+ );
+};