Skip to content

Commit

Permalink
Merge pull request #120 from MortoSasye/language_menu
Browse files Browse the repository at this point in the history
Language Menu + Yangyu -> Konjin
  • Loading branch information
Ephemeralis authored Oct 1, 2024
2 parents a08185c + 8954a43 commit d4d3839
Show file tree
Hide file tree
Showing 26 changed files with 546 additions and 18 deletions.
2 changes: 2 additions & 0 deletions code/__DEFINES/~doppler_defines/declarations.dm
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,5 @@
#define TRAIT_EXCITABLE "wagwag"
/// Trait for hemophages particularly!
#define TRAIT_OXYIMMUNE "oxyimmune" // Immune to oxygen damage, ideally give this to all non-breathing species or bad stuff will happen
// Trait for extra language point.
#define TRAIT_LINGUIST "Linguist"
5 changes: 5 additions & 0 deletions code/__DEFINES/~doppler_defines/language.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#define LANGUAGE_UNDERSTOOD 1
#define LANGUAGE_SPOKEN 2
// LANGUAGE SOURCE DEFINES
/// Use this when granting languages in special() for spawner roles, to prevent prefs from removing them
#define LANGUAGE_SPAWNER "spawner"
1 change: 1 addition & 0 deletions code/_globalvars/traits/_traits.dm
Original file line number Diff line number Diff line change
Expand Up @@ -699,6 +699,7 @@ GLOBAL_LIST_INIT(traits_by_type, list(
"TRAIT_EXCITABLE" = TRAIT_EXCITABLE,
"TRAIT_TWITCH_ADAPTED" = TRAIT_TWITCH_ADAPTED,
"TRAIT_GOLEM_LIMBATTACHMENT" = TRAIT_GOLEM_LIMBATTACHMENT,
"TRAIT_LINGUIST" = TRAIT_LINGUIST,
),
// DOPPLER EDIT ADDITION END
))
Expand Down
1 change: 1 addition & 0 deletions code/_globalvars/traits/admin_tooling.dm
Original file line number Diff line number Diff line change
Expand Up @@ -382,6 +382,7 @@ GLOBAL_LIST_INIT(admin_visible_traits, list(
"TRAIT_DETECTIVE" = TRAIT_DETECTIVE,
"TRAIT_EXCITABLE" = TRAIT_EXCITABLE,
"TRAIT_TWITCH_ADAPTED" = TRAIT_TWITCH_ADAPTED,
"TRAIT_LINGUIST" = TRAIT_LINGUIST
),
// DOPPLER EDIT ADDITION END
))
Expand Down
12 changes: 12 additions & 0 deletions code/modules/client/preferences/middleware/_middleware.dm
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,15 @@
/// Called when a character is changed.
/datum/preference_middleware/proc/on_new_character(mob/user)
return

//DOPPLER EDIT
/// Called after every update_preference, returns TRUE if this handled it.
/datum/preference_middleware/proc/post_set_preference(mob/user, preference, value)
return FALSE

/// Called when applying preferences to the mob.
/datum/preference_middleware/proc/apply_to_human(mob/living/carbon/human/target, datum/preferences/preferences, visuals_only = FALSE) //NOVA EDIT CHANGE
SHOULD_NOT_SLEEP(TRUE)
SHOULD_CALL_PARENT(FALSE)
return
//DOPPLER EDIT END
1 change: 0 additions & 1 deletion code/modules/surgery/organs/internal/tongue/_tongue.dm
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,6 @@
/datum/language/shadowtongue,
/datum/language/terrum,
/datum/language/nekomimetic,
/datum/language/yangyu, //DOPPLER ADDITION
)

/obj/item/organ/internal/tongue/proc/handle_speech(datum/source, list/speech_args)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,4 @@
icon_state = "omgkittyhiii"
icon = 'modular_doppler/hearthkin/primitive_genemod/icons/language_icon.dmi'
default_priority = 94
// secret = TRUE //this needs a dedicated module for language
secret = TRUE
22 changes: 22 additions & 0 deletions modular_doppler/languages/code/language menu/_language.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/datum/language/
/// Should this be hidden on the language buy menu?
var/secret = FALSE


/datum/language/aphasia
secret = TRUE

/datum/language/codespeak
secret = TRUE

/datum/language/drone
secret = TRUE

/datum/language/narsie
secret = TRUE

/datum/language/piratespeak
secret = TRUE

