From c7cbbfbfb83c8a5c259fed5432cd34e86443d167 Mon Sep 17 00:00:00 2001 From: Wildkins Date: Mon, 2 Mar 2026 04:59:22 -0500 Subject: [PATCH] SmartFridge Fixes (#21957) - moves smartfridge icon generation to ui_static_data and adds a cache (inspired by / ported in part from tgstation/tgstation#48394 ) - because we cache it, we can now gFI the items and so we fix certain things not having icons - bottlemaxxers stay winning, etc. i actually tested this pr and it works great, upvotes to the left image --- code/modules/cooking/machinery/smartfridge.dm | 85 ++++++--- html/changelogs/johnwildkins-smartfridge.yml | 14 ++ tgui/packages/tgui/interfaces/SmartFridge.tsx | 171 +++++++++--------- 3 files changed, 162 insertions(+), 108 deletions(-) create mode 100644 html/changelogs/johnwildkins-smartfridge.yml diff --git a/code/modules/cooking/machinery/smartfridge.dm b/code/modules/cooking/machinery/smartfridge.dm index 1d2e4ad6281..5b3f3a30918 100644 --- a/code/modules/cooking/machinery/smartfridge.dm +++ b/code/modules/cooking/machinery/smartfridge.dm @@ -43,6 +43,8 @@ var/datum/wires/smartfridge/wires = null atmos_canpass = CANPASS_DENSITY + VAR_PRIVATE/static/list/base64_cache = list() + /obj/machinery/smartfridge/secure is_secure = 1 @@ -95,6 +97,7 @@ for(var/obj/item/reagent_containers/food/snacks/grown/g in contents) item_quants[g.name]++ update_overlays() + update_static_data_for_all_viewers() /obj/machinery/smartfridge/Initialize() . = ..() @@ -230,6 +233,10 @@ if(S.on_dry(src)) //Drying rack keeps the item but changes the name. This prevents pre-dried item lingering in the UI as vendable item_quants[S.name]++ item_quants[old_name]-- + if(item_quants[old_name] <= 0 || item_quants[S.name] == 1) + update_static_data_for_all_viewers() + if(item_quants[old_name] <= 0) + item_quants -= old_name return /obj/machinery/smartfridge/drying_rack/update_overlays() @@ -360,25 +367,32 @@ user.remove_from_mob(attacking_item) attacking_item.forceMove(src) item_quants[attacking_item.name]++ + if(item_quants[attacking_item.name] == 1) + update_static_data_for_all_viewers() user.visible_message("[user] adds \a [attacking_item] to [src].", SPAN_NOTICE("You add [attacking_item] to [src].")) update_overlays() - return + return TRUE if(istype(attacking_item, /obj/item/storage)) var/obj/item/storage/P = attacking_item var/plants_loaded = 0 + var/update_static_data = FALSE for(var/obj/G in P.contents) if(accept_check(G)) if(length(contents) >= max_n_of_items) break P.remove_from_storage(G,src) item_quants[G.name]++ + if(item_quants[G.name] == 1) + update_static_data = TRUE plants_loaded++ if(plants_loaded) user.visible_message("[user] loads [src] with [P].", SPAN_NOTICE("You load [src] with [P].")) if(length(P.contents) > 0) to_chat(user, SPAN_NOTICE("Some items are refused.")) update_overlays() + if(update_static_data) + update_static_data_for_all_viewers() return TRUE to_chat(user, SPAN_NOTICE("[src] smartly refuses [attacking_item].")) return TRUE @@ -416,43 +430,55 @@ /obj/machinery/smartfridge/ui_data(mob/user) var/list/data = list() - data["contents"] = null data["electrified"] = seconds_electrified > 0 data["shoot_inventory"] = shoot_inventory data["locked"] = locked data["secure"] = is_secure data["sort_alphabetically"] = ui_sort_alphabetically - var/list/items = list() - for (var/i = 1 to length(item_quants)) + var/list/stocks + for (var/i in 1 to length(item_quants)) var/K = item_quants[i] var/count = item_quants[K] if(count > 0) + LAZYADDASSOC(stocks, capitalize(K), count) - var/obj/item/item_used = null - for (var/obj/item/O in contents) - if(O.name == K) - item_used = O - break + data["stocks"] = stocks - var/icon/item_icon = null - if(item_used) - if(istype(item_used, /obj/item/seeds)) - var/obj/item/seeds/S = item_used - item_icon = S.update_appearance(TRUE) - else - item_icon = new /icon(item_used.icon, item_used.icon_state) + return data - var/final_icon = null - if(item_icon) - final_icon = icon2base64(item_icon) +/obj/machinery/smartfridge/ui_static_data(mob/user) + var/list/data = list() + data["contents"] = null - items.Add(list(list( - "display_name" = capitalize(K), - "vend" = i, - "quantity" = count, - "icon" = final_icon, - ))) + var/list/items = list() + for (var/i = 1 to length(item_quants)) + var/K = item_quants[i] + var/obj/item/item_used = null + for (var/obj/item/O in contents - component_parts) + if(O.name == K) + item_used = O + break + + if(!item_used) + continue + + var/final_icon = base64_cache["[K]_[item_used.type]_[item_used.icon_state]"] + if(!final_icon) + var/icon/item_icon + if(istype(item_used, /obj/item/seeds)) + var/obj/item/seeds/S = item_used + item_icon = S.update_appearance(TRUE) + else + item_icon = getFlatIcon(item_used) + final_icon = icon2base64(item_icon) + base64_cache["[K]_[item_used.type]_[item_used.icon_state]"] = final_icon + + items.Add(list(list( + "display_name" = capitalize(K), + "vend" = i, + "icon" = final_icon, + ))) if(length(items) > 0) data["contents"] = items @@ -497,6 +523,10 @@ update_overlays() if(i <= 0) break + + if(item_quants[K] <= 0) + update_static_data_for_all_viewers() + item_quants -= K if("switch_sort_alphabetically") ui_sort_alphabetically = !ui_sort_alphabetically @@ -510,6 +540,7 @@ for (var/O in item_quants) if(item_quants[O] <= 0) //Try to use a record that actually has something to dump. + item_quants -= O continue item_quants[O]-- @@ -518,6 +549,10 @@ T.forceMove(loc) throw_item = T break + + if(item_quants[O] <= 0) + update_static_data_for_all_viewers() + item_quants -= O break if(!throw_item) return FALSE diff --git a/html/changelogs/johnwildkins-smartfridge.yml b/html/changelogs/johnwildkins-smartfridge.yml new file mode 100644 index 00000000000..2667a7ec58f --- /dev/null +++ b/html/changelogs/johnwildkins-smartfridge.yml @@ -0,0 +1,14 @@ +# Your name. +author: JohnWildkins + +# Optional: Remove this file after generating master changelog. Useful for PR changelogs that won't get used again. +delete-after: True + +# Any changes you've made. See valid prefix list above. +# INDENT WITH TWO SPACES. NOT TABS. SPACES. +# SCREW THIS UP AND IT WON'T WORK. +# Also, this gets changed to [] after reading. Just remove the brackets when you add new shit. +# Please surround your changes in double quotes ("). It works without them, but if you use certain characters it screws up compiling. The quotes will not show up in the changelog. +changes: + - refactor: "Move smartfridge icon generation to static data and cache results to improve performance." + - bugfix: "Fix chemical bottles not having proper colors in SmartFridge UI." diff --git a/tgui/packages/tgui/interfaces/SmartFridge.tsx b/tgui/packages/tgui/interfaces/SmartFridge.tsx index ee0c6ef1b63..fee46ce3100 100644 --- a/tgui/packages/tgui/interfaces/SmartFridge.tsx +++ b/tgui/packages/tgui/interfaces/SmartFridge.tsx @@ -5,6 +5,7 @@ import { Window } from '../layouts'; export type FridgeData = { contents: Item[]; + stocks: { [display_name: string]: number }; seconds_electrified: BooleanLike; shoot_inventory: BooleanLike; locked: number; // goes to -1 @@ -15,7 +16,6 @@ export type FridgeData = { type Item = { display_name: string; vend: number; - quantity: number; icon?: string | null; }; @@ -87,93 +87,98 @@ export const ContentsWindow = (props, context) => { }) : itemList; + const itemStock = (item: Item) => data.stocks[item.display_name] || 0; + return (
- {itemListSorted.map((item) => ( - - {/* Item icon */} - {item.icon ? ( + {itemListSorted.map((item) => { + const quantity = itemStock(item); + return ( + + {/* Item icon */} + {item.icon ? ( + + ) : null} + {/* Name stuff */} - ) : null} - {/* Name stuff */} - - {item.display_name} + style={{ + flex: 2, + minWidth: 0, + whiteSpace: 'normal', + overflowWrap: 'break-word', + wordBreak: 'break-word', + }} + > + {item.display_name} + + {/* Amount */}x{quantity} + {/* Vend buttons */} + + {quantity > 0 && ( +
);