diff --git a/code/__DEFINES/dcs/signals.dm b/code/__DEFINES/dcs/signals.dm index 1ef8b9bb66..6255319a08 100644 --- a/code/__DEFINES/dcs/signals.dm +++ b/code/__DEFINES/dcs/signals.dm @@ -190,6 +190,7 @@ #define COMSIG_LIVING_MINOR_SHOCK "living_minor_shock" //sent by stuff like stunbatons and tasers: () #define COMSIG_LIVING_REVIVE "living_revive" //from base of mob/living/revive() (full_heal, admin_revive) #define COMSIG_MOB_CLIENT_LOGIN "comsig_mob_client_login" //sent when a mob/login() finishes: (client) +#define COMSIG_MOB_CLIENT_LOGOUT "comsig_mob_client_logout" //sent when a mob/logout() starts: (client) #define COMSIG_MOB_CLIENT_MOVE "comsig_mob_client_move" //sent when client/Move() finishes with no early returns: (client, direction, n, oldloc) #define COMSIG_LIVING_GUN_PROCESS_FIRE "living_gun_process_fire" //from base of /obj/item/gun/proc/process_fire(): (atom/target, params, zone_override) #define COMSIG_LIVING_COMBAT_ENABLED "combatmode_enabled" //from base of mob/living/enable_combat_mode() (was_forced) diff --git a/code/__DEFINES/storage.dm b/code/__DEFINES/storage.dm index 065551b975..27bf502252 100644 --- a/code/__DEFINES/storage.dm +++ b/code/__DEFINES/storage.dm @@ -30,3 +30,5 @@ // UI defines /// Minimum pixels an item must have in volumetric scaled storage UI #define MINIMUM_PIXELS_PER_ITEM 5 +/// Maximum number of objects that will be allowed to be displayed using the volumetric display system. Arbitrary number to prevent server lockups. +#define MAXIMUM_VOLUMETRIC_ITEMS 256 diff --git a/code/_onclick/hud/screen_objects.dm b/code/_onclick/hud/screen_objects.dm index f5eb8535a5..4ed286eb08 100644 --- a/code/_onclick/hud/screen_objects.dm +++ b/code/_onclick/hud/screen_objects.dm @@ -210,20 +210,6 @@ user.swap_hand(held_index) return TRUE -/obj/screen/close - name = "close" - layer = ABOVE_HUD_LAYER - plane = ABOVE_HUD_PLANE - icon_state = "backpack_close" - -/obj/screen/close/Initialize(mapload, new_master) - . = ..() - master = new_master - -/obj/screen/close/Click() - var/datum/component/storage/S = master - S.hide_from(usr) - return TRUE /obj/screen/drop name = "drop" @@ -406,30 +392,6 @@ else icon_state = "act_rest0" -/obj/screen/storage - name = "storage" - icon_state = "block" - screen_loc = "7,7 to 10,8" - layer = HUD_LAYER - plane = HUD_PLANE - -/obj/screen/storage/Initialize(mapload, new_master) - . = ..() - master = new_master - -/obj/screen/storage/Click(location, control, params) - if(world.time <= usr.next_move) - return TRUE - if(usr.incapacitated()) - return TRUE - if (ismecha(usr.loc)) // stops inventory actions in a mech - return TRUE - if(master) - var/obj/item/I = usr.get_active_held_item() - if(I) - master.attackby(null, I, usr, params) - return TRUE - /obj/screen/throw_catch name = "throw/catch" icon = 'icons/mob/screen_midnight.dmi' diff --git a/code/_onclick/hud/screen_objects/storage.dm b/code/_onclick/hud/screen_objects/storage.dm new file mode 100644 index 0000000000..5e78ba45e0 --- /dev/null +++ b/code/_onclick/hud/screen_objects/storage.dm @@ -0,0 +1,41 @@ +/obj/screen/storage + name = "storage" + var/insertion_click = FALSE + +/obj/screen/storage/Initialize(mapload, new_master) + . = ..() + master = new_master + +/obj/screen/storage/Click(location, control, params) + if(!insertion_click) + return ..() + if(world.time <= usr.next_move) + return TRUE + if(usr.incapacitated()) + return TRUE + if (ismecha(usr.loc)) // stops inventory actions in a mech + return TRUE + if(master) + var/obj/item/I = usr.get_active_held_item() + if(I) + master.attackby(null, I, usr, params) + return TRUE + +/obj/screen/storage/boxes + name = "storage" + icon_state = "block" + screen_loc = "7,7 to 10,8" + layer = HUD_LAYER + plane = HUD_PLANE + insertion_click = TRUE + +/obj/screen/storage/close + name = "close" + layer = ABOVE_HUD_LAYER + plane = ABOVE_HUD_PLANE + icon_state = "backpack_close" + +/obj/screen/storage/close/Click() + var/datum/component/storage/S = master + S.hide_from(usr) + return TRUE diff --git a/code/datums/components/storage/storage.dm b/code/datums/components/storage/storage.dm index 576bd196dc..c3ac3c48a8 100644 --- a/code/datums/components/storage/storage.dm +++ b/code/datums/components/storage/storage.dm @@ -46,8 +46,19 @@ var/display_numerical_stacking = FALSE //stack things of the same type and show as a single object with a number. - var/obj/screen/storage/boxes //storage display object - var/obj/screen/close/closer //close button object + /// "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 right side + var/obj/screen/storage/right/ui_right + /// New volumetric storage display mode's center 'blocks' + var/obj/screen/storage/continuous/ui_continuous + /// The close button, used in all modes. + var/obj/screen/storage/close/ui_closer + /// Associative list of list(item = screen object) for volumetric storage item screen blocks + var/list/ui_item_blocks + var/current_maxscreensize var/allow_big_nesting = FALSE //allow storage objects of the same or greater size. @@ -120,8 +131,15 @@ /datum/component/storage/Destroy() close_all() - QDEL_NULL(boxes) - QDEL_NULL(closer) + QDEL_NULL(ui_boxes) + QDEL_NULL(ui_closer) + QDEL_NULL(ui_continuous) + QDEL_NULL(ui_left) + QDEL_NULL(ui_right) + // DO NOT USE QDEL_LIST_ASSOC. + for(var/i in ui_item_blocks) + qdel(ui_item_blocks[i]) //qdel the screen object not the item + ui_item_blocks.Cut() LAZYCLEARLIST(is_using) return ..() @@ -309,103 +327,6 @@ if(check_locked()) close_all() -/datum/component/storage/proc/_process_numerical_display() - . = list() - for(var/obj/item/I in accessible_items()) - if(QDELETED(I)) - continue - if(!.[I.type]) - .[I.type] = new /datum/numbered_display(I, 1) - else - var/datum/numbered_display/ND = .[I.type] - ND.number++ - . = sortTim(., /proc/cmp_numbered_displays_name_asc, associative = TRUE) - -//This proc determines the size of the inventory to be displayed. Please touch it only if you know what you're doing. -/datum/component/storage/proc/orient2hud(mob/user, maxcolumns) - var/list/accessible_contents = accessible_items() - var/adjusted_contents = length(accessible_contents) - - //Numbered contents display - var/list/datum/numbered_display/numbered_contents - if(display_numerical_stacking) - numbered_contents = _process_numerical_display() - adjusted_contents = numbered_contents.len - - var/columns = CLAMP(max_items, 1, maxcolumns ? maxcolumns : screen_max_columns) - var/rows = CLAMP(CEILING(adjusted_contents / columns, 1), 1, screen_max_rows) - standard_orient_objs(rows, columns, numbered_contents) - -//This proc draws out the inventory and places the items on it. It uses the standard position. -/datum/component/storage/proc/standard_orient_objs(rows, cols, list/obj/item/numerical_display_contents) - boxes.screen_loc = "[screen_start_x]:[screen_pixel_x],[screen_start_y]:[screen_pixel_y] to [screen_start_x+cols-1]:[screen_pixel_x],[screen_start_y+rows-1]:[screen_pixel_y]" - var/cx = screen_start_x - var/cy = screen_start_y - if(islist(numerical_display_contents)) - for(var/type in numerical_display_contents) - var/datum/numbered_display/ND = numerical_display_contents[type] - ND.sample_object.mouse_opacity = MOUSE_OPACITY_OPAQUE - ND.sample_object.screen_loc = "[cx]:[screen_pixel_x],[cy]:[screen_pixel_y]" - ND.sample_object.maptext = "[(ND.number > 1)? "[ND.number]" : ""]" - ND.sample_object.layer = ABOVE_HUD_LAYER - ND.sample_object.plane = ABOVE_HUD_PLANE - cx++ - if(cx - screen_start_x >= cols) - cx = screen_start_x - cy++ - if(cy - screen_start_y >= rows) - break - else - 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]" - O.maptext = "" - O.layer = ABOVE_HUD_LAYER - O.plane = ABOVE_HUD_PLANE - cx++ - if(cx - screen_start_x >= cols) - cx = screen_start_x - cy++ - if(cy - screen_start_y >= rows) - break - closer.screen_loc = "[screen_start_x + cols]:[screen_pixel_x],[screen_start_y]:[screen_pixel_y]" - -/datum/component/storage/proc/show_to(mob/M, set_screen_size = TRUE) - if(!M.client) - return FALSE - var/list/cview = getviewsize(M.client.view) - var/maxallowedscreensize = cview[1]-8 - if(set_screen_size) - current_maxscreensize = maxallowedscreensize - else if(current_maxscreensize) - maxallowedscreensize = current_maxscreensize - if(M.active_storage != src && (M.stat == CONSCIOUS)) - for(var/obj/item/I in accessible_items()) - if(I.on_found(M)) - return FALSE - if(M.active_storage) - M.active_storage.hide_from(M) - orient2hud(M, (isliving(M) ? maxallowedscreensize : 7)) - M.client.screen |= boxes - M.client.screen |= closer - M.client.screen |= accessible_items() - M.active_storage = src - LAZYOR(is_using, M) - return TRUE - -/datum/component/storage/proc/hide_from(mob/M) - if(!M.client) - return TRUE - var/atom/real_location = real_location() - M.client.screen -= boxes - M.client.screen -= closer - M.client.screen -= real_location.contents - if(M.active_storage == src) - M.active_storage = null - LAZYREMOVE(is_using, M) - return TRUE /datum/component/storage/proc/close(mob/M) hide_from(M) @@ -427,24 +348,6 @@ var/datum/component/storage/concrete/master = master() master.emp_act(source, severity) -//This proc draws out the inventory and places the items on it. tx and ty are the upper left tile and mx, my are the bottm right. -//The numbers are calculated from the bottom-left The bottom-left slot being 1,1. -/datum/component/storage/proc/orient_objs(tx, ty, mx, my) - var/atom/real_location = real_location() - var/cx = tx - var/cy = ty - boxes.screen_loc = "[tx]:,[ty] to [mx],[my]" - for(var/obj/O in real_location) - if(QDELETED(O)) - continue - O.screen_loc = "[cx],[cy]" - O.layer = ABOVE_HUD_LAYER - O.plane = ABOVE_HUD_PLANE - cx++ - if(cx > mx) - cx = tx - cy-- - closer.screen_loc = "[mx+1],[my]" //Resets something that is being removed from storage. /datum/component/storage/proc/_removal_reset(atom/movable/thing) diff --git a/code/datums/components/storage/ui.dm b/code/datums/components/storage/ui.dm new file mode 100644 index 0000000000..f73aff43ec --- /dev/null +++ b/code/datums/components/storage/ui.dm @@ -0,0 +1,259 @@ +/** + * Generates a list of numbered_display datums for the numerical display system. + */ +/datum/component/storage/proc/_process_numerical_display() + . = list() + for(var/obj/item/I in accessible_items()) + if(QDELETED(I)) + continue + if(!.[I.type]) + .[I.type] = new /datum/numbered_display(I, 1) + else + var/datum/numbered_display/ND = .[I.type] + ND.number++ + . = sortTim(., /proc/cmp_numbered_displays_name_asc, associative = TRUE) + +/** + * Orients all objects in legacy mode, and returns the objects to show to the user. + */ +/datum/component/storage/proc/orient2hud_legacy(mob/user, maxcolumns) + . = list() + var/list/accessible_contents = accessible_items() + var/adjusted_contents = length(accessible_contents) + + //Numbered contents display + var/list/datum/numbered_display/numbered_contents + if(display_numerical_stacking) + numbered_contents = _process_numerical_display() + adjusted_contents = numbered_contents.len + + var/columns = CLAMP(max_items, 1, maxcolumns ? maxcolumns : screen_max_columns) + var/rows = CLAMP(CEILING(adjusted_contents / columns, 1), 1, screen_max_rows) + + // First, boxes. + ui_boxes.screen_loc = "[screen_start_x]:[screen_pixel_x],[screen_start_y]:[screen_pixel_y] to [screen_start_x+columns-1]:[screen_pixel_x],[screen_start_y+rows-1]:[screen_pixel_y]" + . += ui_boxes + // Then, closer. + closer.screen_loc = "[screen_start_x + columns]:[screen_pixel_x],[screen_start_y]:[screen_pixel_y]" + . += ui_closer + // Then orient the actual items. + var/cx = screen_start_x + var/cy = screen_start_y + if(islist(numerical_display_contents)) + for(var/type in numerical_display_contents) + var/datum/numbered_display/ND = numerical_display_contents[type] + ND.sample_object.mouse_opacity = MOUSE_OPACITY_OPAQUE + ND.sample_object.screen_loc = "[cx]:[screen_pixel_x],[cy]:[screen_pixel_y]" + ND.sample_object.maptext = "[(ND.number > 1)? "[ND.number]" : ""]" + ND.sample_object.layer = ABOVE_HUD_LAYER + ND.sample_object.plane = ABOVE_HUD_PLANE + . += ND.sample_object + cx++ + if(cx - screen_start_x >= columns) + cx = screen_start_x + cy++ + if(cy - screen_start_y >= rows) + break + else + 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]" + O.maptext = "" + O.layer = ABOVE_HUD_LAYER + O.plane = ABOVE_HUD_PLANE + . += O + cx++ + if(cx - screen_start_x >= columns) + cx = screen_start_x + cy++ + if(cy - screen_start_y >= rows) + break + +/** + * Orients all objects in .. volumetric mode. + */ +/datum/component/storage/proc/orient2hud_volumetric(mob/user, maxcolumns) + . = list() + var/list/accessible_contents = accessible_items() + var/adjusted_contents = length(accessible_contents) + + //Numbered contents display + var/list/datum/numbered_display/numbered_contents + if(display_numerical_stacking) + numbered_contents = _process_numerical_display() + adjusted_contents = numbered_contents.len + + var/columns = CLAMP(max_items, 1, maxcolumns ? maxcolumns : screen_max_columns) + var/rows = CLAMP(CEILING(adjusted_contents / columns, 1), 1, screen_max_rows) + + // First, continuous section. + ui_continuous.screen_loc = "[screen_start_x]:[screen_pixel_x],[screen_start_y]:[screen_pixel_y] to [screen_start_x+columns-1]:[screen_pixel_x],[screen_start_y+rows-1]:[screen_pixel_y]" + . += ui_continuous + // Then, left and right. + ui_left.screen_loc = "[screen_start_x]:[screen_pixel_x - 2],[screen_start_y]:[screen_pixel_y] to [screen_start_x]:[screen_pixel_x - 2],[screen_start_y+rows-1]:[screen_pixel_y]" + . += ui_left + ui_right.screen_loc = "[screen_start_x+columns-1]:[screen_pixel_x + 2],[screen_start_y]:[screen_pixel_y] to [screen_start_x+columns-1]:[screen_pixel_x + 2],[screen_start_y+rows-1]:[screen_pixel_y]" + . += ui_right + // Then, closer. + closer.screen_loc = "[screen_start_x + columns]:[screen_pixel_x],[screen_start_y]:[screen_pixel_y]" + . += ui_closer + // Generate ui_item_blocks for missing ones. + + + + + + + var/cx = screen_start_x + var/cy = screen_start_y + if(islist(numerical_display_contents)) + for(var/type in numerical_display_contents) + var/datum/numbered_display/ND = numerical_display_contents[type] + ND.sample_object.mouse_opacity = MOUSE_OPACITY_OPAQUE + ND.sample_object.screen_loc = "[cx]:[screen_pixel_x],[cy]:[screen_pixel_y]" + ND.sample_object.maptext = "[(ND.number > 1)? "[ND.number]" : ""]" + ND.sample_object.layer = ABOVE_HUD_LAYER + ND.sample_object.plane = ABOVE_HUD_PLANE + . += ND.sample_object + cx++ + if(cx - screen_start_x >= columns) + cx = screen_start_x + cy++ + if(cy - screen_start_y >= rows) + break + else + 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]" + O.maptext = "" + O.layer = ABOVE_HUD_LAYER + O.plane = ABOVE_HUD_PLANE + . += O + cx++ + if(cx - screen_start_x >= columns) + cx = screen_start_x + cy++ + if(cy - screen_start_y >= rows) + break + +/** + * Shows our UI to a mob. + */ +/datum/component/storage/proc/ui_show(mob/M, set_screen_size = TRUE) + if(!M.client) + return FALSE + 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) + if(M.active_storage) + M.active_storage.ui_hide(M) + M.active_storage = src + LAZOR(is_using, M) + if(volumetric_ui()) + //new volumetric ui bay-style + M.client.screen |= orient2hud_volumetric(M, maxallowedscreensize) + else + //old ui + M.client.screen |= orient2hud_legacy(M, maxallowedscreensize) + return TRUE + +/** + * VV hooked to ensure no lingering screen objects. + */ +/datum/component/storage/vv_edit_var(var_name, var_value) + var/list/old + if(var_name == NAMEOF(storage_flags)) + old = is_using.Copy() + for(var/i in is_using) + ui_hide(i) + . = ..() + if(old) + for(var/i in old) + ui_show(i) + +/** + * Proc triggered by signal to ensure logging out clients don't linger. + */ +/datum/component/storage/proc/on_logout(datum/source, client/C) + ui_hide(source) + +/** + * Hides our UI from a mob + */ +/datum/component/storage/proc/ui_hide(mob/M) + if(!M.client) + return TRUE + M.client.screen -= list(ui_boxes, ui_closer, ui_left, ui_right, ui_continuous, get_ui_item_objects_hide()) + if(M.active_storage == src) + M.active_storage = null + LAZYREMOVE(is_using, M) + return TRUE + +/** + * Returns TRUE if we are using volumetric UI instead of box UI + */ +/datum/component/storage/proc/volumetric_ui() + var/atom/real_location = real_location() + return (storage_flags & STORAGE_LIMIT_VOLUME) && (length(real_location.contents) <= MAXIMUM_VOLUMETRIC_OBJECTS) + +/** + * Gets the ui item objects to ui_hide. + */ +/datum/component/storage/proc/get_ui_item_objects_hide() + if(!volumetric_ui()) + var/atom/real_location = real_location() + return real_location.contents + else + . = list() + for(var/i in ui_item_blocks) + . += ui_item_blocks[i] //get the block not the item + +/** + * 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 + +/** + * 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 + +/** + * Gets our ui_right, making it if it doesn't exist. + */ +/datum/component/storage/proc/get_ui_right() + if(!ui_right) + ui_right = new(null, src) + return ui_right + +/** + * 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 + +/** + * 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 diff --git a/code/modules/mob/logout.dm b/code/modules/mob/logout.dm index e5aaa016bd..536eacca7d 100644 --- a/code/modules/mob/logout.dm +++ b/code/modules/mob/logout.dm @@ -1,4 +1,5 @@ /mob/Logout() + SEND_SIGNAL(src, COMSIG_MOB_CLIENT_LOGOUT, client) log_message("[key_name(src)] is no longer owning mob [src]([src.type])", LOG_OWNERSHIP) SStgui.on_logout(src) unset_machine() diff --git a/icons/mob/screen_gen.dmi b/icons/mob/screen_gen.dmi index ea946f60d9..ffbffcab0d 100644 Binary files a/icons/mob/screen_gen.dmi and b/icons/mob/screen_gen.dmi differ