From aac161bce95b8a0b91aa5d25096878d6342ab616 Mon Sep 17 00:00:00 2001 From: Y0SH1M4S73R Date: Sun, 6 Jul 2025 23:55:16 -0400 Subject: [PATCH] Allows blocking language speech and comprehension separately (#91884) ## About The Pull Request There was a PR I wanted to make, but one of the features I wanted the addition to have was for an affected mob to only speak aphasic, but understand whatever languages they'd normally be able to understand. This, in turn, required refactoring language holders to separately block language speech and comprehension. This change, I decided, would be good to split into its own PR. ## Why It's Good For The Game Allows more fine-grained control over what languages an atom is blocked from speaking or understanding. This will be useful for future PRs or admin events. ## Changelog no player-facing changes --- code/datums/brain_damage/severe.dm | 4 +- .../diseases/advance/symptoms/voice_change.dm | 4 +- code/datums/mutations/speech.dm | 4 +- code/game/atoms_movable.dm | 8 +- code/modules/language/_language_holder.dm | 73 ++++++++++++------- code/modules/language/_language_menu.dm | 12 ++- .../mob/living/carbon/human/_species.dm | 12 ++- .../reagents/drinks/alcohol_reagents.dm | 4 +- code/modules/unit_tests/say.dm | 2 +- 9 files changed, 76 insertions(+), 47 deletions(-) diff --git a/code/datums/brain_damage/severe.dm b/code/datums/brain_damage/severe.dm index bd821037419..57cfae25cf5 100644 --- a/code/datums/brain_damage/severe.dm +++ b/code/datums/brain_damage/severe.dm @@ -29,13 +29,13 @@ lose_text = span_notice("You suddenly remember how languages work.") /datum/brain_trauma/severe/aphasia/on_gain() - owner.add_blocked_language(subtypesof(/datum/language) - /datum/language/aphasia, LANGUAGE_APHASIA) + owner.add_blocked_language(subtypesof(/datum/language) - /datum/language/aphasia, source = LANGUAGE_APHASIA) owner.grant_language(/datum/language/aphasia, source = LANGUAGE_APHASIA) . = ..() /datum/brain_trauma/severe/aphasia/on_lose() if(!QDELING(owner)) - owner.remove_blocked_language(subtypesof(/datum/language), LANGUAGE_APHASIA) + owner.remove_blocked_language(subtypesof(/datum/language), source = LANGUAGE_APHASIA) owner.remove_language(/datum/language/aphasia, source = LANGUAGE_APHASIA) ..() diff --git a/code/datums/diseases/advance/symptoms/voice_change.dm b/code/datums/diseases/advance/symptoms/voice_change.dm index 9654365c49d..92583e1fca7 100644 --- a/code/datums/diseases/advance/symptoms/voice_change.dm +++ b/code/datums/diseases/advance/symptoms/voice_change.dm @@ -57,7 +57,7 @@ H.SetSpecialVoice(H.generate_random_mob_name()) if(scramble_language && !current_language) // Last part prevents rerolling language with small amounts of cure. current_language = pick(subtypesof(/datum/language) - /datum/language/common) - H.add_blocked_language(subtypesof(/datum/language) - current_language, LANGUAGE_VOICECHANGE) + H.add_blocked_language(subtypesof(/datum/language) - current_language, source = LANGUAGE_VOICECHANGE) H.grant_language(current_language, source = LANGUAGE_VOICECHANGE) /datum/symptom/voice_change/End(datum/disease/advance/A) @@ -66,5 +66,5 @@ var/mob/living/carbon/human/H = A.affected_mob H.UnsetSpecialVoice() if(scramble_language) - A.affected_mob.remove_blocked_language(subtypesof(/datum/language), LANGUAGE_VOICECHANGE) + A.affected_mob.remove_blocked_language(subtypesof(/datum/language), source = LANGUAGE_VOICECHANGE) A.affected_mob.remove_all_languages(LANGUAGE_VOICECHANGE) // In case someone managed to get more than one anyway. diff --git a/code/datums/mutations/speech.dm b/code/datums/mutations/speech.dm index 7fd59cb1930..7345e87e798 100644 --- a/code/datums/mutations/speech.dm +++ b/code/datums/mutations/speech.dm @@ -231,12 +231,12 @@ if(!.) return owner.grant_language(/datum/language/beachbum, source = LANGUAGE_STONER) - owner.add_blocked_language(subtypesof(/datum/language) - /datum/language/beachbum, LANGUAGE_STONER) + owner.add_blocked_language(subtypesof(/datum/language) - /datum/language/beachbum, source = LANGUAGE_STONER) /datum/mutation/stoner/on_losing(mob/living/carbon/human/owner) ..() owner.remove_language(/datum/language/beachbum, source = LANGUAGE_STONER) - owner.remove_blocked_language(subtypesof(/datum/language) - /datum/language/beachbum, LANGUAGE_STONER) + owner.remove_blocked_language(subtypesof(/datum/language) - /datum/language/beachbum, source = LANGUAGE_STONER) /datum/mutation/medieval name = "Medieval" diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm index a1de74bc00a..e08e055fd7d 100644 --- a/code/game/atoms_movable.dm +++ b/code/game/atoms_movable.dm @@ -1573,12 +1573,12 @@ return get_language_holder().remove_all_partial_languages(source) /// Adds a language to the blocked language list. Use this over remove_language in cases where you will give languages back later. -/atom/movable/proc/add_blocked_language(language, source = LANGUAGE_ATOM) - return get_language_holder().add_blocked_language(language, source) +/atom/movable/proc/add_blocked_language(language, language_flags = ALL, source = LANGUAGE_ATOM) + return get_language_holder().add_blocked_language(language, language_flags, source) /// Removes a language from the blocked language list. -/atom/movable/proc/remove_blocked_language(language, source = LANGUAGE_ATOM) - return get_language_holder().remove_blocked_language(language, source) +/atom/movable/proc/remove_blocked_language(language, language_flags = ALL, source = LANGUAGE_ATOM) + return get_language_holder().remove_blocked_language(language, language_flags, source) /// Checks if atom has the language. If spoken is true, only checks if atom can speak the language. /atom/movable/proc/has_language(language, flags_to_check) diff --git a/code/modules/language/_language_holder.dm b/code/modules/language/_language_holder.dm index 79172b72e50..dc65a708841 100644 --- a/code/modules/language/_language_holder.dm +++ b/code/modules/language/_language_holder.dm @@ -41,9 +41,10 @@ Key procs /// Lazyassoclist of languages that can be spoken. /// Tongue organ may also set limits beyond this list. var/list/spoken_languages - /// Lazyassoclist of blocked languages. - /// Used to prevent understanding and speaking certain languages, ie for certain mobs, mutations etc. - var/list/blocked_languages + /// Lazyassoclist of languages we are blocked from understanding, used for certain mobs, mutations, quirks, etc. + var/list/blocked_understanding + /// Lazyassoclist of languages we are blocked from speaking, used for certain mobs, mutations, quirks, etc. + var/list/blocked_speaking /// If true, overrides tongue aforementioned limitations. var/omnitongue = FALSE /// Handles displaying the language menu UI. @@ -104,7 +105,7 @@ Key procs for(var/language in mutual_understanding) for(var/source in mutual_understanding[language]) // if this mutual understanding comes from a language, and that language is blocked, skip it - if(LAZYACCESS(blocked_languages, source)) + if(LAZYACCESS(blocked_understanding, source)) continue if(!best_mutual_languages[language] || best_mutual_languages[language] < mutual_understanding[language][source]) best_mutual_languages[language] = mutual_understanding[language][source] @@ -190,37 +191,43 @@ Key procs return TRUE /// Adds a single language or list of languages to the blocked language list. -/datum/language_holder/proc/add_blocked_language(languages, source = LANGUAGE_MIND) +/datum/language_holder/proc/add_blocked_language(languages, language_flags = ALL, source = LANGUAGE_MIND) if(!islist(languages)) languages = list(languages) for(var/language in languages) - LAZYORASSOCLIST(blocked_languages, language, source) + if(language_flags & SPOKEN_LANGUAGE) + LAZYORASSOCLIST(blocked_speaking, language, source) + if(language_flags & UNDERSTOOD_LANGUAGE) + LAZYORASSOCLIST(blocked_understanding, language, source) calculate_best_mutual_language() return TRUE /// Removes a single language or list of languages from the blocked language list. -/datum/language_holder/proc/remove_blocked_language(languages, source = LANGUAGE_MIND) +/datum/language_holder/proc/remove_blocked_language(languages, language_flags = ALL, source = LANGUAGE_MIND) if(!islist(languages)) languages = list(languages) for(var/language in languages) if(source == LANGUAGE_ALL) - LAZYREMOVE(blocked_languages, language) + if(language_flags & SPOKEN_LANGUAGE) + LAZYREMOVE(blocked_speaking, language) + if(language_flags & UNDERSTOOD_LANGUAGE) + LAZYREMOVE(blocked_understanding, language) else - LAZYREMOVEASSOC(blocked_languages, language, source) + if(language_flags & SPOKEN_LANGUAGE) + LAZYREMOVEASSOC(blocked_speaking, language, source) + if(language_flags & UNDERSTOOD_LANGUAGE) + LAZYREMOVEASSOC(blocked_understanding, language, source) calculate_best_mutual_language() return TRUE /// Checks if you have the language passed. /datum/language_holder/proc/has_language(language, flag_to_check = UNDERSTOOD_LANGUAGE) - if(language in blocked_languages) - return FALSE - var/list/langs_to_check = list() - if(flag_to_check & SPOKEN_LANGUAGE) + if(flag_to_check & SPOKEN_LANGUAGE && !LAZYACCESS(blocked_speaking, language)) langs_to_check |= spoken_languages - if(flag_to_check & UNDERSTOOD_LANGUAGE) + if(flag_to_check & UNDERSTOOD_LANGUAGE && !LAZYACCESS(blocked_understanding, language)) langs_to_check |= understood_languages return language in langs_to_check @@ -239,7 +246,7 @@ Key procs for(var/lang in spoken_languages) var/datum/language/language = lang var/priority = initial(language.default_priority) - if((!highest_priority || (priority > highest_priority)) && !(language in blocked_languages)) + if((!highest_priority || (priority > highest_priority)) && !(language in blocked_speaking)) if(can_speak_language(language)) selected_language = language highest_priority = priority @@ -284,8 +291,10 @@ Key procs grant_language(language, UNDERSTOOD_LANGUAGE, from_holder.understood_languages[language]) for(var/language in from_holder.spoken_languages) grant_language(language, SPOKEN_LANGUAGE, from_holder.spoken_languages[language]) - for(var/language in from_holder.blocked_languages) - add_blocked_language(language, from_holder.blocked_languages[language]) + for(var/language in from_holder.blocked_speaking) + add_blocked_language(language, SPOKEN_LANGUAGE, from_holder.blocked_speaking[language]) + for(var/language in from_holder.blocked_understanding) + add_blocked_language(language, UNDERSTOOD_LANGUAGE, from_holder.blocked_understanding[language]) return TRUE /// Transfers all mind languages to the supplied language holder. @@ -298,10 +307,14 @@ Key procs if(LANGUAGE_MIND in spoken_languages[language]) remove_language(language, SPOKEN_LANGUAGE, LANGUAGE_MIND) to_holder.grant_language(language, SPOKEN_LANGUAGE, LANGUAGE_MIND) - for(var/language in blocked_languages) - if(LANGUAGE_MIND in blocked_languages[language]) - remove_blocked_language(language, LANGUAGE_MIND) - to_holder.add_blocked_language(language, LANGUAGE_MIND) + for(var/language in blocked_speaking) + if(LANGUAGE_MIND in blocked_speaking[language]) + remove_blocked_language(language, SPOKEN_LANGUAGE, LANGUAGE_MIND) + to_holder.add_blocked_language(language, SPOKEN_LANGUAGE, LANGUAGE_MIND) + for(var/language in blocked_understanding) + if(LANGUAGE_MIND in blocked_understanding[language]) + remove_blocked_language(language, UNDERSTOOD_LANGUAGE, LANGUAGE_MIND) + to_holder.add_blocked_language(language, UNDERSTOOD_LANGUAGE, LANGUAGE_MIND) for(var/language in mutual_understanding) var/mind_understanding = mutual_understanding[language][LANGUAGE_MIND] if(mind_understanding > 0) @@ -335,7 +348,8 @@ GLOBAL_LIST_INIT(prototype_language_holders, init_language_holder_prototypes()) /datum/language_holder/alien understood_languages = list(/datum/language/xenocommon = list(LANGUAGE_ATOM)) spoken_languages = list(/datum/language/xenocommon = list(LANGUAGE_ATOM)) - blocked_languages = list(/datum/language/common = list(LANGUAGE_ATOM)) + blocked_speaking = list(/datum/language/common = list(LANGUAGE_ATOM)) + blocked_understanding = list(/datum/language/common = list(LANGUAGE_ATOM)) /datum/language_holder/construct understood_languages = list( @@ -350,10 +364,12 @@ GLOBAL_LIST_INIT(prototype_language_holders, init_language_holder_prototypes()) /datum/language_holder/drone understood_languages = list(/datum/language/drone = list(LANGUAGE_ATOM)) spoken_languages = list(/datum/language/drone = list(LANGUAGE_ATOM)) - blocked_languages = list(/datum/language/common = list(LANGUAGE_ATOM)) + blocked_speaking = list(/datum/language/common = list(LANGUAGE_ATOM)) + blocked_understanding = list(/datum/language/common = list(LANGUAGE_ATOM)) /datum/language_holder/drone/syndicate - blocked_languages = null + blocked_speaking = null + blocked_understanding = null /datum/language_holder/human_basic understood_languages = list(/datum/language/common = list(LANGUAGE_ATOM)) @@ -372,7 +388,8 @@ GLOBAL_LIST_INIT(prototype_language_holders, init_language_holder_prototypes()) /datum/language_holder/lightbringer understood_languages = list(/datum/language/slime = list(LANGUAGE_ATOM)) spoken_languages = list(/datum/language/slime = list(LANGUAGE_ATOM)) - blocked_languages = list(/datum/language/common = list(LANGUAGE_ATOM)) + blocked_speaking = list(/datum/language/common = list(LANGUAGE_ATOM)) + blocked_understanding = list(/datum/language/common = list(LANGUAGE_ATOM)) /datum/language_holder/lizard understood_languages = list( @@ -438,12 +455,14 @@ GLOBAL_LIST_INIT(prototype_language_holders, init_language_holder_prototypes()) /datum/language_holder/venus understood_languages = list(/datum/language/sylvan = list(LANGUAGE_ATOM)) spoken_languages = list(/datum/language/sylvan = list(LANGUAGE_ATOM)) - blocked_languages = list(/datum/language/common = list(LANGUAGE_ATOM)) + blocked_speaking = list(/datum/language/common = list(LANGUAGE_ATOM)) + blocked_understanding = list(/datum/language/common = list(LANGUAGE_ATOM)) /datum/language_holder/spider understood_languages = list(/datum/language/buzzwords = list(LANGUAGE_ATOM)) spoken_languages = list(/datum/language/buzzwords = list(LANGUAGE_ATOM)) - blocked_languages = list(/datum/language/common = list(LANGUAGE_ATOM)) + blocked_speaking = list(/datum/language/common = list(LANGUAGE_ATOM)) + blocked_understanding = list(/datum/language/common = list(LANGUAGE_ATOM)) /datum/language_holder/synthetic understood_languages = list( diff --git a/code/modules/language/_language_menu.dm b/code/modules/language/_language_menu.dm index b9ffa4fab58..039afb2e1c3 100644 --- a/code/modules/language/_language_menu.dm +++ b/code/modules/language/_language_menu.dm @@ -83,10 +83,16 @@ if("Both") adding_flags |= ALL - if(LAZYACCESS(language_holder.blocked_languages, language_datum)) - choice = tgui_alert(user, "Do you want to lift the blockage that's also preventing the language to be spoken or understood?", "[language_datum]", list("Yes", "No")) + var/ask_to_remove_block + var/list/block_being_removed_on = list() + if(adding_flags & SPOKEN_LANGUAGE && LAZYACCESS(language_holder.blocked_speaking, language_datum)) + ask_to_remove_block = TRUE + block_being_removed_on += "spoken" + + if(ask_to_remove_block) + choice = tgui_alert(user, "Do you want to lift the blockage that's also preventing the language to be [block_being_removed_on.Join(" or ")]?", "[language_datum]", list("Yes", "No")) if(choice == "Yes") - language_holder.remove_blocked_language(language_datum, LANGUAGE_ALL) + language_holder.remove_blocked_language(language_datum, adding_flags, LANGUAGE_ALL) language_holder.grant_language(language_datum, adding_flags) if(is_admin) message_admins("[key_name_admin(user)] granted the [language_name] language to [key_name_admin(speaker)].") diff --git a/code/modules/mob/living/carbon/human/_species.dm b/code/modules/mob/living/carbon/human/_species.dm index 384439899ac..1811616359d 100644 --- a/code/modules/mob/living/carbon/human/_species.dm +++ b/code/modules/mob/living/carbon/human/_species.dm @@ -418,8 +418,10 @@ GLOBAL_LIST_EMPTY(features_by_species) human_who_gained_species.grant_language(language, UNDERSTOOD_LANGUAGE, LANGUAGE_SPECIES) for(var/language in gaining_holder.spoken_languages) human_who_gained_species.grant_language(language, SPOKEN_LANGUAGE, LANGUAGE_SPECIES) - for(var/language in gaining_holder.blocked_languages) - human_who_gained_species.add_blocked_language(language, LANGUAGE_SPECIES) + for(var/language in gaining_holder.blocked_speaking) + human_who_gained_species.add_blocked_language(language, SPOKEN_LANGUAGE, LANGUAGE_SPECIES) + for(var/language in gaining_holder.blocked_understanding) + human_who_gained_species.add_blocked_language(language, UNDERSTOOD_LANGUAGE, LANGUAGE_SPECIES) if(regenerate_icons) human_who_gained_species.regenerate_icons() @@ -474,8 +476,10 @@ GLOBAL_LIST_EMPTY(features_by_species) human.remove_language(language, UNDERSTOOD_LANGUAGE, LANGUAGE_SPECIES) for(var/language in losing_holder.spoken_languages) human.remove_language(language, SPOKEN_LANGUAGE, LANGUAGE_SPECIES) - for(var/language in losing_holder.blocked_languages) - human.remove_blocked_language(language, LANGUAGE_SPECIES) + for(var/language in losing_holder.blocked_speaking) + human.remove_blocked_language(language, SPOKEN_LANGUAGE, LANGUAGE_SPECIES) + for(var/language in losing_holder.blocked_understanding) + human.remove_blocked_language(language, UNDERSTOOD_LANGUAGE, LANGUAGE_SPECIES) SEND_SIGNAL(human, COMSIG_SPECIES_LOSS, src) diff --git a/code/modules/reagents/chemistry/reagents/drinks/alcohol_reagents.dm b/code/modules/reagents/chemistry/reagents/drinks/alcohol_reagents.dm index 5bdccb8fcfa..d617603cdfc 100644 --- a/code/modules/reagents/chemistry/reagents/drinks/alcohol_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/drinks/alcohol_reagents.dm @@ -353,12 +353,12 @@ /datum/reagent/consumable/ethanol/rum/aged/on_mob_metabolize(mob/living/drinker) . = ..() - drinker.add_blocked_language(subtypesof(/datum/language) - /datum/language/piratespeak, LANGUAGE_DRINK) + drinker.add_blocked_language(subtypesof(/datum/language) - /datum/language/piratespeak, source = LANGUAGE_DRINK) drinker.grant_language(/datum/language/piratespeak, source = LANGUAGE_DRINK) /datum/reagent/consumable/ethanol/rum/aged/on_mob_end_metabolize(mob/living/drinker) if(!QDELING(drinker)) - drinker.remove_blocked_language(subtypesof(/datum/language), LANGUAGE_DRINK) + drinker.remove_blocked_language(subtypesof(/datum/language), source = LANGUAGE_DRINK) drinker.remove_language(/datum/language/piratespeak, source = LANGUAGE_DRINK) return ..() diff --git a/code/modules/unit_tests/say.dm b/code/modules/unit_tests/say.dm index a845b6ccf98..6d09fe08715 100644 --- a/code/modules/unit_tests/say.dm +++ b/code/modules/unit_tests/say.dm @@ -90,7 +90,7 @@ var/surfer_quote = "surfing in the USA" host_mob.grant_language(/datum/language/beachbum, SPOKEN_LANGUAGE) // can speak but can't understand - host_mob.add_blocked_language(subtypesof(/datum/language) - /datum/language/beachbum, LANGUAGE_STONER) + host_mob.add_blocked_language(subtypesof(/datum/language) - /datum/language/beachbum, source = LANGUAGE_STONER) TEST_ASSERT_NOTEQUAL(surfer_quote, host_mob.translate_language(host_mob, /datum/language/beachbum, surfer_quote), "Language test failed. Mob was supposed to understand: [surfer_quote]") host_mob.grant_language(/datum/language/beachbum, ALL) // can now understand