diff --git a/code/__DEFINES/language.dm b/code/__DEFINES/language.dm index 2ee6ba37703..6c0affaffe2 100644 --- a/code/__DEFINES/language.dm +++ b/code/__DEFINES/language.dm @@ -2,8 +2,12 @@ #define TONGUELESS_SPEECH (1<<1) #define LANGUAGE_HIDE_ICON_IF_UNDERSTOOD (1<<2) #define LANGUAGE_HIDE_ICON_IF_NOT_UNDERSTOOD (1<<3) +/// This language can be selected in preferences. +#define ROUNDSTART_LANGUAGE (1<<4) /// This language is signed, not spoken. -#define SIGNED_LANGUAGE (1<<4) +#define SIGNED_LANGUAGE (1<<5) +/// Sarathi do not "sss" when speaking this language +#define NO_HISS (1<<6) // LANGUAGE SOURCE DEFINES /// For use in full removal only. diff --git a/code/__DEFINES/preferences.dm b/code/__DEFINES/preferences.dm index 131af782bf1..c5b37e2694d 100644 --- a/code/__DEFINES/preferences.dm +++ b/code/__DEFINES/preferences.dm @@ -128,5 +128,16 @@ #define PROSTHETIC_AMPUTATED "amputated" #define PROSTHETIC_ROBOTIC "prosthetic" +/// You cannot speak or understand this language whatsoever. +#define LANGUAGE_UNKNOWN "Unknown (0)" +/// You cannot speak this language, but can recognize some of the words. +#define LANGUAGE_RECOGNIZED "Recognized (1)" +/// You are familiar with this language enough to sort of speak it, but cannot understand it very well. +#define LANGUAGE_FAMILIAR "Familiar (2)" +/// You are fluent in this language, and can both understand and speak it perfectly. +#define LANGUAGE_FLUENT "Fluent (3)" +/// Maximum number of additional languages that can be selected. +#define MAX_LANGUAGE_POINTS 4 + #define NOT_SYNTHETIC FALSE #define IS_SYNTHETIC TRUE diff --git a/code/controllers/subsystem/overmap.dm b/code/controllers/subsystem/overmap.dm index 6c474ef77b2..6a8986a46bd 100644 --- a/code/controllers/subsystem/overmap.dm +++ b/code/controllers/subsystem/overmap.dm @@ -23,6 +23,9 @@ SUBSYSTEM_DEF(overmap) /// The mandatory and default star system var/datum/overmap_star_system/default_system + /// The secondary star system that allows planet spawns + var/datum/overmap_star_system/wild_system + ///Should events be processed var/events_enabled = TRUE @@ -41,8 +44,6 @@ SUBSYSTEM_DEF(overmap) //if(length(tracked_star_systems) >= 1) // CRASH("Attempted to create more than 1 star system. Having mutiple star systems is not supported.") - if(length(tracked_star_systems) >= 1) - WARNING("Attempted to create more than 1 star system. Bugs may occur as this isn't very well supported, you have been warned") tracked_star_systems += new_starsystem return new_starsystem @@ -56,7 +57,9 @@ SUBSYSTEM_DEF(overmap) dynamic_encounters = list() events = list() - default_system = create_new_star_system(new /datum/overmap_star_system/shiptest) + var/list/sector_types = pick(subtypesof(/datum/overmap_star_system/safezone)) + default_system = create_new_star_system(new sector_types) + wild_system = create_new_star_system (new /datum/overmap_star_system/shiptest) return ..() /datum/controller/subsystem/overmap/proc/spawn_new_star_system(datum/overmap_star_system/system_to_spawn=/datum/overmap_star_system) @@ -64,7 +67,6 @@ SUBSYSTEM_DEF(overmap) return create_new_star_system(system_to_spawn) return create_new_star_system(new system_to_spawn) - /datum/controller/subsystem/overmap/fire() for(var/datum/overmap_star_system/current_system as anything in tracked_star_systems) if(!current_system.encounters_refresh) @@ -345,6 +347,9 @@ SUBSYSTEM_DEF(overmap) //can our pallete be selected randomly roundstart? set to no for subtypes or if you dont change the pallete var/can_be_selected_randomly = TRUE + /// Datum type for the main outpost spawned here + var/default_outpost_type + COOLDOWN_DECLARE(dynamic_despawn_cooldown) /datum/overmap_star_system/New(generate_now=TRUE) @@ -371,7 +376,7 @@ SUBSYSTEM_DEF(overmap) if(!size) size = CONFIG_GET(number/overmap_size) if(!max_overmap_dynamic_events) - max_overmap_dynamic_events = CONFIG_GET(number/max_overmap_dynamic_events) + max_overmap_dynamic_events = isnull(max_overmap_dynamic_events) overmap_container = new/list(size, size, 0) @@ -516,24 +521,23 @@ SUBSYSTEM_DEF(overmap) /datum/overmap_star_system/proc/spawn_outpost() var/list/location = get_unused_overmap_square_in_radius(rand(4, round(size/5))) - var/datum/overmap/outpost/found_type if(fexists(OUTPOST_OVERRIDE_FILEPATH)) var/file_text = trim_right(file2text(OUTPOST_OVERRIDE_FILEPATH)) // trim_right because there's often a trailing newline var/datum/overmap/outpost/potential_type = text2path(file_text) if(!potential_type || !ispath(potential_type, /datum/overmap/outpost)) stack_trace("SSovermap found an outpost override file at [OUTPOST_OVERRIDE_FILEPATH], but was unable to find the outpost type [potential_type]!") else - found_type = potential_type + default_outpost_type = potential_type fdel(OUTPOST_OVERRIDE_FILEPATH) // don't want it to affect 2 rounds in a row. - if(!found_type) + if(!default_outpost_type) var/list/possible_types = subtypesof(/datum/overmap/outpost) for(var/datum/overmap/outpost/outpost_type as anything in possible_types) if(!initial(outpost_type.main_template)) possible_types -= outpost_type - found_type = pick(possible_types) + default_outpost_type = pick(possible_types) - var/datum/overmap/outpost/our_outpost = new found_type(location, src) + var/datum/overmap/outpost/our_outpost = new default_outpost_type(location, src) //gets rid of nearby events that casue radio interference for(var/direction as anything in GLOB.cardinals) @@ -970,11 +974,7 @@ SUBSYSTEM_DEF(overmap) override_object_colors = TRUE overmap_icon_state = "overmap" - dynamic_probabilities = list(\ - DYNAMIC_WORLD_BEACHPLANET = 10, - DYNAMIC_WORLD_SPACERUIN = 5, - DYNAMIC_WORLD_MOON = 20, - ) + max_overmap_dynamic_events = 0 /datum/overmap_star_system/zx_spectrum_pallete //main colors, used for dockable terrestrials, and background @@ -1067,9 +1067,11 @@ SUBSYSTEM_DEF(overmap) else datum_to_edit.token.add_filter("gloweffect", 5, list("type"="drop_shadow", "color"= "#808080", "size"=2, "offset"=1)) -/datum/overmap_star_system/ngr - name = "Gorlex Controlled - Ecbatana" +/datum/overmap_star_system/safezone/ngr + name = "Gorlex Controlled - Value of Public Works" starname = "Ecbatana" + startype = /datum/overmap/star/dwarf + default_outpost_type = /datum/overmap/outpost/ngr_rock //main colors, used for dockable terrestrials, and background primary_color = "#d9ad82" @@ -1086,6 +1088,69 @@ SUBSYSTEM_DEF(overmap) override_object_colors = TRUE overmap_icon_state = "overmap_dark" +/datum/overmap_star_system/safezone/clip + name = "CLIP Controlled - High-Pier" + starname = "Chana" + startype = /datum/overmap/star/dwarf/orange + default_outpost_type = /datum/overmap/outpost/clip_ocean + + //main colors, used for dockable terrestrials, and background + primary_color = "#6fa8de" + secondary_color = "#96b6d4" + + //hazard colors, used for the overmap hazards and sun + hazard_primary_color = "#d5e3f0" + hazard_secondary_color = "#96a6b5" + + //structure colors, used for ships and outposts/colonies + primary_structure_color = "#97dfe8" + secondary_structure_color = "#6fa8de" + + override_object_colors = TRUE + overmap_icon_state = "overmap_dark" + +/datum/overmap_star_system/safezone/trifuge + name = "Independent - Minya" + starname = "Aubaine" + startype = /datum/overmap/star/medium + default_outpost_type = /datum/overmap/outpost/indie_space + + //main colors, used for dockable terrestrials, and background + primary_color = "#5e5e5e" + secondary_color = "#242424" + + //hazard colors, used for the overmap hazards and sun + hazard_primary_color = "#b56060" + hazard_secondary_color = "#824242" + + //structure colors, used for ships and outposts/colonies + primary_structure_color = "#ffffff" + secondary_structure_color = "#ffffff" + + override_object_colors = TRUE + overmap_icon_state = "overmap" + +/datum/overmap_star_system/safezone/nt + name = "Nanotrasen Controlled - Persei-277" + starname = "Persei-277" + startype = /datum/overmap/star/medium + default_outpost_type = /datum/overmap/outpost/nanotrasen_ice + + //main colors, used for dockable terrestrials, and background + primary_color = "#7e8cd9" + secondary_color = "#33324a" + + //hazard colors, used for the overmap hazards and sun + hazard_primary_color = "#ededed" + hazard_secondary_color = "#7f7db0" + + //structure colors, used for ships and outposts/colonies + primary_structure_color = "#4272db" + secondary_structure_color = "#38a0eb" + + override_object_colors = TRUE + overmap_icon_state = "overmap_dark" + /datum/overmap_star_system/c64 //main colors, used for dockable terrestrials, and background @@ -1105,13 +1170,13 @@ SUBSYSTEM_DEF(overmap) //default shiptest overmap /datum/overmap_star_system/shiptest - has_outpost = TRUE + has_outpost = FALSE can_be_selected_randomly = FALSE encounters_refresh = TRUE + max_overmap_dynamic_events = 15 /datum/overmap_star_system/shiptest/create_map() . = ..() - set_station_name(starname) /datum/overmap_star_system/admin_sandbox name = "Admin Sandbox" diff --git a/code/controllers/subsystem/processing/quirks.dm b/code/controllers/subsystem/processing/quirks.dm index 0813bc6640b..04d796c09ba 100644 --- a/code/controllers/subsystem/processing/quirks.dm +++ b/code/controllers/subsystem/processing/quirks.dm @@ -24,16 +24,14 @@ PROCESSING_SUBSYSTEM_DEF(quirks) SetupQuirks() quirk_blacklist = list( - list("Blind","Nearsighted"), \ - list("Ageusia","Vegetarian","Deviant Tastes"), \ + list("Ananas Affinity","Ananas Aversion"), //PENTEST ADDED + list("Blind","Nearsighted"), + list("Ageusia","Vegetarian","Deviant Tastes"), list("Alcohol Tolerance","Light Drinker"), - list("Bad Touch", "Friendly"), \ - list("Self-Aware", "Congenital Analgesia"), \ - //list("(Language) Moth Pidgin", "(Language) Solarian International", "(Language) Teceti Unified Standard", "(Language) Kalixcian Common"), \ - //PENTEST EDITS BELOW - list("Ananas Affinity","Ananas Aversion"), \ - list("(Language) Moth Pidgin", "(Language) Terran International", "(Language) Teceti Unified Standard", "(Language) Draconic Common"), \ - ) + list("Bad Touch", "Friendly"), + list("Self-Aware", "Congenital Analgesia"), + list("Trilingual", "Monolingual"), + ) species_blacklist = list("Blood Deficiency" = list(SPECIES_IPC, SPECIES_JELLYPERSON, SPECIES_PLASMAMAN, SPECIES_VAMPIRE)) diff --git a/code/datums/traits/negative/monolingual.dm b/code/datums/traits/negative/monolingual.dm new file mode 100644 index 00000000000..e30b8454052 --- /dev/null +++ b/code/datums/traits/negative/monolingual.dm @@ -0,0 +1,4 @@ +/datum/quirk/monolingual + name = "Monolingual" + desc = "You're only fluent in your native language. (-2 language points)" + value = -1 diff --git a/code/datums/traits/neutral/languages.dm b/code/datums/traits/neutral/languages.dm deleted file mode 100644 index 816a518d927..00000000000 --- a/code/datums/traits/neutral/languages.dm +++ /dev/null @@ -1,97 +0,0 @@ -//Languages - not worth their own file - -// Kalixcian Common -/datum/quirk/lang_kalixcian - name = "(Language) Kalixcian Common" - desc = "You're fluent in Kalixcian Common." - value = 0 - gain_text = span_notice("You know Kalixcian Common.") - lose_text = span_danger("You forget Kalixcian Common.") - detectable = FALSE - -/datum/quirk/lang_kalixcian/add() - var/mob/living/carbon/human/knower = quirk_holder - knower.grant_language(/datum/language/kalixcian_common, source = LANGUAGE_MIND) - -/datum/quirk/lang_kalixcian/remove() - if(quirk_holder) - var/mob/living/carbon/human/knower = quirk_holder - knower.remove_language(/datum/language/kalixcian_common, source = LANGUAGE_MIND) - -// Teceti Unified Standard -/datum/quirk/lang_tuc - name = "(Language) Teceti Unified Standard" - desc = "You're fluent in Teceti Unified Standard." - value = 0 - gain_text = span_notice("You know Teceti Unified.") - lose_text = span_danger("You forget Teceti Unified.") - detectable = FALSE - -/datum/quirk/lang_tuc/add() - var/mob/living/carbon/human/knower = quirk_holder - knower.grant_language(/datum/language/teceti_unified, source = LANGUAGE_MIND) - -/datum/quirk/lang_tuc/remove() - if(quirk_holder) - var/mob/living/carbon/human/knower = quirk_holder - knower.remove_language(/datum/language/teceti_unified, source = LANGUAGE_MIND) - -// Solarian International -/datum/quirk/lang_solarian_international - name = "(Language) Solarian International" - desc = "You're fluent in Solarian International." - value = 0 - gain_text = span_notice("You know Solarian International.") - lose_text = span_danger("You forget Solarian International.") - detectable = FALSE - -/datum/quirk/lang_solarian_international/add() - var/mob/living/carbon/human/knower = quirk_holder - knower.grant_language(/datum/language/solarian_international, source = LANGUAGE_MIND) - -/datum/quirk/lang_solarian_international/remove() - if(quirk_holder) - var/mob/living/carbon/human/knower = quirk_holder - knower.remove_language(/datum/language/solarian_international, source = LANGUAGE_MIND) - -// Moth Pidgin -/datum/quirk/lang_moth - name = "(Language) Moth Pidgin" - desc = "You're fluent in Moth Pidgin." - gain_text = span_notice("You know Moth Pidgin.") - lose_text = span_danger("You forget Moth Pidgin.") - detectable = FALSE - -/datum/quirk/lang_moth/add() - var/mob/living/carbon/human/knower = quirk_holder - knower.grant_language(/datum/language/moffic, source = LANGUAGE_MIND) - -/datum/quirk/lang_moth/remove() - if(quirk_holder) - var/mob/living/carbon/human/knower = quirk_holder - knower.remove_language(/datum/language/moffic, source = LANGUAGE_MIND) - -// Sign Language -/datum/quirk/signer - name = "Signer" - desc = "You're fluent in Universal Sign Language and have translation gloves to communicate over radio." - value = 0 - gain_text = span_notice("You know Universal Sign Language.") - lose_text = span_danger("You forget Universal Sign Language.") - detectable = FALSE - -/datum/quirk/signer/add() - var/mob/living/carbon/human/knower = quirk_holder - knower.grant_language(/datum/language/sign_language, source = LANGUAGE_MIND) - // Give translation gloves so they can use sign language over radio - var/obj/item/clothing/gloves/radio/translator_gloves = new(get_turf(knower)) - if(!knower.equip_to_slot_if_possible(translator_gloves, ITEM_SLOT_GLOVES, disable_warning = TRUE)) - // If they can't equip them (already wearing gloves), put them in hands or drop nearby - if(!knower.put_in_hands(translator_gloves)) - translator_gloves.forceMove(get_turf(knower)) - to_chat(knower, span_notice("A pair of translation gloves appears at your feet.")) - -/datum/quirk/signer/remove() - if(quirk_holder) - var/mob/living/carbon/human/knower = quirk_holder - knower.remove_language(/datum/language/sign_language, source = LANGUAGE_MIND) diff --git a/code/datums/traits/positive/trilingual.dm b/code/datums/traits/positive/trilingual.dm new file mode 100644 index 00000000000..a7492c430c0 --- /dev/null +++ b/code/datums/traits/positive/trilingual.dm @@ -0,0 +1,4 @@ +/datum/quirk/trilingual + name = "Trilingual" + desc = "You're fluent in three languages. (+2 language points)" + value = 1 diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm index 29d69256e75..e34377749e1 100644 --- a/code/game/atoms_movable.dm +++ b/code/game/atoms_movable.dm @@ -1205,7 +1205,10 @@ /// Gets a lazylist of all mutually understood languages. /atom/movable/proc/get_partially_understood_languages() - return get_language_holder().best_mutual_languages + var/datum/language_holder/our_holder = get_language_holder() + if(!our_holder.best_mutual_languages) + our_holder.calculate_best_mutual_language() + return our_holder.best_mutual_languages /// Gets a random spoken language, useful for forced speech and such. /atom/movable/proc/get_random_spoken_language() diff --git a/code/game/objects/effects/contraband.dm b/code/game/objects/effects/contraband.dm index 35f620fbf20..a70ec9ca0b0 100644 --- a/code/game/objects/effects/contraband.dm +++ b/code/game/objects/effects/contraband.dm @@ -788,7 +788,7 @@ /obj/structure/sign/poster/retro/radio name = "Radio" - desc = "A poster advertising one of Nanotrasen's earliest products, a radio. One of its main selling points was a integrated OS and two way automatic translation for Solarian Common, and Kalixcian Common, which made it a smash hit. This thing is ancient." + desc = "A poster advertising one of Nanotrasen's earliest products, a radio. One of its main selling points was a integrated OS and two way automatic translation for Solarian Common and Gezenan, which made it a smash hit. This thing is ancient." icon_state = "poster-radio70_retro" //Safety moth posters, credit to AspEv for the art which the below posters are based on and to Ayy-Robotics for the sprites. diff --git a/code/modules/admin/verbs/randomverbs.dm b/code/modules/admin/verbs/randomverbs.dm index 820c5ed62a7..a564e868ad9 100644 --- a/code/modules/admin/verbs/randomverbs.dm +++ b/code/modules/admin/verbs/randomverbs.dm @@ -961,7 +961,7 @@ nova.size = inputed inputed = input(usr, "Choose Maximum amount of Dynamic Events", "Spawn Overmap", nova.max_overmap_dynamic_events) as num - if(!inputed) + if(isnull(inputed)) QDEL_NULL(nova) return nova.max_overmap_dynamic_events = inputed diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm index bd89cf70c77..9bf04fad3bf 100644 --- a/code/modules/client/preferences.dm +++ b/code/modules/client/preferences.dm @@ -163,6 +163,15 @@ GLOBAL_LIST_EMPTY(preferences_datums) //Job preferences 2.0 - indexed by job title , no key or value implies never var/list/job_preferences = list() + /// Languages this character knows besides their native language. + var/list/learned_languages = list() + + /// This character's native language. + var/datum/language/native_language = /datum/language/galactic_common + + /// Associated list with language levels of understanding and their point costs. + var/static/list/language_level_costs = list(LANGUAGE_UNKNOWN = 0, LANGUAGE_RECOGNIZED = 1, LANGUAGE_FAMILIAR = 2, LANGUAGE_FLUENT = 3) + // 0 = character settings, 1 = game preferences var/current_tab = 0 @@ -359,6 +368,18 @@ GLOBAL_LIST_EMPTY(preferences_datums) dat += "Custom Job Preferences:
" dat += "Preferred AI Core Display: [preferred_ai_core_display]
" + dat += "

