Adds customizable language keys to language menu and character setup

This commit is contained in:
Casey
2022-08-21 15:40:43 -04:00
committed by CHOMPStation2
parent 8898dc5d39
commit 144fd1b219
8 changed files with 303 additions and 14 deletions

View File

@@ -11,15 +11,20 @@
S["extra_languages"] >> pref.extra_languages
testing("LANGSANI: Loaded from [pref.client]'s character [pref.real_name || "-name not yet loaded-"] savefile: [english_list(pref.alternate_languages || list())]")
S["language_prefixes"] >> pref.language_prefixes
<<<<<<< HEAD
//CHOMPEdit Begin
S["species"] >> pref.species
//CHOMPEdit End
=======
S["language_custom_keys"] >> pref.language_custom_keys
>>>>>>> 574adc02e0... Merge pull request #13541 from Heroman3003/customizable_key
/datum/category_item/player_setup_item/general/language/save_character(var/savefile/S)
S["language"] << pref.alternate_languages
S["extra_languages"] << pref.extra_languages
testing("LANGSANI: Saved to [pref.client]'s character [pref.real_name || "-name not yet loaded-"] savefile: [english_list(pref.alternate_languages || list())]")
S["language_prefixes"] << pref.language_prefixes
S["language_custom_keys"] << pref.language_custom_keys
/datum/category_item/player_setup_item/general/language/sanitize_character()
if(!islist(pref.alternate_languages)) pref.alternate_languages = list()
@@ -43,6 +48,14 @@
for(var/prefix in pref.language_prefixes)
if(prefix in forbidden_prefixes)
pref.language_prefixes -= prefix
if(isnull(pref.language_custom_keys))
pref.language_custom_keys = list()
var/datum/species/S = GLOB.all_species[pref.species]
for(var/key in pref.language_custom_keys)
if(!pref.language_custom_keys[key])
pref.language_custom_keys.Remove(key)
if(!((pref.language_custom_keys[key] == S.language) || (pref.language_custom_keys[key] == S.default_language && S.default_language != S.language) || (pref.language_custom_keys[key] in pref.alternate_languages)))
pref.language_custom_keys.Remove(key)
/datum/category_item/player_setup_item/general/language/content()
. += "<b>Languages</b><br>"
@@ -51,14 +64,19 @@
testing("LANGSANI: Truncated [pref.client]'s character [pref.real_name || "-name not yet loaded-"] language list because it was too long (len: [pref.alternate_languages.len], allowed: [S.num_alternate_languages])")
pref.alternate_languages.len = (S.num_alternate_languages + pref.extra_languages) // Truncate to allowed length
if(S.language)
. += "- [S.language]<br>"
. += "- [S.language] - <a href='?src=\ref[src];set_custom_key=[S.language]'>Set Custom Key</a><br>"
if(S.default_language && S.default_language != S.language)
. += "- [S.default_language]<br>"
. += "- [S.default_language] - <a href='?src=\ref[src];set_custom_key=[S.default_language]'>Set Custom Key</a><br>"
if(S.num_alternate_languages + pref.extra_languages)
if(pref.alternate_languages.len)
for(var/i = 1 to pref.alternate_languages.len)
var/lang = pref.alternate_languages[i]
<<<<<<< HEAD
. += "- [lang] - <a href='?src=\ref[src];remove_language=[i]'>remove</a><br>"
=======
. += "- [lang] - <a href='?src=\ref[src];remove_language=[i]'>remove</a> - <a href='?src=\ref[src];set_custom_key=[lang]'>Set Custom Key</a><br>"
>>>>>>> 574adc02e0... Merge pull request #13541 from Heroman3003/customizable_key
if(pref.alternate_languages.len < (S.num_alternate_languages + pref.extra_languages))
. += "- <a href='?src=\ref[src];add_language=1'>add</a> ([(S.num_alternate_languages + pref.extra_languages) - pref.alternate_languages.len] remaining)<br>"
else
@@ -125,4 +143,32 @@
pref.language_prefixes = config.language_prefixes.Copy()
return TOPIC_REFRESH
else if(href_list["set_custom_key"])
var/lang = href_list["set_custom_key"]
if(!(lang in GLOB.all_languages))
return TOPIC_REFRESH
var/oldkey = ""
for(var/key in pref.language_custom_keys)
if(pref.language_custom_keys[key] == lang)
oldkey = key
break
var/char = tgui_input_text(user, "Input a language key for [lang]. Input a single space to reset.", "Language Custom Key", oldkey)
if(length(char) != 1)
return TOPIC_REFRESH
else if(char == " ")
for(var/key in pref.language_custom_keys)
if(pref.language_custom_keys[key] == lang)
pref.language_custom_keys -= key
break
else if(contains_az09(char))
if(!(char in pref.language_custom_keys))
pref.language_custom_keys += char
pref.language_custom_keys[char] = lang
else
tgui_alert_async(user, "Improper language key. Rejected.", "Error")
return TOPIC_REFRESH
return ..()

