diff --git a/code/__DEFINES/traits.dm b/code/__DEFINES/traits.dm
index b7750556d5..3d041edfd2 100644
--- a/code/__DEFINES/traits.dm
+++ b/code/__DEFINES/traits.dm
@@ -197,6 +197,7 @@
#define TRAIT_EMPATH "empath"
#define TRAIT_FRIENDLY "friendly"
#define TRAIT_SNOB "snob"
+#define TRAIT_MULTILINGUAL "multilingual"
#define TRAIT_CULT_EYES "cult_eyes"
#define TRAIT_AUTO_CATCH_ITEM "auto_catch_item"
#define TRAIT_CLOWN_MENTALITY "clown_mentality" // The future is now, clownman.
diff --git a/code/datums/traits/good.dm b/code/datums/traits/good.dm
index 23fd75f982..4a4108f005 100644
--- a/code/datums/traits/good.dm
+++ b/code/datums/traits/good.dm
@@ -219,3 +219,19 @@
/datum/quirk/night_vision/on_spawn()
var/mob/living/carbon/human/H = quirk_holder
H.update_sight()
+
+/datum/quirk/multilingual
+ name = "Multi-Lingual"
+ desc = "You spent a portion of your life learning to understand an additional language. You may or may not be able to speak it based on your anatomy."
+ value = 1
+ mob_trait = TRAIT_MULTILINGUAL
+ gain_text = "You've learned an extra language!"
+ lose_text = "You've forgotten your extra language."
+
+/datum/quirk/multilingual/add()
+ var/mob/living/carbon/human/H = quirk_holder
+ H.grant_language(H.client.prefs.language, TRUE, TRUE, LANGUAGE_MIND)
+
+/datum/quirk/multilingual/remove()
+ var/mob/living/carbon/human/H = quirk_holder
+ H.remove_language(H.client.prefs.language, TRUE, TRUE, LANGUAGE_MIND)
diff --git a/code/game/objects/items/devices/radio/headset.dm b/code/game/objects/items/devices/radio/headset.dm
index 939d77a1f0..015b8774ad 100644
--- a/code/game/objects/items/devices/radio/headset.dm
+++ b/code/game/objects/items/devices/radio/headset.dm
@@ -69,7 +69,8 @@ GLOBAL_LIST_INIT(channel_tokens, list(
/obj/item/radio/headset/talk_into(mob/living/M, message, channel, list/spans,datum/language/language)
if (!listening)
return ITALICS | REDUCE_RANGE
- return ..()
+ if (language != /datum/language/signlanguage)
+ return ..()
/obj/item/radio/headset/can_receive(freq, level, AIuser)
if(ishuman(src.loc))
diff --git a/code/game/objects/items/devices/radio/radio.dm b/code/game/objects/items/devices/radio/radio.dm
index 4623a405de..3435a0d0f0 100644
--- a/code/game/objects/items/devices/radio/radio.dm
+++ b/code/game/objects/items/devices/radio/radio.dm
@@ -208,6 +208,8 @@
return
if(!M.IsVocal())
return
+ if(language == /datum/language/signlanguage)
+ return
if(use_command)
spans |= commandspan
diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm
index 7cc8be800c..572c71b1e0 100644
--- a/code/modules/client/preferences.dm
+++ b/code/modules/client/preferences.dm
@@ -99,6 +99,8 @@ GLOBAL_LIST_EMPTY(preferences_datums)
var/be_random_body = 0 //whether we'll have a random body every round
var/gender = MALE //gender of character (well duh)
var/age = 30 //age of character
+ var/language = "Random" //bonus language
+ var/choselanguage = "Random" //language appearance
var/underwear = "Nude" //underwear type
var/undie_color = "FFFFFF"
var/undershirt = "Nude" //undershirt type
@@ -308,6 +310,7 @@ GLOBAL_LIST_EMPTY(preferences_datums)
dat += "Gender: [gender == MALE ? "Male" : (gender == FEMALE ? "Female" : (gender == PLURAL ? "Non-binary" : "Object"))]
"
dat += "Age: [age]
"
+ dat += "Language: [choselanguage]
"
dat += "Special Names:
"
var/old_group
@@ -2315,6 +2318,28 @@ GLOBAL_LIST_EMPTY(preferences_datums)
features["body_model"] = chosengender
gender = chosengender
+ if("language")
+ choselanguage = input(user, "Select a language.", "Language", language) as null|anything in list("Beachtongue","Draconic","Dwarven",
+ "Chimpanzee","Space Sign Language","Random")
+ if(!choselanguage)
+ return
+ switch(choselanguage)
+ if("Rachidian")
+ language = /datum/language/arachnid
+ if("Beachtongue")
+ language = /datum/language/beachbum
+ if("Draconic")
+ language = /datum/language/draconic
+ if("Dwarven")
+ language = /datum/language/dwarf
+ if("Chimpanzee")
+ language = /datum/language/monkey
+ if("Space Sign Language")
+ language = /datum/language/signlanguage
+ if("Random")
+ language = pick(list("Rachidian", "Beachtongue","Draconic","Dwarven",
+ "Chimpanzee","Space Sign Language"))
+
if("body_size")
var/new_body_size = input(user, "Choose your desired sprite size: (90-125%)\nWarning: This may make your character look distorted. Additionally, any size under 100% takes a 10% maximum health penalty", "Character Preference", features["body_size"]*100) as num|null
if(new_body_size)
diff --git a/code/modules/client/preferences_savefile.dm b/code/modules/client/preferences_savefile.dm
index 0db9fbb66c..088f7753cf 100644
--- a/code/modules/client/preferences_savefile.dm
+++ b/code/modules/client/preferences_savefile.dm
@@ -604,6 +604,8 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
S["body_model"] >> features["body_model"]
S["body_size"] >> features["body_size"]
S["age"] >> age
+ S["language"] >> language
+ S["choselanguage"] >> choselanguage
S["hair_color"] >> hair_color
S["facial_hair_color"] >> facial_hair_color
S["eye_type"] >> eye_type
@@ -947,6 +949,8 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
WRITE_FILE(S["body_model"] , features["body_model"])
WRITE_FILE(S["body_size"] , features["body_size"])
WRITE_FILE(S["age"] , age)
+ WRITE_FILE(S["language"] , language)
+ WRITE_FILE(S["choselanguage"] , choselanguage)
WRITE_FILE(S["hair_color"] , hair_color)
WRITE_FILE(S["facial_hair_color"] , facial_hair_color)
WRITE_FILE(S["eye_type"] , eye_type)
diff --git a/code/modules/language/signlanguage.dm b/code/modules/language/signlanguage.dm
new file mode 100644
index 0000000000..97705d4a4c
--- /dev/null
+++ b/code/modules/language/signlanguage.dm
@@ -0,0 +1,12 @@
+/datum/language/signlanguage
+ name = "Space Sign Language"
+ desc = "Those who cannot speak can learn this instead."
+ speech_verb = "signs"
+ whisper_verb = "gestures"
+ key = "9"
+ flags = TONGUELESS_SPEECH
+
+ syllables = list(".")
+
+ icon_state = "ssl"
+ default_priority = 90
diff --git a/code/modules/mob/living/carbon/say.dm b/code/modules/mob/living/carbon/say.dm
index 27d0e9cbaf..d75843a3e5 100644
--- a/code/modules/mob/living/carbon/say.dm
+++ b/code/modules/mob/living/carbon/say.dm
@@ -10,6 +10,8 @@
/mob/living/carbon/can_speak_vocal(message)
if(silent)
return 0
+ if(get_message_language(message) == /datum/language/signlanguage && (handcuffed || (!src.get_bodypart(BODY_ZONE_L_ARM) && !src.get_bodypart(BODY_ZONE_R_ARM)) || get_num_held_items() == held_items.len))
+ return 0
return ..()
/mob/living/carbon/could_speak_language(datum/language/language)
diff --git a/code/modules/mob/living/say.dm b/code/modules/mob/living/say.dm
index 4c03e74d70..606daed0c5 100644
--- a/code/modules/mob/living/say.dm
+++ b/code/modules/mob/living/say.dm
@@ -329,7 +329,7 @@ GLOBAL_LIST_INIT(department_radio_keys, list(
return 1
/mob/living/proc/can_speak_vocal(message) //Check AFTER handling of xeno and ling channels
- if(HAS_TRAIT(src, TRAIT_MUTE))
+ if(HAS_TRAIT(src, TRAIT_MUTE) && get_message_language(message) != /datum/language/signlanguage)
return 0
if(is_muzzled())
diff --git a/tgstation.dme b/tgstation.dme
index 5ea3b652e7..ee8b47c91f 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -2336,6 +2336,7 @@
#include "code\modules\language\mushroom.dm"
#include "code\modules\language\narsian.dm"
#include "code\modules\language\ratvarian.dm"
+#include "code\modules\language\signlanguage.dm"
#include "code\modules\language\slime.dm"
#include "code\modules\language\swarmer.dm"
#include "code\modules\language\sylvan.dm"