diff --git a/code/__DEFINES/language.dm b/code/__DEFINES/language.dm index c2f7f343b949..8f9c76299bca 100644 --- a/code/__DEFINES/language.dm +++ b/code/__DEFINES/language.dm @@ -15,6 +15,7 @@ #define LANGUAGE_CURATOR "curator" #define LANGUAGE_GLAND "gland" #define LANGUAGE_HAT "hat" +#define LANGUAGE_QUIRK "quirk" #define LANGUAGE_MALF "malf" #define LANGUAGE_PIRATE "pirate" #define LANGUAGE_MASTER "master" diff --git a/code/__DEFINES/traits.dm b/code/__DEFINES/traits.dm index 08878e89b560..06c53494d35e 100644 --- a/code/__DEFINES/traits.dm +++ b/code/__DEFINES/traits.dm @@ -165,6 +165,8 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai #define TRAIT_ILLITERATE "illiterate" /// Mute. Can't talk. #define TRAIT_MUTE "mute" +/// Softspoken. Always whisper. +#define TRAIT_SOFTSPOKEN "softspoken" /// Gibs on death and slips like ice. #define TRAIT_CURSED "cursed" /// Emotemute. Can't... emote. diff --git a/code/_globalvars/lists/mobs.dm b/code/_globalvars/lists/mobs.dm index 26755614e46f..12927d237b59 100644 --- a/code/_globalvars/lists/mobs.dm +++ b/code/_globalvars/lists/mobs.dm @@ -94,6 +94,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 c13711db364d..88e92e2f93c5 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 f674f6ab265c..2b92c86569b9 100644 --- a/code/controllers/subsystem/processing/quirks.dm +++ b/code/controllers/subsystem/processing/quirks.dm @@ -29,7 +29,11 @@ PROCESSING_SUBSYSTEM_DEF(quirks) list("Prosthetic Limb", "Quadruple Amputee", "Body Purist"), list("Quadruple Amputee", "Paraplegic"), list("Quadruple Amputee", "Frail"), - list("Gigantism", "Dwarfism") + list("Gigantism", "Dwarfism"), + 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/negative_quirks.dm b/code/datums/quirks/negative_quirks.dm index 9e91d3f1c664..3a6f72acdc9b 100644 --- a/code/datums/quirks/negative_quirks.dm +++ b/code/datums/quirks/negative_quirks.dm @@ -459,6 +459,26 @@ quirk_holder.set_move_intent(MOVE_INTENT_WALK) quirk_holder.add_mood_event("nyctophobia", /datum/mood_event/nyctophobia) +/datum/quirk/softspoken + name = "Soft-Spoken" + desc = "You are soft-spoken, and your voice is hard to hear." + icon = FA_ICON_COMMENT + value = -2 + mob_trait = TRAIT_SOFTSPOKEN + gain_text = span_danger("You feel like you're speaking more quietly.") + lose_text = span_notice("You feel like you're speaking louder.") + medical_record_text = "Patient is soft-spoken and difficult to hear." + +/datum/quirk/clumsy + name = "Clumsy" + desc = "You're clumsy, a goofball, a silly dude. You big loveable himbo/bimbo you! Hope you weren't planning on using your hands for anything that takes even a LICK of dexterity." + icon = FA_ICON_FACE_DIZZY + value = -8 + mob_trait = TRAIT_CLUMSY + gain_text = span_danger("You feel your IQ sink like your brain is liquid.") + lose_text = span_notice("You feel like your IQ went up to at least average.") + medical_record_text = "Patient has demonstrated an extreme difficulty with high motor skill paired with an inability to demonstrate critical thinking." + /datum/quirk/nonviolent name = "Pacifist" desc = "The thought of violence makes you sick. So much so, in fact, that you can't hurt anyone." @@ -471,6 +491,17 @@ hardcore_value = 6 mail_goodies = list(/obj/effect/spawner/random/decoration/flower, /obj/effect/spawner/random/contraband/cannabis) // flower power +/datum/quirk/bighands + name = "Big Hands" + desc = "You have big hands, it sure does make it hard to use a lot of things." + icon = FA_ICON_HAND_DOTS + value = -6 + mob_trait = TRAIT_CHUNKYFINGERS + gain_text = span_danger("Your hands are huge! You can't use small things anymore!") + lose_text = span_notice("Your hands are back to normal.") + medical_record_text = "Patient has unusually large hands. Made me question my masculinity..." + hardcore_value = 5 + /datum/quirk/paraplegic name = "Paraplegic" desc = "Your legs do not function. Nothing will ever fix this. But hey, free wheelchair!" @@ -667,6 +698,8 @@ if(HAS_TRAIT(quirk_holder, TRAIT_FEARLESS)) return + if(HAS_TRAIT(source, TRAIT_SIGN_LANG)) // No modifiers for signers, so you're less anxious when you go non-verbal + return var/moodmod if(quirk_holder.mob_mood) diff --git a/code/datums/quirks/neutral_quirks.dm b/code/datums/quirks/neutral_quirks.dm index 16d10ce84423..4bc4c2168ab0 100644 --- a/code/datums/quirks/neutral_quirks.dm +++ b/code/datums/quirks/neutral_quirks.dm @@ -49,7 +49,7 @@ var/mob/living/carbon/human/human_holder = quirk_holder human_holder.add_blocked_language(/datum/language/common) if(ishumanbasic(human_holder)) - human_holder.grant_language(/datum/language/uncommon) + human_holder.grant_language(/datum/language/uncommon, understood = TRUE, spoken = TRUE, source = LANGUAGE_QUIRK) /datum/quirk/foreigner/remove() var/mob/living/carbon/human/human_holder = quirk_holder diff --git a/code/datums/quirks/positive_quirks.dm b/code/datums/quirks/positive_quirks.dm index 9cf8b6c352a1..673bc1007335 100644 --- a/code/datums/quirks/positive_quirks.dm +++ b/code/datums/quirks/positive_quirks.dm @@ -207,6 +207,31 @@ // We've either added or removed TRAIT_NIGHT_VISION before calling this proc. Just refresh the eyes. eyes.refresh() +/datum/quirk/bilingual + name = "Bilingual" + desc = "Over the years you've picked up an extra language!" + icon = FA_ICON_GLOBE + value = 4 + 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." + mail_goodies = list(/obj/item/taperecorder, /obj/item/clothing/head/frenchberet, /obj/item/clothing/mask/fakemoustache/italian) + +/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 + 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" desc = "You have some great posters! Hang them up and make everyone have a great time." diff --git a/code/modules/client/preferences/language.dm b/code/modules/client/preferences/language.dm new file mode 100644 index 000000000000..d6baec86ddfb --- /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 edbb6571f591..ac449c7794c9 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) @@ -80,7 +82,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 @@ -275,23 +277,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) @@ -303,7 +310,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/code/modules/mob/living/living_say.dm b/code/modules/mob/living/living_say.dm index 91632747496b..2e060ed92c74 100644 --- a/code/modules/mob/living/living_say.dm +++ b/code/modules/mob/living/living_say.dm @@ -142,6 +142,9 @@ GLOBAL_LIST_INIT(message_modes_stat_limits, list( say_dead(original_message) return + if(HAS_TRAIT(src, TRAIT_SOFTSPOKEN) && !HAS_TRAIT(src, TRAIT_SIGN_LANG)) // softspoken trait only applies to spoken languages + message_mods[WHISPER_MODE] = MODE_WHISPER + if(client && SSlag_switch.measures[SLOWMODE_SAY] && !HAS_TRAIT(src, TRAIT_BYPASS_MEASURES) && !forced && src == usr) if(!COOLDOWN_FINISHED(client, say_slowmode)) to_chat(src, span_warning("Message not sent due to slowmode. Please wait [SSlag_switch.slowmode_cooldown/10] seconds between messages.\n\"[message]\"")) diff --git a/tgstation.dme b/tgstation.dme index 9b14418a0428..da98c6207d65 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -3267,6 +3267,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 000000000000..6b1a1be1220b --- /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, +};