View File

@@ -63,7 +63,8 @@ var/list/preferences_datums = list()
var/species = SPECIES_HUMAN //Species datum to use.
var/species_preview //Used for the species selection window.
var/list/alternate_languages = list() //Secondary language(s)
var/list/language_prefixes = list() //Kanguage prefix keys
var/list/language_prefixes = list() //Language prefix keys
var/list/language_custom_keys = list() //Language custom call keys
var/list/gear //Left in for Legacy reasons, will no longer save.
var/list/gear_list = list() //Custom/fluff item loadouts.
var/gear_slot = 1 //The current gear save slot

View File

@@ -192,6 +192,9 @@
/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)
@@ -240,7 +243,8 @@
for(var/datum/language/L in languages)
if(!(L.flags & NONGLOBAL))
. += "<b>[L.name] ([get_language_prefix()][L.key])</b><br/>[L.desc]<br/><br/>"
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()
. = ""
@@ -250,12 +254,13 @@
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])</b> - default - <a href='byond://?src=\ref[src];default_lang=reset'>reset</a><br/>[L.desc]<br/><br/>"
. += "<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])</b> - <a href='byond://?src=\ref[src];default_lang=\ref[L]'>set default</a><br/>[L.desc]<br/><br/>"
. += "<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])</b> - cannot speak!<br/>[L.desc]<br/><br/>"
. += "<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"
@@ -279,6 +284,22 @@
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 ..()
@@ -287,5 +308,17 @@
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

View File

@@ -94,6 +94,7 @@
var/list/pinned = list() // List of things pinning this creature to walls (see living_defense.dm)
var/list/embedded = list() // Embedded items, since simple mobs don't have organs.
var/list/languages = list() // For speaking/listening.
var/list/language_keys = list() // List of language keys indexing languages
var/species_language = null // For species who want reset to use a specified default.
var/only_species_language = 0 // For species who can only speak their default and no other languages. Does not affect understanding.
var/list/speak_emote = list("says") // Verbs used when speaking. Defaults to 'say' if speak_emote is null.

View File

@@ -623,6 +623,11 @@
if(chosen_language)
if(is_lang_whitelisted(src,chosen_language) || (new_character.species && (chosen_language.name in new_character.species.secondary_langs)))
new_character.add_language(lang)
for(var/key in client.prefs.language_custom_keys)
if(client.prefs.language_custom_keys[key])
var/datum/language/keylang = GLOB.all_languages[client.prefs.language_custom_keys[key]]
if(keylang)
new_character.language_keys[key] = keylang
// And uncomment this, too.
//new_character.dna.UpdateSE()

View File

@@ -193,6 +193,8 @@
// Okay, we're definitely now trying to invoke a language (probably)
// This "[]" is probably unnecessary but BYOND will runtime if a number is used
var/datum/language/L = GLOB.language_keys["[language_key]"]
if((language_key in language_keys) && language_keys[language_key])
L = language_keys[language_key]
// MULTILINGUAL_SPACE enforces a space after the language key
if(client && (client.prefs.multilingual_mode == MULTILINGUAL_SPACE) && (text2ascii(copytext(selection, 3, 4)) != 32)) // If we're looking for a space and we don't find one

View File

