From e78ab9e59a2e452341ecff8ca92c9d9c431ee4f4 Mon Sep 17 00:00:00 2001 From: FearfulFurnishing <139661819+FearfulFurnishing@users.noreply.github.com> Date: Sun, 14 Jul 2024 21:33:56 -0500 Subject: [PATCH] Fix Tongue Based Speech Modifiers Being Applied To Users Native Languages (#84952) ## About The Pull Request This change allows tongue based speech modifications to be ignored if the user is speaking in a native language or using hand signs, putting it back to where it was functionally before moving to speechmod components. ## Why It's Good For The Game This is correcting some of the speaking code to how it was working prior to speechmods, meaning lizard people won't be elongating there s's in draconic. Fly people are the other species with a tongue based speech modifier and receive the same fix. This also corrects tongue based speech mods getting applied to sign language. Speech modifiers are still applied if the user is talking in a non-native language, same as it was pre speechmod. Before: ![speechmod_demo_before](https://github.com/user-attachments/assets/86d9bca0-2d1d-44fe-8448-b8aebde8957e) After: ![speechmod_demo_after](https://github.com/user-attachments/assets/9f071df5-0a60-4898-9aae-e8b8edd0db9e) ## Changelog :cl: fix: fixing speech modifiers being applied to a tongue's native languages. /:cl: --------- Co-authored-by: Ghom <42542238+Ghommie@users.noreply.github.com> --- code/datums/components/speechmod.dm | 11 ++++- .../dna_infuser/organ_sets/fly_organs.dm | 2 +- .../surgery/organs/internal/tongue/_tongue.dm | 8 +++- code/modules/unit_tests/say.dm | 44 ++++++++++++++++++- 4 files changed, 59 insertions(+), 6 deletions(-) diff --git a/code/datums/components/speechmod.dm b/code/datums/components/speechmod.dm index 71991c80d83..2506a0b9140 100644 --- a/code/datums/components/speechmod.dm +++ b/code/datums/components/speechmod.dm @@ -13,8 +13,10 @@ var/slots /// If set to true, turns all text to uppercase var/uppercase = FALSE + /// Any additional checks that we should do before applying the speech modification + var/datum/callback/should_modify_speech = null -/datum/component/speechmod/Initialize(replacements = list(), end_string = "", end_string_chance = 100, slots, uppercase = FALSE) +/datum/component/speechmod/Initialize(replacements = list(), end_string = "", end_string_chance = 100, slots, uppercase = FALSE, should_modify_speech) if (!ismob(parent) && !isitem(parent) && !istype(parent, /datum/mutation/human)) return COMPONENT_INCOMPATIBLE @@ -23,6 +25,7 @@ src.end_string_chance = end_string_chance src.slots = slots src.uppercase = uppercase + src.should_modify_speech = should_modify_speech if (istype(parent, /datum/mutation/human)) RegisterSignal(parent, COMSIG_MUTATION_GAINED, PROC_REF(on_mutation_gained)) @@ -51,6 +54,8 @@ var/message = speech_args[SPEECH_MESSAGE] if(message[1] == "*") return + if(!isnull(should_modify_speech) && !should_modify_speech.Invoke(source, speech_args)) + return for (var/to_replace in replacements) var/replacement = replacements[to_replace] @@ -123,3 +128,7 @@ return UnregisterSignal(targeted, COMSIG_MOB_SAY) targeted = null + +/datum/component/speechmod/Destroy() + should_modify_speech = null + return ..() diff --git a/code/game/machinery/dna_infuser/organ_sets/fly_organs.dm b/code/game/machinery/dna_infuser/organ_sets/fly_organs.dm index 34d50fc3fc2..e3e7112b0fe 100644 --- a/code/game/machinery/dna_infuser/organ_sets/fly_organs.dm +++ b/code/game/machinery/dna_infuser/organ_sets/fly_organs.dm @@ -51,7 +51,7 @@ /obj/item/organ/internal/tongue/fly/New(class, timer, datum/mutation/human/copymut) . = ..() - AddComponent(/datum/component/speechmod, replacements = speech_replacements) + AddComponent(/datum/component/speechmod, replacements = speech_replacements, should_modify_speech = CALLBACK(src, PROC_REF(should_modify_speech))) /obj/item/organ/internal/tongue/fly/Initialize(mapload) . = ..() diff --git a/code/modules/surgery/organs/internal/tongue/_tongue.dm b/code/modules/surgery/organs/internal/tongue/_tongue.dm index d0d3d95f287..7904c4bea91 100644 --- a/code/modules/surgery/organs/internal/tongue/_tongue.dm +++ b/code/modules/surgery/organs/internal/tongue/_tongue.dm @@ -94,11 +94,15 @@ /obj/item/organ/internal/tongue/proc/handle_speech(datum/source, list/speech_args) SIGNAL_HANDLER + if(should_modify_speech(source, speech_args)) + modify_speech(source, speech_args) + +/obj/item/organ/internal/tongue/proc/should_modify_speech(datum/source, list/speech_args) if(speech_args[SPEECH_LANGUAGE] in languages_native) // Speaking a native language? return FALSE // Don't modify speech if(HAS_TRAIT(source, TRAIT_SIGN_LANG)) // No modifiers for signers - I hate this but I simply cannot get these to combine into one statement return FALSE // Don't modify speech - modify_speech(source, speech_args) + return TRUE /obj/item/organ/internal/tongue/proc/modify_speech(datum/source, list/speech_args) return speech_args[SPEECH_MESSAGE] @@ -195,7 +199,7 @@ /obj/item/organ/internal/tongue/lizard/New(class, timer, datum/mutation/human/copymut) . = ..() - AddComponent(/datum/component/speechmod, replacements = speech_replacements) + AddComponent(/datum/component/speechmod, replacements = speech_replacements, should_modify_speech = CALLBACK(src, PROC_REF(should_modify_speech))) /obj/item/organ/internal/tongue/lizard/silver name = "silver tongue" diff --git a/code/modules/unit_tests/say.dm b/code/modules/unit_tests/say.dm index ec58dcedc88..7536392e70c 100644 --- a/code/modules/unit_tests/say.dm +++ b/code/modules/unit_tests/say.dm @@ -22,6 +22,46 @@ TEST_ASSERT(!expected_mods.len, "Some message mods were expected, but were not returned by get_message_mods: [json_encode(expected_mods)]. Message: [message]") +/// Test to ensure native tongue languages properly impact speech +/datum/unit_test/speech_modifiers + var/mob/living/carbon/human/talking_lizard + var/list/handle_speech_result = null + +/datum/unit_test/speech_modifiers/proc/handle_speech(datum/source, list/speech_args) + SIGNAL_HANDLER + + TEST_ASSERT(speech_args[SPEECH_MESSAGE], "Handle speech signal does not have a message arg") + TEST_ASSERT(speech_args[SPEECH_LANGUAGE], "Handle speech signal does not have a language arg") + + // saving hearing_args directly via handle_speech_result = speech_args won't work since the arg list + // is a temporary variable that gets garbage collected after it's done being used by procs + // therefore we need to create a new list and transfer the args + handle_speech_result = list() + handle_speech_result += speech_args + +/datum/unit_test/speech_modifiers/Run() + talking_lizard = allocate(/mob/living/carbon/human/consistent) + talking_lizard.set_species(/datum/species/lizard) + var/hissed_quote = "SSShe isss ssso sssasssy" + var/unhissed_quote = "She is so sassy" + + RegisterSignal(talking_lizard, COMSIG_MOB_SAY, PROC_REF(handle_speech)) + + // lizard's forked tongue causes hissing when speaking common + talking_lizard.set_active_language(/datum/language/common) + talking_lizard.say(unhissed_quote) + TEST_ASSERT(handle_speech_result, "Handle speech signal was not fired") + TEST_ASSERT_EQUAL(hissed_quote, handle_speech_result[SPEECH_MESSAGE], "Speech modifier test failed: [handle_speech_result[SPEECH_LANGUAGE]] did not equal [hissed_quote] when spoken by a lizard in language [handle_speech_result[SPEECH_LANGUAGE]]") + + handle_speech_result = null + + // lizard's forked tongue does not cause hissing when speaking native draconic + talking_lizard.set_active_language(/datum/language/draconic) + talking_lizard.say(unhissed_quote) + TEST_ASSERT(handle_speech_result, "Handle speech signal was not fired") + TEST_ASSERT_EQUAL(unhissed_quote, handle_speech_result[SPEECH_MESSAGE], "Speech modifier test failed: [handle_speech_result[SPEECH_LANGUAGE]] did not equal [unhissed_quote] when spoken by a lizard in language [handle_speech_result[SPEECH_LANGUAGE]]") + + /// Test to verify COMSIG_MOB_SAY is sent the exact same list as the message args, as they're operated on /datum/unit_test/say_signal @@ -76,7 +116,7 @@ TEST_ASSERT(speech_args[SPEECH_LANGUAGE], "Handle speech signal does not have a language arg") TEST_ASSERT(speech_args[SPEECH_RANGE], "Handle speech signal does not have a range arg") - // saving hearing_args directly via handle_speech_result = speech_args won't work since the arg list + // saving speech_args directly via handle_speech_result = speech_args won't work since the arg list // is a temporary variable that gets garbage collected after it's done being used by procs // therefore we need to create a new list and transfer the args handle_speech_result = list() @@ -126,7 +166,7 @@ var/datum/client_interface/mock_client = new() listener.mock_client = mock_client - RegisterSignal(speaker, COMSIG_MOB_SAY, PROC_REF(handle_speech)) + RegisterSignal(speaker, COMSIG_MOB_SAY, PROC_REF(handle_speech)) // RegisterSignal(speaker_radio, COMSIG_RADIO_NEW_MESSAGE, PROC_REF(handle_radio_hearing)) RegisterSignal(listener, COMSIG_MOVABLE_HEAR, PROC_REF(handle_hearing))