Skip to content

Commit

Permalink
Reworks trashbags slightly (tgstation#73761)
Browse files Browse the repository at this point in the history
## About The Pull Request

I'm a bit sad about the state of trashbags. 
They're very clunky to use, so they almost never get touched. S
depressing. Let's try and fix that.

Let's make em fit in the belt slot (again), but as a tradeoff we'll make
it harder to pull one thing from your bag.
We'll give it a say, 1.5 second delay, so you can't quickdraw from em.
If you try and dump them out into something else, we'll throw any
spillover on the ground below you

I'm also doing some general code cleanup here. Making procs more
readable, vars more direct, removing some old legacy stuff.
I've added a remove_single proc to hook into via subtype, which takes a
mob as input. this has required placing extra requirement on some helper
procs, but fortunately it's not something they're unable to meet.

My hope is this will make garbage bags usable without being stupid.

## Why It's Good For The Game

I don't see these get used at all, cause they're a pain to carry around.
They got gimped because people were using them as infinite storage for
shotgun shells and other small items.
I've made using them for this sort of thing hard and slow, so I think we
oughta be fine. If not I'll do some more touching, maybe give the
autodrop a delay.

## Changelog
:cl:
balance: The janitor's trashbag now fits on his belt. In exchange,
taking something out of it sends a visible message, and has a delay.
/:cl:

---------

Co-authored-by: san7890 <the@san7890.com>
  • Loading branch information
LemonInTheDark and san7890 authored Mar 14, 2023
1 parent b594b49 commit 33d9a03
Show file tree
Hide file tree
Showing 39 changed files with 157 additions and 89 deletions.
3 changes: 3 additions & 0 deletions code/__DEFINES/dcs/signals/signals_storage.dm
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,6 @@
#define COMSIG_STORAGE_DUMP_CONTENT "storage_dump_contents"
/// Return to stop the standard dump behavior.
#define STORAGE_DUMP_HANDLED (1<<0)
/// Sent after dumping into some other storage object: (atom/dest_object, mob/user)
#define COMSIG_STORAGE_DUMP_POST_TRANSFER "storage_dump_into_storage"

1 change: 1 addition & 0 deletions code/__HELPERS/mobs.dm
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,7 @@ GLOBAL_LIST_EMPTY(species_list)
return FALSE
if(!isnum(delay))
CRASH("do_after was passed a non-number delay: [delay || "null"].")

if(!interaction_key && target)
interaction_key = target //Use the direct ref to the target
if(interaction_key) //Do we have a interaction_key now?
Expand Down
91 changes: 61 additions & 30 deletions code/datums/storage/storage.dm
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@
var/allow_quick_empty = FALSE
/// the mode for collection when allow_quick_gather is enabled
var/collection_mode = COLLECT_ONE
/// If we support smartly removing/inserting things from ourselves
var/supports_smart_equip = TRUE

/// shows what we can hold in examine text
var/can_hold_description
Expand Down Expand Up @@ -475,6 +477,7 @@ GLOBAL_LIST_EMPTY(cached_storage_typecaches)

/**
* Attempts to remove an item from the storage
* Ignores removal do_afters. Only use this if you're doing it as part of a dumping action
*
* @param obj/item/thing the object we're removing
* @param atom/newLoc where we're placing the item
Expand Down Expand Up @@ -534,6 +537,20 @@ GLOBAL_LIST_EMPTY(cached_storage_typecaches)
thing.pixel_y = thing.base_pixel_y + rand(-8, 8)


/**
* Allows a mob to attempt to remove a single item from the storage
* Allows for hooks into things like removal delays
*
* @param mob/removing the mob doing the removing
* @param obj/item/thing the object we're removing
* @param atom/newLoc where we're placing the item
* @param silent if TRUE, we won't play any exit sounds
*/
/datum/storage/proc/remove_single(mob/removing, obj/item/thing, atom/newLoc, silent = FALSE)
if(!attempt_remove(thing, newLoc, silent))
return FALSE
return TRUE

/**
* Removes only a specific type of item from our storage
*
Expand Down Expand Up @@ -657,23 +674,28 @@ GLOBAL_LIST_EMPTY(cached_storage_typecaches)
if(!resolve_parent)
return

var/list/turf_things = thing.loc.contents.Copy()
var/atom/holder = thing.loc
var/list/pick_up = holder.contents.Copy()

if(collection_mode == COLLECT_SAME)
turf_things = typecache_filter_list(turf_things, typecacheof(thing.type))
pick_up = typecache_filter_list(pick_up, typecacheof(thing.type))

var/amount = length(turf_things)
var/amount = length(pick_up)
if(!amount)
resolve_parent.balloon_alert(user, "nothing to pick up!")
return

var/datum/progressbar/progress = new(user, amount, thing.loc)
var/list/rejections = list()

while(do_after(user, 1 SECONDS, resolve_parent, NONE, FALSE, CALLBACK(src, PROC_REF(handle_mass_pickup), user, turf_things, thing.loc, rejections, progress)))
while(do_after(user, 1 SECONDS, resolve_parent, NONE, FALSE, CALLBACK(src, PROC_REF(handle_mass_pickup), user, pick_up.Copy(), thing.loc, rejections, progress)))
stoplag(1)

progress.end_progress()
// If nothing was actually removed, don't send the pickup message
var/list/current_contents = holder.contents.Copy()
if(length(pick_up | current_contents) == length(current_contents))
return
resolve_parent.balloon_alert(user, "picked up")

/// Signal handler for whenever we drag the storage somewhere.
Expand Down Expand Up @@ -737,7 +759,7 @@ GLOBAL_LIST_EMPTY(cached_storage_typecaches)
continue
dest_object.atom_storage.attempt_insert(to_dump, user)
resolve_parent.update_appearance()

SEND_SIGNAL(src, COMSIG_STORAGE_DUMP_POST_TRANSFER, dest_object, user)
return

var/atom/dump_loc = dest_object.get_dumping_location()
Expand Down Expand Up @@ -783,13 +805,13 @@ GLOBAL_LIST_EMPTY(cached_storage_typecaches)
return

if(!thing.attackby_storage_insert(src, resolve_parent, user))
return FALSE
return

if(iscyborg(user))
return TRUE
return COMPONENT_NO_AFTERATTACK

attempt_insert(thing, user)
return TRUE
return COMPONENT_NO_AFTERATTACK

/// Signal handler for whenever we're attacked by a mob.
/datum/storage/proc/on_attack(datum/source, mob/user)
Expand All @@ -803,7 +825,7 @@ GLOBAL_LIST_EMPTY(cached_storage_typecaches)
if(user.active_storage == src && resolve_parent.loc == user)
user.active_storage.hide_contents(user)
hide_contents(user)
return TRUE
return COMPONENT_CANCEL_ATTACK_CHAIN
if(ishuman(user))
var/mob/living/carbon/human/hum = user
if(hum.l_store == resolve_parent && !hum.get_active_held_item())
Expand All @@ -817,7 +839,7 @@ GLOBAL_LIST_EMPTY(cached_storage_typecaches)

if(resolve_parent.loc == user)
INVOKE_ASYNC(src, PROC_REF(open_storage), user)
return TRUE
return COMPONENT_CANCEL_ATTACK_CHAIN

/// Generates the numbers on an item in storage to show stacking.
/datum/storage/proc/process_numerical_display()
Expand Down Expand Up @@ -952,32 +974,34 @@ GLOBAL_LIST_EMPTY(cached_storage_typecaches)
resolve_parent.balloon_alert(to_show, "locked!")
return FALSE

if(!quickdraw || to_show.get_active_held_item())
if(display_contents)
show_contents(to_show)

if(animated)
animate_parent()

if(rustle_sound)
playsound(resolve_parent, SFX_RUSTLE, 50, TRUE, -5)

return TRUE

var/obj/item/to_remove = locate() in resolve_location
// If we're quickdrawing boys
if(quickdraw && !to_show.get_active_held_item())
var/obj/item/to_remove = locate() in resolve_location
if(!to_remove)
return TRUE

if(!to_remove)
remove_single(to_show, to_remove)
INVOKE_ASYNC(src, PROC_REF(put_in_hands_async), to_show, to_remove)
if(!silent)
to_show.visible_message(
span_warning("[to_show] draws [to_remove] from [resolve_parent]!"),
span_notice("You draw [to_remove] from [resolve_parent]."),
)
return TRUE

attempt_remove(to_remove)
// If nothing else, then we want to open the thing, so do that
if(!show_contents(to_show))
return FALSE

INVOKE_ASYNC(src, PROC_REF(put_in_hands_async), to_show, to_remove)
if(animated)
animate_parent()

if(!silent)
to_show.visible_message(span_warning("[to_show] draws [to_remove] from [resolve_parent]!"), span_notice("You draw [to_remove] from [resolve_parent]."))
if(rustle_sound)
playsound(resolve_parent, SFX_RUSTLE, 50, TRUE, -5)

return TRUE


/// Async version of putting something into a mobs hand.
/datum/storage/proc/put_in_hands_async(mob/toshow, obj/item/toremove)
if(!toshow.put_in_hands(toremove))
Expand Down Expand Up @@ -1021,14 +1045,20 @@ GLOBAL_LIST_EMPTY(cached_storage_typecaches)
* Show our storage to a mob.
*
* @param mob/toshow the mob to show the storage to
*
* @returns FALSE if the show failed, TRUE otherwise
*/
/datum/storage/proc/show_contents(mob/toshow)
var/obj/item/resolve_location = real_location?.resolve()
if(!resolve_location)
return
return FALSE

if(!toshow.client)
return
return FALSE

// You can only inspect hidden contents if you're an observer
if(!isobserver(toshow) && !display_contents)
return FALSE

if(toshow.active_storage != src && (toshow.stat == CONSCIOUS))
for(var/obj/item/thing in resolve_location)
Expand All @@ -1051,6 +1081,7 @@ GLOBAL_LIST_EMPTY(cached_storage_typecaches)
toshow.client.screen |= boxes
toshow.client.screen |= closer
toshow.client.screen |= resolve_location.contents
return TRUE

/**
* Hide our storage from a mob.
Expand Down
14 changes: 14 additions & 0 deletions code/datums/storage/subtypes/trash.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/datum/storage/trash

/datum/storage/trash/remove_single(mob/removing, obj/item/thing, atom/newLoc, silent)
var/obj/item/resolve_location = real_location?.resolve()
if(!resolve_location)
return

resolve_location.visible_message(span_notice("[removing] starts fishing around inside \the [resolve_location]."),
span_notice("You start digging around in \the [resolve_location] to try and pull something out."))
if(!do_after(removing, 1.5 SECONDS, resolve_location))
return

return ..()

4 changes: 2 additions & 2 deletions code/game/atoms.dm
Original file line number Diff line number Diff line change
Expand Up @@ -346,13 +346,13 @@
attack_hand_interact = TRUE,
list/canhold,
list/canthold,
type = /datum/storage,
storage_type = /datum/storage,
)

if(atom_storage)
QDEL_NULL(atom_storage)

atom_storage = new type(src, max_slots, max_specific_storage, max_total_storage, numerical_stacking, allow_quick_gather, collection_mode, attack_hand_interact)
atom_storage = new storage_type(src, max_slots, max_specific_storage, max_total_storage, numerical_stacking, allow_quick_gather, collection_mode, attack_hand_interact)

if(canhold || canthold)
atom_storage.set_holdable(canhold, canthold)
Expand Down
20 changes: 15 additions & 5 deletions code/game/objects/items.dm
Original file line number Diff line number Diff line change
Expand Up @@ -552,7 +552,8 @@ GLOBAL_DATUM_INIT(fire_overlay, /mutable_appearance, mutable_appearance('icons/e


//If the item is in a storage item, take it out
loc.atom_storage?.attempt_remove(src, user.loc, silent = TRUE)
if(loc.atom_storage && !loc.atom_storage.remove_single(user, src, user.loc, silent = TRUE))
return
if(QDELETED(src)) //moving it out of the storage to the floor destroyed it.
return

Expand Down Expand Up @@ -587,7 +588,8 @@ GLOBAL_DATUM_INIT(fire_overlay, /mutable_appearance, mutable_appearance('icons/e
return

//If the item is in a storage item, take it out
loc.atom_storage?.attempt_remove(src, user.loc, silent = TRUE)
if(loc.atom_storage?.remove_single(user, src, user.loc, silent = TRUE))
return
if(QDELETED(src)) //moving it out of the storage to the floor destroyed it.
return

Expand Down Expand Up @@ -823,12 +825,20 @@ GLOBAL_DATUM_INIT(fire_overlay, /mutable_appearance, mutable_appearance('icons/e
pixel_x = rand(-8,8)
pixel_y = rand(-8,8)


/obj/item/proc/remove_item_from_storage(atom/newLoc) //please use this if you're going to snowflake an item out of a obj/item/storage
/// Takes the location to move the item to, and optionally the mob doing the removing
/// If no mob is provided, we'll pass in the location, assuming it is a mob
/// Please use this if you're going to snowflake an item out of a obj/item/storage
/obj/item/proc/remove_item_from_storage(atom/newLoc, mob/removing)
if(!newLoc)
return FALSE
if(!removing)
if(ismob(newLoc))
removing = newLoc
else
stack_trace("Tried to remove an item and place it into [newLoc] without implicitly or explicitly passing in a mob doing the removing")
return
if(loc.atom_storage)
return loc.atom_storage.attempt_remove(src, newLoc, silent = TRUE)
return loc.atom_storage.remove_single(removing, src, newLoc, silent = TRUE)
return FALSE

/// Returns the icon used for overlaying the object on a belt
Expand Down
4 changes: 2 additions & 2 deletions code/game/objects/items/implants/implant_storage.dm
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,15 @@
if(istype(X, type))
var/obj/item/implant/storage/imp_e = X
if(!imp_e.atom_storage)
imp_e.create_storage(type = /datum/storage/implant)
imp_e.create_storage(storage_type = /datum/storage/implant)
qdel(src)
return TRUE
else if(imp_e.atom_storage.max_slots < max_slot_stacking)
imp_e.atom_storage.max_slots += initial(imp_e.atom_storage.max_slots)
imp_e.atom_storage.max_total_storage += initial(imp_e.atom_storage.max_total_storage)
return TRUE
return FALSE
create_storage(type = /datum/storage/implant)
create_storage(storage_type = /datum/storage/implant)

return ..()

Expand Down
2 changes: 1 addition & 1 deletion code/game/objects/items/stacks/sheets/glass.dm
Original file line number Diff line number Diff line change
Expand Up @@ -378,7 +378,7 @@ GLOBAL_LIST_INIT(plastitaniumglass_recipes, list(
var/obj/item/knife/shiv/shiv = new shiv_type
cloth.use(1)
to_chat(user, span_notice("You wrap the [cloth] around the [src], forming a makeshift weapon."))
remove_item_from_storage(src)
remove_item_from_storage(src, user)
qdel(src)
user.put_in_hands(shiv)

Expand Down
2 changes: 1 addition & 1 deletion code/game/objects/items/storage/backpack.dm
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@
/obj/item/storage/backpack/holding/Initialize(mapload)
. = ..()

create_storage(max_specific_storage = WEIGHT_CLASS_GIGANTIC, max_total_storage = 35, max_slots = 30, type = /datum/storage/bag_of_holding)
create_storage(max_specific_storage = WEIGHT_CLASS_GIGANTIC, max_total_storage = 35, max_slots = 30, storage_type = /datum/storage/bag_of_holding)
atom_storage.allow_big_nesting = TRUE

/obj/item/storage/backpack/holding/suicide_act(mob/living/user)
Expand Down
19 changes: 18 additions & 1 deletion code/game/objects/items/storage/bags.dm
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
inhand_icon_state = "trashbag"
lefthand_file = 'icons/mob/inhands/equipment/custodial_lefthand.dmi'
righthand_file = 'icons/mob/inhands/equipment/custodial_righthand.dmi'
slot_flags = null
storage_type = /datum/storage/trash
///If true, can be inserted into the janitor cart
var/insertable = TRUE

Expand All @@ -49,6 +49,23 @@
atom_storage.max_total_storage = 30
atom_storage.max_slots = 30
atom_storage.set_holdable(cant_hold_list = list(/obj/item/disk/nuclear))
atom_storage.supports_smart_equip = FALSE
RegisterSignal(atom_storage, COMSIG_STORAGE_DUMP_POST_TRANSFER, PROC_REF(post_insertion))

/// If you dump a trash bag into something, anything that doesn't get inserted will spill out onto your feet
/obj/item/storage/bag/trash/proc/post_insertion(datum/storage/source, atom/dest_object, mob/user)
SIGNAL_HANDLER
// If there's no item in there, don't do anything
if(!(locate(/obj/item) in src))
return

// Otherwise, we're gonna dump into the dest object
var/turf/dump_onto = get_turf(dest_object)
user.visible_message(
span_notice("[user] dumps the contents of [src] all out on \the [dump_onto]"),
span_notice("The remaining trash in \the [src] falls out onto \the [dump_onto]"),
)
source.remove_all(dump_onto)

/obj/item/storage/bag/trash/suicide_act(mob/living/user)
user.visible_message(span_suicide("[user] puts [src] over [user.p_their()] head and starts chomping at the insides! Disgusting!"))
Expand Down
2 changes: 1 addition & 1 deletion code/game/objects/items/storage/fancy.dm
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,7 @@
/obj/item/storage/fancy/cigarettes/proc/quick_remove_item(obj/item/grabbies, mob/user)
var/obj/item/finger = locate(grabbies) in contents
if(finger)
atom_storage.attempt_remove(finger, drop_location())
atom_storage.remove_single(user, finger, drop_location())
user.put_in_hands(finger)

/obj/item/storage/fancy/cigarettes/add_context(atom/source, list/context, obj/item/held_item, mob/user)
Expand Down
2 changes: 1 addition & 1 deletion code/game/objects/items/storage/medkit.dm
Original file line number Diff line number Diff line change
Expand Up @@ -567,7 +567,7 @@
/obj/item/storage/organbox/Initialize(mapload)
. = ..()

create_storage(type = /datum/storage/organ_box, max_specific_storage = WEIGHT_CLASS_BULKY, max_total_storage = 21)
create_storage(storage_type = /datum/storage/organ_box, max_specific_storage = WEIGHT_CLASS_BULKY, max_total_storage = 21)
atom_storage.set_holdable(list(
/obj/item/organ,
/obj/item/bodypart,
Expand Down
4 changes: 3 additions & 1 deletion code/game/objects/items/storage/storage.dm
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,13 @@
/// BE CAREFUL, THERE'S SOME REALLY NASTY SHIT IN THIS TYPEPATH
/// SANTA IS EVIL
var/preload = FALSE
/// What storage type to use for this item
var/datum/storage/storage_type = /datum/storage

/obj/item/storage/Initialize(mapload)
. = ..()

create_storage()
create_storage(storage_type = storage_type)

PopulateContents()

Expand Down
2 changes: 1 addition & 1 deletion code/game/objects/items/tcg/tcg.dm
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ GLOBAL_LIST_EMPTY(tcgcard_radial_choices)

/obj/item/tcgcard_deck/Initialize(mapload)
. = ..()
create_storage(type = /datum/storage/tcg)
create_storage(storage_type = /datum/storage/tcg)

/obj/item/tcgcard_deck/update_icon_state()
if(!flipped)
Expand Down
Loading

0 comments on commit 33d9a03

Please sign in to comment.