diff --git a/code/__DEFINES/traits.dm b/code/__DEFINES/traits.dm
index f50c4742910..c95f6a57d8a 100644
--- a/code/__DEFINES/traits.dm
+++ b/code/__DEFINES/traits.dm
@@ -436,6 +436,15 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
/// Trait granted by [/obj/item/clothing/head/helmet/space/hardsuit/berserker]
#define BERSERK_TRAIT "berserk_trait"
+/**
+* Trait granted by [/mob/living/carbon/Initialize] and
+* granted/removed by [/obj/item/organ/tongue]
+* Used for ensuring that carbons without tongues cannot taste anything
+* so it is added in Initialize, and then removed when a tongue is inserted
+* and readded when a tongue is removed.
+*/
+#define NO_TONGUE_TRAIT "no_tongue_trait"
+
/// Trait granted by [/mob/living/silicon/robot]
/// Traits applied to a silicon mob by their module.
#define MODULE_TRAIT "module_trait"
diff --git a/code/game/objects/items/taster.dm b/code/game/objects/items/taster.dm
index 3828beb9211..29fa6de03cb 100644
--- a/code/game/objects/items/taster.dm
+++ b/code/game/objects/items/taster.dm
@@ -6,8 +6,6 @@
w_class = WEIGHT_CLASS_TINY
- speech_span = null
-
var/taste_sensitivity = 15
/obj/item/taster/afterattack(atom/O, mob/user, proximity)
@@ -15,6 +13,10 @@
if(!proximity)
return
- if(O.reagents)
- var/message = O.reagents.generate_taste_message(taste_sensitivity)
+ if(!O.reagents)
+ to_chat(user, "[src] cannot taste [O], since [O.p_they()] [O.p_have()] have no reagents.")
+ else if(O.reagents.total_volume == 0)
+ to_chat(user, "[src] cannot taste [O], since [O.p_they()] [O.p_are()] empty.")
+ else
+ var/message = O.reagents.generate_taste_message(user, taste_sensitivity)
to_chat(user, "[src] tastes [message] in [O].")
diff --git a/code/modules/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm
index 05b5a2fb3db..21cfc8a0d00 100644
--- a/code/modules/mob/living/carbon/carbon.dm
+++ b/code/modules/mob/living/carbon/carbon.dm
@@ -3,6 +3,10 @@
create_reagents(1000)
assign_bodypart_ownership()
update_body_parts() //to update the carbon's new bodyparts appearance
+
+ // Carbons cannot taste anything without a tongue; the tongue organ removes this on Insert
+ ADD_TRAIT(src, TRAIT_AGEUSIA, NO_TONGUE_TRAIT)
+
GLOB.carbon_list += src
if(!mapload) //I don't want no gas leaks on my space ruin you hear?
RegisterSignal(src, COMSIG_LIVING_DEATH, .proc/attach_rot)
diff --git a/code/modules/mob/living/taste.dm b/code/modules/mob/living/taste.dm
index 014929f6eee..4c057e729c0 100644
--- a/code/modules/mob/living/taste.dm
+++ b/code/modules/mob/living/taste.dm
@@ -4,21 +4,32 @@
var/last_taste_time
var/last_taste_text
+/**
+ * Gets taste sensitivity of given mob
+ *
+ * This is used in calculating what flavours the mob can pick up,
+ * with a lower number being able to pick up more distinct flavours.
+ */
/mob/living/proc/get_taste_sensitivity()
return DEFAULT_TASTE_SENSITIVITY
/mob/living/carbon/get_taste_sensitivity()
var/obj/item/organ/tongue/tongue = getorganslot(ORGAN_SLOT_TONGUE)
- if(istype(tongue) && !HAS_TRAIT(src, TRAIT_AGEUSIA))
+ if(istype(tongue))
. = tongue.taste_sensitivity
else
- . = 101 // can't taste anything without a tongue
+ // carbons without tongues normally have TRAIT_AGEUSIA but sensible fallback
+ . = DEFAULT_TASTE_SENSITIVITY
// non destructively tastes a reagent container
/mob/living/proc/taste(datum/reagents/from)
+ if(HAS_TRAIT(src, TRAIT_AGEUSIA))
+ return
+
+
if(last_taste_time + 50 < world.time)
var/taste_sensitivity = get_taste_sensitivity()
- var/text_output = from.generate_taste_message(taste_sensitivity,src)
+ var/text_output = from.generate_taste_message(src, taste_sensitivity)
// We dont want to spam the same message over and over again at the
// person. Give it a bit of a buffer.
if(hallucination > 50 && prob(25))
diff --git a/code/modules/reagents/chemistry/holder.dm b/code/modules/reagents/chemistry/holder.dm
index 07bb01cf169..ce2c1b98532 100644
--- a/code/modules/reagents/chemistry/holder.dm
+++ b/code/modules/reagents/chemistry/holder.dm
@@ -946,9 +946,10 @@
* Returns what this holder's reagents taste like
*
* Arguments:
+ * * mob/living/taster - who is doing the tasting. Some mobs can pick up specific flavours.
* * minimum_percent - the lower the minimum percent, the more sensitive the message is.
*/
-/datum/reagents/proc/generate_taste_message(minimum_percent=15,mob/living/taster)
+/datum/reagents/proc/generate_taste_message(mob/living/taster, minimum_percent)
var/list/out = list()
var/list/tastes = list() //descriptor = strength
if(minimum_percent <= 100)
diff --git a/code/modules/surgery/organs/tongue.dm b/code/modules/surgery/organs/tongue.dm
index 10563177449..455c3d082df 100644
--- a/code/modules/surgery/organs/tongue.dm
+++ b/code/modules/surgery/organs/tongue.dm
@@ -8,6 +8,10 @@
attack_verb_simple = list("lick", "slobber", "slap", "french", "tongue")
var/list/languages_possible
var/say_mod = null
+
+ /// Whether the owner of this tongue can taste anything. Being set to FALSE will mean no taste feedback will be provided.
+ var/sense_of_taste = TRUE
+
var/taste_sensitivity = 15 // lower is more sensitive.
var/modifies_speech = FALSE
var/static/list/languages_possible_base = typecacheof(list(
@@ -43,12 +47,24 @@
RegisterSignal(M, COMSIG_MOB_SAY, .proc/handle_speech)
M.UnregisterSignal(M, COMSIG_MOB_SAY)
+ /* This could be slightly simpler, by making the removal of the
+ * NO_TONGUE_TRAIT conditional on the tongue's `sense_of_taste`, but
+ * then you can distinguish between ageusia from no tongue, and
+ * ageusia from having a non-tasting tongue.
+ */
+ REMOVE_TRAIT(M, TRAIT_AGEUSIA, NO_TONGUE_TRAIT)
+ if(!sense_of_taste)
+ ADD_TRAIT(M, TRAIT_AGEUSIA, ORGAN_TRAIT)
+
/obj/item/organ/tongue/Remove(mob/living/carbon/M, special = 0)
..()
if(say_mod && M.dna && M.dna.species)
M.dna.species.say_mod = initial(M.dna.species.say_mod)
UnregisterSignal(M, COMSIG_MOB_SAY, .proc/handle_speech)
M.RegisterSignal(M, COMSIG_MOB_SAY, /mob/living/carbon/.proc/handle_tongueless_speech)
+ REMOVE_TRAIT(M, TRAIT_AGEUSIA, ORGAN_TRAIT)
+ // Carbons by default start with NO_TONGUE_TRAIT caused TRAIT_AGEUSIA
+ ADD_TRAIT(M, TRAIT_AGEUSIA, NO_TONGUE_TRAIT)
/obj/item/organ/tongue/could_speak_language(language)
return is_type_in_typecache(language, languages_possible)
@@ -120,7 +136,7 @@
desc = "A mysterious structure that allows for instant communication between users. Pretty impressive until you need to eat something."
icon_state = "tongueayylmao"
say_mod = "gibbers"
- taste_sensitivity = 101 // ayys cannot taste anything.
+ sense_of_taste = FALSE
modifies_speech = TRUE
var/mothership
@@ -218,7 +234,7 @@
say_mod = "rattles"
attack_verb_continuous = list("bites", "chatters", "chomps", "enamelles", "bones")
attack_verb_simple = list("bite", "chatter", "chomp", "enamel", "bone")
- taste_sensitivity = 101 // skeletons cannot taste anything
+ sense_of_taste = FALSE
modifies_speech = TRUE
var/chattering = FALSE
var/phomeme_type = "sans"
@@ -279,7 +295,9 @@
speech_args[SPEECH_SPANS] |= SPAN_ROBOT
/obj/item/organ/tongue/snail
- name = "snailtongue"
+ name = "radula"
+ color = "#96DB00" // TODO proper sprite, rather than recoloured pink tongue
+ desc = "A minutely toothed, chitious ribbon, which as a side effect, makes all snails talk IINNCCRREEDDIIBBLLYY SSLLOOWWLLYY."
modifies_speech = TRUE
/obj/item/organ/tongue/snail/handle_speech(datum/source, list/speech_args)
@@ -299,7 +317,7 @@
say_mod = "crackles"
attack_verb_continuous = list("shocks", "jolts", "zaps")
attack_verb_simple = list("shock", "jolt", "zap")
- taste_sensitivity = 101 // Not a tongue, they can't taste shit
+ sense_of_taste = FALSE
var/static/list/languages_possible_ethereal = typecacheof(list(
/datum/language/common,
/datum/language/draconic,