mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-07-12 08:36:00 +01:00
[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:
@@ -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"
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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.")
|
||||
@@ -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)
|
||||
|
||||
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user