@@ -78,6 +78,7 @@
if(!is_alien_whitelisted(ghost, GLOB.all_species[ghost_client?.prefs?.species]) && !check_rights(R_ADMIN, 0)) // Prevents a ghost ghosting in on a slot and spawning via a resleever with race they're not whitelisted for, getting around normal join restrictions.
to_chat(ghost, "<span class='warning'>You are not whitelisted to spawn as this species!</span>")
return
<<<<<<< HEAD
// CHOMPedit start
var/datum/species/chosen_species
@@ -89,6 +90,19 @@
return
// CHOMPEdit End: Add checks for Whitelist + Resleeving
=======
/* // Comments out NO_SCAN restriction, as per headmin/maintainer request.
var/datum/species/chosen_species
if(ghost.client.prefs.species) // In case we somehow don't have a species set here.
chosen_species = GLOB.all_species[ghost_client.prefs.species]
if(chosen_species.flags && NO_SCAN) // Sanity. Prevents species like Xenochimera, Proteans, etc from rejoining the round via resleeve, as they should have their own methods of doing so already, as agreed to when you whitelist as them.
to_chat(ghost, "<span class='warning'>This species cannot be resleeved!</span>")
return
*/
>>>>>>> 574adc02e0... Merge pull request #13541 from Heroman3003/customizable_key
//Name matching is ugly but mind doesn't persist to look at.
var/charjob
var/datum/data/record/record_found
@@ -167,6 +181,11 @@
if(chosen_language)
if(is_lang_whitelisted(src,chosen_language) || (new_character.species && (chosen_language.name in new_character.species.secondary_langs)))
new_character.add_language(lang)
for(var/key in ghost_client.prefs.language_custom_keys)
if(ghost_client.prefs.language_custom_keys[key])
var/datum/language/keylang = GLOB.all_languages[ghost_client.prefs.language_custom_keys[key]]
if(keylang)
new_character.language_keys[key] = keylang
//If desired, apply equipment.
if(equip_body)

View File

