diff --git a/code/__DEFINES/dcs/signals.dm b/code/__DEFINES/dcs/signals.dm index 4328e6da90aa..15220b259c3c 100644 --- a/code/__DEFINES/dcs/signals.dm +++ b/code/__DEFINES/dcs/signals.dm @@ -537,6 +537,10 @@ ///from [/obj/structure/closet/supplypod/proc/endlaunch]: #define COMSIG_SUPPLYPOD_LANDED "supplypodgoboom" +// Item mouse siganls +#define COMSIG_ITEM_MOUSE_EXIT "item_mouse_exit" //from base of obj/item/MouseExited(): (location, control, params) +#define COMSIG_ITEM_MOUSE_ENTER "item_mouse_enter" //from base of obj/item/MouseEntered(): (location, control, params) + ///Called when an item is being offered, from [/obj/item/proc/on_offered(mob/living/carbon/offerer)] #define COMSIG_ITEM_OFFERING "item_offering" ///Interrupts the offer proc diff --git a/code/__DEFINES/inventory.dm b/code/__DEFINES/inventory.dm index e644340cd333..546c710e5121 100644 --- a/code/__DEFINES/inventory.dm +++ b/code/__DEFINES/inventory.dm @@ -1,19 +1,5 @@ /*ALL DEFINES RELATED TO INVENTORY OBJECTS, MANAGEMENT, ETC, GO HERE*/ -//ITEM INVENTORY WEIGHT, FOR w_class -/// Usually items smaller then a human hand, (e.g. playing cards, lighter, scalpel, coins/holochips) -#define WEIGHT_CLASS_TINY 1 -/// Pockets can hold small and tiny items, (e.g. flashlight, multitool, grenades, GPS device) -#define WEIGHT_CLASS_SMALL 2 -/// Standard backpacks can carry tiny, small & normal items, (e.g. fire extinguisher, stun baton, gas mask, metal sheets) -#define WEIGHT_CLASS_NORMAL 3 -/// Items that can be weilded or equipped but not stored in an inventory, (e.g. defibrillator, backpack, space suits) -#define WEIGHT_CLASS_BULKY 4 -/// Usually represents objects that require two hands to operate, (e.g. shotgun, two-handed melee weapons) -#define WEIGHT_CLASS_HUGE 5 -/// Essentially means it cannot be picked up or placed in an inventory, (e.g. mech parts, safe) -#define WEIGHT_CLASS_GIGANTIC 6 - //Inventory depth: limits how many nested storage items you can access directly. //1: stuff in mob, 2: stuff in backpack, 3: stuff in box in backpack, etc #define INVENTORY_DEPTH 3 diff --git a/code/__DEFINES/layers.dm b/code/__DEFINES/layers.dm index fd61f4f1123b..40281eb3ded1 100644 --- a/code/__DEFINES/layers.dm +++ b/code/__DEFINES/layers.dm @@ -152,12 +152,22 @@ #define HUD_PLANE 42 #define HUD_LAYER 42 #define HUD_RENDER_TARGET "HUD_PLANE" -#define ABOVE_HUD_PLANE 43 -#define ABOVE_HUD_LAYER 43 +#define VOLUMETRIC_STORAGE_BOX_PLANE 44 +#define VOLUMETRIC_STORAGE_BOX_LAYER 44 +#define VOLUMETRIC_STORAGE_BOX_RENDER_TARGET "VOLUME_STORAGE_BOX_PLANE" + +#define VOLUMETRIC_STORAGE_ITEM_PLANE 46 +#define VOLUMETRIC_STORAGE_ITEM_LAYER 46 +#define VOLUMETRIC_STORAGE_ACTIVE_ITEM_LAYER 48 +#define VOLUMETRIC_STORAGE_ACTIVE_ITEM_PLANE 48 +#define VOLUMETRIC_STORAGE_ITEM_RENDER_TARGET "VOLUME_STORAGE_ITEM_PLANE" + +#define ABOVE_HUD_PLANE 50 +#define ABOVE_HUD_LAYER 50 #define ABOVE_HUD_RENDER_TARGET "ABOVE_HUD_PLANE" -#define SPLASHSCREEN_LAYER 54 -#define SPLASHSCREEN_PLANE 54 +#define SPLASHSCREEN_LAYER 75 +#define SPLASHSCREEN_PLANE 75 #define ADMIN_POPUP_LAYER 1 diff --git a/code/__DEFINES/storage.dm b/code/__DEFINES/storage.dm new file mode 100644 index 000000000000..7b2bfc5d18b7 --- /dev/null +++ b/code/__DEFINES/storage.dm @@ -0,0 +1,85 @@ +// storage_flags variable on /datum/component/storage + +// Storage limits. These can be combined (and usually are combined). +/// Check max_items and contents.len when trying to insert +#define STORAGE_LIMIT_MAX_ITEMS (1<<0) +/// Check max_combined_w_class. +#define STORAGE_LIMIT_COMBINED_W_CLASS (1<<1) +/// Use the new volume system. Will automatically force rendering to use the new volume/baystation scaling UI so this is kind of incompatible with stuff like stack storage etc etc. +#define STORAGE_LIMIT_VOLUME (1<<2) +/// Use max_w_class +#define STORAGE_LIMIT_MAX_W_CLASS (1<<3) + +#define STORAGE_FLAGS_LEGACY_DEFAULT (STORAGE_LIMIT_MAX_ITEMS | STORAGE_LIMIT_COMBINED_W_CLASS | STORAGE_LIMIT_MAX_W_CLASS) +#define STORAGE_FLAGS_VOLUME_DEFAULT (STORAGE_LIMIT_VOLUME | STORAGE_LIMIT_MAX_W_CLASS) + +// UI defines +/// Size of volumetric box icon +#define VOLUMETRIC_STORAGE_BOX_ICON_SIZE 32 +/// Size of EACH left/right border icon for volumetric boxes +#define VOLUMETRIC_STORAGE_BOX_BORDER_SIZE 1 +/// Minimum pixels an item must have in volumetric scaled storage UI +#define MINIMUM_PIXELS_PER_ITEM 16 +/// 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 +/// How much padding to give between items +#define VOLUMETRIC_STORAGE_ITEM_PADDING 3 +/// How much padding to give to edges +#define VOLUMETRIC_STORAGE_EDGE_PADDING 1 + +//ITEM INVENTORY WEIGHT, FOR w_class +/// Usually items smaller then a human hand, ex: Playing Cards, Lighter, Scalpel, Coins/Money +#define WEIGHT_CLASS_TINY 1 +/// Fits within a small pocket, ex: Flashlight, Multitool, Grenades, GPS Device +#define WEIGHT_CLASS_SMALL 2 +/// Fits within a small satchel, ex: Fire extinguisher, Stunbaton, Gas Mask, Metal Sheets +#define WEIGHT_CLASS_NORMAL 3 +/// Items that can be wielded or equipped, (e.g. defibrillator, backpack, space suits). (Often barely) fits inside backpacks and duffels. +#define WEIGHT_CLASS_BULKY 4 +/// Usually represents objects that require two hands to operate, (e.g. shotgun, two-handed melee weapons) May fit on some inventory slots. +#define WEIGHT_CLASS_HUGE 5 +/// Essentially means it cannot be picked up or placed in an inventory, ex: Mech Parts, Safe - Can not fit in Boh +#define WEIGHT_CLASS_GIGANTIC 6 + +// PLEASE KEEP ALL VOLUME DEFINES IN THIS FILE, it's going to be hell to keep track of them later. +#define DEFAULT_VOLUME_TINY 2 +#define DEFAULT_VOLUME_SMALL 3 +#define DEFAULT_VOLUME_NORMAL 4 +#define DEFAULT_VOLUME_BULKY 8 +#define DEFAULT_VOLUME_HUGE 16 +#define DEFAULT_VOLUME_GIGANTIC 32 + +GLOBAL_LIST_INIT(default_weight_class_to_volume, list( + "[WEIGHT_CLASS_TINY]" = DEFAULT_VOLUME_TINY, + "[WEIGHT_CLASS_SMALL]" = DEFAULT_VOLUME_SMALL, + "[WEIGHT_CLASS_NORMAL]" = DEFAULT_VOLUME_NORMAL, + "[WEIGHT_CLASS_BULKY]" = DEFAULT_VOLUME_BULKY, + "[WEIGHT_CLASS_HUGE]" = DEFAULT_VOLUME_HUGE, + "[WEIGHT_CLASS_GIGANTIC]" = DEFAULT_VOLUME_GIGANTIC + )) + +/// Macro for automatically getting the volume of an item from its w_class. +#define AUTO_SCALE_VOLUME(w_class) (GLOB.default_weight_class_to_volume["[w_class]"]) +/// Macro for automatically getting the volume of a storage item from its max_w_class and max_combined_w_class. +#define AUTO_SCALE_STORAGE_VOLUME(w_class, max_combined_w_class) (AUTO_SCALE_VOLUME(w_class) * (max_combined_w_class / w_class)) + +// Let's keep all of this in one place. given what we put above anyways.. + +// volume amount for items +#define ITEM_VOLUME_DISK DEFAULT_VOLUME_TINY +#define ITEM_VOLUME_MOB 45//just over half of a duffelbag. Prevents mob_holder stacking in volumetric + +// #define SAMPLE_VOLUME_AMOUNT 2 + +// max_weight_class for storages +// +#define MAX_WEIGHT_CLASS_S_CONTAINER WEIGHT_CLASS_SMALL +#define MAX_WEIGHT_CLASS_M_CONTAINER WEIGHT_CLASS_NORMAL +#define MAX_WEIGHT_CLASS_BACKPACK WEIGHT_CLASS_BULKY + +// max_volume for storages +#define STORAGE_VOLUME_CONTAINER_M (DEFAULT_VOLUME_NORMAL * 2) +#define STORAGE_VOLUME_SATCHEL (DEFAULT_VOLUME_NORMAL * 6) +#define STORAGE_VOLUME_BACKPACK (DEFAULT_VOLUME_NORMAL * 7) +#define STORAGE_VOLUME_DUFFLEBAG (DEFAULT_VOLUME_NORMAL * 10) +#define STORAGE_VOLUME_BAG_OF_HOLDING (DEFAULT_VOLUME_NORMAL * 20) diff --git a/code/__HELPERS/_lists.dm b/code/__HELPERS/_lists.dm index 39dccd4e64d7..eb380a38d2af 100644 --- a/code/__HELPERS/_lists.dm +++ b/code/__HELPERS/_lists.dm @@ -16,7 +16,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 LAZYISIN(L, V) (L ? (V in L) : FALSE) diff --git a/code/_globalvars/bitfields.dm b/code/_globalvars/bitfields.dm index 406f0bb0b101..3d1c5778ee3c 100644 --- a/code/_globalvars/bitfields.dm +++ b/code/_globalvars/bitfields.dm @@ -263,6 +263,14 @@ DEFINE_BITFIELD(zap_flags, list( "ZAP_OBJ_DAMAGE" = ZAP_OBJ_DAMAGE, )) + +DEFINE_BITFIELD(storage_flags, list( + "STORAGE_LIMIT_MAX_ITEMS" = STORAGE_LIMIT_MAX_ITEMS, + "STORAGE_LIMIT_MAX_W_CLASS" = STORAGE_LIMIT_MAX_W_CLASS, + "STORAGE_LIMIT_COMBINED_W_CLASS" = STORAGE_LIMIT_COMBINED_W_CLASS, + "STORAGE_LIMIT_VOLUME" = STORAGE_LIMIT_VOLUME, +)) + DEFINE_BITFIELD(bodytype, list( "BODYTYPE_ORGANIC" = BODYTYPE_ORGANIC, "BODYTYPE_ROBOTIC" = BODYTYPE_ROBOTIC, diff --git a/code/_onclick/hud/screen_objects.dm b/code/_onclick/hud/screen_objects.dm index 557096d83c82..f82acb84c3cc 100644 --- a/code/_onclick/hud/screen_objects.dm +++ b/code/_onclick/hud/screen_objects.dm @@ -241,20 +241,20 @@ user.swap_hand(held_index) return TRUE -/atom/movable/screen/close - name = "close" - layer = ABOVE_HUD_LAYER - plane = ABOVE_HUD_PLANE - icon_state = "backpack_close" +// /atom/movable/screen/close +// name = "close" +// layer = ABOVE_HUD_LAYER +// plane = ABOVE_HUD_PLANE +// icon_state = "backpack_close" -/atom/movable/screen/close/Initialize(mapload, new_master) - . = ..() - master = new_master +// /atom/movable/screen/close/Initialize(mapload, new_master) +// . = ..() +// master = new_master -/atom/movable/screen/close/Click() - var/datum/component/storage/S = master - S.hide_from(usr) - return TRUE +// /atom/movable/screen/close/Click() +// var/datum/component/storage/S = master +// S.hide_from(usr) +// return TRUE /atom/movable/screen/drop name = "drop" @@ -437,30 +437,6 @@ icon_state = "[base_icon_state][user.resting ? 0 : null]" return ..() -/atom/movable/screen/storage - name = "storage" - icon_state = "block" - screen_loc = "7,7 to 10,8" - layer = HUD_LAYER - plane = HUD_PLANE - -/atom/movable/screen/storage/Initialize(mapload, new_master) - . = ..() - master = new_master - -/atom/movable/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 - /atom/movable/screen/throw_catch name = "throw/catch" icon = 'icons/hud/screen_midnight.dmi' diff --git a/code/_onclick/hud/storage.dm b/code/_onclick/hud/storage.dm new file mode 100644 index 000000000000..55156d9b0de5 --- /dev/null +++ b/code/_onclick/hud/storage.dm @@ -0,0 +1,197 @@ +/atom/movable/screen/storage + name = "storage" + var/insertion_click = FALSE + +/atom/movable/screen/storage/Initialize(mapload, new_master) + . = ..() + master = new_master + +/atom/movable/screen/storage/Click(location, control, params) + if(!insertion_click) + return ..() + if(hud?.mymob && (hud.mymob != usr)) + return + // just redirect clicks + if(master) + var/obj/item/I = usr.get_active_held_item() + if(I) + master.attackby(null, I, usr, params) + return TRUE + +/atom/movable/screen/storage/boxes + name = "storage" + icon_state = "block" + screen_loc = "7,7 to 10,8" + layer = HUD_LAYER + plane = HUD_PLANE + insertion_click = TRUE + +/atom/movable/screen/storage/close + name = "close" + layer = ABOVE_HUD_LAYER + plane = ABOVE_HUD_PLANE + icon_state = "backpack_close" + +/atom/movable/screen/storage/close/Click() + var/datum/component/storage/S = master + S.close(usr) + return TRUE + +/atom/movable/screen/storage/left + icon_state = "storage_start" + insertion_click = TRUE + +/atom/movable/screen/storage/right + icon_state = "storage_end" + insertion_click = TRUE + +/atom/movable/screen/storage/continuous + icon_state = "storage_continue" + insertion_click = TRUE + +/atom/movable/screen/storage/volumetric_box + icon_state = "stored_continue" + layer = VOLUMETRIC_STORAGE_BOX_LAYER + plane = VOLUMETRIC_STORAGE_BOX_PLANE + var/obj/item/our_item + +/atom/movable/screen/storage/volumetric_box/Initialize(mapload, new_master, obj/item/our_item) + src.our_item = our_item + RegisterSignal(our_item, COMSIG_ITEM_MOUSE_ENTER, .proc/on_item_mouse_enter) + RegisterSignal(our_item, COMSIG_ITEM_MOUSE_EXIT, .proc/on_item_mouse_exit) + return ..() + +/atom/movable/screen/storage/volumetric_box/Destroy() + makeItemInactive() + our_item = null + return ..() + +/atom/movable/screen/storage/volumetric_box/Click(location, control, params) + return our_item.Click(location, control, params) + +/atom/movable/screen/storage/volumetric_box/MouseDrop(atom/over, src_location, over_location, src_control, over_control, params) + return our_item.MouseDrop(over, src_location, over_location, src_control, over_control, params) + +/atom/movable/screen/storage/volumetric_box/MouseExited(location, control, params) + makeItemInactive() + +/atom/movable/screen/storage/volumetric_box/MouseEntered(location, control, params) + makeItemActive() + +/atom/movable/screen/storage/volumetric_box/proc/on_item_mouse_enter() + makeItemActive() + +/atom/movable/screen/storage/volumetric_box/proc/on_item_mouse_exit() + makeItemInactive() + +/atom/movable/screen/storage/volumetric_box/proc/makeItemInactive() + return + +/atom/movable/screen/storage/volumetric_box/proc/makeItemActive() + return + +/atom/movable/screen/storage/volumetric_box/center + icon_state = "stored_continue" + var/atom/movable/screen/storage/volumetric_edge/stored_left/left + var/atom/movable/screen/storage/volumetric_edge/stored_right/right + var/atom/movable/screen/storage/item_holder/holder + var/pixel_size + +/atom/movable/screen/storage/volumetric_box/center/Initialize(mapload, new_master, our_item) + left = new(null, src, our_item) + right = new(null, src, our_item) + return ..() + +/atom/movable/screen/storage/volumetric_box/center/Destroy() + QDEL_NULL(left) + QDEL_NULL(right) + vis_contents.Cut() + if(holder) + QDEL_NULL(holder) + return ..() + +/atom/movable/screen/storage/volumetric_box/center/proc/on_screen_objects() + return list(src) + +/** + * Sets the size of this box screen object and regenerates its left/right borders. This includes the actual border's size! + */ +/atom/movable/screen/storage/volumetric_box/center/proc/set_pixel_size(pixels) + if(pixel_size == pixels) + return + pixel_size = pixels + cut_overlays() + vis_contents.Cut() + //our icon size is 32 pixels. + 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) + +/atom/movable/screen/storage/volumetric_box/center/makeItemInactive() + if(!holder) + return + holder.layer = VOLUMETRIC_STORAGE_ITEM_LAYER + holder.plane = VOLUMETRIC_STORAGE_ITEM_PLANE + +/atom/movable/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 + +/atom/movable/screen/storage/volumetric_edge + layer = VOLUMETRIC_STORAGE_BOX_LAYER + plane = VOLUMETRIC_STORAGE_BOX_PLANE + +/atom/movable/screen/storage/volumetric_edge/Initialize(mapload, master, our_item) + src.master = master + return ..() + +/atom/movable/screen/storage/volumetric_edge/Click(location, control, params) + return master.Click(location, control, params) + +/atom/movable/screen/storage/volumetric_edge/MouseDrop(atom/over, src_location, over_location, src_control, over_control, params) + return master.MouseDrop(over, src_location, over_location, src_control, over_control, params) + +/atom/movable/screen/storage/volumetric_edge/MouseExited(location, control, params) + return master.MouseExited(location, control, params) + +/atom/movable/screen/storage/volumetric_edge/MouseEntered(location, control, params) + return master.MouseEntered(location, control, params) + +/atom/movable/screen/storage/volumetric_edge/stored_left + icon_state = "stored_start" + appearance_flags = APPEARANCE_UI | KEEP_APART | RESET_TRANSFORM // Yes I know RESET_TRANSFORM is in APPEARANCE_UI but we're hard-asserting this incase someone changes it. + +/atom/movable/screen/storage/volumetric_edge/stored_right + icon_state = "stored_end" + appearance_flags = APPEARANCE_UI | KEEP_APART | RESET_TRANSFORM + +/atom/movable/screen/storage/item_holder + var/obj/item/our_item + vis_flags = NONE + +/atom/movable/screen/storage/item_holder/Initialize(mapload, new_master, obj/item/I) + . = ..() + our_item = I + vis_contents += I + +/atom/movable/screen/storage/item_holder/Destroy() + vis_contents.Cut() + our_item = null + return ..() + +/atom/movable/screen/storage/item_holder/Click(location, control, params) + return our_item.Click(location, control, params) diff --git a/code/datums/components/storage/concrete/_concrete.dm b/code/datums/components/storage/concrete/_concrete.dm index 4198ba5b974d..b182ecce5bc9 100644 --- a/code/datums/components/storage/concrete/_concrete.dm +++ b/code/datums/components/storage/concrete/_concrete.dm @@ -57,7 +57,7 @@ _contents_limbo = null if(_user_limbo) for(var/i in _user_limbo) - show_to(i) + ui_show(i) _user_limbo = null /datum/component/storage/concrete/_insert_physical_item(obj/item/I, override = FALSE) diff --git a/code/datums/components/storage/concrete/rped.dm b/code/datums/components/storage/concrete/rped.dm index 455eb985f090..58a520d3a7bd 100644 --- a/code/datums/components/storage/concrete/rped.dm +++ b/code/datums/components/storage/concrete/rped.dm @@ -6,6 +6,7 @@ max_w_class = WEIGHT_CLASS_NORMAL max_combined_w_class = 100 max_items = 50 + storage_flags = STORAGE_FLAGS_LEGACY_DEFAULT display_numerical_stacking = TRUE /datum/component/storage/concrete/rped/can_be_inserted(obj/item/I, stop_messages, mob/M) diff --git a/code/datums/components/storage/concrete/stack.dm b/code/datums/components/storage/concrete/stack.dm index 319d1d4b3d41..19ea4fa58584 100644 --- a/code/datums/components/storage/concrete/stack.dm +++ b/code/datums/components/storage/concrete/stack.dm @@ -1,6 +1,7 @@ //Stack-only storage. /datum/component/storage/concrete/stack display_numerical_stacking = TRUE + storage_flags = STORAGE_FLAGS_LEGACY_DEFAULT var/max_combined_stack_amount = 300 max_w_class = WEIGHT_CLASS_NORMAL max_combined_w_class = WEIGHT_CLASS_NORMAL * 14 diff --git a/code/datums/components/storage/storage.dm b/code/datums/components/storage/storage.dm index f10332a3129a..66e054e06d43 100644 --- a/code/datums/components/storage/storage.dm +++ b/code/datums/components/storage/storage.dm @@ -26,9 +26,16 @@ var/locked = FALSE //when locked nothing can see inside or use it. var/locked_flavor = "locked" //prevents tochat messages related to locked from sending - var/max_w_class = WEIGHT_CLASS_SMALL //max size of objects that will fit. - var/max_combined_w_class = 14 //max combined sizes of objects that will fit. - var/max_items = 7 //max number of objects that will fit. + /// Storage flags, including what kinds of limiters we use for how many items we can hold + var/storage_flags = STORAGE_FLAGS_LEGACY_DEFAULT + /// Max w_class we can hold. Applies to [STORAGE_LIMIT_COMBINED_W_CLASS] and [STORAGE_LIMIT_VOLUME] + var/max_w_class = WEIGHT_CLASS_SMALL + /// Max combined w_class. Applies to [STORAGE_LIMIT_COMBINED_W_CLASS] + var/max_combined_w_class = WEIGHT_CLASS_SMALL * 7 + /// Max items we can hold. Applies to [STORAGE_LIMIT_MAX_ITEMS] + var/max_items = 7 + /// Max volume we can hold. Applies to [STORAGE_LIMIT_VOLUME]. Auto scaled on New() if unset. + var/max_volume var/emp_shielded = FALSE @@ -44,8 +51,8 @@ var/display_numerical_stacking = FALSE //stack things of the same type and show as a single object with a number. - var/atom/movable/screen/storage/boxes //storage display object - var/atom/movable/screen/close/closer //close button object + /// 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. @@ -63,14 +70,15 @@ var/screen_start_y = 2 //End + var/limited_random_access = FALSE //Quick if statement in accessible_items to determine if we care at all about what people can access at once. + var/limited_random_access_stack_position = 0 //If >0, can only access top items + var/limited_random_access_stack_bottom_up = FALSE + /datum/component/storage/Initialize(datum/component/storage/concrete/master) if(!isatom(parent)) return COMPONENT_INCOMPATIBLE if(master) change_master(master) - boxes = new(null, src) - closer = new(null, src) - orient2hud() RegisterSignal(parent, COMSIG_CONTAINS_STORAGE, .proc/on_check) RegisterSignal(parent, COMSIG_IS_STORAGE_LOCKED, .proc/check_locked) @@ -113,11 +121,16 @@ /datum/component/storage/Destroy() close_all() - QDEL_NULL(boxes) - QDEL_NULL(closer) + 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() @@ -171,6 +184,19 @@ var/datum/component/storage/concrete/master = master() return master? master.real_location() : null +//What players can access +//this proc can probably eat a refactor at some point. +/datum/component/storage/proc/accessible_items(random_access = TRUE) + var/list/contents = contents() + if(contents) + if(limited_random_access && random_access) + if(limited_random_access_stack_position && (length(contents) > limited_random_access_stack_position)) + if(limited_random_access_stack_bottom_up) + contents.Cut(1, limited_random_access_stack_position + 1) + else + contents.Cut(1, length(contents) - limited_random_access_stack_position + 1) + return contents + /datum/component/storage/proc/canreach_react(datum/source, list/next) SIGNAL_HANDLER @@ -189,7 +215,7 @@ var/atom/A = parent for(var/mob/living/L in can_see_contents()) if(!L.CanReach(A)) - hide_from(L) + ui_hide(L) /datum/component/storage/proc/attack_self(datum/source, mob/M) SIGNAL_HANDLER @@ -312,7 +338,7 @@ if(!_target) _target = get_turf(parent) if(usr) - hide_from(usr) + ui_hide(usr) var/list/contents = contents() var/atom/real_location = real_location() for(var/obj/item/I in contents) @@ -328,109 +354,12 @@ if(locked) close_all() -/datum/component/storage/proc/_process_numerical_display() - . = list() - var/atom/real_location = real_location() - for(var/obj/item/I in real_location.contents) - if(QDELETED(I)) - continue - if(!.["[I.type]-[I.name]"]) - .["[I.type]-[I.name]"] = new /datum/numbered_display(I, 1) - else - var/datum/numbered_display/ND = .["[I.type]-[I.name]"] - ND.number++ - -//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() - var/atom/real_location = real_location() - var/adjusted_contents = real_location.contents.len - - //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, 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 - var/atom/real_location = real_location() - for(var/obj/O in real_location) - 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) - if(!M.client) - return FALSE - var/atom/real_location = real_location() - if(M.active_storage != src && (M.stat == CONSCIOUS)) - for(var/obj/item/I in real_location) - if(I.on_found(M)) - return FALSE - if(M.active_storage) - M.active_storage.hide_from(M) - orient2hud() - M.client.screen |= boxes - M.client.screen |= closer - M.client.screen |= real_location.contents - M.set_active_storage(src) - LAZYOR(is_using, M) - RegisterSignal(M, COMSIG_PARENT_QDELETING, .proc/mob_deleted) - return TRUE - /datum/component/storage/proc/mob_deleted(datum/source) SIGNAL_HANDLER - hide_from(source) - -/datum/component/storage/proc/hide_from(mob/M) - if(M.active_storage == src) - M.set_active_storage(null) - LAZYREMOVE(is_using, M) - - UnregisterSignal(M, COMSIG_PARENT_QDELETING) - 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 - return TRUE + ui_hide(source) /datum/component/storage/proc/close(mob/M) - hide_from(M) + ui_hide(usr) /datum/component/storage/proc/close_all() SIGNAL_HANDLER @@ -448,25 +377,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) if(!istype(thing)) @@ -477,9 +387,7 @@ return master._removal_reset(thing) /datum/component/storage/proc/_remove_and_refresh(datum/source, atom/movable/thing) - SIGNAL_HANDLER - - _removal_reset(thing) + _removal_reset(thing) // THIS NEEDS TO HAPPEN AFTER SO LAYERING DOESN'T BREAK! refresh_mob_views() //Call this proc to handle the removal of an item from the storage item. The item will be moved to the new_location target, if that is null it's being deleted @@ -496,7 +404,7 @@ var/list/seeing = can_see_contents() for(var/i in seeing) - show_to(i) + ui_show(i) return TRUE /datum/component/storage/proc/can_see_contents() @@ -615,7 +523,7 @@ to_chat(M, "[parent] seems to be [locked_flavor]!") return FALSE if(force || M.CanReach(parent, view_only = TRUE)) - show_to(M) + ui_show(M) /datum/component/storage/proc/mousedrop_receive(datum/source, atom/movable/O, mob/M) SIGNAL_HANDLER @@ -644,10 +552,6 @@ host.add_fingerprint(M) to_chat(M, "[host] seems to be [locked_flavor]!") return FALSE - if(real_location.contents.len >= max_items) - if(!stop_messages) - to_chat(M, "[host] is full, make some space!") - return FALSE //Storage item is full if(length(can_hold)) if(!is_type_in_typecache(I, can_hold)) if(!stop_messages) @@ -657,22 +561,34 @@ if(!stop_messages) to_chat(M, "[host] cannot hold [I]!") return FALSE - if(I.w_class > max_w_class && !is_type_in_typecache(I, exception_hold)) - if(!stop_messages) - to_chat(M, "[I] is too big for [host]!") - return FALSE - var/datum/component/storage/biggerfish = real_location.loc.GetComponent(/datum/component/storage) - if(biggerfish && biggerfish.max_w_class < max_w_class)//return false if we are inside of another container, and that container has a smaller max_w_class than us (like if we're a bag in a box) - if(!stop_messages) - to_chat(M, "[I] can't fit in [host] while [real_location.loc] is in the way!") - return FALSE - var/sum_w_class = I.w_class - for(var/obj/item/_I in real_location) - sum_w_class += _I.w_class //Adds up the combined w_classes which will be in the storage item if the item is added to it. - if(sum_w_class > max_combined_w_class) - if(!stop_messages) - to_chat(M, "[I] won't fit in [host], make some space!") - return FALSE + // STORAGE LIMITS + if(storage_flags & STORAGE_LIMIT_MAX_ITEMS) + if(real_location.contents.len >= max_items) + if(!stop_messages) + to_chat(M, "[host] has too much junk in it, make some space!") + return FALSE //Storage item is full + if(storage_flags & STORAGE_LIMIT_MAX_W_CLASS) + if(I.w_class > max_w_class) + if(!stop_messages) + to_chat(M, "[I] is much too long for [host]!") + return FALSE + if(storage_flags & STORAGE_LIMIT_COMBINED_W_CLASS) + var/sum_w_class = I.w_class + for(var/obj/item/_I in real_location) + sum_w_class += _I.w_class //Adds up the combined w_classes which will be in the storage item if the item is added to it. + if(sum_w_class > max_combined_w_class) + if(!stop_messages) + to_chat(M, "[I] won't fit in [host], make some space!") + return FALSE + if(storage_flags & STORAGE_LIMIT_VOLUME) + var/sum_volume = I.get_w_volume() + for(var/obj/item/_I in real_location) + sum_volume += _I.get_w_volume() + if(sum_volume > get_max_volume()) + if(!stop_messages) + to_chat(M, "[I] is too large to fit in [host], make some space!") + return FALSE + ///////////////// if(isitem(host)) var/obj/item/IP = host var/datum/component/storage/STR_I = I.GetComponent(/datum/component/storage) @@ -822,7 +738,7 @@ if(locked) to_chat(user, "[parent] seems to be [locked_flavor]!") else - show_to(user) + ui_show(user) if(use_sound) playsound(A, use_sound, 50, TRUE, -5) @@ -848,7 +764,7 @@ /datum/component/storage/proc/signal_hide_attempt(datum/source, mob/target) SIGNAL_HANDLER - return hide_from(target) + return ui_hide(target) /datum/component/storage/proc/on_alt_click(datum/source, mob/user) SIGNAL_HANDLER @@ -895,3 +811,9 @@ to_chat(user, "[parent] now picks up all items in a tile at once.") if(COLLECT_ONE) to_chat(user, "[parent] now picks up one item at a time.") + +/** + * Gets our max volume + */ +/datum/component/storage/proc/get_max_volume() + return max_volume || AUTO_SCALE_STORAGE_VOLUME(max_w_class, max_combined_w_class) diff --git a/code/datums/components/storage/ui.dm b/code/datums/components/storage/ui.dm new file mode 100644 index 000000000000..e03b8132bc8f --- /dev/null +++ b/code/datums/components/storage/ui.dm @@ -0,0 +1,273 @@ +/** + * 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, src) + else + var/datum/numbered_display/ND = .[I.type] + ND.number++ + +/** + * 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) + var/atom/movable/screen/storage/close/ui_close + var/atom/movable/screen/storage/boxes/ui_boxes + + //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 = get_ui_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. + ui_close = get_ui_close() + ui_close.screen_loc = "[screen_start_x + columns]:[screen_pixel_x],[screen_start_y]:[screen_pixel_y]" + . += ui_close + // Then orient the actual items. + var/cx = screen_start_x + var/cy = screen_start_y + if(islist(numbered_contents)) + for(var/type in numbered_contents) + var/datum/numbered_display/ND = numbered_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 + var/atom/movable/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 + . += D + 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. Does not support numerical display! + */ +/datum/component/storage/proc/orient2hud_volumetric(mob/user, maxcolumns) + . = list() + var/atom/movable/screen/storage/left/ui_left + var/atom/movable/screen/storage/continuous/ui_continuous + var/atom/movable/screen/storage/close/ui_close + + // Generate ui_item_blocks for missing ones and render+orient. + var/list/atom/contents = accessible_items() + // our volume + var/our_volume = get_max_volume() + var/horizontal_pixels = (maxcolumns * world.icon_size) - (VOLUMETRIC_STORAGE_EDGE_PADDING * 2) + var/max_horizontal_pixels = horizontal_pixels * screen_max_rows + // sigh loopmania time + var/used = 0 + // define outside for performance + var/volume + var/list/volume_by_item = list() + var/list/percentage_by_item = list() + for(var/obj/item/I in contents) + if(QDELETED(I)) + continue + volume = I.get_w_volume() + used += volume + volume_by_item[I] = volume + percentage_by_item[I] = volume / get_max_volume() + var/padding_pixels = ((length(percentage_by_item) - 1) * VOLUMETRIC_STORAGE_ITEM_PADDING) + VOLUMETRIC_STORAGE_EDGE_PADDING * 2 + var/min_pixels = (MINIMUM_PIXELS_PER_ITEM * length(percentage_by_item)) + padding_pixels + // do the check for fallback for when someone has too much gamer gear + if((min_pixels) > (max_horizontal_pixels + 4)) // 4 pixel grace zone + to_chat(user, "[parent] was showed to you in legacy mode due to your items overrunning the three row limit! Consider not carrying too much or bugging a maintainer to raise this limit!") + return orient2hud_legacy(user, maxcolumns) + // after this point we are sure we can somehow fit all items into our max number of rows. + + // determine rows + var/rows = clamp(CEILING(min_pixels / horizontal_pixels, 1), 1, screen_max_rows) + + var/overrun = FALSE + if(used > our_volume) + // congratulations we are now in overrun mode. everything will be crammed to minimum storage pixels. + to_chat(user, "[parent] rendered in overrun mode due to more items inside than the maximum volume supports.") + overrun = TRUE + + // how much we are using + var/using_horizontal_pixels = horizontal_pixels * rows + + // item padding + using_horizontal_pixels -= padding_pixels + + // define outside for marginal performance boost + var/obj/item/I + // start at this pixel from screen_start_x. + var/current_pixel = VOLUMETRIC_STORAGE_EDGE_PADDING + var/row = 1 + var/first = TRUE + + for(var/i in percentage_by_item) + I = i + var/percent = percentage_by_item[I] + var/atom/movable/screen/storage/volumetric_box/center/B = new /atom/movable/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)) + pixels_to_use = horizontal_pixels - current_pixel - VOLUMETRIC_STORAGE_EDGE_PADDING + addrow = TRUE + + // now that we have pixels_to_use, place our thing and add it to the returned list. + + 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 + VOLUMETRIC_STORAGE_ITEM_PADDING + + // set various things + B.set_pixel_size(pixels_to_use) + B.name = I.name + + // finally add our things. + . += B.on_screen_objects() + + // go up a row if needed + if(addrow) + row++ + current_pixel = VOLUMETRIC_STORAGE_EDGE_PADDING + + // Then, continuous section. + ui_continuous = get_ui_continuous() + ui_continuous.screen_loc = "[screen_start_x]:[screen_pixel_x],[screen_start_y]:[screen_pixel_y] to [screen_start_x+maxcolumns-1]:[screen_pixel_x],[screen_start_y+rows-1]:[screen_pixel_y]" + . += ui_continuous + // Then, left. + ui_left = get_ui_left() + 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 + // Then, closer, which is also our right element. + ui_close = get_ui_close() + ui_close.screen_loc = "[screen_start_x + maxcolumns]:[screen_pixel_x],[screen_start_y]:[screen_pixel_y] to [screen_start_x + maxcolumns]:[screen_pixel_x],[screen_start_y + row - 1]:[screen_pixel_y]" + . += ui_close + +/** + * Shows our UI to a mob. + */ +/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 + // we got screen size, register signal + 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) + M.active_storage = src + LAZYOR(is_using, M) + if(volumetric_ui()) + //new volumetric ui bay-style + var/list/objects = orient2hud_volumetric(M, maxallowedscreensize) + M.client.screen |= objects + ui_by_mob[M] = objects + else + //old ui + var/list/objects = orient2hud_legacy(M, maxallowedscreensize) + M.client.screen |= objects + ui_by_mob[M] = objects + 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(src, 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 + UnregisterSignal(M, list(COMSIG_PARENT_QDELETING)) + 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) + 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_ITEMS) && !display_numerical_stacking + +/** + * Gets our ui_boxes, making it if it doesn't exist. + */ +/datum/component/storage/proc/get_ui_boxes() + return new /atom/movable/screen/storage/boxes(null, src) + +/** + * Gets our ui_left, making it if it doesn't exist. + */ +/datum/component/storage/proc/get_ui_left() + return new /atom/movable/screen/storage/left(null, src) + +/** + * Gets our ui_close, making it if it doesn't exist. + */ +/datum/component/storage/proc/get_ui_close() + return new /atom/movable/screen/storage/close(null, src) + +/** + * Gets our ui_continuous, making it if it doesn't exist. + */ +/datum/component/storage/proc/get_ui_continuous() + return new /atom/movable/screen/storage/continuous(null, src) diff --git a/code/datums/numbered_display.dm b/code/datums/numbered_display.dm index 9aa880aa75d9..b714be23fbbe 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 /atom/movable/screen/storage/item_holder(null, parent, sample) number = _number diff --git a/code/game/atoms.dm b/code/game/atoms.dm index 4628bb22f92f..68d2fbe1f67e 100644 --- a/code/game/atoms.dm +++ b/code/game/atoms.dm @@ -948,11 +948,8 @@ stoplag(1) progress.end_progress() to_chat(user, "You dump as much of [src_object.parent]'s contents [STR.insert_preposition]to [src] as you can.") - STR.orient2hud(user) - src_object.orient2hud(user) if(user.active_storage) //refresh the HUD to show the transfered contents - user.active_storage.close(user) - user.active_storage.show_to(user) + user.active_storage.ui_show(user) return TRUE ///Get the best place to dump the items contained in the source storage item? diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm index e13cca64caf9..45bd268134cc 100644 --- a/code/game/objects/items.dm +++ b/code/game/objects/items.dm @@ -69,8 +69,11 @@ GLOBAL_VAR_INIT(embedpocalypse, FALSE) // if true, all items will be able to emb ///Whether or not we use stealthy audio levels for this item's attack sounds var/stealthy_audio = FALSE - ///How large is the object, used for stuff like whether it can fit in backpacks or not + /// Weight class for how much storage capacity it uses and how big it physically is meaning storages can't hold it if their maximum weight class isn't as high as it. var/w_class = WEIGHT_CLASS_NORMAL + /// Volume override for the item, otherwise automatically calculated from w_class. + var/w_volume + ///This is used to determine on which slots an item can fit. var/slot_flags = 0 pass_flags = PASSTABLE @@ -863,11 +866,15 @@ GLOBAL_VAR_INIT(embedpocalypse, FALSE) // if true, all items will be able to emb . = ..() remove_outline() -/obj/item/MouseExited() +/obj/item/MouseExited(location,control,params) + SEND_SIGNAL(src, COMSIG_ITEM_MOUSE_EXIT, location, control, params) deltimer(tip_timer)//delete any in-progress timer if the mouse is moved off the item before it finishes closeToolTip(usr) remove_outline() +/obj/item/MouseEntered(location,control,params) + SEND_SIGNAL(src, COMSIG_ITEM_MOUSE_ENTER, location, control, params) + /obj/item/proc/apply_outline(colour = null) if(!(item_flags & IN_INVENTORY || item_flags & IN_STORAGE) || QDELETED(src) || isobserver(usr)) return @@ -986,6 +993,11 @@ GLOBAL_VAR_INIT(embedpocalypse, FALSE) // if true, all items will be able to emb dropped(M, FALSE) return ..() +/// Get an item's volume that it uses when being stored. +/obj/item/proc/get_w_volume() + // if w_volume is 0 you fucked up. + return w_volume || AUTO_SCALE_VOLUME(w_class) + /obj/item/proc/embedded(mob/living/carbon/human/embedded_mob) return diff --git a/code/game/objects/items/storage/backpack.dm b/code/game/objects/items/storage/backpack.dm index 05321933bfe2..cdd43be55c32 100644 --- a/code/game/objects/items/storage/backpack.dm +++ b/code/game/objects/items/storage/backpack.dm @@ -31,9 +31,9 @@ /obj/item/storage/backpack/ComponentInitialize() . = ..() var/datum/component/storage/STR = GetComponent(/datum/component/storage) - STR.max_combined_w_class = 21 - STR.max_w_class = WEIGHT_CLASS_NORMAL - STR.max_items = 21 + STR.storage_flags = STORAGE_FLAGS_VOLUME_DEFAULT + STR.max_volume = STORAGE_VOLUME_BACKPACK + STR.max_w_class = MAX_WEIGHT_CLASS_BACKPACK STR.use_sound = 'sound/items/storage/unzip.ogg' /* @@ -58,9 +58,8 @@ /obj/item/storage/backpack/holding/ComponentInitialize() . = ..() var/datum/component/storage/STR = GetComponent(/datum/component/storage) - STR.allow_big_nesting = TRUE - STR.max_w_class = WEIGHT_CLASS_GIGANTIC - STR.max_combined_w_class = 35 + STR.storage_flags = STORAGE_FLAGS_VOLUME_DEFAULT + STR.max_volume = STORAGE_VOLUME_BAG_OF_HOLDING /obj/item/storage/backpack/santabag name = "Santa's Gift Bag" @@ -223,6 +222,12 @@ greyscale_colors = list(list(11, 12), list(17, 18), list(10, 11)) supports_variations = VOX_VARIATION +/obj/item/storage/backpack/satchel/ComponentInitialize() + . = ..() + var/datum/component/storage/STR = GetComponent(/datum/component/storage) + STR.max_volume = STORAGE_VOLUME_SATCHEL + STR.max_w_class = MAX_WEIGHT_CLASS_M_CONTAINER + /obj/item/storage/backpack/satchel/leather name = "leather satchel" desc = "It's a very fancy satchel made with fine leather." @@ -442,7 +447,7 @@ /obj/item/storage/backpack/duffelbag/ComponentInitialize() . = ..() var/datum/component/storage/STR = GetComponent(/datum/component/storage) - STR.max_combined_w_class = 30 + STR.max_volume = STORAGE_VOLUME_DUFFLEBAG LAZYINITLIST(STR.exception_hold) // This code allows you to fit one mob holder into a duffel bag STR.exception_hold += typecacheof(/obj/item/clothing/head/mob_holder) diff --git a/code/game/objects/items/storage/bags.dm b/code/game/objects/items/storage/bags.dm index 910ea174c3a6..6cf80ed6dd80 100644 --- a/code/game/objects/items/storage/bags.dm +++ b/code/game/objects/items/storage/bags.dm @@ -46,9 +46,11 @@ . = ..() var/datum/component/storage/STR = GetComponent(/datum/component/storage) STR.max_w_class = WEIGHT_CLASS_SMALL - STR.max_combined_w_class = 30 - STR.max_items = 30 + STR.max_combined_w_class = 50 + STR.max_items = 50 STR.set_holdable(null, list(/obj/item/disk/nuclear)) + STR.limited_random_access = TRUE + STR.limited_random_access_stack_position = 5 /obj/item/storage/bag/trash/update_icon_state() switch(contents.len) @@ -83,8 +85,8 @@ /obj/item/storage/bag/trash/bluespace/ComponentInitialize() . = ..() var/datum/component/storage/STR = GetComponent(/datum/component/storage) - STR.max_combined_w_class = 60 - STR.max_items = 60 + STR.max_combined_w_class = 75 + STR.max_items = 75 /obj/item/storage/bag/trash/bluespace/cyborg insertable = FALSE diff --git a/code/modules/mob/living/inhand_holder.dm b/code/modules/mob/living/inhand_holder.dm index e16dcf9e3326..f593a269cbd6 100644 --- a/code/modules/mob/living/inhand_holder.dm +++ b/code/modules/mob/living/inhand_holder.dm @@ -7,7 +7,8 @@ icon_state = "" slot_flags = NONE moth_edible = FALSE - w_class = 20 // so that only one can fit in a duffel bag + w_class = WEIGHT_CLASS_BULKY + w_volume = ITEM_VOLUME_MOB// so that only one can fit in a duffel bag var/mob/living/held_mob /obj/item/clothing/head/mob_holder/Initialize(mapload, mob/living/M, worn_state, head_icon, lh_icon, rh_icon, worn_slot_flags = NONE) diff --git a/icons/hud/screen_gen.dmi b/icons/hud/screen_gen.dmi index cad55a6ea290..b26bc7375dbb 100644 Binary files a/icons/hud/screen_gen.dmi and b/icons/hud/screen_gen.dmi differ diff --git a/shiptest.dme b/shiptest.dme index b56709e8b98b..681777eeb005 100644 --- a/shiptest.dme +++ b/shiptest.dme @@ -132,6 +132,7 @@ #include "code\__DEFINES\stat_tracking.dm" #include "code\__DEFINES\statpanel.dm" #include "code\__DEFINES\status_effects.dm" +#include "code\__DEFINES\storage.dm" #include "code\__DEFINES\subsystems.dm" #include "code\__DEFINES\tgs.config.dm" #include "code\__DEFINES\tgs.dm" @@ -274,6 +275,7 @@ #include "code\_onclick\hud\revenanthud.dm" #include "code\_onclick\hud\robot.dm" #include "code\_onclick\hud\screen_objects.dm" +#include "code\_onclick\hud\storage.dm" #include "code\_onclick\hud\screentip.dm" #include "code\_onclick\hud\swarmer.dm" #include "code\controllers\admin.dm" @@ -539,6 +541,7 @@ #include "code\datums\components\plumbing\reaction_chamber.dm" #include "code\datums\components\plumbing\splitter.dm" #include "code\datums\components\storage\storage.dm" +#include "code\datums\components\storage\ui.dm" #include "code\datums\components\storage\concrete\_concrete.dm" #include "code\datums\components\storage\concrete\bag_of_holding.dm" #include "code\datums\components\storage\concrete\bluespace.dm"