mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2025-12-09 16:05:07 +00:00
## About The Pull Request This PR tackles our piss-poor item action handling. Currently in order to make an item only have actions when its equipped to a certain slot you need to override a proc, which I've changed by introducing an action_slots variable. I've also cleaned up a ton of action code, and most importantly moved a lot of Trigger effects on items to do_effect, which allows actions to not call ui_action_click or attack_self on an item without bypassing IsAvailible and comsigs that parent Trigger has. This resolves issues like jump boots being usable from your hands, HUDs being toggleable out of your pockets, etc. Also moved a few actions from relying on attack_self to individual handling on their side. This also stops welding masks/hardhats from showing their action while you hold them, this part of the change is just something I thought didn't make much sense - you can use their action by using them in-hand, and flickering on your action bar can be annoying when reshuffling your backpack. Closes #89653 ## Why It's Good For The Game Makes action handling significantly less ass, allows us to avoid code like this ```js /obj/item/clothing/mask/gas/sechailer/ui_action_click(mob/user, action) if(istype(action, /datum/action/item_action/halt)) halt() else adjust_visor(user) ```
617 lines
22 KiB
Plaintext
617 lines
22 KiB
Plaintext
//These procs handle putting stuff in your hands
|
|
//as they handle all relevant stuff like adding it to the player's screen and updating their overlays.
|
|
|
|
///Returns the thing we're currently holding
|
|
/mob/proc/get_active_held_item()
|
|
return get_item_for_held_index(active_hand_index)
|
|
|
|
|
|
//Finds the opposite limb for the active one (eg: upper left arm will find the item in upper right arm)
|
|
//So we're treating each "pair" of limbs as a team, so "both" refers to them
|
|
/mob/proc/get_inactive_held_item()
|
|
return get_item_for_held_index(get_inactive_hand_index())
|
|
|
|
|
|
//Finds the opposite index for the active one (eg: upper left arm will find the item in upper right arm)
|
|
//So we're treating each "pair" of limbs as a team, so "both" refers to them
|
|
/mob/proc/get_inactive_hand_index()
|
|
var/other_hand = 0
|
|
if(IS_RIGHT_INDEX(active_hand_index))
|
|
other_hand = active_hand_index-1 //finding the matching "left" limb
|
|
else
|
|
other_hand = active_hand_index+1 //finding the matching "right" limb
|
|
if(other_hand < 0 || other_hand > held_items.len)
|
|
other_hand = 0
|
|
return other_hand
|
|
|
|
|
|
/mob/proc/get_item_for_held_index(i)
|
|
if(i > 0 && i <= held_items.len)
|
|
return held_items[i]
|
|
return null
|
|
|
|
|
|
//Odd = left. Even = right
|
|
/mob/proc/held_index_to_dir(i)
|
|
if(IS_RIGHT_INDEX(i))
|
|
return "r"
|
|
return "l"
|
|
|
|
//Check we have an organ for this hand slot (Dismemberment), Only relevant for humans
|
|
/mob/proc/has_hand_for_held_index(i)
|
|
return TRUE
|
|
|
|
|
|
//Check we have an organ for our active hand slot (Dismemberment),Only relevant for humans
|
|
/mob/proc/has_active_hand()
|
|
return has_hand_for_held_index(active_hand_index)
|
|
|
|
|
|
//Finds the first available (null) index OR all available (null) indexes in held_items based on a side.
|
|
//Lefts: 1, 3, 5, 7...
|
|
//Rights:2, 4, 6, 8...
|
|
/mob/proc/get_empty_held_index_for_side(side = LEFT_HANDS, all = FALSE)
|
|
var/list/empty_indexes = all ? list() : null
|
|
for(var/i in (side == LEFT_HANDS) ? 1 : 2 to held_items.len step 2)
|
|
if(!held_items[i])
|
|
if(!all)
|
|
return i
|
|
empty_indexes += i
|
|
return empty_indexes
|
|
|
|
|
|
//Same as the above, but returns the first or ALL held *ITEMS* for the side
|
|
/mob/proc/get_held_items_for_side(side = LEFT_HANDS, all = FALSE)
|
|
var/list/holding_items = all ? list() : null
|
|
for(var/i in (side == LEFT_HANDS) ? 1 : 2 to held_items.len step 2)
|
|
var/obj/item/I = held_items[i]
|
|
if(I)
|
|
if(!all)
|
|
return I
|
|
holding_items += I
|
|
return holding_items
|
|
|
|
|
|
/mob/proc/get_empty_held_indexes()
|
|
var/list/L
|
|
for(var/i in 1 to held_items.len)
|
|
if(!held_items[i])
|
|
LAZYADD(L, i)
|
|
return L
|
|
|
|
/mob/proc/get_held_index_of_item(obj/item/I)
|
|
return held_items.Find(I)
|
|
|
|
|
|
///Find number of held items, multihand compatible
|
|
/mob/proc/get_num_held_items()
|
|
. = 0
|
|
for(var/i in 1 to held_items.len)
|
|
if(held_items[i])
|
|
.++
|
|
|
|
//Sad that this will cause some overhead, but the alias seems necessary
|
|
//*I* may be happy with a million and one references to "indexes" but others won't be
|
|
/mob/proc/is_holding(obj/item/I)
|
|
return get_held_index_of_item(I)
|
|
|
|
|
|
//Checks if we're holding an item of type: typepath
|
|
/mob/proc/is_holding_item_of_type(typepath)
|
|
for(var/obj/item/I in held_items)
|
|
if(istype(I, typepath))
|
|
return I
|
|
return FALSE
|
|
|
|
// List version of above proc
|
|
// Returns ret_item, which is either the successfully located item or null
|
|
/mob/proc/is_holding_item_of_types(list/typepaths)
|
|
for(var/typepath in typepaths)
|
|
var/ret_item = is_holding_item_of_type(typepath)
|
|
return ret_item
|
|
|
|
//Checks if we're holding a tool that has given quality
|
|
//Returns the tool that has the best version of this quality
|
|
/mob/proc/is_holding_tool_quality(quality)
|
|
var/obj/item/best_item
|
|
var/best_quality = INFINITY
|
|
|
|
for(var/obj/item/I in held_items)
|
|
if(I.tool_behaviour == quality && I.toolspeed < best_quality)
|
|
best_item = I
|
|
best_quality = I.toolspeed
|
|
|
|
return best_item
|
|
|
|
|
|
//To appropriately fluff things like "they are holding [I] in their [get_held_index_name(get_held_index_of_item(I))]"
|
|
//Can be overridden to pass off the fluff to something else (eg: science allowing people to add extra robotic limbs, and having this proc react to that
|
|
// with say "they are holding [I] in their Nanotrasen Brand Utility Arm - Right Edition" or w/e
|
|
/mob/proc/get_held_index_name(i)
|
|
var/list/hand = list()
|
|
if(i > 2)
|
|
hand += "upper "
|
|
var/num = 0
|
|
if(IS_RIGHT_INDEX(i))
|
|
num = i-2
|
|
hand += "right hand"
|
|
else
|
|
num = i-1
|
|
hand += "left hand"
|
|
num -= (num*0.5)
|
|
if(num > 1) //"upper left hand #1" seems weird, but "upper left hand #2" is A-ok
|
|
hand += " #[num]"
|
|
return hand.Join()
|
|
|
|
|
|
|
|
//Returns if a certain item can be equipped to a certain slot.
|
|
// Currently invalid for two-handed items - call obj/item/mob_can_equip() instead.
|
|
/mob/proc/can_equip(obj/item/I, slot, disable_warning = FALSE, bypass_equip_delay_self = FALSE, ignore_equipped = FALSE, indirect_action = FALSE)
|
|
return FALSE
|
|
|
|
/mob/proc/can_put_in_hand(I, hand_index)
|
|
if(hand_index > held_items.len)
|
|
return FALSE
|
|
if(!put_in_hand_check(I))
|
|
return FALSE
|
|
if(!has_hand_for_held_index(hand_index))
|
|
return FALSE
|
|
return !held_items[hand_index]
|
|
|
|
/mob/proc/put_in_hand(obj/item/I, hand_index, forced = FALSE, ignore_anim = TRUE, visuals_only = FALSE)
|
|
if(hand_index == null || !held_items.len || (!forced && !can_put_in_hand(I, hand_index)))
|
|
return FALSE
|
|
|
|
if(isturf(I.loc) && !ignore_anim)
|
|
I.do_pickup_animation(src)
|
|
if(get_item_for_held_index(hand_index))
|
|
dropItemToGround(get_item_for_held_index(hand_index), force = TRUE)
|
|
I.forceMove(src)
|
|
held_items[hand_index] = I
|
|
SET_PLANE_EXPLICIT(I, ABOVE_HUD_PLANE, src)
|
|
if(I.pulledby)
|
|
I.pulledby.stop_pulling()
|
|
if(!I.on_equipped(src, ITEM_SLOT_HANDS, initial = visuals_only))
|
|
return FALSE
|
|
update_held_items()
|
|
I.pixel_x = I.base_pixel_x
|
|
I.pixel_y = I.base_pixel_y
|
|
if(QDELETED(I)) // this is here because some ABSTRACT items like slappers and circle hands could be moved from hand to hand then delete, which meant you'd have a null in your hand until you cleared it (say, by dropping it)
|
|
held_items[hand_index] = null
|
|
return FALSE
|
|
return hand_index
|
|
|
|
//Puts the item into the first available left hand if possible and calls all necessary triggers/updates. returns 1 on success.
|
|
/mob/proc/put_in_l_hand(obj/item/I, visuals_only = FALSE)
|
|
return put_in_hand(I, get_empty_held_index_for_side(LEFT_HANDS), visuals_only = visuals_only)
|
|
|
|
//Puts the item into the first available right hand if possible and calls all necessary triggers/updates. returns 1 on success.
|
|
/mob/proc/put_in_r_hand(obj/item/I, visuals_only = FALSE)
|
|
return put_in_hand(I, get_empty_held_index_for_side(RIGHT_HANDS), visuals_only = visuals_only)
|
|
|
|
/mob/proc/put_in_hand_check(obj/item/I)
|
|
return FALSE //nonliving mobs don't have hands
|
|
|
|
/mob/living/put_in_hand_check(obj/item/I)
|
|
if(istype(I) && ((mobility_flags & MOBILITY_PICKUP) || (I.item_flags & ABSTRACT)) \
|
|
&& !(SEND_SIGNAL(src, COMSIG_LIVING_TRY_PUT_IN_HAND, I) & COMPONENT_LIVING_CANT_PUT_IN_HAND))
|
|
return TRUE
|
|
return FALSE
|
|
|
|
//Puts the item into our active hand if possible. returns TRUE on success.
|
|
/mob/proc/put_in_active_hand(obj/item/I, forced = FALSE, ignore_animation = TRUE, visuals_only = FALSE)
|
|
return put_in_hand(I, active_hand_index, forced, ignore_animation, visuals_only)
|
|
|
|
|
|
//Puts the item into our inactive hand if possible, returns TRUE on success
|
|
/mob/proc/put_in_inactive_hand(obj/item/I, forced = FALSE, visuals_only = FALSE)
|
|
return put_in_hand(I, get_inactive_hand_index(), forced, visuals_only = visuals_only)
|
|
|
|
|
|
//Puts the item our active hand if possible. Failing that it tries other hands. Returns TRUE on success.
|
|
//If both fail it drops it on the floor (or nearby tables if germ sensitive) and returns FALSE.
|
|
//This is probably the main one you need to know :)
|
|
/mob/proc/put_in_hands(obj/item/I, del_on_fail = FALSE, merge_stacks = TRUE, forced = FALSE, ignore_animation = TRUE, visuals_only = FALSE)
|
|
if(QDELETED(I))
|
|
return FALSE
|
|
|
|
// If the item is a stack and we're already holding a stack then merge
|
|
if (isstack(I))
|
|
var/obj/item/stack/item_stack = I
|
|
var/obj/item/stack/active_stack = get_active_held_item()
|
|
|
|
if (item_stack.is_zero_amount(delete_if_zero = TRUE))
|
|
return FALSE
|
|
|
|
if (merge_stacks)
|
|
if (istype(active_stack) && active_stack.can_merge(item_stack, inhand = TRUE))
|
|
if (item_stack.merge(active_stack))
|
|
to_chat(usr, span_notice("Your [active_stack.name] stack now contains [active_stack.get_amount()] [active_stack.singular_name]\s."))
|
|
return TRUE
|
|
else
|
|
var/obj/item/stack/inactive_stack = get_inactive_held_item()
|
|
if (istype(inactive_stack) && inactive_stack.can_merge(item_stack, inhand = TRUE))
|
|
if (item_stack.merge(inactive_stack))
|
|
to_chat(usr, span_notice("Your [inactive_stack.name] stack now contains [inactive_stack.get_amount()] [inactive_stack.singular_name]\s."))
|
|
return TRUE
|
|
|
|
if(put_in_active_hand(I, forced, ignore_animation, visuals_only))
|
|
return TRUE
|
|
|
|
var/hand = get_empty_held_index_for_side(LEFT_HANDS)
|
|
if(!hand)
|
|
hand = get_empty_held_index_for_side(RIGHT_HANDS)
|
|
if(hand)
|
|
if(put_in_hand(I, hand, forced, ignore_animation, visuals_only))
|
|
return TRUE
|
|
if(del_on_fail)
|
|
qdel(I)
|
|
return FALSE
|
|
|
|
// Failed to put in hands - drop the item
|
|
var/atom/location = drop_location()
|
|
|
|
// Try dropping on nearby tables if germ sensitive (except table behind you)
|
|
if(HAS_TRAIT(I, TRAIT_GERM_SENSITIVE))
|
|
var/list/dirs = list( // All dirs in clockwise order
|
|
NORTH,
|
|
NORTHEAST,
|
|
EAST,
|
|
SOUTHEAST,
|
|
SOUTH,
|
|
SOUTHWEST,
|
|
WEST,
|
|
NORTHWEST,
|
|
)
|
|
var/dir_count = dirs.len
|
|
var/facing_dir_index = dirs.Find(dir)
|
|
var/cw_index = facing_dir_index
|
|
var/ccw_index = facing_dir_index
|
|
var/list/turfs_ordered = list(get_step(src, dir))
|
|
|
|
// Build ordered list of turfs starting from the front facing
|
|
for(var/i in 1 to ROUND_UP(dir_count/2) - 1)
|
|
cw_index++
|
|
if(cw_index > dir_count)
|
|
cw_index = 1
|
|
turfs_ordered += get_step(src, dirs[cw_index]) // Add next tile on your right
|
|
ccw_index--
|
|
if(ccw_index <= 0)
|
|
ccw_index = dir_count
|
|
turfs_ordered += get_step(src, dirs[ccw_index]) // Add next tile on your left
|
|
|
|
// Check tables on these turfs
|
|
for(var/turf in turfs_ordered)
|
|
if(locate(/obj/structure/table) in turf || locate(/obj/structure/rack) in turf || locate(/obj/machinery/icecream_vat) in turf)
|
|
location = turf
|
|
break
|
|
|
|
I.forceMove(location)
|
|
I.layer = initial(I.layer)
|
|
SET_PLANE_EXPLICIT(I, initial(I.plane), location)
|
|
I.dropped(src)
|
|
return FALSE
|
|
|
|
/// Returns true if a mob is holding something
|
|
/mob/proc/is_holding_items()
|
|
return !!locate(/obj/item) in held_items
|
|
|
|
/**
|
|
* Returns a list of all dropped held items.
|
|
* If none were dropped, returns an empty list.
|
|
*/
|
|
/mob/proc/drop_all_held_items()
|
|
. = list()
|
|
for(var/obj/item/I in held_items)
|
|
. |= dropItemToGround(I)
|
|
|
|
//Here lie drop_from_inventory and before_item_take, already forgotten and not missed.
|
|
|
|
/mob/proc/canUnEquip(obj/item/I, force)
|
|
if(!I)
|
|
return TRUE
|
|
if(HAS_TRAIT(I, TRAIT_NODROP) && !force)
|
|
return FALSE
|
|
return TRUE
|
|
|
|
/mob/proc/putItemFromInventoryInHandIfPossible(obj/item/I, hand_index, force_removal = FALSE)
|
|
if(!can_put_in_hand(I, hand_index))
|
|
return FALSE
|
|
if(!temporarilyRemoveItemFromInventory(I, force_removal))
|
|
return FALSE
|
|
I.remove_item_from_storage(src)
|
|
if(!put_in_hand(I, hand_index))
|
|
qdel(I)
|
|
CRASH("Assertion failure: putItemFromInventoryInHandIfPossible") //should never be possible
|
|
return TRUE
|
|
|
|
//The following functions are the same save for one small difference
|
|
|
|
/**
|
|
* Used to drop an item (if it exists) to the ground.
|
|
* * Will return null if the item wasn't dropped.
|
|
* * If it was, returns the item.
|
|
* If the item can be dropped, it will be forceMove()'d to the ground and the turf's Entered() will be called.
|
|
*/
|
|
/mob/proc/dropItemToGround(obj/item/I, force = FALSE, silent = FALSE, invdrop = TRUE)
|
|
if (isnull(I))
|
|
return
|
|
|
|
SEND_SIGNAL(src, COMSIG_MOB_DROPPING_ITEM)
|
|
var/try_uneqip = doUnEquip(I, force, drop_location(), FALSE, invdrop = invdrop, silent = silent)
|
|
|
|
if(!try_uneqip || !I) //ensure the item exists and that it was dropped properly.
|
|
return
|
|
|
|
if(!(I.item_flags & NO_PIXEL_RANDOM_DROP))
|
|
I.pixel_x = I.base_pixel_x + rand(-6, 6)
|
|
I.pixel_y = I.base_pixel_y + rand(-6, 6)
|
|
I.do_drop_animation(src)
|
|
return I
|
|
|
|
//for when the item will be immediately placed in a loc other than the ground
|
|
/mob/proc/transferItemToLoc(obj/item/I, newloc = null, force = FALSE, silent = TRUE)
|
|
. = doUnEquip(I, force, newloc, FALSE, silent = silent)
|
|
I.do_drop_animation(src)
|
|
|
|
//visibly unequips I but it is NOT MOVED AND REMAINS IN SRC, newloc is for signal handling checks only which hints where you want to move the object after removal
|
|
//item MUST BE FORCEMOVE'D OR QDEL'D
|
|
/mob/proc/temporarilyRemoveItemFromInventory(obj/item/I, force = FALSE, idrop = TRUE, atom/newloc = src)
|
|
return doUnEquip(I, force, newloc, TRUE, idrop, silent = TRUE)
|
|
|
|
//DO NOT CALL THIS PROC
|
|
//use one of the above 3 helper procs
|
|
//you may override it, but do not modify the args
|
|
/mob/proc/doUnEquip(obj/item/I, force, atom/newloc, no_move, invdrop = TRUE, silent = FALSE) //Force overrides TRAIT_NODROP for things like wizarditis and admin undress.
|
|
//Use no_move if the item is just gonna be immediately moved afterward
|
|
//Invdrop is used to prevent stuff in pockets dropping. only set to false if it's going to immediately be replaced
|
|
PROTECTED_PROC(TRUE)
|
|
if(!I) //If there's nothing to drop, the drop is automatically successful. If(unEquip) should generally be used to check for TRAIT_NODROP.
|
|
return TRUE
|
|
|
|
if(HAS_TRAIT(I, TRAIT_NODROP) && !force)
|
|
return FALSE
|
|
|
|
if((SEND_SIGNAL(I, COMSIG_ITEM_PRE_UNEQUIP, force, newloc, no_move, invdrop, silent) & COMPONENT_ITEM_BLOCK_UNEQUIP) && !force)
|
|
return FALSE
|
|
|
|
var/hand_index = get_held_index_of_item(I)
|
|
if(hand_index)
|
|
held_items[hand_index] = null
|
|
update_held_items()
|
|
if(I)
|
|
if(client)
|
|
client.screen -= I
|
|
I.layer = initial(I.layer)
|
|
SET_PLANE_EXPLICIT(I, initial(I.plane), newloc)
|
|
I.appearance_flags &= ~NO_CLIENT_COLOR
|
|
if(!no_move && !(I.item_flags & DROPDEL)) //item may be moved/qdel'd immedietely, don't bother moving it
|
|
if (isnull(newloc))
|
|
I.moveToNullspace()
|
|
else
|
|
I.forceMove(newloc)
|
|
I.dropped(src, silent)
|
|
SEND_SIGNAL(I, COMSIG_ITEM_POST_UNEQUIP, force, newloc, no_move, invdrop, silent)
|
|
SEND_SIGNAL(src, COMSIG_MOB_UNEQUIPPED_ITEM, I, force, newloc, no_move, invdrop, silent)
|
|
return TRUE
|
|
|
|
/**
|
|
* Used to return a list of equipped items on a mob; does not include held items (use get_all_gear)
|
|
*
|
|
* Argument(s):
|
|
* * Optional - include_flags, (see obj.flags.dm) describes which optional things to include or not (pockets, accessories, held items)
|
|
*/
|
|
|
|
/mob/living/proc/get_equipped_items(include_flags = NONE)
|
|
var/list/items = list()
|
|
for(var/obj/item/item_contents in contents)
|
|
if(item_contents.item_flags & IN_INVENTORY)
|
|
items += item_contents
|
|
if (!(include_flags & INCLUDE_HELD))
|
|
items -= held_items
|
|
return items
|
|
|
|
/**
|
|
* Returns the items that were successfully unequipped.
|
|
*/
|
|
/mob/living/proc/unequip_everything()
|
|
var/list/items = list()
|
|
items |= get_equipped_items(INCLUDE_POCKETS)
|
|
// In case something isn't actually unequipped somehow
|
|
var/list/dropped_items = list()
|
|
for(var/I in items)
|
|
var/return_val = dropItemToGround(I)
|
|
if(!isitem(return_val))
|
|
continue
|
|
dropped_items |= return_val
|
|
var/return_val = drop_all_held_items()
|
|
if(islist(return_val))
|
|
dropped_items |= return_val
|
|
return dropped_items
|
|
|
|
/**
|
|
* Try to equip an item to a slot on the mob
|
|
*
|
|
* This is a SAFE proc. Use this instead of equip_to_slot()!
|
|
*
|
|
* set qdel_on_fail to have it delete W if it fails to equip
|
|
*
|
|
* set disable_warning to disable the 'you are unable to equip that' warning.
|
|
*
|
|
* unset redraw_mob to prevent the mob icons from being redrawn at the end.
|
|
*
|
|
* Initial is used to indicate whether or not this is the initial equipment (job datums etc) or just a player doing it
|
|
*
|
|
* set indirect_action to allow insertions into "soft" locked objects, things that are easily opened by the owning mob
|
|
*/
|
|
/mob/proc/equip_to_slot_if_possible(obj/item/W, slot, qdel_on_fail = FALSE, disable_warning = FALSE, redraw_mob = TRUE, bypass_equip_delay_self = FALSE, initial = FALSE, indirect_action = FALSE)
|
|
if(!istype(W) || QDELETED(W)) //This qdeleted is to prevent stupid behavior with things that qdel during init, like say stacks
|
|
return FALSE
|
|
if(!W.mob_can_equip(src, slot, disable_warning, bypass_equip_delay_self, indirect_action = indirect_action))
|
|
if(qdel_on_fail)
|
|
qdel(W)
|
|
else if(!disable_warning)
|
|
to_chat(src, span_warning("You are unable to equip that!"))
|
|
return FALSE
|
|
equip_to_slot(W, slot, initial, redraw_mob, indirect_action = indirect_action) //This proc should not ever fail.
|
|
return TRUE
|
|
|
|
/**
|
|
* Actually equips an item to a slot (UNSAFE)
|
|
*
|
|
* This is an UNSAFE proc. It merely handles the actual job of equipping. All the checks on
|
|
* whether you can or can't equip need to be done before! Use mob_can_equip() for that task.
|
|
*
|
|
*In most cases you will want to use equip_to_slot_if_possible()
|
|
*/
|
|
/mob/proc/equip_to_slot(obj/item/equipping, slot, initial = FALSE, redraw_mob = FALSE, indirect_action = FALSE)
|
|
return
|
|
|
|
/**
|
|
* Equip an item to the slot or delete
|
|
*
|
|
* This is just a commonly used configuration for the equip_to_slot_if_possible() proc, used to
|
|
* equip people when the round starts and when events happen and such.
|
|
*
|
|
* Also bypasses equip delay checks, since the mob isn't actually putting it on.
|
|
* Initial is used to indicate whether or not this is the initial equipment (job datums etc) or just a player doing it
|
|
* set indirect_action to allow insertions into "soft" locked objects, things that are easily opened by the owning mob
|
|
*/
|
|
/mob/proc/equip_to_slot_or_del(obj/item/W, slot, initial = FALSE, indirect_action = FALSE)
|
|
return equip_to_slot_if_possible(W, slot, TRUE, TRUE, FALSE, TRUE, initial, indirect_action)
|
|
|
|
/**
|
|
* Auto equip the passed in item the appropriate slot based on equipment priority
|
|
*
|
|
* puts the item "W" into an appropriate slot in a human's inventory
|
|
*
|
|
* returns 0 if it cannot, 1 if successful
|
|
*/
|
|
/mob/proc/equip_to_appropriate_slot(obj/item/W, qdel_on_fail = FALSE, indirect_action = FALSE, blacklist, initial) // SKYRAT EDIT CHANGE - ORIGINAL: /mob/proc/equip_to_appropriate_slot(obj/item/W, qdel_on_fail = FALSE, indirect_action = FALSE)
|
|
if(!istype(W))
|
|
return FALSE
|
|
var/slot_priority = W.slot_equipment_priority
|
|
|
|
if(!slot_priority)
|
|
slot_priority = list( \
|
|
ITEM_SLOT_BACK, ITEM_SLOT_ID,\
|
|
ITEM_SLOT_ICLOTHING, ITEM_SLOT_OCLOTHING,\
|
|
ITEM_SLOT_MASK, ITEM_SLOT_HEAD, ITEM_SLOT_NECK,\
|
|
ITEM_SLOT_FEET, ITEM_SLOT_GLOVES,\
|
|
ITEM_SLOT_EARS, ITEM_SLOT_EYES,\
|
|
ITEM_SLOT_BELT, ITEM_SLOT_SUITSTORE,\
|
|
ITEM_SLOT_LPOCKET, ITEM_SLOT_RPOCKET,\
|
|
ITEM_SLOT_DEX_STORAGE\
|
|
)
|
|
|
|
// SKYRAT EDIT ADDITION START - CUSTOMIZATION
|
|
if (blacklist)
|
|
slot_priority -= blacklist
|
|
// SKYRAT EDIT ADDITION END
|
|
|
|
for(var/slot in slot_priority)
|
|
if(equip_to_slot_if_possible(W, slot, disable_warning = TRUE, redraw_mob = TRUE, indirect_action = indirect_action, initial = initial)) // SKYRAT EDIT CHANGE - ORIGINAL: if(equip_to_slot_if_possible(W, slot, disable_warning = TRUE, redraw_mob = TRUE, indirect_action = indirect_action))
|
|
return TRUE
|
|
|
|
if(qdel_on_fail)
|
|
qdel(W)
|
|
return FALSE
|
|
|
|
/// Tries to equip an item, store it in open storage, or in next best storage
|
|
/obj/item/proc/equip_to_best_slot(mob/user)
|
|
if(user.equip_to_appropriate_slot(src))
|
|
user.update_held_items()
|
|
return TRUE
|
|
else
|
|
if(equip_delay_self)
|
|
return
|
|
|
|
if(user.active_storage?.attempt_insert(src, user, messages = FALSE))
|
|
return TRUE
|
|
|
|
var/list/obj/item/possible = list(
|
|
user.get_inactive_held_item(),
|
|
user.get_item_by_slot(ITEM_SLOT_BELT),
|
|
user.get_item_by_slot(ITEM_SLOT_DEX_STORAGE),
|
|
user.get_item_by_slot(ITEM_SLOT_BACK),
|
|
)
|
|
for(var/thing in possible)
|
|
if(isnull(thing))
|
|
continue
|
|
var/obj/item/gear = thing
|
|
if(gear.atom_storage?.attempt_insert(src, user, messages = FALSE))
|
|
return TRUE
|
|
|
|
to_chat(user, span_warning("You are unable to equip that!"))
|
|
return FALSE
|
|
|
|
|
|
/mob/verb/quick_equip()
|
|
set name = "quick-equip"
|
|
set hidden = TRUE
|
|
|
|
DEFAULT_QUEUE_OR_CALL_VERB(VERB_CALLBACK(src, PROC_REF(execute_quick_equip)))
|
|
|
|
///proc extender of [/mob/verb/quick_equip] used to make the verb queuable if the server is overloaded
|
|
/mob/proc/execute_quick_equip()
|
|
var/obj/item/I = get_active_held_item()
|
|
if(!I)
|
|
to_chat(src, span_warning("You are not holding anything to equip!"))
|
|
return
|
|
if(!QDELETED(I))
|
|
I.equip_to_best_slot(src)
|
|
|
|
//used in code for items usable by both carbon and drones, this gives the proper back slot for each mob.(defibrillator, backpack watertank, ...)
|
|
/mob/proc/getBackSlot()
|
|
return ITEM_SLOT_BACK
|
|
|
|
//Inventory.dm is -kind of- an ok place for this I guess
|
|
|
|
//This is NOT for dismemberment, as the user still technically has 2 "hands"
|
|
//This is for multi-handed mobs, such as a human with a third limb installed
|
|
//This is a very rare proc to call (besides admin fuckery) so
|
|
//any cost it has isn't a worry
|
|
/mob/proc/change_number_of_hands(amt)
|
|
if(amt < held_items.len)
|
|
for(var/i in held_items.len to amt step -1)
|
|
dropItemToGround(held_items[i])
|
|
held_items.len = amt
|
|
|
|
if(hud_used)
|
|
hud_used.build_hand_slots()
|
|
|
|
//GetAllContents that is reasonable and not stupid
|
|
/mob/living/proc/get_all_gear(accessories = TRUE, recursive = TRUE)
|
|
var/list/processing_list = get_equipped_items(INCLUDE_POCKETS | INCLUDE_HELD | (accessories ? INCLUDE_ACCESSORIES : NONE))
|
|
list_clear_nulls(processing_list) // handles empty hands
|
|
var/i = 0
|
|
while(i < length(processing_list))
|
|
var/atom/A = processing_list[++i]
|
|
if(A.atom_storage && recursive)
|
|
processing_list += A.atom_storage.return_inv()
|
|
return processing_list
|
|
|
|
/// Returns a list of things that the provided mob has, including any storage-capable implants.
|
|
/mob/living/proc/gather_belongings(accessories = TRUE, recursive = TRUE)
|
|
var/list/belongings = get_all_gear(accessories, recursive)
|
|
for (var/obj/item/implant/storage/internal_bag in implants)
|
|
belongings += internal_bag.contents
|
|
return belongings
|
|
|
|
/// Safely drop everything, without deconstructing the mob
|
|
/mob/living/proc/drop_everything(del_on_drop, force, del_if_nodrop)
|
|
. = list() //list of items that were successfully dropped
|
|
|
|
var/list/all_gear = get_all_gear(recursive = FALSE)
|
|
for(var/obj/item/item in all_gear)
|
|
if(dropItemToGround(item, force))
|
|
if(QDELETED(item)) //DROPDEL can cause this item to be deleted
|
|
continue
|
|
if(del_on_drop)
|
|
qdel(item)
|
|
continue
|
|
. += item
|
|
else if(del_if_nodrop && !(item.item_flags & ABSTRACT))
|
|
qdel(item)
|