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