@@ -0,0 +1,182 @@
/mob/observer/dead/verb/spawn_in_belly()
set category = "Ghost"
set name = "Spawn In Belly"
set desc = "Spawn in someone's belly."
if(!client)
return
// If any ghost-side restrictions are desired, they'll go here
tgui_alert(src,{"
This verb allows you to spawn inside someone's belly when they are in round.
Make sure you to coordinate with your predator OOCly as well as roleplay approprietly.
You are considered to have been in the belly entire time the predator was around and are not added to crew lists.
This is not intended to be used for mechanical advantage or providing assistance, but for facilitating longterm scenes.
Please do not abuse this ability.
"},"OOC Warning") // Warning.
var/list/eligible_targets = list()
for(var/mob/living/pred in living_mob_list)
if(!istype(pred) || !pred.client) // Ignore preds that aren't living mobs or player controlled
continue
if(pred.no_vore) // No vore, no bellies, no inbelly spawning
continue
if(!(get_z(pred) in using_map.station_levels)) // No explo reinforcements
continue
if(ishuman(pred))
var/mob/living/carbon/human/H = pred
if(!H.allow_inbelly_spawning)
continue
eligible_targets += H
continue
if(issilicon(pred))
var/mob/living/silicon/S = pred
if(isAI(S))
continue // Sorry, AI buddies. Your vore works too differently.
if(!S.allow_inbelly_spawning)
continue
eligible_targets += S
continue
if(istype(pred, /mob/living/simple_mob))
var/mob/living/simple_mob/SM = pred
if(!SM.vore_active) // No vore, no bellies, no inbelly spawning
continue
if(!SM.allow_inbelly_spawning)
continue
eligible_targets += SM
continue
// Only humans, simple_mobs and non-AI silicons are included. Obscure stuff like bots is skipped.
if(!eligible_targets.len)
to_chat(src, "<span class=notice>No eligible preds were found.</span>") // :(
return
var/mob/living/target = tgui_input_list(src, "Please specify which character you want to spawn inside of.", "Predator", eligible_targets) // Offer the list of things we gathered.
if(!target || !client) // Did out target cease to exist? Or did we?
return
// Notify them that its now pred's turn
to_chat(src, "<span class=notice>Inbelly spawn request sent to predator.</span>")
target.inbelly_spawn_prompt(client) // Hand reins over to them
/mob/living/proc/inbelly_spawn_prompt(client/potential_prey)
if(!potential_prey || !istype(potential_prey)) // Did our prey cease to exist?
return
// Are we cool with this prey spawning in at all?
var/answer = tgui_alert(src, "[potential_prey.ckey] (as [potential_prey.prefs.real_name]) wants to spawn in one of your bellies. Do you accept?", "Inbelly Spawning", list("Yes", "No"))
if(answer != "Yes")
to_chat(potential_prey, "<span class=notice>Your request was turned down.</span>")
return
// Let them know so that they don't spam it.
to_chat(potential_prey, "<span class=notice>Predator agreed to your request. Wait a bit while they choose a belly.</span>")
// Where we dropping?
var/obj/belly/belly_choice = tgui_input_list(src, "Choose Target Belly", "Belly Choice", src.vore_organs)
// Wdym nowhere?
if(!belly_choice || !istype(belly_choice))
to_chat(potential_prey, "<span class=notice>Something went wrong with predator selecting a belly. Try again?</span>")
to_chat(src, "<span class=notice>No valid belly selected. Inbelly spawn cancelled.</span>")
return
// Extra caution never hurts
if(belly_choice.digest_mode == DM_DIGEST)
var/digest_answer = tgui_alert(src, "[belly_choice] is currently set to Digest. Are you sure you want to spawn prey there?", "Inbelly Spawning", list("Yes", "No"))
if(digest_answer != "Yes")
to_chat(potential_prey, "<span class=notice>Something went wrong with predator selecting a belly. Try again?</span>")
to_chat(src, "<span class=notice>Inbelly spawn cancelled.</span>")
// Are they already fat (and/or appropriate equivalent)?
var/absorbed = FALSE
var/absorbed_answer = tgui_alert(src, "Do you want them to start absorbed?", "Inbelly Spawning", list("Yes", "No"))
if(absorbed_answer == "Yes")
absorbed = TRUE
// They disappeared?
if(!potential_prey)
to_chat(src, "<span class=notice>No prey found. Something went wrong!</span>")
return
// Final confirmation for pred
var/confirmation_pred = tgui_alert(src, "Are you certain that you want [potential_prey.prefs.real_name] spawned in your [belly_choice][absorbed ? ", absorbed" : ""]?", "Inbelly Spawning", list("Yes", "No"))
if(confirmation_pred != "Yes")
to_chat(potential_prey, "<span class=notice>Your pred couldn't finish selection. Try again?</span>")
to_chat(src, "<span class=notice>Inbelly spawn cancelled.</span>")
return
to_chat(src, "<span class=notice>Waiting for prey's confirmation...</span>")
// And final confirmation for prey
var/confirmation_prey = tgui_alert(potential_prey, "Are you certain that you to spawn in [src]'s [belly_choice][absorbed ? ", absorbed" : ""]?", "Inbelly Spawning", list("Yes", "No"))
if(confirmation_prey == "Yes" && potential_prey && src && belly_choice)
//Now we finally spawn them in!
if(!is_alien_whitelisted(potential_prey, GLOB.all_species[potential_prey.prefs.species]))
to_chat(potential_prey, "<span class=notice>You are not whitelisted to play as currently selected character.</span>")
to_chat(src, "<span class=notice>Prey accepted the confirmation, but something went wrong with spawning their character.</span>")
return
inbelly_spawn(potential_prey, src, belly_choice, absorbed)
else
to_chat(potential_prey, "<span class=notice>Inbelly spawn cancelled.</span>")
to_chat(src, "<span class=notice>Prey cancelled their inbelly spawn request.</span>")
return
/proc/inbelly_spawn(client/prey, mob/living/pred, obj/belly/target_belly, var/absorbed = FALSE)
// All this is basically admin late spawn-in, but skipping all parts related to records and equipment and with predteremined location
var/player_key = prey.key
var/picked_ckey = prey.ckey
var/picked_slot = prey.prefs.default_slot
var/mob/living/carbon/human/new_character
new_character = new(null) // Spawn them in nullspace first. Can't have "Defaultname Defaultnameson slides into your Stomach".
if(!new_character)
return
prey.prefs.copy_to(new_character)
if(new_character.dna)
new_character.dna.ResetUIFrom(new_character)
new_character.sync_organ_dna()
new_character.key = player_key
if(new_character.mind)
var/datum/antagonist/antag_data = get_antag_data(new_character.mind.special_role)
if(antag_data)
antag_data.add_antagonist(new_character.mind)
antag_data.place_mob(new_character)
if(new_character.mind)
new_character.mind.loaded_from_ckey = picked_ckey
new_character.mind.loaded_from_slot = picked_slot
for(var/lang in prey.prefs.alternate_languages)
var/datum/language/chosen_language = GLOB.all_languages[lang]
if(chosen_language)
if(is_lang_whitelisted(prey,chosen_language) || (new_character.species && (chosen_language.name in new_character.species.secondary_langs)))
new_character.add_language(lang)
for(var/key in prey.prefs.language_custom_keys)
if(prey.prefs.language_custom_keys[key])
var/datum/language/keylang = GLOB.all_languages[prey.prefs.language_custom_keys[key]]
if(keylang)
new_character.language_keys[key] = keylang
new_character.regenerate_icons()
new_character.update_transform()
new_character.forceMove(target_belly) // Now that they're all setup and configured, send them to their destination.
if(absorbed)
target_belly.absorb_living(new_character) // Glorp.
log_admin("[prey] (as [new_character.real_name] has spawned inside one of [pred]'s bellies.") // Log it. Avoid abuse.
message_admins("[prey] (as [new_character.real_name] has spawned inside one of [pred]'s bellies.", 1)
return new_character // incase its ever needed