1 #include "player.h" // IWYU pragma: associated
2 
3 #include <algorithm>
4 #include <array>
5 #include <cmath>
6 #include <cstdlib>
7 #include <functional>
8 #include <memory>
9 #include <string>
10 #include <tuple>
11 
12 #include "addiction.h"
13 #include "avatar.h"
14 #include "bionics.h"
15 #include "calendar.h"
16 #include "cata_utility.h"
17 #include "character.h"
18 #include "color.h"
19 #include "debug.h"
20 #include "enums.h"
21 #include "event.h"
22 #include "event_bus.h"
23 #include "flag.h"
24 #include "flat_set.h"
25 #include "game.h"
26 #include "item.h"
27 #include "item_category.h"
28 #include "item_contents.h"
29 #include "itype.h"
30 #include "iuse.h"
31 #include "iuse_actor.h"
32 #include "line.h"
33 #include "make_static.h"
34 #include "map.h"
35 #include "material.h"
36 #include "messages.h"
37 #include "monster.h"
38 #include "morale_types.h"
39 #include "mutation.h"
40 #include "npc.h"
41 #include "options.h"
42 #include "pickup.h"
43 #include "pldata.h"
44 #include "recipe.h"
45 #include "recipe_dictionary.h"
46 #include "requirements.h"
47 #include "rng.h"
48 #include "stomach.h"
49 #include "string_formatter.h"
50 #include "translations.h"
51 #include "units.h"
52 #include "value_ptr.h"
53 #include "visitable.h"
54 #include "vitamin.h"
55 
56 static const std::string comesttype_DRINK( "DRINK" );
57 static const std::string comesttype_FOOD( "FOOD" );
58 
59 static const skill_id skill_cooking( "cooking" );
60 static const skill_id skill_survival( "survival" );
61 
62 static const mtype_id mon_player_blob( "mon_player_blob" );
63 
64 static const bionic_id bio_digestion( "bio_digestion" );
65 static const bionic_id bio_taste_blocker( "bio_taste_blocker" );
66 
67 static const efftype_id effect_bloodworms( "bloodworms" );
68 static const efftype_id effect_brainworms( "brainworms" );
69 static const efftype_id effect_common_cold( "common_cold" );
70 static const efftype_id effect_flu( "flu" );
71 static const efftype_id effect_foodpoison( "foodpoison" );
72 static const efftype_id effect_fungus( "fungus" );
73 static const efftype_id effect_hallu( "hallu" );
74 static const efftype_id effect_hunger_engorged( "hunger_engorged" );
75 static const efftype_id effect_hunger_full( "hunger_full" );
76 static const efftype_id effect_nausea( "nausea" );
77 static const efftype_id effect_paincysts( "paincysts" );
78 static const efftype_id effect_poison( "poison" );
79 static const efftype_id effect_tapeworm( "tapeworm" );
80 static const efftype_id effect_visuals( "visuals" );
81 
82 static const json_character_flag json_flag_IMMUNE_SPOIL( "IMMUNE_SPOIL" );
83 static const json_character_flag json_flag_PARAIMMUNE( "PARAIMMUNE" );
84 static const json_character_flag json_flag_PRED1( "PRED1" );
85 static const json_character_flag json_flag_PRED2( "PRED2" );
86 static const json_character_flag json_flag_PRED3( "PRED3" );
87 static const json_character_flag json_flag_PRED4( "PRED4" );
88 static const json_character_flag json_flag_STRICT_HUMANITARIAN( "STRICT_HUMANITARIAN" );
89 
90 static const item_category_id item_category_chems( "chems" );
91 
92 static const itype_id itype_apparatus( "apparatus" );
93 static const itype_id itype_dab_pen_on( "dab_pen_on" );
94 static const itype_id itype_syringe( "syringe" );
95 
96 static const mutation_category_id mutation_category_URSINE( "URSINE" );
97 
98 static const trait_id trait_ACIDBLOOD( "ACIDBLOOD" );
99 static const trait_id trait_AMORPHOUS( "AMORPHOUS" );
100 static const trait_id trait_ANTIFRUIT( "ANTIFRUIT" );
101 static const trait_id trait_ANTIJUNK( "ANTIJUNK" );
102 static const trait_id trait_ANTIWHEAT( "ANTIWHEAT" );
103 static const trait_id trait_CANNIBAL( "CANNIBAL" );
104 static const trait_id trait_CARNIVORE( "CARNIVORE" );
105 static const trait_id trait_EATDEAD( "EATDEAD" );
106 static const trait_id trait_EATHEALTH( "EATHEALTH" );
107 static const trait_id trait_EATPOISON( "EATPOISON" );
108 static const trait_id trait_GIZZARD( "GIZZARD" );
109 static const trait_id trait_GOURMAND( "GOURMAND" );
110 static const trait_id trait_HERBIVORE( "HERBIVORE" );
111 static const trait_id trait_HIBERNATE( "HIBERNATE" );
112 static const trait_id trait_LACTOSE( "LACTOSE" );
113 static const trait_id trait_M_DEPENDENT( "M_DEPENDENT" );
114 static const trait_id trait_M_IMMUNE( "M_IMMUNE" );
115 static const trait_id trait_MEATARIAN( "MEATARIAN" );
116 static const trait_id trait_PROBOSCIS( "PROBOSCIS" );
117 static const trait_id trait_PROJUNK( "PROJUNK" );
118 static const trait_id trait_PROJUNK2( "PROJUNK2" );
119 static const trait_id trait_PSYCHOPATH( "PSYCHOPATH" );
120 static const trait_id trait_RUMINANT( "RUMINANT" );
121 static const trait_id trait_SAPIOVORE( "SAPIOVORE" );
122 static const trait_id trait_SAPROPHAGE( "SAPROPHAGE" );
123 static const trait_id trait_SAPROVORE( "SAPROVORE" );
124 static const trait_id trait_SCHIZOPHRENIC( "SCHIZOPHRENIC" );
125 static const trait_id trait_SLIMESPAWNER( "SLIMESPAWNER" );
126 static const trait_id trait_SPIRITUAL( "SPIRITUAL" );
127 static const trait_id trait_STIMBOOST( "STIMBOOST" );
128 static const trait_id trait_TABLEMANNERS( "TABLEMANNERS" );
129 static const trait_id trait_THRESH_BIRD( "THRESH_BIRD" );
130 static const trait_id trait_THRESH_CATTLE( "THRESH_CATTLE" );
131 static const trait_id trait_THRESH_FELINE( "THRESH_FELINE" );
132 static const trait_id trait_THRESH_LUPINE( "THRESH_LUPINE" );
133 static const trait_id trait_THRESH_PLANT( "THRESH_PLANT" );
134 static const trait_id trait_THRESH_URSINE( "THRESH_URSINE" );
135 static const trait_id trait_VEGETARIAN( "VEGETARIAN" );
136 static const trait_id trait_WATERSLEEP( "WATERSLEEP" );
137 
138 // note: cannot use constants from flag.h (e.g. flag_ALLERGEN_VEGGY) here, as they
139 // might be uninitialized at the time these const arrays are created
140 static const std::array<flag_id, 4> carnivore_blacklist {{
141         flag_id( "ALLERGEN_VEGGY" ), flag_id( "ALLERGEN_FRUIT" ),
142         flag_id( "ALLERGEN_WHEAT" ), flag_id( "ALLERGEN_NUT" )
143     }};
144 
145 static const std::array<flag_id, 2> herbivore_blacklist {{
146         flag_id( "ALLERGEN_MEAT" ), flag_id( "ALLERGEN_EGG" )
147     }};
148 
149 // TODO: Move pizza scraping here.
compute_default_effective_kcal(const item & comest,const Character & you,const cata::flat_set<flag_id> & extra_flags={} )150 static int compute_default_effective_kcal( const item &comest, const Character &you,
151         const cata::flat_set<flag_id> &extra_flags = {} )
152 {
153     if( !comest.get_comestible() ) {
154         return 0;
155     }
156 
157     // As float to avoid rounding too many times
158     float kcal = comest.get_comestible()->default_nutrition.kcal();
159 
160     // Many raw foods give less calories, as your body has expends more energy digesting them.
161     bool cooked = comest.has_flag( flag_COOKED ) || extra_flags.count( flag_COOKED );
162     if( comest.has_flag( flag_RAW ) && !cooked ) {
163         kcal *= 0.75f;
164     }
165 
166     if( you.has_trait( trait_GIZZARD ) ) {
167         kcal *= 0.6f;
168     }
169 
170     if( you.has_trait( trait_CARNIVORE ) && comest.has_flag( flag_CARNIVORE_OK ) &&
171         comest.has_any_flag( carnivore_blacklist ) ) {
172         // TODO: Comment pizza scrapping
173         kcal *= 0.5f;
174     }
175 
176     const float relative_rot = comest.get_relative_rot();
177     // Saprophages get full nutrition from rotting food
178     if( relative_rot > 1.0f && !you.has_trait( trait_SAPROPHAGE ) ) {
179         // everyone else only gets a portion of the nutrition
180         // Scaling linearly from 100% at just-rotten to 0 at halfway-rotten-away
181         const float rottedness = clamp( 2 * relative_rot - 2.0f, 0.1f, 1.0f );
182         kcal *= ( 1.0f - rottedness );
183     }
184 
185     // Bionic digestion gives extra nutrition
186     if( you.has_bionic( bio_digestion ) ) {
187         kcal *= 1.5f;
188     }
189 
190     return static_cast<int>( kcal );
191 }
192 
193 // Compute default effective vitamins for an item, taking into account player
194 // traits, but not components of the item.
compute_default_effective_vitamins(const item & it,const Character & you)195 static std::map<vitamin_id, int> compute_default_effective_vitamins(
196     const item &it, const Character &you )
197 {
198     if( !it.get_comestible() ) {
199         return {};
200     }
201 
202     std::map<vitamin_id, int> res = it.get_comestible()->default_nutrition.vitamins;
203 
204     for( const trait_id &trait : you.get_mutations() ) {
205         const auto &mut = trait.obj();
206         // make sure to iterate over every material defined for vitamin absorption
207         // TODO: put this loop into a function and utilize it again for bionics
208         for( const auto &mat : mut.vitamin_absorb_multi ) {
209             // this is where we are able to check if the food actually is changed by the trait
210             if( mat.first == material_id( "all" ) || it.made_of( mat.first ) ) {
211                 const std::map<vitamin_id, double> &mat_vit_map = mat.second;
212                 for( auto &vit : res ) {
213                     auto vit_factor = mat_vit_map.find( vit.first );
214                     if( vit_factor != mat_vit_map.end() ) {
215                         vit.second *= vit_factor->second;
216                     }
217                 }
218             }
219         }
220     }
221     for( const bionic_id &bid : you.get_bionics() ) {
222         if( bid->vitamin_absorb_mod == 1.0f ) {
223             continue;
224         }
225         for( std::pair<const vitamin_id, int> &vit : res ) {
226             vit.second *= bid->vitamin_absorb_mod;
227         }
228     }
229     return res;
230 }
231 
232 // Calculate the effective nutrients for a given item, taking
233 // into account character traits but not item components.
compute_default_effective_nutrients(const item & comest,const Character & you,const cata::flat_set<flag_id> & extra_flags={} )234 static nutrients compute_default_effective_nutrients( const item &comest,
235         const Character &you, const cata::flat_set<flag_id> &extra_flags = {} )
236 {
237     // Multiply by 1000 to get it in calories
238     return { compute_default_effective_kcal( comest, you, extra_flags ) * 1000,
239              compute_default_effective_vitamins( comest, you ) };
240 }
241 
242 // Calculate the nutrients that the given character would receive from consuming
243 // the given item, taking into account the item components and the character's
244 // traits.
245 // This is used by item display, making actual nutrition available to character.
compute_effective_nutrients(const item & comest) const246 nutrients Character::compute_effective_nutrients( const item &comest ) const
247 {
248     if( !comest.is_comestible() ) {
249         return {};
250     }
251 
252     // if item has components, will derive calories from that instead.
253     if( !comest.components.empty() && !comest.has_flag( flag_NUTRIENT_OVERRIDE ) ) {
254         nutrients tally{};
255         for( const item &component : comest.components ) {
256             nutrients component_value =
257                 compute_effective_nutrients( component ) * component.charges;
258             if( component.has_flag( flag_BYPRODUCT ) ) {
259                 tally -= component_value;
260             } else {
261                 tally += component_value;
262             }
263         }
264         return tally / comest.recipe_charges;
265     } else {
266         return compute_default_effective_nutrients( comest, *this );
267     }
268 }
269 
270 // Calculate range of nutrients obtainable for a given item when crafted via
271 // the given recipe
compute_nutrient_range(const item & comest,const recipe_id & recipe_i,const cata::flat_set<flag_id> & extra_flags) const272 std::pair<nutrients, nutrients> Character::compute_nutrient_range(
273     const item &comest, const recipe_id &recipe_i,
274     const cata::flat_set<flag_id> &extra_flags ) const
275 {
276     if( !comest.is_comestible() ) {
277         return {};
278     }
279 
280     // if item has components, will derive calories from that instead.
281     if( comest.has_flag( flag_NUTRIENT_OVERRIDE ) ) {
282         nutrients result = compute_default_effective_nutrients( comest, *this );
283         return { result, result };
284     }
285 
286     nutrients tally_min;
287     nutrients tally_max;
288 
289     const recipe &rec = *recipe_i;
290 
291     cata::flat_set<flag_id> our_extra_flags = extra_flags;
292 
293     if( rec.hot_result() ) {
294         our_extra_flags.insert( flag_COOKED );
295     }
296 
297     const requirement_data requirements = rec.simple_requirements();
298     const requirement_data::alter_item_comp_vector &component_requirements =
299         requirements.get_components();
300 
301     for( const std::vector<item_comp> &component_options : component_requirements ) {
302         nutrients this_min;
303         nutrients this_max;
304         bool first = true;
305         for( const item_comp &component_option : component_options ) {
306             std::pair<nutrients, nutrients> component_option_range =
307                 compute_nutrient_range( component_option.type, our_extra_flags );
308             component_option_range.first *= component_option.count;
309             component_option_range.second *= component_option.count;
310 
311             if( first ) {
312                 std::tie( this_min, this_max ) = component_option_range;
313                 first = false;
314             } else {
315                 this_min.min_in_place( component_option_range.first );
316                 this_max.max_in_place( component_option_range.second );
317             }
318         }
319         tally_min += this_min;
320         tally_max += this_max;
321     }
322 
323     for( const std::pair<const itype_id, int> &byproduct : rec.byproducts ) {
324         item byproduct_it( byproduct.first, calendar::turn, byproduct.second );
325         nutrients byproduct_nutr = compute_default_effective_nutrients( byproduct_it, *this );
326         tally_min -= byproduct_nutr;
327         tally_max -= byproduct_nutr;
328     }
329 
330     int charges = comest.count();
331     return { tally_min / charges, tally_max / charges };
332 }
333 
334 // Calculate the range of nturients possible for a given item across all
335 // possible recipes
compute_nutrient_range(const itype_id & comest_id,const cata::flat_set<flag_id> & extra_flags) const336 std::pair<nutrients, nutrients> Character::compute_nutrient_range(
337     const itype_id &comest_id, const cata::flat_set<flag_id> &extra_flags ) const
338 {
339     const itype *comest = item::find_type( comest_id );
340     if( !comest->comestible ) {
341         return {};
342     }
343 
344     item comest_it( comest, calendar::turn, 1 );
345     // The default nutrients are always a possibility
346     nutrients min_nutr = compute_default_effective_nutrients( comest_it, *this, extra_flags );
347 
348     if( comest->has_flag( flag_NUTRIENT_OVERRIDE ) ||
349         recipe_dict.is_item_on_loop( comest->get_id() ) ) {
350         return { min_nutr, min_nutr };
351     }
352 
353     nutrients max_nutr = min_nutr;
354 
355     for( const recipe_id &rec : comest->recipes ) {
356         nutrients this_min;
357         nutrients this_max;
358 
359         item result_it = rec->create_result();
360         if( result_it.contents.num_item_stacks() == 1 ) {
361             const item alt_result = result_it.contents.legacy_front();
362             if( alt_result.typeId() == comest_it.typeId() ) {
363                 result_it = alt_result;
364             }
365         }
366         if( result_it.typeId() != comest_it.typeId() ) {
367             debugmsg( "When creating recipe result expected %s, got %s\n",
368                       comest_it.typeId().str(), result_it.typeId().str() );
369         }
370         std::tie( this_min, this_max ) = compute_nutrient_range( result_it, rec, extra_flags );
371         min_nutr.min_in_place( this_min );
372         max_nutr.max_in_place( this_max );
373     }
374 
375     return { min_nutr, max_nutr };
376 }
377 
nutrition_for(const item & comest) const378 int Character::nutrition_for( const item &comest ) const
379 {
380     return compute_effective_nutrients( comest ).kcal() / islot_comestible::kcal_per_nutr;
381 }
382 
fun_for(const item & comest) const383 std::pair<int, int> Character::fun_for( const item &comest ) const
384 {
385     if( !comest.is_comestible() ) {
386         return std::pair<int, int>( 0, 0 );
387     }
388 
389     // As float to avoid rounding too many times
390     float fun = comest.get_comestible_fun();
391     // Food doesn't taste as good when you're sick
392     if( ( has_effect( effect_common_cold ) || has_effect( effect_flu ) ) && fun > 0 ) {
393         fun /= 3;
394     }
395     // Rotten food should be pretty disgusting
396     const float relative_rot = comest.get_relative_rot();
397     if( relative_rot > 1.0f && !has_trait( trait_SAPROPHAGE ) && !has_trait( trait_SAPROVORE ) ) {
398         const float rottedness = clamp( 2 * relative_rot - 2.0f, 0.1f, 1.0f );
399         // Three effects:
400         // penalty for rot goes from -2 to -20
401         // bonus for tasty food drops from 90% to 0%
402         // disgusting food unfun increases from 110% to 200%
403         fun -= rottedness * 10;
404         if( fun > 0 ) {
405             fun *= ( 1.0f - rottedness );
406         } else {
407             fun *= ( 1.0f + rottedness );
408         }
409     }
410 
411     // Food is less enjoyable when eaten too often.
412     if( fun > 0 || comest.has_flag( flag_NEGATIVE_MONOTONY_OK ) ) {
413         for( const consumption_event &event : consumption_history ) {
414             if( event.time > calendar::turn - 2_days && event.type_id == comest.typeId() &&
415                 event.component_hash == comest.make_component_hash() ) {
416                 fun -= comest.get_comestible()->monotony_penalty;
417                 // This effect can't drop fun below 0, unless the food has the right flag.
418                 // 0 is the lowest we'll go, no need to keep looping.
419                 if( fun <= 0 && !comest.has_flag( flag_NEGATIVE_MONOTONY_OK ) ) {
420                     fun = 0;
421                     break;
422                 }
423             }
424         }
425     }
426 
427     float fun_max = fun < 0 ? fun * 6.0f : fun * 3.0f;
428     if( comest.has_flag( flag_EATEN_COLD ) && comest.has_flag( flag_COLD ) ) {
429         if( fun > 0 ) {
430             fun *= 2;
431         } else {
432             fun = 1;
433             fun_max = 5;
434         }
435     }
436 
437     if( comest.has_flag( flag_MELTS ) && !comest.has_flag( flag_FROZEN ) ) {
438         if( fun > 0 ) {
439             fun *= 0.5;
440         } else {
441             // Melted freezable food tastes 25% worse than frozen freezable food.
442             // Frozen freezable food... say that 5 times fast
443             fun *= 1.25;
444         }
445     }
446 
447     if( ( comest.has_flag( flag_LUPINE ) && has_trait( trait_THRESH_LUPINE ) ) ||
448         ( comest.has_flag( flag_FELINE ) && has_trait( trait_THRESH_FELINE ) ) ) {
449         if( fun < 0 ) {
450             fun = -fun;
451             fun /= 2;
452         }
453     }
454 
455     if( has_trait( trait_GOURMAND ) ) {
456         if( fun < -1 ) {
457             fun_max = fun;
458             fun /= 2;
459         } else if( fun > 0 ) {
460             fun_max *= 3;
461             fun = fun * 3 / 2;
462         }
463     }
464 
465     if( has_active_bionic( bio_taste_blocker ) &&
466         get_power_level() > units::from_kilojoule( std::abs( comest.get_comestible_fun() ) ) &&
467         fun < 0 ) {
468         fun = 0;
469     }
470 
471     return { static_cast< int >( fun ), static_cast< int >( fun_max ) };
472 }
473 
vitamin_rate(const vitamin_id & vit) const474 time_duration Character::vitamin_rate( const vitamin_id &vit ) const
475 {
476     time_duration res = vit.obj().rate();
477 
478     for( const auto &m : get_mutations() ) {
479         const auto &mut = m.obj();
480         auto iter = mut.vitamin_rates.find( vit );
481         if( iter != mut.vitamin_rates.end() ) {
482             res += iter->second;
483         }
484     }
485 
486     return res;
487 }
488 
vitamin_mod(const vitamin_id & vit,int qty,bool capped)489 int Character::vitamin_mod( const vitamin_id &vit, int qty, bool capped )
490 {
491     if( !vit.is_valid() ) {
492         debugmsg( "Vitamin with id %s does not exist, and cannot be modified", vit.str() );
493         return 0;
494     }
495     // What's going on here? Emplace returns either an iterator to the inserted
496     // item or, if it already exists, an iterator to the (unchanged) extant item
497     // (Okay, technically it returns a pair<iterator, bool>, the iterator is what we want)
498     auto it = vitamin_levels.emplace( vit, 0 ).first;
499     const vitamin &v = *it->first;
500 
501     if( qty > 0 ) {
502         // Accumulations can never occur from food sources
503         it->second = std::min( it->second + qty, capped ? 0 : v.max() );
504         update_vitamins( vit );
505 
506     } else if( qty < 0 ) {
507         it->second = std::max( it->second + qty, v.min() );
508         update_vitamins( vit );
509     }
510 
511     return it->second;
512 }
513 
vitamins_mod(const std::map<vitamin_id,int> & vitamins,bool capped)514 void Character::vitamins_mod( const std::map<vitamin_id, int> &vitamins, bool capped )
515 {
516     const bool npc_no_food = is_npc() && get_option<bool>( "NO_NPC_FOOD" );
517     if( !npc_no_food ) {
518         for( const std::pair<const vitamin_id, int> &vit : vitamins ) {
519             vitamin_mod( vit.first, vit.second, capped );
520         }
521     }
522 }
523 
vitamin_get(const vitamin_id & vit) const524 int Character::vitamin_get( const vitamin_id &vit ) const
525 {
526     if( get_option<bool>( "NO_VITAMINS" ) && vit->type() == vitamin_type::VITAMIN ) {
527         return 0;
528     }
529 
530     const auto &v = vitamin_levels.find( vit );
531     return v != vitamin_levels.end() ? v->second : 0;
532 }
533 
vitamin_set(const vitamin_id & vit,int qty)534 bool Character::vitamin_set( const vitamin_id &vit, int qty )
535 {
536     auto v = vitamin_levels.find( vit );
537     if( v == vitamin_levels.end() ) {
538         return false;
539     }
540     vitamin_mod( vit, qty - v->second, false );
541 
542     return true;
543 }
544 
metabolic_rate_base() const545 float Character::metabolic_rate_base() const
546 {
547     static const std::string hunger_rate_string( "PLAYER_HUNGER_RATE" );
548     float hunger_rate = get_option< float >( hunger_rate_string );
549     static const std::string metabolism_modifier( "metabolism_modifier" );
550     return hunger_rate * ( 1.0f + mutation_value( metabolism_modifier ) );
551 }
552 
553 // TODO: Make this less chaotic to let NPC retroactive catch up work here
554 // TODO: Involve body heat (cold -> higher metabolism, unless cold-blooded)
555 // TODO: Involve stamina (maybe not here?)
metabolic_rate() const556 float Character::metabolic_rate() const
557 {
558     // First value is effective hunger, second is nutrition multiplier
559     // Note: Values do not match hungry/v.hungry/famished/starving,
560     // because effective hunger is affected by speed (which drops when hungry)
561     static const std::vector<std::pair<float, float>> thresholds = {{
562             { 300.0f, 1.0f },
563             { 2000.0f, 0.8f },
564             { 5000.0f, 0.6f },
565             { 8000.0f, 0.5f }
566         }
567     };
568 
569     // Penalize fast survivors
570     // TODO: Have cold temperature increase, not decrease, metabolism
571     const float effective_hunger = ( get_hunger() + get_starvation() ) * 100.0f / std::max( 50,
572                                    get_speed() );
573     const float modifier = multi_lerp( thresholds, effective_hunger );
574 
575     return modifier * metabolic_rate_base();
576 }
577 
allergy_type(const item & food) const578 morale_type Character::allergy_type( const item &food ) const
579 {
580     using allergy_tuple = std::tuple<trait_id, flag_id, morale_type>;
581     static const std::array<allergy_tuple, 8> allergy_tuples = {{
582             std::make_tuple( trait_VEGETARIAN, flag_ALLERGEN_MEAT, MORALE_VEGETARIAN ),
583             std::make_tuple( trait_MEATARIAN, flag_ALLERGEN_VEGGY, MORALE_MEATARIAN ),
584             std::make_tuple( trait_LACTOSE, flag_ALLERGEN_MILK, MORALE_LACTOSE ),
585             std::make_tuple( trait_ANTIFRUIT, flag_ALLERGEN_FRUIT, MORALE_ANTIFRUIT ),
586             std::make_tuple( trait_ANTIJUNK, flag_ALLERGEN_JUNK, MORALE_ANTIJUNK ),
587             std::make_tuple( trait_ANTIWHEAT, flag_ALLERGEN_WHEAT, MORALE_ANTIWHEAT )
588         }
589     };
590 
591     for( const auto &tp : allergy_tuples ) {
592         if( has_trait( std::get<0>( tp ) ) &&
593             food.has_flag( std::get<1>( tp ) ) ) {
594             return std::get<2>( tp );
595         }
596     }
597 
598     return MORALE_NULL;
599 }
600 
can_eat(const item & food) const601 ret_val<edible_rating> Character::can_eat( const item &food ) const
602 {
603     bool can_fuel_cbm = can_fuel_bionic_with( food );
604     if( !food.is_comestible() && !can_fuel_cbm ) {
605         return ret_val<edible_rating>::make_failure( _( "That doesn't look edible." ) );
606     } else if( can_fuel_cbm ) {
607         std::string item_name = food.tname();
608         material_id mat_type = food.get_base_material().id;
609         if( food.type->magazine ) {
610             const item ammo = item( food.ammo_current() );
611             item_name = ammo.tname();
612             mat_type = ammo.get_base_material().id;
613         }
614         if( get_fuel_capacity( mat_type ) <= 0 ) {
615             return ret_val<edible_rating>::make_failure( _( "No space to store more %s" ), item_name );
616         } else {
617             return ret_val<edible_rating>::make_success();
618         }
619     }
620 
621     const auto &comest = food.get_comestible();
622 
623     if( food.has_flag( flag_INEDIBLE ) ) {
624         if( ( food.has_flag( flag_CATTLE ) && !has_trait( trait_THRESH_CATTLE ) ) ||
625             ( food.has_flag( flag_FELINE ) && !has_trait( trait_THRESH_FELINE ) ) ||
626             ( food.has_flag( flag_LUPINE ) && !has_trait( trait_THRESH_LUPINE ) ) ||
627             ( food.has_flag( flag_BIRD ) && !has_trait( trait_THRESH_BIRD ) ) ) {
628             return ret_val<edible_rating>::make_failure( _( "That doesn't look edible to you." ) );
629         }
630     }
631 
632     if( food.is_craft() ) {
633         return ret_val<edible_rating>::make_failure( _( "That doesn't look edible in its current form." ) );
634     }
635 
636     if( food.has_own_flag( flag_DIRTY ) ) {
637         return ret_val<edible_rating>::make_failure(
638                    _( "This is full of dirt after being on the ground." ) );
639     }
640 
641     const bool eat_verb  = food.has_flag( flag_USE_EAT_VERB );
642     const bool edible    = eat_verb ||  comest->comesttype == comesttype_FOOD;
643     const bool drinkable = !eat_verb && comest->comesttype == comesttype_DRINK;
644 
645     // TODO: This condition occurs way too often. Unify it.
646     // update Sep. 26 2018: this apparently still occurs way too often. yay!
647     if( is_underwater() && !has_trait( trait_WATERSLEEP ) ) {
648         return ret_val<edible_rating>::make_failure( _( "You can't do that while underwater." ) );
649     }
650 
651     if( edible || drinkable ) {
652         for( const auto &elem : food.type->materials ) {
653             if( !elem->edible() ) {
654                 return ret_val<edible_rating>::make_failure( _( "That doesn't look edible in its current form." ) );
655             }
656         }
657         // For all those folks who loved eating marloss berries.  D:< mwuhahaha
658         if( has_trait( trait_M_DEPENDENT ) && !food.has_flag( flag_MYCUS_OK ) ) {
659             return ret_val<edible_rating>::make_failure( INEDIBLE_MUTATION,
660                     _( "We can't eat that.  It's not right for us." ) );
661         }
662     }
663     if( food.has_own_flag( flag_FROZEN ) && !food.has_flag( flag_EDIBLE_FROZEN ) &&
664         !food.has_flag( flag_MELTS ) ) {
665         if( edible ) {
666             return ret_val<edible_rating>::make_failure(
667                        _( "It's frozen solid.  You must defrost it before you can eat it." ) );
668         }
669         if( drinkable ) {
670             return ret_val<edible_rating>::make_failure( _( "You can't drink it while it's frozen." ) );
671         }
672     }
673 
674     const use_function *consume_drug = food.type->get_use( "consume_drug" );
675     if( consume_drug != nullptr ) { //its a drug)
676         const consume_drug_iuse *consume_drug_use = dynamic_cast<const consume_drug_iuse *>
677                 ( consume_drug->get_actor_ptr() );
678         for( auto &tool : consume_drug_use->tools_needed ) {
679             const bool has = item::count_by_charges( tool.first )
680                              ? has_charges( tool.first, ( tool.second == -1 ) ? 1 : tool.second )
681                              : has_amount( tool.first, 1 );
682             if( !has ) {
683                 return ret_val<edible_rating>::make_failure( NO_TOOL,
684                         string_format( _( "You need a %s to consume that!" ),
685                                        item::nname( tool.first ) ) );
686             }
687         }
688     }
689 
690     const use_function *smoking = food.type->get_use( "SMOKING" );
691     if( smoking != nullptr ) {
692         cata::optional<std::string> litcig = iuse::can_smoke( *this->as_player() );
693         if( litcig.has_value() ) {
694             return ret_val<edible_rating>::make_failure( NO_TOOL, _( litcig.value_or( "" ) ) );
695         }
696     }
697 
698     if( !comest->tool.is_null() ) {
699         const bool has = item::count_by_charges( comest->tool )
700                          ? has_charges( comest->tool, 1 )
701                          : has_amount( comest->tool, 1 );
702         if( !has ) {
703             return ret_val<edible_rating>::make_failure( NO_TOOL,
704                     string_format( _( "You need a %s to consume that!" ),
705                                    item::nname( comest->tool ) ) );
706         }
707     }
708 
709     // Here's why PROBOSCIS is such a negative trait.
710     if( has_trait( trait_PROBOSCIS ) && !( drinkable || food.is_medication() ) ) {
711         return ret_val<edible_rating>::make_failure( INEDIBLE_MUTATION, _( "Ugh, you can't drink that!" ) );
712     }
713 
714     if( has_trait( trait_CARNIVORE ) && nutrition_for( food ) > 0 &&
715         food.has_any_flag( carnivore_blacklist ) && !food.has_flag( flag_CARNIVORE_OK ) ) {
716         return ret_val<edible_rating>::make_failure( INEDIBLE_MUTATION,
717                 _( "Eww.  Inedible plant stuff!" ) );
718     }
719 
720     if( ( has_trait( trait_HERBIVORE ) || has_trait( trait_RUMINANT ) ) &&
721         food.has_any_flag( herbivore_blacklist ) ) {
722         // Like non-cannibal, but more strict!
723         return ret_val<edible_rating>::make_failure( INEDIBLE_MUTATION,
724                 _( "The thought of eating that makes you feel sick." ) );
725     }
726 
727     for( const trait_id &mut : get_mutations() ) {
728         if( !food.made_of_any( mut.obj().can_only_eat ) && !mut.obj().can_only_eat.empty() ) {
729             return ret_val<edible_rating>::make_failure( INEDIBLE_MUTATION, _( "You can't eat this." ) );
730         }
731     }
732 
733     return ret_val<edible_rating>::make_success();
734 }
735 
will_eat(const item & food,bool interactive) const736 ret_val<edible_rating> Character::will_eat( const item &food, bool interactive ) const
737 {
738     const auto ret = can_eat( food );
739     if( !ret.success() ) {
740         if( interactive ) {
741             add_msg_if_player( m_info, "%s", ret.c_str() );
742         }
743         return ret;
744     }
745 
746     // exit early for cbm fuel as we've already tested everything in can_eat
747     if( !food.is_comestible() ) {
748         return ret_val<edible_rating>::make_success();
749     }
750 
751     std::vector<ret_val<edible_rating>> consequences;
752     const auto add_consequence = [&consequences]( const std::string & msg, edible_rating code ) {
753         consequences.emplace_back( ret_val<edible_rating>::make_failure( code, msg ) );
754     };
755 
756     const bool saprophage = has_trait( trait_SAPROPHAGE );
757     const auto &comest = food.get_comestible();
758 
759     if( food.rotten() ) {
760         const bool saprovore = has_trait( trait_SAPROVORE );
761         if( !saprophage && !saprovore ) {
762             add_consequence( _( "This is rotten and smells awful!" ), ROTTEN );
763         }
764     }
765 
766     if( food.has_flag( flag_STRICT_HUMANITARIANISM ) &&
767         !has_trait_flag( json_flag_STRICT_HUMANITARIAN ) ) {
768         add_consequence( _( "The thought of eating demihuman flesh makes you feel sick." ), CANNIBALISM );
769     }
770 
771     const bool carnivore = has_trait( trait_CARNIVORE );
772     if( food.has_flag( flag_CANNIBALISM ) &&
773         !has_trait_flag( STATIC( json_character_flag( "CANNIBAL" ) ) ) ) {
774         add_consequence( _( "The thought of eating human flesh makes you feel sick." ), CANNIBALISM );
775     }
776 
777     if( food.get_comestible()->parasites > 0 && !food.has_flag( flag_NO_PARASITES ) &&
778         !has_flag( json_flag_PARAIMMUNE ) ) {
779         add_consequence( _( "Eating this raw meat probably isn't very healthy." ), PARASITES );
780     }
781 
782     const bool edible = comest->comesttype == comesttype_FOOD || food.has_flag( flag_USE_EAT_VERB );
783 
784     if( edible && has_effect( effect_nausea ) ) {
785         add_consequence( _( "You still feel nauseous and will probably puke it all up again." ), NAUSEA );
786     }
787 
788     if( ( allergy_type( food ) != MORALE_NULL ) || ( carnivore && food.has_flag( flag_ALLERGEN_JUNK ) &&
789             !food.has_flag( flag_CARNIVORE_OK ) ) ) {
790         add_consequence( _( "Your stomach won't be happy (allergy)." ), ALLERGY );
791     }
792 
793     if( saprophage && edible && food.rotten() && !food.has_flag( flag_FERTILIZER ) ) {
794         // Note: We're allowing all non-solid "food". This includes drugs
795         // Hard-coding fertilizer for now - should be a separate flag later
796         //~ No, we don't eat "rotten" food. We eat properly aged food, like a normal person.
797         //~ Semantic difference, but greatly facilitates people being proud of their character.
798         add_consequence( _( "Your stomach won't be happy (not rotten enough)." ), ALLERGY_WEAK );
799     }
800 
801     if( food.charges > 0 && food.is_food() &&
802         ( food.charges_per_volume( stomach.stomach_remaining( *this ) ) < 1 ||
803           has_effect( effect_hunger_full ) || has_effect( effect_hunger_engorged ) ) ) {
804         if( edible ) {
805             add_consequence( _( "You're full already and will be forcing yourself to eat." ), TOO_FULL );
806         } else {
807             add_consequence( _( "You're full already and will be forcing yourself to drink." ), TOO_FULL );
808         }
809     }
810 
811     if( !consequences.empty() ) {
812         if( !interactive ) {
813             return consequences.front();
814         }
815         std::string req;
816         for( const auto &elem : consequences ) {
817             req += elem.str() + "\n";
818         }
819 
820         const bool eat_verb  = food.has_flag( flag_USE_EAT_VERB );
821         std::string food_tame = food.tname();
822         const nc_color food_color = food.color_in_inventory();
823         if( eat_verb || comest->comesttype == comesttype_FOOD ) {
824             req += string_format( _( "Eat your %s anyway?" ), colorize( food_tame, food_color ) );
825         } else if( !eat_verb && comest->comesttype == comesttype_DRINK ) {
826             req += string_format( _( "Drink your %s anyway?" ), colorize( food_tame, food_color ) );
827         } else {
828             req += string_format( _( "Consume your %s anyway?" ), colorize( food_tame, food_color ) );
829         }
830 
831         if( !query_yn( req ) ) {
832             return consequences.front();
833         }
834     }
835     // All checks ended, it's edible (or we're pretending it is)
836     return ret_val<edible_rating>::make_success();
837 }
838 
839 /** Eat a comestible.
840 *   @return true if item consumed.
841 */
eat(item & food,player & you,bool force)842 static bool eat( item &food, player &you, bool force )
843 {
844     if( !food.is_food() ) {
845         return false;
846     }
847 
848     const auto ret = force ? you.can_eat( food ) : you.will_eat( food, you.is_player() );
849     if( !ret.success() ) {
850         return false;
851     }
852 
853     int charges_used = 0;
854     if( food.type->has_use() ) {
855         if( !food.type->can_use( "DOGFOOD" ) &&
856             !food.type->can_use( "CATFOOD" ) &&
857             !food.type->can_use( "BIRDFOOD" ) &&
858             !food.type->can_use( "CATTLEFODDER" ) ) {
859             charges_used = food.type->invoke( you, food, you.pos() ).value_or( 0 );
860             if( charges_used <= 0 ) {
861                 return false;
862             }
863         }
864     }
865 
866     // Note: the block below assumes we decided to eat it
867     // No coming back from here
868 
869     if( food.is_container() ) {
870         food.spill_contents( you );
871     }
872 
873     const bool hibernate = you.has_active_mutation( trait_HIBERNATE );
874     const int nutr = you.nutrition_for( food );
875     const int quench = food.get_comestible()->quench;
876     const bool spoiled = food.rotten();
877 
878     // The item is solid food
879     const bool chew = food.get_comestible()->comesttype == comesttype_FOOD ||
880                       food.has_flag( flag_USE_EAT_VERB );
881     // This item is a drink and not a solid food (and not a thick soup)
882     const bool drinkable = !chew && food.get_comestible()->comesttype == comesttype_DRINK;
883     // If neither of the above is true then it's a drug and shouldn't get mealtime penalty/bonus
884 
885     if( hibernate &&
886         ( you.get_hunger() > -60 && you.get_thirst() > -60 ) &&
887         ( you.get_hunger() - nutr < -60 || you.get_thirst() - quench < -60 ) ) {
888         you.add_msg_if_player(
889             _( "You've begun stockpiling calories and liquid for hibernation.  You get the feeling that you should prepare for bed, just in case, but… you're hungry again, and you could eat a whole week's worth of food RIGHT NOW." ) );
890     }
891 
892     const bool will_vomit = you.stomach.stomach_remaining( you ) < food.volume() &&
893                             rng( units::to_milliliter( you.stomach.capacity( you ) ) / 2,
894                                  units::to_milliliter( you.stomach.contains() ) ) > units::to_milliliter(
895                                 you.stomach.capacity( you ) );
896     const bool saprophage = you.has_trait( trait_SAPROPHAGE );
897     if( spoiled && !saprophage ) {
898         you.add_msg_if_player( m_bad, _( "Ick, this %s doesn't taste so good…" ), food.tname() );
899         if( !you.has_flag( json_flag_IMMUNE_SPOIL ) ) {
900             you.add_effect( effect_foodpoison, rng( 6_minutes, ( nutr + 1 ) * 6_minutes ) );
901         }
902     } else if( spoiled && saprophage ) {
903         you.add_msg_if_player( m_good, _( "Mmm, this %s tastes delicious…" ), food.tname() );
904     }
905     if( !you.consume_effects( food ) ) {
906         // Already consumed by using `food.type->invoke`?
907         if( charges_used > 0 ) {
908             food.mod_charges( -charges_used );
909             return true;
910         }
911         return false;
912     }
913     food.mod_charges( -1 );
914 
915     const bool amorphous = you.has_trait( trait_AMORPHOUS );
916 
917     // If it's poisonous... poison us.
918     // TODO: Move this to a flag
919     if( food.poison > 0 && !you.has_trait( trait_EATPOISON ) &&
920         !you.has_trait( trait_EATDEAD ) ) {
921         if( food.poison >= rng( 2, 4 ) ) {
922             you.add_effect( effect_poison, food.poison * 10_minutes );
923         }
924 
925         you.add_effect( effect_foodpoison, food.poison * 30_minutes );
926     }
927 
928     if( food.has_flag( flag_HIDDEN_HALLU ) ) {
929         if( !you.has_effect( effect_hallu ) ) {
930             you.add_effect( effect_hallu, 6_hours );
931         }
932     }
933 
934     if( amorphous ) {
935         you.add_msg_player_or_npc( _( "You assimilate your %s." ), _( "<npcname> assimilates a %s." ),
936                                    food.tname() );
937     } else if( drinkable ) {
938         if( you.has_trait( trait_SCHIZOPHRENIC ) &&
939             one_in( 50 ) && !spoiled && food.goes_bad() && you.is_player() ) {
940 
941             add_msg( m_bad, _( "Ick, this %s (rotten) doesn't taste so good…" ), food.tname() );
942             add_msg( _( "You drink your %s (rotten)." ), food.tname() );
943         } else {
944             you.add_msg_player_or_npc( _( "You drink your %s." ), _( "<npcname> drinks a %s." ),
945                                        food.tname() );
946         }
947     } else if( chew ) {
948         if( you.has_trait( trait_SCHIZOPHRENIC ) &&
949             one_in( 50 ) && !spoiled && food.goes_bad() && you.is_player() ) {
950 
951             add_msg( m_bad, _( "Ick, this %s (rotten) doesn't taste so good…" ), food.tname() );
952             add_msg( _( "You eat your %s (rotten)." ), food.tname() );
953         } else {
954             you.add_msg_player_or_npc( _( "You eat your %s." ), _( "<npcname> eats a %s." ),
955                                        food.tname() );
956         }
957     }
958 
959     if( item::find_type( food.get_comestible()->tool )->tool ) {
960         // Tools like lighters get used
961         you.use_charges( food.get_comestible()->tool, 1 );
962     }
963 
964     if( you.has_active_bionic( bio_taste_blocker ) ) {
965         you.mod_power_level( units::from_kilojoule( -std::abs( food.get_comestible_fun() ) ) );
966     }
967 
968     if( food.has_flag( flag_FUNGAL_VECTOR ) && !you.has_trait( trait_M_IMMUNE ) ) {
969         you.add_effect( effect_fungus, 1_turns, true );
970     }
971 
972     // The fun changes for these effects are applied in fun_for().
973     if( food.has_flag( flag_MUSHY ) ) {
974         you.add_msg_if_player( m_bad,
975                                _( "You try to ignore its mushy texture, but it leaves you with an awful aftertaste." ) );
976     }
977     if( food.get_comestible_fun() > 0 ) {
978         if( you.has_effect( effect_common_cold ) ) {
979             you.add_msg_if_player( m_bad, _( "You can't taste much of anything with this cold." ) );
980         }
981         if( you.has_effect( effect_flu ) ) {
982             you.add_msg_if_player( m_bad, _( "You can't taste much of anything with this flu." ) );
983         }
984     }
985 
986     // Chance to become parasitised
987     if( !will_vomit && !you.has_flag( json_flag_PARAIMMUNE ) ) {
988         if( food.get_comestible()->parasites > 0 && !food.has_flag( flag_NO_PARASITES ) &&
989             one_in( food.get_comestible()->parasites ) ) {
990             switch( rng( 0, 3 ) ) {
991                 case 0:
992                     if( !you.has_trait( trait_EATHEALTH ) ) {
993                         you.add_effect( effect_tapeworm, 1_turns, true );
994                     }
995                     break;
996                 case 1:
997                     if( !you.has_trait( trait_ACIDBLOOD ) ) {
998                         you.add_effect( effect_bloodworms, 1_turns, true );
999                     }
1000                     break;
1001                 case 2:
1002                     you.add_effect( effect_brainworms, 1_turns, true );
1003                     break;
1004                 case 3:
1005                     you.add_effect( effect_paincysts, 1_turns, true );
1006             }
1007         }
1008     }
1009 
1010     for( const std::pair<const diseasetype_id, int> &elem : food.get_comestible()->contamination ) {
1011         if( rng( 1, 100 ) <= elem.second ) {
1012             you.expose_to_disease( elem.first );
1013         }
1014     }
1015 
1016     get_event_bus().send<event_type::character_eats_item>( you.getID(), food.typeId() );
1017 
1018     if( will_vomit ) {
1019         you.vomit();
1020     }
1021 
1022     you.consumption_history.emplace_back( food );
1023     // Clean out consumption_history so it doesn't get bigger than needed.
1024     while( you.consumption_history.front().time < calendar::turn - 2_days ) {
1025         you.consumption_history.pop_front();
1026     }
1027 
1028     return true;
1029 }
1030 
modify_health(const islot_comestible & comest)1031 void Character::modify_health( const islot_comestible &comest )
1032 {
1033     const int effective_health = comest.healthy;
1034     // Effectively no cap on health modifiers from food and meds
1035     const int health_cap = 200;
1036     mod_healthy_mod( effective_health, effective_health >= 0 ? health_cap : -health_cap );
1037 }
1038 
modify_stimulation(const islot_comestible & comest)1039 void Character::modify_stimulation( const islot_comestible &comest )
1040 {
1041     if( comest.stim == 0 ) {
1042         return;
1043     }
1044     const int current_stim = get_stim();
1045     if( ( std::abs( comest.stim ) * 3 ) > std::abs( current_stim ) ) {
1046         mod_stim( comest.stim );
1047     } else {
1048         comest.stim > 0 ? mod_stim( std::max( comest.stim / 2, 1 ) ) : mod_stim( std::min( comest.stim / 2,
1049                 -1 ) );
1050     }
1051     if( has_trait( trait_STIMBOOST ) && ( current_stim > 30 ) &&
1052         ( ( comest.add == add_type::CAFFEINE ) || ( comest.add == add_type::SPEED ) ||
1053           ( comest.add == add_type::COKE ) || ( comest.add == add_type::CRACK ) ) ) {
1054         int hallu_duration = ( current_stim - comest.stim < 30 ) ? current_stim - 30 : comest.stim;
1055         add_effect( effect_visuals, hallu_duration * 30_minutes );
1056         std::vector<std::string> stimboost_msg{ _( "The shadows are getting ever closer." ),
1057                                                 _( "You have a bad feeling about this." ),
1058                                                 _( "A powerful sense of dread comes over you." ),
1059                                                 _( "Your skin starts crawling." ),
1060                                                 _( "They're coming to get you." ),
1061                                                 _( "This might've been a bad idea…" ),
1062                                                 _( "You've really done it this time, haven't you?" ),
1063                                                 _( "You have to stay vigilant.  They're always watching…" ),
1064                                                 _( "mistake mistake mistake mistake mistake" ),
1065                                                 _( "Just gotta stay calm, and you'll make it through this." ),
1066                                                 _( "You're starting to feel very jumpy." ),
1067                                                 _( "Something is twitching at the edge of your vision." ),
1068                                                 _( "They know what you've done…" ),
1069                                                 _( "You're feeling even more paranoid than usual." ) };
1070         add_msg_if_player( m_bad, random_entry_ref( stimboost_msg ) );
1071     }
1072 }
1073 
modify_fatigue(const islot_comestible & comest)1074 void Character::modify_fatigue( const islot_comestible &comest )
1075 {
1076     mod_fatigue( -comest.fatigue_mod );
1077 }
1078 
modify_radiation(const islot_comestible & comest)1079 void Character::modify_radiation( const islot_comestible &comest )
1080 {
1081     irradiate( comest.radiation );
1082 }
1083 
modify_addiction(const islot_comestible & comest)1084 void Character::modify_addiction( const islot_comestible &comest )
1085 {
1086     add_addiction( comest.add, comest.addict );
1087     if( addiction_craving( comest.add ) != MORALE_NULL ) {
1088         rem_morale( addiction_craving( comest.add ) );
1089     }
1090 }
1091 
modify_morale(item & food,const int nutr)1092 void Character::modify_morale( item &food, const int nutr )
1093 {
1094     time_duration morale_time = 2_hours;
1095     if( food.has_flag( flag_HOT ) && food.has_flag( flag_EATEN_HOT ) ) {
1096         morale_time = 3_hours;
1097         int clamped_nutr = std::max( 5, std::min( 20, nutr / 10 ) );
1098         add_morale( MORALE_FOOD_HOT, clamped_nutr, 20, morale_time, morale_time / 2 );
1099     }
1100 
1101     std::pair<int, int> fun = fun_for( food );
1102     if( fun.first < 0 ) {
1103         add_morale( MORALE_FOOD_BAD, fun.first, fun.second, morale_time, morale_time / 2, false,
1104                     food.type );
1105     } else if( fun.first > 0 ) {
1106         add_morale( MORALE_FOOD_GOOD, fun.first, fun.second, morale_time, morale_time / 2, false,
1107                     food.type );
1108     }
1109 
1110     // Morale bonus for eating unspoiled food with chair/table nearby
1111     // Does not apply to non-ingested consumables like bandages or drugs,
1112     // nor to drinks.
1113     if( !food.has_flag( flag_NO_INGEST ) &&
1114         food.get_comestible()->comesttype != "MED" &&
1115         food.get_comestible()->comesttype != comesttype_DRINK ) {
1116         map &here = get_map();
1117         if( here.has_nearby_chair( pos(), 1 ) && here.has_nearby_table( pos(), 1 ) ) {
1118             if( has_trait( trait_TABLEMANNERS ) ) {
1119                 rem_morale( MORALE_ATE_WITHOUT_TABLE );
1120                 if( !food.rotten() ) {
1121                     add_morale( MORALE_ATE_WITH_TABLE, 3, 3, 3_hours, 2_hours, true );
1122                 }
1123             } else if( !food.rotten() ) {
1124                 add_morale( MORALE_ATE_WITH_TABLE, 1, 1, 3_hours, 2_hours, true );
1125             }
1126         } else {
1127             if( has_trait( trait_TABLEMANNERS ) ) {
1128                 rem_morale( MORALE_ATE_WITH_TABLE );
1129                 add_morale( MORALE_ATE_WITHOUT_TABLE, -2, -4, 3_hours, 2_hours, true );
1130             }
1131         }
1132     }
1133 
1134     if( food.has_flag( flag_HIDDEN_HALLU ) ) {
1135         if( has_trait( trait_SPIRITUAL ) ) {
1136             add_morale( MORALE_FOOD_GOOD, 36, 72, 2_hours, 1_hours, false );
1137         } else {
1138             add_morale( MORALE_FOOD_GOOD, 18, 36, 1_hours, 30_minutes, false );
1139         }
1140     }
1141 
1142     if( food.has_flag( flag_CANNIBALISM ) ) {
1143         // Sapiovores don't recognize humans as the same species.
1144         // But let them possibly feel cool about eating sapient stuff - treat like psycho
1145         // However, spiritual sapiovores should still recognize humans as having a soul or special for religious reasons
1146         const bool cannibal = has_trait( trait_CANNIBAL );
1147         const bool psycho = has_trait( trait_PSYCHOPATH );
1148         const bool sapiovore = has_trait( trait_SAPIOVORE );
1149         const bool spiritual = has_trait( trait_SPIRITUAL );
1150         if( ( cannibal || sapiovore ) && psycho && spiritual ) {
1151             add_msg_if_player( m_good,
1152                                _( "You feast upon the human flesh, and in doing so, devour their spirit." ) );
1153             // You're not really consuming anything special; you just think you are.
1154             add_morale( MORALE_CANNIBAL, 25, 300 );
1155         } else if( cannibal && psycho ) {
1156             add_msg_if_player( m_good, _( "You feast upon the human flesh." ) );
1157             add_morale( MORALE_CANNIBAL, 15, 200 );
1158         } else if( ( cannibal || sapiovore ) && spiritual ) {
1159             add_msg_if_player( m_good, _( "You consume the sacred human flesh." ) );
1160             // Boosted because you understand the philosophical implications of your actions, and YOU LIKE THEM.
1161             add_morale( MORALE_CANNIBAL, 15, 200 );
1162         } else if( cannibal ) {
1163             add_msg_if_player( m_good, _( "You indulge your shameful hunger." ) );
1164             add_morale( MORALE_CANNIBAL, 10, 50 );
1165         } else if( ( psycho || sapiovore ) && spiritual ) {
1166             add_msg_if_player( _( "You greedily devour the taboo meat." ) );
1167             // Small bonus for violating a taboo.
1168             add_morale( MORALE_CANNIBAL, 5, 50 );
1169         } else if( psycho || sapiovore ) {
1170             add_msg_if_player( _( "Meh.  You've eaten worse." ) );
1171         } else if( spiritual ) {
1172             add_msg_if_player( m_bad,
1173                                _( "This is probably going to count against you if there's still an afterlife." ) );
1174             add_morale( MORALE_CANNIBAL, -60, -400, 60_minutes, 30_minutes );
1175         } else {
1176             add_msg_if_player( m_bad, _( "You feel horrible for eating a person." ) );
1177             add_morale( MORALE_CANNIBAL, -60, -400, 60_minutes, 30_minutes );
1178         }
1179     }
1180 
1181     // While raw flesh usually means negative morale, carnivores and cullers get a small bonus.
1182     // Hunters, predators, and apex predators don't mind raw flesh at all, maybe even like it.
1183     // Cooked flesh is unaffected, because people with these traits *prefer* it raw. Fat is unaffected.
1184     // Organs are still usually negative due to fun values as low as -35.
1185     // The PREDATOR_FUN flag shouldn't be on human flesh, to not interfere with sapiovores/cannibalism.
1186     if( food.has_flag( flag_PREDATOR_FUN ) ) {
1187         const bool carnivore = has_trait( trait_CARNIVORE );
1188         const bool culler = has_trait_flag( json_flag_PRED1 );
1189         const bool hunter = has_trait_flag( json_flag_PRED2 );
1190         const bool predator = has_trait_flag( json_flag_PRED3 );
1191         const bool apex_predator = has_trait_flag( json_flag_PRED4 );
1192         if( apex_predator ) {
1193             // Largest bonus, balances out to around +5 or +10. Some organs may still be negative.
1194             add_morale( MORALE_MEATARIAN, 20, 10 );
1195             add_msg_if_player( m_good,
1196                                _( "As you tear into the raw flesh, you feel satisfied with your meal." ) );
1197         } else if( predator || hunter ) {
1198             // Should approximately balance the fun to 0 for normal meat.
1199             add_morale( MORALE_MEATARIAN, 15, 5 );
1200             add_msg_if_player( m_good,
1201                                _( "Raw flesh doesn't taste all that bad, actually." ) );
1202         } else if( carnivore || culler ) {
1203             // Only a small bonus (+5), still negative fun.
1204             add_morale( MORALE_MEATARIAN, 5, 0 );
1205             add_msg_if_player( m_bad,
1206                                _( "This doesn't taste very good, but meat is meat." ) );
1207         }
1208     }
1209 
1210     // Allergy check for food that is ingested (not gum)
1211     if( !food.has_flag( flag_NO_INGEST ) ) {
1212         const auto allergy = allergy_type( food );
1213         if( allergy != MORALE_NULL ) {
1214             add_msg_if_player( m_bad, _( "Yuck!  How can anybody eat this stuff?" ) );
1215             add_morale( allergy, -75, -400, 30_minutes, 24_minutes );
1216         }
1217         if( food.has_flag( flag_ALLERGEN_JUNK ) ) {
1218             if( has_trait( trait_PROJUNK ) ) {
1219                 add_msg_if_player( m_good, _( "Mmm, junk food." ) );
1220                 add_morale( MORALE_SWEETTOOTH, 5, 30, 30_minutes, 24_minutes );
1221             }
1222             if( has_trait( trait_PROJUNK2 ) ) {
1223                 if( !one_in( 100 ) ) {
1224                     add_msg_if_player( m_good, _( "When life's got you down, there's always sugar." ) );
1225                 } else {
1226                     add_msg_if_player( m_good, _( "They may do what they must… you've already won." ) );
1227                 }
1228                 add_morale( MORALE_SWEETTOOTH, 10, 50, 1_hours, 50_minutes );
1229             }
1230             // Carnivores CAN eat junk food, but they won't like it much.
1231             // Pizza-scraping happens in consume_effects.
1232             if( has_trait( trait_CARNIVORE ) && !food.has_flag( flag_CARNIVORE_OK ) ) {
1233                 add_msg_if_player( m_bad, _( "Your stomach begins gurgling and you feel bloated and ill." ) );
1234                 add_morale( MORALE_NO_DIGEST, -25, -125, 30_minutes, 24_minutes );
1235             }
1236         }
1237     }
1238     const bool chew = food.get_comestible()->comesttype == comesttype_FOOD ||
1239                       food.has_flag( flag_USE_EAT_VERB );
1240     if( !food.rotten() && chew && has_trait( trait_SAPROPHAGE ) ) {
1241         // It's OK to *drink* things that haven't rotted.  Alternative is to ban water.  D:
1242         add_msg_if_player( m_bad, _( "Your stomach begins gurgling and you feel bloated and ill." ) );
1243         add_morale( MORALE_NO_DIGEST, -75, -400, 30_minutes, 24_minutes );
1244     }
1245     if( food.has_flag( flag_URSINE_HONEY ) && ( !crossed_threshold() ||
1246             has_trait( trait_THRESH_URSINE ) ) &&
1247         mutation_category_level[mutation_category_URSINE] > 40 ) {
1248         // Need at least 5 bear mutations for effect to show, to filter out mutations in common with other categories
1249         int honey_fun = has_trait( trait_THRESH_URSINE ) ?
1250                         std::min( mutation_category_level[mutation_category_URSINE] / 8, 20 ) :
1251                         mutation_category_level[mutation_category_URSINE] / 12;
1252         if( honey_fun < 10 ) {
1253             add_msg_if_player( m_good, _( "You find the sweet taste of honey surprisingly palatable." ) );
1254         } else {
1255             add_msg_if_player( m_good, _( "You feast upon the sweet honey." ) );
1256         }
1257         add_morale( MORALE_HONEY, honey_fun, 100 );
1258     }
1259 }
1260 
1261 // Used when determining stomach fullness from eating.
compute_effective_food_volume_ratio(const item & food) const1262 double Character::compute_effective_food_volume_ratio( const item &food ) const
1263 {
1264     const nutrients food_nutrients = compute_effective_nutrients( food );
1265     units::mass food_weight = ( food.weight() / std::max( 1, food.count() ) );
1266     double ratio = 1.0f;
1267     if( units::to_gram( food_weight ) != 0 ) {
1268         ratio = std::max( static_cast<double>( food_nutrients.kcal() ) / units::to_gram( food_weight ),
1269                           1.0 );
1270         if( ratio > 3.0f ) {
1271             ratio = std::sqrt( 3 * ratio );
1272         }
1273     }
1274     return ratio;
1275 }
1276 
1277 // Remove the water volume from the food, as that gets absorbed and used as water.
1278 // If the remaining dry volume of the food is less dense than water, crunch it down to a density equal to water.
1279 // These maths are made easier by the fact that 1 g = 1 mL. Thanks, metric system.
masticated_volume(const item & food) const1280 units::volume Character::masticated_volume( const item &food ) const
1281 {
1282     units::volume water_vol = ( food.get_comestible()->quench > 0 ) ? food.get_comestible()->quench *
1283                               5_ml : 0_ml;
1284     units::mass water_weight = units::from_gram( units::to_milliliter( water_vol ) );
1285     // handle the division by zero exception when the food count is 0 with std::max()
1286     units::mass food_dry_weight = food.weight() / std::max( 1, food.count() ) - water_weight;
1287     units::volume food_dry_volume = food.volume() / std::max( 1, food.count() ) - water_vol;
1288 
1289     if( units::to_milliliter( food_dry_volume ) != 0 &&
1290         units::to_gram( food_dry_weight ) < units::to_milliliter( food_dry_volume ) ) {
1291         food_dry_volume = units::from_milliliter( units::to_gram( food_dry_weight ) );
1292     }
1293 
1294     return food_dry_volume;
1295 }
1296 
1297 // Used when displaying effective food satiation values.
compute_calories_per_effective_volume(const item & food,const nutrients * nutrient) const1298 int Character::compute_calories_per_effective_volume( const item &food,
1299         const nutrients *nutrient /* = nullptr */ )const
1300 {
1301     /* Understanding how Calories Per Effective Volume are calculated requires a dive into the
1302     stomach fullness source code. Look at issue #44365*/
1303     int kcalories;
1304     if( nutrient ) {
1305         // if given the optional nutrient argument, we will compute kcal based on that. ( Crafting menu ).
1306         kcalories = nutrient->kcal();
1307     } else {
1308         kcalories = compute_effective_nutrients( food ).kcal();
1309     }
1310     double food_vol = round_up( units::to_liter( masticated_volume( food ) ), 2 );
1311     const double energy_density_ratio = compute_effective_food_volume_ratio( food );
1312     const double effective_volume = food_vol * energy_density_ratio;
1313     if( kcalories == 0 && effective_volume == 0.0 ) {
1314         return 0;
1315     }
1316     return std::round( kcalories / effective_volume );
1317 }
1318 
consume_effects(item & food)1319 bool Character::consume_effects( item &food )
1320 {
1321     if( !food.is_comestible() ) {
1322         debugmsg( "called Character::consume_effects with non-comestible" );
1323         return false;
1324     }
1325 
1326     if( has_trait( trait_THRESH_PLANT ) && food.type->can_use( "PLANTBLECH" ) ) {
1327         // Was used to cap nutrition and thirst, but no longer does this
1328         return false;
1329     }
1330     if( ( has_trait( trait_HERBIVORE ) || has_trait( trait_RUMINANT ) ) &&
1331         food.has_any_flag( herbivore_blacklist ) ) {
1332         // No good can come of this.
1333         return false;
1334     }
1335 
1336     const auto &comest = *food.get_comestible();
1337 
1338     // Rotten food causes health loss
1339     const float relative_rot = food.get_relative_rot();
1340     if( relative_rot > 1.0f && !has_flag( json_flag_IMMUNE_SPOIL ) ) {
1341         const float rottedness = clamp( 2 * relative_rot - 2.0f, 0.1f, 1.0f );
1342         // ~-1 health per 1 nutrition at halfway-rotten-away, ~0 at "just got rotten"
1343         // But always round down
1344         int h_loss = -rottedness * comest.get_default_nutr();
1345         mod_healthy_mod( h_loss, -200 );
1346         add_msg_debug( "%d health from %0.2f%% rotten food", h_loss, rottedness );
1347     }
1348 
1349     // Used in hibernation messages.
1350     const int nutr = nutrition_for( food );
1351     const bool skip_health = has_trait( trait_PROJUNK2 ) && comest.healthy < 0;
1352     // We can handle junk just fine
1353     if( !skip_health ) {
1354         modify_health( comest );
1355     }
1356     modify_stimulation( comest );
1357     modify_fatigue( comest );
1358     modify_radiation( comest );
1359     modify_addiction( comest );
1360     modify_morale( food, nutr );
1361 
1362     const bool hibernate = has_active_mutation( trait_HIBERNATE );
1363     if( hibernate ) {
1364         if( ( nutr > 0 && get_hunger() < -60 ) || ( comest.quench > 0 && get_thirst() < -60 ) ) {
1365             // Tell the player what's going on
1366             add_msg_if_player( _( "You gorge yourself, preparing to hibernate." ) );
1367             if( one_in( 2 ) ) {
1368                 // 50% chance of the food tiring you
1369                 mod_fatigue( nutr );
1370             }
1371         }
1372         if( ( nutr > 0 && get_hunger() < -200 ) || ( comest.quench > 0 && get_thirst() < -200 ) ) {
1373             // Hibernation should cut burn to 60/day
1374             add_msg_if_player( _( "You feel stocked for a day or two.  Got your bed all ready and secured?" ) );
1375             if( one_in( 2 ) ) {
1376                 // And another 50%, intended cumulative
1377                 mod_fatigue( nutr );
1378             }
1379         }
1380 
1381         if( ( nutr > 0 && get_hunger() < -400 ) || ( comest.quench > 0 && get_thirst() < -400 ) ) {
1382             add_msg_if_player(
1383                 _( "Mmm.  You can still fit some more in… but maybe you should get comfortable and sleep." ) );
1384             if( !one_in( 3 ) ) {
1385                 // Third check, this one at 66%
1386                 mod_fatigue( nutr );
1387             }
1388         }
1389         if( ( nutr > 0 && get_hunger() < -600 ) || ( comest.quench > 0 && get_thirst() < -600 ) ) {
1390             add_msg_if_player( _( "That filled a hole!  Time for bed…" ) );
1391             // At this point, you're done.  Schlaf gut.
1392             mod_fatigue( nutr );
1393         }
1394     }
1395     // Moved here and changed a bit - it was too complex
1396     // Incredibly minor stuff like this shouldn't require complexity
1397     if( !is_npc() && has_trait( trait_SLIMESPAWNER ) &&
1398         ( get_healthy_kcal() < get_stored_kcal() + 4000 &&
1399           get_thirst() - stomach.get_water() / 5_ml < -20 ) && get_thirst() < 40 ) {
1400         add_msg_if_player( m_mixed,
1401                            _( "You feel as though you're going to split open!  In a good way?" ) );
1402         mod_pain( 5 );
1403         int numslime = 1;
1404         for( int i = 0; i < numslime; i++ ) {
1405             if( monster *const slime = g->place_critter_around( mon_player_blob, pos(), 1 ) ) {
1406                 slime->friendly = -1;
1407             }
1408         }
1409         mod_hunger( 40 );
1410         mod_thirst( 40 );
1411         //~ slimespawns have *small voices* which may be the Nice equivalent
1412         //~ of the Rat King's ALL CAPS invective.  Probably shared-brain telepathy.
1413         add_msg_if_player( m_good, _( "hey, you look like me!  let's work together!" ) );
1414     }
1415 
1416     nutrients food_nutrients = compute_effective_nutrients( food );
1417     const units::volume water_vol = ( food.get_comestible()->quench > 0 ) ?
1418                                     food.get_comestible()->quench *
1419                                     5_ml : 0_ml;
1420     units::volume food_vol = masticated_volume( food );
1421     if( food.count() == 0 ) {
1422         debugmsg( "Tried to eat food with count of zero." );
1423         return false;
1424     }
1425     units::mass food_weight = ( food.weight() / food.count() );
1426     const double ratio = compute_effective_food_volume_ratio( food );
1427     food_summary ingested{
1428         water_vol,
1429         food_vol * ratio,
1430         food_nutrients
1431     };
1432     add_msg_debug(
1433         "Effective volume: %d (solid) %d (liquid)\n multiplier: %g calories: %d, weight: %d",
1434         units::to_milliliter( ingested.solids ), units::to_milliliter( ingested.water ), ratio,
1435         food_nutrients.kcal(), units::to_gram( food_weight ) );
1436     // Maybe move tapeworm to digestion
1437     if( has_effect( effect_tapeworm ) ) {
1438         ingested.nutr /= 2;
1439     }
1440 
1441     // GET IN MAH BELLY!
1442     stomach.ingest( ingested );
1443     return true;
1444 }
1445 
fuel_bionic_with(item & it)1446 bool Character::fuel_bionic_with( item &it )
1447 {
1448     if( !can_fuel_bionic_with( it ) ) {
1449         return false;
1450     }
1451 
1452     if( it.is_favorite &&
1453         !get_avatar().query_yn( _( "Are you sure you want to eat your favorited %s?" ), it.tname() ) ) {
1454         return false;
1455     }
1456 
1457     const bionic_id bio = get_most_efficient_bionic( get_bionic_fueled_with( it ) );
1458 
1459     const bool is_magazine = !!it.type->magazine;
1460     int loadable;
1461     material_id mat = it.get_base_material().id;
1462 
1463     if( is_magazine ) {
1464         const item ammo = item( it.ammo_current() );
1465         mat = ammo.get_base_material().id;
1466         loadable = std::min( it.ammo_remaining(), get_fuel_capacity( mat ) );
1467         it.ammo_set( ammo.typeId(), it.ammo_remaining() - loadable );
1468     } else if( it.flammable() ) {
1469         // This a special case for items that are not fuels and don't have charges
1470         loadable = std::min( units::to_milliliter( it.volume() ), get_fuel_capacity( mat ) );
1471         it.charges -= it.charges_per_volume( units::from_milliliter( loadable ) );
1472     } else {
1473         loadable = std::min( it.charges, get_fuel_capacity( mat ) );
1474         it.charges -= loadable;
1475     }
1476 
1477     const std::string str_loaded  = get_value( mat.str() );
1478     int loaded = 0;
1479     if( !str_loaded.empty() ) {
1480         loaded = std::stoi( str_loaded );
1481     }
1482 
1483     const std::string new_charge = std::to_string( loadable + loaded );
1484 
1485     // Type and amount of fuel
1486     set_value( mat.str(), new_charge );
1487     update_fuel_storage( mat );
1488     add_msg_player_or_npc( m_info,
1489                            //~ %1$i: charge number, %2$s: item name, %3$s: bionics name
1490                            ngettext( "You load %1$i charge of %2$s in your %3$s.",
1491                                      "You load %1$i charges of %2$s in your %3$s.", loadable ),
1492                            //~ %1$i: charge number, %2$s: item name, %3$s: bionics name
1493                            ngettext( "<npcname> load %1$i charge of %2$s in their %3$s.",
1494                                      "<npcname> load %1$i charges of %2$s in their %3$s.", loadable ), loadable, mat->name(),
1495                            bio->name );
1496     mod_moves( -250 );
1497     // Return false for magazines because only their ammo is consumed
1498     return !is_magazine;
1499 }
1500 
get_acquirable_energy(const item & it) const1501 int Character::get_acquirable_energy( const item &it ) const
1502 {
1503     const std::vector<bionic_id> &bids = get_bionic_fueled_with( it );
1504     if( bids.empty() ) {
1505         return 0;
1506     }
1507     const bionic_id &bid = get_most_efficient_bionic( bids );
1508     int to_consume;
1509     int to_charge = 0;
1510     if( it.type->magazine ) {
1511         item ammo = item( it.ammo_current() );
1512         to_consume = std::min( it.ammo_remaining(), bid->fuel_capacity );
1513         to_charge = ammo.fuel_energy() * to_consume * bid->fuel_efficiency;
1514     } else if( it.flammable() ) {
1515         to_consume = std::min( units::to_milliliter( it.volume() ), bid->fuel_capacity );
1516         to_charge = it.get_base_material().id->get_fuel_data().energy * to_consume * bid->fuel_efficiency;
1517     } else {
1518         to_consume = std::min( it.charges, bid->fuel_capacity );
1519         to_charge = it.fuel_energy() * to_consume * bid->fuel_efficiency;
1520     }
1521     return to_charge;
1522 }
1523 
can_estimate_rot() const1524 bool Character::can_estimate_rot() const
1525 {
1526     return get_skill_level( skill_cooking ) >= 3 || get_skill_level( skill_survival ) >= 4;
1527 }
1528 
can_consume_as_is(const item & it) const1529 bool Character::can_consume_as_is( const item &it ) const
1530 {
1531     return it.is_comestible() || can_fuel_bionic_with( it );
1532 }
1533 
can_consume(const item & it) const1534 bool Character::can_consume( const item &it ) const
1535 {
1536     if( can_consume_as_is( it ) ) {
1537         return true;
1538     }
1539     return it.has_item_with( [&]( const item & consumable ) {
1540         // Checking NO_RELOAD to prevent consumption of `battery` when contained in `battery_car` (#20012)
1541         return !consumable.has_flag( flag_NO_RELOAD ) && can_consume_as_is( consumable );
1542     } );
1543 }
1544 
get_consumable_from(item & it) const1545 item &Character::get_consumable_from( item &it ) const
1546 {
1547     item *ret = nullptr;
1548     it.visit_items( [&]( item * it, item * ) {
1549         if( can_consume_as_is( *it ) ) {
1550             ret = it;
1551             return VisitResponse::ABORT;
1552         }
1553         return VisitResponse::NEXT;
1554     } );
1555 
1556     if( ret != nullptr ) {
1557         return *ret;
1558     }
1559 
1560     static item null_comestible;
1561     // Since it's not const.
1562     null_comestible = item();
1563     return null_comestible;
1564 }
1565 
get_consume_time(const item & it)1566 time_duration Character::get_consume_time( const item &it )
1567 {
1568     const int charges = std::max( it.charges, 1 );
1569     int volume = units::to_milliliter( it.volume() ) / charges;
1570     if( 0 == volume && it.type ) {
1571         volume = units::to_milliliter( it.type->volume );
1572     }
1573     time_duration time = time_duration::from_seconds( std::max( ( volume /
1574                          5 ), 1 ) );  //Default 5 mL (1 tablespoon) per second
1575     float consume_time_modifier = 1.0f;//only for food and drinks
1576     const bool eat_verb = it.has_flag( flag_USE_EAT_VERB );
1577     const std::string comest_type = it.get_comestible() ? it.get_comestible()->comesttype : "";
1578     if( eat_verb || comest_type == "FOOD" ) {
1579         time = time_duration::from_seconds( volume / 5 ); //Eat 5 mL (1 teaspoon) per second
1580         consume_time_modifier = mutation_value( "consume_time_modifier" );
1581     } else if( !eat_verb && comest_type == "DRINK" ) {
1582         time = time_duration::from_seconds( volume / 15 ); //Drink 15 mL (1 tablespoon) per second
1583         consume_time_modifier = mutation_value( "consume_time_modifier" );
1584     } else if( it.is_medication() ) {
1585         const use_function *consume_drug = it.type->get_use( "consume_drug" );
1586         const use_function *smoking = it.type->get_use( "SMOKING" );
1587         const use_function *adrenaline_injector = it.type->get_use( "ADRENALINE_INJECTOR" );
1588         const use_function *heal = it.type->get_use( "heal" );
1589         if( consume_drug != nullptr ) { //its a drug
1590             const consume_drug_iuse *consume_drug_use = dynamic_cast<const consume_drug_iuse *>
1591                     ( consume_drug->get_actor_ptr() );
1592             if( consume_drug_use->tools_needed.find( itype_syringe ) != consume_drug_use->tools_needed.end() ) {
1593                 time = time_duration::from_minutes( 5 );//sterile injections take 5 minutes
1594             } else if( consume_drug_use->tools_needed.find( itype_apparatus ) !=
1595                        consume_drug_use->tools_needed.end() ||
1596                        consume_drug_use->tools_needed.find( itype_dab_pen_on ) != consume_drug_use->tools_needed.end() ) {
1597                 time = time_duration::from_seconds( 30 );//smoke a bowl
1598             } else {
1599                 time = time_duration::from_seconds( 5 );//popping a pill is quick
1600             }
1601         } else if( smoking != nullptr ) {
1602             time = time_duration::from_minutes( 1 );//about five minutes for a cig or joint so 1 minute a charge
1603         } else if( adrenaline_injector != nullptr ) {
1604             time = time_duration::from_seconds( 15 );//epi-pens are fairly quick
1605         } else if( heal != nullptr ) {
1606             time = time_duration::from_seconds( 15 );//bandages and disinfectant are fairly quick
1607         } else {
1608             time = time_duration::from_seconds( 5 ); //probably pills so quick
1609         }
1610     } else if( it.get_category_shallow().get_id() == item_category_chems ) {
1611         time = time_duration::from_seconds( std::max( ( volume / 15 ),
1612                                             1 ) ); //Consume 15 mL (1 tablespoon) per second
1613         consume_time_modifier = mutation_value( "consume_time_modifier" );
1614     }
1615 
1616     return time * consume_time_modifier;
1617 }
1618 
query_consume_ownership(item & target,player & p)1619 static bool query_consume_ownership( item &target, player &p )
1620 {
1621     if( !target.is_owned_by( p, true ) ) {
1622         bool choice = true;
1623         if( p.get_value( "THIEF_MODE" ) == "THIEF_ASK" ) {
1624             choice = Pickup::query_thief();
1625         }
1626         if( p.get_value( "THIEF_MODE" ) == "THIEF_HONEST" || !choice ) {
1627             return false;
1628         }
1629         std::vector<npc *> witnesses;
1630         for( npc &elem : g->all_npcs() ) {
1631             if( rl_dist( elem.pos(), p.pos() ) < MAX_VIEW_DISTANCE && elem.sees( p.pos() ) ) {
1632                 witnesses.push_back( &elem );
1633             }
1634         }
1635         for( npc *elem : witnesses ) {
1636             elem->say( "<witnessed_thievery>", 7 );
1637         }
1638         if( !witnesses.empty() && target.is_owned_by( p, true ) ) {
1639             if( p.add_faction_warning( target.get_owner() ) ) {
1640                 for( npc *elem : witnesses ) {
1641                     elem->make_angry();
1642                 }
1643             }
1644         }
1645     }
1646     return true;
1647 }
1648 
1649 /** Consume medication.
1650 *   @return true if item consumed.
1651 */
consume_med(item & target,player & you)1652 static bool consume_med( item &target, player &you )
1653 {
1654     if( !target.is_medication() ) {
1655         return false;
1656     }
1657 
1658     const itype_id tool_type = target.get_comestible()->tool;
1659     const itype *req_tool = item::find_type( tool_type );
1660 
1661     if( req_tool->tool ) {
1662         if( !( you.has_amount( tool_type, 1 ) &&
1663                you.has_charges( tool_type, req_tool->tool->charges_per_use ) ) ) {
1664             you.add_msg_if_player( m_info, _( "You need a %s to consume that!" ), req_tool->nname( 1 ) );
1665             return false;
1666         }
1667         you.use_charges( tool_type, req_tool->tool->charges_per_use );
1668     }
1669 
1670     int amount_used = 1;
1671     if( target.type->has_use() ) {
1672         amount_used = target.type->invoke( you, target, you.pos() ).value_or( 0 );
1673         if( amount_used <= 0 ) {
1674             return false;
1675         }
1676     }
1677 
1678     // TODO: Get the target it was used on
1679     // Otherwise injecting someone will give us addictions etc.
1680     if( target.has_flag( flag_NO_INGEST ) ) {
1681         const auto &comest = *target.get_comestible();
1682         // Assume that parenteral meds don't spoil, so don't apply rot
1683         you.modify_health( comest );
1684         you.modify_stimulation( comest );
1685         you.modify_fatigue( comest );
1686         you.modify_radiation( comest );
1687         you.modify_addiction( comest );
1688         you.modify_morale( target );
1689     } else {
1690         // Take by mouth
1691         you.consume_effects( target );
1692     }
1693 
1694     target.mod_charges( -amount_used );
1695     return true;
1696 }
1697 
cbm_is_full(const player & guy,const item & fuel)1698 static bool cbm_is_full( const player &guy, const item &fuel )
1699 {
1700     material_id fuel_mat;
1701     if( fuel.is_magazine() ) {
1702         fuel_mat = item( fuel.ammo_current() ).get_base_material().id;
1703     } else {
1704         fuel_mat = fuel.get_base_material().id;
1705     }
1706     return guy.get_fuel_capacity( fuel_mat ) > 0;
1707 }
1708 
consume(item & target,bool force)1709 trinary player::consume( item &target, bool force )
1710 {
1711     if( target.is_null() ) {
1712         add_msg_if_player( m_info, _( "You do not have that item." ) );
1713         return trinary::NONE;
1714     }
1715     if( is_underwater() && !has_trait( trait_WATERSLEEP ) ) {
1716         add_msg_if_player( m_info, _( "You can't do that while underwater." ) );
1717         return trinary::NONE;
1718     }
1719 
1720     if( target.is_craft() ) {
1721         add_msg_if_player( m_info, _( "You can't eat your %s." ), target.tname() );
1722         if( is_npc() ) {
1723             debugmsg( "%s tried to eat a %s", name, target.tname() );
1724         }
1725         return trinary::NONE;
1726     }
1727     if( is_player() && !query_consume_ownership( target, *this ) ) {
1728         return trinary::NONE;
1729     }
1730     if( consume_med( target, *this ) ||
1731         ( has_max_power() && get_power_level() < get_max_power_level() &&
1732           cbm_is_full( *this, target ) && fuel_bionic_with( target ) ) ||
1733         eat( target, *this, force ) ) {
1734 
1735         get_event_bus().send<event_type::character_consumes_item>( getID(), target.typeId() );
1736 
1737         target.on_contents_changed();
1738         return target.charges <= 0 ? trinary::ALL : trinary::SOME;
1739     }
1740 
1741     return trinary::NONE;
1742 }
1743 
consume(item_location loc,bool force)1744 trinary player::consume( item_location loc, bool force )
1745 {
1746     if( !loc ) {
1747         debugmsg( "Null loc to consume." );
1748         return trinary::NONE;
1749     }
1750     contents_change_handler handler;
1751     item &target = *loc;
1752     trinary result = consume( target, force );
1753     if( result != trinary::NONE ) {
1754         handler.unseal_pocket_containing( loc );
1755     }
1756     if( result == trinary::ALL ) {
1757         if( loc.where() == item_location::type::character ) {
1758             i_rem( loc.get_item() );
1759         } else {
1760             loc.remove_item();
1761         }
1762     }
1763     handler.handle_by( *this );
1764     return result;
1765 }
1766