diff --git a/code/__DEFINES/obj_flags.dm b/code/__DEFINES/obj_flags.dm
index fb738dc18ac..beee5f6c47d 100644
--- a/code/__DEFINES/obj_flags.dm
+++ b/code/__DEFINES/obj_flags.dm
@@ -59,6 +59,7 @@
#define ORGAN_VITAL (1<<4) //Currently only the brain
#define ORGAN_EDIBLE (1<<5) //is a snack? :D
#define ORGAN_SYNTHETIC_EMP (1<<6) //Synthetic organ affected by an EMP. Deteriorates over time.
+#define ORGAN_UNREMOVABLE (1<<7) //Can't be removed using surgery
/// Integrity defines for clothing (not flags but close enough)
#define CLOTHING_PRISTINE 0 // We have no damage on the clothing
diff --git a/code/__DEFINES/traits.dm b/code/__DEFINES/traits.dm
index 656c2f1592a..2b74bd98042 100644
--- a/code/__DEFINES/traits.dm
+++ b/code/__DEFINES/traits.dm
@@ -192,6 +192,7 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
#define TRAIT_NICE_SHOT "nice_shot" //hnnnnnnnggggg..... you're pretty good....
#define TRAIT_TUMOR_SUPPRESSED "brain_tumor_suppressed" //prevents the damage done by a brain tumor
#define TRAIT_PERMANENTLY_ONFIRE "permanently_onfire" //overrides the update_fire proc to always add fire (for lava)
+#define TRAIT_SIGN_LANG "sign_language" //Galactic Common Sign Language
//SKILLS
#define TRAIT_UNDERWATER_BASKETWEAVING_KNOWLEDGE "underwater_basketweaving"
diff --git a/code/_globalvars/traits.dm b/code/_globalvars/traits.dm
index 287d168c329..f196a136317 100644
--- a/code/_globalvars/traits.dm
+++ b/code/_globalvars/traits.dm
@@ -120,6 +120,7 @@ GLOBAL_LIST_INIT(traits_by_type, list(
"TRAIT_NICE_SHOT" = TRAIT_NICE_SHOT,
"TRAIT_TUMOR_SUPPRESSION" = TRAIT_TUMOR_SUPPRESSED,
"TRAIT_PERMANENTLY_ONFIRE" = TRAIT_PERMANENTLY_ONFIRE,
+ "TRAIT_SIGN_LANG" = TRAIT_SIGN_LANG,
"TRAIT_UNDERWATER_BASKETWEAVING_KNOWLEDGE" = TRAIT_UNDERWATER_BASKETWEAVING_KNOWLEDGE,
"TRAIT_WINE_TASTER" = TRAIT_WINE_TASTER,
"TRAIT_BONSAI" = TRAIT_BONSAI,
@@ -150,6 +151,7 @@ GLOBAL_LIST_INIT(traits_by_type, list(
"TRAIT_GRABWEAKNESS" = TRAIT_GRABWEAKNESS,
"TRAIT_SNOB" = TRAIT_SNOB,
"TRAIT_BALD" = TRAIT_BALD
+
),
/obj/item/bodypart = list(
"TRAIT_PARALYSIS" = TRAIT_PARALYSIS
diff --git a/code/datums/components/crafting/recipes.dm b/code/datums/components/crafting/recipes.dm
index 80fea40c72e..358d80ae207 100644
--- a/code/datums/components/crafting/recipes.dm
+++ b/code/datums/components/crafting/recipes.dm
@@ -385,6 +385,17 @@
/obj/item/organ/ears/cat = 1)
category = CAT_CLOTHING
+
+/datum/crafting_recipe/radiogloves
+ name = "Translation Gloves"
+ result = /obj/item/clothing/gloves/radio
+ time = 15
+ reqs = list(/obj/item/clothing/gloves/color/black = 1,
+ /obj/item/stack/cable_coil = 2,
+ /obj/item/radio = 1)
+ tools = list(TOOL_WIRECUTTER)
+ category = CAT_CLOTHING
+
/datum/crafting_recipe/mixedbouquet
name = "Mixed bouquet"
result = /obj/item/bouquet
@@ -414,6 +425,7 @@
parts = list(/obj/item/camera = 1)
category = CAT_MISC
+
/datum/crafting_recipe/skateboard
name = "Skateboard"
result = /obj/vehicle/ridden/scooter/skateboard
diff --git a/code/datums/traits/neutral.dm b/code/datums/traits/neutral.dm
index 9cdb7e3ae4f..94db88f2bcf 100644
--- a/code/datums/traits/neutral.dm
+++ b/code/datums/traits/neutral.dm
@@ -244,3 +244,27 @@
SIGNAL_HANDLER
SEND_SIGNAL(quirk_holder, COMSIG_ADD_MOOD_EVENT, "bad_hair_day", /datum/mood_event/bald)
+
+
+/datum/quirk/tongue_tied
+ name = "Tongue Tied"
+ desc = "Due to a past incident, your ability to communicate has been relegated to your hands."
+ value = 0
+ medical_record_text = "During physical examination, patient's tongue was found to be uniquely damaged."
+
+//Adds tongue & gloves
+/datum/quirk/tongue_tied/on_spawn()
+ var/mob/living/carbon/human/H = quirk_holder
+ var/obj/item/organ/tongue/old_tongue = locate() in H.internal_organs
+ var/obj/item/organ/tongue/tied/new_tongue = new(get_turf(H))
+ var/obj/item/clothing/gloves/radio/gloves = new(get_turf(H))
+ old_tongue.Remove(H)
+ new_tongue.Insert(H)
+ qdel(old_tongue)
+ H.put_in_hands(gloves)
+ H.equip_to_slot(gloves, ITEM_SLOT_GLOVES)
+ H.regenerate_icons()
+
+/datum/quirk/tongue_tied/post_add()
+ to_chat(quirk_holder, "Because you speak with your hands, having them full hinders your ability to communicate!")
+
diff --git a/code/game/objects/items/devices/radio/radio.dm b/code/game/objects/items/devices/radio/radio.dm
index 73e368a8236..5de46b3ae22 100644
--- a/code/game/objects/items/devices/radio/radio.dm
+++ b/code/game/objects/items/devices/radio/radio.dm
@@ -186,6 +186,19 @@
. = TRUE
/obj/item/radio/talk_into(atom/movable/M, message, channel, list/spans, datum/language/language, list/message_mods)
+ if(HAS_TRAIT(M, TRAIT_SIGN_LANG)) //Forces Sign Language users to wear the translation gloves to speak over radios
+ var/mob/living/carbon/mute = M
+ if(istype(mute))
+ var/empty_indexes = mute.get_empty_held_indexes() //How many hands the player has empty
+ var/obj/item/clothing/gloves/radio/G = mute.get_item_by_slot(ITEM_SLOT_GLOVES)
+ if(!istype(G))
+ return FALSE
+ if(length(empty_indexes) == 1)
+ message = stars(message)
+ if(length(empty_indexes) == 0) //Due to the requirement of gloves, the arm check for normal speech would be redundant here.
+ return FALSE
+ if(mute.handcuffed)//Would be weird if they couldn't sign but their words still went over the radio
+ return FALSE
if(!spans)
spans = list(M.speech_span)
if(!language)
diff --git a/code/modules/clothing/gloves/miscellaneous.dm b/code/modules/clothing/gloves/miscellaneous.dm
index ffbf74fe636..79353906d63 100644
--- a/code/modules/clothing/gloves/miscellaneous.dm
+++ b/code/modules/clothing/gloves/miscellaneous.dm
@@ -146,6 +146,12 @@
icon_state = "wizard"
inhand_icon_state = "purplegloves"
+/obj/item/clothing/gloves/radio
+ name = "translation gloves"
+ desc = "A pair of electronic gloves which connect to nearby radios wirelessly. Allows for sign language users to 'speak' over comms."
+ icon_state = "radio_g"
+ inhand_icon_state = "radio_g"
+
/obj/item/clothing/gloves/color/plasmaman/head_of_personnel
name = "head of personnel's envirogloves"
desc = "Covers up those scandalous, bony hands. Appears to be an attempt at making a replica of the captain's gloves."
diff --git a/code/modules/mob/living/carbon/carbon_say.dm b/code/modules/mob/living/carbon/carbon_say.dm
index b57d4e4d270..7ce6fca9030 100644
--- a/code/modules/mob/living/carbon/carbon_say.dm
+++ b/code/modules/mob/living/carbon/carbon_say.dm
@@ -11,7 +11,10 @@
/mob/living/carbon/can_speak_vocal(message)
if(silent)
- return 0
+ if(HAS_TRAIT(src, TRAIT_SIGN_LANG))
+ return ..()
+ else
+ return FALSE
return ..()
/mob/living/carbon/could_speak_language(datum/language/language)
diff --git a/code/modules/mob/living/living_say.dm b/code/modules/mob/living/living_say.dm
index b9a1eeac630..a19d680222c 100644
--- a/code/modules/mob/living/living_say.dm
+++ b/code/modules/mob/living/living_say.dm
@@ -222,6 +222,27 @@ GLOBAL_LIST_INIT(department_radio_keys, list(
var/deaf_message
var/deaf_type
+ if(HAS_TRAIT(speaker, TRAIT_SIGN_LANG)) //Checks if speaker is using sign language
+ deaf_message = compose_message(speaker, message_language, raw_message, radio_freq, spans, message_mods)
+ if(speaker != src)
+ if(!radio_freq) //I'm about 90% sure there's a way to make this less cluttered
+ deaf_type = 1
+ else
+ deaf_type = 2
+
+ // Create map text prior to modifying message for goonchat, sign lang edition
+ if (client?.prefs.chat_on_map && !(stat == UNCONSCIOUS || stat == HARD_CRIT) && (client.prefs.see_chat_non_mob || ismob(speaker)))
+ create_chat_message(speaker, message_language, raw_message, spans)
+
+ if(is_blind(src))
+ return FALSE
+
+
+ message = deaf_message
+
+ show_message(message, MSG_VISUAL, deaf_message, deaf_type, avoid_highlighting = speaker == src)
+ return message
+
if(speaker != src)
if(!radio_freq) //These checks have to be seperate, else people talking on the radio will make "You can't hear yourself!" appear when hearing people over the radio while deaf.
deaf_message = "[speaker] [speaker.verb_say] something but you cannot hear [speaker.p_them()]."
@@ -246,6 +267,24 @@ GLOBAL_LIST_INIT(department_radio_keys, list(
eavesdrop_range = EAVESDROP_EXTRA_RANGE
var/list/listening = get_hearers_in_view(message_range+eavesdrop_range, source)
var/list/the_dead = list()
+ if(HAS_TRAIT(src, TRAIT_SIGN_LANG))
+ var/mob/living/carbon/mute = src
+ if(istype(mute))
+ var/empty_indexes = get_empty_held_indexes() //How many hands the player has empty
+ if(length(empty_indexes) == 1 || !mute.get_bodypart(BODY_ZONE_L_ARM) || !mute.get_bodypart(BODY_ZONE_R_ARM))
+ message = stars(message)
+ if(length(empty_indexes) == 0)//Both hands full, can't sign
+ to_chat(src, "You can't sign with your hands full!")
+ return FALSE
+ if(!mute.get_bodypart(BODY_ZONE_L_ARM) && !mute.get_bodypart(BODY_ZONE_R_ARM))//Can't sign with no arms!
+ to_chat(src, "You can't sign with no hands!")
+ return FALSE
+ if(mute.handcuffed)//Can't sign when your hands are cuffed, but can at least make a visual effort to
+ mute.visible_message("[src] tries to sign, but can't with [src.p_their()] hands cuffed!")
+ return FALSE
+ if(mute.has_status_effect(STATUS_EFFECT_PARALYZED))
+ to_chat(src, "You can't sign in this state!")
+ return FALSE
if(client) //client is so that ghosts don't have to listen to mice
for(var/_M in GLOB.player_list)
var/mob/M = _M
@@ -303,14 +342,15 @@ GLOBAL_LIST_INIT(department_radio_keys, list(
return TRUE
/mob/living/proc/can_speak_vocal(message) //Check AFTER handling of xeno and ling channels
+ var/mob/living/carbon/human/H = src
if(HAS_TRAIT(src, TRAIT_MUTE))
- return FALSE
+ return (HAS_TRAIT(src, TRAIT_SIGN_LANG) && !H.mind.miming) //Makes sure mimes can't speak using sign language
if(is_muzzled())
- return FALSE
+ return (HAS_TRAIT(src, TRAIT_SIGN_LANG) && !H.mind.miming)
if(!IsVocal())
- return FALSE
+ return (HAS_TRAIT(src, TRAIT_SIGN_LANG) && !H.mind.miming)
return TRUE
@@ -376,9 +416,15 @@ GLOBAL_LIST_INIT(department_radio_keys, list(
else if(message_mods[MODE_SING])
. = verb_sing
else if(stuttering)
- . = "stammers"
+ if(HAS_TRAIT(src, TRAIT_SIGN_LANG))
+ . = "shakily signs"
+ else
+ . = "stammers"
else if(derpspeech)
- . = "gibbers"
+ if(HAS_TRAIT(src, TRAIT_SIGN_LANG))
+ . = "incoherently signs"
+ else
+ . = "gibbers"
else
. = ..()
diff --git a/code/modules/surgery/organ_manipulation.dm b/code/modules/surgery/organ_manipulation.dm
index 829a74b6225..dc5f90442d0 100644
--- a/code/modules/surgery/organ_manipulation.dm
+++ b/code/modules/surgery/organ_manipulation.dm
@@ -119,12 +119,16 @@
I = organs[I]
if(!I)
return -1
+ if(I.organ_flags & ORGAN_UNREMOVABLE)
+ to_chat(user, "[I] is too well connected to take out!")
+ return -1
display_results(user, target, "You begin to extract [I] from [target]'s [parse_zone(target_zone)]...",
"[user] begins to extract [I] from [target]'s [parse_zone(target_zone)].",
"[user] begins to extract something from [target]'s [parse_zone(target_zone)].")
else
return -1
+
/datum/surgery_step/manipulate_organs/success(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery, default_display_results)
if(current_type == "insert")
if(istype(tool, /obj/item/organ_storage))
diff --git a/code/modules/surgery/organs/tongue.dm b/code/modules/surgery/organs/tongue.dm
index 2acdd9d3a7a..4aacbd38612 100644
--- a/code/modules/surgery/organs/tongue.dm
+++ b/code/modules/surgery/organs/tongue.dm
@@ -318,3 +318,53 @@
/obj/item/organ/tongue/ethereal/Initialize(mapload)
. = ..()
languages_possible = languages_possible_ethereal
+
+//Sign Language Tongue - yep, that's how you speak sign language.
+/obj/item/organ/tongue/tied
+ name = "tied tongue"
+ desc = "If only one had a sword so we may finally untie this knot."
+ say_mod = "signs"
+ icon_state = "tonguetied"
+ modifies_speech = TRUE
+ organ_flags = ORGAN_UNREMOVABLE
+
+/obj/item/organ/tongue/tied/Insert(mob/living/carbon/M)
+ . = ..()
+ M.verb_ask = "signs"
+ M.verb_exclaim = "signs"
+ M.verb_whisper = "subtly signs"
+ M.verb_sing = "rythmically signs"
+ M.verb_yell = "emphatically signs"
+ ADD_TRAIT(M, TRAIT_SIGN_LANG, "tongue")
+ REMOVE_TRAIT(M, TRAIT_MUTE, "tongue")
+
+/obj/item/organ/tongue/tied/Remove(mob/living/carbon/M, special = 0)
+ ..()
+ M.verb_ask = initial(verb_ask)
+ M.verb_exclaim = initial(verb_exclaim)
+ M.verb_whisper = initial(verb_whisper)
+ M.verb_sing = initial(verb_sing)
+ M.verb_yell = initial(verb_yell)
+ REMOVE_TRAIT(M, TRAIT_SIGN_LANG, "tongue") //People who are Ahealed get "cured" of their sign language-having ways. If I knew how to make the tied tongue persist through aheals, I'd do that.
+
+//Thank you Jwapplephobia for helping me with the literal hellcode below
+
+/obj/item/organ/tongue/tied/handle_speech(datum/source, list/speech_args)
+ var/new_message
+ var/message = speech_args[SPEECH_MESSAGE]
+ var/exclamation_found = findtext(message, "!")
+ var/question_found = findtext(message, "?")
+ var/mob/living/carbon/M = owner
+ new_message = message
+ if(exclamation_found)
+ new_message = replacetext(new_message, "!", "")
+ if(question_found)
+ new_message = replacetext(new_message, "?", "")
+ speech_args[SPEECH_MESSAGE] = new_message
+
+ if(exclamation_found && question_found)
+ M.visible_message("[M] lowers one of [M.p_their()] eyebrows, raising the other.")
+ else if(exclamation_found)
+ M.visible_message("[M] raises [M.p_their()] eyebrows.")
+ else if(question_found)
+ M.visible_message("[M] lowers [M.p_their()] eyebrows.")
diff --git a/icons/mob/clothing/hands.dmi b/icons/mob/clothing/hands.dmi
index e00aac8c7d3..deaa2c08902 100644
Binary files a/icons/mob/clothing/hands.dmi and b/icons/mob/clothing/hands.dmi differ
diff --git a/icons/obj/clothing/gloves.dmi b/icons/obj/clothing/gloves.dmi
index d10a3931fa5..cbf0904277a 100644
Binary files a/icons/obj/clothing/gloves.dmi and b/icons/obj/clothing/gloves.dmi differ
diff --git a/icons/obj/surgery.dmi b/icons/obj/surgery.dmi
index c681bcfd235..ea1169737b8 100755
Binary files a/icons/obj/surgery.dmi and b/icons/obj/surgery.dmi differ