Files
8c3fc471d8 Refactor tasting code and reagent taste carrying, no longer taste your stomach contents or have non-existing flavours win out (#87329)
## About The Pull Request

Soooooooooooooooooooooooooooooooo tasting code has been jank for a good
damn while, and with this we aim to fix that- let's start from the
beginning.

Earlier when I was making a food item, I noticed that it'd never
actually have the taste I gave it, just tasting indescribable as things
without tastes do.
Weird, cause it definitely has a taste set, and plenty of nutriment
subtype reagents!...
When looking into it, I noticed that we only copy the tastes to
*specifically* the base nutriment and the vitamin subtype:

https://github.com/tgstation/tgstation/blob/57b718ad20c3b6af02dbdbc87f3cae4dd4d7e297/code/datums/components/food/edible.dm#L215-L216
But! Surely the other reagents should have tastes, right?.... No:

https://github.com/tgstation/tgstation/blob/57b718ad20c3b6af02dbdbc87f3cae4dd4d7e297/code/modules/reagents/chemistry/reagents/food_reagents.dm#L119-L120
we were always returning the tastes data, but never actually setting it
except for those two reagents, overriding any and all tastes.
This caused the non-taste of the proteins and fats to override the food
taste applied to the vitamins with their volume.

So, to fix this, we replace these hardcoded ids with a check for
`carry_food_tastes` on the nutriment reagent, such that each can
individually define whether they carry food tastes. This lets oils have
their own tastes, while letting all other nutriments carry the tastes.
We then make `get_taste_description(...)` return the base flavour if no
special flavour is found, and add base flavours to proteins and vitamins
based on what pure proteins/vitamins with no further additives would
roughly taste like.

This fixes our issue!....... Except, we now always taste something "rich
earthy pungent" when we eat. Looking into it, that's the organ tissue
reagent, used in our stomachs:

https://github.com/tgstation/tgstation/blob/57b718ad20c3b6af02dbdbc87f3cae4dd4d7e297/code/modules/reagents/chemistry/reagents/food_reagents.dm#L232-L236
....Because whenever we ingest something, we taste the contents of our
stomach. This was previously less noticeable, as while it *did* count,
it only added a weight for _no flavour_.

To fix this we just, well, make it so you taste what you're eating and
not the container itself.
It's as simple as separating the tasting code from the reagent holder,
and making the tasting that happens on ingest exposure use that logic
instead.
We keep the old logic for the INTJ skillchip and tasting machine, just
re-routed through this, and split up some procs to avoid too much
duplication.

This fixes our issue! Though, there's still some minor issues.
This lets us add organ tissue instead of nutriment to other organs so
they no longer taste indescribable, as it actually works properly now,
but letting organ tissue hold flavours would risk having the stomach
organ tissue permanently leech the flavours of the organ tissue you eat.
So we make a subtype of organ tissue for the stomach, stomach lining,
which cannot hold flavours.
This lets us apply the base organ tissue to all other organs without
risking stomach contamination.
## Why It's Good For The Game

It's jank to taste your stomach contents.
It's jank for organs to taste like nothing.
It's jank for reagents with flavours to never taste like anything.
It's jank for tasteless reagents to block out food flavours.
It's jank for organs to only sometimes contain organ tissues.
yeah.
## Changelog
🆑
refactor: Refactored tasting code. Please report any jank tasting
behaviours.
fix: You taste what you eat instead of the contents of your own stomach.
fix: Other basic nutriment subtypes also carry food flavours, instead of
outweighing the food flavours with their lack of flavour.
fix: Basic nutriment subtypes have a flavour they default to, in case of
not carrying any food flavours.
fix: Oils taste like oil instead of tasting indescribable.
fix: Clothing eaten by moths actually carries its flavours instead of
tasting indescribable.
fix: Organs use organ tissue instead of nutriment.
fix: You can actually taste organ tissue.
qol: Mineral slurry tastes like minerals instead of tasting
indescribable.
fix: Tasted flavours can actually be "strong" in addition to mild or "a
hint of", as intended.
qol: Flavours are only strong when their percentage of what you're
tasting is more than 4x taste sensitivity instead of 3x, for most
default tongues being 60% and lizards 40% of the drink.
qol: Tasted flavours are sorted by strength, stronger flavours coming
first.
qol: Tasted flavours are grouped into "strong", "mild", and "hint" in
the message, replacing "You can taste a hint of bitterness, alcohol, a
hint of sweetness, and a hint of cola" with "You can taste alcohol and a
hint of bitterness, sweetness, and cola".
/🆑

---------

Co-authored-by: MrMelbert <51863163+MrMelbert@users.noreply.github.com>
2024-11-02 01:44:34 +01:00

190 lines
6.2 KiB
Plaintext

#define DEFAULT_TASTE_SENSITIVITY 15
/mob/living
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 = get_organ_slot(ORGAN_SLOT_TONGUE)
if(istype(tongue))
. = tongue.taste_sensitivity
else
// carbons without tongues normally have TRAIT_AGEUSIA but sensible fallback
. = DEFAULT_TASTE_SENSITIVITY
/**
* Non-destructively tastes a reagent container
* and gives feedback to the user.
* Arguments:
* * datum/reagents/from - Reagent holder to taste from.
**/
/mob/living/proc/taste_container(datum/reagents/from)
if(check_tasting_blocks())
return
var/taste_sensitivity = get_taste_sensitivity()
var/text_output = from.generate_taste_message(src, taste_sensitivity)
send_taste_message(text_output)
/**
* Non-destructively tastes a reagent list
* and gives feedback to the user.
* Arguments:
* * list/from - List of reagents to taste from.
**/
/mob/living/proc/taste_list(list/from)
if(check_tasting_blocks())
return
var/taste_sensitivity = get_taste_sensitivity()
var/text_output = generate_reagents_taste_message(from, src, taste_sensitivity)
send_taste_message(text_output)
/**
* Check for anything blocking/overriding our tasting.
* Returns TRUE on a block, FALSE if not.
**/
/mob/living/proc/check_tasting_blocks()
if(HAS_TRAIT(src, TRAIT_AGEUSIA))
return TRUE
if(last_taste_time + 50 >= world.time)
return TRUE
// Sometimes, try send a replacement message if we're hallucinating
if(get_timed_status_effect_duration(/datum/status_effect/hallucination) > 100 SECONDS && prob(25))
var/text_output = pick("spiders","dreams","nightmares","the future","the past","victory",\
"defeat","pain","bliss","revenge","poison","time","space","death","life","truth","lies","justice","memory",\
"regrets","your soul","suffering","music","noise","blood","hunger","the american way")
send_taste_message(text_output)
return TRUE
return FALSE
/**
* Attempt to send a taste message using given tastes text.
**/
/mob/living/proc/send_taste_message(tastes_text)
if(tastes_text == last_taste_text && last_taste_time + 100 >= world.time)
return
to_chat(src, span_notice("You can taste [tastes_text]."))
// "something indescribable" -> too many tastes, not enough flavor.
last_taste_time = world.time
last_taste_text = tastes_text
/**
* Gets food flags that this mob likes
**/
/mob/living/proc/get_liked_foodtypes()
return NONE
/mob/living/carbon/get_liked_foodtypes()
if(HAS_TRAIT(src, TRAIT_AGEUSIA))
return NONE
// Handled in here since the brain trauma can't modify taste directly (/datum/brain_trauma/severe/flesh_desire)
if(HAS_TRAIT(src, TRAIT_FLESH_DESIRE))
return GORE | MEAT
var/obj/item/organ/tongue/tongue = get_organ_slot(ORGAN_SLOT_TONGUE)
. = tongue.liked_foodtypes
if(HAS_TRAIT(src, TRAIT_VEGETARIAN))
. &= ~MEAT
/**
* Gets food flags that this mob dislikes
**/
/mob/living/proc/get_disliked_foodtypes()
if(HAS_TRAIT(src, TRAIT_VEGETARIAN))
return MEAT
return NONE
/mob/living/carbon/get_disliked_foodtypes()
if(HAS_TRAIT(src, TRAIT_AGEUSIA))
return NONE
var/obj/item/organ/tongue/tongue = get_organ_slot(ORGAN_SLOT_TONGUE)
. = tongue.disliked_foodtypes
if(HAS_TRAIT(src, TRAIT_VEGETARIAN))
. |= MEAT
/**
* Gets food flags that this mob hates
* Toxic food is the only category that ignores ageusia, KEEP IT LIKE THAT!
**/
/mob/living/proc/get_toxic_foodtypes()
return TOXIC
/mob/living/carbon/get_toxic_foodtypes()
var/obj/item/organ/tongue/tongue = get_organ_slot(ORGAN_SLOT_TONGUE)
if(!tongue)
return ..()
if(HAS_TRAIT(src, TRAIT_FLESH_DESIRE))
return VEGETABLES | DAIRY | FRUIT | FRIED
return tongue.toxic_foodtypes
/**
* Gets food this mob is allergic to
* Essentially toxic food+, not only disgusting but outright lethal
*/
/mob/living/proc/get_allergic_foodtypes()
var/datum/quirk/item_quirk/food_allergic/allergy = get_quirk(/datum/quirk/item_quirk/food_allergic)
return allergy?.target_foodtypes || NONE
/**
* Checks if the mob has an allergic reaction to the given food type.
* If so, the mob will contract anaphylaxis.
*
* * to_foodtype: The food type to check for an allergic reaction to.
* * chance: The chance of an allergic reaction occurring. Default is 100 (guaranteed).
* * histamine_add: The amount of histamine to add to the mob if they are already experiencing an allergic reaction.
*
* Returns TRUE if the mob had an allergic reaction, FALSE otherwise.
*/
/mob/living/proc/check_allergic_reaction(to_foodtype = NONE, chance = 100, histamine_add = 0)
if(!(get_allergic_foodtypes() & to_foodtype))
return FALSE
if(!prob(chance))
return FALSE
if(ForceContractDisease(new /datum/disease/anaphylaxis(), make_copy = FALSE, del_on_fail = TRUE))
to_chat(src, span_warning("You feel your throat start to itch."))
add_mood_event("allergic_food", /datum/mood_event/allergic_food)
else if(histamine_add)
reagents.add_reagent(/datum/reagent/toxin/histamine, histamine_add)
return TRUE
/**
* Gets the food reaction a mob would normally have from the given food item,
* assuming that no check_liked callback was used in the edible component.
*
* Does not get called if the owner has ageusia.
**/
/mob/living/proc/get_food_taste_reaction(obj/item/food, foodtypes)
var/food_taste_reaction
if(foodtypes & get_toxic_foodtypes())
food_taste_reaction = FOOD_TOXIC
else if(foodtypes & get_disliked_foodtypes())
food_taste_reaction = FOOD_DISLIKED
else if(foodtypes & get_liked_foodtypes())
food_taste_reaction = FOOD_LIKED
return food_taste_reaction
/mob/living/carbon/get_food_taste_reaction(obj/item/food, foodtypes)
var/obj/item/organ/tongue/tongue = get_organ_slot(ORGAN_SLOT_TONGUE)
// No tongue, no tastin'
if(!tongue?.sense_of_taste || HAS_TRAIT(src, TRAIT_AGEUSIA))
// i hate that i have to do this, but we want to ensure toxic food is still BAD
if(foodtypes & get_toxic_foodtypes())
return FOOD_TOXIC
return
return tongue.get_food_taste_reaction(food, foodtypes)
#undef DEFAULT_TASTE_SENSITIVITY