/datum/language/xenocommon
secret = TRUE
169 changes: 169 additions & 0 deletions modular_doppler/languages/code/language menu/client_languages.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
#define MAX_LANGUAGES_NORMAL 3
#define MAX_LANGUAGES_LINGUIST 4

/datum/asset/spritesheet/languages
name = "languages"
early = TRUE
cross_round_cachable = TRUE

/datum/asset/spritesheet/languages/create_spritesheets()
var/list/to_insert = list()

if(!GLOB.all_languages.len)
stack_trace("Warning: Language spritesheets could not be created because language subsystem has not been loaded yet. This should not happen--adjust the init_order in master_files/code/controllers/subsystem/language.dm.")
return

for (var/language_name in GLOB.all_languages)
var/datum/language/language = GLOB.language_datum_instances[language_name]
var/icon/language_icon = icon(language.icon, icon_state = language.icon_state)
to_insert[sanitize_css_class_name(language.name)] = language_icon

for (var/spritesheet_key in to_insert)
Insert(spritesheet_key, to_insert[spritesheet_key])

/// Middleware to handle languages
/datum/preference_middleware/languages
/// A associative list of language names to their typepath
var/static/list/name_to_language = list()
action_delegations = list(
"give_language" = PROC_REF(give_language),
"remove_language" = PROC_REF(remove_language),
)

/datum/preference_middleware/languages/apply_to_human(mob/living/carbon/human/target, datum/preferences/preferences, visuals_only = FALSE)
var/datum/language_holder/language_holder = target.get_language_holder()
language_holder.adjust_languages_to_prefs(preferences)

/datum/preference_middleware/languages/get_ui_assets()
return list(
get_asset_datum(/datum/asset/spritesheet/languages),
)

/datum/preference_middleware/languages/post_set_preference(mob/user, preference, value)
if(preference != "species")
return
preferences.languages = list()
var/species_type = preferences.read_preference(/datum/preference/choiced/species)
var/datum/species/species = new species_type()
var/datum/language_holder/lang_holder = new species.species_language_holder()
for(var/language in preferences.get_adjusted_language_holder())
preferences.languages[language] = LANGUAGE_SPOKEN
qdel(lang_holder)
qdel(species)

for(var/language in lang_holder.spoken_languages)
preferences.languages[language] = LANGUAGE_SPOKEN

qdel(lang_holder)
qdel(species)

return ..()

/datum/preference_middleware/languages/get_ui_data(mob/user)
if(length(name_to_language) != length(GLOB.all_languages))
initialize_name_to_language()

var/list/data = list()

var/max_languages = preferences.all_quirks.Find(TRAIT_LINGUIST) ? MAX_LANGUAGES_LINGUIST : MAX_LANGUAGES_NORMAL
var/species_type = preferences.read_preference(/datum/preference/choiced/species)
var/datum/species/species = new species_type()
var/datum/language_holder/lang_holder = preferences.get_adjusted_language_holder()
if(!preferences.languages || !preferences.languages.len || (preferences.languages && preferences.languages.len > max_languages)) // Too many languages, or no languages.
preferences.languages = list()
for(var/language in lang_holder.spoken_languages)
preferences.languages[language] = LANGUAGE_SPOKEN

var/list/selected_languages = list()
var/list/unselected_languages = list()

for (var/language_name in GLOB.all_languages)
var/datum/language/language = GLOB.language_datum_instances[language_name]

if(language.secret && !(language.type in species.language_prefs_whitelist)) // For ghostrole species who are able to speak a secret language, e.g. ashwalkers, display it.
continue

if(species.always_customizable && !(language.type in lang_holder.spoken_languages)) // For the ghostrole species. We don't want ashwalkers speaking beachtongue now.
continue
if(preferences.languages[language.type])
selected_languages += list(list(
"description" = language.desc,
"name" = language.name,
"icon" = sanitize_css_class_name(language.name)
))
else
unselected_languages += list(list(
"description" = language.desc,
"name" = language.name,
"icon" = sanitize_css_class_name(language.name)
))

qdel(lang_holder)
qdel(species)

data["total_language_points"] = max_languages
data["selected_languages"] = selected_languages
data["unselected_languages"] = unselected_languages
return data

/// (Re-)Initializes the `name_to_language` associative list, to ensure that it's properly populated.
/datum/preference_middleware/languages/proc/initialize_name_to_language()
name_to_language = list()
for(var/language_name in GLOB.all_languages)
var/datum/language/language = GLOB.language_datum_instances[language_name]
name_to_language[language.name] = language_name

