mirror of
https://github.com/Citadel-Station-13/Citadel-Station-13-RP.git
synced 2026-08-25 16:56:59 +01:00
<!-- Write **BELOW** The Headers and **ABOVE** The comments else it may not be viewable. --> <!-- You can view Contributing.MD for a detailed description of the pull request process. --> ## About The Pull Request title, will be used when i tgify machines (upgradable machinery) <!-- Describe The Pull Request. Please be sure every change is documented or this can delay review and even discourage maintainers from merging your PR! --> ## Why It's Good For The Game <!-- Argue for the merits of your changes and how they benefit the game, especially if they are controversial and/or far reaching. If you can't actually explain WHY what you are doing will improve the game, then it probably isn't good for the game in the first place. --> ## Changelog <!-- If your PR modifies aspects of the game that can be concretely observed by players or admins you should add a changelog. If your change does NOT meet this description, remove this section. Please note that maintainers freely reserve the right to remove and add tags should they deem it appropriate. You can attempt to finagle the system all you want, but it's best to shoot for clear communication right off the bat. --> 🆑 code: properly implement `INTERACT_ATOM_*` /🆑 <!-- Both 🆑's are required for the changelog to work! You can put your name to the right of the first 🆑 if you want to overwrite your GitHub username as author ingame. --> <!-- You can use multiple of the same prefix (they're only used for the icon ingame) and delete the unneeded ones. Despite some of the tags, changelogs should generally represent how a player might be affected by the changes rather than a summary of the PR's contents. -->
249 lines
7.7 KiB
Plaintext
249 lines
7.7 KiB
Plaintext
//* This file is explicitly licensed under the MIT license. *//
|
|
//* Copyright (c) 2024 Citadel Station Developers *//
|
|
|
|
/**
|
|
* Abstraction procs
|
|
*
|
|
* With these, you can implement different inventory handling per mob
|
|
* These should usually not be called by non-mobs / items / etc.
|
|
*
|
|
* Core abstraction should never be called from outside
|
|
* Optional abstraction can be called with the caveat that you should be very careful in doing so.
|
|
*/
|
|
|
|
//* Core Abstraction *//
|
|
|
|
/**
|
|
* THESE PROCS MUST BE OVERRIDDEN FOR NEW SLOTS ON MOBS
|
|
* yes, i managed to shove all basic behaviors that needed overriding into 5-6 procs
|
|
* you're
|
|
* welcome.
|
|
*
|
|
* These are UNSAFE PROCS.
|
|
*
|
|
* oh and can_equip_x* might need overriding for complex mobs like humans but frankly
|
|
* sue me, there's no better way right now.
|
|
*/
|
|
|
|
/**
|
|
* sets a slot to icon or null
|
|
*
|
|
* some behaviors may be included other than update icons
|
|
* even update icons is unpreferred but we're stuck with this for now.
|
|
*
|
|
* todo: logic should be moved out of the proc, but where?
|
|
*
|
|
* @params
|
|
* slot - slot to set
|
|
* I - item or null
|
|
* update_icons - update icons immediately?
|
|
* logic - apply logic like dropping stuff from pockets when unequippiing a jumpsuit imemdiately?
|
|
*/
|
|
/mob/proc/_set_inv_slot(slot, obj/item/I, flags)
|
|
. = INVENTORY_SLOT_DOES_NOT_EXIST
|
|
CRASH("Attempting to set inv slot of [slot] to [I] went to base /mob. You probably had someone assigning to a nonexistant slot!")
|
|
|
|
/**
|
|
* ""expensive"" proc that scans for the real slot of an item
|
|
* usually used when safety checks detect something is amiss
|
|
*/
|
|
/mob/proc/_slot_by_item(obj/item/I)
|
|
|
|
/**
|
|
* doubles as slot detection
|
|
* returns -1 if no slot
|
|
* YES, MAGIC VALUE BUT SOLE USER IS 20 LINES ABOVE, SUE ME.
|
|
*/
|
|
/mob/proc/_item_by_slot(slot) as /obj/item
|
|
return INVENTORY_SLOT_DOES_NOT_EXIST
|
|
|
|
/mob/proc/_get_all_slots(include_restraints)
|
|
return list()
|
|
|
|
/**
|
|
* return all slot ids we implement
|
|
*/
|
|
/mob/proc/_get_inventory_slot_ids()
|
|
return list()
|
|
|
|
/**
|
|
* override this if you need to make a slot not semantically exist
|
|
* useful for other species that don't have a slot so you don't have jumpsuit requirements apply
|
|
*/
|
|
/mob/proc/_semantic_slot_id_check(id)
|
|
return TRUE
|
|
|
|
//* Optional Behaviors *//
|
|
|
|
/**
|
|
* checks for slot conflict
|
|
*/
|
|
/mob/proc/inventory_slot_conflict_check(obj/item/I, slot, flags)
|
|
var/obj/item/conflicting = _item_by_slot(slot)
|
|
if(conflicting)
|
|
if((flags & (INV_OP_CAN_DISPLACE | INV_OP_IS_FINAL_CHECK)) == (INV_OP_CAN_DISPLACE | INV_OP_IS_FINAL_CHECK))
|
|
drop_item_to_ground(conflicting, INV_OP_FORCE)
|
|
if(_item_by_slot(slot))
|
|
return CAN_EQUIP_SLOT_CONFLICT_HARD
|
|
else
|
|
return CAN_EQUIP_SLOT_CONFLICT_HARD
|
|
switch(slot)
|
|
if(SLOT_ID_LEFT_EAR, SLOT_ID_RIGHT_EAR)
|
|
if(I.slot_flags & SLOT_TWOEARS)
|
|
if(_item_by_slot(SLOT_ID_LEFT_EAR) || _item_by_slot(SLOT_ID_RIGHT_EAR))
|
|
return CAN_EQUIP_SLOT_CONFLICT_SOFT
|
|
else
|
|
var/obj/item/left_ear = _item_by_slot(SLOT_ID_LEFT_EAR)
|
|
var/obj/item/right_ear = _item_by_slot(SLOT_ID_RIGHT_EAR)
|
|
if(left_ear && left_ear != INVENTORY_SLOT_DOES_NOT_EXIST && left_ear != I && left_ear.slot_flags & SLOT_TWOEARS)
|
|
return CAN_EQUIP_SLOT_CONFLICT_SOFT
|
|
else if(right_ear && right_ear != INVENTORY_SLOT_DOES_NOT_EXIST && right_ear != I && right_ear.slot_flags & SLOT_TWOEARS)
|
|
return CAN_EQUIP_SLOT_CONFLICT_SOFT
|
|
return CAN_EQUIP_SLOT_CONFLICT_NONE
|
|
|
|
/**
|
|
* checks if you can reach a slot
|
|
* return null or the first item blocking
|
|
*/
|
|
/mob/proc/inventory_slot_reachability_conflict(obj/item/I, slot, mob/user)
|
|
return null
|
|
|
|
/**
|
|
* semantic check - should this item fit here? slot flag checks/etc should go in here.
|
|
*
|
|
* return TRUE if conflicting, otherwise FALSE
|
|
*/
|
|
/mob/proc/inventory_slot_semantic_conflict(obj/item/I, datum/inventory_slot/slot, mob/user)
|
|
. = FALSE
|
|
slot = resolve_inventory_slot(slot)
|
|
return slot._equip_check(I, src, user)
|
|
|
|
/**
|
|
* checks if we are missing the bodypart for a slot
|
|
* return FALSE if we are missing, or TRUE if we're not
|
|
*
|
|
* this proc should give the feedback of what's missing!
|
|
*/
|
|
/mob/proc/inventory_slot_bodypart_check(obj/item/I, slot, mob/user, flags)
|
|
return TRUE
|
|
|
|
//* Hands *//
|
|
|
|
/**
|
|
* Hands are a bit of a special case
|
|
*
|
|
* They don't use inventory slots like the inventory system does,
|
|
* But still exists in the inventory module because they do a number of things,
|
|
* like count as equipped under [SLOT_ID_HANDS].
|
|
*
|
|
* This semi-integration lets us do a few cool things like not needing separate hooks
|
|
* for when someone has something in hand, but not in inventory, transitioning
|
|
* to them having it in inventory, but not in hands.
|
|
*/
|
|
|
|
/**
|
|
* the big, bad proc ultimately in charge of putting something into someone's hand
|
|
* whether it's from the ground, from a slot, or from another hand.
|
|
*/
|
|
/mob/proc/equip_hand_impl(obj/item/I, index, flags)
|
|
if(!I)
|
|
return TRUE
|
|
// let's not do that if it's deleted!
|
|
if(QDELETED(I))
|
|
to_chat(src, SPAN_DANGER("A deleted item [I] ([REF(I)]) was sent into inventory hand procs with flags [flags]. Report this line to coders immediately."))
|
|
to_chat(src, SPAN_DANGER("The inventory system will attempt to reject the bad equip. Glitches may occur."))
|
|
return FALSE
|
|
|
|
if(length(inventory?.held_items) < index)
|
|
return FALSE
|
|
|
|
var/obj/item/existing = inventory?.held_items[index]
|
|
if(!isnull(existing))
|
|
if(flags & INV_OP_FORCE)
|
|
drop_held_index(index, flags | INV_OP_NO_UPDATE_ICONS)
|
|
if(!isnull(inventory?.held_items[index]))
|
|
// failed to drop
|
|
return FALSE
|
|
else
|
|
return FALSE
|
|
|
|
var/existing_slot = is_in_inventory(I)
|
|
if(existing_slot == SLOT_ID_HANDS)
|
|
handle_item_handswap(I, index, get_held_index(I), flags)
|
|
else
|
|
if(existing_slot)
|
|
// already in inv
|
|
if(!_handle_item_reequip(I, SLOT_ID_HANDS, existing_slot, flags, src, index))
|
|
return FALSE
|
|
log_inventory("equip-to-hand: keyname [key_name(src)] index [index] item [I]([ref(I)]) from slot [existing_slot]")
|
|
else
|
|
// newly eqiupped
|
|
var/atom/old_loc = I.loc
|
|
if(I.loc != src)
|
|
I.forceMove(src)
|
|
if(I.loc != src)
|
|
return FALSE
|
|
log_inventory("pickup-to-hand: keyname [key_name(src)] index [index] item [I]([ref(I)])")
|
|
I.held_index = index
|
|
I.pickup(src, flags, old_loc)
|
|
I.equipped(src, SLOT_ID_HANDS, flags)
|
|
|
|
inventory.held_items[index] = I
|
|
inventory.on_item_entered(I, index)
|
|
// TODO: pickup can't detect it because we set it after pickup
|
|
I.inv_slot_or_index = index
|
|
|
|
if(!(flags & INV_OP_NO_UPDATE_ICONS))
|
|
update_inv_hand(index)
|
|
|
|
if(!(I.interaction_flags_atom & INTERACT_ATOM_NO_FINGERPRINT_ATTACK_HAND))
|
|
I.add_fingerprint(src)
|
|
else
|
|
I.add_hiddenprint(src)
|
|
|
|
return TRUE
|
|
|
|
/**
|
|
* get something out of our hand
|
|
*
|
|
* @return unequipped item
|
|
*/
|
|
/mob/proc/unequip_hand_impl(obj/item/I, index, flags)
|
|
ASSERT(inventory?.held_items[index] == I)
|
|
|
|
inventory.held_items[index] = null
|
|
inventory.on_item_exited(I, index)
|
|
|
|
I.held_index = null
|
|
I.unequipped(src, SLOT_ID_HANDS, flags)
|
|
I.on_inv_unequipped(src, inventory, index, flags)
|
|
|
|
if(!(flags & INV_OP_NO_UPDATE_ICONS))
|
|
update_inv_hand(index)
|
|
|
|
return TRUE
|
|
|
|
/**
|
|
* handle swapping item from one hand index to another
|
|
*/
|
|
/mob/proc/handle_item_handswap(obj/item/I, index, old_index, flags, mob/user = src)
|
|
ASSERT(inventory?.held_items[old_index] == I)
|
|
ASSERT(isnull(inventory?.held_items[index]))
|
|
|
|
inventory.held_items[old_index] = null
|
|
inventory.held_items[index] = I
|
|
I.held_index = index
|
|
inventory.on_item_swapped(I, old_index, index)
|
|
|
|
if(!(flags & INV_OP_NO_UPDATE_ICONS))
|
|
update_inv_hand(old_index)
|
|
update_inv_hand(index)
|
|
|
|
/**
|
|
* Checks if we can pick up an item.
|
|
* * Despite 'actor' being provided, 'actor.performer' should always be 'src' in this context.
|
|
* It's just provided to allow a common API surface.
|
|
*/
|
|
/mob/proc/should_allow_pickup(obj/item/item, datum/event_args/actor/actor, silent)
|
|
return TRUE
|