From 8e3d62afe883b47cda944aa3b347fc4a57e7efe4 Mon Sep 17 00:00:00 2001 From: RKz Date: Wed, 25 Oct 2023 05:36:21 -0400 Subject: [PATCH 1/7] https://github.com/tgstation/tgstation/pull/48633 --- code/__DEFINES/dcs/signals/signals_atom.dm | 4 +- .../dcs/signals/signals_mob/signals_living.dm | 2 + code/__DEFINES/is_helpers.dm | 1 + code/_onclick/click.dm | 4 +- code/game/machinery/recycler.dm | 2 +- .../carbon/human/species_types/dullahan.dm | 58 ++++++++++++++----- .../surgery/bodyparts/dismemberment.dm | 9 +-- 7 files changed, 59 insertions(+), 21 deletions(-) diff --git a/code/__DEFINES/dcs/signals/signals_atom.dm b/code/__DEFINES/dcs/signals/signals_atom.dm index 7e4dfd2a96b46..6cf2a0234d358 100644 --- a/code/__DEFINES/dcs/signals/signals_atom.dm +++ b/code/__DEFINES/dcs/signals/signals_atom.dm @@ -124,6 +124,7 @@ ///! called when teleporting into a protected turf: (channel, turf/origin) #define COMSIG_ATOM_INTERCEPT_TELEPORT "intercept_teleport" #define COMPONENT_BLOCK_TELEPORT 1 +#define COMSIG_ATOM_HEARER_IN_VIEW "atom_hearer_in_view" //called when an atom is added to the hearers on get_hearers_in_view(): (list/processing_list, list/hearers) ///called when an atom starts orbiting another atom: (atom) #define COMSIG_ATOM_ORBIT_BEGIN "atom_orbit_begin" /// called when an atom stops orbiting another atom: (atom) @@ -164,7 +165,8 @@ ///////////////// #define COMSIG_CLICK "atom_click" //! from base of atom/Click(): (location, control, params, mob/user) -#define COMSIG_CLICK_SHIFT "shift_click" //! from base of atom/ShiftClick(): (/mob) +#define COMSIG_CLICK_SHIFT "shift_click" //from base of atom/ShiftClick(): (/mob) + #define COMPONENT_ALLOW_EXAMINATE 1 //Allows the user to examinate regardless of client.eye. #define COMSIG_CLICK_CTRL "ctrl_click" //! from base of atom/CtrlClickOn(): (/mob) #define COMSIG_CLICK_ALT "alt_click" //! from base of atom/AltClick(): (/mob) #define COMPONENT_INTERCEPT_ALT 1 diff --git a/code/__DEFINES/dcs/signals/signals_mob/signals_living.dm b/code/__DEFINES/dcs/signals/signals_mob/signals_living.dm index 0651d6667790c..7ee7d0420290b 100644 --- a/code/__DEFINES/dcs/signals/signals_mob/signals_living.dm +++ b/code/__DEFINES/dcs/signals/signals_mob/signals_living.dm @@ -13,6 +13,8 @@ #define COMSIG_LIVING_ELECTROCUTE_ACT "living_electrocute_act" ///from base of mob/living/revive() (full_heal, admin_revive) #define COMSIG_LIVING_REVIVE "living_revive" +//from base of /mob/living/regenerate_limbs(): (noheal, excluded_limbs) +#define COMSIG_LIVING_REGENERATE_LIMBS "living_regen_limbs" ///from base of mob/living/set_buckled(): (new_buckled) #define COMSIG_LIVING_SET_BUCKLED "living_set_buckled" #define COMSIG_LIVING_MINOR_SHOCK "living_minor_shock" //! sent by stuff like stunbatons and tasers: () diff --git a/code/__DEFINES/is_helpers.dm b/code/__DEFINES/is_helpers.dm index fbb6dc398b428..9131626a3dd4a 100644 --- a/code/__DEFINES/is_helpers.dm +++ b/code/__DEFINES/is_helpers.dm @@ -87,6 +87,7 @@ GLOBAL_LIST_INIT(turfs_without_ground, typecacheof(list( #define iscatperson(A) (is_species(A, /datum/species/human/felinid) ) #define isethereal(A) (is_species(A, /datum/species/ethereal)) #define isvampire(A) (is_species(A,/datum/species/vampire)) +#define isdullahan(A) (is_species(A, /datum/species/dullahan)) #define isipc(A) (is_species(A, /datum/species/ipc)) #define isapid(A) (is_species(A, /datum/species/apid)) #define isandroid(A) (is_species(A, /datum/species/android)) diff --git a/code/_onclick/click.dm b/code/_onclick/click.dm index e8aac70c8206d..1d1268319ece4 100644 --- a/code/_onclick/click.dm +++ b/code/_onclick/click.dm @@ -302,8 +302,8 @@ A.ShiftClick(src) return /atom/proc/ShiftClick(mob/user) - SEND_SIGNAL(src, COMSIG_CLICK_SHIFT, user) - if(user.client && user.client.eye == user || user.client.eye == user.loc) + var/flags = SEND_SIGNAL(src, COMSIG_CLICK_SHIFT, user) + if(user.client && (user.client.eye == user || user.client.eye == user.loc || flags & COMPONENT_ALLOW_EXAMINATE)) user.examinate(src) return diff --git a/code/game/machinery/recycler.dm b/code/game/machinery/recycler.dm index 618bdb29fa05d..b120d481b5c83 100644 --- a/code/game/machinery/recycler.dm +++ b/code/game/machinery/recycler.dm @@ -107,7 +107,7 @@ var/atom/movable/AM = i var/obj/item/bodypart/head/as_head = AM var/obj/item/mmi/as_mmi = AM - var/brain_holder = istype(AM, /obj/item/organ/brain) || (istype(as_head) && as_head.brain) || (istype(as_mmi) && as_mmi.brain) || istype(AM, /mob/living/brain) + var/brain_holder = istype(AM, /obj/item/organ/brain) || (istype(as_head) && as_head.brain) || (istype(as_mmi) && as_mmi.brain) || isbrain(AM) || istype(AM, /obj/item/dullahan_relay)) if(brain_holder) emergency_stop(AM) else if(isliving(AM)) 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 9539e0a6cb596..0a83b691f5e5d 100644 --- a/code/modules/mob/living/carbon/human/species_types/dullahan.dm +++ b/code/modules/mob/living/carbon/human/species_types/dullahan.dm @@ -29,9 +29,13 @@ var/obj/item/bodypart/head/head = H.get_bodypart(BODY_ZONE_HEAD) if(head) head.drop_limb() - head.throwforce = 25 - myhead = new /obj/item/dullahan_relay (head, H) - H.put_in_hands(head) + if(!QDELETED(head)) //drop_limb() deletes the limb if no drop location exists and character setup dummies are located in nullspace. + head.throwforce = 25 + myhead = new /obj/item/dullahan_relay (head, H) + H.put_in_hands(head) + var/obj/item/organ/eyes/E = H.getorganslot(ORGAN_SLOT_EYES) + var/datum/action/item_action/organ_action/dullahan/D = locate() in E?.actions + D?.Trigger() /datum/species/dullahan/on_species_loss(mob/living/carbon/human/H) H.become_hearing_sensitive() @@ -117,7 +121,7 @@ /obj/item/organ/tongue/dullahan/handle_speech(datum/source, list/speech_args) if(ishuman(owner)) var/mob/living/carbon/human/H = owner - if(H.dna.species.id == SPECIES_DULLAHAN) + if(isdullahan(H)) var/datum/species/dullahan/D = H.dna.species if(isobj(D.myhead.loc)) var/obj/O = D.myhead.loc @@ -132,6 +136,7 @@ desc = "An abstraction." actions_types = list(/datum/action/item_action/organ_action/dullahan) zone = "abstract" + tint = INFINITY // to switch the vision perspective to the head on species_gain() without issue. /datum/action/item_action/organ_action/dullahan name = "Toggle Perspective" @@ -147,17 +152,23 @@ if(ishuman(owner)) var/mob/living/carbon/human/H = owner - if(H.dna.species.id == SPECIES_DULLAHAN) + if(isdullahan(H)) var/datum/species/dullahan/D = H.dna.species D.update_vision_perspective(H) /obj/item/dullahan_relay + name = "dullahan relay" var/mob/living/owner -/obj/item/dullahan_relay/Initialize(mapload,new_owner) +/obj/item/dullahan_relay/Initialize(mapload, mob/living/carbon/human/new_owner) . = ..() + if(!new_owner) + return INITIALIZE_HINT_QDEL owner = new_owner START_PROCESSING(SSobj, src) + RegisterSignal(owner, COMSIG_CLICK_SHIFT, .proc/examinate_check) + RegisterSignal(owner, COMSIG_LIVING_REGENERATE_LIMBS, .proc/unlist_head) + RegisterSignal(owner, COMSIG_LIVING_REVIVE, .proc/retrieve_head) become_hearing_sensitive(ROUNDSTART_TRAIT) /obj/item/dullahan_relay/process() @@ -165,19 +176,40 @@ . = PROCESS_KILL qdel(src) -/obj/item/dullahan_relay/Hear(message, atom/movable/speaker, message_language, raw_message, radio_freq, list/spans, list/message_mods = list()) - . = ..() - if(!QDELETED(owner)) - message = compose_message(speaker, message_language, raw_message, radio_freq, spans, message_mods) - to_chat(owner,message) - else +/obj/item/dullahan_relay/process() + if(!istype(loc, /obj/item/bodypart/head) || QDELETED(owner)) + . = PROCESS_KILL qdel(src) +/obj/item/dullahan_relay/proc/examinate_check(atom/source, mob/user) + SIGNAL_HANDLER + if(user.client.eye == src) + return COMPONENT_ALLOW_EXAMINATE + +///Adds the owner to the list of hearers in hearers_in_view(), for visible/hearable on top of say messages +/obj/item/dullahan_relay/proc/include_owner(datum/source, list/hearers) + SIGNAL_HANDLER + if(!QDELETED(owner)) + hearers += owner + +///Stops dullahans from gibbing when regenerating limbs +/obj/item/dullahan_relay/proc/unlist_head(datum/source, noheal = FALSE, list/excluded_zones) + SIGNAL_HANDLER + excluded_zones |= BODY_ZONE_HEAD + +///Retrieving the owner's head for better ahealing. +/obj/item/dullahan_relay/proc/retrieve_head(datum/source, full_heal, admin_revive) + SIGNAL_HANDLER + if(admin_revive) + var/obj/item/bodypart/head/H = loc + var/turf/T = get_turf(owner) + if(H && istype(H) && T && !(H in owner.get_all_contents())) + H.forceMove(T) /obj/item/dullahan_relay/Destroy() if(!QDELETED(owner)) var/mob/living/carbon/human/H = owner - if(H.dna.species.id == SPECIES_DULLAHAN) + if(isdullahan(H)) var/datum/species/dullahan/D = H.dna.species D.myhead = null owner.investigate_log("has been gibbed by the destruction of their head/body relay.", INVESTIGATE_DEATHS) diff --git a/code/modules/surgery/bodyparts/dismemberment.dm b/code/modules/surgery/bodyparts/dismemberment.dm index 86deb37be5a1c..2b05af349384c 100644 --- a/code/modules/surgery/bodyparts/dismemberment.dm +++ b/code/modules/surgery/bodyparts/dismemberment.dm @@ -372,12 +372,13 @@ C.dna.species.bodytype = all_limb_flags //Regenerates all limbs. Returns amount of limbs regenerated -/mob/living/proc/regenerate_limbs(noheal, excluded_limbs) - return 0 +/mob/living/proc/regenerate_limbs(noheal = FALSE, list/excluded_limbs = list()) + SEND_SIGNAL(src, COMSIG_LIVING_REGENERATE_LIMBS, noheal, excluded_limbs) -/mob/living/carbon/regenerate_limbs(noheal, list/excluded_limbs) +/mob/living/carbon/regenerate_limbs(noheal = FALSE, list/excluded_limbs = list()) + . = ..() var/list/limb_list = list(BODY_ZONE_HEAD, BODY_ZONE_CHEST, BODY_ZONE_R_ARM, BODY_ZONE_L_ARM, BODY_ZONE_R_LEG, BODY_ZONE_L_LEG) - if(excluded_limbs) + if(length(excluded_limbs)) limb_list -= excluded_limbs for(var/Z in limb_list) . += regenerate_limb(Z, noheal) From 3700d643e56169cff1a2e3afbfd9bf8fd801121b Mon Sep 17 00:00:00 2001 From: RKz Date: Wed, 25 Oct 2023 05:37:35 -0400 Subject: [PATCH 2/7] makes em undead --- code/modules/mob/living/carbon/human/species_types/dullahan.dm | 1 + 1 file changed, 1 insertion(+) 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 0a83b691f5e5d..294073e140fdb 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,7 @@ species_traits = list(EYECOLOR,HAIR,FACEHAIR,LIPS) inherent_traits = list(TRAIT_NOHUNGER,TRAIT_NOBREATH, TRAIT_NONECRODISEASE) default_features = list("mcolor" = "FFF", "tail_human" = "None", "ears" = "None", "wings" = "None", "body_size" = "Normal") + inherent_biotypes = MOB_UNDEAD|MOB_HUMANOID use_skintones = TRUE mutant_brain = /obj/item/organ/brain/dullahan mutanteyes = /obj/item/organ/eyes/dullahan From abe48fae0e75aa09481215b6010388489be99b00 Mon Sep 17 00:00:00 2001 From: RKz Date: Wed, 25 Oct 2023 07:01:49 -0400 Subject: [PATCH 3/7] https://github.com/tgstation/tgstation/pull/63696 --- .../dcs/signals/signals_mob/signals_mob.dm | 2 + code/_onclick/click.dm | 2 +- code/game/machinery/recycler.dm | 2 +- code/game/say.dm | 4 +- code/modules/mob/living/brain/brain_item.dm | 5 +- code/modules/mob/living/carbon/human/human.dm | 6 + .../mob/living/carbon/human/species.dm | 16 ++ .../carbon/human/species_types/dullahan.dm | 173 +++++++++++------- code/modules/mob/living/living.dm | 14 +- code/modules/mob/living/login.dm | 4 + code/modules/mob/living/say.dm | 16 +- code/modules/surgery/bodyparts/head.dm | 7 +- 12 files changed, 168 insertions(+), 83 deletions(-) diff --git a/code/__DEFINES/dcs/signals/signals_mob/signals_mob.dm b/code/__DEFINES/dcs/signals/signals_mob/signals_mob.dm index a98a439dabcd9..9752dd76eacd4 100644 --- a/code/__DEFINES/dcs/signals/signals_mob/signals_mob.dm +++ b/code/__DEFINES/dcs/signals/signals_mob/signals_mob.dm @@ -52,6 +52,8 @@ #define SPEECH_LANGUAGE 5 #define SPEECH_IGNORE_SPAM 6 #define SPEECH_FORCED 7 */ + #define SPEECH_RANGE 8 + #define COMSIG_MOB_EMOTE "mob_emote" // from /mob/living/emote(): () #define COMSIG_MOB_SWAP_HANDS "mob_swap_hands" //from base of mob/swap_hand() #define COMPONENT_BLOCK_SWAP 1 diff --git a/code/_onclick/click.dm b/code/_onclick/click.dm index 1d1268319ece4..f43c4e04dec14 100644 --- a/code/_onclick/click.dm +++ b/code/_onclick/click.dm @@ -302,7 +302,7 @@ A.ShiftClick(src) return /atom/proc/ShiftClick(mob/user) - var/flags = SEND_SIGNAL(src, COMSIG_CLICK_SHIFT, user) + var/flags = SEND_SIGNAL(user, COMSIG_CLICK_SHIFT, src) if(user.client && (user.client.eye == user || user.client.eye == user.loc || flags & COMPONENT_ALLOW_EXAMINATE)) user.examinate(src) return diff --git a/code/game/machinery/recycler.dm b/code/game/machinery/recycler.dm index b120d481b5c83..e9ab0eadf8913 100644 --- a/code/game/machinery/recycler.dm +++ b/code/game/machinery/recycler.dm @@ -107,7 +107,7 @@ var/atom/movable/AM = i var/obj/item/bodypart/head/as_head = AM var/obj/item/mmi/as_mmi = AM - var/brain_holder = istype(AM, /obj/item/organ/brain) || (istype(as_head) && as_head.brain) || (istype(as_mmi) && as_mmi.brain) || isbrain(AM) || istype(AM, /obj/item/dullahan_relay)) + var/brain_holder = istype(AM, /obj/item/organ/brain) || (istype(as_head) && as_head.brain) || (istype(as_mmi) && as_mmi.brain) || isbrain(AM) || istype(AM, /obj/item/dullahan_relay) if(brain_holder) emergency_stop(AM) else if(isliving(AM)) diff --git a/code/game/say.dm b/code/game/say.dm index 4b44b53e89127..71e137b1e6e6a 100644 --- a/code/game/say.dm +++ b/code/game/say.dm @@ -19,7 +19,7 @@ GLOBAL_LIST_INIT(freqtospan, list( "[FREQ_CTF_BLUE]" = "blueteamradio" )) -/atom/movable/proc/say(message, bubble_type, var/list/spans = list(), sanitize = TRUE, datum/language/language = null, ignore_spam = FALSE, forced = null, atom/source=src) +/atom/movable/proc/say(message, bubble_type, var/list/spans = list(), sanitize = TRUE, datum/language/language = null, ignore_spam = FALSE, forced = null, range = 7, atom/source=src) if(!can_speak()) return if(message == "" || !message) @@ -27,7 +27,7 @@ GLOBAL_LIST_INIT(freqtospan, list( spans |= speech_span if(!language) language = get_selected_language() - send_speech(message, 7, source, , spans, message_language=language) + send_speech(message, range, source, , spans, message_language=language) /atom/movable/proc/Hear(message, atom/movable/speaker, message_language, raw_message, radio_freq, list/spans, list/message_mods = list()) SEND_SIGNAL(src, COMSIG_MOVABLE_HEAR, args) diff --git a/code/modules/mob/living/brain/brain_item.dm b/code/modules/mob/living/brain/brain_item.dm index d251ef72d5936..ed2a6e84634cc 100644 --- a/code/modules/mob/living/brain/brain_item.dm +++ b/code/modules/mob/living/brain/brain_item.dm @@ -20,8 +20,9 @@ var/suicided = FALSE var/mob/living/brain/brainmob = null var/brain_death = FALSE //if the brainmob was intentionally killed by attacking the brain after removal, or by severe braindamage - var/decoy_override = FALSE //if it's a fake brain with no brainmob assigned. Feedback messages will be faked as if it does have a brainmob. See changelings & dullahans. - //two variables necessary for calculating whether we get a brain trauma or not + /// If it's a fake brain with no brainmob assigned. Feedback messages will be faked as if it does have a brainmob. See changelings & dullahans. + var/decoy_override = FALSE + /// Two variables necessary for calculating whether we get a brain trauma or not var/damage_delta = 0 var/list/datum/brain_trauma/traumas = list() diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm index 2869e2cb35b20..cd4ceaaa3b472 100644 --- a/code/modules/mob/living/carbon/human/human.dm +++ b/code/modules/mob/living/carbon/human/human.dm @@ -136,6 +136,12 @@ INVOKE_ASYNC(C, TYPE_PROC_REF(/obj/vehicle/sealed/car, RunOver), src) spreadFire(AM) +/mob/living/carbon/human/reset_perspective(atom/new_eye, force_reset = FALSE) + if(dna?.species?.prevent_perspective_change && !force_reset) // This is in case a species needs to prevent perspective changes in certain cases, like Dullahans preventing perspective changes when they're looking through their head. + update_fullscreen() + return + return ..() + /mob/living/carbon/human/Topic(href, href_list) if(href_list["embedded_object"] && usr.canUseTopic(src, BE_CLOSE, NO_DEXTERITY)) var/obj/item/bodypart/L = locate(href_list["embedded_limb"]) in bodyparts diff --git a/code/modules/mob/living/carbon/human/species.dm b/code/modules/mob/living/carbon/human/species.dm index 4a327a85284cb..b495e7cd4945b 100644 --- a/code/modules/mob/living/carbon/human/species.dm +++ b/code/modules/mob/living/carbon/human/species.dm @@ -111,6 +111,9 @@ GLOBAL_LIST_EMPTY(features_by_species) /// if false, having no tongue makes you unable to speak var/speak_no_tongue = TRUE + /// Do we try to prevent reset_perspective() from working? Useful for Dullahans to stop perspective changes when they're looking through their head. + var/prevent_perspective_change = FALSE + /////////// // PROCS // /////////// @@ -2309,6 +2312,19 @@ GLOBAL_LIST_EMPTY(features_by_species) /datum/species/proc/prepare_human_for_preview(mob/living/carbon/human/human) return +/** + * Owner login + */ + +/** + * A simple proc to be overwritten if something needs to be done when a mob logs in. Does nothing by default. + * + * Arguments: + * * owner - The owner of our species. + */ +/datum/species/proc/on_owner_login(mob/living/carbon/human/owner) + return + /** * Gets a short description for the specices. Should be relatively succinct. * Used in the preference menu. 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 294073e140fdb..12e63ad2d3c7b 100644 --- a/code/modules/mob/living/carbon/human/species_types/dullahan.dm +++ b/code/modules/mob/living/carbon/human/species_types/dullahan.dm @@ -4,9 +4,13 @@ max_bodypart_count = 5 //No head default_color = "FFFFFF" species_traits = list(EYECOLOR,HAIR,FACEHAIR,LIPS) - inherent_traits = list(TRAIT_NOHUNGER,TRAIT_NOBREATH, TRAIT_NONECRODISEASE) - default_features = list("mcolor" = "FFF", "tail_human" = "None", "ears" = "None", "wings" = "None", "body_size" = "Normal") - inherent_biotypes = MOB_UNDEAD|MOB_HUMANOID + inherent_traits = list( + TRAIT_NOHUNGER, + TRAIT_NOBREATH, + TRAIT_NONECRODISEASE, + ) + inherent_biotypes = list(MOB_UNDEAD, MOB_HUMANOID) + default_features = list("mcolor" = "FFF", "wings" = "None", "body_size" = "Normal") use_skintones = TRUE mutant_brain = /obj/item/organ/brain/dullahan mutanteyes = /obj/item/organ/eyes/dullahan @@ -16,7 +20,11 @@ skinned_type = /obj/item/stack/sheet/animalhide/human changesource_flags = MIRROR_BADMIN | WABBAJACK | MIRROR_PRIDE | MIRROR_MAGIC | ERT_SPAWN - var/obj/item/dullahan_relay/myhead + /// 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() @@ -24,50 +32,81 @@ return TRUE return ..() -/datum/species/dullahan/on_species_gain(mob/living/carbon/human/H, datum/species/old_species) +/datum/species/dullahan/on_species_gain(mob/living/carbon/human/human, datum/species/old_species) . = ..() - REMOVE_TRAIT(src, TRAIT_HEARING_SENSITIVE, TRAIT_GENERIC) - var/obj/item/bodypart/head/head = H.get_bodypart(BODY_ZONE_HEAD) + REMOVE_TRAIT(human, TRAIT_HEARING_SENSITIVE, TRAIT_GENERIC) + var/obj/item/bodypart/head/head = human.get_bodypart(BODY_ZONE_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 - myhead = new /obj/item/dullahan_relay (head, H) - H.put_in_hands(head) - var/obj/item/organ/eyes/E = H.getorganslot(ORGAN_SLOT_EYES) - var/datum/action/item_action/organ_action/dullahan/D = locate() in E?.actions - D?.Trigger() - -/datum/species/dullahan/on_species_loss(mob/living/carbon/human/H) - H.become_hearing_sensitive() - H.reset_perspective(H) - if(myhead) - var/obj/item/dullahan_relay/DR = myhead - myhead = null - DR.owner = null - qdel(DR) - H.regenerate_limb(BODY_ZONE_HEAD,FALSE) - ..() - -/datum/species/dullahan/spec_life(mob/living/carbon/human/human) - if(QDELETED(myhead)) - myhead = null - human.investigate_log("has been gibbed by the loss of [human.p_their()] head.", INVESTIGATE_DEATHS) - human.gib() - var/obj/item/bodypart/head/head2 = human.get_bodypart(BODY_ZONE_HEAD) - if(head2) - myhead = null + my_head = new /obj/item/dullahan_relay(head, human) + human.put_in_hands(head) + head.show_organs_on_examine = FALSE + + // 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/eyes(head) + head.eyes.eye_color = human.eye_color + head.update_icon_dropped() + +/datum/species/dullahan/on_species_loss(mob/living/carbon/human/human) + . = ..() + + if(my_head) + var/obj/item/bodypart/head/detached_head = my_head.loc + my_head.owner = null + QDEL_NULL(my_head) + if(detached_head) + qdel(detached_head) + + 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, delta_time, times_fired) + if(QDELETED(my_head)) + my_head = null human.gib() + return -/datum/species/dullahan/proc/update_vision_perspective(mob/living/carbon/human/H) - var/obj/item/organ/eyes/eyes = H.getorganslot(ORGAN_SLOT_EYES) + 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.gib() // 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/eyes/eyes = human.getorganslot(ORGAN_SLOT_EYES) if(eyes) - H.update_tint() + human.update_tint() if(eyes.tint) - H.reset_perspective(H) + prevent_perspective_change = FALSE + human.reset_perspective(human, TRUE) else - H.reset_perspective(myhead) + human.reset_perspective(my_head, TRUE) + prevent_perspective_change = TRUE + +/datum/species/dullahan/on_owner_login(mob/living/carbon/human/owner) + var/obj/item/organ/eyes/eyes = owner.getorganslot(ORGAN_SLOT_EYES) + if(owner_first_client_connection_handled) + if(!eyes.tint) + owner.reset_perspective(my_head, TRUE) + prevent_perspective_change = TRUE + return + // As it's the first time there's a client in our mob, we can finally update its vision to place it in the head instead! + var/datum/action/item_action/organ_action/dullahan/eyes_toggle_perspective_action = locate() in eyes?.actions + + eyes_toggle_perspective_action?.Trigger() + owner_first_client_connection_handled = TRUE /datum/species/dullahan/get_species_description() return "An angry spirit, hanging onto the land of the living for \ @@ -121,12 +160,12 @@ /obj/item/organ/tongue/dullahan/handle_speech(datum/source, list/speech_args) if(ishuman(owner)) - var/mob/living/carbon/human/H = owner - if(isdullahan(H)) - var/datum/species/dullahan/D = H.dna.species - if(isobj(D.myhead.loc)) - var/obj/O = D.myhead.loc - O.say(speech_args[SPEECH_MESSAGE]) + var/mob/living/carbon/human/human = owner + if(isdullahan(human)) + var/datum/species/dullahan/dullahan_species = human.dna.species + if(isobj(dullahan_species.my_head.loc)) + var/obj/head = dullahan_species.my_head.loc + head.say(speech_args[SPEECH_MESSAGE], spans = speech_args[SPEECH_SPANS], sanitize = FALSE, range = speech_args[SPEECH_RANGE]) speech_args[SPEECH_MESSAGE] = "" /obj/item/organ/ears/dullahan @@ -145,20 +184,19 @@ /datum/action/item_action/organ_action/dullahan/Trigger() . = ..() - var/obj/item/organ/eyes/dullahan/DE = target - if(DE.tint) - DE.tint = 0 - else - DE.tint = INFINITY + var/obj/item/organ/eyes/dullahan/dullahan_eyes = target + dullahan_eyes.tint = dullahan_eyes.tint ? NONE : INFINITY if(ishuman(owner)) - var/mob/living/carbon/human/H = owner - if(isdullahan(H)) - var/datum/species/dullahan/D = H.dna.species - D.update_vision_perspective(H) + var/mob/living/carbon/human/human = owner + if(isdullahan(human)) + var/datum/species/dullahan/dullahan_species = human.dna.species + dullahan_species.update_vision_perspective(human) + /obj/item/dullahan_relay name = "dullahan relay" + /// The mob (a dullahan) that owns this relay. var/mob/living/owner /obj/item/dullahan_relay/Initialize(mapload, mob/living/carbon/human/new_owner) @@ -172,21 +210,26 @@ RegisterSignal(owner, COMSIG_LIVING_REVIVE, .proc/retrieve_head) become_hearing_sensitive(ROUNDSTART_TRAIT) -/obj/item/dullahan_relay/process() - if(!istype(loc, /obj/item/bodypart/head) || QDELETED(owner)) - . = PROCESS_KILL - qdel(src) +/obj/item/dullahan_relay/Destroy() + //lose_hearing_sensitivity(ROUNDSTART_TRAIT) + owner = null + return ..() /obj/item/dullahan_relay/process() if(!istype(loc, /obj/item/bodypart/head) || QDELETED(owner)) . = PROCESS_KILL qdel(src) -/obj/item/dullahan_relay/proc/examinate_check(atom/source, mob/user) +/obj/item/dullahan_relay/proc/examinate_check(mob/user, atom/source) SIGNAL_HANDLER if(user.client.eye == src) return COMPONENT_ALLOW_EXAMINATE +/obj/item/dullahan_relay/Hear(message, atom/movable/speaker, message_language, raw_message, radio_freq, list/spans, list/message_mods = list()) + . = ..() + if(owner) + owner.Hear(message, speaker, message_language, raw_message, radio_freq, spans, message_mods) + ///Adds the owner to the list of hearers in hearers_in_view(), for visible/hearable on top of say messages /obj/item/dullahan_relay/proc/include_owner(datum/source, list/hearers) SIGNAL_HANDLER @@ -202,17 +245,17 @@ /obj/item/dullahan_relay/proc/retrieve_head(datum/source, full_heal, admin_revive) SIGNAL_HANDLER if(admin_revive) - var/obj/item/bodypart/head/H = loc - var/turf/T = get_turf(owner) - if(H && istype(H) && T && !(H in owner.get_all_contents())) - H.forceMove(T) + var/obj/item/bodypart/head/head = loc + var/turf/body_turf = get_turf(owner) + if(head && istype(head) && body_turf && !(head in owner.GetAllContents())) + head.forceMove(body_turf) /obj/item/dullahan_relay/Destroy() if(!QDELETED(owner)) - var/mob/living/carbon/human/H = owner - if(isdullahan(H)) - var/datum/species/dullahan/D = H.dna.species - D.myhead = null + var/mob/living/carbon/human/human = owner + if(isdullahan(human)) + var/datum/species/dullahan/dullahan_species = human.dna.species + dullahan_species.my_head = null owner.investigate_log("has been gibbed by the destruction of their head/body relay.", INVESTIGATE_DEATHS) owner.gib() owner = null diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index 9389fd41ff0b8..723e8a6d42fc2 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -1407,13 +1407,17 @@ /mob/living/reset_perspective(atom/A) if(..()) update_sight() - if(client.eye && client.eye != src) - var/atom/AT = client.eye - AT.get_remote_view_fullscreens(src) - else - clear_fullscreen("remote_view", 0) + update_fullscreen() update_pipe_vision() +/// Proc used to handle the fullscreen overlay updates, realistically meant for the reset_perspective() proc. +/mob/living/proc/update_fullscreen() + if(client.eye && client.eye != src) + var/atom/client_eye = client.eye + client_eye.get_remote_view_fullscreens(src) + else + clear_fullscreen("remote_view", 0) + /mob/living/update_mouse_pointer() ..() if (client && ranged_ability && ranged_ability.ranged_mousepointer) diff --git a/code/modules/mob/living/login.dm b/code/modules/mob/living/login.dm index c8f1f02c6bc9b..49fae764e1324 100644 --- a/code/modules/mob/living/login.dm +++ b/code/modules/mob/living/login.dm @@ -1,5 +1,9 @@ /mob/living/Login() ..() + + //if(ishuman()) + //qdel_list_wrapperdna?.species?.on_owner_login(src) + //Mind updates sync_mind() mind.show_memory(src, 0) diff --git a/code/modules/mob/living/say.dm b/code/modules/mob/living/say.dm index 3b9ff88c46ccd..a4d34a9b08293 100644 --- a/code/modules/mob/living/say.dm +++ b/code/modules/mob/living/say.dm @@ -176,11 +176,6 @@ GLOBAL_LIST_INIT(department_radio_keys, list( log_talk(message, LOG_SAY, forced_by = forced, custom_say_emote = message_mods[MODE_CUSTOM_SAY_EMOTE]) message = treat_message(message) // unfortunately we still need this - var/sigreturn = SEND_SIGNAL(src, COMSIG_MOB_SAY, args) - if(sigreturn & COMPONENT_UPPERCASE_SPEECH) - message = uppertext(message) - if(!message) - return spans |= speech_span @@ -201,6 +196,17 @@ GLOBAL_LIST_INIT(department_radio_keys, list( // radios don't pick up whispers very well radio_message = stars(radio_message) spans |= SPAN_ITALICS + + var/list/speech_arguments = args + message_range + // Leaving this here so that anything that handles speech this way will be able to have spans affecting it and all that. + var/sigreturn = SEND_SIGNAL(src, COMSIG_MOB_SAY, speech_arguments) + if (sigreturn & COMPONENT_UPPERCASE_SPEECH) + message = uppertext(message) + if(!message) + if(succumbed) + succumb() + return + var/radio_return = radio(radio_message, message_mods, spans, language) if(radio_return & ITALICS) spans |= SPAN_ITALICS diff --git a/code/modules/surgery/bodyparts/head.dm b/code/modules/surgery/bodyparts/head.dm index 6936a65885396..859aead953791 100644 --- a/code/modules/surgery/bodyparts/head.dm +++ b/code/modules/surgery/bodyparts/head.dm @@ -21,6 +21,9 @@ var/obj/item/organ/ears/ears var/obj/item/organ/tongue/tongue + /// Do we show the information about missing organs upon being examined? Defaults to TRUE, useful for Dullahan heads. + var/show_organs_on_examine = TRUE + //Limb appearance info: var/real_name = "" //Replacement name //Hair colour and style @@ -65,7 +68,7 @@ /obj/item/bodypart/head/examine(mob/user) . = ..() - if(IS_ORGANIC_LIMB(src)) + if(IS_ORGANIC_LIMB(src) && show_organs_on_examine) if(!brain) . += "The brain has been removed from [src]." else if(brain.suicided || brainmob?.suiciding) @@ -187,7 +190,7 @@ ..() /obj/item/bodypart/head/update_icon_dropped() - var/list/standing = get_limb_icon(1) + var/list/standing = get_limb_icon(TRUE) if(!standing.len) icon_state = initial(icon_state)//no overlays found, we default back to initial icon. return From 7d327d27816202ff880f56a32615bd9460ccf798 Mon Sep 17 00:00:00 2001 From: RKz Date: Wed, 25 Oct 2023 07:04:40 -0400 Subject: [PATCH 4/7] https://github.com/tgstation/tgstation/pull/63143 --- code/modules/surgery/bodyparts/head.dm | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/code/modules/surgery/bodyparts/head.dm b/code/modules/surgery/bodyparts/head.dm index 859aead953791..8f9e7135e0544 100644 --- a/code/modules/surgery/bodyparts/head.dm +++ b/code/modules/surgery/bodyparts/head.dm @@ -251,6 +251,18 @@ if(eyes.eye_color) eyes_overlay.color = "#" + eyes.eye_color + +/obj/item/bodypart/head/talk_into(mob/holder, message, channel, spans, datum/language/language, list/message_mods) + var/mob/headholder = holder + if(istype(headholder)) + headholder.log_talk(message, LOG_SAY, tag = "beheaded talk") + + say(message, language, sanitize = FALSE) + return NOPASS + +/obj/item/bodypart/head/GetVoice() + return "The head of [real_name]" + /obj/item/bodypart/head/monkey icon = 'icons/mob/animal_parts.dmi' icon_state = "default_monkey_head" From b8f9474bf0772a314539921df35364f601527fb2 Mon Sep 17 00:00:00 2001 From: RKz Date: Wed, 25 Oct 2023 07:05:24 -0400 Subject: [PATCH 5/7] https://github.com/tgstation/tgstation/pull/63939 --- code/modules/mob/living/say.dm | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/code/modules/mob/living/say.dm b/code/modules/mob/living/say.dm index a4d34a9b08293..29a7294af4ee7 100644 --- a/code/modules/mob/living/say.dm +++ b/code/modules/mob/living/say.dm @@ -188,6 +188,15 @@ GLOBAL_LIST_INIT(department_radio_keys, list( message = "[randomnote] [message] [randomnote]" spans |= SPAN_SINGING + // Leaving this here so that anything that handles speech this way will be able to have spans affecting it and all that. + var/sigreturn = SEND_SIGNAL(src, COMSIG_MOB_SAY, args, message_range) + if (sigreturn & COMPONENT_UPPERCASE_SPEECH) + message = uppertext(message) + if(!message) + if(succumbed) + succumb() + return + //This is before anything that sends say a radio message, and after all important message type modifications, so you can scumb in alien chat or something if(saymode && !saymode.early && !saymode.handle_message(src, message, language)) return @@ -197,16 +206,6 @@ GLOBAL_LIST_INIT(department_radio_keys, list( radio_message = stars(radio_message) spans |= SPAN_ITALICS - var/list/speech_arguments = args + message_range - // Leaving this here so that anything that handles speech this way will be able to have spans affecting it and all that. - var/sigreturn = SEND_SIGNAL(src, COMSIG_MOB_SAY, speech_arguments) - if (sigreturn & COMPONENT_UPPERCASE_SPEECH) - message = uppertext(message) - if(!message) - if(succumbed) - succumb() - return - var/radio_return = radio(radio_message, message_mods, spans, language) if(radio_return & ITALICS) spans |= SPAN_ITALICS From 91fbf8d84c42a8dea7b6c892a7cc78a073a14e6d Mon Sep 17 00:00:00 2001 From: Tsar-Salat <62388554+Tsar-Salat@users.noreply.github.com> Date: Thu, 26 Oct 2023 10:57:46 -0400 Subject: [PATCH 6/7] 515 --- .../mob/living/carbon/human/species_types/dullahan.dm | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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 12e63ad2d3c7b..055ff4fcd5098 100644 --- a/code/modules/mob/living/carbon/human/species_types/dullahan.dm +++ b/code/modules/mob/living/carbon/human/species_types/dullahan.dm @@ -205,9 +205,9 @@ return INITIALIZE_HINT_QDEL owner = new_owner START_PROCESSING(SSobj, src) - RegisterSignal(owner, COMSIG_CLICK_SHIFT, .proc/examinate_check) - RegisterSignal(owner, COMSIG_LIVING_REGENERATE_LIMBS, .proc/unlist_head) - RegisterSignal(owner, COMSIG_LIVING_REVIVE, .proc/retrieve_head) + RegisterSignal(owner, COMSIG_CLICK_SHIFT, PROC_REF(examinate_check)) + RegisterSignal(owner, COMSIG_LIVING_REGENERATE_LIMBS, PROC_REF(unlist_head)) + RegisterSignal(owner, COMSIG_LIVING_REVIVE, PROC_REF(retrieve_head)) become_hearing_sensitive(ROUNDSTART_TRAIT) /obj/item/dullahan_relay/Destroy() From 0d78bc537820ff92f4a1087c70e31c67f1799a92 Mon Sep 17 00:00:00 2001 From: Tsar-Salat <62388554+Tsar-Salat@users.noreply.github.com> Date: Thu, 26 Oct 2023 15:02:01 -0400 Subject: [PATCH 7/7] add back INVESTIGATE_DEATHS to dulla --- code/modules/mob/living/carbon/human/species_types/dullahan.dm | 2 ++ 1 file changed, 2 insertions(+) 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 055ff4fcd5098..cc0e26423393c 100644 --- a/code/modules/mob/living/carbon/human/species_types/dullahan.dm +++ b/code/modules/mob/living/carbon/human/species_types/dullahan.dm @@ -69,6 +69,7 @@ /datum/species/dullahan/spec_life(mob/living/carbon/human/human, delta_time, 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() return @@ -81,6 +82,7 @@ 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 the loss of [human.p_their()] head.", INVESTIGATE_DEATHS) human.gib() // 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)