/**
* Proc that gives a language to a character, granted that they don't already have too many
* of them, based on their maximum amount of languages.
*
* Arguments:
* * params - List of parameters, given to us by the `act()` method from TGUI. Needs to
* contain a value under `"language_name"`.
*
* Returns TRUE all the time, to ensure that the UI is updated.
*/
/datum/preference_middleware/languages/proc/give_language(list/params)
var/language_name = params["language_name"]
var/max_languages = preferences.all_quirks.Find(TRAIT_LINGUIST) ? MAX_LANGUAGES_LINGUIST : MAX_LANGUAGES_NORMAL

if(preferences.languages && preferences.languages.len == max_languages) // too many languages
return TRUE

preferences.languages[name_to_language[language_name]] = LANGUAGE_SPOKEN
return TRUE

/**
* Proc that removes a language from a character.
*
* Arguments:
* * params - List of parameters, given to us by the `act()` method from TGUI. Needs to
* contain a value under `"language_name"`.
*
* Returns TRUE all the time, to ensure that the UI is updated.
*/
/datum/preference_middleware/languages/proc/remove_language(list/params)
var/language_name = params["language_name"]
preferences.languages -= name_to_language[language_name]
return TRUE

/// Cleans up any invalid languages. Typically happens on language renames and codedels.
/datum/preferences/proc/sanitize_languages()
var/languages_edited = FALSE
for(var/lang_path as anything in languages)
if(!lang_path)
languages.Remove(lang_path)
languages_edited = TRUE
continue

var/datum/language/language = new lang_path()
// Yes, checking subtypes is VERY necessary, because byond doesn't check to see if a path is valid at runtime!
// If you delete /datum/language/meme, it will still load as /datum/language/meme, and will instantiate with /datum/language's defaults!
var/species_type = read_preference(/datum/preference/choiced/species)
var/datum/species/species = new species_type()
if(!(language.type in subtypesof(/datum/language)) || (language.secret && !(language.type in species.language_prefs_whitelist)))
languages.Remove(lang_path)
languages_edited = TRUE
qdel(species)
qdel(language)
return languages_edited
111 changes: 111 additions & 0 deletions modular_doppler/languages/code/language menu/language_holder.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
GLOBAL_DATUM_INIT(language_holder_adjustor, /datum/language_holder_adjustor, new)

/// Language code needs to be purged. Make sure, once and for all, that we get the correct languages on spawn.
/// Every time a crew member joins the adjustor will personally fix their language, because there is too much coupling between mind and language code to do it reliably otherwise.
/// It has already needed to be fixed like 3 times. This will (hopefully) be the final time.
/datum/language_holder_adjustor/New()
RegisterSignal(SSdcs, COMSIG_GLOB_CREWMEMBER_JOINED, PROC_REF(handle_new_player))

/datum/language_holder_adjustor/proc/handle_new_player(datum/source, mob/living/carbon/human/new_crewmember, rank)
SIGNAL_HANDLER

// sanity checking because we really do not want to be causing any runtimes
if(!istype(new_crewmember))
return
if(isnull(new_crewmember.mind))
return

var/datum/language_holder/language_holder = new_crewmember.get_language_holder()

if(isnull(language_holder))
return

language_holder.adjust_languages_to_prefs(new_crewmember.client?.prefs)

/datum/language_holder_adjustor/Destroy()
..()
UnregisterSignal(SSdcs, COMSIG_GLOB_CREWMEMBER_JOINED)

/datum/language_holder/proc/adjust_languages_to_prefs(datum/preferences/preferences)
// no prefs? then don't remove any languages
if(!preferences)
return

// remove the innate languages (like common, and other species languages) and instead use the language prefs
// do not remove any languages granted by spawners, which are denoted by source = LANGUAGE_SPAWNER
remove_languages_by_source(list(LANGUAGE_MIND, LANGUAGE_ATOM, LANGUAGE_SPECIES))

for(var/lang_path in preferences.languages)
grant_language(lang_path)

get_selected_language()

/// Removes every language whose source(s) match the provided source list arg
/datum/language_holder/proc/remove_languages_by_source(list/sources)
if(!length(sources))
return
for(var/language in understood_languages)
for(var/source in sources)
remove_language(language, ALL, source)
// in most cases spoken_languages should be empty by now, but just in case we should remove what's left
for(var/language in spoken_languages)
for(var/source in sources)
remove_language(language, ALL, source)

//************************************************
//* Specific language holders *
//* Use atom language sources only. *
//************************************************/

