mirror of
https://github.com/Citadel-Station-13/Citadel-Station-13-RP.git
synced 2026-08-22 17:36:50 +01:00
292 lines
9.5 KiB
Plaintext
292 lines
9.5 KiB
Plaintext
//* This file is explicitly licensed under the MIT license. *//
|
|
//* Copyright (c) 2024 Citadel Station Developers *//
|
|
|
|
/**
|
|
* Internal inventory logic
|
|
* You shouldn't be calling or modifying these without good reason.
|
|
*/
|
|
|
|
//* Routing *//
|
|
|
|
/**
|
|
* handles the insertion
|
|
* item can be moved or not moved before calling
|
|
*
|
|
* slot must be a typepath
|
|
*
|
|
* @return true/false based on if it worked
|
|
*/
|
|
/mob/proc/handle_abstract_slot_insertion(obj/item/I, slot, flags)
|
|
if(!ispath(slot, /datum/inventory_slot/abstract))
|
|
slot = resolve_inventory_slot(slot)?.type
|
|
if(!ispath(slot, /datum/inventory_slot/abstract))
|
|
stack_trace("invalid slot: [slot]")
|
|
else if(slot != /datum/inventory_slot/abstract/put_in_hands)
|
|
stack_trace("attempted usage of slot id in abstract insertion converted successfully")
|
|
. = FALSE
|
|
switch(slot)
|
|
if(/datum/inventory_slot/abstract/hand/left)
|
|
return put_in_left_hand(I, flags)
|
|
if(/datum/inventory_slot/abstract/hand/right)
|
|
return put_in_right_hand(I, flags)
|
|
if(/datum/inventory_slot/abstract/put_in_belt)
|
|
var/obj/item/held = item_by_slot_id(SLOT_ID_BELT)
|
|
if(flags & INV_OP_FORCE)
|
|
return held?.obj_storage?.insert(I, new /datum/event_args/actor(src), flags & INV_OP_SUPPRESS_SOUND)
|
|
return held?.obj_storage?.auto_handle_inventory_insertion(I, new /datum/event_args/actor(src), flags & INV_OP_SUPPRESS_WARNING, flags & INV_OP_SUPPRESS_SOUND)
|
|
if(/datum/inventory_slot/abstract/put_in_backpack)
|
|
var/obj/item/held = item_by_slot_id(SLOT_ID_BACK)
|
|
if(flags & INV_OP_FORCE)
|
|
return held?.obj_storage?.insert(I, new /datum/event_args/actor(src), flags & INV_OP_SUPPRESS_SOUND)
|
|
return held?.obj_storage?.auto_handle_inventory_insertion(I, new /datum/event_args/actor(src), flags & INV_OP_SUPPRESS_WARNING, flags & INV_OP_SUPPRESS_SOUND)
|
|
if(/datum/inventory_slot/abstract/put_in_hands)
|
|
return put_in_hands(I, flags)
|
|
if(/datum/inventory_slot/abstract/put_in_storage, /datum/inventory_slot/abstract/put_in_storage_try_active)
|
|
if(slot == /datum/inventory_slot/abstract/put_in_storage_try_active)
|
|
// todo: redirection
|
|
if(flags & INV_OP_FORCE)
|
|
if(active_storage?.insert(I, new /datum/event_args/actor(src), flags & INV_OP_SUPPRESS_WARNING))
|
|
return TRUE
|
|
else
|
|
if(active_storage?.auto_handle_inventory_insertion(I, new /datum/event_args/actor(src), flags & INV_OP_SUPPRESS_WARNING, flags & INV_OP_SUPPRESS_SOUND))
|
|
return TRUE
|
|
for(var/obj/item/held in get_equipped_items_in_slots(list(
|
|
SLOT_ID_BELT,
|
|
SLOT_ID_BACK,
|
|
SLOT_ID_UNIFORM,
|
|
SLOT_ID_SUIT,
|
|
SLOT_ID_LEFT_POCKET,
|
|
SLOT_ID_RIGHT_POCKET
|
|
)) + get_held_items())
|
|
if(isnull(held?.obj_storage))
|
|
continue
|
|
if(flags & INV_OP_FORCE)
|
|
return held.obj_storage.insert(I, new /datum/event_args/actor(src), flags & INV_OP_SUPPRESS_SOUND)
|
|
return held.obj_storage.auto_handle_inventory_insertion(I, new /datum/event_args/actor(src), flags & INV_OP_SUPPRESS_WARNING, flags & INV_OP_SUPPRESS_SOUND)
|
|
return FALSE
|
|
if(/datum/inventory_slot/abstract/attach_as_accessory)
|
|
for(var/obj/item/clothing/C in get_equipped_items())
|
|
if(C.attempt_attach_accessory(I))
|
|
return TRUE
|
|
return FALSE
|
|
else
|
|
CRASH("Invalid abstract slot [slot]")
|
|
|
|
//* Unequip *//
|
|
|
|
/**
|
|
* handles internal logic of unequipping an item
|
|
*
|
|
* @params
|
|
* - I - item
|
|
* - flags - inventory operation hint bitfield, see defines
|
|
* - newloc - where to transfer to. null for nullspace, FALSE for don't transfer
|
|
* - user - can be null - person doing the removals
|
|
*
|
|
* @return TRUE/FALSE for success
|
|
*/
|
|
/mob/proc/_unequip_item(obj/item/I, flags, newloc, mob/user = src)
|
|
PROTECTED_PROC(TRUE)
|
|
if(!I)
|
|
return TRUE
|
|
|
|
var/hand = get_held_index(I)
|
|
var/old
|
|
if(hand)
|
|
if(!can_unequip(I, SLOT_ID_HANDS, flags, user))
|
|
return FALSE
|
|
unequip_hand_impl(I, hand, flags)
|
|
old = SLOT_ID_HANDS
|
|
else
|
|
if(!I.worn_slot)
|
|
stack_trace("tried to unequip an item without current equipped slot.")
|
|
I.worn_slot = _slot_by_item(I)
|
|
if(!can_unequip(I, I.worn_slot, flags, user))
|
|
return FALSE
|
|
old = I.worn_slot
|
|
_unequip_slot(old, flags, I)
|
|
I.on_inv_unequipped(src, inventory, old, flags)
|
|
I.unequipped(src, old, flags)
|
|
handle_item_denesting(I, old, flags, user)
|
|
|
|
// this qdeleted catches unequipped() deleting the item.
|
|
. = QDELETED(I)? FALSE : TRUE
|
|
|
|
if(I)
|
|
// todo: better rendering that takes observers into account
|
|
if(client)
|
|
client.screen -= I
|
|
I.screen_loc = null
|
|
//! at some point we should have /pre_dropped and /pre_pickup, because dropped should logically come after move.
|
|
if(I.dropped(src, flags, newloc) == ITEM_RELOCATED_BY_DROPPED)
|
|
. = FALSE
|
|
else if(QDELETED(I))
|
|
// this check RELIES on dropped() being the first if
|
|
// make sure you don't blindly move it!!
|
|
// this is meant to catch any potential deletions dropped can cause.
|
|
. = FALSE
|
|
else
|
|
if(!(I.item_flags & ITEM_DROPDEL))
|
|
if(newloc == null)
|
|
I.moveToNullspace()
|
|
else if(newloc != FALSE)
|
|
I.forceMove(newloc)
|
|
|
|
log_inventory("[key_name(src)] unequipped [I] from [old].")
|
|
|
|
/mob/proc/_unequip_slot(slot, flags, obj/item/yanking)
|
|
SHOULD_NOT_OVERRIDE(TRUE)
|
|
var/obj/item/old = _item_by_slot(slot)
|
|
if(old != yanking)
|
|
return FALSE
|
|
. = _set_inv_slot(slot, null, flags) != INVENTORY_SLOT_DOES_NOT_EXIST
|
|
if(.)
|
|
inventory.on_item_exited(old, resolve_inventory_slot(slot))
|
|
|
|
/**
|
|
* handles removing an item from our hud
|
|
*
|
|
* some things call us from outside inventory code. this is shitcode and shouldn't be propageted.
|
|
*/
|
|
/mob/proc/_handle_inventory_hud_remove(obj/item/I)
|
|
if(client)
|
|
client.screen -= I
|
|
I.screen_loc = null
|
|
|
|
//* Equip *//
|
|
|
|
/**
|
|
* handles internal logic of equipping an item
|
|
*
|
|
* @params
|
|
* - I - item to equip
|
|
* - flags - inventory operation hint flags, see defines
|
|
* - slot - slot to equip it to
|
|
* - user - user trying to put it on us
|
|
*
|
|
* @return TRUE/FALSE on success
|
|
*/
|
|
/mob/proc/_equip_item(obj/item/I, flags, slot, mob/user = src)
|
|
if(!I) // how tf would we put on "null"?
|
|
return FALSE
|
|
|
|
// resolve slot
|
|
var/datum/inventory_slot/slot_meta = resolve_inventory_slot(slot)
|
|
if(slot_meta.inventory_slot_flags & INV_SLOT_IS_ABSTRACT)
|
|
// if it's abstract, we go there directly - do not use can_equip as that will just guess.
|
|
return handle_abstract_slot_insertion(I, slot, flags)
|
|
|
|
// slots must have IDs.
|
|
ASSERT(!isnull(slot_meta.id))
|
|
// convert to ID after abstract slot checks
|
|
slot = slot_meta.id
|
|
|
|
var/old_slot = slot_id_by_item(I)
|
|
|
|
if(old_slot)
|
|
. = _handle_item_reequip(I, slot, old_slot, flags, user)
|
|
if(!.)
|
|
return
|
|
|
|
log_inventory("[key_name(src)] moved [I] from [old_slot] to [slot].")
|
|
else
|
|
if(!can_equip(I, slot, flags | INV_OP_IS_FINAL_CHECK, user))
|
|
return FALSE
|
|
|
|
var/atom/oldLoc = I.loc
|
|
if(I.loc != src)
|
|
I.forceMove(src)
|
|
if(I.loc != src)
|
|
// UH OH, SOMEONE MOVED US
|
|
log_inventory("[key_name(src)] failed to equip [I] to slot (loc sanity failed).")
|
|
// UH OH x2, WE GOT WORN OVER SOMETHING
|
|
if(I.worn_over)
|
|
handle_item_denesting(I, slot, INV_OP_FATAL, user)
|
|
return FALSE
|
|
|
|
_equip_slot(I, slot, flags)
|
|
|
|
// TODO: HANDLE DELETIONS IN PICKUP AND EQUIPPED PROPERLY
|
|
I.pickup(src, flags, oldLoc)
|
|
I.equipped(src, slot, flags)
|
|
|
|
log_inventory("[key_name(src)] equipped [I] to [slot].")
|
|
|
|
if(I.zoom)
|
|
I.zoom()
|
|
|
|
return TRUE
|
|
|
|
/mob/proc/_equip_slot(obj/item/I, slot, flags)
|
|
SHOULD_NOT_OVERRIDE(TRUE)
|
|
. = _set_inv_slot(slot, I, flags) != INVENTORY_SLOT_DOES_NOT_EXIST
|
|
if(.)
|
|
inventory.on_item_entered(I, resolve_inventory_slot(slot))
|
|
|
|
//* Slot Change *//
|
|
|
|
/**
|
|
* checks if we already have something in our inventory
|
|
* if so, this will try to shift the slots over, calling equipped/unequipped automatically
|
|
*
|
|
* INV_OP_FORCE will allow ignoring can unequip.
|
|
*
|
|
* return true/false based on if we succeeded
|
|
*/
|
|
/mob/proc/_handle_item_reequip(obj/item/I, slot, old_slot, flags, mob/user = src, hand_index)
|
|
ASSERT(slot)
|
|
if(!old_slot)
|
|
// DO NOT USE _slot_by_item - at this point, the item has already been var-set into the new slot!
|
|
// slot_id_by_item however uses cached values still!
|
|
old_slot = slot_id_by_item(I)
|
|
if(!old_slot)
|
|
// still not there, wasn't already in inv
|
|
return FALSE
|
|
// this IS a slot shift!
|
|
. = old_slot
|
|
if((slot == old_slot) && (slot != SLOT_ID_HANDS))
|
|
// lol we're done (unless it was hands)
|
|
return TRUE
|
|
if(slot == SLOT_ID_HANDS)
|
|
// if we're going into hands,
|
|
// just check can unequip
|
|
if(!can_unequip(I, old_slot, flags, user))
|
|
// check can unequip
|
|
return FALSE
|
|
// call procs
|
|
if(old_slot == SLOT_ID_HANDS)
|
|
unequip_hand_impl(I, get_held_index(I), flags)
|
|
else
|
|
_unequip_slot(old_slot, flags, I)
|
|
I.on_inv_unequipped(src, inventory, old_slot, flags)
|
|
I.unequipped(src, old_slot, flags)
|
|
// sigh
|
|
handle_item_denesting(I, old_slot, flags, user)
|
|
// TODO: HANDLE DELETIONS ON EQUIPPED PROPERLY, INCLUDING ON HANDS
|
|
// ? we don't do this on hands, hand procs do it
|
|
// _equip_slot(I, slot, update_icons)
|
|
I.held_index = hand_index
|
|
I.equipped(src, slot, flags)
|
|
log_inventory("[key_name(src)] moved [I] from [old_slot] to hands.")
|
|
// hand procs handle rest
|
|
return TRUE
|
|
else
|
|
// else, this gets painful
|
|
if(!can_unequip(I, old_slot, flags, user))
|
|
return FALSE
|
|
if(!can_equip(I, slot, flags | INV_OP_IS_FINAL_CHECK, user, old_slot))
|
|
return FALSE
|
|
// ?if it's from hands, hands aren't a slot.
|
|
if(old_slot == SLOT_ID_HANDS)
|
|
unequip_hand_impl(I, get_held_index(I), flags)
|
|
else
|
|
_unequip_slot(old_slot, flags, I)
|
|
I.unequipped(src, old_slot, flags)
|
|
I.on_inv_unequipped(src, inventory, old_slot, flags)
|
|
// TODO: HANDLE DELETIONS ON EQUIPPED PROPERLY
|
|
// sigh
|
|
_equip_slot(I, slot, flags)
|
|
I.equipped(src, slot, flags)
|
|
log_inventory("[key_name(src)] moved [I] from [old_slot] to [slot].")
|
|
return TRUE
|