Uploading all files.

This commit is contained in:
Quotefox
2019-09-23 18:26:34 +02:00
commit 7091cdbe8c
7779 changed files with 2601004 additions and 0 deletions
@@ -0,0 +1,207 @@
// External storage-related logic:
// /mob/proc/ClickOn() in /_onclick/click.dm - clicking items in storages
// /mob/living/Move() in /modules/mob/living/living.dm - hiding storage boxes on mob movement
/datum/component/storage/concrete
var/drop_all_on_deconstruct = TRUE
var/drop_all_on_destroy = FALSE
var/drop_all_on_break = FALSE
var/unlock_on_break = FALSE
var/transfer_contents_on_component_transfer = FALSE
var/list/datum/component/storage/slaves = list()
var/list/_contents_limbo // Where objects go to live mid transfer
var/list/_user_limbo // The last users before the component started moving
/datum/component/storage/concrete/Initialize()
. = ..()
RegisterSignal(parent, COMSIG_ATOM_CONTENTS_DEL, .proc/on_contents_del)
RegisterSignal(parent, COMSIG_OBJ_DECONSTRUCT, .proc/on_deconstruct)
RegisterSignal(parent, COMSIG_OBJ_BREAK, .proc/on_break)
/datum/component/storage/concrete/Destroy()
var/atom/real_location = real_location()
for(var/atom/_A in real_location)
_A.mouse_opacity = initial(_A.mouse_opacity)
if(drop_all_on_destroy)
do_quick_empty()
for(var/i in slaves)
var/datum/component/storage/slave = i
slave.change_master(null)
QDEL_LIST(_contents_limbo)
_user_limbo = null
return ..()
/datum/component/storage/concrete/master()
return src
/datum/component/storage/concrete/real_location()
return parent
/datum/component/storage/concrete/PreTransfer()
if(is_using)
_user_limbo = is_using.Copy()
close_all()
if(transfer_contents_on_component_transfer)
_contents_limbo = list()
for(var/atom/movable/AM in parent)
_contents_limbo += AM
AM.moveToNullspace()
/datum/component/storage/concrete/PostTransfer()
if(!isatom(parent))
return COMPONENT_INCOMPATIBLE
if(transfer_contents_on_component_transfer)
for(var/i in _contents_limbo)
var/atom/movable/AM = i
AM.forceMove(parent)
_contents_limbo = null
if(_user_limbo)
for(var/i in _user_limbo)
show_to(i)
_user_limbo = null
/datum/component/storage/concrete/_insert_physical_item(obj/item/I, override = FALSE)
. = TRUE
var/atom/real_location = real_location()
if(I.loc != real_location)
I.forceMove(real_location)
refresh_mob_views()
/datum/component/storage/concrete/refresh_mob_views()
. = ..()
for(var/i in slaves)
var/datum/component/storage/slave = i
slave.refresh_mob_views()
/datum/component/storage/concrete/emp_act(datum/source, severity)
if(emp_shielded)
return
var/atom/real_location = real_location()
for(var/i in real_location)
var/atom/A = i
A.emp_act(severity)
/datum/component/storage/concrete/proc/on_slave_link(datum/component/storage/S)
if(S == src)
return FALSE
slaves += S
return TRUE
/datum/component/storage/concrete/proc/on_slave_unlink(datum/component/storage/S)
slaves -= S
return FALSE
/datum/component/storage/concrete/proc/on_contents_del(datum/source, atom/A)
var/atom/real_location = parent
if(A in real_location)
usr = null
remove_from_storage(A, null)
/datum/component/storage/concrete/proc/on_deconstruct(datum/source, disassembled)
if(drop_all_on_deconstruct)
do_quick_empty()
/datum/component/storage/concrete/proc/on_break(datum/source, damage_flag)
if(drop_all_on_break)
do_quick_empty()
if(unlock_on_break)
set_locked(source, FALSE)
/datum/component/storage/concrete/can_see_contents()
. = ..()
for(var/i in slaves)
var/datum/component/storage/slave = i
. |= slave.can_see_contents()
//Resets screen loc and other vars of something being removed from storage.
/datum/component/storage/concrete/_removal_reset(atom/movable/thing)
thing.layer = initial(thing.layer)
thing.plane = initial(thing.plane)
thing.mouse_opacity = initial(thing.mouse_opacity)
if(thing.maptext)
thing.maptext = ""
/datum/component/storage/concrete/remove_from_storage(atom/movable/AM, atom/new_location)
//Cache this as it should be reusable down the bottom, will not apply if anyone adds a sleep to dropped
//or moving objects, things that should never happen
var/atom/parent = src.parent
var/list/seeing_mobs = can_see_contents()
for(var/mob/M in seeing_mobs)
M.client.screen -= AM
if(ismob(parent.loc) && isitem(AM))
var/obj/item/I = AM
var/mob/M = parent.loc
I.dropped(M)
if(new_location)
//Reset the items values
_removal_reset(AM)
AM.forceMove(new_location)
//We don't want to call this if the item is being destroyed
AM.on_exit_storage(src)
else
//Being destroyed, just move to nullspace now (so it's not in contents for the icon update)
AM.moveToNullspace()
refresh_mob_views()
if(isobj(parent))
var/obj/O = parent
O.update_icon()
return TRUE
/datum/component/storage/concrete/proc/slave_can_insert_object(datum/component/storage/slave, obj/item/I, stop_messages = FALSE, mob/M)
return TRUE
/datum/component/storage/concrete/proc/handle_item_insertion_from_slave(datum/component/storage/slave, obj/item/I, prevent_warning = FALSE, M)
. = handle_item_insertion(I, prevent_warning, M, slave)
if(. && !prevent_warning)
slave.mob_item_insertion_feedback(usr, M, I)
/datum/component/storage/concrete/handle_item_insertion(obj/item/I, prevent_warning = FALSE, mob/M, datum/component/storage/remote) //Remote is null or the slave datum
var/datum/component/storage/concrete/master = master()
var/atom/parent = src.parent
var/moved = FALSE
if(!istype(I))
return FALSE
if(M)
if(!M.temporarilyRemoveItemFromInventory(I))
return FALSE
else
moved = TRUE //At this point if the proc fails we need to manually move the object back to the turf/mob/whatever.
if(I.pulledby)
I.pulledby.stop_pulling()
if(silent)
prevent_warning = TRUE
if(!_insert_physical_item(I))
if(moved)
if(M)
if(!M.put_in_active_hand(I))
I.forceMove(parent.drop_location())
else
I.forceMove(parent.drop_location())
return FALSE
I.on_enter_storage(master)
refresh_mob_views()
I.mouse_opacity = MOUSE_OPACITY_OPAQUE //So you can click on the area around the item to equip it, instead of having to pixel hunt
if(M)
if(M.client && M.active_storage != src)
M.client.screen -= I
if(M.observers && M.observers.len)
for(var/i in M.observers)
var/mob/dead/observe = i
if(observe.client && observe.active_storage != src)
observe.client.screen -= I
if(!remote)
parent.add_fingerprint(M)
if(!prevent_warning)
mob_item_insertion_feedback(usr, M, I)
update_icon()
return TRUE
/datum/component/storage/concrete/update_icon()
if(isobj(parent))
var/obj/O = parent
O.update_icon()
for(var/i in slaves)
var/datum/component/storage/slave = i
slave.update_icon()
@@ -0,0 +1,36 @@
/datum/component/storage/concrete/bluespace/bag_of_holding/handle_item_insertion(obj/item/W, prevent_warning = FALSE, mob/living/user)
var/atom/A = parent
if(A == W) //don't put yourself into yourself.
return
var/list/obj/item/storage/backpack/holding/matching = typecache_filter_list(W.GetAllContents(), typecacheof(/obj/item/storage/backpack/holding))
matching -= A
if(istype(W, /obj/item/storage/backpack/holding) || matching.len)
var/safety = alert(user, "Doing this will have extremely dire consequences for the station and its crew. Be sure you know what you're doing.", "Put in [A.name]?", "Abort", "Proceed")
if(safety != "Proceed" || QDELETED(A) || QDELETED(W) || QDELETED(user) || !user.canUseTopic(A, BE_CLOSE, iscarbon(user)))
return
var/turf/loccheck = get_turf(A)
if(is_reebe(loccheck.z))
user.visible_message("<span class='warning'>An unseen force knocks [user] to the ground!</span>", "<span class='big_brass'>\"I think not!\"</span>")
user.Knockdown(60)
return
if(istype(loccheck.loc, /area/fabric_of_reality))
to_chat(user, "<span class='danger'>You can't do that here!</span>")
to_chat(user, "<span class='danger'>The Bluespace interfaces of the two devices catastrophically malfunction!</span>")
qdel(W)
playsound(loccheck,'sound/effects/supermatter.ogg', 200, 1)
user.gib(TRUE, TRUE, TRUE)
for(var/turf/T in range(6,loccheck))
if(istype(T, /turf/open/space/transit))
continue
for(var/mob/living/M in T)
if(M.movement_type & FLYING)
M.visible_message("<span class='danger'>The bluespace collapse crushes the air towards it, pulling [M] towards the ground...</span>")
M.Knockdown(5, TRUE, TRUE) //Overrides stun absorbs.
T.TerraformTurf(/turf/open/chasm/magic, /turf/open/chasm/magic)
for (var/obj/structure/ladder/unbreakable/binary/ladder in GLOB.ladders)
ladder.ActivateAlmonds()
message_admins("[ADMIN_LOOKUPFLW(user)] detonated a bag of holding at [ADMIN_VERBOSEJMP(loccheck)].")
log_game("[key_name(user)] detonated a bag of holding at [loc_name(loccheck)].")
qdel(A)
return
. = ..()
@@ -0,0 +1,21 @@
/datum/component/storage/concrete/bluespace
var/dumping_range = 8
var/dumping_sound = 'sound/items/pshoom.ogg'
var/alt_sound = 'sound/items/pshoom_2.ogg'
/datum/component/storage/concrete/bluespace/dump_content_at(atom/dest, mob/M)
var/atom/A = parent
if(A.Adjacent(M))
var/atom/dumping_location = dest.get_dumping_location()
if(get_dist(M, dumping_location) < dumping_range)
if(dumping_location.storage_contents_dump_act(src, M))
if(alt_sound && prob(1))
playsound(src, alt_sound, 40, 1)
else
playsound(src, dumping_sound, 40, 1)
M.Beam(dumping_location, icon_state="rped_upgrade", time=5)
return TRUE
to_chat(M, "The [A.name] buzzes.")
playsound(src, 'sound/machines/buzz-sigh.ogg', 50, 0)
return FALSE
@@ -0,0 +1,38 @@
/datum/component/storage/concrete/emergency
drop_all_on_break = TRUE
unlock_on_break = TRUE
locked = TRUE
/datum/component/storage/concrete/emergency/Initialize()
. = ..()
RegisterSignal(parent, COMSIG_ATOM_EMAG_ACT, .proc/unlock_me)
/datum/component/storage/concrete/emergency/on_attack_hand(datum/source, mob/user)
var/atom/A = parent
if(!attack_hand_interact)
return
if(user.active_storage == src && A.loc == user) //if you're already looking inside the storage item
user.active_storage.close(user)
close(user)
. = COMPONENT_NO_ATTACK_HAND
return
. = COMPONENT_NO_ATTACK_HAND
if(!check_locked(source, user, TRUE))
show_to(user)
A.do_jiggle()
if(rustle_sound)
playsound(A, "rustle", 50, 1, -5)
/datum/component/storage/concrete/emergency/signal_insertion_attempt(datum/source, obj/item/I, mob/M, silent = FALSE, force = FALSE)
if(!silent && istype(I, /obj/item/card/emag))
silent = TRUE // suppresses the message
return ..()
/datum/component/storage/concrete/emergency/check_locked(datum/source, mob/user, message = FALSE)
. = locked && GLOB.security_level < SEC_LEVEL_RED
if(message && . && user)
to_chat(user, "The storage unit will only unlock during a Red or Delta security alert.")
/datum/component/storage/concrete/emergency/proc/unlock_me(datum/source)
if(locked)
set_locked(source, FALSE)
@@ -0,0 +1,18 @@
/datum/component/storage/concrete/implant
max_w_class = WEIGHT_CLASS_NORMAL
max_combined_w_class = 6
max_items = 2
drop_all_on_destroy = TRUE
drop_all_on_deconstruct = TRUE
silent = TRUE
allow_big_nesting = TRUE
/datum/component/storage/concrete/implant/Initialize()
. = ..()
cant_hold = typecacheof(list(/obj/item/disk/nuclear))
/datum/component/storage/concrete/implant/InheritComponent(datum/component/storage/concrete/implant/I, original)
if(!istype(I))
return ..()
max_combined_w_class += I.max_combined_w_class
max_items += I.max_items
@@ -0,0 +1,89 @@
/datum/component/storage/concrete/pockets
max_items = 2
max_w_class = WEIGHT_CLASS_SMALL
max_combined_w_class = 50
rustle_sound = FALSE
/datum/component/storage/concrete/pockets/handle_item_insertion(obj/item/I, prevent_warning, mob/user)
. = ..()
if(. && silent && !prevent_warning)
if(quickdraw)
to_chat(user, "<span class='notice'>You discreetly slip [I] into [parent]. Alt-click [parent] to remove it.</span>")
else
to_chat(user, "<span class='notice'>You discreetly slip [I] into [parent].</span>")
/datum/component/storage/concrete/pockets
max_w_class = WEIGHT_CLASS_NORMAL
/datum/component/storage/concrete/pockets/small
max_items = 1
attack_hand_interact = FALSE
/datum/component/storage/concrete/pockets/small/collar
max_items = 1
/datum/component/storage/concrete/pockets/small/collar/Initialize()
. = ..()
can_hold = typecacheof(list(
/obj/item/reagent_containers/food/snacks/cookie,
/obj/item/reagent_containers/food/snacks/sugarcookie))
/datum/component/storage/concrete/pockets/small/collar/locked/Initialize()
. = ..()
can_hold = typecacheof(list(
/obj/item/reagent_containers/food/snacks/cookie,
/obj/item/reagent_containers/food/snacks/sugarcookie,
/obj/item/key/collar))
/datum/component/storage/concrete/pockets/tiny
max_items = 1
max_w_class = WEIGHT_CLASS_TINY
attack_hand_interact = FALSE
/datum/component/storage/concrete/pockets/small/detective
attack_hand_interact = TRUE // so the detectives would discover pockets in their hats
/datum/component/storage/concrete/pockets/shoes
attack_hand_interact = FALSE
quickdraw = TRUE
silent = TRUE
/datum/component/storage/concrete/pockets/shoes/Initialize()
. = ..()
cant_hold = typecacheof(list(/obj/item/screwdriver/power))
can_hold = typecacheof(list(
/obj/item/kitchen/knife, /obj/item/switchblade, /obj/item/pen,
/obj/item/scalpel, /obj/item/reagent_containers/syringe, /obj/item/dnainjector,
/obj/item/reagent_containers/hypospray/medipen, /obj/item/reagent_containers/dropper,
/obj/item/implanter, /obj/item/screwdriver, /obj/item/weldingtool/mini,
/obj/item/firing_pin
))
/datum/component/storage/concrete/pockets/shoes/clown/Initialize()
. = ..()
cant_hold = typecacheof(list(/obj/item/screwdriver/power))
can_hold = typecacheof(list(
/obj/item/kitchen/knife, /obj/item/switchblade, /obj/item/pen,
/obj/item/scalpel, /obj/item/reagent_containers/syringe, /obj/item/dnainjector,
/obj/item/reagent_containers/hypospray/medipen, /obj/item/reagent_containers/dropper,
/obj/item/implanter, /obj/item/screwdriver, /obj/item/weldingtool/mini,
/obj/item/firing_pin, /obj/item/bikehorn))
/datum/component/storage/concrete/pockets/pocketprotector
max_items = 3
max_w_class = WEIGHT_CLASS_TINY
var/atom/original_parent
/datum/component/storage/concrete/pockets/pocketprotector/Initialize()
original_parent = parent
. = ..()
can_hold = typecacheof(list( //Same items as a PDA
/obj/item/pen,
/obj/item/toy/crayon,
/obj/item/lipstick,
/obj/item/flashlight/pen,
/obj/item/clothing/mask/cigarette))
/datum/component/storage/concrete/pockets/pocketprotector/real_location()
// if the component is reparented to a jumpsuit, the items still go in the protector
return original_parent
@@ -0,0 +1,36 @@
/datum/component/storage/concrete/rped
collection_mode = COLLECT_EVERYTHING
allow_quick_gather = TRUE
allow_quick_empty = TRUE
click_gather = TRUE
max_w_class = WEIGHT_CLASS_NORMAL
max_combined_w_class = 100
max_items = 100
display_numerical_stacking = TRUE
/datum/component/storage/concrete/rped/can_be_inserted(obj/item/I, stop_messages, mob/M)
. = ..()
if(!I.get_part_rating())
if (!stop_messages)
to_chat(M, "<span class='warning'>[parent] only accepts machine parts!</span>")
return FALSE
/datum/component/storage/concrete/bluespace/rped
collection_mode = COLLECT_EVERYTHING
allow_quick_gather = TRUE
allow_quick_empty = TRUE
click_gather = TRUE
max_w_class = WEIGHT_CLASS_BULKY // can fit vending refills
max_combined_w_class = 800
max_items = 350
display_numerical_stacking = TRUE
/datum/component/storage/concrete/bluespace/rped/can_be_inserted(obj/item/I, stop_messages, mob/M)
. = ..()
if(!I.get_part_rating())
if (!stop_messages)
to_chat(M, "<span class='warning'>[parent] only accepts machine parts!</span>")
return FALSE
/datum/component/storage/concrete/cyborg/rped
max_items = 150
@@ -0,0 +1,5 @@
/datum/component/storage/concrete/secret_satchel/can_be_inserted(I,msg,user)
if(SSpersistence.spawned_objects[I])
to_chat(user, "<span class='warning'>[I] is unstable after its journey through space and time, it wouldn't survive another trip.</span>")
return FALSE
return ..()
@@ -0,0 +1,67 @@
//Stack-only storage.
/datum/component/storage/concrete/stack
display_numerical_stacking = TRUE
var/max_combined_stack_amount = 300
max_w_class = WEIGHT_CLASS_NORMAL
max_combined_w_class = WEIGHT_CLASS_NORMAL * 14
/datum/component/storage/concrete/stack/proc/total_stack_amount()
. = 0
var/atom/real_location = real_location()
for(var/i in real_location)
var/obj/item/stack/S = i
if(!istype(S))
continue
. += S.amount
/datum/component/storage/concrete/stack/proc/remaining_space()
return max(0, max_combined_stack_amount - total_stack_amount())
//emptying procs do not need modification as stacks automatically merge.
/datum/component/storage/concrete/stack/_insert_physical_item(obj/item/I, override = FALSE)
if(!istype(I, /obj/item/stack))
if(override)
return ..()
return FALSE
var/atom/real_location = real_location()
var/obj/item/stack/S = I
var/can_insert = min(S.amount, remaining_space())
if(!can_insert)
return FALSE
for(var/i in real_location) //combine.
if(QDELETED(I))
return
var/obj/item/stack/_S = i
if(!istype(_S))
continue
if(_S.merge_type == S.merge_type)
_S.add(can_insert)
S.use(can_insert, TRUE)
return TRUE
return ..(S.change_stack(null, can_insert), override)
/datum/component/storage/concrete/stack/remove_from_storage(obj/item/I, atom/new_location)
var/atom/real_location = real_location()
var/obj/item/stack/S = I
if(!istype(S))
return ..()
if(S.amount > S.max_amount)
var/overrun = S.amount - S.max_amount
S.amount = S.max_amount
var/obj/item/stack/temp = new S.type(real_location, overrun)
handle_item_insertion(temp)
return ..(S, new_location)
/datum/component/storage/concrete/stack/_process_numerical_display()
var/atom/real_location = real_location()
. = list()
for(var/i in real_location)
var/obj/item/stack/I = i
if(!istype(I) || QDELETED(I)) //We're specialized stack storage, just ignore non stacks.
continue
if(!.[I.merge_type])
.[I.merge_type] = new /datum/numbered_display(I, I.amount)
else
var/datum/numbered_display/ND = .[I.merge_type]
ND.number += I.amount
+778
View File
@@ -0,0 +1,778 @@
#define COLLECT_ONE 0
#define COLLECT_EVERYTHING 1
#define COLLECT_SAME 2
#define DROP_NOTHING 0
#define DROP_AT_PARENT 1
#define DROP_AT_LOCATION 2
// External storage-related logic:
// /mob/proc/ClickOn() in /_onclick/click.dm - clicking items in storages
// /mob/living/Move() in /modules/mob/living/living.dm - hiding storage boxes on mob movement
/datum/component/storage
dupe_mode = COMPONENT_DUPE_UNIQUE
var/datum/component/storage/concrete/master //If not null, all actions act on master and this is just an access point.
var/list/can_hold //if this is set, only things in this typecache will fit.
var/list/cant_hold //if this is set, anything in this typecache will not be able to fit.
var/list/mob/is_using //lazy list of mobs looking at the contents of this storage.
var/locked = FALSE //when locked nothing can see inside or use it.
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.
var/emp_shielded = FALSE
var/silent = FALSE //whether this makes a message when things are put in.
var/click_gather = FALSE //whether this can be clicked on items to pick it up rather than the other way around.
var/rustle_sound = TRUE //play rustle sound on interact.
var/allow_quick_empty = FALSE //allow empty verb which allows dumping on the floor of everything inside quickly.
var/allow_quick_gather = FALSE //allow toggle mob verb which toggles collecting all items from a tile.
var/collection_mode = COLLECT_EVERYTHING
var/insert_preposition = "in" //you put things "in" a bag, but "on" a tray.
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
var/allow_big_nesting = FALSE //allow storage objects of the same or greater size.
var/attack_hand_interact = TRUE //interact on attack hand.
var/quickdraw = FALSE //altclick interact
var/datum/action/item_action/storage_gather_mode/modeswitch_action
//Screen variables: Do not mess with these vars unless you know what you're doing. They're not defines so storage that isn't in the same location can be supported in the future.
var/screen_max_columns = 7 //These two determine maximum screen sizes.
var/screen_max_rows = INFINITY
var/screen_pixel_x = 16 //These two are pixel values for screen loc of boxes and closer
var/screen_pixel_y = 16
var/screen_start_x = 4 //These two are where the storage starts being rendered, screen_loc wise.
var/screen_start_y = 2
//End
/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)
RegisterSignal(parent, COMSIG_TRY_STORAGE_SHOW, .proc/signal_show_attempt)
RegisterSignal(parent, COMSIG_TRY_STORAGE_INSERT, .proc/signal_insertion_attempt)
RegisterSignal(parent, COMSIG_TRY_STORAGE_CAN_INSERT, .proc/signal_can_insert)
RegisterSignal(parent, COMSIG_TRY_STORAGE_TAKE_TYPE, .proc/signal_take_type)
RegisterSignal(parent, COMSIG_TRY_STORAGE_FILL_TYPE, .proc/signal_fill_type)
RegisterSignal(parent, COMSIG_TRY_STORAGE_SET_LOCKSTATE, .proc/set_locked)
RegisterSignal(parent, COMSIG_TRY_STORAGE_TAKE, .proc/signal_take_obj)
RegisterSignal(parent, COMSIG_TRY_STORAGE_QUICK_EMPTY, .proc/signal_quick_empty)
RegisterSignal(parent, COMSIG_TRY_STORAGE_HIDE_FROM, .proc/signal_hide_attempt)
RegisterSignal(parent, COMSIG_TRY_STORAGE_HIDE_ALL, .proc/close_all)
RegisterSignal(parent, COMSIG_TRY_STORAGE_RETURN_INVENTORY, .proc/signal_return_inv)
RegisterSignal(parent, COMSIG_PARENT_ATTACKBY, .proc/attackby)
RegisterSignal(parent, COMSIG_ATOM_ATTACK_HAND, .proc/on_attack_hand)
RegisterSignal(parent, COMSIG_ATOM_ATTACK_PAW, .proc/on_attack_hand)
RegisterSignal(parent, COMSIG_ATOM_EMP_ACT, .proc/emp_act)
RegisterSignal(parent, COMSIG_ATOM_ATTACK_GHOST, .proc/show_to_ghost)
RegisterSignal(parent, COMSIG_ATOM_ENTERED, .proc/refresh_mob_views)
RegisterSignal(parent, COMSIG_ATOM_EXITED, .proc/_remove_and_refresh)
RegisterSignal(parent, COMSIG_ATOM_CANREACH, .proc/canreach_react)
RegisterSignal(parent, COMSIG_ITEM_PRE_ATTACK, .proc/preattack_intercept)
RegisterSignal(parent, COMSIG_ITEM_ATTACK_SELF, .proc/attack_self)
RegisterSignal(parent, COMSIG_ITEM_PICKUP, .proc/signal_on_pickup)
RegisterSignal(parent, COMSIG_MOVABLE_POST_THROW, .proc/close_all)
RegisterSignal(parent, COMSIG_CLICK_ALT, .proc/on_alt_click)
RegisterSignal(parent, COMSIG_MOUSEDROP_ONTO, .proc/mousedrop_onto)
RegisterSignal(parent, COMSIG_MOUSEDROPPED_ONTO, .proc/mousedrop_receive)
update_actions()
/datum/component/storage/Destroy()
close_all()
QDEL_NULL(boxes)
QDEL_NULL(closer)
LAZYCLEARLIST(is_using)
return ..()
/datum/component/storage/PreTransfer()
update_actions()
/datum/component/storage/proc/update_actions()
QDEL_NULL(modeswitch_action)
if(!isitem(parent) || !allow_quick_gather)
return
var/obj/item/I = parent
modeswitch_action = new(I)
RegisterSignal(modeswitch_action, COMSIG_ACTION_TRIGGER, .proc/action_trigger)
if(I.obj_flags & IN_INVENTORY)
var/mob/M = I.loc
if(!istype(M))
return
modeswitch_action.Grant(M)
/datum/component/storage/proc/change_master(datum/component/storage/concrete/new_master)
if(new_master == src || (!isnull(new_master) && !istype(new_master)))
return FALSE
if(master)
master.on_slave_unlink(src)
master = new_master
if(master)
master.on_slave_link(src)
return TRUE
/datum/component/storage/proc/master()
if(master == src)
return //infinite loops yo.
return master
/datum/component/storage/proc/real_location()
var/datum/component/storage/concrete/master = master()
return master? master.real_location() : null
/datum/component/storage/proc/canreach_react(datum/source, list/next)
var/datum/component/storage/concrete/master = master()
if(!master)
return
. = COMPONENT_BLOCK_REACH
next += master.parent
for(var/i in master.slaves)
var/datum/component/storage/slave = i
next += slave.parent
/datum/component/storage/proc/attack_self(datum/source, mob/M)
if(check_locked(source, M, TRUE))
return FALSE
if((M.get_active_held_item() == parent) && allow_quick_empty)
quick_empty(M)
/datum/component/storage/proc/preattack_intercept(datum/source, obj/O, mob/M, params)
if(!isitem(O) || !click_gather || SEND_SIGNAL(O, COMSIG_CONTAINS_STORAGE))
return FALSE
. = COMPONENT_NO_ATTACK
if(check_locked(source, M, TRUE))
return FALSE
var/atom/A = parent
var/obj/item/I = O
if(collection_mode == COLLECT_ONE)
if(can_be_inserted(I, null, M))
handle_item_insertion(I, null, M)
A.do_squish()
return
if(!isturf(I.loc))
return
var/list/things = I.loc.contents.Copy()
if(collection_mode == COLLECT_SAME)
things = typecache_filter_list(things, typecacheof(I.type))
var/len = length(things)
if(!len)
to_chat(M, "<span class='notice'>You failed to pick up anything with [parent].</span>")
return
var/datum/progressbar/progress = new(M, len, I.loc)
var/list/rejections = list()
while(do_after(M, 10, TRUE, parent, FALSE, CALLBACK(src, .proc/handle_mass_pickup, things, I.loc, rejections, progress)))
stoplag(1)
qdel(progress)
to_chat(M, "<span class='notice'>You put everything you could [insert_preposition] [parent].</span>")
A.do_squish(1.4, 0.4)
/datum/component/storage/proc/handle_mass_item_insertion(list/things, datum/component/storage/src_object, mob/user, datum/progressbar/progress)
var/atom/source_real_location = src_object.real_location()
for(var/obj/item/I in things)
things -= I
if(I.loc != source_real_location)
continue
if(user.active_storage != src_object)
if(I.on_found(user))
break
if(can_be_inserted(I,FALSE,user))
handle_item_insertion(I, TRUE, user)
if (TICK_CHECK)
progress.update(progress.goal - things.len)
return TRUE
progress.update(progress.goal - things.len)
return FALSE
/datum/component/storage/proc/handle_mass_pickup(list/things, atom/thing_loc, list/rejections, datum/progressbar/progress)
var/atom/real_location = real_location()
for(var/obj/item/I in things)
things -= I
if(I.loc != thing_loc)
continue
if(I.type in rejections) // To limit bag spamming: any given type only complains once
continue
if(!can_be_inserted(I, stop_messages = TRUE)) // Note can_be_inserted still makes noise when the answer is no
if(real_location.contents.len >= max_items)
break
rejections += I.type // therefore full bags are still a little spammy
continue
handle_item_insertion(I, TRUE) //The TRUE stops the "You put the [parent] into [S]" insertion message from being displayed.
if (TICK_CHECK)
progress.update(progress.goal - things.len)
return TRUE
progress.update(progress.goal - things.len)
return FALSE
/datum/component/storage/proc/quick_empty(mob/M)
var/atom/A = parent
if((!ishuman(M) && (A.loc != M)) || (M.stat != CONSCIOUS) || M.restrained() || !M.canmove)
return
if(check_locked(null, M, TRUE))
return FALSE
A.add_fingerprint(M)
to_chat(M, "<span class='notice'>You start dumping out [parent].</span>")
var/turf/T = get_turf(A)
var/list/things = contents()
var/datum/progressbar/progress = new(M, length(things), T)
while (do_after(M, 10, TRUE, T, FALSE, CALLBACK(src, .proc/mass_remove_from_storage, T, things, progress)))
stoplag(1)
qdel(progress)
A.do_squish(0.8, 1.2)
/datum/component/storage/proc/mass_remove_from_storage(atom/target, list/things, datum/progressbar/progress, trigger_on_found = TRUE)
var/atom/real_location = real_location()
for(var/obj/item/I in things)
things -= I
if(I.loc != real_location)
continue
remove_from_storage(I, target)
if(trigger_on_found && I.on_found())
return FALSE
if(TICK_CHECK)
progress.update(progress.goal - length(things))
return TRUE
progress.update(progress.goal - length(things))
return FALSE
/datum/component/storage/proc/do_quick_empty(atom/_target)
if(!_target)
_target = get_turf(parent)
if(usr)
hide_from(usr)
var/list/contents = contents()
var/atom/real_location = real_location()
for(var/obj/item/I in contents)
if(I.loc != real_location)
continue
remove_from_storage(I, _target)
return TRUE
/datum/component/storage/proc/set_locked(datum/source, new_state)
locked = new_state
if(check_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.type] = new /datum/numbered_display(I, 1)
else
var/datum/numbered_display/ND = .[I.type]
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(mob/user, maxcolumns)
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, 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 = "<font color='white'>[(ND.number > 1)? "[ND.number]" : ""]</font>"
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/list/cview = getviewsize(M.client.view)
var/maxallowedscreensize = cview[1]-8
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, (isliving(M) ? maxallowedscreensize : 7))
M.client.screen |= boxes
M.client.screen |= closer
M.client.screen |= real_location.contents
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)
/datum/component/storage/proc/close_all()
. = FALSE
for(var/mob/M in can_see_contents())
close(M)
. = TRUE //returns TRUE if any mobs actually got a close(M) call
/datum/component/storage/proc/emp_act(datum/source, severity)
if(emp_shielded)
return
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))
return FALSE
var/datum/component/storage/concrete/master = master()
if(!istype(master))
return FALSE
return master._removal_reset(thing)
/datum/component/storage/proc/_remove_and_refresh(datum/source, atom/movable/thing)
_removal_reset(thing)
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
/datum/component/storage/proc/remove_from_storage(atom/movable/AM, atom/new_location)
if(!istype(AM))
return FALSE
var/datum/component/storage/concrete/master = master()
if(!istype(master))
return FALSE
return master.remove_from_storage(AM, new_location)
/datum/component/storage/proc/refresh_mob_views()
var/list/seeing = can_see_contents()
for(var/i in seeing)
show_to(i)
return TRUE
/datum/component/storage/proc/can_see_contents()
var/list/cansee = list()
for(var/mob/M in is_using)
if(M.active_storage == src && M.client)
cansee |= M
else
LAZYREMOVE(is_using, M)
return cansee
//Tries to dump content
/datum/component/storage/proc/dump_content_at(atom/dest_object, mob/M)
var/atom/A = parent
var/atom/dump_destination = dest_object.get_dumping_location()
if(A.Adjacent(M) && dump_destination && M.Adjacent(dump_destination))
if(check_locked(null, M, TRUE))
return FALSE
if(dump_destination.storage_contents_dump_act(src, M))
playsound(A, "rustle", 50, 1, -5)
A.do_squish(0.8, 1.2)
return TRUE
return FALSE
//This proc is called when you want to place an item into the storage item.
/datum/component/storage/proc/attackby(datum/source, obj/item/I, mob/M, params)
if(istype(I, /obj/item/hand_labeler))
var/obj/item/hand_labeler/labeler = I
if(labeler.mode)
return FALSE
. = TRUE //no afterattack
if(iscyborg(M))
return
if(!can_be_inserted(I, FALSE, M))
var/atom/real_location = real_location()
if(real_location.contents.len >= max_items) //don't use items on the backpack if they don't fit
return TRUE
return FALSE
handle_item_insertion(I, FALSE, M)
var/atom/A = parent
A.do_squish()
/datum/component/storage/proc/return_inv(recursive)
var/list/ret = list()
ret |= contents()
if(recursive)
for(var/i in ret.Copy())
var/atom/A = i
SEND_SIGNAL(A, COMSIG_TRY_STORAGE_RETURN_INVENTORY, ret, TRUE)
return ret
/datum/component/storage/proc/contents() //ONLY USE IF YOU NEED TO COPY CONTENTS OF REAL LOCATION, COPYING IS NOT AS FAST AS DIRECT ACCESS!
var/atom/real_location = real_location()
return real_location.contents.Copy()
//Abuses the fact that lists are just references, or something like that.
/datum/component/storage/proc/signal_return_inv(datum/source, list/interface, recursive = TRUE)
if(!islist(interface))
return FALSE
interface |= return_inv(recursive)
return TRUE
/datum/component/storage/proc/mousedrop_onto(datum/source, atom/over_object, mob/M)
set waitfor = FALSE
. = COMPONENT_NO_MOUSEDROP
var/atom/A = parent
if(ismob(M)) //all the check for item manipulation are in other places, you can safely open any storages as anything and its not buggy, i checked
A.add_fingerprint(M)
if(!over_object)
return FALSE
if(ismecha(M.loc)) // stops inventory actions in a mech
return FALSE
// this must come before the screen objects only block, dunno why it wasn't before
if(over_object == M)
user_show_to_mob(M)
if(!M.incapacitated())
if(!istype(over_object, /obj/screen))
dump_content_at(over_object, M)
return
if(A.loc != M)
return
playsound(A, "rustle", 50, 1, -5)
A.do_jiggle()
if(istype(over_object, /obj/screen/inventory/hand))
var/obj/screen/inventory/hand/H = over_object
M.putItemFromInventoryInHandIfPossible(A, H.held_index)
return
A.add_fingerprint(M)
/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
show_to(M)
/datum/component/storage/proc/mousedrop_receive(datum/source, atom/movable/O, mob/M)
if(isitem(O))
var/obj/item/I = O
if(iscarbon(M) || isdrone(M))
var/mob/living/L = M
if(!L.incapacitated() && I == L.get_active_held_item())
if(!SEND_SIGNAL(I, COMSIG_CONTAINS_STORAGE) && can_be_inserted(I, FALSE)) //If it has storage it should be trying to dump, not insert.
handle_item_insertion(I, FALSE, L)
var/atom/A = parent
A.do_squish()
//This proc return 1 if the item can be picked up and 0 if it can't.
//Set the stop_messages to stop it from printing messages
/datum/component/storage/proc/can_be_inserted(obj/item/I, stop_messages = FALSE, mob/M)
if(!istype(I) || (I.item_flags & ABSTRACT))
return FALSE //Not an item
if(I == parent)
return FALSE //no paradoxes for you
var/atom/real_location = real_location()
var/atom/host = parent
if(real_location == I.loc)
return FALSE //Means the item is already in the storage item
if(check_locked(null, M, !stop_messages))
if(M && !stop_messages)
host.add_fingerprint(M)
return FALSE
if(real_location.contents.len >= max_items)
if(!stop_messages)
to_chat(M, "<span class='warning'>[host] is full, make some space!</span>")
return FALSE //Storage item is full
if(length(can_hold))
if(!is_type_in_typecache(I, can_hold))
if(!stop_messages)
to_chat(M, "<span class='warning'>[host] cannot hold [I]!</span>")
return FALSE
if(is_type_in_typecache(I, cant_hold)) //Check for specific items which this container can't hold.
if(!stop_messages)
to_chat(M, "<span class='warning'>[host] cannot hold [I]!</span>")
return FALSE
if(I.w_class > max_w_class)
if(!stop_messages)
to_chat(M, "<span class='warning'>[I] is too big for [host]!</span>")
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, "<span class='warning'>[I] won't fit in [host], make some space!</span>")
return FALSE
if(isitem(host))
var/obj/item/IP = host
GET_COMPONENT_FROM(STR_I, /datum/component/storage, I)
if((I.w_class >= IP.w_class) && STR_I && !allow_big_nesting)
if(!stop_messages)
to_chat(M, "<span class='warning'>[IP] cannot hold [I] as it's a storage item of the same size!</span>")
return FALSE //To prevent the stacking of same sized storage items.
if(HAS_TRAIT(I, TRAIT_NODROP)) //SHOULD be handled in unEquip, but better safe than sorry.
to_chat(M, "<span class='warning'>\the [I] is stuck to your hand, you can't put it in \the [host]!</span>")
return FALSE
var/datum/component/storage/concrete/master = master()
if(!istype(master))
return FALSE
return master.slave_can_insert_object(src, I, stop_messages, M)
/datum/component/storage/proc/_insert_physical_item(obj/item/I, override = FALSE)
return FALSE
//This proc handles items being inserted. It does not perform any checks of whether an item can or can't be inserted. That's done by can_be_inserted()
//The stop_warning parameter will stop the insertion message from being displayed. It is intended for cases where you are inserting multiple items at once,
//such as when picking up all the items on a tile with one click.
/datum/component/storage/proc/handle_item_insertion(obj/item/I, prevent_warning = FALSE, mob/M, datum/component/storage/remote)
var/atom/parent = src.parent
var/datum/component/storage/concrete/master = master()
if(!istype(master))
return FALSE
if(silent)
prevent_warning = TRUE
if(M)
parent.add_fingerprint(M)
. = master.handle_item_insertion_from_slave(src, I, prevent_warning, M)
/datum/component/storage/proc/mob_item_insertion_feedback(mob/user, mob/M, obj/item/I, override = FALSE)
if(silent && !override)
return
if(rustle_sound)
playsound(parent, "rustle", 50, 1, -5)
for(var/mob/viewing in viewers(user, null))
if(M == viewing)
to_chat(usr, "<span class='notice'>You put [I] [insert_preposition]to [parent].</span>")
else if(in_range(M, viewing)) //If someone is standing close enough, they can tell what it is...
viewing.show_message("<span class='notice'>[M] puts [I] [insert_preposition]to [parent].</span>", 1)
else if(I && I.w_class >= 3) //Otherwise they can only see large or normal items from a distance...
viewing.show_message("<span class='notice'>[M] puts [I] [insert_preposition]to [parent].</span>", 1)
/datum/component/storage/proc/update_icon()
if(isobj(parent))
var/obj/O = parent
O.update_icon()
/datum/component/storage/proc/signal_insertion_attempt(datum/source, obj/item/I, mob/M, silent = FALSE, force = FALSE)
if((!force && !can_be_inserted(I, TRUE, M)) || (I == parent))
return FALSE
return handle_item_insertion(I, silent, M)
/datum/component/storage/proc/signal_can_insert(datum/source, obj/item/I, mob/M, silent = FALSE)
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)
/datum/component/storage/proc/signal_show_attempt(datum/source, mob/showto, force = FALSE)
return user_show_to_mob(showto, force)
/datum/component/storage/proc/on_check()
return TRUE
/datum/component/storage/proc/check_locked(datum/source, mob/user, message = FALSE)
. = locked
if(message && . && user)
to_chat(user, "<span class='warning'>[parent] seems to be locked!</span>")
/datum/component/storage/proc/signal_take_type(datum/source, type, atom/destination, amount = INFINITY, check_adjacent = FALSE, force = FALSE, mob/user, list/inserted)
if(!force)
if(check_adjacent)
if(!user || !user.CanReach(destination) || !user.CanReach(parent))
return FALSE
var/list/taking = typecache_filter_list(contents(), typecacheof(type))
if(taking.len > amount)
taking.len = amount
if(inserted) //duplicated code for performance, don't bother checking retval/checking for list every item.
for(var/i in taking)
if(remove_from_storage(i, destination))
inserted |= i
else
for(var/i in taking)
remove_from_storage(i, destination)
return TRUE
/datum/component/storage/proc/remaining_space_items()
var/atom/real_location = real_location()
return max(0, max_items - real_location.contents.len)
/datum/component/storage/proc/signal_fill_type(datum/source, type, amount = 20, force = FALSE)
var/atom/real_location = real_location()
if(!force)
amount = min(remaining_space_items(), amount)
for(var/i in 1 to amount)
handle_item_insertion(new type(real_location), TRUE)
CHECK_TICK
return TRUE
/datum/component/storage/proc/on_attack_hand(datum/source, mob/user)
var/atom/A = parent
if(!attack_hand_interact)
return
if(user.active_storage == src && A.loc == user) //if you're already looking inside the storage item
user.active_storage.close(user)
close(user)
. = COMPONENT_NO_ATTACK_HAND
return
if(rustle_sound)
playsound(A, "rustle", 50, 1, -5)
if(ishuman(user))
var/mob/living/carbon/human/H = user
if(H.l_store == A && !H.get_active_held_item()) //Prevents opening if it's in a pocket.
. = COMPONENT_NO_ATTACK_HAND
H.put_in_hands(A)
H.l_store = null
return
if(H.r_store == A && !H.get_active_held_item())
. = COMPONENT_NO_ATTACK_HAND
H.put_in_hands(A)
H.r_store = null
return
if(A.loc == user)
. = COMPONENT_NO_ATTACK_HAND
if(!check_locked(source, user, TRUE))
show_to(user)
A.do_jiggle()
/datum/component/storage/proc/signal_on_pickup(datum/source, mob/user)
var/atom/A = parent
update_actions()
for(var/mob/M in range(1, A))
if(M.active_storage == src)
close(M)
/datum/component/storage/proc/signal_take_obj(datum/source, atom/movable/AM, new_loc, force = FALSE)
if(!(AM in real_location()))
return FALSE
return remove_from_storage(AM, new_loc)
/datum/component/storage/proc/signal_quick_empty(datum/source, atom/loctarget)
return do_quick_empty(loctarget)
/datum/component/storage/proc/signal_hide_attempt(datum/source, mob/target)
return hide_from(target)
/datum/component/storage/proc/on_alt_click(datum/source, mob/user)
if(!isliving(user) || !user.CanReach(parent))
return
if(check_locked(source, user, TRUE))
return
var/atom/A = parent
if(!quickdraw)
A.add_fingerprint(user)
user_show_to_mob(user)
if(rustle_sound)
playsound(A, "rustle", 50, 1, -5)
return
if(!user.incapacitated())
var/obj/item/I = locate() in real_location()
if(!I)
return
A.add_fingerprint(user)
remove_from_storage(I, get_turf(user))
if(!user.put_in_hands(I))
to_chat(user, "<span class='notice'>You fumble for [I] and it falls on the floor.</span>")
return
user.visible_message("<span class='warning'>[user] draws [I] from [parent]!</span>", "<span class='notice'>You draw [I] from [parent].</span>")
return
/datum/component/storage/proc/action_trigger(datum/signal_source, datum/action/source)
gather_mode_switch(source.owner)
return COMPONENT_ACTION_BLOCK_TRIGGER
/datum/component/storage/proc/gather_mode_switch(mob/user)
collection_mode = (collection_mode+1)%3
switch(collection_mode)
if(COLLECT_SAME)
to_chat(user, "[parent] now picks up all items of a single type at once.")
if(COLLECT_EVERYTHING)
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.")