CSL / Mutual understanding changes (#96774)

## About The Pull Request

1. Redid the formula for calculating mutual language understanding 

Old: `base + (10 * (1 - (min(commonness, 1250) / 500)))`, or in other
words `base + 10% to -15%`
New: `base + max(20 - (commonness / 1000) * 20, -5)`, or in other words
`base + 20% to -5%`

2. CSL strength preference has been changed 

Old: `"90%", "75%", "50%", "33%", "25%", "10%"`
New: `"75%", "50%", "25%"`

3. If you understand a language partially, the icon in chat is
differentiated slightly

<img width="414" height="30" alt="image"
src="https://github.com/user-attachments/assets/16af293a-698e-4ab8-9638-580920a38543"
/>

(Icon subject to change, it's just a `?` spliced with the icon rn)

4. CSL can now be taken by humans, doing so grants you gal-uncommon

5. Fixed a bug where contractions, due to being encoded, would be
treated as lower frequency / rarer and thus be more likely to be
translated

## Why It's Good For The Game

1. The current formula was a bit too harsh and resulted in more words
being translated than you'd expect. The altered formula should make it
less persistent.

2. These values were tweaked according to the new formula. If I left the
90% value it'd mean like every word would get translated and that
defeats the point.

3. Makes things a bit clearer when it comes to partial understanding. 

4. Requested, sounds like fun so why not.

5. Bugfix.


## Changelog

🆑 Melbert
fix: Fixed a bug where contractions were treated as rarer words than
they should be for the sake of partial language understanding
balance: Tweaked the formula of partial language understanding: Less
common words are penalized less, more common words are rewarded more. In
other words more words will be understood on average.
balance: Common Second Language numbers were tweaked according to the
new formula. Now "75%", "50%", "25%".
balance: Humans can now take Common Second Language. Doing so also
grants Galactic Uncommon.
add: Partially understood sentences now get an altered language icon in
chat and runechat, indicating that only some of the words were
translated.
/🆑
This commit is contained in:
MrMelbert
2026-07-11 16:52:28 +01:00
committed by GitHub
parent 5aaf3ab13d
commit 4962a86b95
19 changed files with 110 additions and 62 deletions
+15 -15
View File
@@ -110,19 +110,19 @@
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)
if((flags & LANGUAGE_HIDE_ICON_IF_UNDERSTOOD) && understands)
return FALSE
if((flags & LANGUAGE_HIDE_ICON_IF_NOT_UNDERSTOOD) && !understands)
return FALSE
return TRUE
/// Returns what icon to display when speaking this language
/datum/language/proc/display_icon_type(atom/movable/hearer, list/message_mods)
if(hearer.has_language(type))
if(flags & LANGUAGE_HIDE_ICON_IF_UNDERSTOOD)
return DISPLAY_LANGUAGE_ICON_NONE
/// Returns the icon to display in the chat window when speaking this language.
/datum/language/proc/get_icon()
var/datum/asset/spritesheet_batched/sheet = get_asset_datum(/datum/asset/spritesheet_batched/chat)
return sheet.icon_tag("language-[icon_state]")
else
if(hearer.has_partial_language(type) || LAZYACCESSASSOC(message_mods, LANGUAGE_MUTUAL_BONUS, type))
return DISPLAY_LANGUAGE_ICON_PARTIAL
if(flags & LANGUAGE_HIDE_ICON_IF_NOT_UNDERSTOOD)
return DISPLAY_LANGUAGE_ICON_NONE
return DISPLAY_LANGUAGE_ICON_FULL
/// Simple helper for getting a default firstname lastname
/datum/language/proc/default_name(gender = NEUTER)
@@ -259,12 +259,12 @@
var/list/translated_index = list()
for(var/word in splittext(input, " "))
var/translate_prob = mutual_languages?[type] || 0
var/base_word = strip_outer_punctuation(word)
var/base_word = strip_outer_punctuation(html_decode(word))
if(translate_prob > 0)
// the probability of managing to understand a word is based on how common it is (+10%, -15%)
// the probability of managing to understand a word is based on how common it is (up to +20%, though -5% for very uncommon words)
// 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_frequency[LOWER_TEXT(base_word)] || 1250
translate_prob += (10 * (1 - (min(commonness, 1250) / 500)))
translate_prob += max(20 - (commonness / 1000) * 20, -5)
if(prob(translate_prob))
scrambled_words += word
translated_index += FALSE
@@ -232,6 +232,10 @@ Key procs
return language in langs_to_check
/// Checks if you have partial understanding of the language passed.
/datum/language_holder/proc/has_partial_language(language)
return LAZYACCESS(best_mutual_languages, language)
/// Checks if you can speak the language. Tongue limitations should be supplied as an argument.
/datum/language_holder/proc/can_speak_language(language)
var/can_speak_language_path = omnitongue || owner.could_speak_language(language)
@@ -272,6 +276,10 @@ Key procs
else
return /datum/language/common
/// Returns a list of languages we understand
/datum/language_holder/proc/get_understood_languages()
return assoc_to_keys(understood_languages) - assoc_to_keys(blocked_understanding)
/// Opens a language menu reading from the language holder.
/datum/language_holder/proc/open_language_menu(mob/user)
if(!language_menu)
+1 -1
View File
@@ -3,7 +3,7 @@
desc = "Various fishy sounds that might make sense to space carps."
key = "c"
icon_state = "fish"
flags = NO_STUTTER|TONGUELESS_SPEECH
flags = LANGUAGE_NO_STUTTER|LANGUAGE_TONGUELESS_SPEECH
sentence_chance = 0
space_chance = 0
between_word_sentence_chance = 0
+1 -1
View File
@@ -3,7 +3,7 @@
desc = "Syndicate operatives can use a series of codewords to convey complex information, while sounding like random concepts and drinks to anyone listening in."
key = "t"
default_priority = 0
flags = TONGUELESS_SPEECH | LANGUAGE_HIDE_ICON_IF_NOT_UNDERSTOOD
flags = LANGUAGE_TONGUELESS_SPEECH | LANGUAGE_HIDE_ICON_IF_NOT_UNDERSTOOD
icon_state = "codespeak"
always_use_default_namelist = TRUE // No syllables anyways
+1 -1
View File
@@ -3,7 +3,7 @@
name = "Galactic Common"
desc = "The common galactic tongue."
key = "0"
flags = TONGUELESS_SPEECH | LANGUAGE_HIDE_ICON_IF_UNDERSTOOD
flags = LANGUAGE_TONGUELESS_SPEECH | LANGUAGE_HIDE_ICON_IF_UNDERSTOOD
default_priority = 100
space_chance = 20
sentence_chance = 0
+1 -1
View File
@@ -2,7 +2,7 @@
name = "Draconic"
desc = "The common language of lizard-people, composed of sibilant hisses and rattles."
key = "o"
flags = TONGUELESS_SPEECH
flags = LANGUAGE_TONGUELESS_SPEECH
space_chance = 12
sentence_chance = 0
between_word_sentence_chance = 10
+1 -1
View File
@@ -3,7 +3,7 @@
desc = "A heavily encoded damage control coordination stream, with special flags for hats."
spans = list(SPAN_ROBOT)
key = "d"
flags = NO_STUTTER
flags = LANGUAGE_NO_STUTTER
syllables = list(".", "|")
// ...|..||.||||.|.||.|.|.|||.|||
space_chance = 0
+1 -1
View File
@@ -3,7 +3,7 @@
desc = "An efficient language of encoded tones developed by synthetics and cyborgs."
spans = list(SPAN_ROBOT)
key = "6"
flags = NO_STUTTER
flags = LANGUAGE_NO_STUTTER
syllables = list(
"beep", "beep", "beep", "beep", "beep", "boop", "boop", "boop",
"bop", "bop", "dee", "dee", "doo", "doo", "hiss", "hss", "buzz",
+1 -1
View File
@@ -10,7 +10,7 @@
between_word_space_chance = 75
additional_syllable_low = 0
additional_syllable_high = 0
flags = TONGUELESS_SPEECH
flags = LANGUAGE_TONGUELESS_SPEECH
syllables = list(
"v", "od", "noy", "ned", "ele", "dn", "ey", "da", "ny", "et", "mes", "yat",
"che", "bu", "pol", "ye", "eli", "nyy", "se", "god", "ya", "dom", "ash", "zav",
+1 -1
View File
@@ -2,7 +2,7 @@
name = "Galactic Uncommon"
desc = "The second-most spoken Human language."
key = "!"
flags = TONGUELESS_SPEECH
flags = LANGUAGE_TONGUELESS_SPEECH
space_chance = 20
sentence_chance = 0
between_word_sentence_chance = 10