From f11570dd7930d786d533a632bc4b499948287033 Mon Sep 17 00:00:00 2001 From: Jacquerel Date: Thu, 12 Oct 2023 20:15:23 +0100 Subject: [PATCH] Dullahan Fixes (#78938) ## About The Pull Request Let's see if these can be like 50% playable by Halloween Fixes #70890 (mostly) Also one half of #71020 but does not close it because they still cannot hear audible messages There was a list of issues in #70890 which are largely fixed by this, but some of them were not _bugs_. Anything which is simply an obvious consequence of "not having a head or being able to wear anything on your head" isn't and cannot be fixed, because this species doesn't have a head. That means that all of these things are still true: ``` Dullahans have no radio Can't get spaceproofed because without the helmet you still take pressure damage Can't wear modsuits Can't wear sunglasses or HUDs Can't wear clown mask ``` The issue causing everything _else_ is that when we sever the head, we remove the brain. Removing the brain removes the traits `TRAIT_ADVANCEDTOOLUSER, TRAIT_LITERATE, TRAIT_CAN_STRIP`. Without these three traits, Dullahans are blocked from several important actions. Easy fix: Make these properties of the dullahan species independent of whether they have a brain or not. Clearly they can do these things without brains, on account of not having them. Additionally I gave dullahans their own head subtype which preserves internal organs, because their brains and eyes were decaying and giving them brain trauma and blindness. Finally, I changed a few things from checks in `spec_life` to signal responses, because `spec_life` checks for "did I gain a head recently?" are gross. ## Changelog :cl: fix: Dullahans can read, strip people, and utilise tools. fix: Dullahan brains and eyes will not decay while inside their living severed head. /:cl: --------- Co-authored-by: san7890 --- .../signals/signals_mob/signals_mob_carbon.dm | 2 + code/modules/client/preferences.dm | 1 + .../carbon/human/species_types/dullahan.dm | 107 +++++++++++------- .../bodyparts/species_parts/misc_bodyparts.dm | 19 ++++ 4 files changed, 86 insertions(+), 43 deletions(-) diff --git a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_carbon.dm b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_carbon.dm index ad0e6e359b1..1d5ab544d10 100644 --- a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_carbon.dm +++ b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_carbon.dm @@ -122,6 +122,8 @@ // /mob/living/carbon/human signals +///Applied preferences to a human +#define COMSIG_HUMAN_PREFS_APPLIED "human_prefs_applied" ///Hit by successful disarm attack (mob/living/carbon/human/attacker,zone_targeted) #define COMSIG_HUMAN_DISARM_HIT "human_disarm_hit" ///Whenever EquipRanked is called, called after job is set diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm index bc643cc9d45..dab23c78e17 100644 --- a/code/modules/client/preferences.dm +++ b/code/modules/client/preferences.dm @@ -509,6 +509,7 @@ GLOBAL_LIST_EMPTY(preferences_datums) character.icon_render_keys = list() character.update_body(is_creating = TRUE) + SEND_SIGNAL(character, COMSIG_HUMAN_PREFS_APPLIED) /// Returns whether the parent mob should have the random hardcore settings enabled. Assumes it has a mind. /datum/preferences/proc/should_be_random_hardcore(datum/job/job, datum/mind/mind) diff --git a/code/modules/mob/living/carbon/human/species_types/dullahan.dm b/code/modules/mob/living/carbon/human/species_types/dullahan.dm index f609d76952e..1599b0a23c0 100644 --- a/code/modules/mob/living/carbon/human/species_types/dullahan.dm +++ b/code/modules/mob/living/carbon/human/species_types/dullahan.dm @@ -6,6 +6,17 @@ TRAIT_NOBREATH, TRAIT_NOHUNGER, TRAIT_USES_SKINTONES, + TRAIT_ADVANCEDTOOLUSER, // Normally applied by brain but we don't have one + TRAIT_LITERATE, + TRAIT_CAN_STRIP, + ) + bodypart_overrides = list( + BODY_ZONE_L_ARM = /obj/item/bodypart/arm/left, + BODY_ZONE_R_ARM = /obj/item/bodypart/arm/right, + BODY_ZONE_HEAD = /obj/item/bodypart/head/dullahan, + BODY_ZONE_L_LEG = /obj/item/bodypart/leg/left, + BODY_ZONE_R_LEG = /obj/item/bodypart/leg/right, + BODY_ZONE_CHEST = /obj/item/bodypart/chest, ) inherent_biotypes = MOB_UNDEAD|MOB_HUMANOID mutant_bodyparts = list("wings" = "None") @@ -20,11 +31,9 @@ /// The dullahan relay that's associated with the owner, used to handle many things such as talking and hearing. var/obj/item/dullahan_relay/my_head - /// Did our owner's first client connection get handled yet? Useful for when some proc needs to be called once we're sure that a client has moved into our owner, like for Dullahans. var/owner_first_client_connection_handled = FALSE - /datum/species/dullahan/check_roundstart_eligible() if(check_holidays(HALLOWEEN)) return TRUE @@ -33,62 +42,59 @@ /datum/species/dullahan/on_species_gain(mob/living/carbon/human/human, datum/species/old_species) . = ..() human.lose_hearing_sensitivity(TRAIT_GENERIC) + RegisterSignal(human, COMSIG_CARBON_ATTACH_LIMB, PROC_REF(on_gained_part)) + var/obj/item/bodypart/head/head = human.get_bodypart(BODY_ZONE_HEAD) + head?.drop_limb() + if(QDELETED(head)) //drop_limb() deletes the limb if no drop location exists and character setup dummies are located in nullspace. + return + my_head = new /obj/item/dullahan_relay(head, human) + human.put_in_hands(head) - if(head) - head.drop_limb() - - if(!QDELETED(head)) //drop_limb() deletes the limb if no drop location exists and character setup dummies are located in nullspace. - head.throwforce = 25 - my_head = new /obj/item/dullahan_relay(head, human) - human.put_in_hands(head) - head.show_organs_on_examine = FALSE - head.speech_span = null // so we don't look roboty when talking through it - - // We want to give the head some boring old eyes just so it doesn't look too jank on the head sprite. - head.eyes = new /obj/item/organ/internal/eyes(head) - head.eyes.eye_color_left = human.eye_color_left - head.eyes.eye_color_right = human.eye_color_right - human.update_body() - head.update_icon_dropped() - + // We want to give the head some boring old eyes just so it doesn't look too jank on the head sprite. + head.eyes = new /obj/item/organ/internal/eyes(head) + head.eyes.eye_color_left = human.eye_color_left + head.eyes.eye_color_right = human.eye_color_right + human.update_body() + head.update_icon_dropped() human.set_safe_hunger_level() + RegisterSignal(head, COMSIG_QDELETING, PROC_REF(on_head_destroyed)) + +/// If we gained a new body part, it had better not be a head +/datum/species/dullahan/proc/on_gained_part(mob/living/carbon/human/dullahan, obj/item/bodypart/part) + SIGNAL_HANDLER + if (part.body_zone != BODY_ZONE_HEAD) + return + my_head = null + dullahan.investigate_log("has been gibbed by having an illegal head put on [dullahan.p_their()] shoulders.", INVESTIGATE_DEATHS) + dullahan.gib(DROP_ALL_REMAINS) // Yeah so giving them a head on their body is really not a good idea, so their original head will remain but uh, good luck fixing it after that. + +/// If our head is destroyed, so are we +/datum/species/dullahan/proc/on_head_destroyed() + SIGNAL_HANDLER + var/mob/living/human = my_head?.owner + if (QDELETED(human)) + return // guess we already died + my_head = null + human.investigate_log("has been gibbed by the loss of [human.p_their()] head.", INVESTIGATE_DEATHS) + human.gib(DROP_ALL_REMAINS) /datum/species/dullahan/on_species_loss(mob/living/carbon/human/human) . = ..() - if(my_head) var/obj/item/bodypart/head/detached_head = my_head.loc + UnregisterSignal(detached_head, COMSIG_QDELETING) my_head.owner = null QDEL_NULL(my_head) if(detached_head) qdel(detached_head) + UnregisterSignal(human, COMSIG_CARBON_ATTACH_LIMB) human.regenerate_limb(BODY_ZONE_HEAD, FALSE) human.become_hearing_sensitive() prevent_perspective_change = FALSE human.reset_perspective(human) -/datum/species/dullahan/spec_life(mob/living/carbon/human/human, seconds_per_tick, times_fired) - . = ..() - if(QDELETED(my_head)) - my_head = null - human.investigate_log("has been gibbed by the loss of [human.p_their()] head.", INVESTIGATE_DEATHS) - human.gib(DROP_ALL_REMAINS) - return - - if(my_head.loc.name != human.real_name && istype(my_head.loc, /obj/item/bodypart/head)) - var/obj/item/bodypart/head/detached_head = my_head.loc - detached_head.real_name = human.real_name - detached_head.name = human.real_name - detached_head.brain.name = "[human.name]'s brain" - - var/obj/item/bodypart/head/illegal_head = human.get_bodypart(BODY_ZONE_HEAD) - if(illegal_head) - my_head = null - human.investigate_log("has been gibbed by having an illegal head put on [human.p_their()] shoulders.", INVESTIGATE_DEATHS) - human.gib(DROP_ALL_REMAINS) // Yeah so giving them a head on their body is really not a good idea, so their original head will remain but uh, good luck fixing it after that. - /datum/species/dullahan/proc/update_vision_perspective(mob/living/carbon/human/human) var/obj/item/organ/internal/eyes/eyes = human.get_organ_slot(ORGAN_SLOT_EYES) if(eyes) @@ -212,11 +218,15 @@ . = ..() if(!new_owner) return INITIALIZE_HINT_QDEL + var/obj/item/bodypart/head/detached_head = loc + if (!istype(detached_head)) + return INITIALIZE_HINT_QDEL owner = new_owner START_PROCESSING(SSobj, src) RegisterSignal(owner, COMSIG_CLICK_SHIFT, PROC_REF(examinate_check)) RegisterSignal(owner, COMSIG_CARBON_REGENERATE_LIMBS, PROC_REF(unlist_head)) RegisterSignal(owner, COMSIG_LIVING_REVIVE, PROC_REF(retrieve_head)) + RegisterSignal(owner, COMSIG_HUMAN_PREFS_APPLIED, PROC_REF(update_prefs_name)) become_hearing_sensitive(ROUNDSTART_TRAIT) /obj/item/dullahan_relay/Destroy() @@ -225,9 +235,20 @@ return ..() /obj/item/dullahan_relay/process() - if(!istype(loc, /obj/item/bodypart/head) || QDELETED(owner)) - . = PROCESS_KILL - qdel(src) + if(istype(loc, /obj/item/bodypart/head) && !QDELETED(owner)) + return + qdel(src) + return PROCESS_KILL + +/// Updates our names after applying name prefs +/obj/item/dullahan_relay/proc/update_prefs_name(mob/living/carbon/human/wearer) + SIGNAL_HANDLER + var/obj/item/bodypart/head/detached_head = loc + if (!istype(detached_head)) + return // It's so over + detached_head.real_name = wearer.real_name + detached_head.name = wearer.real_name + detached_head.brain.name = "[wearer.name]'s brain" /obj/item/dullahan_relay/proc/examinate_check(mob/user, atom/source) SIGNAL_HANDLER diff --git a/code/modules/surgery/bodyparts/species_parts/misc_bodyparts.dm b/code/modules/surgery/bodyparts/species_parts/misc_bodyparts.dm index 408afea6679..b8b7d427b67 100644 --- a/code/modules/surgery/bodyparts/species_parts/misc_bodyparts.dm +++ b/code/modules/surgery/bodyparts/species_parts/misc_bodyparts.dm @@ -387,6 +387,25 @@ burn_modifier = 1.25 speed_modifier = 0.75 //big fungus big fungus +/// Dullahan head preserves organs inside it +/obj/item/bodypart/head/dullahan + throwforce = 25 // It's also a potent weapon + show_organs_on_examine = FALSE + speech_span = null + +/obj/item/bodypart/head/dullahan/Entered(obj/item/organ/arrived, atom/old_loc, list/atom/old_locs) + . = ..() + if (!isorgan(arrived)) + return + arrived.organ_flags |= ORGAN_FROZEN + +/obj/item/bodypart/head/dullahan/Exited(obj/item/organ/gone, direction) + . = ..() + if (!isorgan(gone)) + return + gone.organ_flags &= ~ORGAN_FROZEN + + //GOLEM /obj/item/bodypart/head/golem icon = 'icons/mob/human/species/golems.dmi'