From 63936e804567af1e71d849c92a0b1d49d6d9e7dc Mon Sep 17 00:00:00 2001 From: Davi Date: Sun, 27 Oct 2019 05:47:54 -0400 Subject: [PATCH 1/8] Remove unused methods from stomach.cpp --- src/stomach.cpp | 57 ++----------------------------------------------- src/stomach.h | 20 ----------------- 2 files changed, 2 insertions(+), 75 deletions(-) diff --git a/src/stomach.cpp b/src/stomach.cpp index da3c8598f8c81..7b26d74e53e4f 100644 --- a/src/stomach.cpp +++ b/src/stomach.cpp @@ -253,49 +253,6 @@ void stomach_contents::absorb_water( player &p, units::volume amount ) p.mod_thirst( -units::to_milliliter( amount ) / 5 ); } -void stomach_contents::absorb_kcal( int amount ) -{ - if( amount <= 0 ) { - return; - } - calories_absorbed += amount; - calories -= amount; - if( calories < 0 ) { // just a little trickery to avoid overflow - calories_absorbed += calories; - calories = 0; - } -} - -bool stomach_contents::absorb_vitamin( const vitamin_id &vit, int amount ) -{ - if( amount <= 0 ) { - return false; - } - vitamins_absorbed[vit] += amount; - vitamins[vit] -= amount; - if( vitamins[vit] < 0 ) { - vitamins_absorbed[vit] += vitamins[vit]; - vitamins[vit] = amount; - } - return true; -} - -bool stomach_contents::absorb_vitamin( const std::pair &vit ) -{ - return absorb_vitamin( vit.first, vit.second ); -} - -bool stomach_contents::absorb_vitamins( const std::map &vitamins ) -{ - bool absorbed = false; - for( const std::pair vit : vitamins ) { - if( absorb_vitamin( vit ) ) { - absorbed = true; - } - } - return absorbed; -} - stomach_pass_rates stomach_contents::get_pass_rates( bool stomach ) { stomach_pass_rates rates; @@ -378,11 +335,6 @@ void stomach_contents::mod_calories( int cal ) calories += cal; } -void stomach_contents::set_calories( int cal ) -{ - calories = cal; -} - void stomach_contents::mod_nutr( int nutr ) { // nutr is legacy type code, this function simply converts old nutrition to new kcal @@ -420,11 +372,6 @@ int stomach_contents::get_calories_absorbed() const return calories_absorbed; } -void stomach_contents::set_calories_absorbed( int cal ) -{ - calories_absorbed = cal; -} - units::volume stomach_contents::get_water() const { return water; @@ -445,7 +392,7 @@ void Character::initialize_stomach_contents() { stomach = stomach_contents( 2500_ml ); guts = stomach_contents( 24000_ml ); - guts.set_calories( 300 ); - stomach.set_calories( 800 ); + guts.mod_calories( 300 ); + stomach.mod_calories( 800 ); stomach.mod_contents( 475_ml ); } diff --git a/src/stomach.h b/src/stomach.h index 28f975400f217..c3b1e4c58c14e 100644 --- a/src/stomach.h +++ b/src/stomach.h @@ -73,13 +73,10 @@ class stomach_contents int get_calories() const; int get_calories_absorbed() const; - void set_calories_absorbed( int cal ); units::volume get_water() const; // changes calorie amount void mod_calories( int calories ); - // sets calories amount - void set_calories( int cal ); // changes calorie amount based on old nutr value void mod_nutr( int nutr ); @@ -131,21 +128,4 @@ class stomach_contents // when did this stomach_contents call stomach_contents::ingest() time_point last_ate; - // turns calories into absorbed calories. - // they are not added to your fat stores just yet - // only does anything if the input is a positive number - void absorb_kcal( int amount ); - // absorbs a single vitamin. - // does not add it to player vitamins yet - // returns true if vitamins are absorbed - bool absorb_vitamin( const vitamin_id &vit, int amount ); - // absorbs a single vitamin - // does not add it to player vitamins yet - // returns true if vitamins are absorbed - bool absorb_vitamin( const std::pair &vit ); - // absorbs multiple vitamins - // does not add it to player vitamins yet - // returns true if any vitamins are absorbed - bool absorb_vitamins( const std::map &vitamins ); - }; From aaf8a46c1474c37ca0ec961bad3a98ddbee2eb24 Mon Sep 17 00:00:00 2001 From: Davi Date: Mon, 28 Oct 2019 15:41:26 -0400 Subject: [PATCH 2/8] Combine several methods so stomach.cpp is simpler There were several issues with the previous implementation of the stomach_contents class. For instance, using the class required intricate knowledge of its implementation. Additionally, the most important and commonly performed task, digesting food every half-hour, required calling four specific methods in a specific order, when they could easily be combined into one method. Overall, the class suffered from over-complexity and I decided to fix this by making the following changes: Combined store_water, calculate_absorbed, store_absorbed, and bowel_movement into one method, "digest". Removed references to calories_absorbed and vitamins_absorbed, as these are no longer used after the above change. Combined pass_rates and absorb_rates: stomach-types never absorbed anything, and guts-types passed so slowly as to be inconsequential, so these concepts were combined into digest_rates. Whether a stomach_contents is a stomach or guts is now decided when the object is constructed; it was already that way in practice, I just made it official. --- src/character.cpp | 3 +- src/player.cpp | 28 ++-- src/stomach.cpp | 264 +++++++++---------------------- src/stomach.h | 87 +++++----- tests/stomach_contents_tests.cpp | 11 +- 5 files changed, 131 insertions(+), 262 deletions(-) diff --git a/src/character.cpp b/src/character.cpp index 2a9622fae4062..9e0032586684c 100644 --- a/src/character.cpp +++ b/src/character.cpp @@ -4494,8 +4494,7 @@ void Character::vomit() g->events().send( getID() ); if( stomach.contains() != 0_ml ) { - // empty stomach contents - stomach.bowel_movement(); + stomach.empty(); g->m.add_field( adjacent_tile(), fd_bile, 1 ); add_msg_player_or_npc( m_bad, _( "You throw up heavily!" ), _( " throws up heavily!" ) ); } diff --git a/src/player.cpp b/src/player.cpp index c25129e0e3677..4b18769a82191 100644 --- a/src/player.cpp +++ b/src/player.cpp @@ -3232,19 +3232,21 @@ void player::update_stomach( const time_point &from, const time_point &to ) const bool mycus = has_trait( trait_M_DEPENDENT ); const float kcal_per_time = get_bmr() / ( 12.0f * 24.0f ); const int five_mins = ticks_between( from, to, 5_minutes ); + const int half_hours = ticks_between( from, to, 30_minutes ); if( five_mins > 0 ) { - stomach.absorb_water( *this, 250_ml * five_mins ); - guts.absorb_water( *this, 250_ml * five_mins ); - } - if( ticks_between( from, to, 30_minutes ) > 0 ) { - // the stomach does not currently have rates of absorption, but this is where it goes - stomach.calculate_absorbed( stomach.get_absorb_rates( true, rates ) ); - guts.calculate_absorbed( guts.get_absorb_rates( false, rates ) ); - stomach.store_absorbed( *this ); - guts.store_absorbed( *this ); - guts.bowel_movement( guts.get_pass_rates( false ) ); - stomach.bowel_movement( stomach.get_pass_rates( true ), guts ); + // Digest nutrients in stomach, they are destined for the guts (except water) + nutrients digested_to_guts = stomach.digest( *this, five_mins, half_hours ); + // Digest nutrients in guts, they will be distributed to needs levels + nutrients digested_to_body = guts.digest( *this, five_mins, half_hours ); + // Water from stomach skips guts and gets absorbed by body + set_thirst( std::max( + -100, get_thirst() - units::to_milliliter( digested_to_guts.water ) / 5 ) ); + guts.ingest( digested_to_guts ); + if( !is_npc() || !get_option( "NO_NPC_FOOD" ) ) { + mod_stored_kcal( digested_to_body.kcal ); + vitamins_mod( digested_to_body.vitamins, false ); + } } if( stomach.time_since_ate() > 10_minutes ) { if( stomach.contains() >= stomach.capacity() && get_hunger() > -61 ) { @@ -3257,12 +3259,10 @@ void player::update_stomach( const time_point &from, const time_point &to ) // that's really all the food you need to feel full set_hunger( -1 ); } else if( stomach.contains() == 0_ml ) { - if( guts.get_calories() == 0 && guts.get_calories_absorbed() == 0 && - get_stored_kcal() < get_healthy_kcal() && get_hunger() < 300 ) { + if( guts.get_calories() == 0 && get_stored_kcal() < get_healthy_kcal() && get_hunger() < 300 ) { // there's no food except what you have stored in fat set_hunger( 300 ); } else if( get_hunger() < 100 && ( ( guts.get_calories() == 0 && - guts.get_calories_absorbed() == 0 && get_stored_kcal() >= get_healthy_kcal() ) || get_stored_kcal() < get_healthy_kcal() ) ) { set_hunger( 100 ); } else if( get_hunger() < 0 ) { diff --git a/src/stomach.cpp b/src/stomach.cpp index 7b26d74e53e4f..5c0784211ca66 100644 --- a/src/stomach.cpp +++ b/src/stomach.cpp @@ -21,9 +21,10 @@ stomach_contents::stomach_contents() = default; -stomach_contents::stomach_contents( units::volume max_vol ) +stomach_contents::stomach_contents( units::volume max_vol, bool is_stomach ) { max_volume = max_vol; + stomach = is_stomach; last_ate = calendar::before_time_starts; } @@ -36,7 +37,6 @@ void stomach_contents::serialize( JsonOut &json ) const { json.start_object(); json.member( "vitamins", vitamins ); - json.member( "vitamins_absorbed", vitamins_absorbed ); json.member( "calories", calories ); json.member( "water", ml_to_string( water ) ); json.member( "max_volume", ml_to_string( max_volume ) ); @@ -54,7 +54,6 @@ void stomach_contents::deserialize( JsonIn &json ) { JsonObject jo = json.get_object(); jo.read( "vitamins", vitamins ); - jo.read( "vitamins_absorbed", vitamins_absorbed ); jo.read( "calories", calories ); std::string str; jo.read( "water", str ); @@ -94,117 +93,6 @@ units::volume stomach_contents::contains() const return contents + water; } -bool stomach_contents::store_absorbed( player &p ) -{ - if( p.is_npc() && get_option( "NO_NPC_FOOD" ) ) { - return false; - } - bool absorbed = false; - if( calories_absorbed != 0 ) { - p.mod_stored_kcal( calories_absorbed ); - absorbed = true; - } - p.vitamins_mod( vitamins_absorbed, false ); - vitamins_absorbed.clear(); - return absorbed; -} - -void stomach_contents::bowel_movement( const stomach_pass_rates &rates ) -{ - int cal_rate = static_cast( round( calories * rates.percent_kcal ) ); - cal_rate = clamp( cal_rate, std::min( rates.min_kcal, calories - calories_absorbed ), - calories - calories_absorbed ); - if( cal_rate < 0 ) { - cal_rate = 0; - } - mod_calories( -cal_rate ); - calories_absorbed = 0; - units::volume contents_rate = clamp( units::from_milliliter( round( - units::to_milliliter - ( contents ) * rates.percent_vol ) ), rates.min_vol, contents ); - mod_contents( -contents_rate ); - // water is a special case. - water = 0_ml; - - for( auto &vit : vitamins ) { - const int vit_absorbed = vitamins_absorbed[vit.first]; - // need to take absorbed vitamins into account for % moved - int percent = static_cast( ( vit.second + vit_absorbed ) * rates.percent_vit ); - // minimum vitamins that will move - int min = rates.min_vit; - int calc = std::max( percent, min ); - - if( vit_absorbed >= calc ) { - // vitamins are absorbed instead of moved - // assumed absorption is called from another function - calc = 0; - } else if( vit_absorbed != 0 ) { - calc -= vit_absorbed; - } - - if( calc > vitamins[vit.first] ) { - calc = vitamins[vit.first]; - } - - vitamins[vit.first] -= calc; - } -} - -void stomach_contents::bowel_movement() -{ - stomach_pass_rates rates; - - /** - * The min numbers aren't technically needed since the percentage will - * take care of the entirety, but some values needed to be assigned - */ - rates.percent_kcal = 1; - rates.min_kcal = 250; - rates.percent_vol = 1; - rates.min_vol = 25000_ml; - rates.percent_vit = 1; - rates.min_vit = 100; - - bowel_movement( rates ); -} - -void stomach_contents::bowel_movement( const stomach_pass_rates &rates, stomach_contents &move_to ) -{ - int cal_rate = static_cast( round( calories * rates.percent_kcal ) ); - cal_rate = clamp( cal_rate, std::min( rates.min_kcal, calories - calories_absorbed ), - calories - calories_absorbed ); - move_to.mod_calories( cal_rate ); - units::volume contents_rate = clamp( units::from_milliliter( round( - units::to_milliliter - ( contents ) * rates.percent_vol ) ), rates.min_vol, contents ); - move_to.mod_contents( -contents_rate ); - move_to.water += water; - - for( auto &vit : vitamins ) { - const int vit_absorbed = vitamins_absorbed[vit.first]; - // need to take absorbed vitamins into account for % moved - int percent = static_cast( ( vit.second + vit_absorbed ) * rates.percent_vit ); - // minimum vitamins that will move - int min = rates.min_vit; - int calc = std::max( percent, min ); - - if( vit_absorbed >= calc ) { - // vitamins are absorbed instead of moved - // assumed absorption is called from another function - calc = 0; - } else if( vit_absorbed != 0 ) { - calc -= vit_absorbed; - } - - if( calc > vitamins[vit.first] ) { - calc = vitamins[vit.first]; - } - - move_to.vitamins[vit.first] += calc; - } - bowel_movement( rates ); -} - void stomach_contents::ingest( player &p, item &food, int charges = 1 ) { item comest; @@ -237,95 +125,90 @@ void stomach_contents::ingest( player &p, item &food, int charges = 1 ) mod_calories( p.kcal_for( comest ) ); } -void stomach_contents::absorb_water( player &p, units::volume amount ) +void stomach_contents::ingest( nutrients ingested ) { - if( water < amount ) { - amount = water; - water = 0_ml; - } else { - water -= amount; + contents += ingested.solids; + water += ingested.water; + calories += ingested.kcal; + for( auto &vit : ingested.vitamins ) { + vitamins[vit.first] += vit.second; } - if( p.get_thirst() < -100 ) { - return; - } else if( p.get_thirst() - units::to_milliliter( amount / 5 ) < -100 ) { - p.set_thirst( -100 ); +} + +nutrients stomach_contents::digest( player &owner, int five_mins, int half_hours ) +{ + nutrients digested; + stomach_digest_rates rates = get_digest_rates( owner ); + + // Digest water, but no more than in stomach. + digested.water = std::min( water, rates.water * five_mins ); + water -= digested.water; + + // If no half-hour intervals have passed, we only process water, so bail out early. + if( half_hours == 0 ) { + // We need to initialize these to zero first, though. + digested.kcal = 0; + digested.solids = 0_ml; + for( const auto &vit : digested.vitamins ) { + digested.vitamins[vit.first] = 0; + } + return digested; + } + + // Digest solids, but no more than in stomach. + digested.solids = std::min( contents, rates.solids * half_hours ); + contents -= digested.solids; + + // Digest kCal -- use min_kcal by default, but no more than what's in stomach, + // and no less than percentage_kcal of what's in stomach. + digested.kcal = half_hours * clamp( rates.min_kcal, + static_cast( round( calories * rates.percent_kcal ) ), calories ); + calories -= digested.kcal; + + // Digest vitamins just like we did kCal, but we need to do one at a time. + for( const auto &vit : vitamins ) { + digested.vitamins[vit.first] = half_hours * clamp( rates.min_vitamin, + static_cast( round( vit.second * rates.percent_vitamin ) ), vit.second ); + vitamins[vit.first] -= digested.vitamins[vit.first]; } - p.mod_thirst( -units::to_milliliter( amount ) / 5 ); + + return digested; } -stomach_pass_rates stomach_contents::get_pass_rates( bool stomach ) +void stomach_contents::empty() { - stomach_pass_rates rates; - // a human will digest food in about 53 hours - // most of the excrement is completely indigestible - // ie almost all of the calories ingested are absorbed. - // 3 hours will be accounted here as stomach - // the rest will be guts + calories = 0; + water = 0_ml; + contents = 0_ml; + vitamins.clear(); +} + +stomach_digest_rates stomach_contents::get_digest_rates( player &owner ) +{ + stomach_digest_rates rates; if( stomach ) { - rates.min_vol = capacity() / 6; + // The stomach is focused on passing material on to the guts. // 3 hours to empty in 30 minute increments - rates.percent_vol = 1.0f / 6.0f; - rates.min_vit = 1; - rates.percent_vit = 1.0f / 6.0f; + rates.solids = capacity() / 6; + rates.water = 250_ml; // Water is special, passes very quickly, in 5 minute intervals + rates.min_vitamin = 1; + rates.percent_vitamin = 1.0f / 6.0f; rates.min_kcal = 5; rates.percent_kcal = 1.0f / 6.0f; } else { - rates.min_vol = std::max( capacity() / 100, 1_ml ); - // 50 hours to empty in 30 minute increments - rates.percent_vol = 0.01f; - rates.min_vit = 1; - rates.percent_vit = 0.01f; - rates.min_kcal = 5; - rates.percent_kcal = 0.01f; - } - return rates; -} - -stomach_absorb_rates stomach_contents::get_absorb_rates( bool stomach, - const needs_rates &metabolic_rates ) -{ - stomach_absorb_rates rates; - if( !stomach ) { + const needs_rates metabolic_rates = owner.calc_needs_rates(); + // The guts are focused on absorption into the body, we don't care about passing rates. + // Solids rate doesn't do anything impactful here so just make it big enough to avoid overflow. + rates.solids = 250_ml; + rates.water = 250_ml; rates.min_kcal = roll_remainder( metabolic_rates.kcal / 24.0 * metabolic_rates.hunger ); rates.percent_kcal = 0.05f * metabolic_rates.hunger; - rates.min_vitamin_default = round( 100.0 / 24.0 * metabolic_rates.hunger ); - rates.percent_vitamin_default = 0.05f * metabolic_rates.hunger; - } else { - rates.min_kcal = 0; - rates.percent_kcal = 0.0f; - rates.min_vitamin_default = 0; - rates.percent_vitamin_default = 0.0f; + rates.min_vitamin = round( 100.0 / 24.0 * metabolic_rates.hunger ); + rates.percent_vitamin = 0.05f * metabolic_rates.hunger; } return rates; } -void stomach_contents::calculate_absorbed( stomach_absorb_rates rates ) -{ - for( const auto vit : vitamins ) { - int min_v; - float percent_v; - if( rates.min_vitamin.find( vit.first ) != rates.min_vitamin.end() ) { - min_v = rates.min_vitamin[vit.first]; - } else { - min_v = rates.min_vitamin_default; - } - if( rates.percent_vitamin.find( vit.first ) != rates.percent_vitamin.end() ) { - percent_v = rates.percent_vitamin[vit.first]; - } else { - percent_v = rates.percent_vitamin_default; - } - int vit_absorbed = std::max( min_v, static_cast( percent_v * vit.second ) ); - // make sure the vitamins don't overflow - vit_absorbed = std::min( vit_absorbed, vit.second ); - vitamins_absorbed[vit.first] += vit_absorbed; - vitamins[vit.first] -= vit_absorbed; - } - int cal = clamp( static_cast( round( calories * rates.percent_kcal ) ), - std::min( rates.min_kcal, calories ), calories ); - calories_absorbed += cal; - calories -= cal; -} - void stomach_contents::mod_calories( int cal ) { if( -cal >= calories ) { @@ -367,11 +250,6 @@ int stomach_contents::get_calories() const return calories; } -int stomach_contents::get_calories_absorbed() const -{ - return calories_absorbed; -} - units::volume stomach_contents::get_water() const { return water; @@ -390,8 +268,8 @@ time_duration stomach_contents::time_since_ate() const // sets default stomach contents when starting the game void Character::initialize_stomach_contents() { - stomach = stomach_contents( 2500_ml ); - guts = stomach_contents( 24000_ml ); + stomach = stomach_contents( 2500_ml, true ); + guts = stomach_contents( 24000_ml, false ); guts.mod_calories( 300 ); stomach.mod_calories( 800 ); stomach.mod_contents( 475_ml ); diff --git a/src/stomach.h b/src/stomach.h index c3b1e4c58c14e..3747c813fd6ed 100644 --- a/src/stomach.h +++ b/src/stomach.h @@ -13,26 +13,23 @@ class JsonIn; class JsonOut; class item; -// how much the stomach_contents passes -// based on 30 minute increments -struct stomach_pass_rates { - units::volume min_vol; - float percent_vol; - int min_kcal; - float percent_kcal; - int min_vit; - float percent_vit; +// Contains all information that can pass out of (or into) a stomach +struct nutrients { + units::volume water; + units::volume solids; + int kcal; + std::map vitamins; }; -// how much a stomach_contents can absorb +// how much a stomach_contents can digest // based on 30 minute increments -struct stomach_absorb_rates { +struct stomach_digest_rates { + units::volume solids; + units::volume water; float percent_kcal; int min_kcal; - std::map percent_vitamin; - std::map min_vitamin; - float percent_vitamin_default; - int min_vitamin_default; + float percent_vitamin; + int min_vitamin; }; // an abstract of food that has been eaten. @@ -40,21 +37,34 @@ class stomach_contents { public: stomach_contents(); - stomach_contents( units::volume max_volume ); - - // empties the stomach_contents of all of its contents - void bowel_movement(); - // empties contents equal to amount as ratio - // amount ranges from 0 to 1 - void bowel_movement( const stomach_pass_rates &rates ); - // moves @rates contents to other stomach_contents - // amount ranges from 0 to 1 - void bowel_movement( const stomach_pass_rates &rates, stomach_contents &move_to ); + /** + * @brief Construct a new stomach contents object + * Stomachs always process their food in a few hours. Guts take significantly longer, + * and their rate is dependent on the metabolic rate of their owner. + * @param max_volume Base capacity, subject to modification, i.e. by traits + * @param is_stomach If true, treated as stomach, if false, treated as guts + */ + stomach_contents( units::volume max_volume, bool is_stomach ); // turns an item into stomach contents // will still add contents if past maximum volume. void ingest( player &p, item &food, int charges ); + // Directly adds nutrients to stomach contents + void ingest( nutrients ingested ); + + /** + * @brief Processes food and outputs nutrients that are finished processing + * @param owner The owner of this stomach + * @param five_mins Five-minute intervals passed since this method was last called + * @param half_hours Half-hour intervals passed since this method was last called + * @return nutrients that are done processing in this stomach + */ + nutrients digest( player &owner, int five_mins, int half_hours ); + + // Empties the stomach of all contents. + void empty(); + // calculates max volume for a stomach_contents units::volume capacity() const; // how much stomach capacity you have left before you puke from stuffing your gob @@ -62,17 +72,7 @@ class stomach_contents // how much volume is in the stomach_contents units::volume contains() const; - // calculates and sets absorbed kcal and vitamins - void calculate_absorbed( stomach_absorb_rates rates ); - - // gets the rates of passing contents out of stomach_contents if true and guts if false - stomach_pass_rates get_pass_rates( bool stomach ); - // gets the absorption rates for kcal and vitamins - // stomach == true, guts == false - stomach_absorb_rates get_absorb_rates( bool stomach, const needs_rates &metabolic_rates ); - int get_calories() const; - int get_calories_absorbed() const; units::volume get_water() const; // changes calorie amount @@ -89,13 +89,6 @@ class stomach_contents // adds volume to your stomach void mod_contents( units::volume vol ); - void absorb_water( player &p, units::volume amount ); - - // moves absorbed nutrients to the player for use - // returns true if any calories are absorbed - // does not empty absorbed calories - bool store_absorbed( player &p ); - // how long has it been since i ate? // only really relevant for player::stomach time_duration time_since_ate() const; @@ -107,14 +100,13 @@ class stomach_contents private: + // If true, this object represents a stomach; if false, this object represents guts. + bool stomach; + // vitamins in stomach_contents std::map vitamins; - // vitamins to be absorbed in stomach_contents - std::map vitamins_absorbed; // number of calories in stomach_contents int calories = 0; - // number of calories to be absorbed in stomach_contents - int calories_absorbed = 0; // volume of water in stomach_contents units::volume water; /** @@ -128,4 +120,7 @@ class stomach_contents // when did this stomach_contents call stomach_contents::ingest() time_point last_ate; + // Gets the rates at which this stomach will digest things. + stomach_digest_rates get_digest_rates( player &owner ); + }; diff --git a/tests/stomach_contents_tests.cpp b/tests/stomach_contents_tests.cpp index e53662e01e427..6226294da01c7 100644 --- a/tests/stomach_contents_tests.cpp +++ b/tests/stomach_contents_tests.cpp @@ -31,10 +31,8 @@ static void pass_time( player &p, time_duration amt ) static void clear_stomach( player &p ) { - p.guts.set_calories( 0 ); - p.stomach.set_calories( 0 ); - p.stomach.bowel_movement(); - p.guts.bowel_movement(); + p.stomach.empty(); + p.guts.empty(); } static void set_all_vitamins( int target, player &p ) @@ -63,9 +61,8 @@ static void print_stomach_contents( player &p, const bool print ) if( !print ) { return; } - printf( "stomach: %d/%d guts: %d/%d player: %d/%d hunger: %d\n", p.stomach.get_calories(), - p.stomach.get_calories_absorbed(), p.guts.get_calories(), - p.guts.get_calories_absorbed(), p.get_stored_kcal(), p.get_healthy_kcal(), p.get_hunger() ); + printf( "stomach: %d guts: %d player: %d/%d hunger: %d\n", p.stomach.get_calories(), + p.guts.get_calories(), p.get_stored_kcal(), p.get_healthy_kcal(), p.get_hunger() ); printf( "stomach: %d mL/ %d mL guts %d mL/ %d mL\n", units::to_milliliter( p.stomach.contains() ), units::to_milliliter( p.stomach.capacity() ), From b006b3b3c6c1e93ec00734715f18e0807e0a9b88 Mon Sep 17 00:00:00 2001 From: Davi Date: Tue, 29 Oct 2019 14:59:50 -0400 Subject: [PATCH 3/8] Make all_nutrition_starve_test far more strict Now that vitamins aren't disappearing, this test can be tuned to make sure that vitamins are being absorbed into the body correctly. --- data/json/items/comestibles/meat_dishes.json | 10 ++++++++++ tests/stomach_contents_tests.cpp | 21 +++----------------- 2 files changed, 13 insertions(+), 18 deletions(-) diff --git a/data/json/items/comestibles/meat_dishes.json b/data/json/items/comestibles/meat_dishes.json index 22f598469cc1e..7ac81106e63bd 100644 --- a/data/json/items/comestibles/meat_dishes.json +++ b/data/json/items/comestibles/meat_dishes.json @@ -400,6 +400,16 @@ "fun": -8, "vitamins": [ [ "iron", 3 ] ] }, + { + "type": "COMESTIBLE", + "id": "debug_nutrition", + "copy-from": "can_spam", + "name": "holy SPAM of debugging", + "calories": 2100, + "description": "A mysterious lump of SPAM that contains just enough calories and vitamins to feed you for a day. For debug use only.", + "//": "This is used for the all_nutrient_starve_test.", + "vitamins": [ [ "vitA", 96 ], [ "vitB", 96 ], [ "vitC", 96 ], [ "calcium", 96 ], [ "iron", 96 ] ] + }, { "type": "COMESTIBLE", "id": "can_sardine", diff --git a/tests/stomach_contents_tests.cpp b/tests/stomach_contents_tests.cpp index 6226294da01c7..1994b39b68e10 100644 --- a/tests/stomach_contents_tests.cpp +++ b/tests/stomach_contents_tests.cpp @@ -75,24 +75,9 @@ static void print_stomach_contents( player &p, const bool print ) // accounting for appropriate vitamins static void eat_all_nutrients( player &p ) { - // Absorption rates are imperfect for now, so target is 140% DV (== 135 vitamin units if rate is 15m) - item f( "fried_brain" ); - p.eat( f ); - f = item( "carrot" ); - p.eat( f ); - f = item( "carrot" ); - p.eat( f ); - f = item( "hotdogs_cooked" ); - p.eat( f ); - f = item( "veggy" ); - p.eat( f ); - f = item( "can_herring" ); - p.eat( f ); - f = item( "can_herring" ); - p.eat( f ); - f = item( "can_herring" ); - p.eat( f ); - f = item( "junk_burrito" ); + // Vitamin target: 100% DV -- or 96 vitamin "units" since all vitamins currently decay every 15m. + // Energy target: 2100 kcal -- debug target will be completely sedentary. + item f( "debug_nutrition" ); p.eat( f ); } From 7fe926024a2155eb7982140b64c4e8a7fe9ef2ae Mon Sep 17 00:00:00 2001 From: Davi Date: Wed, 30 Oct 2019 07:30:11 -0400 Subject: [PATCH 4/8] Implement suggestions from KorGgenT - Replace auto with type specification. - Add const where applicable - Replaces 'player' type parameters with 'needs_rates' Co-Authored-By: Curtis Merrill --- src/player.cpp | 8 ++++---- src/player.h | 2 +- src/stomach.cpp | 16 ++++++++-------- src/stomach.h | 10 ++++++---- 4 files changed, 19 insertions(+), 17 deletions(-) diff --git a/src/player.cpp b/src/player.cpp index 4b18769a82191..8f2fa7f823f6c 100644 --- a/src/player.cpp +++ b/src/player.cpp @@ -3236,9 +3236,9 @@ void player::update_stomach( const time_point &from, const time_point &to ) if( five_mins > 0 ) { // Digest nutrients in stomach, they are destined for the guts (except water) - nutrients digested_to_guts = stomach.digest( *this, five_mins, half_hours ); + nutrients digested_to_guts = stomach.digest( rates, five_mins, half_hours ); // Digest nutrients in guts, they will be distributed to needs levels - nutrients digested_to_body = guts.digest( *this, five_mins, half_hours ); + nutrients digested_to_body = guts.digest( rates, five_mins, half_hours ); // Water from stomach skips guts and gets absorbed by body set_thirst( std::max( -100, get_thirst() - units::to_milliliter( digested_to_guts.water ) / 5 ) ); @@ -3539,9 +3539,9 @@ void player::check_needs_extremes() } -needs_rates player::calc_needs_rates() +needs_rates player::calc_needs_rates() const { - effect &sleep = get_effect( effect_sleep ); + const effect &sleep = get_effect( effect_sleep ); const bool has_recycler = has_bionic( bio_recycler ); const bool asleep = !sleep.is_null(); diff --git a/src/player.h b/src/player.h index 527319acc269e..819ece2122108 100644 --- a/src/player.h +++ b/src/player.h @@ -243,7 +243,7 @@ class player : public Character void update_stomach( const time_point &from, const time_point &to ); /** Increases hunger, thirst, fatigue and stimulants wearing off. `rate_multiplier` is for retroactive updates. */ void update_needs( int rate_multiplier ); - needs_rates calc_needs_rates(); + needs_rates calc_needs_rates() const; /** * Handles passive regeneration of pain and maybe hp. diff --git a/src/stomach.cpp b/src/stomach.cpp index 5c0784211ca66..09a48dce74316 100644 --- a/src/stomach.cpp +++ b/src/stomach.cpp @@ -125,20 +125,21 @@ void stomach_contents::ingest( player &p, item &food, int charges = 1 ) mod_calories( p.kcal_for( comest ) ); } -void stomach_contents::ingest( nutrients ingested ) +void stomach_contents::ingest( const nutrients &ingested ) { contents += ingested.solids; water += ingested.water; calories += ingested.kcal; - for( auto &vit : ingested.vitamins ) { + for( const std::pair &vit : ingested.vitamins ) { vitamins[vit.first] += vit.second; } } -nutrients stomach_contents::digest( player &owner, int five_mins, int half_hours ) +nutrients stomach_contents::digest( const needs_rates &metabolic_rates, + int five_mins, int half_hours ) { nutrients digested; - stomach_digest_rates rates = get_digest_rates( owner ); + stomach_digest_rates rates = get_digest_rates( metabolic_rates ); // Digest water, but no more than in stomach. digested.water = std::min( water, rates.water * five_mins ); @@ -149,7 +150,7 @@ nutrients stomach_contents::digest( player &owner, int five_mins, int half_hours // We need to initialize these to zero first, though. digested.kcal = 0; digested.solids = 0_ml; - for( const auto &vit : digested.vitamins ) { + for( const std::pair &vit : digested.vitamins ) { digested.vitamins[vit.first] = 0; } return digested; @@ -166,7 +167,7 @@ nutrients stomach_contents::digest( player &owner, int five_mins, int half_hours calories -= digested.kcal; // Digest vitamins just like we did kCal, but we need to do one at a time. - for( const auto &vit : vitamins ) { + for( const std::pair &vit : vitamins ) { digested.vitamins[vit.first] = half_hours * clamp( rates.min_vitamin, static_cast( round( vit.second * rates.percent_vitamin ) ), vit.second ); vitamins[vit.first] -= digested.vitamins[vit.first]; @@ -183,7 +184,7 @@ void stomach_contents::empty() vitamins.clear(); } -stomach_digest_rates stomach_contents::get_digest_rates( player &owner ) +stomach_digest_rates stomach_contents::get_digest_rates( const needs_rates &metabolic_rates ) { stomach_digest_rates rates; if( stomach ) { @@ -196,7 +197,6 @@ stomach_digest_rates stomach_contents::get_digest_rates( player &owner ) rates.min_kcal = 5; rates.percent_kcal = 1.0f / 6.0f; } else { - const needs_rates metabolic_rates = owner.calc_needs_rates(); // The guts are focused on absorption into the body, we don't care about passing rates. // Solids rate doesn't do anything impactful here so just make it big enough to avoid overflow. rates.solids = 250_ml; diff --git a/src/stomach.h b/src/stomach.h index 3747c813fd6ed..dd93e84a36284 100644 --- a/src/stomach.h +++ b/src/stomach.h @@ -51,16 +51,18 @@ class stomach_contents void ingest( player &p, item &food, int charges ); // Directly adds nutrients to stomach contents - void ingest( nutrients ingested ); + void ingest( const nutrients &ingested ); /** * @brief Processes food and outputs nutrients that are finished processing - * @param owner The owner of this stomach + * Metabolic rates are required because they determine the rate of absorption of + * nutrients into the body. + * @param metabolic_rates The metabolic rates of the owner of this stomach * @param five_mins Five-minute intervals passed since this method was last called * @param half_hours Half-hour intervals passed since this method was last called * @return nutrients that are done processing in this stomach */ - nutrients digest( player &owner, int five_mins, int half_hours ); + nutrients digest( const needs_rates &metabolic_rates, int five_mins, int half_hours ); // Empties the stomach of all contents. void empty(); @@ -121,6 +123,6 @@ class stomach_contents time_point last_ate; // Gets the rates at which this stomach will digest things. - stomach_digest_rates get_digest_rates( player &owner ); + stomach_digest_rates get_digest_rates( const needs_rates &metabolic_rates ); }; From e6cfee19bac41e47de989f1ac1ae4327ec1a9a52 Mon Sep 17 00:00:00 2001 From: Davi Date: Wed, 30 Oct 2019 08:45:16 -0400 Subject: [PATCH 5/8] Remove unused #includes --- src/stomach.cpp | 10 ---------- src/stomach.h | 3 --- 2 files changed, 13 deletions(-) diff --git a/src/stomach.cpp b/src/stomach.cpp index 09a48dce74316..f054da3a7dbc7 100644 --- a/src/stomach.cpp +++ b/src/stomach.cpp @@ -1,23 +1,13 @@ -#include -#include #include -#include #include "avatar.h" -#include "calendar.h" #include "cata_utility.h" #include "json.h" #include "player.h" #include "stomach.h" #include "units.h" -#include "compatibility.h" #include "game.h" -#include "item.h" #include "itype.h" -#include "optional.h" -#include "rng.h" -#include "character.h" -#include "options.h" stomach_contents::stomach_contents() = default; diff --git a/src/stomach.h b/src/stomach.h index dd93e84a36284..f09e0da044bba 100644 --- a/src/stomach.h +++ b/src/stomach.h @@ -1,11 +1,8 @@ #pragma once #include -#include #include "units.h" -#include "calendar.h" -#include "type_id.h" struct needs_rates; class player; From 4b25ba7d4ad16597990b833ee8ca0f7f144de9e1 Mon Sep 17 00:00:00 2001 From: Davi Date: Thu, 31 Oct 2019 07:47:09 -0400 Subject: [PATCH 6/8] Refactor ingest function The previous ingest function in stomach.cpp used the 'player' object, which we are moving away from. Since the only call of that function was in the 'player' scope, it was trivial to move that code there and call the new, more generalized ingest function instead. --- src/consumption.cpp | 15 ++++++++++++++- src/player.cpp | 1 + src/stomach.cpp | 34 +--------------------------------- src/stomach.h | 13 +++++++------ 4 files changed, 23 insertions(+), 40 deletions(-) diff --git a/src/consumption.cpp b/src/consumption.cpp index bec14267c885b..f87233b6abaf7 100644 --- a/src/consumption.cpp +++ b/src/consumption.cpp @@ -1139,8 +1139,21 @@ bool player::consume_effects( item &food ) // Note: We want this here to prevent "you can't finish this" messages set_hunger( capacity ); } + + // Set up food for ingestion + nutrients ingested; + const item &contained_food = food.is_container() ? food.get_contained() : food; + // maybe move tapeworm to digestion + for( const std::pair &v : vitamins_from( contained_food ) ) { + ingested.vitamins[v.first] += has_effect( efftype_id( "tapeworm" ) ) ? v.second / 2 : v.second; + } + // @TODO: Move quench values to mL and remove the magic number here + ingested.water = contained_food.type->comestible->quench * 5_ml; + ingested.solids = contained_food.base_volume() - std::max(ingested.water, 0_ml); + ingested.kcal = kcal_for( contained_food ); + // GET IN MAH BELLY! - stomach.ingest( *this, food, 1 ); + stomach.ingest( ingested ); return true; } diff --git a/src/player.cpp b/src/player.cpp index 8f2fa7f823f6c..e763813f91f94 100644 --- a/src/player.cpp +++ b/src/player.cpp @@ -3243,6 +3243,7 @@ void player::update_stomach( const time_point &from, const time_point &to ) set_thirst( std::max( -100, get_thirst() - units::to_milliliter( digested_to_guts.water ) / 5 ) ); guts.ingest( digested_to_guts ); + // Apply nutrients, unless this is an NPC and NO_NPC_FOOD is enabled. if( !is_npc() || !get_option( "NO_NPC_FOOD" ) ) { mod_stored_kcal( digested_to_body.kcal ); vitamins_mod( digested_to_body.vitamins, false ); diff --git a/src/stomach.cpp b/src/stomach.cpp index f054da3a7dbc7..806ada2cc29cf 100644 --- a/src/stomach.cpp +++ b/src/stomach.cpp @@ -3,7 +3,6 @@ #include "avatar.h" #include "cata_utility.h" #include "json.h" -#include "player.h" #include "stomach.h" #include "units.h" #include "game.h" @@ -83,38 +82,6 @@ units::volume stomach_contents::contains() const return contents + water; } -void stomach_contents::ingest( player &p, item &food, int charges = 1 ) -{ - item comest; - if( food.is_container() ) { - comest = food.get_contained(); - } else { - comest = food; - } - if( charges == 0 ) { - // if charges is 0, it means the item is not count_by_charges - charges = 1; - } - // maybe move tapeworm to digestion - for( const auto &v : p.vitamins_from( comest ) ) { - vitamins[v.first] += p.has_effect( efftype_id( "tapeworm" ) ) ? v.second / 2 : v.second; - } - auto &comest_t = comest.type->comestible; - const units::volume add_water = std::max( 0_ml, comest_t->quench * 5_ml ); - // if this number is negative, we decrease the quench/increase the thirst of the player - if( comest_t->quench < 0 ) { - p.mod_thirst( -comest_t->quench ); - } else { - mod_quench( comest_t->quench ); - } - // @TODO: Move quench values to mL and remove the magic number here - mod_contents( comest.base_volume() * charges - add_water ); - - ate(); - - mod_calories( p.kcal_for( comest ) ); -} - void stomach_contents::ingest( const nutrients &ingested ) { contents += ingested.solids; @@ -123,6 +90,7 @@ void stomach_contents::ingest( const nutrients &ingested ) for( const std::pair &vit : ingested.vitamins ) { vitamins[vit.first] += vit.second; } + ate(); } nutrients stomach_contents::digest( const needs_rates &metabolic_rates, diff --git a/src/stomach.h b/src/stomach.h index f09e0da044bba..8001c6628e27c 100644 --- a/src/stomach.h +++ b/src/stomach.h @@ -5,7 +5,6 @@ #include "units.h" struct needs_rates; -class player; class JsonIn; class JsonOut; class item; @@ -43,17 +42,19 @@ class stomach_contents */ stomach_contents( units::volume max_volume, bool is_stomach ); - // turns an item into stomach contents - // will still add contents if past maximum volume. - void ingest( player &p, item &food, int charges ); - - // Directly adds nutrients to stomach contents + /** + * @brief Directly adds nutrients to stomach contents. + * Will still add contents if past maximum volume. Also updates last_ate to current turn. + * @param ingested The nutrients to be ingested + */ void ingest( const nutrients &ingested ); /** * @brief Processes food and outputs nutrients that are finished processing * Metabolic rates are required because they determine the rate of absorption of * nutrients into the body. + * All returned values are >= 0, with the exception of water, which + * can be negative in some circumstances (i.e. after eating dry/salty food). * @param metabolic_rates The metabolic rates of the owner of this stomach * @param five_mins Five-minute intervals passed since this method was last called * @param half_hours Half-hour intervals passed since this method was last called From 2a3c79e824f4ad87c141acc9d10927ee4fbdfa44 Mon Sep 17 00:00:00 2001 From: Davi Date: Thu, 31 Oct 2019 14:39:26 -0400 Subject: [PATCH 7/8] Fix bug with stomach capacity calculation Stomach capacity was always based on the player's mutations, no matter who owned said stomach. Now the capacity function takes a Character reference as a parameter, and uses that Character's mutations instead. --- src/consumption.cpp | 10 +++++----- src/debug_menu.cpp | 4 ++-- src/iuse.cpp | 2 +- src/player.cpp | 23 ++++++++++++----------- src/stomach.cpp | 23 ++++++++++++----------- src/stomach.h | 21 ++++++++++++++++----- tests/stomach_contents_tests.cpp | 4 ++-- 7 files changed, 50 insertions(+), 37 deletions(-) diff --git a/src/consumption.cpp b/src/consumption.cpp index f87233b6abaf7..1c4f52718dee3 100644 --- a/src/consumption.cpp +++ b/src/consumption.cpp @@ -594,7 +594,7 @@ ret_val player::will_eat( const item &food, bool interactive ) co add_consequence( _( "Your stomach won't be happy (not rotten enough)." ), ALLERGY_WEAK ); } - if( stomach.stomach_remaining() < food.volume() / food.charges && !food.has_infinite_charges() ) { + if( stomach.stomach_remaining( *this ) < food.volume() / food.charges && !food.has_infinite_charges() ) { if( edible ) { add_consequence( _( "You're full already and will be forcing yourself to eat." ), TOO_FULL ); } else { @@ -675,10 +675,10 @@ bool player::eat( item &food, bool force ) _( "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." ) ); } - const bool will_vomit = stomach.stomach_remaining() < food.volume() && - rng( units::to_milliliter( stomach.capacity() ) / 2, + const bool will_vomit = stomach.stomach_remaining( *this ) < food.volume() && + rng( units::to_milliliter( stomach.capacity( *this ) ) / 2, units::to_milliliter( stomach.contains() ) ) > units::to_milliliter( - stomach.capacity() ); + stomach.capacity( *this ) ); const bool saprophage = has_trait( trait_id( "SAPROPHAGE" ) ); if( spoiled && !saprophage ) { add_msg_if_player( m_bad, _( "Ick, this %s doesn't taste so good…" ), food.tname() ); @@ -1149,7 +1149,7 @@ bool player::consume_effects( item &food ) } // @TODO: Move quench values to mL and remove the magic number here ingested.water = contained_food.type->comestible->quench * 5_ml; - ingested.solids = contained_food.base_volume() - std::max(ingested.water, 0_ml); + ingested.solids = contained_food.base_volume() - std::max( ingested.water, 0_ml ); ingested.kcal = kcal_for( contained_food ); // GET IN MAH BELLY! diff --git a/src/debug_menu.cpp b/src/debug_menu.cpp index 709cc6c3400c0..8b2eeff23328c 100644 --- a/src/debug_menu.cpp +++ b/src/debug_menu.cpp @@ -1106,11 +1106,11 @@ void debug() std::string stom = _( "Stomach Contents: %d ml / %d ml kCal: %d, Water: %d ml" ); add_msg( m_info, stom.c_str(), units::to_milliliter( u.stomach.contains() ), - units::to_milliliter( u.stomach.capacity() ), u.stomach.get_calories(), + units::to_milliliter( u.stomach.capacity( u ) ), u.stomach.get_calories(), units::to_milliliter( u.stomach.get_water() ), u.get_hunger() ); stom = _( "Guts Contents: %d ml / %d ml kCal: %d, Water: %d ml\nHunger: %d, Thirst: %d, kCal: %d / %d" ); add_msg( m_info, stom.c_str(), units::to_milliliter( u.guts.contains() ), - units::to_milliliter( u.guts.capacity() ), u.guts.get_calories(), + units::to_milliliter( u.guts.capacity( u ) ), u.guts.get_calories(), units::to_milliliter( u.guts.get_water() ), u.get_hunger(), u.get_thirst(), u.get_stored_kcal(), u.get_healthy_kcal() ); add_msg( m_info, _( "Body Mass Index: %.0f\nBasal Metabolic Rate: %i" ), u.get_bmi(), u.get_bmr() ); diff --git a/src/iuse.cpp b/src/iuse.cpp index 317f3de649160..206957b26284f 100644 --- a/src/iuse.cpp +++ b/src/iuse.cpp @@ -1250,7 +1250,7 @@ static void marloss_common( player &p, item &it, const trait_id ¤t_color ) // previously used to set hunger to -10. with the new system, needs to do something // else that actually makes sense, so it is a little bit more involved. - units::volume fulfill_vol = std::max( p.stomach.capacity() / 8 - p.stomach.contains(), 0_ml ); + units::volume fulfill_vol = std::max( p.stomach.capacity( p ) / 8 - p.stomach.contains(), 0_ml ); if( fulfill_vol != 0_ml ) { p.add_msg_if_player( m_good, _( "It is delicious, and very filling!" ) ); int fulfill_cal = units::to_milliliter( fulfill_vol * 6 ); diff --git a/src/player.cpp b/src/player.cpp index e763813f91f94..dc5a0d19f4867 100644 --- a/src/player.cpp +++ b/src/player.cpp @@ -3233,30 +3233,31 @@ void player::update_stomach( const time_point &from, const time_point &to ) const float kcal_per_time = get_bmr() / ( 12.0f * 24.0f ); const int five_mins = ticks_between( from, to, 5_minutes ); const int half_hours = ticks_between( from, to, 30_minutes ); + const units::volume stomach_capacity = stomach.capacity( *this ); if( five_mins > 0 ) { // Digest nutrients in stomach, they are destined for the guts (except water) - nutrients digested_to_guts = stomach.digest( rates, five_mins, half_hours ); + nutrients digested_to_guts = stomach.digest( *this, rates, five_mins, half_hours ); // Digest nutrients in guts, they will be distributed to needs levels - nutrients digested_to_body = guts.digest( rates, five_mins, half_hours ); + nutrients digested_to_body = guts.digest( *this, rates, five_mins, half_hours ); // Water from stomach skips guts and gets absorbed by body set_thirst( std::max( -100, get_thirst() - units::to_milliliter( digested_to_guts.water ) / 5 ) ); guts.ingest( digested_to_guts ); - // Apply nutrients, unless this is an NPC and NO_NPC_FOOD is enabled. + // Apply nutrients, unless this is an NPC and NO_NPC_FOOD is enabled. if( !is_npc() || !get_option( "NO_NPC_FOOD" ) ) { mod_stored_kcal( digested_to_body.kcal ); vitamins_mod( digested_to_body.vitamins, false ); } } if( stomach.time_since_ate() > 10_minutes ) { - if( stomach.contains() >= stomach.capacity() && get_hunger() > -61 ) { + if( stomach.contains() >= stomach_capacity && get_hunger() > -61 ) { // you're engorged! your stomach is full to bursting! set_hunger( -61 ); - } else if( stomach.contains() >= stomach.capacity() / 2 && get_hunger() > -21 ) { + } else if( stomach.contains() >= stomach_capacity / 2 && get_hunger() > -21 ) { // sated set_hunger( -21 ); - } else if( stomach.contains() >= stomach.capacity() / 8 && get_hunger() > -1 ) { + } else if( stomach.contains() >= stomach_capacity / 8 && get_hunger() > -1 ) { // that's really all the food you need to feel full set_hunger( -1 ); } else if( stomach.contains() == 0_ml ) { @@ -3280,13 +3281,13 @@ void player::update_stomach( const time_point &from, const time_point &to ) // if you just ate but your stomach is still empty it will still // delay your filling up (drugs?) { - if( stomach.contains() >= stomach.capacity() && get_hunger() > -61 ) { + if( stomach.contains() >= stomach_capacity && get_hunger() > -61 ) { // you're engorged! your stomach is full to bursting! set_hunger( -61 ); - } else if( stomach.contains() >= stomach.capacity() * 3 / 4 && get_hunger() > -21 ) { + } else if( stomach.contains() >= stomach_capacity * 3 / 4 && get_hunger() > -21 ) { // sated set_hunger( -21 ); - } else if( stomach.contains() >= stomach.capacity() / 2 && get_hunger() > -1 ) { + } else if( stomach.contains() >= stomach_capacity / 2 && get_hunger() > -1 ) { // that's really all the food you need to feel full set_hunger( -1 ); } else if( stomach.contains() > 0_ml && get_kcal_percent() > 0.95 ) { @@ -3384,7 +3385,7 @@ void player::check_needs_extremes() } else { if( calendar::once_every( 1_hours ) ) { std::string message; - if( stomach.contains() <= stomach.capacity() / 4 ) { + if( stomach.contains() <= stomach.capacity( *this ) / 4 ) { if( get_kcal_percent() < 0.1f ) { message = _( "Food…" ); } else if( get_kcal_percent() < 0.25f ) { @@ -10446,7 +10447,7 @@ std::pair player::get_hunger_description() const { const bool calorie_deficit = get_bmi() < character_weight_category::normal; const units::volume contains = stomach.contains(); - const units::volume cap = stomach.capacity(); + const units::volume cap = stomach.capacity( *this ); std::string hunger_string; nc_color hunger_color = c_white; // i ate just now! diff --git a/src/stomach.cpp b/src/stomach.cpp index 806ada2cc29cf..063945766647c 100644 --- a/src/stomach.cpp +++ b/src/stomach.cpp @@ -54,27 +54,27 @@ void stomach_contents::deserialize( JsonIn &json ) jo.read( "last_ate", last_ate ); } -units::volume stomach_contents::capacity() const +units::volume stomach_contents::capacity( const Character &owner ) const { float max_mod = 1; - if( g->u.has_trait( trait_id( "GIZZARD" ) ) ) { + if( owner.has_trait( trait_id( "GIZZARD" ) ) ) { max_mod *= 0.9; } - if( g->u.has_active_mutation( trait_id( "HIBERNATE" ) ) ) { + if( owner.has_active_mutation( trait_id( "HIBERNATE" ) ) ) { max_mod *= 3; } - if( g->u.has_active_mutation( trait_id( "GOURMAND" ) ) ) { + if( owner.has_active_mutation( trait_id( "GOURMAND" ) ) ) { max_mod *= 2; } - if( g->u.has_trait( trait_id( "SLIMESPAWNER" ) ) ) { + if( owner.has_trait( trait_id( "SLIMESPAWNER" ) ) ) { max_mod *= 3; } return max_volume * max_mod; } -units::volume stomach_contents::stomach_remaining() const +units::volume stomach_contents::stomach_remaining( const Character &owner ) const { - return capacity() - contents - water; + return capacity( owner ) - contents - water; } units::volume stomach_contents::contains() const @@ -93,11 +93,11 @@ void stomach_contents::ingest( const nutrients &ingested ) ate(); } -nutrients stomach_contents::digest( const needs_rates &metabolic_rates, +nutrients stomach_contents::digest( const Character &owner, const needs_rates &metabolic_rates, int five_mins, int half_hours ) { nutrients digested; - stomach_digest_rates rates = get_digest_rates( metabolic_rates ); + stomach_digest_rates rates = get_digest_rates( metabolic_rates, owner ); // Digest water, but no more than in stomach. digested.water = std::min( water, rates.water * five_mins ); @@ -142,13 +142,14 @@ void stomach_contents::empty() vitamins.clear(); } -stomach_digest_rates stomach_contents::get_digest_rates( const needs_rates &metabolic_rates ) +stomach_digest_rates stomach_contents::get_digest_rates( const needs_rates &metabolic_rates, + const Character &owner ) { stomach_digest_rates rates; if( stomach ) { // The stomach is focused on passing material on to the guts. // 3 hours to empty in 30 minute increments - rates.solids = capacity() / 6; + rates.solids = capacity( owner ) / 6; rates.water = 250_ml; // Water is special, passes very quickly, in 5 minute intervals rates.min_vitamin = 1; rates.percent_vitamin = 1.0f / 6.0f; diff --git a/src/stomach.h b/src/stomach.h index 8001c6628e27c..21a27ecddc26d 100644 --- a/src/stomach.h +++ b/src/stomach.h @@ -55,20 +55,30 @@ class stomach_contents * nutrients into the body. * All returned values are >= 0, with the exception of water, which * can be negative in some circumstances (i.e. after eating dry/salty food). + * TODO: Update stomach capacity upon mutation changes, instead of calculating every time we + * need it, so we can get rid of 'owner' parameter here. + * @param owner The owner of this stomach * @param metabolic_rates The metabolic rates of the owner of this stomach * @param five_mins Five-minute intervals passed since this method was last called * @param half_hours Half-hour intervals passed since this method was last called * @return nutrients that are done processing in this stomach */ - nutrients digest( const needs_rates &metabolic_rates, int five_mins, int half_hours ); + nutrients digest( const Character &owner, const needs_rates &metabolic_rates, + int five_mins, int half_hours ); // Empties the stomach of all contents. void empty(); - // calculates max volume for a stomach_contents - units::volume capacity() const; + /** + * @brief Calculates the capacity of this stomach. + * This function needs a ref to the stomach's owner so it can account for relevant mutations. + * TODO: JSONize stomach capacity multipliers. + * @param owner This stomach's owner + * @return This stomach's capacity, in units::volume + */ + units::volume capacity( const Character &owner ) const; // how much stomach capacity you have left before you puke from stuffing your gob - units::volume stomach_remaining() const; + units::volume stomach_remaining( const Character &owner ) const; // how much volume is in the stomach_contents units::volume contains() const; @@ -121,6 +131,7 @@ class stomach_contents time_point last_ate; // Gets the rates at which this stomach will digest things. - stomach_digest_rates get_digest_rates( const needs_rates &metabolic_rates ); + stomach_digest_rates get_digest_rates( const needs_rates &metabolic_rates, + const Character &owner ); }; diff --git a/tests/stomach_contents_tests.cpp b/tests/stomach_contents_tests.cpp index 1994b39b68e10..dee3711b14cd1 100644 --- a/tests/stomach_contents_tests.cpp +++ b/tests/stomach_contents_tests.cpp @@ -65,9 +65,9 @@ static void print_stomach_contents( player &p, const bool print ) p.guts.get_calories(), p.get_stored_kcal(), p.get_healthy_kcal(), p.get_hunger() ); printf( "stomach: %d mL/ %d mL guts %d mL/ %d mL\n", units::to_milliliter( p.stomach.contains() ), - units::to_milliliter( p.stomach.capacity() ), + units::to_milliliter( p.stomach.capacity( p ) ), units::to_milliliter( p.guts.contains() ), - units::to_milliliter( p.guts.capacity() ) ); + units::to_milliliter( p.guts.capacity( p ) ) ); printf( "metabolic rate: %.2f\n", p.metabolic_rate() ); } From a38d174c382bcc1bde6d6329cd82cfd75d2bf0bf Mon Sep 17 00:00:00 2001 From: Davi Date: Fri, 1 Nov 2019 01:02:14 -0400 Subject: [PATCH 8/8] Astyle --- src/consumption.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/consumption.cpp b/src/consumption.cpp index 1c4f52718dee3..1b34da66be746 100644 --- a/src/consumption.cpp +++ b/src/consumption.cpp @@ -594,7 +594,8 @@ ret_val player::will_eat( const item &food, bool interactive ) co add_consequence( _( "Your stomach won't be happy (not rotten enough)." ), ALLERGY_WEAK ); } - if( stomach.stomach_remaining( *this ) < food.volume() / food.charges && !food.has_infinite_charges() ) { + if( stomach.stomach_remaining( *this ) < food.volume() / food.charges && + !food.has_infinite_charges() ) { if( edible ) { add_consequence( _( "You're full already and will be forcing yourself to eat." ), TOO_FULL ); } else {