Languages

" + dat += "" + dat += "" + if(!learned_languages?.len) + init_learned_languages() + for(var/datum/language/lang_type as anything in learned_languages) + if(lang_type == native_language) + continue + dat += "" + dat += "" + dat += "
[get_language_point_balance()] points left.
Native Language: [initial(native_language.name)]
[initial(lang_type.name)]: [learned_languages[lang_type]]
Reset Languages
" + dat += "" dat += "

Clothing

" @@ -1502,6 +1523,24 @@ GLOBAL_LIST_EMPTY(preferences_datums) if(SSquirks.quirk_points[q] > 0) .++ +/datum/preferences/proc/init_learned_languages() + learned_languages = list() + for(var/datum/language/lang_type as anything in subtypesof(/datum/language)) + if(initial(lang_type.flags) & ROUNDSTART_LANGUAGE) + learned_languages[lang_type] = LANGUAGE_UNKNOWN + +/datum/preferences/proc/get_language_point_balance() + var/points_balance = MAX_LANGUAGE_POINTS + for(var/datum/language/lang_type as anything in learned_languages) + if(lang_type == native_language) + continue // this should happen but just in case + points_balance -= language_level_costs[learned_languages[lang_type]] + if("Trilingual" in all_quirks) + points_balance += 2 + if("Monolingual" in all_quirks) + points_balance -= 2 + return points_balance + /datum/preferences/Topic(href, href_list, hsrc) //yeah, gotta do this I guess.. . = ..() if(href_list["close"]) @@ -1575,6 +1614,18 @@ GLOBAL_LIST_EMPTY(preferences_datums) if(balance + value < 0) to_chat(user, span_warning("Refunding this would cause you to go below your balance!")) return + // PENTEST ADDITION - START - Check if removing Trilingual or adding Monolingual would cause negative language points + if(quirk == "Trilingual" || quirk == "Monolingual") + var/temp_quirks = all_quirks.Copy() + temp_quirks -= quirk + var/old_all_quirks = all_quirks + all_quirks = temp_quirks + var/lang_balance = get_language_point_balance() + all_quirks = old_all_quirks + if(lang_balance < 0) + to_chat(user, span_warning("You cannot remove this quirk as it would leave you with insufficient language points! Reset your languages first.")) + return + // PENTEST ADDITION - END all_quirks -= quirk else var/is_positive_quirk = SSquirks.quirk_points[quirk] > 0 @@ -1584,6 +1635,18 @@ GLOBAL_LIST_EMPTY(preferences_datums) if(balance - value < 0) to_chat(user, span_warning("You don't have enough balance to gain this quirk!")) return + // PENTEST ADDITION - START - Check if adding Monolingual or removing Trilingual would cause negative language points + if(quirk == "Trilingual" || quirk == "Monolingual") + var/temp_quirks = all_quirks.Copy() + temp_quirks += quirk + var/old_all_quirks = all_quirks + all_quirks = temp_quirks + var/lang_balance = get_language_point_balance() + all_quirks = old_all_quirks + if(lang_balance < 0) + to_chat(user, span_warning("You cannot select this quirk as it would leave you with insufficient language points! Reset your languages first.")) + return + // PENTEST ADDITION - END all_quirks += quirk SetQuirks(user) if("reset") @@ -2098,6 +2161,34 @@ GLOBAL_LIST_EMPTY(preferences_datums) if(ai_core_icon) preferred_ai_core_display = ai_core_icon + if("native_language") + var/list/language_list = list() + for(var/datum/language/lang_type as anything in learned_languages) + language_list[initial(lang_type.name)] = lang_type + var/datum/language/new_lang = language_list[tgui_input_list(user, "Select a native language:", "Native Language", language_list)] + if(ispath(new_lang, /datum/language) && (initial(new_lang.flags) & ROUNDSTART_LANGUAGE)) // double-check to prevent exploits to gain codespeak or something as a native language + native_language = new_lang + learned_languages[new_lang] = LANGUAGE_UNKNOWN + + if("learned_language") + var/datum/language/selected_lang = locate(href_list["language"]) + if(selected_lang.type == native_language) // wuh oh + CRASH("[usr] attempted to change level of understanding for [selected_lang] despite it being their native language!") + if(selected_lang && (selected_lang.flags & ROUNDSTART_LANGUAGE)) // no using html exploits to learn codespeak + var/understanding = tgui_input_list(user, "Select level of understanding:", "Learn Language", language_level_costs) + if(!understanding) + return + if(!(understanding in language_level_costs)) + CRASH("[usr] attempted to set level of understanding for [selected_lang.type] to \"[understanding]\"") + var/old_value = learned_languages[selected_lang.type] + learned_languages[selected_lang.type] = understanding + if(get_language_point_balance() < 0 && understanding != LANGUAGE_UNKNOWN) // in case something breaks REAL bad, you can still disable languages to fix it + learned_languages[selected_lang.type] = old_value + to_chat(usr, span_warning("You don't have enough language points!")) + + if("reset_languages") + init_learned_languages() + if ("clientfps") var/desiredfps = input(user, "Choose your desired fps. (0 = default, 60 FPS))", "Character Preference", clientfps) as null|num //WS Edit - Client FPS Tweak - if (!isnull(desiredfps)) @@ -2566,6 +2657,36 @@ GLOBAL_LIST_EMPTY(preferences_datums) character.update_body_parts(TRUE) character.dna.update_body_size() + if(!character_setup && get_language_point_balance() < 0) + // PENTEST ADDITON - START - This is a check to prevent exploits where players can gain more languages than they should be able to. If this happens, it will reset their languages and log it to admins. + log_admin("WARNING: [parent?.ckey] spawned with negative language points. Resetting languages.") + message_admins("WARNING: [key_name_admin(parent)] spawned with negative language points. Languages have been reset.") + // PENTEST ADDITION - END + init_learned_languages() // no exploits allowed + else if(get_language_point_balance() < 0) + // Pentest ADDITION - START - Additional check during character setup + log_admin("WARNING: [parent?.ckey] in character setup with negative language points. Resetting languages.") + message_admins("WARNING: [key_name_admin(parent)] in character setup with negative language points. Languages have been reset.") + init_learned_languages() + // PENTEST ADDITION - END + character.grant_language(native_language) + character.get_language_holder().selected_language = native_language + for(var/datum/language/lang_type as anything in learned_languages) + if(lang_type == native_language) + continue + switch(learned_languages[lang_type]) + if(LANGUAGE_FLUENT) + character.grant_language(lang_type) + if(LANGUAGE_FAMILIAR) + character.grant_language(lang_type, SPOKEN_LANGUAGE) + character.remove_language(lang_type, UNDERSTOOD_LANGUAGE) + character.grant_partial_language(lang_type, 80) + if(LANGUAGE_RECOGNIZED) + character.remove_language(lang_type) + character.grant_partial_language(lang_type, 40) + if(LANGUAGE_UNKNOWN) + character.remove_language(lang_type) + /datum/preferences/proc/get_default_name(name_id) switch(name_id) if("ai") diff --git a/code/modules/client/preferences_savefile.dm b/code/modules/client/preferences_savefile.dm index d29c6113145..17bd8fd7046 100644 --- a/code/modules/client/preferences_savefile.dm +++ b/code/modules/client/preferences_savefile.dm @@ -438,6 +438,10 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car READ_FILE(S["body_size"], features["body_size"]) READ_FILE(S["prosthetic_limbs"], prosthetic_limbs) prosthetic_limbs ||= list(BODY_ZONE_L_ARM = PROSTHETIC_NORMAL, BODY_ZONE_R_ARM = PROSTHETIC_NORMAL, BODY_ZONE_L_LEG = PROSTHETIC_NORMAL, BODY_ZONE_R_LEG = PROSTHETIC_NORMAL) + READ_FILE(S["learned_languages"], learned_languages) + if(!learned_languages?.len) init_learned_languages() + READ_FILE(S["native_language"], native_language) + native_language ||= /datum/language/galactic_common READ_FILE(S["feature_mcolor"], features["mcolor"]) READ_FILE(S["feature_mcolor2"], features["mcolor2"]) READ_FILE(S["feature_ethcolor"], features["ethcolor"]) @@ -502,6 +506,13 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car //Quirks READ_FILE(S["all_quirks"], all_quirks) + var/list/removed_quirks = list() + for(var/quirk_name in all_quirks.Copy()) + if(!(quirk_name in SSquirks.quirks)) + all_quirks.Remove(quirk_name) + removed_quirks.Add(quirk_name) + if(removed_quirks.len) + to_chat(parent, "Some of your previously selected quirks have been removed: [english_list(removed_quirks)].") //Flavor Text S["feature_flavor_text"] >> features["flavor_text"] @@ -599,6 +610,15 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car /datum/preferences/proc/save_character() if(!path) return FALSE + + // PENTEST ADDITION - START - Validate language points before saving + var/lang_balance = get_language_point_balance() + if(lang_balance < 0) + log_admin("WARNING: [parent?.ckey] attempted to save character with negative language points ([lang_balance]). Resetting languages.") + message_admins("WARNING: [key_name_admin(parent)] attempted to save character with negative language points ([lang_balance]). Languages have been reset.") + init_learned_languages() + // PENTEST ADDITION - END + var/savefile/S = new /savefile(path) if(!S) return FALSE @@ -631,6 +651,8 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car WRITE_FILE(S["generic_adjective"] , generic_adjective) WRITE_FILE(S["body_size"] , features["body_size"]) WRITE_FILE(S["prosthetic_limbs"] , prosthetic_limbs) + WRITE_FILE(S["learned_languages"] , learned_languages) + WRITE_FILE(S["native_language"] , native_language) WRITE_FILE(S["feature_mcolor"] , features["mcolor"]) WRITE_FILE(S["feature_mcolor2"] , features["mcolor2"]) WRITE_FILE(S["feature_ethcolor"] , features["ethcolor"]) diff --git a/code/modules/clothing/outfits/factions/gezena.dm b/code/modules/clothing/outfits/factions/gezena.dm index ce24f187e64..9b74156b9ab 100644 --- a/code/modules/clothing/outfits/factions/gezena.dm +++ b/code/modules/clothing/outfits/factions/gezena.dm @@ -3,12 +3,6 @@ faction = FACTION_PLAYER_GEZENA // faction_icon = "bg_pgf" -/datum/outfit/job/gezena/post_equip(mob/living/carbon/human/H, visualsOnly) - . = ..() - if(visualsOnly) - return - H.grant_language(/datum/language/kalixcian_common) - //Playable Roles (put in ships): /datum/outfit/job/gezena/assistant name = "PGF - Crewman" diff --git a/code/modules/clothing/outfits/factions/solgov.dm b/code/modules/clothing/outfits/factions/solgov.dm index 5663a9655dd..d78880cd965 100644 --- a/code/modules/clothing/outfits/factions/solgov.dm +++ b/code/modules/clothing/outfits/factions/solgov.dm @@ -3,12 +3,6 @@ faction = FACTION_PLAYER_SOLCON faction_icon = "bg_solgov" -/datum/outfit/job/solgov/post_equip(mob/living/carbon/human/H, visualsOnly) - . = ..() - if(visualsOnly) - return - H.grant_language(/datum/language/solarian_international) - /datum/outfit/job/solgov/assistant name = "SolGov - Scribe" id_assignment = "Scribe" diff --git a/code/modules/faction/faction_datum.dm b/code/modules/faction/faction_datum.dm index 7d8d58c6290..30b9ad6bcf7 100644 --- a/code/modules/faction/faction_datum.dm +++ b/code/modules/faction/faction_datum.dm @@ -8,6 +8,8 @@ var/list/prefixes /// List/Typecache of factions that this faction is allowed to interact with. Non-recursive. var/list/allowed_factions = list() + /// The official language of this faction. Galactic Common by default. + var/official_language = /datum/language/galactic_common /// Theme color for this faction, currently only used for the wiki var/color = "#ffffff" /// Contrast color for this faction, used for links on the wiki @@ -68,6 +70,7 @@ /datum/faction/syndicate/suns name = FACTION_SUNS short_name = "SUNS" + official_language = /datum/language/solarian_international prefixes = PREFIX_SUNS color = "#CD94D3" @@ -79,6 +82,7 @@ /datum/faction/solgov name = FACTION_SOLCON parent_faction = /datum/faction/solgov + official_language = /datum/language/solarian_international prefixes = PREFIX_SOLCON color = "#444e5f" @@ -132,6 +136,7 @@ name = FACTION_PGF short_name = "PGF" parent_faction = /datum/faction/pgf + official_language = /datum/language/gezena_kalixcian prefixes = PREFIX_PGF color = "#359829" diff --git a/code/modules/language/buzzwords.dm b/code/modules/language/buzzwords.dm deleted file mode 100644 index ad41497b80d..00000000000 --- a/code/modules/language/buzzwords.dm +++ /dev/null @@ -1,15 +0,0 @@ -/datum/language/buzzwords - name = "Buzzwords" - desc = "A common language innate to all bugs, made by the rhythmic beating of wings." - speech_verb = "buzzes" - ask_verb = "buzzes" - exclaim_verb = "loudly buzzes" - sing_verb = "hums" - flags = LANGUAGE_HIDE_ICON_IF_NOT_UNDERSTOOD - key = "z" - space_chance = 0 - syllables = list( - "bzz","zzz","z","bz","bzzz","zzzz", "bzzzz", "b", "zz", "zzzzz" - ) - icon_state = "buzz" - default_priority = 90 diff --git a/code/modules/language/common.dm b/code/modules/language/common.dm index 9bcc2e07b08..ce4f0ce4a18 100644 --- a/code/modules/language/common.dm +++ b/code/modules/language/common.dm @@ -3,14 +3,15 @@ name = "Galactic Common" desc = "The common galactic tongue." key = "0" - flags = TONGUELESS_SPEECH | LANGUAGE_HIDE_ICON_IF_UNDERSTOOD + flags = TONGUELESS_SPEECH | LANGUAGE_HIDE_ICON_IF_UNDERSTOOD | ROUNDSTART_LANGUAGE default_priority = 100 icon_state = "galcom" mutual_understanding = list( /datum/language/solarian_international = 20, - /datum/language/kalixcian_common = 20, + /datum/language/gezena_kalixcian = 20, + /datum/language/zohil_kalixcian = 15, // similar to gezenan ) diff --git a/code/modules/language/draconic.dm b/code/modules/language/gezenan.dm similarity index 69% rename from code/modules/language/draconic.dm rename to code/modules/language/gezenan.dm index 710beb6600a..d1e1699960e 100644 --- a/code/modules/language/draconic.dm +++ b/code/modules/language/gezenan.dm @@ -1,12 +1,12 @@ -/datum/language/kalixcian_common - name = "Kalixcian Common" - desc = "The most prevalent language to come out of Kalixcis, and generally understood by all those native to it." +/datum/language/gezena_kalixcian + name = "Gezenan" + desc = "The most widely spoken Kalixcian language, and the official language of the Pan-Gezena Federation." speech_verb = "hisses" ask_verb = "hisses" exclaim_verb = "roars" sing_verb = "sings" key = "o" - flags = TONGUELESS_SPEECH | LANGUAGE_HIDE_ICON_IF_NOT_UNDERSTOOD + flags = TONGUELESS_SPEECH | LANGUAGE_HIDE_ICON_IF_NOT_UNDERSTOOD | ROUNDSTART_LANGUAGE | NO_HISS space_chance = 12 sentence_chance = 0 between_word_sentence_chance = 10 @@ -20,7 +20,11 @@ "ka", "ak", "ke", "ek", "ki", "ik", "ko", "ok", "ku", "uk", "ks", "sk", "sa", "as", "se", "es", "si", "is", "so", "os", "su", "us", "ss", "ss", "ra", "ar", "re", "er", "ri", "ir", "ro", "or", "ru", "ur", "rs", "sr", + "na", "an", "ne", "en", "ni", "in", "no", "on", "nu", "un", "ng", "ts", "a", "a", "e", "e", "i", "i", "o", "o", "u", "u", "s", "s" ) icon_state = "lizard" default_priority = 90 + mutual_understanding = list( + /datum/language/zohil_kalixcian = 90, // enough to sort of understand each other, but not perfectly + ) diff --git a/code/modules/language/language.dm b/code/modules/language/language.dm index b7b66dd4b23..76c9ef38388 100644 --- a/code/modules/language/language.dm +++ b/code/modules/language/language.dm @@ -2,6 +2,12 @@ #define SCRAMBLE_CACHE_LEN 50 /// Last 20 spoken sentences will be cached before we start cycling them out (re-randomizing them) #define SENTENCE_CACHE_LEN 20 +/// Number of hex characters the MD5 will use to scramble text (16**SCRAMBLE_HASH_SIZE must not exceed 2**32) +#define SCRAMBLE_HASH_SIZE 4 +/// Probability check using MD5 hash +#define HASH_PROB(probability, raw_hash, hash_offset) (probability >= 100 ? TRUE : (probability / 100 >= hex2num(copytext(raw_hash, hash_offset, hash_offset + SCRAMBLE_HASH_SIZE)) / ((16**SCRAMBLE_HASH_SIZE) - 1))) +/// Pick from a list using MD5 hash (will cause problems if SCRAMBLE_HASH_SIZE is too low) +#define HASH_PICK(list, raw_hash, hash_offset) (list?.len ? list[1 + hex2num(copytext(raw_hash, hash_offset, hash_offset + SCRAMBLE_HASH_SIZE)) % list.len] : null) /// Datum based languages. Easily editable and modular. /datum/language @@ -18,6 +24,8 @@ var/list/syllables /// List of characters that will randomly be inserted between syllables. var/list/special_characters + /// Likelihood of inserting special characters between syllables. + var/special_character_chance = 0 // These modify how syllables are combined. /// Likelihood of making a new sentence after each syllable. @@ -126,7 +134,8 @@ /// Checks whether we should display the language icon to the passed hearer. /datum/language/proc/display_icon(atom/movable/hearer) - var/understands = hearer.has_language(src.type) + var/list/partial_understanding = hearer.get_partially_understood_languages() + var/understands = hearer.has_language(type) || (partial_understanding?[type] >= 50) if((flags & LANGUAGE_HIDE_ICON_IF_UNDERSTOOD) && understands) return FALSE if((flags & LANGUAGE_HIDE_ICON_IF_NOT_UNDERSTOOD) && !understands) @@ -274,16 +283,16 @@ for(var/word in splittext(input, " ")) var/translate_prob = mutual_languages?[type] || 0 var/base_word = strip_outer_punctuation(word) + var/raw_hash = md5("[lowertext(base_word)]/[GLOB.round_id]") if(translate_prob > 0) // the probability of managing to understand a word is based on how common it is (+10%, -15%) // 1000 words in the list, so words outside the list are just treated as "the 1250th most common word" var/commonness = GLOB.most_common_words[lowertext(base_word)] || 1250 translate_prob += (10 * (1 - (min(commonness, 1250) / 500))) - if(prob(translate_prob)) + if(HASH_PROB(translate_prob, raw_hash, 1)) scrambled_words += word translated_index += FALSE continue - var/scrambled_word = scramble_word(base_word) scrambled_words += scrambled_word translated_index += (scrambled_word != base_word) @@ -296,11 +305,12 @@ if(!translated_index[i]) sentence += " [word]" continue + var/raw_hash = md5("[lowertext(word)]/[lowertext(scrambled_words[i - 1])]/[GLOB.round_id]") // if the last word was scrambled, always include a space - if(translated_index[i - 1] || prob(between_word_space_chance)) + if(translated_index[i - 1] || HASH_PROB(between_word_space_chance, raw_hash, 1)) sentence += " " // lastly try inserting a new sentence - else if(prob(between_word_sentence_chance)) + else if(HASH_PROB(between_word_sentence_chance, raw_hash, 1 + SCRAMBLE_HASH_SIZE)) sentence += ". " word = capitalize(word) @@ -333,30 +343,29 @@ var/add_period = FALSE word = "" while(length_char(word) < input_size) + // uses the MD5 hash to make sure each word is always scrambled to the same thing within a given round + var/raw_hash = md5("[lowertext(input)]/[lowertext(word)]/[GLOB.round_id]") // add in the last syllable's period or space first if(add_period) word += ". " else if(add_space) word += " " // insert special chars if we're not at the start of the word - else if(word && prob(1) && length(special_characters)) - word += pick(special_characters) + else if(word && HASH_PROB(special_character_chance, raw_hash, 1) && length(special_characters)) + word += HASH_PICK(special_characters, raw_hash, 1 + SCRAMBLE_HASH_SIZE) // generate the next syllable (capitalize if we just added a period) - var/next = pick_weight_recursive(syllables) + var/next = HASH_PICK(syllables, raw_hash, 1 + SCRAMBLE_HASH_SIZE * 2) word += add_period ? capitalize(next) : next // determine if the next syllable gets a period or space - add_period = prob(sentence_chance) - add_space = prob(space_chance) + add_period = HASH_PROB(sentence_chance, raw_hash, 1 + SCRAMBLE_HASH_SIZE * 3) + add_space = HASH_PROB(space_chance, raw_hash, 1 + SCRAMBLE_HASH_SIZE * 4) write_word_cache(input, word) // If they're shouting, we're shouting return (is_uppercase(input) && length_char(input) >= 2) ? uppertext(word) : word -/** - * Called from mob/living/say() - */ -/datum/language/proc/on_say(atom/movable/speaker, message, bubble_type, list/spans = list(), datum/language/language = null) - return - +#undef HASH_PICK +#undef HASH_PROB +#undef SCRAMBLE_HASH_SIZE #undef SCRAMBLE_CACHE_LEN diff --git a/code/modules/language/language_holder.dm b/code/modules/language/language_holder.dm index abaa89e71c7..d78a3b9a7d9 100644 --- a/code/modules/language/language_holder.dm +++ b/code/modules/language/language_holder.dm @@ -362,7 +362,7 @@ GLOBAL_LIST_INIT(prototype_language_holders, init_language_holder_prototypes()) spoken_languages = list(/datum/language/galactic_common = list(LANGUAGE_ATOM)) /datum/language_holder/lizard/ash - selected_language = /datum/language/kalixcian_common + selected_language = /datum/language/gezena_kalixcian /datum/language_holder/monkey understood_languages = list(/datum/language/galactic_common = list(LANGUAGE_ATOM), @@ -381,7 +381,7 @@ GLOBAL_LIST_INIT(prototype_language_holders, init_language_holder_prototypes()) /datum/language_holder/synthetic understood_languages = list(/datum/language/galactic_common = list(LANGUAGE_ATOM), /datum/language/machine = list(LANGUAGE_ATOM), - /datum/language/kalixcian_common = list(LANGUAGE_ATOM), + /datum/language/gezena_kalixcian = list(LANGUAGE_ATOM), /datum/language/moffic = list(LANGUAGE_ATOM), /datum/language/rachnidian = list(LANGUAGE_ATOM), /datum/language/teceti_unified = list(LANGUAGE_ATOM), @@ -389,7 +389,7 @@ GLOBAL_LIST_INIT(prototype_language_holders, init_language_holder_prototypes()) /datum/language/sign_language = list(LANGUAGE_ATOM)) spoken_languages = list(/datum/language/galactic_common = list(LANGUAGE_ATOM), /datum/language/machine = list(LANGUAGE_ATOM), - /datum/language/kalixcian_common = list(LANGUAGE_ATOM), + /datum/language/gezena_kalixcian = list(LANGUAGE_ATOM), /datum/language/moffic = list(LANGUAGE_ATOM), /datum/language/teceti_unified = list(LANGUAGE_ATOM), /datum/language/solarian_international = list(LANGUAGE_ATOM), diff --git a/code/modules/language/language_menu.dm b/code/modules/language/language_menu.dm index 33b96171c9f..11d76c9e75e 100644 --- a/code/modules/language/language_menu.dm +++ b/code/modules/language/language_menu.dm @@ -23,6 +23,7 @@ var/list/data = list() var/atom/movable/speaker = language_holder.owner + var/list/partial_languages = speaker?.get_partially_understood_languages() data["languages"] = list() for(var/datum/language/language as anything in GLOB.all_languages) var/list/lang_data = list() @@ -37,6 +38,7 @@ lang_data["can_speak"] = !!speaker.has_language(language, SPOKEN_LANGUAGE) lang_data["could_speak"] = !!(language_holder.omnitongue || speaker.could_speak_language(language)) lang_data["can_understand"] = !!speaker.has_language(language, UNDERSTOOD_LANGUAGE) + lang_data["partial_understanding"] = partial_languages?[language] || 0 UNTYPED_LIST_ADD(data["languages"], lang_data) @@ -80,6 +82,14 @@ if("Both") adding_flags |= ALL + if(adding_flags & UNDERSTOOD_LANGUAGE) + var/partial_understanding = tgui_input_number(user, "Set level of understanding:", "[language_datum]", 100, 1, 100) + if(isnull(partial_understanding)) + return + if(partial_understanding < 100) + adding_flags &= ~UNDERSTOOD_LANGUAGE + language_holder.grant_partial_language(language_datum, partial_understanding) + 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")) if(choice == "Yes") @@ -105,6 +115,8 @@ removing_flags |= ALL language_holder.remove_language(language_datum, removing_flags) + if(removing_flags & UNDERSTOOD_LANGUAGE) + language_holder.remove_partial_language(language_datum) if(is_admin) message_admins("[key_name_admin(user)] removed the [language_name] language to [key_name_admin(speaker)].") log_admin("[key_name(user)] removed the language [language_name] to [key_name(speaker)].") diff --git a/code/modules/language/moffic.dm b/code/modules/language/moffic.dm index 9c43cda6642..a74eea63210 100644 --- a/code/modules/language/moffic.dm +++ b/code/modules/language/moffic.dm @@ -4,7 +4,7 @@ speech_verb = "flutters" ask_verb = "fluffs" exclaim_verb = "floofs" - flags = LANGUAGE_HIDE_ICON_IF_NOT_UNDERSTOOD + flags = LANGUAGE_HIDE_ICON_IF_NOT_UNDERSTOOD | ROUNDSTART_LANGUAGE key = "m" space_chance = 55 syllables = list( //hollow knight diff --git a/code/modules/language/sign.dm b/code/modules/language/sign.dm index 1c2e6af2df3..37669e528d5 100644 --- a/code/modules/language/sign.dm +++ b/code/modules/language/sign.dm @@ -8,7 +8,7 @@ whisper_verb = "subtly signs" sing_verb = "rythmically signs" key = "u" - flags = TONGUELESS_SPEECH | LANGUAGE_HIDE_ICON_IF_UNDERSTOOD | SIGNED_LANGUAGE + flags = TONGUELESS_SPEECH | LANGUAGE_HIDE_ICON_IF_UNDERSTOOD | ROUNDSTART_LANGUAGE | SIGNED_LANGUAGE | NO_HISS default_priority = 99 use_tone_indicators = TRUE bubble_override = "signlang" diff --git a/code/modules/language/solarian.dm b/code/modules/language/solarian.dm index 425156824e0..bbd0a931649 100644 --- a/code/modules/language/solarian.dm +++ b/code/modules/language/solarian.dm @@ -2,7 +2,7 @@ name = "Solarian International Standard" desc = "The natural fusion of the Solarian languages that survived the Night Of Fire, which gradually coalesced into a single language." key = "c" - flags = TONGUELESS_SPEECH | LANGUAGE_HIDE_ICON_IF_NOT_UNDERSTOOD + flags = TONGUELESS_SPEECH | LANGUAGE_HIDE_ICON_IF_NOT_UNDERSTOOD | ROUNDSTART_LANGUAGE default_priority = 90 space_chance = 20 sentence_chance = 0 diff --git a/code/modules/language/teceti_unified.dm b/code/modules/language/teceti_unified.dm index 5d2f8339961..f3a9ffb86be 100644 --- a/code/modules/language/teceti_unified.dm +++ b/code/modules/language/teceti_unified.dm @@ -4,7 +4,7 @@ speech_verb = "chirps" ask_verb = "chirps" key = "f" - flags = LANGUAGE_HIDE_ICON_IF_NOT_UNDERSTOOD + flags = LANGUAGE_HIDE_ICON_IF_NOT_UNDERSTOOD | ROUNDSTART_LANGUAGE space_chance = 40 sentence_chance = 0 between_word_sentence_chance = 20 diff --git a/code/modules/language/zohilan.dm b/code/modules/language/zohilan.dm new file mode 100644 index 00000000000..a64d15ef880 --- /dev/null +++ b/code/modules/language/zohilan.dm @@ -0,0 +1,33 @@ +/datum/language/zohil_kalixcian + name = "Zohilan" + desc = "A Kalixcian language commonly spoken in the Zohil Explorat and its former colonies in the Maxin system. Speakers of other Kalixcian languages often find it difficult to pronounce." + speech_verb = "hisses" + ask_verb = "hisses" + exclaim_verb = "roars" + sing_verb = "sings" + key = "z" + flags = TONGUELESS_SPEECH | LANGUAGE_HIDE_ICON_IF_NOT_UNDERSTOOD | ROUNDSTART_LANGUAGE | NO_HISS + space_chance = 12 + sentence_chance = 0 + between_word_sentence_chance = 10 + between_word_space_chance = 75 + additional_syllable_low = 0 + additional_syllable_high = 3 + syllables = list( + "za", "az", "ze", "ez", "zi", "iz", "zo", "oz", "zu", "uz", "zs", "sz", + "ha", "ah", "he", "eh", "hi", "ih", "ho", "oh", "hu", "uh", "hs", "sh", + "la", "al", "le", "el", "li", "il", "lo", "ol", "lu", "ul", "ls", "sl", + "ka", "ak", "ke", "ek", "ki", "ik", "ko", "ok", "ku", "uk", "ks", "sk", + "sa", "as", "se", "es", "si", "is", "so", "os", "su", "us", "ss", "ss", + "ra", "ar", "re", "er", "ri", "ir", "ro", "or", "ru", "ur", "rs", "sr", + "ta", "at", "te", "et", "ti", "it", "to", "ot", "tu", "ut", "th", "zh", + "qa", "aq", "qe", "eq", "qi", "iq", "qo", "oq", "qu", "uq", "bh", "zl", + "a", "a", "e", "e", "i", "i", "o", "o", "u", "u", "s", "s" + ) + special_characters = list("'") + special_character_chance = 20 + icon_state = "lizard-blue" + default_priority = 90 + mutual_understanding = list( + /datum/language/gezena_kalixcian = 90, // enough to sort of understand each other, but not perfectly + ) diff --git a/code/modules/mob/dead/new_player/ship_select.dm b/code/modules/mob/dead/new_player/ship_select.dm index 276e88ce611..c2fe8b46799 100644 --- a/code/modules/mob/dead/new_player/ship_select.dm +++ b/code/modules/mob/dead/new_player/ship_select.dm @@ -56,6 +56,12 @@ to_chat(spawnee, span_warning("You cannot join this ship anymore, as its join mode has changed!")) return + var/datum/faction/registered_faction = target.shuttle_port.registered_faction + var/datum/language/official_lang = initial(registered_faction.official_language) + if(official_lang != spawnee.client.prefs.native_language && spawnee.client.prefs.learned_languages[official_lang] != LANGUAGE_FLUENT && \ + tgui_alert(spawnee, "Your character does not fully understand this faction's official language ([initial(official_lang.name)]), are you sure?", "Official language", list("Yes", "No")) != "Yes") + return // pop-up warning for new players that forgot to set their + ui.close() var/datum/job/selected_job = locate(params["job"]) in target.job_slots //boots you out if you're banned from officer roles diff --git a/code/modules/overmap/helm.dm b/code/modules/overmap/helm.dm index d645c2da5c2..0a3078e4a30 100644 --- a/code/modules/overmap/helm.dm +++ b/code/modules/overmap/helm.dm @@ -50,12 +50,8 @@ icon_state = "computer-solgov" deconpath = /obj/structure/frame/computer/solgov -/datum/config_entry/number/bluespace_jump_wait - default = 5 MINUTES - /obj/machinery/computer/helm/Initialize(mapload, obj/item/circuitboard/C) . = ..() - jump_allowed = world.time + CONFIG_GET(number/bluespace_jump_wait) ntnet_relay = new(src) /obj/machinery/computer/helm/examine(mob/user) @@ -72,10 +68,6 @@ if(current_ship.docked_to || current_ship.docking) say("Bluespace Jump Calibration detected interference in the local area.") return - if(world.time < jump_allowed) - var/jump_wait = DisplayTimeText(jump_allowed - world.time) - say("Bluespace Jump Calibration is currently recharging. ETA: [jump_wait].") - return message_admins("[ADMIN_LOOKUPFLW(usr)] has initiated a bluespace jump in [ADMIN_VERBOSEJMP(src)]") jump_timer = addtimer(CALLBACK(src, PROC_REF(jump_sequence), TRUE), JUMP_CHARGEUP_TIME, TIMER_STOPPABLE) if(new_system) @@ -148,6 +140,11 @@ current_ship.helms -= src current_ship = port.current_ship current_ship.helms |= src + if(port.registered_faction) + var/datum/language/official_language = port.registered_faction.official_language + var/datum/language_holder/lang_holder = get_language_holder() + grant_language(official_language) + lang_holder.selected_language = official_language /** * This proc manually rechecks that the helm computer is connected to a proper ship diff --git a/code/modules/shuttle/shuttle.dm b/code/modules/shuttle/shuttle.dm index 9d8885f6f24..674ed536f6a 100644 --- a/code/modules/shuttle/shuttle.dm +++ b/code/modules/shuttle/shuttle.dm @@ -472,6 +472,9 @@ ///List of all stationary docking ports that spawned on the ship roundstart, used for docking to other ships. var/list/obj/docking_port/stationary/docking_points + ///The faction this shuttle is registered under. + var/datum/faction/registered_faction + /// Does this shuttle play sounds upon landing and takeoff? var/shuttle_sounds = TRUE /// The take off sound to be played @@ -536,6 +539,7 @@ /obj/docking_port/mobile/proc/load(datum/map_template/shuttle/source_template) + registered_faction = source_template.faction shuttle_areas = list() var/list/all_turfs = return_ordered_turfs(x, y, z, dir) for(var/turf/curT as anything in all_turfs) diff --git a/code/modules/surgery/organs/tongue.dm b/code/modules/surgery/organs/tongue.dm index dac77bb7e9a..302d38e7f5e 100644 --- a/code/modules/surgery/organs/tongue.dm +++ b/code/modules/surgery/organs/tongue.dm @@ -16,7 +16,8 @@ var/modifies_speech = FALSE var/static/list/languages_possible_base = typecacheof(list( /datum/language/galactic_common, - /datum/language/kalixcian_common, + /datum/language/gezena_kalixcian, + /datum/language/zohil_kalixcian, /datum/language/teceti_unified, //datum/language/solarian_international, /datum/language/moffic, @@ -59,8 +60,8 @@ /obj/item/organ/tongue/lizard/handle_speech(datum/source, list/speech_args) // Sarathi tongues don't hiss when speaking Kalixcian. Or when signing. // we should make non-sarathi hiss in Kalixcian - var/datum/language/language_used = speech_args[SPEECH_LANGUAGE] - if((language_used == /datum/language/kalixcian_common) || (initial(language_used?.flags) & SIGNED_LANGUAGE)) + var/datum/language/lang_type = speech_args[SPEECH_LANGUAGE] + if(initial(lang_type.flags) & NO_HISS) return var/static/regex/lizard_hiss = new("s+", "g") @@ -95,7 +96,8 @@ var/list/phomeme_types = list("sans", "papyrus") var/static/list/languages_possible_skeleton = typecacheof(list( /datum/language/galactic_common, - /datum/language/kalixcian_common, + /datum/language/gezena_kalixcian, + /datum/language/zohil_kalixcian, /datum/language/codespeak, /datum/language/monkey, /datum/language/aphasia, @@ -161,7 +163,8 @@ taste_sensitivity = 101 // Not a tongue, they can't taste shit var/static/list/languages_possible_ethereal = typecacheof(list( /datum/language/galactic_common, - /datum/language/kalixcian_common, + /datum/language/gezena_kalixcian, + /datum/language/zohil_kalixcian, /datum/language/teceti_unified, /datum/language/solarian_international, /datum/language/moffic, @@ -183,7 +186,8 @@ say_mod = "flutters" var/static/list/languages_possible_moth = typecacheof(list( /datum/language/galactic_common, - /datum/language/kalixcian_common, + /datum/language/gezena_kalixcian, + /datum/language/zohil_kalixcian, /datum/language/teceti_unified, /datum/language/solarian_international, /datum/language/moffic, @@ -202,7 +206,8 @@ say_mod = "chirps" var/static/list/languages_possible_kepi = typecacheof(list( /datum/language/galactic_common, - /datum/language/kalixcian_common, + /datum/language/gezena_kalixcian, + /datum/language/zohil_kalixcian, /datum/language/teceti_unified, /datum/language/solarian_international, /datum/language/moffic, @@ -223,7 +228,8 @@ say_mod = "shrieks" var/static/list/languages_possible_vox = typecacheof(list( /datum/language/galactic_common, - /datum/language/kalixcian_common, + /datum/language/gezena_kalixcian, + /datum/language/zohil_kalixcian, /datum/language/teceti_unified, /datum/language/solarian_international, /datum/language/moffic, @@ -245,13 +251,13 @@ say_mod = "chitters" var/static/list/languages_possible_arachnid = typecacheof(list( /datum/language/galactic_common, - /datum/language/kalixcian_common, + /datum/language/gezena_kalixcian, + /datum/language/zohil_kalixcian, /datum/language/codespeak, /datum/language/monkey, /datum/language/aphasia, /datum/language/moffic, - /datum/language/rachnidian, - /datum/language/buzzwords + /datum/language/rachnidian )) /obj/item/organ/tongue/spider/Initialize(mapload) @@ -286,7 +292,8 @@ say_mod = "blorbles" var/static/list/languages_possible_slime = typecacheof(list( /datum/language/galactic_common, - /datum/language/kalixcian_common, + /datum/language/gezena_kalixcian, + /datum/language/zohil_kalixcian, /datum/language/teceti_unified, /datum/language/solarian_international, /datum/language/moffic, @@ -420,7 +427,8 @@ modifies_speech = TRUE var/static/list/languages_possible_fly = typecacheof(list( /datum/language/galactic_common, - /datum/language/kalixcian_common, + /datum/language/gezena_kalixcian, + /datum/language/zohil_kalixcian, /datum/language/teceti_unified, /datum/language/solarian_international, /datum/language/moffic, diff --git a/icons/misc/language.dmi b/icons/misc/language.dmi index 9adf235b3a8..c6ced0ec683 100644 Binary files a/icons/misc/language.dmi and b/icons/misc/language.dmi differ diff --git a/modular_pentest/master_files/code/modules/language/language_holder.dm b/modular_pentest/master_files/code/modules/language/language_holder.dm index b8d38baf1f8..cdfaef69360 100644 --- a/modular_pentest/master_files/code/modules/language/language_holder.dm +++ b/modular_pentest/master_files/code/modules/language/language_holder.dm @@ -6,35 +6,3 @@ understood_languages = list(/datum/language/swarmer = list(LANGUAGE_ATOM)) spoken_languages = list(/datum/language/swarmer = list(LANGUAGE_ATOM)) blocked_languages = list(/datum/language/galactic_common = list(LANGUAGE_ATOM)) - -/* -/datum/language_holder/human - understood_languages = list(/datum/language/galactic_common = list(LANGUAGE_ATOM), - /datum/language/solarian_international = list(LANGUAGE_ATOM)) - spoken_languages = list(/datum/language/galactic_common = list(LANGUAGE_ATOM), - /datum/language/solarian_international = list(LANGUAGE_ATOM)) -*/ - -/datum/language_holder/lizard - understood_languages = list(/datum/language/galactic_common = list(LANGUAGE_ATOM), - /datum/language/kalixcian_common = list(LANGUAGE_ATOM)) - spoken_languages = list(/datum/language/galactic_common = list(LANGUAGE_ATOM), - /datum/language/kalixcian_common = list(LANGUAGE_ATOM)) - -/datum/language_holder/moth - understood_languages = list(/datum/language/galactic_common = list(LANGUAGE_ATOM), - /datum/language/moffic = list(LANGUAGE_ATOM)) - spoken_languages = list(/datum/language/galactic_common = list(LANGUAGE_ATOM), - /datum/language/moffic = list(LANGUAGE_ATOM)) - -/datum/language_holder/ethereal - understood_languages = list(/datum/language/galactic_common = list(LANGUAGE_ATOM), - /datum/language/kalixcian_common = list(LANGUAGE_ATOM)) - spoken_languages = list(/datum/language/galactic_common = list(LANGUAGE_ATOM), - /datum/language/kalixcian_common = list(LANGUAGE_ATOM)) - -/datum/language_holder/kepori - understood_languages = list(/datum/language/galactic_common = list(LANGUAGE_ATOM), - /datum/language/teceti_unified = list(LANGUAGE_ATOM)) - spoken_languages = list(/datum/language/galactic_common = list(LANGUAGE_ATOM), - /datum/language/teceti_unified = list(LANGUAGE_ATOM)) diff --git a/shiptest.dme b/shiptest.dme index 03f4b126a71..f35c34b5e57 100644 --- a/shiptest.dme +++ b/shiptest.dme @@ -918,6 +918,7 @@ #include "code\datums\traits\negative\frail.dm" #include "code\datums\traits\negative\heavy_sleeper.dm" #include "code\datums\traits\negative\light_drinker.dm" +#include "code\datums\traits\negative\monolingual.dm" #include "code\datums\traits\negative\mute.dm" #include "code\datums\traits\negative\nearsighted.dm" #include "code\datums\traits\negative\paraplegic.dm" @@ -928,7 +929,6 @@ #include "code\datums\traits\neutral\bald.dm" #include "code\datums\traits\neutral\deviant_tastes.dm" #include "code\datums\traits\neutral\gunslinger.dm" -#include "code\datums\traits\neutral\languages.dm" #include "code\datums\traits\neutral\monochromatic.dm" #include "code\datums\traits\neutral\musician.dm" #include "code\datums\traits\neutral\photographer.dm" @@ -941,6 +941,7 @@ #include "code\datums\traits\positive\friendly.dm" #include "code\datums\traits\positive\light_step.dm" #include "code\datums\traits\positive\self_aware.dm" +#include "code\datums\traits\positive\trilingual.dm" #include "code\datums\votes\_vote_datum.dm" #include "code\datums\votes\custom_vote.dm" #include "code\datums\votes\restart_vote.dm" @@ -2542,11 +2543,10 @@ #include "code\modules\keybindings\focus.dm" #include "code\modules\keybindings\setup.dm" #include "code\modules\language\aphasia.dm" -#include "code\modules\language\buzzwords.dm" #include "code\modules\language\codespeak.dm" #include "code\modules\language\common.dm" -#include "code\modules\language\draconic.dm" #include "code\modules\language\drone.dm" +#include "code\modules\language\gezenan.dm" #include "code\modules\language\language.dm" #include "code\modules\language\language_holder.dm" #include "code\modules\language\language_menu.dm" @@ -2563,6 +2563,7 @@ #include "code\modules\language\teceti_unified.dm" #include "code\modules\language\vox_pidgin.dm" #include "code\modules\language\xenocommon.dm" +#include "code\modules\language\zohilan.dm" #include "code\modules\library\lib_items.dm" #include "code\modules\library\lib_machines.dm" #include "code\modules\library\random_books.dm" diff --git a/tgui/packages/tgui/interfaces/LanguageMenu.tsx b/tgui/packages/tgui/interfaces/LanguageMenu.tsx index ad58fd2163c..03577e0402a 100644 --- a/tgui/packages/tgui/interfaces/LanguageMenu.tsx +++ b/tgui/packages/tgui/interfaces/LanguageMenu.tsx @@ -70,6 +70,7 @@ const LangUnderstandIcon = (props: LanguageProps) => { borderBottom: '2px dotted rgba(255, 255, 255, 0.8)', }} > + {language.partial_understanding}%