diff --git a/code/datums/components/food/edible.dm b/code/datums/components/food/edible.dm index 6ad197b7c00..62e446f5a28 100644 --- a/code/datums/components/food/edible.dm +++ b/code/datums/components/food/edible.dm @@ -212,10 +212,12 @@ Behavior that's still missing from this component that original food items had t for(var/rid in reagents) var/amount = reagents[rid] - if(length(tastes) && (rid == /datum/reagent/consumable/nutriment || rid == /datum/reagent/consumable/nutriment/vitamin)) - owner.reagents.add_reagent(rid, amount, tastes.Copy(), added_purity = reagent_purity) - else - owner.reagents.add_reagent(rid, amount, added_purity = reagent_purity) + if(length(tastes) && ispath(rid, /datum/reagent/consumable/nutriment)) + var/datum/reagent/consumable/nutriment/nid = rid + if(initial(nid.carry_food_tastes)) + owner.reagents.add_reagent(rid, amount, tastes.Copy(), added_purity = reagent_purity) + continue + owner.reagents.add_reagent(rid, amount, added_purity = reagent_purity) /datum/component/edible/proc/examine(datum/source, mob/user, list/examine_list) SIGNAL_HANDLER @@ -282,7 +284,7 @@ Behavior that's still missing from this component that original food items had t examine_list += span_notice("It could use a little more Sodium Chloride...") if (isliving(user)) var/mob/living/living_user = user - living_user.taste(owner.reagents) + living_user.taste_container(owner.reagents) /datum/component/edible/proc/UseFromHand(obj/item/source, mob/living/M, mob/living/user) SIGNAL_HANDLER @@ -686,7 +688,7 @@ Behavior that's still missing from this component that original food items had t bitecount++ . = COMPONENT_CANCEL_ATTACK_CHAIN - doggy.taste(food.reagents) // why should carbons get all the fun? + doggy.taste_container(food.reagents) // why should carbons get all the fun? if(bitecount >= 5) var/satisfaction_text = pick("burps from enjoyment.", "yaps for more!", "woofs twice.", "looks at the area where \the [food] was.") doggy.manual_emote(satisfaction_text) diff --git a/code/datums/quirks/positive_quirks/friendly.dm b/code/datums/quirks/positive_quirks/friendly.dm index ef030530169..26b42f554f9 100644 --- a/code/datums/quirks/positive_quirks/friendly.dm +++ b/code/datums/quirks/positive_quirks/friendly.dm @@ -19,5 +19,5 @@ // We have a bigger heart full of love! holder_heart.reagents.add_reagent(/datum/reagent/love, 2.5) // Like, physically bigger. - holder_heart.reagents.add_reagent(/datum/reagent/consumable/nutriment, 5) + holder_heart.reagents.add_reagent(/datum/reagent/consumable/nutriment/organ_tissue, 5) holder_heart.transform = holder_heart.transform.Scale(1.5) diff --git a/code/game/machinery/dna_infuser/organ_sets/fish_organs.dm b/code/game/machinery/dna_infuser/organ_sets/fish_organs.dm index 8f19a70cdbf..aa479f1d316 100644 --- a/code/game/machinery/dna_infuser/organ_sets/fish_organs.dm +++ b/code/game/machinery/dna_infuser/organ_sets/fish_organs.dm @@ -418,7 +418,7 @@ organ_traits = list(TRAIT_TETRODOTOXIN_HEALING, TRAIT_ALCOHOL_TOLERANCE) //drink like a fish :^) liver_resistance = parent_type::liver_resistance * 1.5 - food_reagents = list(/datum/reagent/consumable/nutriment = 5, /datum/reagent/iron = 5, /datum/reagent/toxin/tetrodotoxin = 5) + food_reagents = list(/datum/reagent/consumable/nutriment/organ_tissue = 5, /datum/reagent/iron = 5, /datum/reagent/toxin/tetrodotoxin = 5) grind_results = list(/datum/reagent/consumable/nutriment/peptides = 5, /datum/reagent/toxin/tetrodotoxin = 5) // Seafood instead of meat, because it's a fish organ diff --git a/code/modules/antagonists/nightmare/nightmare_organs.dm b/code/modules/antagonists/nightmare/nightmare_organs.dm index 70cf4c64083..ddfe675d412 100644 --- a/code/modules/antagonists/nightmare/nightmare_organs.dm +++ b/code/modules/antagonists/nightmare/nightmare_organs.dm @@ -72,7 +72,7 @@ color = COLOR_CRAYON_BLACK decay_factor = 0 // No love is to be found in a heart so twisted. - food_reagents = list(/datum/reagent/consumable/nutriment = 5) + food_reagents = list(/datum/reagent/consumable/nutriment/organ_tissue = 5) /// How many life ticks in the dark the owner has been dead for. Used for nightmare respawns. var/respawn_progress = 0 /// The armblade granted to the host of this heart. diff --git a/code/modules/mob/living/carbon/alien/organs.dm b/code/modules/mob/living/carbon/alien/organs.dm index f0bf6bb2d11..7db1bb103cc 100644 --- a/code/modules/mob/living/carbon/alien/organs.dm +++ b/code/modules/mob/living/carbon/alien/organs.dm @@ -1,6 +1,6 @@ /obj/item/organ/alien icon_state = "acid" - food_reagents = list(/datum/reagent/consumable/nutriment = 5, /datum/reagent/toxin/acid = 10) + food_reagents = list(/datum/reagent/consumable/nutriment/organ_tissue = 5, /datum/reagent/toxin/acid = 10) /obj/item/organ/alien/plasmavessel name = "plasma vessel" @@ -8,7 +8,7 @@ w_class = WEIGHT_CLASS_NORMAL zone = BODY_ZONE_CHEST slot = ORGAN_SLOT_XENO_PLASMAVESSEL - food_reagents = list(/datum/reagent/consumable/nutriment = 5, /datum/reagent/toxin/plasma = 10) + food_reagents = list(/datum/reagent/consumable/nutriment/organ_tissue = 5, /datum/reagent/toxin/plasma = 10) actions_types = list( /datum/action/cooldown/alien/make_structure/plant_weeds, /datum/action/cooldown/alien/transfer, diff --git a/code/modules/mob/living/carbon/alien/special/alien_embryo.dm b/code/modules/mob/living/carbon/alien/special/alien_embryo.dm index b6b2d2f9953..b70139cee05 100644 --- a/code/modules/mob/living/carbon/alien/special/alien_embryo.dm +++ b/code/modules/mob/living/carbon/alien/special/alien_embryo.dm @@ -4,7 +4,7 @@ name = "alien embryo" icon = 'icons/mob/nonhuman-player/alien.dmi' icon_state = "larva0_dead" - food_reagents = list(/datum/reagent/consumable/nutriment = 5, /datum/reagent/toxin/acid = 10) + food_reagents = list(/datum/reagent/consumable/nutriment/organ_tissue = 5, /datum/reagent/toxin/acid = 10) ///What stage of growth the embryo is at. Developed embryos give the host symptoms suggesting that an embryo is inside them. var/stage = 0 /// Are we bursting out of the poor sucker who's the xeno mom? diff --git a/code/modules/mob/living/living_defense.dm b/code/modules/mob/living/living_defense.dm index ca6096914fd..c40042958df 100644 --- a/code/modules/mob/living/living_defense.dm +++ b/code/modules/mob/living/living_defense.dm @@ -631,7 +631,7 @@ return if(methods & (INGEST | INHALE)) - taste(source) + taste_list(reagents) var/touch_protection = (methods & VAPOR) ? getarmor(null, BIO) * 0.01 : 0 SEND_SIGNAL(source, COMSIG_REAGENTS_EXPOSE_MOB, src, reagents, methods, volume_modifier, show_message, touch_protection) diff --git a/code/modules/mob/living/taste.dm b/code/modules/mob/living/taste.dm index 8790def4bd1..5da5781b2ed 100644 --- a/code/modules/mob/living/taste.dm +++ b/code/modules/mob/living/taste.dm @@ -22,28 +22,65 @@ . = DEFAULT_TASTE_SENSITIVITY /** - * Non destructively tastes a reagent container + * 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(datum/reagents/from) - if(HAS_TRAIT(src, TRAIT_AGEUSIA)) +/mob/living/proc/taste_container(datum/reagents/from) + if(check_tasting_blocks()) return - if(last_taste_time + 50 < world.time) - var/taste_sensitivity = get_taste_sensitivity() - 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(get_timed_status_effect_duration(/datum/status_effect/hallucination) > 100 SECONDS && prob(25)) - text_output = pick("spiders","dreams","nightmares","the future","the past","victory",\ + 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") - if(text_output != last_taste_text || last_taste_time + 100 < world.time) - to_chat(src, span_notice("You can taste [text_output].")) - // "something indescribable" -> too many tastes, not enough flavor. + send_taste_message(text_output) + return TRUE - last_taste_time = world.time - last_taste_text = text_output + 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 diff --git a/code/modules/reagents/chemistry/holder/properties.dm b/code/modules/reagents/chemistry/holder/properties.dm index b949866a6d4..a68404c9fc7 100644 --- a/code/modules/reagents/chemistry/holder/properties.dm +++ b/code/modules/reagents/chemistry/holder/properties.dm @@ -161,35 +161,4 @@ * * minimum_percent - the lower the minimum percent, the more sensitive the message is. */ /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) - for(var/datum/reagent/reagent as anything in reagent_list) - if(!reagent.taste_mult) - continue - - var/list/taste_data = reagent.get_taste_description(taster) - for(var/taste in taste_data) - if(taste in tastes) - tastes[taste] += taste_data[taste] * reagent.volume * reagent.taste_mult - else - tastes[taste] = taste_data[taste] * reagent.volume * reagent.taste_mult - //deal with percentages - // TODO it would be great if we could sort these from strong to weak - var/total_taste = counterlist_sum(tastes) - if(total_taste > 0) - for(var/taste_desc in tastes) - var/percent = tastes[taste_desc]/total_taste * 100 - if(percent < minimum_percent) - continue - var/intensity_desc = "a hint of" - if(percent > minimum_percent * 2 || percent == 100) - intensity_desc = "" - else if(percent > minimum_percent * 3) - intensity_desc = "the strong flavor of" - if(intensity_desc != "") - out += "[intensity_desc] [taste_desc]" - else - out += "[taste_desc]" - - return english_list(out, "something indescribable") + return generate_reagents_taste_message(reagent_list, taster, minimum_percent) diff --git a/code/modules/reagents/chemistry/reagents/food_reagents.dm b/code/modules/reagents/chemistry/reagents/food_reagents.dm index f17dbf7fe9e..90937471bf1 100644 --- a/code/modules/reagents/chemistry/reagents/food_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/food_reagents.dm @@ -66,6 +66,9 @@ color = "#664330" // rgb: 102, 67, 48 chemical_flags = REAGENT_CAN_BE_SYNTHESIZED + /// Whether this reagent should get the tastes of food it's in applied onto it + var/carry_food_tastes = TRUE + var/brute_heal = 1 var/burn_heal = 0 @@ -117,11 +120,14 @@ data = taste_amounts /datum/reagent/consumable/nutriment/get_taste_description(mob/living/taster) - return data + if(length(data)) + return data + return ..() /datum/reagent/consumable/nutriment/vitamin name = "Vitamin" description = "All the best vitamins, minerals, and carbohydrates the body needs in pure form." + taste_description = "bitterness" chemical_flags = REAGENT_CAN_BE_SYNTHESIZED brute_heal = 1 @@ -136,6 +142,7 @@ /datum/reagent/consumable/nutriment/protein name = "Protein" description = "A natural polyamide made up of amino acids. An essential constituent of mosts known forms of life." + taste_description = "chalk" brute_heal = 0.8 //Rewards the player for eating a balanced diet. nutriment_factor = 9 //45% as calorie dense as oil. chemical_flags = REAGENT_CAN_BE_SYNTHESIZED @@ -208,6 +215,7 @@ color = "#EADD6B" //RGB: 234, 221, 107 (based off of canola oil) taste_mult = 0.8 taste_description = "oil" + carry_food_tastes = FALSE nutriment_factor = 7 //Not very healthy on its own metabolization_rate = 10 * REAGENTS_METABOLISM penetrates_skin = NONE @@ -235,9 +243,15 @@ taste_description = "rich earthy pungent" chemical_flags = REAGENT_CAN_BE_SYNTHESIZED +/datum/reagent/consumable/nutriment/organ_tissue/stomach_lining + name = "Stomach Lining" + description = "Natural tissue that keeps your stomach safe." + carry_food_tastes = FALSE // Don't want stomachs to leech the flavours of what they eat + /datum/reagent/consumable/nutriment/cloth_fibers name = "Cloth Fibers" description = "It's not actually a form of nutriment but it does keep Mothpeople going for a short while..." + taste_description = "cloth" nutriment_factor = 30 chemical_flags = REAGENT_CAN_BE_SYNTHESIZED brute_heal = 0 @@ -262,6 +276,7 @@ /datum/reagent/consumable/nutriment/mineral name = "Mineral Slurry" description = "Minerals pounded into a paste, nutritious only if you too are made of rocks." + taste_description = "minerals" color = COLOR_WEBSAFE_DARK_GRAY chemical_flags = NONE brute_heal = 0 diff --git a/code/modules/reagents/chemistry/taste.dm b/code/modules/reagents/chemistry/taste.dm new file mode 100644 index 00000000000..06cee0bba2d --- /dev/null +++ b/code/modules/reagents/chemistry/taste.dm @@ -0,0 +1,77 @@ + +#define TEXT_NO_TASTE "something indescribable" + +//================================TASTE=================================================== +/** + * Returns what this reagents in our given list taste like + * + * Arguments: + * * list/reagent_list - List of reagents to taste. + * * 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. + * * weight_modifier - Value to multiply each reagent's taste weight with. + */ +/proc/generate_reagents_taste_message(list/reagent_list, mob/living/taster, minimum_percent) + // We can't taste anything + if(minimum_percent > 100) + return TEXT_NO_TASTE + + // Associative list of our tastes, descriptor - strength + var/list/tastes = list() + // Total of our taste strengths so far + var/total_taste = 0 + + for(var/datum/reagent/reagent as anything in reagent_list) + if(!reagent.taste_mult) + continue + + var/list/taste_data = reagent.get_taste_description(taster) + for(var/taste in taste_data) + var/taste_strength = taste_data[taste] * reagent.volume * reagent.taste_mult + if(taste in tastes) + tastes[taste] += taste_strength + else + tastes[taste] = taste_strength + total_taste += taste_strength + + // None of our reagents had any flavour + if(total_taste <= 0) + return TEXT_NO_TASTE + + // If we have exactly one taste, don't bother with relative strengths + if(length(tastes) == 1) + return tastes[1] + + // Sort tastes descending by strength, so strong flavours come first + sortTim(tastes, cmp = GLOBAL_PROC_REF(cmp_numeric_dsc), associative = TRUE) + + // Lazylists for different taste strength categories, no need to initialize if we don't have such flavors + var/list/strong + var/list/mild + var/list/hint + + for(var/taste_desc in tastes) + var/percent = tastes[taste_desc]/total_taste * 100 + if(percent < minimum_percent) + continue + + if(percent <= minimum_percent * 2) + LAZYADD(hint, taste_desc) + else if(percent > minimum_percent * 4) + LAZYADD(strong, taste_desc) + else + LAZYADD(mild, taste_desc) + + var/list/out = list() + + if(LAZYLEN(strong)) + out += "the strong flavor of [english_list(strong, TEXT_NO_TASTE)]" + if(LAZYLEN(mild)) + // Prefix "some " if there are strong flavors to avoid seeming like a strong flavor + out += "[LAZYLEN(strong) ? "some " : ""][english_list(mild, TEXT_NO_TASTE)]" + if(LAZYLEN(hint)) + out += "a hint of [english_list(hint, TEXT_NO_TASTE)]" + + return english_list(out, TEXT_NO_TASTE) + +#undef TEXT_NO_TASTE diff --git a/code/modules/reagents/reagent_containers.dm b/code/modules/reagents/reagent_containers.dm index 36c17e6f04a..2320eca9160 100644 --- a/code/modules/reagents/reagent_containers.dm +++ b/code/modules/reagents/reagent_containers.dm @@ -72,7 +72,7 @@ . += span_notice("Left-click or right-click in-hand to view its transfer amount.") if(isliving(user) && HAS_TRAIT(user, TRAIT_REMOTE_TASTING)) var/mob/living/living_user = user - living_user.taste(reagents) + living_user.taste_container(reagents) /obj/item/reagent_containers/create_reagents(max_vol, flags) . = ..() diff --git a/code/modules/surgery/organs/internal/appendix/_appendix.dm b/code/modules/surgery/organs/internal/appendix/_appendix.dm index 98e8a0f4ed1..020d5186af9 100644 --- a/code/modules/surgery/organs/internal/appendix/_appendix.dm +++ b/code/modules/surgery/organs/internal/appendix/_appendix.dm @@ -9,7 +9,7 @@ zone = BODY_ZONE_PRECISE_GROIN slot = ORGAN_SLOT_APPENDIX - food_reagents = list(/datum/reagent/consumable/nutriment = 5, /datum/reagent/toxin/bad_food = 5) + food_reagents = list(/datum/reagent/consumable/nutriment/organ_tissue = 5, /datum/reagent/toxin/bad_food = 5) grind_results = list(/datum/reagent/toxin/bad_food = 5) healing_factor = STANDARD_ORGAN_HEALING decay_factor = STANDARD_ORGAN_DECAY diff --git a/code/modules/surgery/organs/internal/heart/_heart.dm b/code/modules/surgery/organs/internal/heart/_heart.dm index efc060967c6..20ad378529f 100644 --- a/code/modules/surgery/organs/internal/heart/_heart.dm +++ b/code/modules/surgery/organs/internal/heart/_heart.dm @@ -19,7 +19,7 @@ attack_verb_simple = list("beat", "thump") // Love is stored in the heart. - food_reagents = list(/datum/reagent/consumable/nutriment = 5, /datum/reagent/love = 2.5) + food_reagents = list(/datum/reagent/consumable/nutriment/organ_tissue = 5, /datum/reagent/love = 2.5) // Heart attack code is in code/modules/mob/living/carbon/human/life.dm diff --git a/code/modules/surgery/organs/internal/liver/_liver.dm b/code/modules/surgery/organs/internal/liver/_liver.dm index 0464bfca993..866813d02aa 100755 --- a/code/modules/surgery/organs/internal/liver/_liver.dm +++ b/code/modules/surgery/organs/internal/liver/_liver.dm @@ -15,7 +15,7 @@ healing_factor = STANDARD_ORGAN_HEALING decay_factor = STANDARD_ORGAN_DECAY // smack in the middle of decay times - food_reagents = list(/datum/reagent/consumable/nutriment = 5, /datum/reagent/iron = 5) + food_reagents = list(/datum/reagent/consumable/nutriment/organ_tissue = 5, /datum/reagent/iron = 5) grind_results = list(/datum/reagent/consumable/nutriment/peptides = 5) /// Affects how much damage the liver takes from alcohol diff --git a/code/modules/surgery/organs/internal/lungs/_lungs.dm b/code/modules/surgery/organs/internal/lungs/_lungs.dm index d20cb4968b2..53535cfe131 100644 --- a/code/modules/surgery/organs/internal/lungs/_lungs.dm +++ b/code/modules/surgery/organs/internal/lungs/_lungs.dm @@ -21,7 +21,7 @@ var/failed = FALSE var/operated = FALSE //whether we can still have our damages fixed through surgery - food_reagents = list(/datum/reagent/consumable/nutriment = 5, /datum/reagent/medicine/salbutamol = 5) + food_reagents = list(/datum/reagent/consumable/nutriment/organ_tissue = 5, /datum/reagent/medicine/salbutamol = 5) /// Our previous breath's partial pressures, in the form gas id -> partial pressure var/list/last_partial_pressures = list() diff --git a/code/modules/surgery/organs/internal/stomach/_stomach.dm b/code/modules/surgery/organs/internal/stomach/_stomach.dm index d57ea9680bf..768f3483c03 100644 --- a/code/modules/surgery/organs/internal/stomach/_stomach.dm +++ b/code/modules/surgery/organs/internal/stomach/_stomach.dm @@ -20,7 +20,7 @@ high_threshold_cleared = span_info("The pain in your stomach dies down for now, but food still seems unappealing.") low_threshold_cleared = span_info("The last bouts of pain in your stomach have died out.") - food_reagents = list(/datum/reagent/consumable/nutriment/organ_tissue = 5) + food_reagents = list(/datum/reagent/consumable/nutriment/organ_tissue/stomach_lining = 5) //This is a reagent user and needs more then the 10u from edible component reagent_vol = 1000 diff --git a/tgstation.dme b/tgstation.dme index 3191d598006..216c94247b6 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -5712,6 +5712,7 @@ #include "code\modules\reagents\chemistry\items.dm" #include "code\modules\reagents\chemistry\reagents.dm" #include "code\modules\reagents\chemistry\recipes.dm" +#include "code\modules\reagents\chemistry\taste.dm" #include "code\modules\reagents\chemistry\holder\holder.dm" #include "code\modules\reagents\chemistry\holder\mob_life.dm" #include "code\modules\reagents\chemistry\holder\properties.dm"