Files
VOREStation/code/modules/mob/language/language.dm
VerySoft 9327ccb9a1 Fancy!!!
Makes a few changes to things, most of which are functionally identical to before.

Changes the shadekin empathy color to something unique, and more visible in dark mode than the changeling color. Also gives it a special font.

Makes a slightly lighter color as an alt color for dark mode

Makes it so that hivemind languages don't HAVE to be italics, but makes it so all the existing hivemind languages minus shadekin empathy are italicized. (The font I picked for empathy was hard to read while italics)
2023-09-11 18:18:12 -04:00

346 lines
13 KiB
Plaintext

#define SCRAMBLE_CACHE_LEN 20
/*
Datum based languages. Easily editable and modular.
*/
/datum/language
var/name = "an unknown language" // Fluff name of language if any.
var/desc = "A language." // Short description for 'Check Languages'.
var/speech_verb = "says" // 'says', 'hisses', 'farts'.
var/ask_verb = "asks" // Used when sentence ends in a ?
var/exclaim_verb = "exclaims" // Used when sentence ends in a !
var/whisper_verb // Optional. When not specified speech_verb + quietly/softly is used instead.
var/signlang_verb = list("signs", "gestures") // list of emotes that might be displayed if this language has NONVERBAL or SIGNLANG flags
var/signlang_verb_understood = list("signs") // snowflake bs, used only for echo
var/colour = "body" // CSS style to use for strings in this language.
var/key = "x" // Character used to speak in language eg. :o for Unathi.
var/flags = 0 // Various language flags.
var/native // If set, non-native speakers will have trouble speaking.
var/list/syllables // Used when scrambling text for a non-speaker.
var/list/space_chance = 55 // Likelihood of getting a space in the random scramble string
var/machine_understands = 1 // Whether machines can parse and understand this language
var/list/partial_understanding // List of languages that can /somehwat/ understand it, format is: name = chance of understanding a word
var/ignore_adverb = FALSE // For inaudible languages that we dont want adverb for
/datum/language/proc/get_random_name(var/gender, name_count=2, syllable_count=4, syllable_divisor=2)
if(!syllables || !syllables.len)
if(gender==FEMALE)
return capitalize(pick(first_names_female)) + " " + capitalize(pick(last_names))
else
return capitalize(pick(first_names_male)) + " " + capitalize(pick(last_names))
var/full_name = ""
var/new_name = ""
for(var/i = 0;i<name_count;i++)
new_name = ""
for(var/x = rand(FLOOR(syllable_count/syllable_divisor, 1),syllable_count);x>0;x--)
new_name += pick(syllables)
full_name += " [capitalize(lowertext(new_name))]"
return "[trim(full_name)]"
/datum/language
var/list/scramble_cache = list()
/datum/language/proc/scramble(var/input, var/list/known_languages)
var/understand_chance = 0
for(var/datum/language/L in known_languages)
if(partial_understanding && partial_understanding[L.name])
understand_chance += partial_understanding[L.name]
if(L.partial_understanding && L.partial_understanding[name])
understand_chance += L.partial_understanding[name] * 0.5
var/scrambled_text = ""
var/list/words = splittext(input, " ")
for(var/w in words)
if(prob(understand_chance))
scrambled_text += " [w] "
else
var/nword = scramble_word(w)
var/ending = copytext(scrambled_text, length(scrambled_text)-1)
if(findtext(ending,"."))
nword = capitalize(nword)
else if(findtext(ending,"!"))
nword = capitalize(nword)
else if(findtext(ending,"?"))
nword = capitalize(nword)
scrambled_text += nword
scrambled_text = replacetext(scrambled_text," "," ")
scrambled_text = capitalize(scrambled_text)
scrambled_text = trim(scrambled_text)
var/ending = copytext(scrambled_text, length(scrambled_text))
if(ending == ".")
scrambled_text = copytext(scrambled_text,1,length(scrambled_text)-1)
var/input_ending = copytext(input, length(input))
if(input_ending in list("!","?","."))
scrambled_text += input_ending
return scrambled_text
/datum/language/proc/scramble_word(var/input)
if(!syllables || !syllables.len)
return stars(input)
// If the input is cached already, move it to the end of the cache and return it
if(input in scramble_cache)
var/n = scramble_cache[input]
scramble_cache -= input
scramble_cache[input] = n
return n
var/input_size = length(input)
var/scrambled_text = ""
var/capitalize = 0
while(length(scrambled_text) < input_size)
var/next = pick(syllables)
if(capitalize)
next = capitalize(next)
capitalize = 0
scrambled_text += next
var/chance = rand(100)
if(chance <= 5)
scrambled_text += ". "
capitalize = 1
else if(chance > 5 && chance <= space_chance)
scrambled_text += " "
// 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, scramble_cache.len-SCRAMBLE_CACHE_LEN-1)
return scrambled_text
/datum/language/proc/format_message(message, verb)
return "<span class='message'><span class='[colour]'>[message]</span></span>"
/datum/language/proc/format_message_plain(message, verb)
return "[capitalize(message)]"
/datum/language/proc/format_message_radio(message, verb)
return "<span class='[colour]'>[capitalize(message)]</span>"
/datum/language/proc/get_talkinto_msg_range(message)
// if you yell, you'll be heard from two tiles over instead of one
return (copytext(message, length(message)) == "!") ? 2 : 1
/datum/language/proc/broadcast(var/mob/living/speaker,var/message,var/speaker_mask)
log_say("(HIVE) [message]", speaker)
speaker.verbs |= /mob/proc/adjust_hive_range
if(!speaker_mask) speaker_mask = speaker.real_name
message = "[get_spoken_verb(message)], \"[format_message(message, get_spoken_verb(message))]\""
//VOREStation Edit Start
if(speaker.hive_lang_range == -1)
var/turf/t = get_turf(speaker)
for(var/mob/player in player_list)
var/turf/b = get_turf(player)
if (t.z == b.z)
player.hear_broadcast(src, speaker, speaker_mask, message)
else if(speaker.hive_lang_range)
var/turf/t = get_turf(speaker)
for(var/mob/player in player_list)
var/turf/b = get_turf(player)
if(get_dist(t,b) <= speaker.hive_lang_range)
player.hear_broadcast(src, speaker, speaker_mask, message)
else
for(var/mob/player in player_list)
player.hear_broadcast(src, speaker, speaker_mask, message)
//VOREStation Edit End
/mob/proc/hear_broadcast(var/datum/language/language, var/mob/speaker, var/speaker_name, var/message)
if((language in languages) && language.check_special_condition(src))
var/msg = "<i><span class='game say'>[language.name], <span class='name'>[speaker_name]</i></span> [message]</span>"
to_chat(src,msg)
/mob/new_player/hear_broadcast(var/datum/language/language, var/mob/speaker, var/speaker_name, var/message)
return
/mob/observer/dead/hear_broadcast(var/datum/language/language, var/mob/speaker, var/speaker_name, var/message)
if(speaker.name == speaker_name || antagHUD)
to_chat(src, "<i><span class='game say'>[language.name], <span class='name'>[speaker_name]</i></span> ([ghost_follow_link(speaker, src)]) [message]</span>")
else
to_chat(src, "<i><span class='game say'>[language.name], <span class='name'>[speaker_name]</i></span> [message]</span>")
/datum/language/proc/check_special_condition(var/mob/other)
return 1
/datum/language/proc/get_spoken_verb(var/msg_end)
switch(msg_end)
if("!")
return exclaim_verb
if("?")
return ask_verb
return speech_verb
/datum/language/proc/can_speak_special(var/mob/speaker)
. = TRUE
if(name != "Noise") // Audible Emotes
if(ishuman(speaker))
var/mob/living/carbon/human/H = speaker
if(H.species.has_organ[O_VOICE] && !(flags & SIGNLANG) && !(flags & NONVERBAL)) // Does the species need a voicebox? Is the language even spoken?
var/obj/item/organ/internal/voicebox/vocal = H.internal_organs_by_name[O_VOICE]
if(!vocal || vocal.is_broken() || vocal.mute)
return FALSE
if(src.name in H.species.assisted_langs)
. = FALSE
var/obj/item/organ/internal/voicebox/vox = locate() in H.internal_organs // Only voiceboxes for now. Maybe someday it'll include other organs, but I'm not that clever
if(vox)
if(!vox.is_broken() && (src in vox.assists_languages))
. = TRUE
// Language handling.
/mob/proc/add_language(var/language)
var/datum/language/new_language = GLOB.all_languages[language]
if(!istype(new_language) || (new_language in languages))
return 0
languages.Add(new_language)
//VOREStation Addition Start
if(new_language.flags & HIVEMIND)
verbs |= /mob/proc/adjust_hive_range
//VOREStation Addition End
return 1
/mob/proc/remove_language(var/rem_language)
var/datum/language/L = GLOB.all_languages[rem_language]
. = (L in languages)
var/prefix = get_custom_prefix_by_lang(src, L)
if(prefix)
language_keys.Remove(prefix)
languages.Remove(L)
/mob/living/remove_language(rem_language)
var/datum/language/L = GLOB.all_languages[rem_language]
if(default_language == L)
default_language = null
return ..()
// Can we speak this language, as opposed to just understanding it?
/mob/proc/can_speak(datum/language/speaking)
//Prevents someone from speaking a null language.
if(!speaking)
log_debug("[src] attempted to speak a null language.")
return 0
if(speaking == GLOB.all_languages["Noise"])
return 1
if (only_species_language && speaking != GLOB.all_languages[species_language])
return 0
if(speaking.can_speak_special(src))
if(universal_speak)
return 1
if(speaking && (speaking.flags & INNATE))
return 1
if(speaking in src.languages)
return 1
return 0
/mob/proc/get_language_prefix()
if(client && client.prefs.language_prefixes && client.prefs.language_prefixes.len)
return client.prefs.language_prefixes[1]
return config.language_prefixes[1]
/mob/proc/is_language_prefix(var/prefix)
if(client && client.prefs.language_prefixes && client.prefs.language_prefixes.len)
return prefix in client.prefs.language_prefixes
return prefix in config.language_prefixes
//TBD
/mob/proc/check_lang_data()
. = ""
for(var/datum/language/L in languages)
if(!(L.flags & NONGLOBAL))
var/lang_key = get_custom_prefix_by_lang(src, L)
. += "<b>[L.name] ([get_language_prefix()][L.key][lang_key ? " [get_language_prefix()][lang_key]" : ""])</b><br/>[L.desc]<br/><br/>"
/mob/living/check_lang_data()
. = ""
if(default_language)
. += "Current default language: [default_language] - <a href='byond://?src=\ref[src];default_lang=reset'>reset</a><br/><br/>"
for(var/datum/language/L in languages)
if(!(L.flags & NONGLOBAL))
var/lang_key = get_custom_prefix_by_lang(src, L)
if(L == default_language)
. += "<b>[L.name] ([get_language_prefix()][L.key][lang_key ? " [get_language_prefix()][lang_key]" : ""])</b> <a href='byond://?src=\ref[src];set_lang_key=\ref[L]'>Edit Custom Key</a> - default - <a href='byond://?src=\ref[src];default_lang=reset'>reset</a><br/>[L.desc]<br/><br/>"
else if (can_speak(L))
. += "<b>[L.name] ([get_language_prefix()][L.key][lang_key ? " [get_language_prefix()][lang_key]" : ""])</b> <a href='byond://?src=\ref[src];set_lang_key=\ref[L]'>Edit Custom Key</a> - <a href='byond://?src=\ref[src];default_lang=\ref[L]'>set default</a><br/>[L.desc]<br/><br/>"
else
. += "<b>[L.name] ([get_language_prefix()][L.key][lang_key ? " [get_language_prefix()][lang_key]" : ""])</b> <a href='byond://?src=\ref[src];set_lang_key=\ref[L]'>Edit Custom Key</a> - cannot speak!<br/>[L.desc]<br/><br/>"
/mob/verb/check_languages()
set name = "Check Known Languages"
set category = "IC"
set src = usr
var/datum/browser/popup = new(src, "checklanguage", "Known Languages", 420, 470)
popup.set_content(check_lang_data())
popup.open()
/mob/living/Topic(href, href_list)
if(href_list["default_lang"])
if(href_list["default_lang"] == "reset")
if (species_language)
set_default_language(GLOB.all_languages[species_language])
else
set_default_language(GLOB.all_languages[LANGUAGE_GIBBERISH])
else
var/datum/language/L = locate(href_list["default_lang"])
if(L && (L in languages))
set_default_language(L)
check_languages()
return 1
else if(href_list["set_lang_key"])
var/datum/language/L = locate(href_list["set_lang_key"])
if(L && (L in languages))
var/old_key = get_custom_prefix_by_lang(src, L)
var/custom_key = tgui_input_text(src, "Input a new key for [L.name]", "Language Key", old_key)
if(custom_key && length(custom_key) == 1)
if(contains_az09(custom_key))
language_keys[custom_key] = L
if(old_key && old_key != custom_key)
language_keys.Remove(old_key)
else if(custom_key == " ")
if(old_key && old_key != custom_key)
language_keys.Remove(old_key)
else
tgui_alert_async(src, "Improper language key. Rejected.", "Error")
check_languages()
else
return ..()
/proc/transfer_languages(var/mob/source, var/mob/target, var/except_flags)
for(var/datum/language/L in source.languages)
if(L.flags & except_flags)
continue
target.add_language(L.name)
for(var/key in source.language_keys)
if(L == source.language_keys[key])
if(!(key in target.language_keys))
target.language_keys[key] = L
/proc/get_custom_prefix_by_lang(var/mob/our_mob, var/language)
if(!our_mob || !our_mob.language_keys.len || !language)
return
for(var/key in our_mob.language_keys)
if(our_mob.language_keys[key] == language)
return key
#undef SCRAMBLE_CACHE_LEN