From 8ce0592ac02fcc2046938ce89486373f57b9fab3 Mon Sep 17 00:00:00 2001 From: tigercat2000 Date: Mon, 8 Oct 2018 22:50:39 -0700 Subject: [PATCH] SSOverlays & Hover Inventory Indicator This ports the hover inventory indicator from /tg/, and by necessity, SSOverlays. The hover inventory indicator basically shows where you are trying to put an item, and then shows an item ghost in red or green, giving visual feedback as to if that item will go in the slot or not. SSOverlays is a much broader system for managing overlays, which should eventually take over *all* overlays, but I have only ported the basic system and implemented it where necessary for it to work for the hover inventory items currently. --- code/__DEFINES/flags.dm | 2 + code/__DEFINES/subsystems.dm | 19 ++ code/_onclick/hud/screen_objects.dm | 30 +++ code/controllers/subsystem/overlays.dm | 213 ++++++++++++++++++ code/game/atoms.dm | 6 + .../food_and_drinks/food/customizables.dm | 54 ++--- paradise.dme | 1 + 7 files changed, 298 insertions(+), 27 deletions(-) create mode 100644 code/controllers/subsystem/overlays.dm 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"