From ef2482c6b0f8d665e33e1d0b2a74d92e81cdc7bb Mon Sep 17 00:00:00 2001 From: silicons <2003111+silicons@users.noreply.github.com> Date: Tue, 13 Apr 2021 07:16:21 -0600 Subject: [PATCH] renders storage objects independently per user (#14545) * sigh * sigh * override * hah.. * kinda working * done --- code/__HELPERS/_lists.dm | 2 +- code/_onclick/hud/screen_objects/storage.dm | 56 ++++++++++--- code/datums/components/storage/storage.dm | 43 +++------- code/datums/components/storage/ui.dm | 85 ++++++++------------ code/datums/numbered_display.dm | 4 +- icons/mob/screen_gen.dmi | Bin 119019 -> 119012 bytes 6 files changed, 94 insertions(+), 96 deletions(-) diff --git a/code/__HELPERS/_lists.dm b/code/__HELPERS/_lists.dm index 31f34c5d4c..7e2abebcf9 100644 --- a/code/__HELPERS/_lists.dm +++ b/code/__HELPERS/_lists.dm @@ -15,7 +15,7 @@ #define LAZYREMOVE(L, I) if(L) { L -= I; if(!length(L)) { L = null; } } #define LAZYADD(L, I) if(!L) { L = list(); } L += I; #define LAZYOR(L, I) if(!L) { L = list(); } L |= I; -#define LAZYFIND(L, V) L ? L.Find(V) : 0 +#define LAZYFIND(L, V) (L ? L.Find(V) : 0) #define LAZYACCESS(L, I) (L ? (isnum(I) ? (I > 0 && I <= length(L) ? L[I] : null) : L[I]) : null) #define LAZYSET(L, K, V) if(!L) { L = list(); } L[K] = V; #define LAZYLEN(L) length(L) diff --git a/code/_onclick/hud/screen_objects/storage.dm b/code/_onclick/hud/screen_objects/storage.dm index 72b2d035a3..1ce1df24c6 100644 --- a/code/_onclick/hud/screen_objects/storage.dm +++ b/code/_onclick/hud/screen_objects/storage.dm @@ -85,21 +85,16 @@ makeItemInactive() /obj/screen/storage/volumetric_box/proc/makeItemInactive() - if(!our_item) - return - our_item.layer = VOLUMETRIC_STORAGE_ITEM_LAYER - our_item.plane = VOLUMETRIC_STORAGE_ITEM_PLANE + return /obj/screen/storage/volumetric_box/proc/makeItemActive() - if(!our_item) - return - our_item.layer = VOLUMETRIC_STORAGE_ACTIVE_ITEM_LAYER //make sure we display infront of the others! - our_item.plane = VOLUMETRIC_STORAGE_ACTIVE_ITEM_PLANE + return /obj/screen/storage/volumetric_box/center icon_state = "stored_continue" var/obj/screen/storage/volumetric_edge/stored_left/left var/obj/screen/storage/volumetric_edge/stored_right/right + var/obj/screen/storage/item_holder/holder var/pixel_size /obj/screen/storage/volumetric_box/center/Initialize(mapload, new_master, our_item) @@ -110,6 +105,9 @@ /obj/screen/storage/volumetric_box/center/Destroy() QDEL_NULL(left) QDEL_NULL(right) + vis_contents.Cut() + if(holder) + QDEL_NULL(holder) return ..() /obj/screen/storage/volumetric_box/center/proc/on_screen_objects() @@ -123,13 +121,36 @@ return pixel_size = pixels cut_overlays() + vis_contents.Cut() //our icon size is 32 pixels. - transform = matrix((pixels - (VOLUMETRIC_STORAGE_BOX_BORDER_SIZE * 2)) / VOLUMETRIC_STORAGE_BOX_ICON_SIZE, 0, 0, 0, 1, 0) + var/multiplier = (pixels - (VOLUMETRIC_STORAGE_BOX_BORDER_SIZE * 2)) / VOLUMETRIC_STORAGE_BOX_ICON_SIZE + transform = matrix(multiplier, 0, 0, 0, 1, 0) + if(our_item) + if(holder) + qdel(holder) + holder = new(null, src, our_item) + holder.transform = matrix(1 / multiplier, 0, 0, 0, 1, 0) + holder.mouse_opacity = MOUSE_OPACITY_TRANSPARENT + holder.appearance_flags &= ~RESET_TRANSFORM + makeItemInactive() + vis_contents += holder left.pixel_x = -((pixels - VOLUMETRIC_STORAGE_BOX_ICON_SIZE) * 0.5) - VOLUMETRIC_STORAGE_BOX_BORDER_SIZE right.pixel_x = ((pixels - VOLUMETRIC_STORAGE_BOX_ICON_SIZE) * 0.5) + VOLUMETRIC_STORAGE_BOX_BORDER_SIZE add_overlay(left) add_overlay(right) +/obj/screen/storage/volumetric_box/center/makeItemInactive() + if(!holder) + return + holder.layer = VOLUMETRIC_STORAGE_ITEM_LAYER + holder.plane = VOLUMETRIC_STORAGE_ITEM_PLANE + +/obj/screen/storage/volumetric_box/center/makeItemActive() + if(!holder) + return + holder.our_item.layer = VOLUMETRIC_STORAGE_ACTIVE_ITEM_LAYER //make sure we display infront of the others! + holder.our_item.plane = VOLUMETRIC_STORAGE_ACTIVE_ITEM_PLANE + /obj/screen/storage/volumetric_edge layer = VOLUMETRIC_STORAGE_BOX_LAYER plane = VOLUMETRIC_STORAGE_BOX_PLANE @@ -157,3 +178,20 @@ /obj/screen/storage/volumetric_edge/stored_right icon_state = "stored_end" appearance_flags = APPEARANCE_UI | KEEP_APART | RESET_TRANSFORM + +/obj/screen/storage/item_holder + var/obj/item/our_item + vis_flags = NONE + +/obj/screen/storage/item_holder/Initialize(mapload, new_master, obj/item/I) + . = ..() + our_item = I + vis_contents += I + +/obj/screen/storage/item_holder/Destroy() + vis_contents.Cut() + our_item = null + return ..() + +/obj/screen/storage/item_holder/Click(location, control, params) + return our_item.Click(location, control, params) diff --git a/code/datums/components/storage/storage.dm b/code/datums/components/storage/storage.dm index 2fa5a20d7a..7968caed54 100644 --- a/code/datums/components/storage/storage.dm +++ b/code/datums/components/storage/storage.dm @@ -47,18 +47,8 @@ var/display_numerical_stacking = FALSE //stack things of the same type and show as a single object with a number. - /// "legacy"/default view mode's storage "boxes" - var/obj/screen/storage/boxes/ui_boxes - /// New volumetric storage display mode's left side - var/obj/screen/storage/left/ui_left - /// New volumetric storage display mode's center 'blocks' - var/obj/screen/storage/continuous/ui_continuous - /// The close button, used in all modes. Frames right side in volumetric mode. - var/obj/screen/storage/close/ui_close - /// Associative list of list(item = screen object) for volumetric storage item screen blocks - var/list/ui_item_blocks - - var/current_maxscreensize + /// Ui objects by person. mob = list(objects) + var/list/ui_by_mob = list() var/allow_big_nesting = FALSE //allow storage objects of the same or greater size. @@ -125,18 +115,16 @@ /datum/component/storage/Destroy() close_all() - QDEL_NULL(ui_boxes) - QDEL_NULL(ui_close) - QDEL_NULL(ui_continuous) - QDEL_NULL(ui_left) - // DO NOT USE QDEL_LIST_ASSOC. - if(ui_item_blocks) - for(var/i in ui_item_blocks) - qdel(ui_item_blocks[i]) //qdel the screen object not the item - ui_item_blocks.Cut() + wipe_ui_objects() LAZYCLEARLIST(is_using) return ..() +/datum/component/storage/proc/wipe_ui_objects() + for(var/i in ui_by_mob) + var/list/objects = ui_by_mob[i] + QDEL_LIST(objects) + ui_by_mob.Cut() + /datum/component/storage/PreTransfer() update_actions() @@ -351,13 +339,6 @@ return master._removal_reset(thing) /datum/component/storage/proc/_remove_and_refresh(datum/source, atom/movable/thing) - if(LAZYACCESS(ui_item_blocks, thing)) - var/obj/screen/storage/volumetric_box/center/C = ui_item_blocks[thing] - for(var/i in can_see_contents()) //runtimes result if mobs can access post deletion. - var/mob/M = i - M.client?.screen -= C.on_screen_objects() - ui_item_blocks -= thing - qdel(C) _removal_reset(thing) // THIS NEEDS TO HAPPEN AFTER SO LAYERING DOESN'T BREAK! refresh_mob_views() @@ -467,14 +448,14 @@ return A.add_fingerprint(M) -/datum/component/storage/proc/user_show_to_mob(mob/M, force = FALSE, ghost = FALSE) +/datum/component/storage/proc/user_show_to_mob(mob/M, force = FALSE) var/atom/A = parent if(!istype(M)) return FALSE A.add_fingerprint(M) if(!force && (check_locked(null, M) || !M.CanReach(parent, view_only = TRUE))) return FALSE - ui_show(M, !ghost) + ui_show(M) /datum/component/storage/proc/mousedrop_receive(datum/source, atom/movable/O, mob/M) if(isitem(O)) @@ -596,7 +577,7 @@ return can_be_inserted(I, silent, M) /datum/component/storage/proc/show_to_ghost(datum/source, mob/dead/observer/M) - return user_show_to_mob(M, TRUE, TRUE) + return user_show_to_mob(M, TRUE) /datum/component/storage/proc/signal_show_attempt(datum/source, mob/showto, force = FALSE) return user_show_to_mob(showto, force) diff --git a/code/datums/components/storage/ui.dm b/code/datums/components/storage/ui.dm index c7ac0d3549..044afa2850 100644 --- a/code/datums/components/storage/ui.dm +++ b/code/datums/components/storage/ui.dm @@ -7,7 +7,7 @@ if(QDELETED(I)) continue if(!.[I.type]) - .[I.type] = new /datum/numbered_display(I, 1) + .[I.type] = new /datum/numbered_display(I, 1, src) else var/datum/numbered_display/ND = .[I.type] ND.number++ @@ -20,6 +20,8 @@ . = list() var/list/accessible_contents = accessible_items() var/adjusted_contents = length(accessible_contents) + var/obj/screen/storage/close/ui_close + var/obj/screen/storage/boxes/ui_boxes //Numbered contents display var/list/datum/numbered_display/numbered_contents @@ -60,12 +62,13 @@ for(var/obj/O in accessible_items()) if(QDELETED(O)) continue - O.mouse_opacity = MOUSE_OPACITY_OPAQUE //This is here so storage items that spawn with contents correctly have the "click around item to equip" - O.screen_loc = "[cx]:[screen_pixel_x],[cy]:[screen_pixel_y]" + var/obj/screen/storage/item_holder/D = new(null, src, O) + D.mouse_opacity = MOUSE_OPACITY_OPAQUE //This is here so storage items that spawn with contents correctly have the "click around item to equip" + D.screen_loc = "[cx]:[screen_pixel_x],[cy]:[screen_pixel_y]" O.maptext = "" O.layer = ABOVE_HUD_LAYER O.plane = ABOVE_HUD_PLANE - . += O + . += D cx++ if(cx - screen_start_x >= columns) cx = screen_start_x @@ -78,6 +81,9 @@ */ /datum/component/storage/proc/orient2hud_volumetric(mob/user, maxcolumns) . = list() + var/obj/screen/storage/left/ui_left + var/obj/screen/storage/continuous/ui_continuous + var/obj/screen/storage/close/ui_close // Generate ui_item_blocks for missing ones and render+orient. var/list/atom/contents = accessible_items() @@ -128,14 +134,10 @@ var/first = TRUE var/row = 1 - LAZYINITLIST(ui_item_blocks) - for(var/i in percentage_by_item) I = i var/percent = percentage_by_item[I] - if(!ui_item_blocks[I]) - ui_item_blocks[I] = new /obj/screen/storage/volumetric_box/center(null, src, I) - var/obj/screen/storage/volumetric_box/center/B = ui_item_blocks[I] + var/obj/screen/storage/volumetric_box/center/B = new /obj/screen/storage/volumetric_box/center(null, src, I) var/pixels_to_use = overrun? MINIMUM_PIXELS_PER_ITEM : max(using_horizontal_pixels * percent, MINIMUM_PIXELS_PER_ITEM) var/addrow = FALSE if(CEILING(pixels_to_use, 1) >= FLOOR(horizontal_pixels - current_pixel - VOLUMETRIC_STORAGE_EDGE_PADDING, 1)) @@ -143,25 +145,17 @@ addrow = TRUE // now that we have pixels_to_use, place our thing and add it to the returned list. - B.screen_loc = I.screen_loc = "[screen_start_x]:[round(current_pixel + (pixels_to_use * 0.5) + (first? 0 : VOLUMETRIC_STORAGE_ITEM_PADDING), 1)],[screen_start_y+row-1]:[screen_pixel_y]" + B.screen_loc = "[screen_start_x]:[round(current_pixel + (pixels_to_use * 0.5) + (first? 0 : VOLUMETRIC_STORAGE_ITEM_PADDING), 1)],[screen_start_y+row-1]:[screen_pixel_y]" // add the used pixels to pixel after we place the object current_pixel += pixels_to_use + (first? 0 : VOLUMETRIC_STORAGE_ITEM_PADDING) first = FALSE //apply padding to everything after this // set various things B.set_pixel_size(pixels_to_use) - B.layer = VOLUMETRIC_STORAGE_BOX_LAYER - B.plane = VOLUMETRIC_STORAGE_BOX_PLANE B.name = I.name - I.mouse_opacity = MOUSE_OPACITY_ICON - I.maptext = "" - I.layer = VOLUMETRIC_STORAGE_ITEM_LAYER - I.plane = VOLUMETRIC_STORAGE_ITEM_PLANE - // finally add our things. . += B.on_screen_objects() - . += I // go up a row if needed if(addrow) @@ -185,18 +179,19 @@ /** * Shows our UI to a mob. */ -/datum/component/storage/proc/ui_show(mob/M, set_screen_size = TRUE) +/datum/component/storage/proc/ui_show(mob/M) if(!M.client) return FALSE + if(ui_by_mob[M] || LAZYFIND(is_using, M)) + // something went horribly wrong + // hide first + ui_hide(M) var/list/cview = getviewsize(M.client.view) // in tiles var/maxallowedscreensize = cview[1]-8 - if(set_screen_size) - current_maxscreensize = maxallowedscreensize - else if(current_maxscreensize) - maxallowedscreensize = current_maxscreensize // we got screen size, register signal RegisterSignal(M, COMSIG_MOB_CLIENT_LOGOUT, .proc/on_logout, override = TRUE) + RegisterSignal(M, COMSIG_PARENT_QDELETING, .proc/on_logout, override = TRUE) if(M.active_storage != src) if(M.active_storage) M.active_storage.ui_hide(M) @@ -204,10 +199,14 @@ LAZYOR(is_using, M) if(!M.client?.prefs?.no_tetris_storage && volumetric_ui()) //new volumetric ui bay-style - M.client.screen |= orient2hud_volumetric(M, maxallowedscreensize) + var/list/objects = orient2hud_volumetric(M, maxallowedscreensize) + M.client.screen |= objects + ui_by_mob[M] = objects else //old ui - M.client.screen |= orient2hud_legacy(M, maxallowedscreensize) + var/list/objects = orient2hud_legacy(M, maxallowedscreensize) + M.client.screen |= objects + ui_by_mob[M] = objects return TRUE /** @@ -236,8 +235,10 @@ /datum/component/storage/proc/ui_hide(mob/M) if(!M.client) return TRUE - UnregisterSignal(M, COMSIG_MOB_CLIENT_LOGOUT) - M.client.screen -= list(ui_boxes, ui_close, ui_left, ui_continuous) + get_ui_item_objects_hide(M) + UnregisterSignal(M, list(COMSIG_PARENT_QDELETING, COMSIG_MOB_CLIENT_LOGOUT)) + M.client.screen -= ui_by_mob[M] + var/list/objects = ui_by_mob[M] + QDEL_LIST(objects) if(M.active_storage == src) M.active_storage = null LAZYREMOVE(is_using, M) @@ -250,48 +251,26 @@ var/atom/real_location = real_location() return (storage_flags & STORAGE_LIMIT_VOLUME) && (length(real_location.contents) <= MAXIMUM_VOLUMETRIC_ITEMS) && !display_numerical_stacking -/** - * Gets the ui item objects to ui_hide. - */ -/datum/component/storage/proc/get_ui_item_objects_hide(mob/M) - if(!volumetric_ui() || M.client?.prefs?.no_tetris_storage) - var/atom/real_location = real_location() - return real_location.contents - else - . = list() - for(var/i in ui_item_blocks) - // get both the box and the item - . += ui_item_blocks[i] - . += i - /** * Gets our ui_boxes, making it if it doesn't exist. */ /datum/component/storage/proc/get_ui_boxes() - if(!ui_boxes) - ui_boxes = new(null, src) - return ui_boxes + return new /obj/screen/storage/boxes(null, src) /** * Gets our ui_left, making it if it doesn't exist. */ /datum/component/storage/proc/get_ui_left() - if(!ui_left) - ui_left = new(null, src) - return ui_left + return new /obj/screen/storage/left(null, src) /** * Gets our ui_close, making it if it doesn't exist. */ /datum/component/storage/proc/get_ui_close() - if(!ui_close) - ui_close = new(null, src) - return ui_close + return new /obj/screen/storage/close(null, src) /** * Gets our ui_continuous, making it if it doesn't exist. */ /datum/component/storage/proc/get_ui_continuous() - if(!ui_continuous) - ui_continuous = new(null, src) - return ui_continuous + return new /obj/screen/storage/continuous(null, src) diff --git a/code/datums/numbered_display.dm b/code/datums/numbered_display.dm index 9aa880aa75..fc2035b39f 100644 --- a/code/datums/numbered_display.dm +++ b/code/datums/numbered_display.dm @@ -3,8 +3,8 @@ var/obj/item/sample_object var/number -/datum/numbered_display/New(obj/item/sample, _number = 1) +/datum/numbered_display/New(obj/item/sample, _number = 1, datum/component/storage/parent) if(!istype(sample)) qdel(src) - sample_object = sample + sample_object = new /obj/screen/storage/item_holder(null, parent, sample) number = _number diff --git a/icons/mob/screen_gen.dmi b/icons/mob/screen_gen.dmi index 60c413c372a7e495568a68b51cdbab64048acd42..95aaf330ce62da02797cff0e9451b8e142c0bf3e 100644 GIT binary patch delta 6795 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 delta 6804 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