/datum/language_holder/machine // SYNTHETIC LIZARD & CO LANGUAGE
understood_languages = list(/datum/language/common = list(LANGUAGE_ATOM),
/datum/language/machine = list(LANGUAGE_ATOM))
spoken_languages = list(/datum/language/common = list(LANGUAGE_ATOM),
/datum/language/machine = list(LANGUAGE_ATOM))

/// Modularized the Cyborg and AI language_holder, add here the languages that you want them to be able to speak and understand.
/datum/language_holder/synthetic
understood_languages = list(
/datum/language/common = list(LANGUAGE_ATOM),
/datum/language/uncommon = list(LANGUAGE_ATOM),
/datum/language/machine = list(LANGUAGE_ATOM),
/datum/language/draconic = list(LANGUAGE_ATOM),
/datum/language/moffic = list(LANGUAGE_ATOM),
/datum/language/calcic = list(LANGUAGE_ATOM),
/datum/language/voltaic = list(LANGUAGE_ATOM),
/datum/language/nekomimetic = list(LANGUAGE_ATOM),
/datum/language/gutter = list(LANGUAGE_ATOM),
/datum/language/konjin = list(LANGUAGE_ATOM),
/datum/language/monkey = list(LANGUAGE_ATOM),
/datum/language/slime = list(LANGUAGE_ATOM),
/datum/language/beachbum = list(LANGUAGE_ATOM),
/datum/language/mushroom = list(LANGUAGE_ATOM),
/datum/language/shadowtongue = list(LANGUAGE_ATOM),
/datum/language/buzzwords = list(LANGUAGE_ATOM),
/datum/language/terrum = list(LANGUAGE_ATOM),
/datum/language/sylvan = list(LANGUAGE_ATOM),
)
spoken_languages = list(
/datum/language/common = list(LANGUAGE_ATOM),
/datum/language/uncommon = list(LANGUAGE_ATOM),
/datum/language/machine = list(LANGUAGE_ATOM),
/datum/language/draconic = list(LANGUAGE_ATOM),
/datum/language/moffic = list(LANGUAGE_ATOM),
/datum/language/calcic = list(LANGUAGE_ATOM),
/datum/language/voltaic = list(LANGUAGE_ATOM),
/datum/language/nekomimetic = list(LANGUAGE_ATOM),
/datum/language/gutter = list(LANGUAGE_ATOM),
/datum/language/konjin = list(LANGUAGE_ATOM),
/datum/language/monkey = list(LANGUAGE_ATOM),
/datum/language/slime = list(LANGUAGE_ATOM),
/datum/language/beachbum = list(LANGUAGE_ATOM),
/datum/language/mushroom = list(LANGUAGE_ATOM),
/datum/language/shadowtongue = list(LANGUAGE_ATOM),
/datum/language/buzzwords = list(LANGUAGE_ATOM),
/datum/language/terrum = list(LANGUAGE_ATOM),
/datum/language/sylvan = list(LANGUAGE_ATOM),
)

/datum/language_holder/drone_nova
understood_languages = list(/datum/language/drone = list(LANGUAGE_ATOM), /datum/language/common = list(LANGUAGE_ATOM))
spoken_languages = list(/datum/language/drone = list(LANGUAGE_ATOM))
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#define MAX_MUTANT_ROWS 4

/datum/preferences
/// Associative list, keyed by language typepath, pointing to LANGUAGE_UNDERSTOOD, or LANGUAGE_SPOKEN, for whether we understand or speak the language
var/list/languages = list()

/datum/preferences/proc/species_updated(species_type)
all_quirks = list()
// Reset cultural stuff
languages[try_get_common_language()] = LANGUAGE_SPOKEN
save_character()

/// This helper proc gets the current species language holder and does any post-processing that's required in one easy to track place.
/// This proc should *always* be edited or used when modifying or getting the default languages of a player controlled, unrestricted species, to prevent any errant conflicts.
/datum/preferences/proc/get_adjusted_language_holder()
var/datum/species/species = read_preference(/datum/preference/choiced/species)
species = new species()
var/datum/language_holder/language_holder = new species.species_language_holder()

// Do language post procesing here. Used to house our foreigner functionality.
// I saw little reason to remove this proc, considering it makes code using this a little easier to read.

return language_holder

/// Tries to get the topmost language of the language holder. Should be the species' native language, and if it isn't, you should pester a coder.
/datum/preferences/proc/try_get_common_language()
var/datum/language_holder/language_holder = get_adjusted_language_holder()
var/language = language_holder.spoken_languages[1]
return language
Loading

0 comments on commit d4d3839

Please sign in to comment.