From a2c8cce5359162a8a697ce109801ec268bf0c8a5 Mon Sep 17 00:00:00 2001 From: John Willard <53777086+JohnFulpWillard@users.noreply.github.com> Date: Fri, 7 Jul 2023 19:26:35 -0400 Subject: [PATCH] Bilingual can now choose their language (#76609) ## About The Pull Request This was one of the tradeoffs for removing other, more consistent sources of languages, and was requested by Melbert among many others. This does go against my wanted goal of decreasing the risk of eavesdropping by other players through just magically knowing a language, but it is an expensive quirk and it is in their medical records, which makes it better than language encryption keys or silicon just innately knowing them. This also limits Bilingual to only roundstart languages (+Uncommon), rather than being randomly selected from a list (that had very useless ones like monkey, podpeople, and beachbum). This is mostly just for modularity, I didn't want to make it look terrible code-wise and thought this may be the optimal way to handle it. This is also me going back on https://github.com/tgstation/tgstation/pull/71773 - which I had closed myself. ## Why It's Good For The Game If we're gonna keep the Bilingual quirk, it might as well be something players can choose the language of, it's their character and they should be allowed to decide how their character is, and it is my fault that this stupid compromise of "getting a random language" was made in the first place. It never should've happened. It now actually limits it to roundstart-only languages, so there's no way you can spy on people who prepare in advance through becoming podpeople, or monkeys, etc. ## Changelog :cl: balance: Bilingual quirk now lets you choose your language between ones given to roundstart species. balance: Foreigner and Bilingual are now mutually exclusive languages. /:cl: --- code/_globalvars/lists/mobs.dm | 2 + code/controllers/subsystem/language.dm | 5 +- .../subsystem/processing/quirks.dm | 1 + code/datums/quirks/positive_quirks.dm | 50 +++++-------------- code/modules/client/preferences/language.dm | 34 +++++++++++++ .../mob/living/carbon/human/_species.dm | 17 +++++-- tgstation.dme | 1 + .../character_preferences/language.tsx | 6 +++ 8 files changed, 71 insertions(+), 45 deletions(-) create mode 100644 code/modules/client/preferences/language.dm create mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/language.tsx diff --git a/code/_globalvars/lists/mobs.dm b/code/_globalvars/lists/mobs.dm index 1c6b4b90bc6..6f7d9c987f7 100644 --- a/code/_globalvars/lists/mobs.dm +++ b/code/_globalvars/lists/mobs.dm @@ -57,6 +57,8 @@ GLOBAL_LIST_EMPTY(narcd_underages) GLOBAL_LIST_EMPTY(language_datum_instances) GLOBAL_LIST_EMPTY(all_languages) +///List of all languages ("name" = type) +GLOBAL_LIST_EMPTY(language_types_by_name) GLOBAL_LIST_EMPTY(sentient_disease_instances) diff --git a/code/controllers/subsystem/language.dm b/code/controllers/subsystem/language.dm index c13711db364..88e92e2f93c 100644 --- a/code/controllers/subsystem/language.dm +++ b/code/controllers/subsystem/language.dm @@ -4,15 +4,14 @@ SUBSYSTEM_DEF(language) flags = SS_NO_FIRE /datum/controller/subsystem/language/Initialize() - for(var/L in subtypesof(/datum/language)) - var/datum/language/language = L + for(var/datum/language/language as anything in subtypesof(/datum/language)) if(!initial(language.key)) continue GLOB.all_languages += language + GLOB.language_types_by_name[initial(language.name)] = language var/datum/language/instance = new language - GLOB.language_datum_instances[language] = instance return SS_INIT_SUCCESS diff --git a/code/controllers/subsystem/processing/quirks.dm b/code/controllers/subsystem/processing/quirks.dm index 000f5b9b64d..3a796c3f5b0 100644 --- a/code/controllers/subsystem/processing/quirks.dm +++ b/code/controllers/subsystem/processing/quirks.dm @@ -32,6 +32,7 @@ PROCESSING_SUBSYSTEM_DEF(quirks) list("Social Anxiety", "Mute"), list("Mute", "Soft-Spoken"), list("Stormtrooper Aim", "Big Hands"), + list("Bilingual", "Foreigner"), ) /datum/controller/subsystem/processing/quirks/Initialize() diff --git a/code/datums/quirks/positive_quirks.dm b/code/datums/quirks/positive_quirks.dm index a82315aacbe..4283afe69be 100644 --- a/code/datums/quirks/positive_quirks.dm +++ b/code/datums/quirks/positive_quirks.dm @@ -215,46 +215,22 @@ gain_text = span_notice("Some of the words of the people around you certainly aren't common. Good thing you studied for this.") lose_text = span_notice("You seem to have forgotten your second language.") medical_record_text = "Patient speaks multiple languages." - var/list/possible_languages = list( - /datum/language/aphasia, - /datum/language/beachbum, - /datum/language/calcic, - /datum/language/draconic, - /datum/language/moffic, - /datum/language/monkey, - /datum/language/mushroom, - /datum/language/nekomimetic, - /datum/language/piratespeak, - /datum/language/shadowtongue, - /datum/language/slime, - /datum/language/sylvan, - /datum/language/terrum, - /datum/language/voltaic, - ) - var/datum/language/extra_language mail_goodies = list(/obj/item/taperecorder, /obj/item/clothing/head/frenchberet, /obj/item/clothing/mask/fakemoustache/italian) -/datum/quirk/bilingual/add(client/client_source) - //prevents yourself from learning a language you already have - for(var/datum/language/spoken as anything in possible_languages) - if(quirk_holder.has_language(spoken)) - possible_languages -= spoken - if(!length(possible_languages)) - return - extra_language = pick(possible_languages) - quirk_holder.grant_language(extra_language, understood = TRUE, spoken = TRUE, source = LANGUAGE_QUIRK) - -/datum/quirk/bilingual/post_add() - if(extra_language) - to_chat(quirk_holder, span_info("From your bilingualism, you are additionally fluent in [initial(extra_language.name)].")) +/datum/quirk/bilingual/add_unique(client/client_source) + var/wanted_language = client_source?.prefs.read_preference(/datum/preference/choiced/language) + var/datum/language/language_type + if(wanted_language == "Random") + language_type = pick(GLOB.roundstart_languages) else - to_chat(quirk_holder, span_info("You are already fluent in all languages, making you far more than bilingual.")) - -/datum/quirk/bilingual/remove() - if(!extra_language) - return - - quirk_holder.remove_language(extra_language) + language_type = GLOB.language_types_by_name[wanted_language] + if(quirk_holder.has_language(language_type)) + language_type = /datum/language/uncommon + if(quirk_holder.has_language(language_type)) + to_chat(quirk_holder, span_boldnotice("You are already familiar with the quirk in your preferences, so you did not learn one.")) + return + to_chat(quirk_holder, span_boldnotice("You are already familiar with the quirk in your preferences, so you learned Galactic Uncommon instead.")) + quirk_holder.grant_language(language_type, source = LANGUAGE_QUIRK) /datum/quirk/item_quirk/poster_boy name = "Poster Boy" diff --git a/code/modules/client/preferences/language.dm b/code/modules/client/preferences/language.dm new file mode 100644 index 00000000000..d6baec86ddf --- /dev/null +++ b/code/modules/client/preferences/language.dm @@ -0,0 +1,34 @@ +/datum/preference/choiced/language + category = PREFERENCE_CATEGORY_SECONDARY_FEATURES + savefile_key = "language" + savefile_identifier = PREFERENCE_CHARACTER + +/datum/preference/choiced/language/is_accessible(datum/preferences/preferences) + if (!..(preferences)) + return FALSE + + return "Bilingual" in preferences.all_quirks + +/datum/preference/choiced/language/init_possible_values() + var/list/values = list() + + if(!GLOB.roundstart_languages.len) + generate_selectable_species_and_languages() + + values += "Random" + + //we add uncommon as it's foreigner-only. + var/datum/language/uncommon/uncommon_language = /datum/language/uncommon + values += initial(uncommon_language.name) + + for(var/datum/language/language_type as anything in GLOB.roundstart_languages) + if(ispath(language_type, /datum/language/common)) + continue + if(initial(language_type.name) in values) + continue + values += initial(language_type.name) + + return values + +/datum/preference/choiced/language/apply_to_human(mob/living/carbon/human/target, value) + return diff --git a/code/modules/mob/living/carbon/human/_species.dm b/code/modules/mob/living/carbon/human/_species.dm index ea4830e3e4d..c9e1a55615c 100644 --- a/code/modules/mob/living/carbon/human/_species.dm +++ b/code/modules/mob/living/carbon/human/_species.dm @@ -1,4 +1,6 @@ GLOBAL_LIST_EMPTY(roundstart_races) +///List of all roundstart languages by path +GLOBAL_LIST_EMPTY(roundstart_languages) /// An assoc list of species types to their features (from get_features()) GLOBAL_LIST_EMPTY(features_by_species) @@ -52,7 +54,7 @@ GLOBAL_LIST_EMPTY(features_by_species) ///flags for inventory slots the race can't equip stuff to. Golems cannot wear jumpsuits, for example. var/no_equip_flags ///What languages this species can understand and say. Use a [language holder datum][/datum/language_holder] in this var. - var/species_language_holder = /datum/language_holder + var/datum/language_holder/species_language_holder = /datum/language_holder /** * Visible CURRENT bodyparts that are unique to a species. * DO NOT USE THIS AS A LIST OF ALL POSSIBLE BODYPARTS AS IT WILL FUCK @@ -207,23 +209,28 @@ GLOBAL_LIST_EMPTY(features_by_species) RETURN_TYPE(/list) if (!GLOB.roundstart_races.len) - GLOB.roundstart_races = generate_selectable_species() + GLOB.roundstart_races = generate_selectable_species_and_languages() return GLOB.roundstart_races - /** * Generates species available to choose in character setup at roundstart * * This proc generates which species are available to pick from in character setup. * If there are no available roundstart species, defaults to human. */ -/proc/generate_selectable_species() +/proc/generate_selectable_species_and_languages() var/list/selectable_species = list() for(var/species_type in subtypesof(/datum/species)) var/datum/species/species = new species_type if(species.check_roundstart_eligible()) selectable_species += species.id + var/datum/language_holder/temp_holder = new species.species_language_holder + for(var/datum/language/spoken_languages as anything in temp_holder.understood_languages) + if(spoken_languages in GLOB.roundstart_languages) + continue + GLOB.roundstart_languages += spoken_languages + qdel(temp_holder) qdel(species) if(!selectable_species.len) @@ -235,7 +242,7 @@ GLOBAL_LIST_EMPTY(features_by_species) * Checks if a species is eligible to be picked at roundstart. * * Checks the config to see if this species is allowed to be picked in the character setup menu. - * Used by [/proc/generate_selectable_species]. + * Used by [/proc/generate_selectable_species_and_languages]. */ /datum/species/proc/check_roundstart_eligible() if(id in (CONFIG_GET(keyed_list/roundstart_races))) diff --git a/tgstation.dme b/tgstation.dme index 9e292a78546..3cc12d27771 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -3109,6 +3109,7 @@ #include "code\modules\client\preferences\hotkeys.dm" #include "code\modules\client\preferences\item_outlines.dm" #include "code\modules\client\preferences\jobless_role.dm" +#include "code\modules\client\preferences\language.dm" #include "code\modules\client\preferences\mod_select.dm" #include "code\modules\client\preferences\multiz_parallax.dm" #include "code\modules\client\preferences\multiz_performance.dm" diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/language.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/language.tsx new file mode 100644 index 00000000000..6b1a1be1220 --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/language.tsx @@ -0,0 +1,6 @@ +import { FeatureChoiced, FeatureDropdownInput } from '../base'; + +export const language: FeatureChoiced = { + name: 'Language', + component: FeatureDropdownInput, +};