mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-07-13 00:55:20 +01:00
Reworks language translations. Add partial language understanding. Bilingual update. (#90252)
Fixes #89445 (well, technically. It fixes the bug associated but these `say`s should really be emotes.) Three things: 1. Reworks how language translation works. Rather than scrambling a sentence into a language entirely, sentences are now scrambled on a per-word basis. Additionally, the 1000 most common words of a language are *never* re-scrambled across the duration of a round. Once it's set it's set in stone. Example: (Sample / Old / New)  This allows for a number of things: - More consistent translations, making it (more) viable to actually "teach" someone words for something - Maintaining emphasis such as caps (but not `||`, `++`, or `__` - at least not yet) - The following: 2. Adds partial language understanding Some languages can understand portions of other languages.  This pr adds the following: - Those who understand Beachtongue can understand 50% of Common and 33% of Uncommon words. - Those who understand Common can understand 33% of Beachtongue and 20% of Uncommon words. - Those who understand Uncommon can understand 20% of Common and 20% of Beachtongue words. 3. Bilingual quirk has been expanded to accomodate these changes. There are now two more preferences: - Language Speakable - You can toggle this, so you only understand the language, rather than understand AND speak. - Language Skill - If you choose to be unable to speak the language, you can set how much of the language you can understand, down to 10%. Playing around languages is fun, but due to the way our translation works, ALL context is immediately lost for what the other person may be saying. If the other person is shouting in all caps? Output language is normal chatting. This is lame! Even if someone is unable to understand you, there's a LOT you can convey just by how you speak, and getting that across in game is quite difficult when all translations get mauled so badly. So this changes that. - Emphasis like caps lock is maintained, so you see someone shouting in caps in a foreign language you can probably intuit something is wrong (but not what is wrong!) - Some languages can gleam bits of other languages, so you MIGHT be able to pick out context if you pay close attention - "Brother" languages will now feel more like "brothers" and not completely divergent - You can even "teach" someone words in your language - at least the most common words! (Until next round) 🆑 Melbert add: Languages can now have partial understanding of other languages. More common English words are more likely to be mutually understood. add: Those who understand Beachtongue can understand 50% of Common and 33% of Uncommon words. add: Those who understand Common can understand 33% of Beachtongue and 20% of Uncommon words. add: Those who understand Uncommon can understand 20% of Common and 20% of Beachtongue words. add: Bilingual quirk: You can now choose between being able to speak or not speak the language add: Bilingual quirk: You can now choose to have partial understanding of your language, rather than full. qol: If you speak in ALL CAPS in a foreign language, the translated words will also be ALL CAPS. qol: Many more forms of punctuation are now conveyed across translations. qol: The 1000 most common English words will now never be scrambled when translating into other languages for the duration of the round. This means you can actually "learn" some words if you are especially attentive! (Until the next round at least) refactor: Refactored language translations. Report if you see any super odd looking translations. fix: Force-says forcing you to speak common (such as cult invocations) will now correctly force you to speak common (even if you don't know common) /🆑
This commit is contained in:
@@ -1,5 +1,7 @@
|
||||
/// maximum of 50 specific scrambled lines per language
|
||||
/// Last 50 spoken (uncommon) words will be cached before we start cycling them out (re-randomizing them)
|
||||
#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
|
||||
|
||||
/// Datum based languages. Easily editable and modular.
|
||||
/datum/language
|
||||
@@ -16,15 +18,52 @@
|
||||
var/list/syllables
|
||||
/// List of characters that will randomly be inserted between syllables.
|
||||
var/list/special_characters
|
||||
|
||||
// These modify how syllables are combined.
|
||||
/// Likelihood of making a new sentence after each syllable.
|
||||
var/sentence_chance = 5
|
||||
/// Likelihood of getting a space in the random scramble string
|
||||
var/space_chance = 55
|
||||
var/sentence_chance = 2
|
||||
/// Likelihood of making a new sentence after each word.
|
||||
var/between_word_sentence_chance = 0
|
||||
/// Likelihood of adding a space between syllables.
|
||||
var/space_chance = 20
|
||||
/// Likelyhood of adding a space between words.
|
||||
var/between_word_space_chance = 100
|
||||
/// Scramble word interprets the word as this much longer than it really is (low end)
|
||||
/// You can set this to an arbitarily large negative number to make all words only one syllable.
|
||||
var/additional_syllable_low = -1
|
||||
/// Scramble word interprets the word as this much longer than it really is (high end)
|
||||
/// You can set this to an arbitarily large negative number to make all words only one syllable.
|
||||
var/additional_syllable_high = 3
|
||||
|
||||
/// Spans to apply from this language
|
||||
var/list/spans
|
||||
/// Cache of recently scrambled text
|
||||
/// This allows commonly reused words to not require a full re-scramble every time.
|
||||
var/list/scramble_cache = list()
|
||||
/**
|
||||
* Cache of recently scrambled text
|
||||
* This allows commonly reused words to not require a full re-scramble every time.
|
||||
* Is limited to the last SCRAMBLE_CACHE_LEN words spoken. After surpassing this limit,
|
||||
* the oldest word will be removed from the cache and rescrambled if spoken again.
|
||||
*
|
||||
* Case insensitive, punctuation insensitive.
|
||||
*/
|
||||
VAR_PRIVATE/list/scramble_cache = list()
|
||||
/**
|
||||
* Scramble cache, but for the 1000 most common words in the English language.
|
||||
* These are never rescrambled, so they will consistently be the same thing.
|
||||
*
|
||||
* Case insensitive, punctuation insensitive.
|
||||
*/
|
||||
VAR_PRIVATE/list/most_common_cache = list()
|
||||
/**
|
||||
* Cache of recently spoken sentences
|
||||
* So if one person speaks over the radio, everyone hears the same thing.
|
||||
*
|
||||
* This is an assoc list [sentence] = [key, scrambled_text]
|
||||
* Where key is a string that is used to determine context about the listener (like what languages they know)
|
||||
*
|
||||
* Case sensitive, punctuation sensitive.
|
||||
*/
|
||||
VAR_PRIVATE/list/last_sentence_cache = list()
|
||||
|
||||
/// The language that an atom knows with the highest "default_priority" is selected by default.
|
||||
var/default_priority = 0
|
||||
/// If TRUE, when generating names, we will always use the default human namelist, even if we have syllables set.
|
||||
@@ -34,7 +73,7 @@
|
||||
/// if you are seeing someone speak popcorn language, then something is wrong.
|
||||
var/icon = 'icons/ui/chat/language.dmi'
|
||||
/// Icon state displayed in the chat window when speaking this language.
|
||||
var/icon_state = "popcorn"
|
||||
var/icon_state = "unknown"
|
||||
|
||||
/// By default, random names picks this many names
|
||||
var/default_name_count = 2
|
||||
@@ -45,6 +84,32 @@
|
||||
/// What char to place in between randomly generated names
|
||||
var/random_name_spacer = " "
|
||||
|
||||
/**
|
||||
* Assoc Lazylist of other language types that would have a degree of mutual understanding with this language.
|
||||
* For example, you could do `list(/datum/language/common = 50)` to say that this language has a 50% chance to understand common words
|
||||
* And yeah if you give a 100% chance, they can basically just understand the language.
|
||||
* Not sure why you would do that though.
|
||||
*/
|
||||
var/list/mutual_understanding
|
||||
|
||||
// Primarily for debugging, allows for easy iteration and testing of languages.
|
||||
/datum/language/vv_edit_var(var_name, var_value)
|
||||
. = ..()
|
||||
var/list/delete_cache = list(
|
||||
NAMEOF(src, additional_syllable_high),
|
||||
NAMEOF(src, additional_syllable_low),
|
||||
NAMEOF(src, between_word_sentence_chance),
|
||||
NAMEOF(src, between_word_space_chance),
|
||||
NAMEOF(src, sentence_chance),
|
||||
NAMEOF(src, space_chance),
|
||||
NAMEOF(src, special_characters),
|
||||
NAMEOF(src, syllables),
|
||||
)
|
||||
if(var_name in delete_cache)
|
||||
scramble_cache.Cut()
|
||||
most_common_cache.Cut()
|
||||
last_sentence_cache.Cut()
|
||||
|
||||
/// 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)
|
||||
@@ -109,56 +174,138 @@
|
||||
|
||||
return result
|
||||
|
||||
/datum/language/proc/check_cache(input)
|
||||
var/lookup = scramble_cache[input]
|
||||
if(lookup)
|
||||
scramble_cache -= input
|
||||
scramble_cache[input] = lookup
|
||||
. = lookup
|
||||
/// Checks the word cache for a word
|
||||
/datum/language/proc/read_word_cache(input)
|
||||
SHOULD_NOT_OVERRIDE(TRUE)
|
||||
// we generally want "The" and "the" to translate to the same thing.
|
||||
// so we lowercase everything, making it case insensitive.
|
||||
var/lowertext_input = LOWER_TEXT(input)
|
||||
if(most_common_cache[lowertext_input])
|
||||
return most_common_cache[lowertext_input]
|
||||
|
||||
/datum/language/proc/add_to_cache(input, scrambled_text)
|
||||
. = scramble_cache[lowertext_input]
|
||||
if(. && scramble_cache[1] != lowertext_input)
|
||||
// bumps it to the top of the cache
|
||||
scramble_cache -= lowertext_input
|
||||
scramble_cache[lowertext_input] = .
|
||||
return .
|
||||
|
||||
/// Adds a word to the cache
|
||||
/datum/language/proc/write_word_cache(input, scrambled_text)
|
||||
SHOULD_NOT_OVERRIDE(TRUE)
|
||||
var/lowertext_input = LOWER_TEXT(input)
|
||||
// The most common words are always cached
|
||||
if(GLOB.most_common_words[lowertext_input])
|
||||
most_common_cache[lowertext_input] = scrambled_text
|
||||
return
|
||||
// Add it to cache, cutting old entries if the list is too long
|
||||
scramble_cache[input] = scrambled_text
|
||||
if(scramble_cache.len > SCRAMBLE_CACHE_LEN)
|
||||
scramble_cache.Cut(1, 2)
|
||||
scramble_cache[lowertext_input] = scrambled_text
|
||||
if(length(scramble_cache) > SCRAMBLE_CACHE_LEN)
|
||||
scramble_cache.Cut(1, scramble_cache.len - SCRAMBLE_CACHE_LEN + 1)
|
||||
|
||||
/datum/language/proc/scramble(input)
|
||||
/// Checks the sentence cache for a sentence
|
||||
/datum/language/proc/read_sentence_cache(input)
|
||||
SHOULD_NOT_OVERRIDE(TRUE)
|
||||
// the only handling we do is capitalizing the first word, as say auto-capitalizes the first word anyway
|
||||
// the actual structure of the sentence is otherwise case sensitive so it's preserved
|
||||
var/input_capitalized = capitalize(input)
|
||||
. = last_sentence_cache[input_capitalized]
|
||||
if(. && last_sentence_cache[1] != input_capitalized)
|
||||
// bumps it to the top of the cache (don't anticipate this happening often)
|
||||
last_sentence_cache -= input_capitalized
|
||||
last_sentence_cache[input_capitalized] = .
|
||||
return .
|
||||
|
||||
/// Adds a sentence to the cache, though the sentence should be modified with a key
|
||||
/datum/language/proc/write_sentence_cache(input, key, result_scramble)
|
||||
SHOULD_NOT_OVERRIDE(TRUE)
|
||||
var/input_capitalized = capitalize(input)
|
||||
// Add to the cache (the cache being an assoc list of assoc lists), cutting old entries if the list is too long
|
||||
LAZYSET(last_sentence_cache[input_capitalized], key, result_scramble)
|
||||
if(length(last_sentence_cache) > SENTENCE_CACHE_LEN)
|
||||
last_sentence_cache.Cut(1, last_sentence_cache.len - SENTENCE_CACHE_LEN + 1)
|
||||
|
||||
/**
|
||||
* Scrambles a sentence in this language.
|
||||
*
|
||||
* Takes into account any languages the hearer knows that has mutual understanding with this language.
|
||||
*/
|
||||
/datum/language/proc/scramble_sentence(input, list/mutual_languages)
|
||||
var/cache_key = "[mutual_languages?[type] || 0]-understanding"
|
||||
var/list/cache = read_sentence_cache(cache_key)
|
||||
if(cache?[cache_key])
|
||||
return cache[cache_key]
|
||||
|
||||
var/list/real_words = splittext(input, " ")
|
||||
var/list/scrambled_words = list()
|
||||
for(var/word in real_words)
|
||||
var/translate_prob = mutual_languages?[type] || 0
|
||||
var/base_word = strip_outer_punctuation(word)
|
||||
if(translate_prob > 0)
|
||||
// the probability of managing to understand a word is based on how common it is
|
||||
// 1000 words in the list, so words outside the list are just treated as "the 1500th most common word"
|
||||
var/commonness = GLOB.most_common_words[LOWER_TEXT(base_word)] || 1500
|
||||
translate_prob += (translate_prob * 0.2 * (1 - (min(commonness, 1500) / 500)))
|
||||
if(prob(translate_prob))
|
||||
scrambled_words += base_word
|
||||
continue
|
||||
|
||||
scrambled_words += scramble_word(base_word)
|
||||
|
||||
// start building the new sentence. first word is capitalized and otherwise untouched
|
||||
var/sentence = capitalize(popleft(scrambled_words))
|
||||
for(var/word in scrambled_words)
|
||||
if(prob(between_word_sentence_chance))
|
||||
sentence += ". "
|
||||
word = capitalize(word)
|
||||
else if(prob(between_word_space_chance))
|
||||
sentence += " "
|
||||
|
||||
sentence += word
|
||||
|
||||
// scrambling the words will drop punctuation, so re-add it at the end
|
||||
sentence += find_last_punctuation(input)
|
||||
|
||||
write_sentence_cache(input, cache_key, sentence)
|
||||
|
||||
return sentence
|
||||
|
||||
/**
|
||||
* Scrambles a single word in this language.
|
||||
*/
|
||||
/datum/language/proc/scramble_word(input)
|
||||
// If the input is cached already, move it to the end of the cache and return it
|
||||
var/word = read_word_cache(input)
|
||||
if(word)
|
||||
return (is_uppercase(input) && length_char(input) >= 2) ? uppertext(word) : word
|
||||
|
||||
if(!length(syllables))
|
||||
return stars(input)
|
||||
word = stars(input)
|
||||
|
||||
// If the input is cached already, move it to the end of the cache and return it
|
||||
var/lookup = check_cache(input)
|
||||
if(lookup)
|
||||
return lookup
|
||||
else
|
||||
var/input_size = max(length_char(input) + rand(additional_syllable_low, additional_syllable_high), 1)
|
||||
var/add_space = FALSE
|
||||
var/add_period = FALSE
|
||||
word = ""
|
||||
while(length_char(word) < input_size)
|
||||
// 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)
|
||||
// generate the next syllable (capitalize if we just added a period)
|
||||
var/next = pick_weight_recursive(syllables)
|
||||
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)
|
||||
|
||||
var/input_size = length_char(input)
|
||||
var/scrambled_text = ""
|
||||
var/capitalize = TRUE
|
||||
write_word_cache(input, word)
|
||||
|
||||
while(length_char(scrambled_text) < input_size)
|
||||
var/next = (length(scrambled_text) && length(special_characters) && prob(1)) ? pick(special_characters) : pick_weight_recursive(syllables)
|
||||
if(capitalize)
|
||||
next = capitalize(next)
|
||||
capitalize = FALSE
|
||||
scrambled_text += next
|
||||
var/chance = rand(100)
|
||||
if(chance <= sentence_chance)
|
||||
scrambled_text += ". "
|
||||
capitalize = TRUE
|
||||
else if(chance > sentence_chance && chance <= space_chance)
|
||||
scrambled_text += " "
|
||||
|
||||
scrambled_text = trim(scrambled_text)
|
||||
var/ending = copytext_char(scrambled_text, -1)
|
||||
if(ending == ".")
|
||||
scrambled_text = copytext_char(scrambled_text, 1, -2)
|
||||
var/input_ending = copytext_char(input, -1)
|
||||
if(input_ending in list("!","?","."))
|
||||
scrambled_text += input_ending
|
||||
|
||||
add_to_cache(input, scrambled_text)
|
||||
|
||||
return scrambled_text
|
||||
// If they're shouting, we're shouting
|
||||
return (is_uppercase(input) && length_char(input) >= 2) ? uppertext(word) : word
|
||||
|
||||
#undef SCRAMBLE_CACHE_LEN
|
||||
|
||||
@@ -47,11 +47,17 @@ Key procs
|
||||
/// If true, overrides tongue aforementioned limitations.
|
||||
var/omnitongue = FALSE
|
||||
/// Handles displaying the language menu UI.
|
||||
var/datum/language_menu/language_menu
|
||||
VAR_FINAL/datum/language_menu/language_menu
|
||||
/// Currently spoken language
|
||||
var/selected_language
|
||||
/// Tracks the entity that owns the holder.
|
||||
var/atom/movable/owner
|
||||
VAR_FINAL/atom/movable/owner
|
||||
/// Lazyassoclist of all mutual understanding this holder has
|
||||
/// You generally don't want to access this, you want [best_mutual_languages] instead
|
||||
/// Format: list(language_type = list(source = % of understanding))
|
||||
VAR_PROTECTED/list/mutual_understanding
|
||||
/// Cached form of the mutual language list which only contains the best understanding available to each language
|
||||
VAR_FINAL/list/best_mutual_languages
|
||||
|
||||
/// Initializes, and copies in the languages from the current atom if available.
|
||||
/datum/language_holder/New(atom/new_owner)
|
||||
@@ -66,16 +72,48 @@ Key procs
|
||||
// If we have an owner, we'll set a default selected language
|
||||
if(owner)
|
||||
get_selected_language()
|
||||
// Normally this is applied in grant_language, which we bypass
|
||||
for(var/language in understood_languages)
|
||||
gain_partial_understanding_from_language(language)
|
||||
|
||||
/datum/language_holder/Destroy()
|
||||
QDEL_NULL(language_menu)
|
||||
owner = null
|
||||
return ..()
|
||||
|
||||
/// Helper to get all the partial understanding from the passed language
|
||||
/// Does effectively nothing if given a language already understood
|
||||
/datum/language_holder/proc/gain_partial_understanding_from_language(language)
|
||||
PRIVATE_PROC(TRUE)
|
||||
|
||||
var/datum/language/prototype = GLOB.language_datum_instances[language]
|
||||
for(var/other_language in prototype.mutual_understanding)
|
||||
grant_partial_language(other_language, prototype.mutual_understanding[other_language], language)
|
||||
|
||||
/// Helper to remove all the partial understanding from the passed language
|
||||
/datum/language_holder/proc/lose_partial_understanding_from_language(language)
|
||||
PRIVATE_PROC(TRUE)
|
||||
|
||||
var/datum/language/prototype = GLOB.language_datum_instances[language]
|
||||
for(var/other_language in prototype.mutual_understanding)
|
||||
remove_partial_language(other_language, language)
|
||||
|
||||
/// Calculates the "best mutual language list"
|
||||
/datum/language_holder/proc/calculate_best_mutual_language()
|
||||
best_mutual_languages = list()
|
||||
for(var/language in mutual_understanding)
|
||||
for(var/source in mutual_understanding[language])
|
||||
// if this mutual understanding comes from a language, and that language is blocked, skip it
|
||||
if(LAZYACCESS(blocked_languages, source))
|
||||
continue
|
||||
if(!best_mutual_languages[language] || best_mutual_languages[language] < mutual_understanding[language][source])
|
||||
best_mutual_languages[language] = mutual_understanding[language][source]
|
||||
|
||||
/// Grants the supplied language.
|
||||
/datum/language_holder/proc/grant_language(language, language_flags = ALL, source = LANGUAGE_MIND)
|
||||
if(language_flags & UNDERSTOOD_LANGUAGE)
|
||||
LAZYORASSOCLIST(understood_languages, language, source)
|
||||
gain_partial_understanding_from_language(language)
|
||||
. = TRUE
|
||||
if(language_flags & SPOKEN_LANGUAGE)
|
||||
LAZYORASSOCLIST(spoken_languages, language, source)
|
||||
@@ -91,6 +129,14 @@ Key procs
|
||||
omnitongue = TRUE
|
||||
return TRUE
|
||||
|
||||
/// Grants partial understanding of the passed language.
|
||||
/// Giving 100 understanding is basically equivalent to knowning the language, just with butchered punctuation.
|
||||
/datum/language_holder/proc/grant_partial_language(language, amount = 50, source = LANGUAGE_MIND)
|
||||
LAZYINITLIST(mutual_understanding)
|
||||
LAZYSET(mutual_understanding[language], source, amount)
|
||||
calculate_best_mutual_language()
|
||||
return TRUE
|
||||
|
||||
/// Removes a single language or source, removing all sources returns the pre-removal state of the language.
|
||||
/datum/language_holder/proc/remove_language(language, language_flags = ALL, source = LANGUAGE_ALL)
|
||||
if(language_flags & UNDERSTOOD_LANGUAGE)
|
||||
@@ -98,6 +144,8 @@ Key procs
|
||||
LAZYREMOVE(understood_languages, language)
|
||||
else
|
||||
LAZYREMOVEASSOC(understood_languages, language, source)
|
||||
if(!LAZYACCESS(understood_languages, language))
|
||||
lose_partial_understanding_from_language(language)
|
||||
. = TRUE
|
||||
|
||||
if(language_flags & SPOKEN_LANGUAGE)
|
||||
@@ -117,6 +165,30 @@ Key procs
|
||||
omnitongue = FALSE
|
||||
return TRUE
|
||||
|
||||
/// Removes partial understanding of the passed language.
|
||||
/datum/language_holder/proc/remove_partial_language(language, source = LANGUAGE_MIND)
|
||||
. = FALSE
|
||||
if(source == LANGUAGE_ALL)
|
||||
for(var/other_source in mutual_understanding[language])
|
||||
if(ispath(other_source, /datum/language))
|
||||
continue
|
||||
. = remove_partial_language(language, other_source) || .
|
||||
else if(LAZYACCESSASSOC(mutual_understanding, language, source))
|
||||
LAZYREMOVE(mutual_understanding[language], source)
|
||||
ASSOC_UNSETEMPTY(mutual_understanding, language)
|
||||
UNSETEMPTY(mutual_understanding)
|
||||
. = TRUE
|
||||
|
||||
if(.)
|
||||
calculate_best_mutual_language()
|
||||
return .
|
||||
|
||||
/// Removes all partial understandings of all languages.
|
||||
/datum/language_holder/proc/remove_all_partial_languages(source = LANGUAGE_MIND)
|
||||
for(var/language in mutual_understanding)
|
||||
remove_partial_language(language, source)
|
||||
return TRUE
|
||||
|
||||
/// Adds a single language or list of languages to the blocked language list.
|
||||
/datum/language_holder/proc/add_blocked_language(languages, source = LANGUAGE_MIND)
|
||||
if(!islist(languages))
|
||||
@@ -124,6 +196,7 @@ Key procs
|
||||
|
||||
for(var/language in languages)
|
||||
LAZYORASSOCLIST(blocked_languages, language, source)
|
||||
calculate_best_mutual_language()
|
||||
return TRUE
|
||||
|
||||
/// Removes a single language or list of languages from the blocked language list.
|
||||
@@ -136,7 +209,7 @@ Key procs
|
||||
LAZYREMOVE(blocked_languages, language)
|
||||
else
|
||||
LAZYREMOVEASSOC(blocked_languages, language, source)
|
||||
|
||||
calculate_best_mutual_language()
|
||||
return TRUE
|
||||
|
||||
/// Checks if you have the language passed.
|
||||
@@ -229,6 +302,11 @@ Key procs
|
||||
if(LANGUAGE_MIND in blocked_languages[language])
|
||||
remove_blocked_language(language, LANGUAGE_MIND)
|
||||
to_holder.add_blocked_language(language, LANGUAGE_MIND)
|
||||
for(var/language in mutual_understanding)
|
||||
var/mind_understanding = mutual_understanding[language][LANGUAGE_MIND]
|
||||
if(mind_understanding > 0)
|
||||
remove_partial_language(language, LANGUAGE_MIND)
|
||||
to_holder.grant_partial_language(language, mind_understanding, LANGUAGE_MIND)
|
||||
|
||||
if(owner)
|
||||
get_selected_language()
|
||||
|
||||
@@ -24,6 +24,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()
|
||||
@@ -38,6 +39,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)
|
||||
|
||||
|
||||
@@ -4,7 +4,12 @@
|
||||
flags = LANGUAGE_HIDE_ICON_IF_NOT_UNDERSTOOD
|
||||
key = "i"
|
||||
syllables = list("m","n","gh","h","l","s","r","a","e","i","o","u")
|
||||
space_chance = 20
|
||||
space_chance = 0
|
||||
sentence_chance = 0
|
||||
between_word_sentence_chance = 10
|
||||
between_word_space_chance = 75
|
||||
additional_syllable_low = 0
|
||||
additional_syllable_high = 0
|
||||
default_priority = 10
|
||||
icon_state = "aphasia"
|
||||
always_use_default_namelist = TRUE // Shouldn't generate names for this anyways
|
||||
|
||||
@@ -2,7 +2,12 @@
|
||||
name = "Beachtongue"
|
||||
desc = "An ancient language from the distant Beach Planet. People magically learn to speak it under the influence of space drugs."
|
||||
key = "u"
|
||||
space_chance = 85
|
||||
space_chance = 80
|
||||
sentence_chance = 5
|
||||
between_word_sentence_chance = 0
|
||||
between_word_space_chance = 100
|
||||
additional_syllable_low = -2
|
||||
additional_syllable_high = -1
|
||||
default_priority = 90
|
||||
syllables = list(
|
||||
"cowabunga", "rad", "radical", "dudes", "bogus", "weeed", "every",
|
||||
@@ -19,3 +24,8 @@
|
||||
)
|
||||
icon_state = "beach"
|
||||
always_use_default_namelist = TRUE
|
||||
|
||||
mutual_understanding = list(
|
||||
/datum/language/common = 50,
|
||||
/datum/language/uncommon = 33,
|
||||
)
|
||||
|
||||
@@ -3,6 +3,11 @@
|
||||
desc = "A common language to all insects, made by the rhythmic beating of wings."
|
||||
key = "z"
|
||||
space_chance = 0
|
||||
sentence_chance = 0
|
||||
between_word_sentence_chance = 5
|
||||
between_word_space_chance = 0
|
||||
additional_syllable_low = 0
|
||||
additional_syllable_high = 0
|
||||
syllables = list(
|
||||
"bzz","zzz","z","bz","bzzz","zzzz", "bzzzz", "b", "zz", "zzzzz"
|
||||
)
|
||||
|
||||
@@ -3,6 +3,11 @@
|
||||
desc = "The disjointed and staccato language of plasmamen. Also understood by skeletons."
|
||||
key = "b"
|
||||
space_chance = 10
|
||||
sentence_chance = 2
|
||||
between_word_sentence_chance = 10
|
||||
between_word_space_chance = 75
|
||||
additional_syllable_low = 0
|
||||
additional_syllable_high = 1
|
||||
syllables = list(
|
||||
"k", "ck", "ack", "ick", "cl", "tk", "sk", "isk", "tak",
|
||||
"kl", "hs", "ss", "ks", "lk", "dk", "gk", "ka", "ska", "la", "pk",
|
||||
|
||||
@@ -5,7 +5,11 @@
|
||||
icon_state = "fish"
|
||||
flags = NO_STUTTER|TONGUELESS_SPEECH
|
||||
sentence_chance = 0
|
||||
space_chance = 75
|
||||
space_chance = 0
|
||||
between_word_sentence_chance = 0
|
||||
between_word_space_chance = 75
|
||||
additional_syllable_low = -2
|
||||
additional_syllable_high = -1
|
||||
syllables = list("glub", "blub", "bloop")
|
||||
default_priority = 25
|
||||
default_name_syllable_min = 1
|
||||
|
||||
@@ -7,26 +7,21 @@
|
||||
icon_state = "codespeak"
|
||||
always_use_default_namelist = TRUE // No syllables anyways
|
||||
|
||||
/datum/language/codespeak/scramble(input)
|
||||
var/lookup = check_cache(input)
|
||||
if(lookup)
|
||||
return lookup
|
||||
/datum/language/codespeak/scramble_sentence(input, list/mutual_languages)
|
||||
var/sentence = read_word_cache(input)
|
||||
if(sentence)
|
||||
return sentence
|
||||
|
||||
. = ""
|
||||
sentence = ""
|
||||
var/list/words = list()
|
||||
while(length_char(.) < length_char(input))
|
||||
while(length_char(sentence) < length_char(input))
|
||||
words += generate_code_phrase(return_list=TRUE)
|
||||
. = jointext(words, ", ")
|
||||
sentence = jointext(words, ", ")
|
||||
|
||||
. = capitalize(.)
|
||||
sentence = capitalize(sentence)
|
||||
|
||||
var/input_ending = copytext_char(input, -1)
|
||||
sentence += find_last_punctuation(input)
|
||||
|
||||
var/static/list/endings
|
||||
if(!endings)
|
||||
endings = list("!", "?", ".")
|
||||
write_word_cache(input, sentence)
|
||||
|
||||
if(input_ending in endings)
|
||||
. += input_ending
|
||||
|
||||
add_to_cache(input, .)
|
||||
return sentence
|
||||
|
||||
@@ -5,6 +5,12 @@
|
||||
key = "0"
|
||||
flags = TONGUELESS_SPEECH | LANGUAGE_HIDE_ICON_IF_UNDERSTOOD
|
||||
default_priority = 100
|
||||
space_chance = 20
|
||||
sentence_chance = 0
|
||||
between_word_sentence_chance = 10
|
||||
between_word_space_chance = 75
|
||||
additional_syllable_low = 0
|
||||
additional_syllable_high = 0
|
||||
|
||||
icon_state = "galcom"
|
||||
// Default namelist is the human namelist, and common is the human language, so might as well.
|
||||
@@ -55,3 +61,8 @@
|
||||
"his", "ing", "ion", "ith", "not", "ome", "oul", "our", "sho", "ted", "ter", "tha", "the", "thi",
|
||||
),
|
||||
)
|
||||
|
||||
mutual_understanding = list(
|
||||
/datum/language/beachbum = 33,
|
||||
/datum/language/uncommon = 20,
|
||||
)
|
||||
|
||||
@@ -3,7 +3,12 @@
|
||||
desc = "The common language of lizard-people, composed of sibilant hisses and rattles."
|
||||
key = "o"
|
||||
flags = TONGUELESS_SPEECH
|
||||
space_chance = 40
|
||||
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",
|
||||
|
||||
@@ -8,6 +8,10 @@
|
||||
// ...|..||.||||.|.||.|.|.|||.|||
|
||||
space_chance = 0
|
||||
sentence_chance = 0
|
||||
between_word_sentence_chance = 0
|
||||
between_word_space_chance = 0
|
||||
additional_syllable_low = 0
|
||||
additional_syllable_high = 0
|
||||
default_priority = 20
|
||||
|
||||
icon_state = "drone"
|
||||
|
||||
@@ -9,7 +9,12 @@
|
||||
"bop", "bop", "dee", "dee", "doo", "doo", "hiss", "hss", "buzz",
|
||||
"buzz", "bzz", "ksssh", "keey", "wurr", "wahh", "tzzz",
|
||||
)
|
||||
space_chance = 10
|
||||
space_chance = 0
|
||||
sentence_chance = 0
|
||||
between_word_sentence_chance = 10
|
||||
between_word_space_chance = 10
|
||||
additional_syllable_low = 0
|
||||
additional_syllable_high = 0
|
||||
default_priority = 90
|
||||
|
||||
icon_state = "eal"
|
||||
|
||||
@@ -2,7 +2,12 @@
|
||||
name = "Moffic"
|
||||
desc = "The language of the Mothpeople borders on complete unintelligibility."
|
||||
key = "m"
|
||||
space_chance = 10
|
||||
space_chance = 5
|
||||
sentence_chance = 0
|
||||
between_word_sentence_chance = 10
|
||||
between_word_space_chance = 25
|
||||
additional_syllable_low = 0
|
||||
additional_syllable_high = 0
|
||||
syllables = list(
|
||||
"år", "i", "går", "sek", "mo", "ff", "ok", "gj", "ø", "gå", "la", "le",
|
||||
"lit", "ygg", "van", "dår", "næ", "møt", "idd", "hvo", "ja", "på", "han",
|
||||
|
||||
@@ -2,7 +2,12 @@
|
||||
name = "Chimpanzee"
|
||||
desc = "Ook ook ook."
|
||||
key = "1"
|
||||
space_chance = 100
|
||||
space_chance = 0
|
||||
sentence_chance = 0
|
||||
between_word_sentence_chance = 10
|
||||
between_word_space_chance = 75
|
||||
additional_syllable_low = 0
|
||||
additional_syllable_high = 0
|
||||
syllables = list("oop", "aak", "chee", "eek")
|
||||
default_priority = 80
|
||||
|
||||
|
||||
@@ -3,6 +3,11 @@
|
||||
desc = "A language that consists of the sound of periodic gusts of spore-filled air being released."
|
||||
key = "y"
|
||||
sentence_chance = 0
|
||||
sentence_chance = 0
|
||||
between_word_sentence_chance = 10
|
||||
between_word_space_chance = 75
|
||||
additional_syllable_low = -4
|
||||
additional_syllable_high = -2
|
||||
default_priority = 80
|
||||
syllables = list("poof", "pff", "pFfF", "piff", "puff", "pooof", "pfffff", "piffpiff", "puffpuff", "poofpoof", "pifpafpofpuf")
|
||||
default_name_syllable_min = 1
|
||||
|
||||
@@ -2,8 +2,12 @@
|
||||
name = "Nar'Sian"
|
||||
desc = "The ancient, blood-soaked, impossibly complex language of Nar'Sian cultists."
|
||||
key = "n"
|
||||
sentence_chance = 8
|
||||
space_chance = 95 //very high due to the potential length of each syllable
|
||||
space_chance = 75 //very high due to the potential length of each syllable
|
||||
sentence_chance = 10
|
||||
between_word_sentence_chance = 5
|
||||
between_word_space_chance = 95
|
||||
additional_syllable_low = -1
|
||||
additional_syllable_high = 0
|
||||
var/static/list/base_syllables = list(
|
||||
"h", "v", "c", "e", "g", "d", "r", "n", "h", "o", "p",
|
||||
"ra", "so", "at", "il", "ta", "gh", "sh", "ya", "te", "sh", "ol", "ma", "om", "ig", "ni", "in",
|
||||
|
||||
@@ -2,7 +2,12 @@
|
||||
name = "Nekomimetic"
|
||||
desc = "To the casual observer, this language is an incomprehensible mess of broken Japanese. To the felinids, it's somehow comprehensible."
|
||||
key = "f"
|
||||
space_chance = 70
|
||||
space_chance = 15
|
||||
sentence_chance = 0
|
||||
between_word_sentence_chance = 10
|
||||
between_word_space_chance = 75
|
||||
additional_syllable_low = -1
|
||||
additional_syllable_high = 1
|
||||
syllables = list(
|
||||
"neko", "nyan", "mimi", "moe", "mofu", "fuwa", "kyaa", "kawaii", "poka", "munya",
|
||||
"puni", "munyu", "ufufu", "uhuhu", "icha", "doki", "kyun", "kusu", "nya", "nyaa",
|
||||
|
||||
@@ -3,6 +3,11 @@
|
||||
desc = "The language of space pirates."
|
||||
key = "p"
|
||||
space_chance = 100
|
||||
sentence_chance = 10
|
||||
between_word_sentence_chance = 10
|
||||
between_word_space_chance = 75
|
||||
additional_syllable_low = -2
|
||||
additional_syllable_high = -1
|
||||
default_priority = 90
|
||||
syllables = list(
|
||||
"arr", "ahoy", "rum", "aye", "blimey", "booty", "bucko", "grog", "treasure",
|
||||
|
||||
@@ -4,7 +4,12 @@
|
||||
name = "Shadowtongue"
|
||||
desc = "What a grand and intoxicating innocence."
|
||||
key = "x"
|
||||
space_chance = 50
|
||||
space_chance = 40
|
||||
sentence_chance = 0
|
||||
between_word_sentence_chance = 10
|
||||
between_word_space_chance = 75
|
||||
additional_syllable_low = 0
|
||||
additional_syllable_high = 1
|
||||
syllables = list(
|
||||
"er", "sint", "en", "et", "nor", "bahr", "sint", "un", "ku'elm", "lakor", "eri",
|
||||
"noj", "dashilu", "as", "ot", "lih", "morh", "ghinu", "kin", "sha", "marik", "jibu",
|
||||
|
||||
@@ -4,6 +4,12 @@
|
||||
name = "Spinwarder"
|
||||
desc = "The official language of the Spinward Stellar Coalition, as inherited from the Third Soviet Union."
|
||||
key = "s"
|
||||
space_chance = 20
|
||||
sentence_chance = 0
|
||||
between_word_sentence_chance = 10
|
||||
between_word_space_chance = 75
|
||||
additional_syllable_low = 0
|
||||
additional_syllable_high = 0
|
||||
flags = TONGUELESS_SPEECH
|
||||
syllables = list(
|
||||
"v", "od", "noy", "ned", "ele", "dn", "ey", "da", "ny", "et", "mes", "yat",
|
||||
|
||||
@@ -3,7 +3,12 @@
|
||||
name = "Sylvan"
|
||||
desc = "A complicated, ancient language spoken by sentient plants."
|
||||
key = "h"
|
||||
space_chance = 20
|
||||
space_chance = 10
|
||||
sentence_chance = 0
|
||||
between_word_sentence_chance = 10
|
||||
between_word_space_chance = 50
|
||||
additional_syllable_low = 1
|
||||
additional_syllable_high = 2
|
||||
syllables = list(
|
||||
"fii", "sii", "rii", "rel", "maa", "ala", "san", "tol", "tok", "dia", "eres",
|
||||
"fal", "tis", "bis", "qel", "aras", "losk", "rasa", "eob", "hil", "tanl", "aere",
|
||||
|
||||
@@ -2,7 +2,12 @@
|
||||
name = "Terrum"
|
||||
desc = "The language of the golems. Sounds similar to old-earth Hebrew."
|
||||
key = "g"
|
||||
space_chance = 40
|
||||
space_chance = 20
|
||||
sentence_chance = 0
|
||||
between_word_sentence_chance = 10
|
||||
between_word_space_chance = 75
|
||||
additional_syllable_low = 1
|
||||
additional_syllable_high = 2
|
||||
syllables = list(
|
||||
"sha", "vu", "nah", "ha", "yom", "ma", "cha", "ar", "et", "mol", "lua",
|
||||
"ch", "na", "sh", "ni", "yah", "bes", "ol", "hish", "ev", "la", "ot", "la",
|
||||
|
||||
@@ -3,7 +3,12 @@
|
||||
desc = "The second-most spoken Human language."
|
||||
key = "!"
|
||||
flags = TONGUELESS_SPEECH
|
||||
space_chance = 50
|
||||
space_chance = 20
|
||||
sentence_chance = 0
|
||||
between_word_sentence_chance = 10
|
||||
between_word_space_chance = 75
|
||||
additional_syllable_low = 0
|
||||
additional_syllable_high = 0
|
||||
syllables = list(
|
||||
"ba", "be", "bo", "ca", "ce", "co", "da", "de", "do",
|
||||
"fa", "fe", "fo", "ga", "ge", "go", "ha", "he", "ho",
|
||||
@@ -14,3 +19,8 @@
|
||||
)
|
||||
icon_state = "galuncom"
|
||||
default_priority = 90
|
||||
|
||||
mutual_understanding = list(
|
||||
/datum/language/common = 20,
|
||||
/datum/language/beachbum = 20,
|
||||
)
|
||||
|
||||
@@ -4,6 +4,11 @@
|
||||
desc = "A sparky language made by manipulating electrical discharge."
|
||||
key = "v"
|
||||
space_chance = 20
|
||||
sentence_chance = 0
|
||||
between_word_sentence_chance = 10
|
||||
between_word_space_chance = 75
|
||||
additional_syllable_low = 0
|
||||
additional_syllable_high = 1
|
||||
syllables = list(
|
||||
"bzzt", "skrrt", "zzp", "mmm", "hzz", "tk", "shz", "k", "z",
|
||||
"bzt", "zzt", "skzt", "skzz", "hmmt", "zrrt", "hzzt", "hz",
|
||||
|
||||
@@ -4,6 +4,11 @@
|
||||
key = "4"
|
||||
syllables = list("sss","sSs","SSS")
|
||||
default_priority = 50
|
||||
|
||||
space_chance = 0
|
||||
sentence_chance = 0
|
||||
between_word_sentence_chance = 0
|
||||
between_word_space_chance = 50
|
||||
additional_syllable_low = 0
|
||||
additional_syllable_high = 0
|
||||
icon_state = "xeno"
|
||||
always_use_default_namelist = TRUE // Sssss Ssss?
|
||||
|
||||
Reference in New Issue
Block a user