Files
Bubberstation/code/datums/components/mutant_hands.dm
SkyratBot 7f29afc8ec [MIRROR] Refactors species mutanthands into human component [MDB IGNORE] (#19355)
* Refactors species mutanthands into human component

* wew

---------

Co-authored-by: MrMelbert <51863163+MrMelbert@users.noreply.github.com>
Co-authored-by: Gandalf <9026500+Gandalf2k15@users.noreply.github.com>
2023-03-10 02:39:41 +00:00

171 lines
6.1 KiB
Plaintext

/**
* ## Mutant hands component
*
* This component applies to humans, and forces them to hold
* a certain typepath item in every hand no matter what*.
*
* For example, zombies being forced to hold "zombie claws" - disallowing them from holding items
* but giving them powerful weapons to infect people
*
* It is suggested that the item path supplied has NODROP (and likely DROPDEL),
* but nothing's preventing you from not having that.
*
* If they lose or gain hands, new mutant hands will be created immediately.
*
* Does not override nodrop items that already exist in hand slots.
* However if those nodrop items are lost, will immediately create a new mutant hand.
*/
/datum/component/mutant_hands
// First come, first serve
dupe_mode = COMPONENT_DUPE_UNIQUE
/// The item typepath that we insert into the parent's hands
var/obj/item/mutant_hand_path = /obj/item/mutant_hand
/datum/component/mutant_hands/Initialize(obj/item/mutant_hand_path = /obj/item/mutant_hand)
if(!ishuman(parent))
return COMPONENT_INCOMPATIBLE
src.mutant_hand_path = mutant_hand_path
/datum/component/mutant_hands/RegisterWithParent()
// Give them a hand before registering ANYTHING just so it's clean
INVOKE_ASYNC(src, PROC_REF(apply_mutant_hands))
RegisterSignals(parent, list(COMSIG_CARBON_POST_ATTACH_LIMB, COMSIG_CARBON_POST_REMOVE_LIMB), PROC_REF(try_reapply_hands))
RegisterSignal(parent, COMSIG_MOB_EQUIPPED_ITEM, PROC_REF(mob_equipped_item))
RegisterSignal(parent, COMSIG_MOB_UNEQUIPPED_ITEM, PROC_REF(mob_dropped_item))
/datum/component/mutant_hands/UnregisterFromParent()
UnregisterSignal(parent, list(
COMSIG_CARBON_POST_ATTACH_LIMB,
COMSIG_CARBON_POST_REMOVE_LIMB,
COMSIG_MOB_EQUIPPED_ITEM,
COMSIG_MOB_UNEQUIPPED_ITEM,
))
// Remove all their hands after unregistering everything so they don't return
INVOKE_ASYNC(src, PROC_REF(remove_mutant_hands))
/**
* Tries to give the parent mob mutant hands.
*
* * If a hand slot is empty, places the mutanthand type into their hand.
* * If a hand slot is filled with a nodrop item, it will do nothing.
* * If a hand slot is filled with a non-nodrop item, drops the item to the ground.
* * If a hand slot is filled with a hand already, does nothing.
*/
/datum/component/mutant_hands/proc/apply_mutant_hands()
var/mob/living/carbon/human/human_parent = parent
for(var/obj/item/hand_slot as anything in human_parent.held_items)
// This slot is already a mutant hand
if(istype(hand_slot, mutant_hand_path))
continue
// This slot is not empty
// Yes the held item lists contains nulls to represent empty hands
// It saves us a /item cast by using as anything in the loop
if(!isnull(hand_slot))
if(HAS_TRAIT(hand_slot, TRAIT_NODROP) || (hand_slot.item_flags & ABSTRACT))
// There's a nodrop / abstract item in the way of putting a mutant hand in
// It can stay, for now, but if it gets dropped / unequipped we'll swoop in to replace the slot
continue
// Drop any existing non-nodrop items to the ground
human_parent.dropItemToGround(hand_slot)
// Put in hands has a sleep somewhere in there
human_parent.put_in_hands(new mutant_hand_path(), del_on_fail = TRUE)
/**
* Removes all mutant idems from the parent's hand slots
*/
/datum/component/mutant_hands/proc/remove_mutant_hands()
var/mob/living/carbon/human/human_parent = parent
for(var/obj/item/hand_slot in human_parent.held_items)
// Not a mutant hand, don't need to delete it
if(!istype(hand_slot, mutant_hand_path))
continue
// Just send it to the shadow realm, this will handle unequipping and remove it for us
qdel(hand_slot)
/**
* Signal proc for any signals that may result in the number of hands of the parent mob changing
*
* Always try to re-insert mutanthands if we gain or lose hands
*/
/datum/component/mutant_hands/proc/try_reapply_hands(datum/source)
SIGNAL_HANDLER
if(QDELING(src) || QDELING(parent))
return
INVOKE_ASYNC(src, PROC_REF(apply_mutant_hands))
/**
* Signal proc for [COMSIG_MOB_EQUIPPED_ITEM]
*
* This is a failsafe - the mob managed to pick up something that isn't a mutant hand
*/
/datum/component/mutant_hands/proc/mob_equipped_item(mob/living/carbon/human/source, obj/item/thing, slot)
SIGNAL_HANDLER
if(!(slot & ITEM_SLOT_HANDS)) // Who cares
return
if(istype(thing, mutant_hand_path)) // This is definitely meant to be here
return
if(HAS_TRAIT(thing, TRAIT_NODROP) || (thing.item_flags & ABSTRACT)) // This is meant to be here
return
// We equipped something to hands that wasn't a mutant hand, and wasn't abstract!
// This means they're meant to have a mutant hand. So help them out.
INVOKE_ASYNC(src, PROC_REF(apply_mutant_hands))
/**
* Signal proc for [COMSIG_MOB_UNEQUIPPED_ITEM]
*
* This is another failsafe - the mob dropped something, maybe from their hands, so try to re-equip
*/
/datum/component/mutant_hands/proc/mob_dropped_item(mob/living/carbon/human/source, obj/item/thing)
SIGNAL_HANDLER
if(QDELING(src) || QDELING(parent))
return
if(null in source.held_items)
INVOKE_ASYNC(src, PROC_REF(apply_mutant_hands))
/**
* Generic mutant hand type for use with the mutant hands component
* (Technically speaking, the component doesn't require you use this type. But it's here for posterity)
*
* Implements nothing except changing its icon state between left and right depending on hand slot equipped in
*/
/obj/item/mutant_hand
name = "mutant hand"
desc = "Won't somebody give me a hand?"
icon = 'icons/effects/blood.dmi'
icon_state = "bloodhand_left"
base_icon_state = "bloodhand"
item_flags = ABSTRACT | DROPDEL | HAND_ITEM
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF
/obj/item/mutant_hand/Initialize(mapload)
. = ..()
ADD_TRAIT(src, TRAIT_NODROP, HAND_REPLACEMENT_TRAIT)
/obj/item/mutant_hand/visual_equipped(mob/user, slot)
. = ..()
if(!base_icon_state)
return
// Even hand indexes are right hands,
// Odd hand indexes are left hand
// ...But also, we swap it intentionally here,
// so right icon is shown on the left (Because hands)
if(user.get_held_index_of_item(src) % 2 == 1)
icon_state = "[base_icon_state]_right"
else
icon_state = "[base_icon_state]_left"