diff --git a/code/modules/language/_language_menu.dm b/code/modules/language/_language_menu.dm
index 905be8169e2..487c0e67be1 100644
--- a/code/modules/language/_language_menu.dm
+++ b/code/modules/language/_language_menu.dm
@@ -12,6 +12,9 @@
return GLOB.language_menu_state
/datum/language_menu/ui_interact(mob/user, datum/tgui/ui)
+ if(isnull(language_holder.selected_language))
+ language_holder.get_selected_language()
+
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, "LanguageMenu")
@@ -21,38 +24,26 @@
var/list/data = list()
var/atom/movable/speaker = language_holder.owner
- data["is_living"] = isliving(speaker)
data["languages"] = list()
for(var/datum/language/language as anything in GLOB.all_languages)
- var/result = language_holder.has_language(language) || language_holder.has_language(language, SPOKEN_LANGUAGE)
- if(!result)
- continue
var/list/lang_data = list()
lang_data["name"] = initial(language.name)
lang_data["desc"] = initial(language.desc)
lang_data["key"] = initial(language.key)
lang_data["is_default"] = (language == language_holder.selected_language)
+ lang_data["icon"] = initial(language.icon)
+ lang_data["icon_state"] = initial(language.icon_state)
if(speaker)
- lang_data["can_speak"] = speaker.can_speak_language(language)
- lang_data["can_understand"] = speaker.has_language(language)
+ 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)
UNTYPED_LIST_ADD(data["languages"], lang_data)
- if(check_rights_for(user.client, R_ADMIN) || isobserver(speaker))
- data["admin_mode"] = TRUE
- data["omnitongue"] = language_holder.omnitongue
- data["unknown_languages"] = list()
- for(var/datum/language/language as anything in GLOB.all_languages)
- if(language_holder.has_language(language) || language_holder.has_language(language, SPOKEN_LANGUAGE))
- continue
- var/list/lang_data = list()
-
- lang_data["name"] = initial(language.name)
- lang_data["desc"] = initial(language.desc)
- lang_data["key"] = initial(language.key)
-
- UNTYPED_LIST_ADD(data["unknown_languages"], lang_data)
+ data["is_living"] = isliving(speaker)
+ data["admin_mode"] = check_rights_for(user.client, R_ADMIN) || isobserver(speaker)
+ data["omnitongue"] = language_holder.omnitongue
return data
@@ -60,7 +51,8 @@
. = ..()
if(.)
return
- var/mob/user = usr
+
+ var/mob/user = ui.user
var/atom/movable/speaker = language_holder.owner
var/is_admin = check_rights_for(user.client, R_ADMIN)
var/language_name = params["language_name"]
diff --git a/tgui/packages/tgui/interfaces/LanguageMenu.jsx b/tgui/packages/tgui/interfaces/LanguageMenu.jsx
deleted file mode 100644
index 404ac8484ef..00000000000
--- a/tgui/packages/tgui/interfaces/LanguageMenu.jsx
+++ /dev/null
@@ -1,112 +0,0 @@
-import { Button, LabeledList, Section } from 'tgui-core/components';
-
-import { useBackend } from '../backend';
-import { Window } from '../layouts';
-
-export const LanguageMenu = (props) => {
- const { act, data } = useBackend();
- const {
- admin_mode,
- is_living,
- omnitongue,
- languages = [],
- unknown_languages = [],
- } = data;
- return (
-
-
-
-
- {languages.map((language) => (
-
- {!!is_living && (
-
- ))}
-
-
- {!!admin_mode && (
- act('toggle_omnitongue')}
- />
- }
- >
-
- {unknown_languages.map((language) => (
-
- act('grant_language', {
- language_name: language.name,
- })
- }
- />
- }
- >
- {language.desc} Key: ,{language.key}{' '}
- {!!language.shadow && '(gained from mob)'}{' '}
- {language.can_speak ? 'Can speak.' : 'Cannot speak.'}
-
- ))}
-
-
- )}
-
-
- );
-};
diff --git a/tgui/packages/tgui/interfaces/LanguageMenu.tsx b/tgui/packages/tgui/interfaces/LanguageMenu.tsx
new file mode 100644
index 00000000000..d134000ddd5
--- /dev/null
+++ b/tgui/packages/tgui/interfaces/LanguageMenu.tsx
@@ -0,0 +1,276 @@
+import {
+ Box,
+ Button,
+ DmIcon,
+ Icon,
+ Section,
+ Stack,
+ Table,
+ Tooltip,
+} from 'tgui-core/components';
+import { BooleanLike } from 'tgui-core/react';
+
+import { useBackend } from '../backend';
+import { Window } from '../layouts';
+
+type Language = {
+ name: string;
+ desc: string;
+ key: string; // the key used to speak the language
+ is_default: BooleanLike; // the user's selected default language
+ can_speak: BooleanLike; // mentally, we know how to speak it
+ could_speak: BooleanLike; // physically, we are capable of speaking it
+ can_understand: BooleanLike; // we can understand it regardless
+ icon: string;
+ icon_state: string;
+};
+
+type Data = {
+ is_living: BooleanLike;
+ admin_mode: BooleanLike;
+ omnitongue: BooleanLike;
+ languages: Language[];
+};
+
+type LanguageProps = {
+ language: Language;
+};
+
+type LanguagePropsPassRest = LanguageProps & {
+ [key: string]: any;
+};
+
+const LangSpeakIcon = (props: LanguagePropsPassRest) => {
+ const { language, ...rest } = props;
+ return (
+
+ );
+};
+
+const LangUnderstandIcon = (props: LanguageProps) => {
+ const { language } = props;
+ return ;
+};
+
+const LanguageNameAndDesc = (props: LanguageProps) => {
+ const { language } = props;
+ return language.desc ? (
+
+
+ {language.name}
+
+
+ ) : (
+ {language.name}
+ );
+};
+
+const LanguageRow = (props: LanguageProps) => {
+ const { act, data } = useBackend();
+ const { is_living, admin_mode } = data;
+ const { language } = props;
+
+ return (
+
+
+ {language.icon && language.icon_state ? (
+
+
+
+
+
+
+
+
+ ) : (
+
+ )}
+
+
+ {!language.could_speak ? (
+
+
+
+ ) : (
+
+ )}
+
+
+
+
+ ,{language.key}
+ {!!is_living && (
+
+
+ act('select_default', {
+ language_name: language.name,
+ })
+ }
+ />
+
+ )}
+ {!!admin_mode && (
+
+
+ act('grant_language', {
+ language_name: language.name,
+ })
+ }
+ >
+ Grant
+
+
+ act('remove_language', {
+ language_name: language.name,
+ })
+ }
+ >
+ Remove
+
+
+ )}
+
+ );
+};
+
+const OmnitongueToggle = (props) => {
+ const { act, data } = useBackend();
+ const { omnitongue } = data;
+ return (
+ act('toggle_omnitongue')}
+ >
+ {'Omnitongue ' + (omnitongue ? 'Enabled' : 'Disabled')}
+
+ );
+};
+
+export const LanguageMenu = (props) => {
+ const { data } = useBackend();
+ const { admin_mode, is_living, languages } = data;
+
+ // only show languages we can speak OR understand, UNLESS we're an admin
+ // also, push all languages we can speak to the top, then all languagse we can only understand, then alphabetize
+ const shown_languages = languages
+ .filter(
+ (language) => admin_mode || language.can_speak || language.can_understand,
+ )
+ .sort(
+ (a, b) =>
+ ((a.can_speak ? 1 : 0) - (b.can_speak ? 1 : 0)) * -2 +
+ (a.name > b.name ? 1 : 0),
+ );
+
+ return (
+
+
+ - Admin Mode - : null}
+ buttons={admin_mode ? : null}
+ fill
+ >
+
+
+ Name
+ Speak
+ Understand
+
+
+
+ Key
+
+
+
+ {!!is_living && (
+
+
+
+ Default
+
+
+
+ )}
+ {!!admin_mode && }
+
+ {shown_languages.map((language) => (
+
+ ))}
+
+
+
+
+ );
+};