[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>
This commit is contained in:
SkyratBot
2023-03-10 03:39:41 +01:00
committed by GitHub
parent f48b604aff
commit 7f29afc8ec
18 changed files with 296 additions and 87 deletions
@@ -32,6 +32,8 @@
#define COMPONENT_NO_ATTACH (1<<0)
///from base of /obj/item/bodypart/proc/try_attach_limb(): (new_limb, special)
#define COMSIG_CARBON_ATTACH_LIMB "carbon_attach_limb"
///from base of /obj/item/bodypart/proc/try_attach_limb(): (new_limb, special)
#define COMSIG_CARBON_POST_ATTACH_LIMB "carbon_post_attach_limb"
#define COMSIG_BODYPART_GAUZED "bodypart_gauzed" // from /obj/item/bodypart/proc/apply_gauze(/obj/item/stack/gauze)
#define COMSIG_BODYPART_GAUZE_DESTROYED "bodypart_degauzed" // from [/obj/item/bodypart/proc/seep_gauze] when it runs out of absorption
@@ -47,6 +49,8 @@
/// Called from carbon losing a limb /obj/item/bodypart/proc/drop_limb(obj/item/bodypart/lost_limb, dismembered)
#define COMSIG_CARBON_REMOVE_LIMB "carbon_remove_limb"
/// Called from carbon losing a limb /obj/item/bodypart/proc/drop_limb(obj/item/bodypart/lost_limb, dismembered)
#define COMSIG_CARBON_POST_REMOVE_LIMB "carbon_post_remove_limb"
/// Called from bodypart being removed /obj/item/bodypart/proc/drop_limb(mob/living/carbon/old_owner, dismembered)
#define COMSIG_BODYPART_REMOVED "bodypart_removed"
@@ -136,4 +140,3 @@
///from /atom/movable/screen/alert/give/proc/handle_transfer(): (taker, item)
#define COMSIG_CARBON_ITEM_GIVEN "carbon_item_given"
+170
View File
@@ -0,0 +1,170 @@
/**
* ## 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"
@@ -310,7 +310,6 @@
|| targetspecies.mutantheart != initial(targetspecies.mutantheart) \
|| targetspecies.mutanteyes != initial(targetspecies.mutanteyes) \
|| targetspecies.mutantears != initial(targetspecies.mutantears) \
|| targetspecies.mutanthands != initial(targetspecies.mutanthands) \
|| targetspecies.mutanttongue != initial(targetspecies.mutanttongue) \
|| targetspecies.mutantliver != initial(targetspecies.mutantliver) \
|| targetspecies.mutantstomach != initial(targetspecies.mutantstomach) \
@@ -83,43 +83,32 @@
/// Callback for the ghoul status effect - what effects are applied to the ghoul.
/datum/heretic_knowledge/limited_amount/risen_corpse/proc/apply_to_risen(mob/living/risen)
LAZYADD(created_items, WEAKREF(risen))
for(var/obj/item/held as anything in risen.held_items)
if(istype(held))
risen.dropItemToGround(held)
risen.put_in_hands(new /obj/item/risen_hand(), del_on_fail = TRUE)
risen.AddComponent(/datum/component/mutant_hands, mutant_hand_path = /obj/item/mutant_hand/shattered_risen)
/// Callback for the ghoul status effect - cleaning up effects after the ghoul status is removed.
/datum/heretic_knowledge/limited_amount/risen_corpse/proc/remove_from_risen(mob/living/risen)
LAZYREMOVE(created_items, WEAKREF(risen))
for(var/obj/item/risen_hand/hand in risen.held_items)
qdel(hand)
qdel(risen.GetComponent(/datum/component/mutant_hands))
#undef RISEN_MAX_HEALTH
/// The "hand" "weapon" used by shattered risen
/obj/item/risen_hand
/obj/item/mutant_hand/shattered_risen
name = "bone-shards"
desc = "What once appeared to be a normal human fist, now holds a maulled nest of sharp bone-shards."
icon = 'icons/effects/blood.dmi'
base_icon_state = "bloodhand"
color = "#001aff"
item_flags = ABSTRACT | DROPDEL | HAND_ITEM
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF
hitsound = SFX_SHATTER
force = 16
sharpness = SHARP_EDGED
wound_bonus = -30
bare_wound_bonus = 15
demolition_mod = 1.5
sharpness = SHARP_EDGED
/obj/item/risen_hand/Initialize(mapload)
/obj/item/mutant_hand/shattered_risen/Initialize(mapload)
. = ..()
ADD_TRAIT(src, TRAIT_NODROP, HAND_REPLACEMENT_TRAIT)
/obj/item/risen_hand/visual_equipped(mob/user, slot)
/obj/item/mutant_hand/shattered_risen/visual_equipped(mob/user, slot)
. = ..()
// Even hand indexes are right hands,
-1
View File
@@ -474,7 +474,6 @@
if(hud_used)
hud_used.build_hand_slots()
/mob/living/carbon/human/change_number_of_hands(amt)
var/old_limbs = held_items.len
if(amt < old_limbs)
+4 -3
View File
@@ -18,9 +18,10 @@
if(wear_neck && !(obscured & ITEM_SLOT_NECK))
. += "[t_He] [t_is] wearing [wear_neck.get_examine_string(user)] around [t_his] neck."
for(var/obj/item/I in held_items)
if(!(I.item_flags & ABSTRACT))
. += "[t_He] [t_is] holding [I.get_examine_string(user)] in [t_his] [get_held_index_name(get_held_index_of_item(I))]."
for(var/obj/item/held_thing in held_items)
if(held_thing.item_flags & (ABSTRACT|EXAMINE_SKIP|HAND_ITEM))
continue
. += "[t_He] [t_is] holding [held_thing.get_examine_string(user)] in [t_his] [get_held_index_name(get_held_index_of_item(held_thing))]."
if (back)
. += "[t_He] [t_has] [back.get_examine_string(user)] on [t_his] back."
@@ -87,9 +87,10 @@
. += "[t_He] [t_has] [back.get_examine_string(user)] on [t_his] back."
//Hands
for(var/obj/item/I in held_items)
if(!(I.item_flags & ABSTRACT) && !(I.item_flags & EXAMINE_SKIP))
. += "[t_He] [t_is] holding [I.get_examine_string(user)] in [t_his] [get_held_index_name(get_held_index_of_item(I))]."
for(var/obj/item/held_thing in held_items)
if(held_thing.item_flags & (ABSTRACT|EXAMINE_SKIP|HAND_ITEM))
continue
. += "[t_He] [t_is] holding [held_thing.get_examine_string(user)] in [t_his] [get_held_index_name(get_held_index_of_item(held_thing))]."
//gloves
if(gloves && !(obscured & ITEM_SLOT_GLOVES) && !(gloves.item_flags & EXAMINE_SKIP))
@@ -206,12 +206,9 @@
. += thing?.slowdown
/mob/living/carbon/human/doUnEquip(obj/item/I, force, newloc, no_move, invdrop = TRUE, silent = FALSE)
var/index = get_held_index_of_item(I)
. = ..() //See mob.dm for an explanation on this and some rage about people copypasting instead of calling ..() like they should.
if(!. || !I)
return
if(index && !QDELETED(src) && dna.species.mutanthands) //hand freed, fill with claws, skip if we're getting deleted.
put_in_hand(new dna.species.mutanthands(), index)
if(I == wear_suit)
if(s_store && invdrop)
dropItemToGround(s_store, TRUE) //It makes no sense for your suit storage to stay on you if you drop your suit.
@@ -192,8 +192,6 @@ GLOBAL_LIST_EMPTY(features_by_species)
var/obj/item/organ/internal/stomach/mutantstomach = /obj/item/organ/internal/stomach
///Replaces default appendix with a different organ.
var/obj/item/organ/internal/appendix/mutantappendix = /obj/item/organ/internal/appendix
///Forces an item into this species' hands. Only an honorary mutantthing because this is not an organ and not loaded in the same way, you've been warned to do your research.
var/obj/item/mutanthands
///Bitflag that controls what in game ways something can select this species as a spawnable source, such as magic mirrors. See [mob defines][code/__DEFINES/mobs.dm] for possible sources.
var/changesource_flags = NONE
@@ -469,21 +467,6 @@ GLOBAL_LIST_EMPTY(features_by_species)
if(exotic_bloodtype && C.dna.blood_type != exotic_bloodtype)
C.dna.blood_type = exotic_bloodtype
if(old_species.mutanthands)
for(var/obj/item/I in C.held_items)
if(istype(I, old_species.mutanthands))
qdel(I)
if(mutanthands)
// Drop items in hands
// If you're lucky enough to have a TRAIT_NODROP item, then it stays.
for(var/V in C.held_items)
var/obj/item/I = V
if(istype(I))
C.dropItemToGround(I)
else //Entries in the list should only ever be items or null, so if it's not an item, we can assume it's an empty hand
INVOKE_ASYNC(C, TYPE_PROC_REF(/mob, put_in_hands), new mutanthands)
if(ishuman(C))
var/mob/living/carbon/human/human = C
for(var/obj/item/organ/external/organ_path as anything in external_organs)
@@ -89,7 +89,6 @@
name = "Infectious Zombie"
id = SPECIES_ZOMBIE_INFECTIOUS
examine_limb_id = SPECIES_ZOMBIE
mutanthands = /obj/item/zombie_hand
armor = 20 // 120 damage to KO a zombie, which kills it
speedmod = 1.6
mutanteyes = /obj/item/organ/internal/eyes/night_vision/zombie
@@ -121,6 +120,14 @@
TRAIT_STABLELIVER, // Not necessary but for consistency with above
)
/datum/species/zombie/infectious/on_species_gain(mob/living/carbon/C, datum/species/old_species)
. = ..()
C.AddComponent(/datum/component/mutant_hands, mutant_hand_path = /obj/item/mutant_hand/zombie)
/datum/species/zombie/infectious/on_species_loss(mob/living/carbon/human/C, datum/species/new_species, pref_load)
. = ..()
qdel(C.GetComponent(/datum/component/mutant_hands))
/datum/species/zombie/infectious/check_roundstart_eligible()
return FALSE
@@ -262,9 +262,10 @@
. = list("<span class='info'>This is [icon2html(src, user)] \a <b>[src]</b>!", EXAMINE_SECTION_BREAK) //SKYRAT EDIT CHANGE
//Hands
for(var/obj/item/I in held_items)
if(!(I.item_flags & ABSTRACT))
. += "It has [I.get_examine_string(user)] in its [get_held_index_name(get_held_index_of_item(I))]."
for(var/obj/item/held_thing in held_items)
if(held_thing.item_flags & (ABSTRACT|EXAMINE_SKIP|HAND_ITEM))
continue
. += "It has [held_thing.get_examine_string(user)] in its [get_held_index_name(get_held_index_of_item(held_thing))]."
//Internal storage
if(internal_storage && !(internal_storage.item_flags & ABSTRACT))
@@ -24,8 +24,9 @@
if(dextrous)
. = list("<span class='info'>This is [icon2html(src)] \a <b>[src]</b>!\n[desc]", EXAMINE_SECTION_BREAK) //SKYRAT EDIT CHANGE
for(var/obj/item/held_item in held_items)
if(!(held_item.item_flags & ABSTRACT))
. += "It has [held_item.get_examine_string(user)] in its [get_held_index_name(get_held_index_of_item(held_item))]."
if(held_item.item_flags & (ABSTRACT|EXAMINE_SKIP|HAND_ITEM))
continue
. += "It has [held_item.get_examine_string(user)] in its [get_held_index_name(get_held_index_of_item(held_item))]."
if(internal_storage && !(internal_storage.item_flags & ABSTRACT))
. += "It is holding [internal_storage.get_examine_string(user)] in its internal storage."
. += "</span>"
@@ -149,6 +149,7 @@
return
forceMove(drop_loc)
SEND_SIGNAL(phantom_owner, COMSIG_CARBON_POST_REMOVE_LIMB, src, dismembered)
/**
* get_mangled_state() is relevant for flesh and bone bodyparts, and returns whether this bodypart has mangled skin, mangled bone, or both (or neither i guess)
@@ -321,8 +322,6 @@
if(held_index > new_limb_owner.hand_bodyparts.len)
new_limb_owner.hand_bodyparts.len = held_index
new_limb_owner.hand_bodyparts[held_index] = src
if(new_limb_owner.dna.species.mutanthands && !is_pseudopart)
new_limb_owner.put_in_hand(new new_limb_owner.dna.species.mutanthands(), held_index)
if(new_limb_owner.hud_used)
var/atom/movable/screen/inventory/hand/hand = new_limb_owner.hud_used.hand_slots["[held_index]"]
if(hand)
@@ -368,6 +367,7 @@
new_limb_owner.updatehealth()
new_limb_owner.update_body()
new_limb_owner.update_damage_overlays()
SEND_SIGNAL(new_limb_owner, COMSIG_CARBON_POST_ATTACH_LIMB, src, special)
return TRUE
/obj/item/bodypart/head/try_attach_limb(mob/living/carbon/new_head_owner, special = FALSE, abort = FALSE)
+1
View File
@@ -154,6 +154,7 @@
#include "modular_map_loader.dm"
#include "monkey_business.dm"
#include "mouse_bite_cable.dm"
#include "mutant_hands_consistency.dm"
#include "novaflower_burn.dm"
#include "ntnetwork_tests.dm"
#include "nuke_cinematic.dm"
@@ -0,0 +1,71 @@
/**
* Test: Mutant hands component
*
* Adding mutant hand component gives two mutant hands in each hand slot
* Losing a limb removes the associated hand, and re-gaining the limb re-gives the associated hand
*/
/datum/unit_test/mutant_hands
/datum/unit_test/mutant_hands/Run()
var/mob/living/carbon/human/incredible_hulk = allocate(/mob/living/carbon/human/consistent)
var/obj/item/item_to_hold = allocate(/obj/item/storage/toolbox)
incredible_hulk.put_in_hands(item_to_hold)
incredible_hulk.AddComponent(/datum/component/mutant_hands)
for(var/obj/item/hand as anything in incredible_hulk.held_items)
if(!istype(hand, /obj/item/mutant_hand))
TEST_FAIL("Dummy didn't have a mutant hand on gaining mutant hands comp! Had: [hand || "nothing"].")
var/obj/item/bodypart/left_arm = incredible_hulk.get_bodypart(BODY_ZONE_L_ARM)
left_arm.drop_limb()
TEST_ASSERT(left_arm.try_attach_limb(incredible_hulk), "Mutant hands test failed to re-attach the limb after losing it.")
for(var/obj/item/hand as anything in incredible_hulk.held_items)
if(!istype(hand, /obj/item/mutant_hand))
TEST_FAIL("Dummy didn't have a mutant hand after re-gaining a limb! Had: [hand || "nothing"].")
/**
* Test: Mutant hands component with a nodrop item in place
*
* Adding mutant hand component does not force no-drop items out of hands
* If the no-drop item disappears / is deleted, a new hand should re-appear immediately
*/
/datum/unit_test/mutant_hands_with_nodrop
/datum/unit_test/mutant_hands_with_nodrop/Run()
var/mob/living/carbon/human/incredible_hulk = allocate(/mob/living/carbon/human/consistent)
var/obj/item/item_to_hold = allocate(/obj/item/storage/toolbox)
ADD_TRAIT(item_to_hold, TRAIT_NODROP, TRAIT_SOURCE_UNIT_TESTS)
incredible_hulk.put_in_hand(item_to_hold, 1)
incredible_hulk.AddComponent(/datum/component/mutant_hands)
if(!istype(incredible_hulk.held_items[1], /obj/item/storage/toolbox))
TEST_FAIL("Dummy's left hand was not a toolbox, though it was supposed to be. Was: [incredible_hulk.held_items[1] || "nothing"].")
if(!istype(incredible_hulk.held_items[2], /obj/item/mutant_hand))
TEST_FAIL("Dummy 's right hand was not a mutant hand! Was: [incredible_hulk.held_items[2] || "nothing"].")
QDEL_NULL(item_to_hold)
if(!istype(incredible_hulk.held_items[1], /obj/item/mutant_hand))
TEST_FAIL("Dummy's left hand was not a mutant hand after losing the nodrop item. Was: [incredible_hulk.held_items[1] || "nothing"].")
/**
* Test: Mutant hands fireman carrying
*
* Mutant hands currently do not support fireman carrying despite being theoretically allowed,
* tests that this continues to be the case. Can be updated if this assertion is changed.
*/
/datum/unit_test/mutant_hands_carry
/datum/unit_test/mutant_hands_carry/Run()
var/mob/living/carbon/human/incredible_hulk = allocate(/mob/living/carbon/human/consistent)
var/mob/living/carbon/human/carried = allocate(/mob/living/carbon/human/consistent)
incredible_hulk.AddComponent(/datum/component/mutant_hands)
carried.set_resting(TRUE, instant = TRUE)
// Try a fireman carry. It should fail, we have no open hands
incredible_hulk.buckle_mob(carried, force = TRUE, check_loc = TRUE, buckle_mob_flags = CARRIER_NEEDS_ARM)
TEST_ASSERT(!length(incredible_hulk.buckled_mobs), "Someone with mutant hands was able to fireman carry, despite having no hands to do so.")
+6 -25
View File
@@ -1,36 +1,17 @@
/obj/item/zombie_hand
/obj/item/mutant_hand/zombie
name = "zombie claw"
desc = "A zombie's claw is its primary tool, capable of infecting \
humans, butchering all other living things to \
sustain the zombie, smashing open airlock doors and opening \
child-safe caps on bottles."
item_flags = ABSTRACT | DROPDEL | HAND_ITEM
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF
icon = 'icons/effects/blood.dmi'
icon_state = "bloodhand_left"
var/icon_left = "bloodhand_left"
var/icon_right = "bloodhand_right"
hitsound = 'sound/hallucinations/growl1.ogg'
force = 21 // Just enough to break airlocks with melee attacks
sharpness = SHARP_EDGED
wound_bonus = -30
bare_wound_bonus = 15
damtype = BRUTE
sharpness = SHARP_EDGED
/obj/item/zombie_hand/Initialize(mapload)
. = ..()
ADD_TRAIT(src, TRAIT_NODROP, HAND_REPLACEMENT_TRAIT)
/obj/item/zombie_hand/visual_equipped(mob/user, slot)
. = ..()
//these are intentionally inverted
var/i = user.get_held_index_of_item(src)
if(!(i % 2))
icon_state = icon_left
else
icon_state = icon_right
/obj/item/zombie_hand/afterattack(atom/target, mob/user, proximity_flag)
/obj/item/mutant_hand/zombie/afterattack(atom/target, mob/user, proximity_flag)
. = ..()
if(!proximity_flag)
return
@@ -59,14 +40,14 @@
infection = new()
infection.Insert(target)
/obj/item/zombie_hand/suicide_act(mob/living/user)
/obj/item/mutant_hand/zombie/suicide_act(mob/living/user)
user.visible_message(span_suicide("[user] is ripping [user.p_their()] brains out! It looks like [user.p_theyre()] trying to commit suicide!"))
var/obj/item/bodypart/head = user.get_bodypart(BODY_ZONE_HEAD)
if(head)
head.dismember()
return BRUTELOSS
/obj/item/zombie_hand/proc/check_feast(mob/living/target, mob/living/user)
/obj/item/mutant_hand/zombie/proc/check_feast(mob/living/target, mob/living/user)
if(target.stat == DEAD)
var/hp_gained = target.maxHealth
target.investigate_log("has been devoured by a zombie.", INVESTIGATE_DEATHS)
@@ -64,20 +64,24 @@
/datum/species/mutant/infectious
name = "Mutated Abomination"
id = SPECIES_MUTANT_INFECTIOUS
mutanthands = /obj/item/mutant_hand
speedmod = 1
armor = 10
mutanteyes = /obj/item/organ/internal/eyes/night_vision/zombie
changesource_flags = MIRROR_BADMIN | WABBAJACK | ERT_SPAWN
var/hands_to_give = /obj/item/hnz_mutant_hand
/// The rate the mutants regenerate at
var/heal_rate = 1
/// The cooldown before the mutant can start regenerating
COOLDOWN_DECLARE(regen_cooldown)
/datum/species/mutant/infectious/on_species_gain(mob/living/carbon/C, datum/species/old_species)
. = ..()
C.AddComponent(/datum/component/mutant_hands, mutant_hand_path = hands_to_give)
/datum/species/mutant/infectious/fast
name = "Fast Mutated Abomination"
id = SPECIES_MUTANT_FAST
mutanthands = /obj/item/mutant_hand/fast
hands_to_give = /obj/item/hnz_mutant_hand/fast
armor = 0
/// The rate the mutants regenerate at
heal_rate = 0.5
@@ -131,7 +135,7 @@
else
. = ..()
/obj/item/mutant_hand
/obj/item/hnz_mutant_hand
name = "mutant claw"
desc = "A mutant's claw is its primary tool, capable of infecting \
humans, butchering all other living things to \
@@ -152,17 +156,17 @@
var/icon_left = "bloodhand_left"
var/icon_right = "bloodhand_right"
/obj/item/mutant_hand/fast
/obj/item/hnz_mutant_hand/fast
name = "weak mutant claw"
force = 21
sharpness = NONE
wound_bonus = -40
/obj/item/mutant_hand/Initialize(mapload)
/obj/item/hnz_mutant_hand/Initialize(mapload)
. = ..()
ADD_TRAIT(src, TRAIT_NODROP, HAND_REPLACEMENT_TRAIT)
/obj/item/mutant_hand/equipped(mob/user, slot)
/obj/item/hnz_mutant_hand/equipped(mob/user, slot)
. = ..()
//these are intentionally inverted
var/i = user.get_held_index_of_item(src)
@@ -171,7 +175,7 @@
else
icon_state = icon_right
/obj/item/mutant_hand/afterattack(atom/target, mob/user, proximity_flag)
/obj/item/hnz_mutant_hand/afterattack(atom/target, mob/user, proximity_flag)
. = ..()
if(!proximity_flag)
return
@@ -217,7 +221,7 @@
if(infection)
qdel(infection)
/obj/item/mutant_hand/proc/check_feast(mob/living/target, mob/living/user)
/obj/item/hnz_mutant_hand/proc/check_feast(mob/living/target, mob/living/user)
if(target.stat == DEAD)
var/hp_gained = target.maxHealth
target.investigate_log("has been feasted upon by the mutant [user].", INVESTIGATE_DEATHS)
+1
View File
@@ -987,6 +987,7 @@
#include "code\datums\components\mirv.dm"
#include "code\datums\components\mob_harvest.dm"
#include "code\datums\components\multiple_lives.dm"
#include "code\datums\components\mutant_hands.dm"
#include "code\datums\components\ntnet_interface.dm"
#include "code\datums\components\nuclear_bomb_operator.dm"
#include "code\datums\components\omen.dm"