From cbd3ab0da27ff61d5aba72fa2d4714e82e607011 Mon Sep 17 00:00:00 2001 From: Batrachophreno Date: Fri, 5 Jun 2026 19:22:00 -0400 Subject: [PATCH] More TGUI Hotfixes (#22632) changes: - bugfix: "Fixes LateJoin and Manifest interfaces not displaying department colors by creating a shared component for both interfaces to use." - bugfix: "Fixes the Manifest interface not displaying colors for crew status." - bugfix: "Fixes status composer not allowing AIs to view TGUIs." - bugfix: "Fixes inline icons in alt-click atom viewer." - bugfix: "Fixes PDA light toggle icon not appearing." - bugfix: "Fixes autolathe category view not filtering items." --------- Signed-off-by: Batrachophreno --- code/controllers/subsystems/records.dm | 2 +- code/modules/tgui/states/default.dm | 10 +++++++- code/modules/tgui/status_composers.dm | 28 ++++++++++++++++++++--- html/changelogs/Bat-TGUIHotfixes2.yml | 3 +++ html/statbrowser.css | 22 ++++++++++++++++++ html/statbrowser.js | 19 +++++++-------- tgui/packages/tgui/layouts/NtosWindow.tsx | 13 +++++++++++ 7 files changed, 83 insertions(+), 14 deletions(-) diff --git a/code/controllers/subsystems/records.dm b/code/controllers/subsystems/records.dm index 07679c4e122..d20e39211f5 100644 --- a/code/controllers/subsystems/records.dm +++ b/code/controllers/subsystems/records.dm @@ -213,7 +213,7 @@ SUBSYSTEM_DEF(records) return GLOB.always_state /datum/controller/subsystem/records/ui_status(mob/user, datum/ui_state/state) - return (isnewplayer(user) || isghost(user) || issilicon(user)) ? UI_INTERACTIVE : UI_CLOSE + return (isnewplayer(user) || isghost(user) || tgui_silicon_user(user)) ? UI_INTERACTIVE : UI_CLOSE /datum/controller/subsystem/records/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state) . = ..() diff --git a/code/modules/tgui/states/default.dm b/code/modules/tgui/states/default.dm index 3e02f893958..475131803ae 100644 --- a/code/modules/tgui/states/default.dm +++ b/code/modules/tgui/states/default.dm @@ -18,6 +18,13 @@ GLOBAL_DATUM_INIT(default_state, /datum/ui_state/default, new) /mob/proc/default_can_use_topic(src_object) return UI_CLOSE // Don't allow interaction by default. +/mob/abstract/eye/freelook/aiEye/default_can_use_topic(src_object) + if(!isAI(owner)) + return UI_CLOSE + + var/mob/living/silicon/ai/ai = owner + return ai.default_can_use_topic(src_object) + /mob/living/default_can_use_topic(src_object) . = shared_ui_interaction(src_object) if(. > UI_CLOSE && loc) //must not be in nullspace. @@ -45,7 +52,8 @@ GLOBAL_DATUM_INIT(default_state, /datum/ui_state/default, new) return UI_INTERACTIVE // The AI can interact with anything it can see nearby, or with cameras while wireless control is enabled. - if(!control_disabled && can_see(src_object)) + var/atom/interaction_source = eyeobj || src + if(!control_disabled && can_see(interaction_source, src_object)) return UI_INTERACTIVE return UI_CLOSE diff --git a/code/modules/tgui/status_composers.dm b/code/modules/tgui/status_composers.dm index 8f60e03aa6a..2ca997bb7a0 100644 --- a/code/modules/tgui/status_composers.dm +++ b/code/modules/tgui/status_composers.dm @@ -11,6 +11,16 @@ ) ) +/// Returns the silicon mob represented by a TGUI user, including an AI using its camera eye. +/proc/tgui_silicon_user(mob/user) + if(issilicon(user)) + return user + + if(istype(user, /mob/abstract/eye/freelook/aiEye)) + var/mob/abstract/eye/freelook/aiEye/eye = user + if(issilicon(eye.owner)) + return eye.owner + /// Returns a UI status such that users adjacent to source will be able to interact, /// far away users will be able to see, and anyone farther won't see anything. /// Dead users will receive updates no matter what, though you likely want to add @@ -24,6 +34,9 @@ /// Returns a UI status such that the dead will be able to watch, but not interact. /proc/ui_status_only_living(mob/user, source) + if(tgui_silicon_user(user)) + return UI_INTERACTIVE + if (isliving(user)) return UI_INTERACTIVE @@ -49,25 +62,33 @@ /// Being dead will disable UI, being incapacitated will continue updating it, /// and anything else will make it interactive. /proc/ui_status_user_is_abled(mob/user, atom/source) + var/mob/living/silicon/silicon_user = tgui_silicon_user(user) + if(silicon_user && silicon_user != user) + return silicon_user.shared_ui_interaction(source) return user.shared_ui_interaction(source) /// Returns a UI status such that those without blocked hands will be able to interact, /// but everyone else can only watch. /proc/ui_status_user_has_free_hands(mob/user, atom/source) + if(tgui_silicon_user(user)) + return UI_INTERACTIVE return user.use_check() ? UI_UPDATE : UI_INTERACTIVE /// Returns a UI status such that advanced tool users will be able to interact, /// but everyone else can only watch. /proc/ui_status_user_is_advanced_tool_user(mob/user) + var/mob/living/silicon/silicon_user = tgui_silicon_user(user) + if(silicon_user) + return silicon_user.IsAdvancedToolUser() ? UI_INTERACTIVE : UI_UPDATE return user.IsAdvancedToolUser() ? UI_INTERACTIVE : UI_UPDATE /// Returns a UI status such that silicons will be able to interact with whatever /// they would have access to if this was a machine. For example, AIs can /// interact if there's cameras with wireless control is enabled. /proc/ui_status_silicon_has_access(mob/user, atom/source) - if (!issilicon(user)) + var/mob/living/silicon/silicon_user = tgui_silicon_user(user) + if(!silicon_user) return UI_CLOSE - var/mob/living/silicon/silicon_user = user return silicon_user.get_ui_access(source) /// Returns a UI status representing this silicon's capability to access @@ -87,7 +108,8 @@ return UI_INTERACTIVE // The AI can interact with anything it can see nearby, or with cameras while wireless control is enabled. - if(!control_disabled && can_see(source)) + var/atom/interaction_source = eyeobj || src + if(!control_disabled && can_see(interaction_source, source)) return UI_INTERACTIVE return UI_CLOSE diff --git a/html/changelogs/Bat-TGUIHotfixes2.yml b/html/changelogs/Bat-TGUIHotfixes2.yml index e967e332d8e..ae9154ea48e 100644 --- a/html/changelogs/Bat-TGUIHotfixes2.yml +++ b/html/changelogs/Bat-TGUIHotfixes2.yml @@ -3,3 +3,6 @@ delete-after: True changes: - bugfix: "Fixes LateJoin and Manifest interfaces not displaying department colors by creating a shared component for both interfaces to use." - bugfix: "Fixes the Manifest interface not displaying colors for crew status." + - bugfix: "Fixes status composer not allowing AIs to view TGUIs." + - bugfix: "Fixes inline icons in alt-click atom viewer." + - bugfix: "Fixes PDA light toggle icon not appearing." diff --git a/html/statbrowser.css b/html/statbrowser.css index 0e1a67db22a..07704633be2 100644 --- a/html/statbrowser.css +++ b/html/statbrowser.css @@ -37,6 +37,28 @@ img { image-rendering: pixelated; } +.listed-turf-list { + display: flex; + flex-direction: column; + gap: 0.25em; +} + +.listed-turf-item { + display: inline-flex; + align-items: center; + gap: 0.35em; + width: 100%; + cursor: pointer; +} + +.listed-turf-item img { + flex: 0 0 auto; +} + +.listed-turf-item .link { + min-width: 0; +} + .stat-container { display: flex; flex-direction: column; diff --git a/html/statbrowser.js b/html/statbrowser.js index 2292f199096..d9a54fb4bfc 100644 --- a/html/statbrowser.js +++ b/html/statbrowser.js @@ -443,7 +443,8 @@ function iconError(e) { function draw_listedturf() { statcontentdiv.textContent = ''; - var table = document.createElement('table'); + var list = document.createElement('div'); + list.className = 'listed-turf-list'; for (var i = 0; i < turfcontents.length; i++) { var part = turfcontents[i]; var clickfunc = ((part) => { @@ -474,31 +475,31 @@ function draw_listedturf() { window.location.href = clickcatcher; }; })(part); + var row = document.createElement('div'); + row.className = 'listed-turf-item'; + row.onmousedown = clickfunc; if (storedimages[part[1]] == null && part[2]) { var img = document.createElement('img'); img.src = part[2]; img.id = part[1]; storedimages[part[1]] = part[2]; img.onerror = iconError; - img.onmousedown = clickfunc; - table.appendChild(img); + row.appendChild(img); } else { var img = document.createElement('img'); img.onerror = iconError; - img.onmousedown = clickfunc; img.src = storedimages[part[1]]; img.id = part[1]; - table.appendChild(img); + row.appendChild(img); } var b = document.createElement('div'); var clickcatcher = ''; b.className = 'link'; - b.onmousedown = clickfunc; b.textContent = part[0]; - table.appendChild(b); - table.appendChild(document.createElement('br')); + row.appendChild(b); + list.appendChild(row); } - document.getElementById('statcontent').appendChild(table); + document.getElementById('statcontent').appendChild(list); } function remove_listedturf() { diff --git a/tgui/packages/tgui/layouts/NtosWindow.tsx b/tgui/packages/tgui/layouts/NtosWindow.tsx index 3e674a611b9..c4b2fd8aa99 100644 --- a/tgui/packages/tgui/layouts/NtosWindow.tsx +++ b/tgui/packages/tgui/layouts/NtosWindow.tsx @@ -24,6 +24,8 @@ export type NTOSData = { PC_batteryicon: string | null; PC_batterypercent: string | null; PC_device_theme: string; + PC_haslight: BooleanLike; + PC_lighton: BooleanLike; PC_lowpower_mode: BooleanLike; PC_ntneticon: string; PC_programheaders: Program[]; @@ -58,6 +60,8 @@ export const NtosWindow = (props) => { PC_device_theme, PC_batteryicon, PC_batterypercent, + PC_haslight, + PC_lighton, PC_ntneticon, PC_stationdate, PC_stationtime, @@ -114,6 +118,15 @@ export const NtosWindow = (props) => { {PC_batterypercent} )} + {!!PC_haslight && ( +