diff --git a/code/__DEFINES/flags.dm b/code/__DEFINES/flags.dm index cd6688d095f..6c34d3144b0 100644 --- a/code/__DEFINES/flags.dm +++ b/code/__DEFINES/flags.dm @@ -54,6 +54,8 @@ // LAVA_PROTECT used on the flags_2 variable for both SUIT and HEAD items, and stops lava damage. Must be present in both to stop lava damage. #define LAVA_PROTECT_2 2048 +#define OVERLAY_QUEUED_2 4096 + //Reagent flags #define REAGENT_NOREACT 1 diff --git a/code/__DEFINES/subsystems.dm b/code/__DEFINES/subsystems.dm index a03ce11a648..b85677ff777 100644 --- a/code/__DEFINES/subsystems.dm +++ b/code/__DEFINES/subsystems.dm @@ -124,3 +124,22 @@ #define RUNLEVEL_POSTGAME 8 #define RUNLEVELS_DEFAULT (RUNLEVEL_SETUP | RUNLEVEL_GAME | RUNLEVEL_POSTGAME) + +#define COMPILE_OVERLAYS(A)\ + if (TRUE) {\ + var/list/ad = A.add_overlays;\ + var/list/rm = A.remove_overlays;\ + var/list/po = A.priority_overlays;\ + if(LAZYLEN(rm)){\ + A.overlays -= rm;\ + rm.Cut();\ + }\ + if(LAZYLEN(ad)){\ + A.overlays |= ad;\ + ad.Cut();\ + }\ + if(LAZYLEN(po)){\ + A.overlays |= po;\ + }\ + A.flags_2 &= ~OVERLAY_QUEUED_2;\ +} \ No newline at end of file diff --git a/code/_onclick/hud/screen_objects.dm b/code/_onclick/hud/screen_objects.dm index 67f2db660f8..7c38ee1072a 100644 --- a/code/_onclick/hud/screen_objects.dm +++ b/code/_onclick/hud/screen_objects.dm @@ -275,8 +275,38 @@ /obj/screen/inventory var/slot_id //The indentifier for the slot. It has nothing to do with ID cards. + var/list/object_overlays = list() layer = 19 +/obj/screen/inventory/MouseEntered() + ..() + add_overlays() + +/obj/screen/inventory/MouseExited() + ..() + cut_overlay(object_overlays) + object_overlays.Cut() + +/obj/screen/inventory/proc/add_overlays() + var/mob/user = hud.mymob + + if(hud && user && slot_id) + var/obj/item/holding = user.get_active_hand() + + if(!holding || user.get_item_by_slot(slot_id)) + return + + var/image/item_overlay = image(holding) + item_overlay.alpha = 92 + + if(!user.can_equip(holding, slot_id, disable_warning = TRUE)) + item_overlay.color = "#ff0000" + else + item_overlay.color = "#00ff00" + + object_overlays += item_overlay + add_overlay(object_overlays) + /obj/screen/inventory/Click() // At this point in client Click() code we have passed the 1/10 sec check and little else // We don't even know if it's a middle click diff --git a/code/controllers/subsystem/overlays.dm b/code/controllers/subsystem/overlays.dm new file mode 100644 index 00000000000..154f7bfb8d9 --- /dev/null +++ b/code/controllers/subsystem/overlays.dm @@ -0,0 +1,213 @@ +SUBSYSTEM_DEF(overlays) + name = "Overlay" + flags = SS_TICKER + wait = 1 + priority = FIRE_PRIORITY_OVERLAYS + init_order = INIT_ORDER_OVERLAY + + var/list/queue + var/list/stats + var/list/overlay_icon_state_caches + var/list/overlay_icon_cache + +/datum/controller/subsystem/overlays/PreInit() + overlay_icon_state_caches = list() + overlay_icon_cache = list() + queue = list() + stats = list() + +/datum/controller/subsystem/overlays/Initialize() + initialized = TRUE + fire(mc_check = FALSE) + return ..() + + +/datum/controller/subsystem/overlays/stat_entry() + ..("Ov:[length(queue)]") + +/datum/controller/subsystem/overlays/Recover() + overlay_icon_state_caches = SSoverlays.overlay_icon_state_caches + overlay_icon_cache = SSoverlays.overlay_icon_cache + queue = SSoverlays.queue + + +/datum/controller/subsystem/overlays/fire(resumed = FALSE, mc_check = TRUE) + var/list/queue = src.queue + var/static/count = 0 + if (count) + var/c = count + count = 0 //so if we runtime on the Cut, we don't try again. + queue.Cut(1,c+1) + + for (var/thing in queue) + count++ + if(thing) + var/atom/A = thing + COMPILE_OVERLAYS(A) + if(mc_check) + if(MC_TICK_CHECK) + break + else + CHECK_TICK + + if (count) + queue.Cut(1,count+1) + count = 0 + +/proc/iconstate2appearance(icon, iconstate) + var/static/image/stringbro = new() + var/list/icon_states_cache = SSoverlays.overlay_icon_state_caches + var/list/cached_icon = icon_states_cache[icon] + if (cached_icon) + var/cached_appearance = cached_icon["[iconstate]"] + if (cached_appearance) + return cached_appearance + stringbro.icon = icon + stringbro.icon_state = iconstate + if (!cached_icon) //not using the macro to save an associated lookup + cached_icon = list() + icon_states_cache[icon] = cached_icon + var/cached_appearance = stringbro.appearance + cached_icon["[iconstate]"] = cached_appearance + return cached_appearance + +/proc/icon2appearance(icon) + var/static/image/iconbro = new() + var/list/icon_cache = SSoverlays.overlay_icon_cache + . = icon_cache[icon] + if (!.) + iconbro.icon = icon + . = iconbro.appearance + icon_cache[icon] = . + +/atom/proc/build_appearance_list(old_overlays) + var/static/image/appearance_bro = new() + var/list/new_overlays = list() + if (!islist(old_overlays)) + old_overlays = list(old_overlays) + for (var/overlay in old_overlays) + if(!overlay) + continue + if (istext(overlay)) + new_overlays += iconstate2appearance(icon, overlay) + else if(isicon(overlay)) + new_overlays += icon2appearance(overlay) + else + if(isloc(overlay)) + var/atom/A = overlay + if (A.flags_2 & OVERLAY_QUEUED_2) + COMPILE_OVERLAYS(A) + appearance_bro.appearance = overlay //this works for images and atoms too! + if(!ispath(overlay)) + var/image/I = overlay + appearance_bro.dir = I.dir + new_overlays += appearance_bro.appearance + return new_overlays + +#define NOT_QUEUED_ALREADY (!(flags_2 & OVERLAY_QUEUED_2)) +#define QUEUE_FOR_COMPILE flags_2 |= OVERLAY_QUEUED_2; SSoverlays.queue += src; +/atom/proc/cut_overlays(priority = FALSE) + LAZYINITLIST(priority_overlays) + LAZYINITLIST(remove_overlays) + LAZYINITLIST(add_overlays) + remove_overlays = overlays.Copy() + add_overlays.Cut() + + if(priority) + priority_overlays.Cut() + + //If not already queued for work and there are overlays to remove + if(NOT_QUEUED_ALREADY && remove_overlays.len) + QUEUE_FOR_COMPILE + +/atom/proc/cut_overlay(list/overlays, priority) + if(!overlays) + return + overlays = build_appearance_list(overlays) + LAZYINITLIST(add_overlays) //always initialized after this point + LAZYINITLIST(priority_overlays) + LAZYINITLIST(remove_overlays) + var/a_len = add_overlays.len + var/r_len = remove_overlays.len + var/p_len = priority_overlays.len + remove_overlays += overlays + add_overlays -= overlays + + + if(priority) + var/list/cached_priority = priority_overlays + LAZYREMOVE(cached_priority, overlays) + + var/fa_len = add_overlays.len + var/fr_len = remove_overlays.len + var/fp_len = priority_overlays.len + + //If not already queued and there is work to be done + if(NOT_QUEUED_ALREADY && (fa_len != a_len || fr_len != r_len || fp_len != p_len)) + QUEUE_FOR_COMPILE + +/atom/proc/add_overlay(list/overlays, priority = FALSE) + if(!overlays) + return + + overlays = build_appearance_list(overlays) + + LAZYINITLIST(add_overlays) //always initialized after this point + LAZYINITLIST(priority_overlays) + var/a_len = add_overlays.len + var/p_len = priority_overlays.len + + if(priority) + priority_overlays += overlays //or in the image. Can we use [image] = image? + var/fp_len = priority_overlays.len + if(NOT_QUEUED_ALREADY && fp_len != p_len) + QUEUE_FOR_COMPILE + else + add_overlays += overlays + var/fa_len = add_overlays.len + if(NOT_QUEUED_ALREADY && fa_len != a_len) + QUEUE_FOR_COMPILE + +/atom/proc/copy_overlays(atom/other, cut_old) //copys our_overlays from another atom + if(!other) + if(cut_old) + cut_overlays() + return + + var/list/cached_other = other.overlays.Copy() + if(cached_other) + if(cut_old || !LAZYLEN(overlays)) + remove_overlays = overlays + add_overlays = cached_other + if(NOT_QUEUED_ALREADY) + QUEUE_FOR_COMPILE + else if(cut_old) + cut_overlays() + +#undef NOT_QUEUED_ALREADY +#undef QUEUE_FOR_COMPILE + +//TODO: Better solution for these? +/image/proc/add_overlay(x) + overlays |= x + +/image/proc/cut_overlay(x) + overlays -= x + +/image/proc/cut_overlays(x) + overlays.Cut() + +/image/proc/copy_overlays(atom/other, cut_old) + if(!other) + if(cut_old) + cut_overlays() + return + + var/list/cached_other = other.overlays.Copy() + if(cached_other) + if(cut_old || !overlays.len) + overlays = cached_other + else + overlays |= cached_other + else if(cut_old) + cut_overlays() diff --git a/code/game/atoms.dm b/code/game/atoms.dm index 805e6b8834f..6d8915cdb43 100644 --- a/code/game/atoms.dm +++ b/code/game/atoms.dm @@ -44,6 +44,10 @@ var/initialized = FALSE + var/list/priority_overlays //overlays that should remain on top and not normally removed when using cut_overlay functions, like c4. + var/list/remove_overlays // a very temporary list of overlays to remove + var/list/add_overlays // a very temporary list of overlays to add + /atom/New(loc, ...) if(use_preloader && (src.type == _preloader.target_path))//in case the instanciated atom is creating other atoms in New() _preloader.load(src) @@ -137,6 +141,8 @@ QDEL_NULL(reagents) invisibility = 101 + LAZYCLEARLIST(overlays) + LAZYCLEARLIST(priority_overlays) return ..() //Hook for running code when a dir change occurs diff --git a/code/modules/food_and_drinks/food/customizables.dm b/code/modules/food_and_drinks/food/customizables.dm index 70027028344..b0ac9d20d6c 100644 --- a/code/modules/food_and_drinks/food/customizables.dm +++ b/code/modules/food_and_drinks/food/customizables.dm @@ -73,7 +73,7 @@ bitesize = 4 var/top = 1 //Do we have a top? var/obj/item/toptype - var/add_overlays = 1 //Do we stack? + var/snack_overlays = 1 //Do we stack? // var/offsetstuff = 1 //Do we offset the overlays? var/sandwich_limit = 40 var/fullycustom = 0 @@ -87,7 +87,7 @@ icon_state = "personal_pizza" baseicon = "personal_pizza" basename = "personal pizza" - add_overlays = 0 + snack_overlays = 0 top = 0 /obj/item/reagent_containers/food/snacks/customizable/pasta @@ -96,7 +96,7 @@ icon_state = "pasta_bot" baseicon = "pasta_bot" basename = "pasta" - add_overlays = 0 + snack_overlays = 0 top = 0 /obj/item/reagent_containers/food/snacks/customizable/cook/bread @@ -105,7 +105,7 @@ icon_state = "breadcustom" baseicon = "breadcustom" basename = "bread" - add_overlays = 0 + snack_overlays = 0 top = 0 /obj/item/reagent_containers/food/snacks/customizable/cook/pie @@ -114,7 +114,7 @@ icon_state = "piecustom" baseicon = "piecustom" basename = "pie" - add_overlays = 0 + snack_overlays = 0 top = 0 /obj/item/reagent_containers/food/snacks/customizable/cook/cake @@ -123,7 +123,7 @@ icon_state = "cakecustom" baseicon = "cakecustom" basename = "cake" - add_overlays = 0 + snack_overlays = 0 top = 0 /obj/item/reagent_containers/food/snacks/customizable/cook/jelly @@ -132,7 +132,7 @@ icon_state = "jellycustom" baseicon = "jellycustom" basename = "jelly" - add_overlays = 0 + snack_overlays = 0 top = 0 /obj/item/reagent_containers/food/snacks/customizable/cook/donkpocket @@ -141,7 +141,7 @@ icon_state = "donkcustom" baseicon = "donkcustom" basename = "donk pocket" - add_overlays = 0 + snack_overlays = 0 top = 0 /obj/item/reagent_containers/food/snacks/customizable/cook/kebab @@ -150,7 +150,7 @@ icon_state = "kababcustom" baseicon = "kababcustom" basename = "kebab" - add_overlays = 0 + snack_overlays = 0 top = 0 /obj/item/reagent_containers/food/snacks/customizable/cook/salad @@ -159,7 +159,7 @@ icon_state = "saladcustom" baseicon = "saladcustom" basename = "salad" - add_overlays = 0 + snack_overlays = 0 top = 0 /obj/item/reagent_containers/food/snacks/customizable/cook/waffles @@ -168,7 +168,7 @@ icon_state = "wafflecustom" baseicon = "wafflecustom" basename = "waffles" - add_overlays = 0 + snack_overlays = 0 top = 0 /obj/item/reagent_containers/food/snacks/customizable/candy/cookie @@ -177,7 +177,7 @@ icon_state = "cookiecustom" baseicon = "cookiecustom" basename = "cookie" - add_overlays = 0 + snack_overlays = 0 top = 0 /obj/item/reagent_containers/food/snacks/customizable/candy/cotton @@ -186,7 +186,7 @@ icon_state = "cottoncandycustom" baseicon = "cottoncandycustom" basename = "flavored cotton candy" - add_overlays = 0 + snack_overlays = 0 top = 0 /obj/item/reagent_containers/food/snacks/customizable/candy/gummybear @@ -195,7 +195,7 @@ icon_state = "gummybearcustom" baseicon = "gummybearcustom" basename = "flavored giant gummy bear" - add_overlays = 0 + snack_overlays = 0 top = 0 /obj/item/reagent_containers/food/snacks/customizable/candy/gummyworm @@ -204,7 +204,7 @@ icon_state = "gummywormcustom" baseicon = "gummywormcustom" basename = "flavored giant gummy worm" - add_overlays = 0 + snack_overlays = 0 top = 0 /obj/item/reagent_containers/food/snacks/customizable/candy/jellybean @@ -213,7 +213,7 @@ icon_state = "jellybeancustom" baseicon = "jellybeancustom" basename = "flavored giant jelly bean" - add_overlays = 0 + snack_overlays = 0 top = 0 /obj/item/reagent_containers/food/snacks/customizable/candy/jawbreaker @@ -222,7 +222,7 @@ icon_state = "jawbreakercustom" baseicon = "jawbreakercustom" basename = "flavored jawbreaker" - add_overlays = 0 + snack_overlays = 0 top = 0 /obj/item/reagent_containers/food/snacks/customizable/candy/candycane @@ -231,7 +231,7 @@ icon_state = "candycanecustom" baseicon = "candycanecustom" basename = "flavored candy cane" - add_overlays = 0 + snack_overlays = 0 top = 0 /obj/item/reagent_containers/food/snacks/customizable/candy/gum @@ -240,7 +240,7 @@ icon_state = "gumcustom" baseicon = "gumcustom" basename = "flavored gum" - add_overlays = 0 + snack_overlays = 0 top = 0 /obj/item/reagent_containers/food/snacks/customizable/candy/donut @@ -249,7 +249,7 @@ icon_state = "donutcustom" baseicon = "donutcustom" basename = "filled donut" - add_overlays = 0 + snack_overlays = 0 top = 0 /obj/item/reagent_containers/food/snacks/customizable/candy/bar @@ -258,7 +258,7 @@ icon_state = "barcustom" baseicon = "barcustom" basename = "flavored chocolate bar" - add_overlays = 0 + snack_overlays = 0 top = 0 /obj/item/reagent_containers/food/snacks/customizable/candy/sucker @@ -267,7 +267,7 @@ icon_state = "suckercustom" baseicon = "suckercustom" basename = "flavored sucker" - add_overlays = 0 + snack_overlays = 0 top = 0 /obj/item/reagent_containers/food/snacks/customizable/candy/cash @@ -276,7 +276,7 @@ icon_state = "cashcustom" baseicon = "cashcustom" basename = "flavored cash" - add_overlays = 0 + snack_overlays = 0 top = 0 /obj/item/reagent_containers/food/snacks/customizable/candy/coin @@ -285,7 +285,7 @@ icon_state = "coincustom" baseicon = "coincustom" basename = "flavored coin" - add_overlays = 0 + snack_overlays = 0 top = 0 /obj/item/reagent_containers/food/snacks/customizable/fullycustom // In the event you fuckers find something I forgot to add a customizable food for. @@ -294,7 +294,7 @@ icon_state = "fullycustom" baseicon = "fullycustom" basename = "on a plate" - add_overlays = 0 + snack_overlays = 0 top = 0 sandwich_limit = 20 fullycustom = 1 @@ -305,7 +305,7 @@ icon_state = "soup" baseicon = "soup" basename = "soup" - add_overlays = 0 + snack_overlays = 0 trash = /obj/item/trash/bowl top = 0 @@ -355,7 +355,7 @@ I.color = pick("#FF0000","#0000FF","#008000","#FFFF00") else I.color = pick("#FF0000","#0000FF","#008000","#FFFF00") - if(add_overlays) + if(snack_overlays) I.pixel_x = pick(list(-1,0,1)) I.pixel_y = (i*2)+1 overlays += I diff --git a/paradise.dme b/paradise.dme index 579a8a6b8f1..a20b8d43042 100644 --- a/paradise.dme +++ b/paradise.dme @@ -210,6 +210,7 @@ #include "code\controllers\subsystem\nightshift.dm" #include "code\controllers\subsystem\npcai.dm" #include "code\controllers\subsystem\npcpool.dm" +#include "code\controllers\subsystem\overlays.dm" #include "code\controllers\subsystem\shuttles.dm" #include "code\controllers\subsystem\spacedrift.dm" #include "code\controllers\subsystem\sun.dm"