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 60d1b10e231..f86a161b1ce 100644 --- a/code/game/machinery/dna_infuser/organ_sets/fly_organs.dm +++ b/code/game/machinery/dna_infuser/organ_sets/fly_organs.dm @@ -63,7 +63,7 @@ /obj/item/organ/internal/tongue/fly/New(class, timer, datum/mutation/human/copymut) . = ..() - AddComponent(/datum/component/speechmod, replacements = CONFIG_GET(flag/russian_text_formation) ? russian_speech_replacements : speech_replacements) // SKYRAT EDIT CHANGE - ORIGINAL: AddComponent(/datum/component/speechmod, replacements = speech_replacements) + AddComponent(/datum/component/speechmod, replacements = CONFIG_GET(flag/russian_text_formation) ? russian_speech_replacements : speech_replacements, should_modify_speech = CALLBACK(src, PROC_REF(should_modify_speech))) // SKYRAT EDIT CHANGE - ORIGINAL: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 ee2f19a8a10..e4ce3b126fc 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] @@ -216,7 +220,7 @@ /obj/item/organ/internal/tongue/lizard/New(class, timer, datum/mutation/human/copymut) . = ..() - AddComponent(/datum/component/speechmod, replacements = CONFIG_GET(flag/russian_text_formation) ? russian_speech_replacements : speech_replacements) // SKYRAT EDIT CHANGE - ORIGINAL: AddComponent(/datum/component/speechmod, replacements = speech_replacements) + AddComponent(/datum/component/speechmod, replacements = CONFIG_GET(flag/russian_text_formation) ? russian_speech_replacements : speech_replacements, should_modify_speech = CALLBACK(src, PROC_REF(should_modify_speech))) // SKYRAT EDIT CHANGE - ORIGINAL: 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 6fde4de9dbc..37585745925 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))