From d80d3af91af29a71783875dfb509d2bede8b8b52 Mon Sep 17 00:00:00 2001 From: Tsar-Salat <62388554+Tsar-Salat@users.noreply.github.com> Date: Tue, 22 Oct 2024 23:07:01 -0400 Subject: [PATCH] The revival of Dullahans! + Ventriloquism! (#10093) * https://github.com/tgstation/tgstation/pull/48633 * makes em undead * https://github.com/tgstation/tgstation/pull/63696 * https://github.com/tgstation/tgstation/pull/63143 * https://github.com/tgstation/tgstation/pull/63939 * 515 * add back INVESTIGATE_DEATHS to dulla --- .../dcs/signals/signals_atom/signals_atom.dm | 4 +- .../dcs/signals/signals_mob/signals_mob.dm | 2 + code/__DEFINES/is_helpers.dm | 1 + code/_onclick/click.dm | 4 +- 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 | 207 ++++++++++++------ code/modules/mob/living/living.dm | 17 +- code/modules/mob/living/say.dm | 15 +- code/modules/surgery/bodyparts/head.dm | 19 +- 13 files changed, 218 insertions(+), 84 deletions(-) diff --git a/code/__DEFINES/dcs/signals/signals_atom/signals_atom.dm b/code/__DEFINES/dcs/signals/signals_atom/signals_atom.dm index 2b5b5eae98d90..dd0bed49487cb 100644 --- a/code/__DEFINES/dcs/signals/signals_atom/signals_atom.dm +++ b/code/__DEFINES/dcs/signals/signals_atom/signals_atom.dm @@ -120,6 +120,7 @@ ///! from the base of atom/intercept_teleport: (channel, turf/origin, turf/destination) #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) @@ -144,7 +145,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_mob.dm b/code/__DEFINES/dcs/signals/signals_mob/signals_mob.dm index 016833787b053..9492d89c06ac6 100644 --- a/code/__DEFINES/dcs/signals/signals_mob/signals_mob.dm +++ b/code/__DEFINES/dcs/signals/signals_mob/signals_mob.dm @@ -56,6 +56,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/__DEFINES/is_helpers.dm b/code/__DEFINES/is_helpers.dm index 2831abed7a3b2..2ad043144f5bd 100644 --- a/code/__DEFINES/is_helpers.dm +++ b/code/__DEFINES/is_helpers.dm @@ -96,6 +96,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 b9a4877f4e464..027d1f498fc82 100644 --- a/code/_onclick/click.dm +++ b/code/_onclick/click.dm @@ -318,8 +318,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(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 f267f5d59053d..5317fc5ef6256 100644 --- a/code/game/machinery/recycler.dm +++ b/code/game/machinery/recycler.dm @@ -121,7 +121,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/game/say.dm b/code/game/say.dm index 2d22f4a42dd86..6046a95bd01f6 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 5c6633ac29519..a7f54cd301a41 100644 --- a/code/modules/mob/living/brain/brain_item.dm +++ b/code/modules/mob/living/brain/brain_item.dm @@ -21,8 +21,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 ca1c84c81f8b2..289e2d2fd1da6 100644 --- a/code/modules/mob/living/carbon/human/human.dm +++ b/code/modules/mob/living/carbon/human/human.dm @@ -137,6 +137,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 906261228a0c9..a937df100d483 100644 --- a/code/modules/mob/living/carbon/human/species.dm +++ b/code/modules/mob/living/carbon/human/species.dm @@ -156,6 +156,9 @@ GLOBAL_LIST_EMPTY(features_by_species) // Species specific bitflags. Used for things like if the race is unable to become a changeling. var/species_bitflags = NONE + /// 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 // /////////// @@ -2498,6 +2501,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 c5370e1f69784..dfb68c3d0aaa0 100644 --- a/code/modules/mob/living/carbon/human/species_types/dullahan.dm +++ b/code/modules/mob/living/carbon/human/species_types/dullahan.dm @@ -3,9 +3,19 @@ id = SPECIES_DULLAHAN 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) - mutant_bodyparts = list("mcolor" = "FFF", "tail_human" = "None", "ears" = "None", "wings" = "None", "body_size" = "Normal") + species_traits = list( + EYECOLOR, + HAIR, + FACEHAIR, + LIPS + ) + inherent_traits = list( + TRAIT_NOHUNGER, + TRAIT_NOBREATH, + TRAIT_NONECRODISEASE, + ) + inherent_biotypes = list(MOB_UNDEAD, MOB_HUMANOID) + mutant_bodyparts = list("wings" = "None", "body_size" = "Normal") use_skintones = TRUE mutantbrain = /obj/item/organ/brain/dullahan mutanteyes = /obj/item/organ/eyes/dullahan @@ -15,7 +25,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() @@ -23,45 +37,85 @@ 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) . = ..() - H.lose_hearing_sensitivity(TRAIT_GENERIC) - var/obj/item/bodypart/head/head = H.get_bodypart(BODY_ZONE_HEAD) + human.lose_hearing_sensitivity(TRAIT_GENERIC) + var/obj/item/bodypart/head/head = human.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) - -/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 + + 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 + + // 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 + human.update_body() + 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.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 - human.gib() + 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 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/H) - var/obj/item/organ/eyes/eyes = H.getorganslot(ORGAN_SLOT_EYES) +/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() @@ -108,7 +162,7 @@ /obj/item/organ/brain/dullahan decoy_override = TRUE - organ_flags = 0 + organ_flags = NONE /obj/item/organ/tongue/dullahan zone = "abstract" @@ -116,12 +170,12 @@ /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) - 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 @@ -132,6 +186,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" @@ -139,52 +194,80 @@ /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(H.dna.species.id == SPECIES_DULLAHAN) - 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 CREATION_TEST_IGNORE_SUBTYPES(/obj/item/dullahan_relay) -/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_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/Hear(message, atom/movable/speaker, message_language, raw_message, radio_freq, list/spans, list/message_mods = list()) - owner.Hear(arglist(args)) +/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(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(!QDELETED(owner)) - message = compose_message(speaker, message_language, raw_message, radio_freq, spans, message_mods) - to_chat(owner,message) - else - qdel(src) + 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 + 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/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(H.dna.species.id == SPECIES_DULLAHAN) - 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 3095e44139230..58f66906a0bb1 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -1459,16 +1459,19 @@ result += static_virus return result -/mob/living/reset_perspective(atom/new_eye) - if(!..()) - return - update_sight() +/mob/living/reset_perspective(atom/A) + if(..()) + update_sight() + 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/AT = client.eye - AT.get_remote_view_fullscreens(src) + var/atom/client_eye = client.eye + client_eye.get_remote_view_fullscreens(src) else clear_fullscreen("remote_view", 0) - update_pipe_vision() /mob/living/update_mouse_pointer() ..() diff --git a/code/modules/mob/living/say.dm b/code/modules/mob/living/say.dm index 22bbed72e029a..af0c9c8b07bbe 100644 --- a/code/modules/mob/living/say.dm +++ b/code/modules/mob/living/say.dm @@ -164,11 +164,6 @@ GLOBAL_LIST_INIT(department_radio_keys, list( if(message_mods[RADIO_KEY] == RADIO_KEY_UPLINK) // only uplink needs this message_mods[MODE_UNTREATED_MESSAGE] = message // let's store the original message before treating those 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 @@ -181,6 +176,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 @@ -189,6 +193,7 @@ GLOBAL_LIST_INIT(department_radio_keys, list( // radios don't pick up whispers very well radio_message = stars(radio_message) spans |= SPAN_ITALICS + var/radio_return = radio(radio_message, message_mods, spans, language)//roughly 27% of living/say()'s total cost if(radio_return & ITALICS) spans |= SPAN_ITALICS diff --git a/code/modules/surgery/bodyparts/head.dm b/code/modules/surgery/bodyparts/head.dm index 21267b0914734..9f91255a3df71 100644 --- a/code/modules/surgery/bodyparts/head.dm +++ b/code/modules/surgery/bodyparts/head.dm @@ -22,6 +22,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 @@ -66,7 +69,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) @@ -188,7 +191,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 @@ -248,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"