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 4e4f07b0673..3886bca6789 100644 --- a/code/controllers/subsystem/language.dm +++ b/code/controllers/subsystem/language.dm @@ -4,17 +4,13 @@ SUBSYSTEM_DEF(language) flags = SS_NO_FIRE /datum/controller/subsystem/language/Initialize() - // SKYRAT EDIT START - if(!GLOB.all_languages.len) - for(var/L in subtypesof(/datum/language)) - var/datum/language/language = L - if(!initial(language.key)) - continue + for(var/datum/language/language as anything in subtypesof(/datum/language)) + if(!initial(language.key)) + continue - GLOB.all_languages += language + 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 - // SKYRAT EDIT END + 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 3e03e0eafe8..f1e710e6cb4 100644 --- a/code/controllers/subsystem/processing/quirks.dm +++ b/code/controllers/subsystem/processing/quirks.dm @@ -32,6 +32,7 @@ PROCESSING_SUBSYSTEM_DEF(quirks) list("Mute", "Social Anxiety"), list("Mute", "Soft-Spoken"), list("Stormtrooper Aim", "Big Hands"), + list("Bilingual", "Foreigner"), //SKYRAT EDIT ADDITION BEGIN list("Nerve Stapled", "Pacifist"), list("Nerve Stapled", "Nearsighted"), diff --git a/code/datums/quirks/positive_quirks.dm b/code/datums/quirks/positive_quirks.dm index 925dc8493fe..19f92111540 100644 --- a/code/datums/quirks/positive_quirks.dm +++ b/code/datums/quirks/positive_quirks.dm @@ -215,56 +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, - // SKYRAT ADD - /datum/language/skrell, - /datum/language/yangyu, - /datum/language/vox, - /datum/language/schechi, - /datum/language/siiktajr, - /datum/language/xerxian, - /datum/language/canilunzt, - /datum/language/panslavic, - // SKYRAT ADD END - ) - 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 824f463d265..d14ebe7b6cd 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) @@ -64,7 +66,7 @@ GLOBAL_LIST_EMPTY(features_by_species) ///Affects the speech message, for example: Motharula flutters, "My speech message is flutters!" var/say_mod = "says" ///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 @@ -221,23 +223,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) @@ -249,7 +256,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/modular_skyrat/master_files/code/controllers/subsystem/language.dm b/modular_skyrat/master_files/code/controllers/subsystem/language.dm new file mode 100644 index 00000000000..b546d4efe92 --- /dev/null +++ b/modular_skyrat/master_files/code/controllers/subsystem/language.dm @@ -0,0 +1,3 @@ +// We just want to make sure this runs before SSAssets so that the globals are set up correctly for language menu icons +/datum/controller/subsystem/language + init_order = INIT_ORDER_SECURITY_LEVEL diff --git a/modular_skyrat/master_files/code/modules/client/preferences/middleware/languages.dm b/modular_skyrat/master_files/code/modules/client/preferences/middleware/languages.dm index f3fed4e4050..22a0854e88d 100644 --- a/modular_skyrat/master_files/code/modules/client/preferences/middleware/languages.dm +++ b/modular_skyrat/master_files/code/modules/client/preferences/middleware/languages.dm @@ -10,16 +10,8 @@ var/list/to_insert = list() if(!GLOB.all_languages.len) - for(var/iterated_language in subtypesof(/datum/language)) - var/datum/language/language = iterated_language - if(!initial(language.key)) - continue - - GLOB.all_languages += language - - var/datum/language/instance = new language - - GLOB.language_datum_instances[language] = instance + stack_trace("Warning: Language spritesheets could not be created because language subsystem has not been loaded yet. This should not happen--adjust the init_order in master_files/code/controllers/subsystem/language.dm.") + return for (var/language_name in GLOB.all_languages) var/datum/language/language = GLOB.language_datum_instances[language_name] diff --git a/tgstation.dme b/tgstation.dme index 6b12903b91d..129a1b8ee4b 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -3184,6 +3184,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" @@ -5472,6 +5473,7 @@ #include "modular_skyrat\master_files\code\controllers\configuration\entries\skyrat_config_entries.dm" #include "modular_skyrat\master_files\code\controllers\subsystem\dbcore.dm" #include "modular_skyrat\master_files\code\controllers\subsystem\events.dm" +#include "modular_skyrat\master_files\code\controllers\subsystem\language.dm" #include "modular_skyrat\master_files\code\datums\ai_laws.dm" #include "modular_skyrat\master_files\code\datums\emotes.dm" #include "modular_skyrat\master_files\code\datums\ert.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, +};