diff --git a/data/json/items/comestibles/protein.json b/data/json/items/comestibles/protein.json index 2050714537597..81d338553e55f 100644 --- a/data/json/items/comestibles/protein.json +++ b/data/json/items/comestibles/protein.json @@ -105,7 +105,7 @@ { "type": "COMPONENT_ID_SUBSTRING", "condition": "mutant", "name": { "str_sp": "perturbing %s" } } ], "description": "A thick and tasty beverage made from pure refined protein and nutritious fruit. It has been supplemented with extra vitamins and minerals.", - "relative": { "vitamins": [ [ "calcium", "52 mg" ], [ "iron", 5 ], [ "vitC", "5 mg" ] ] } + "relative": { "vitamins": [ [ "calcium", "52 mg" ], [ "iron", "940 μg" ], [ "vitC", "5 mg" ] ] } }, { "id": "milk_fortified", diff --git a/data/json/vitamin.json b/data/json/vitamin.json index 9d7e39f84de1b..c9a1e759601ae 100644 --- a/data/json/vitamin.json +++ b/data/json/vitamin.json @@ -10,7 +10,7 @@ "96 units are consumed per day, with RDA of 1000mg / 96 = 10.42mg", "1000mg for 19-50yr olds from https://ods.od.nih.gov/factsheets/calcium-HealthProfessional/#h2" ], - "weight_per_unit": "10 mg", + "weight_per_unit": "10 mg 420 μg", "rate": "15 m" }, { @@ -24,9 +24,9 @@ "//": [ "96 units are consumed per day, with RDA of 18mg / 96 = 0.1875mg", "18mg for 19-50yr olds from https://ods.od.nih.gov/factsheets/iron-HealthProfessional/#h2", - "As it varies with sex, the max value was chosen", - "still, mass only has mg resolution, so it cannot be in mass units (for now)" + "As it varies with sex, the max value was chosen" ], + "weight_per_unit": "188 ug", "rate": "15 m", "disease": [ [ -9600, -11200 ], [ -11201, -12800 ], [ -12801, -24000 ] ] }, @@ -43,7 +43,7 @@ "90mg for 19-50yr olds from https://ods.od.nih.gov/factsheets/VitaminC-HealthProfessional/#h2", "As it varies with sex, the max value was chosen" ], - "weight_per_unit": "1 mg", + "weight_per_unit": "938 mcg", "rate": "15 m", "disease": [ [ -2800, -3600 ], [ -3601, -4400 ], [ -4401, -5600 ] ] }, diff --git a/doc/JSON_INFO.md b/doc/JSON_INFO.md index ee25d28015792..819a8a92a63ae 100644 --- a/doc/JSON_INFO.md +++ b/doc/JSON_INFO.md @@ -3768,7 +3768,7 @@ CBMs can be defined like this: "cooks_like": "meat_cooked", // (Optional) If the item is used in a recipe, replaces it with its cooks_like "parasites": 10, // (Optional) Probability of becoming parasitized when eating "contamination": [ { "disease": "bad_food", "probability": 5 } ], // (Optional) List of diseases carried by this comestible and their associated probability. Values must be in the [0, 100] range. -"vitamins": [ [ "calcium", 5 ], [ "iron", 12 ] ], // Vitamins provided by consuming a charge (portion) of this. An integer percentage of ideal daily value average. Vitamins array keys include the following: calcium, iron, vitA, vitB, vitC, mutant_toxin, bad_food, blood, and redcells. Note that vitB is B12. +"vitamins": [ [ "calcium", "60 mg" ], [ "iron", 12 ] ], // Vitamins provided by consuming a charge (portion) of this. Some vitamins ("calcium", "iron", "vitC") can be specified with the weight of the vitamins in that food. Vitamins specified by weight can be in grams ("g"), milligrams ("mg") or micrograms ("μg", "ug", "mcg"). If a vitamin is not specified by weight, it is specified in "units", with meaning according to the vitamin definition. Nutrition vitamins ("calcium", "iron", "vitC") are an integer percentage of ideal daily value average. Vitamins array keys include the following: calcium, iron, vitC, mutant_toxin, bad_food, blood, and redcells. "material": [ // All materials (IDs) this food is made of { "type": "flesh", "portion": 3 }, // See Generic Item attributes for type and portion details { "type": "wheat", "portion": 5 } diff --git a/src/item_factory.cpp b/src/item_factory.cpp index 340b04c6a11e7..e75dcf8fd74ea 100644 --- a/src/item_factory.cpp +++ b/src/item_factory.cpp @@ -3329,7 +3329,7 @@ void Item_factory::load( islot_comestible &slot, const JsonObject &jo, const std if( pair.has_int( 1 ) ) { slot.default_nutrition.set_vitamin( vit, pair.get_int( 1 ) ); } else { - units::mass val = read_from_json_string( pair[1], units::mass_units ); + vitamin_units::mass val = read_from_json_string( pair[1], vitamin_units::mass_units ); slot.default_nutrition.set_vitamin( vit, val ); } } @@ -3340,7 +3340,7 @@ void Item_factory::load( islot_comestible &slot, const JsonObject &jo, const std if( pair.has_int( 1 ) ) { slot.default_nutrition.add_vitamin( vit, pair.get_int( 1 ) ); } else { - units::mass val = read_from_json_string( pair[1], units::mass_units ); + vitamin_units::mass val = read_from_json_string( pair[1], vitamin_units::mass_units ); slot.default_nutrition.add_vitamin( vit, val ); } } diff --git a/src/stomach.cpp b/src/stomach.cpp index cf5930ffb6b73..06c436d862fe5 100644 --- a/src/stomach.cpp +++ b/src/stomach.cpp @@ -36,7 +36,7 @@ void nutrients::max_in_place( const nutrients &r ) const vitamin_id &vit = vit_pair.first; int other = r.get_vitamin( vit ); if( other != 0 ) { - std::variant &val = vitamins_[vit]; + std::variant &val = vitamins_[vit]; // We must be finalized because we're calling vitamin::all() val = std::max( std::get( val ), other ); } @@ -51,13 +51,13 @@ std::map nutrients::vitamins() const } std::map ret; - for( const std::pair> &vit : vitamins_ ) { + for( const std::pair> &vit : vitamins_ ) { ret.emplace( vit.first, std::get( vit.second ) ); } return ret; } -void nutrients::set_vitamin( const vitamin_id &vit, units::mass mass ) +void nutrients::set_vitamin( const vitamin_id &vit, vitamin_units::mass mass ) { if( finalized ) { set_vitamin( vit, vit->units_from_mass( mass ) ); @@ -66,18 +66,18 @@ void nutrients::set_vitamin( const vitamin_id &vit, units::mass mass ) vitamins_[vit] = mass; } -void nutrients::add_vitamin( const vitamin_id &vit, units::mass mass ) +void nutrients::add_vitamin( const vitamin_id &vit, vitamin_units::mass mass ) { if( finalized ) { add_vitamin( vit, vit->units_from_mass( mass ) ); return; } - auto iter = vitamins_.emplace( vit, 0_kilogram ).first; - if( !std::holds_alternative( iter->second ) ) { + auto iter = vitamins_.emplace( vit, vitamin_units::mass( 0, {} ) ).first; + if( !std::holds_alternative( iter->second ) ) { debugmsg( "Tried to add mass vitamin to units vitamin before vitamins were finalized!" ); return; } - iter->second = std::get( iter->second ) + mass; + iter->second = std::get( iter->second ) + mass; } void nutrients::set_vitamin( const vitamin_id &vit, int units ) @@ -89,7 +89,7 @@ void nutrients::set_vitamin( const vitamin_id &vit, int units ) void nutrients::add_vitamin( const vitamin_id &vit, int units ) { auto iter = vitamins_.emplace( vit, 0 ).first; - if( std::holds_alternative( iter->second ) ) { + if( std::holds_alternative( iter->second ) ) { debugmsg( "Tried to add mass vitamin to units vitamin before vitamins were finalized!" ); return; } @@ -107,7 +107,7 @@ int nutrients::get_vitamin( const vitamin_id &vit ) const if( it == vitamins_.end() ) { return 0; } - if( !finalized && std::holds_alternative( it->second ) ) { + if( !finalized && std::holds_alternative( it->second ) ) { debugmsg( "Called get_vitamin on a mass vitamin before vitamins were finalized!" ); return 0; } @@ -121,11 +121,11 @@ int nutrients::kcal() const void nutrients::finalize_vitamins() { - for( std::pair > &vit : vitamins_ ) { - if( std::holds_alternative( vit.second ) ) { - vit.second = vit.first->units_from_mass( std::get( vit.second ) ); + for( std::pair > &vit : vitamins_ ) { + if( std::holds_alternative( vit.second ) ) { + vit.second = vit.first->units_from_mass( std::get( vit.second ) ); } - if( std::holds_alternative( vit.second ) ) { + if( std::holds_alternative( vit.second ) ) { debugmsg( "Error occured during vitamin finalization!" ); } } @@ -155,8 +155,9 @@ nutrients &nutrients::operator+=( const nutrients &r ) debugmsg( "Nutrients not finalized when += called!" ); } calories += r.calories; - for( const std::pair> &vit : r.vitamins_ ) { - std::variant &here = vitamins_[vit.first]; + for( const std::pair> &vit : + r.vitamins_ ) { + std::variant &here = vitamins_[vit.first]; here = std::get( here ) + std::get( vit.second ); } return *this; @@ -168,8 +169,9 @@ nutrients &nutrients::operator-=( const nutrients &r ) debugmsg( "Nutrients not finalized when -= called!" ); } calories -= r.calories; - for( const std::pair> &vit : r.vitamins_ ) { - std::variant &here = vitamins_[vit.first]; + for( const std::pair> &vit : + r.vitamins_ ) { + std::variant &here = vitamins_[vit.first]; here = std::get( here ) - std::get( vit.second ); } return *this; @@ -181,8 +183,8 @@ nutrients &nutrients::operator*=( int r ) debugmsg( "Nutrients not finalized when *= called!" ); } calories *= r; - for( const std::pair> &vit : vitamins_ ) { - std::variant &here = vitamins_[vit.first]; + for( const std::pair> &vit : vitamins_ ) { + std::variant &here = vitamins_[vit.first]; here = std::get( here ) * r; } return *this; @@ -194,8 +196,8 @@ nutrients &nutrients::operator/=( int r ) debugmsg( "Nutrients not finalized when -= called!" ); } calories = divide_round_up( calories, r ); - for( const std::pair> &vit : vitamins_ ) { - std::variant &here = vitamins_[vit.first]; + for( const std::pair> &vit : vitamins_ ) { + std::variant &here = vitamins_[vit.first]; here = divide_round_up( std::get( here ), r ); } return *this; diff --git a/src/stomach.h b/src/stomach.h index 12e23ee155dbe..fa2ecdd690804 100644 --- a/src/stomach.h +++ b/src/stomach.h @@ -14,6 +14,23 @@ class JsonObject; class JsonOut; struct needs_rates; +namespace vitamin_units +{ +using mass = units::quantity; + +constexpr mass microgram = units::quantity( 1, {} ); +constexpr mass milligram = units::quantity( 1000, {} ); +constexpr mass gram = units::quantity( 1'000'000, {} ); +const std::vector> mass_units = { { + { "ug", microgram }, + { "μg", microgram }, + { "mcg", microgram }, + { "mg", milligram }, + { "g", gram } + } +}; +} // namespace vitamin_units + // Separate struct for nutrients so that we can easily perform arithmetic on // them struct nutrients { @@ -31,8 +48,8 @@ struct nutrients { // For vitamins that support units::mass quantities // If finalized == true, these will instantly convert to units, // so make sure finalized = false if you call these before vitamins are loaded - void set_vitamin( const vitamin_id &, units::mass mass ); - void add_vitamin( const vitamin_id &, units::mass mass ); + void set_vitamin( const vitamin_id &, vitamin_units::mass mass ); + void add_vitamin( const vitamin_id &, vitamin_units::mass mass ); void set_vitamin( const vitamin_id &, int units ); void add_vitamin( const vitamin_id &, int units ); @@ -72,7 +89,7 @@ struct nutrients { private: /** vitamins potentially provided by this comestible (if any) */ - std::map> vitamins_; + std::map> vitamins_; }; // Contains all information that can pass out of (or into) a stomach diff --git a/src/units_fwd.h b/src/units_fwd.h index 8a018c3b826e0..443111c161b31 100644 --- a/src/units_fwd.h +++ b/src/units_fwd.h @@ -16,6 +16,10 @@ class volume_in_milliliter_tag using volume = quantity; +class mass_in_microgram_tag +{ +}; + class mass_in_milligram_tag { }; diff --git a/src/vitamin.cpp b/src/vitamin.cpp index 9f2380ef5d678..624a2f23fe4cc 100644 --- a/src/vitamin.cpp +++ b/src/vitamin.cpp @@ -62,7 +62,11 @@ void vitamin::load_vitamin( const JsonObject &jo ) vit.min_ = jo.get_int( "min" ); vit.max_ = jo.get_int( "max", 0 ); vit.rate_ = read_from_json_string( jo.get_member( "rate" ), time_duration::units ); - assign( jo, "weight_per_unit", vit.weight_per_unit ); + + if( jo.has_string( "weight_per_unit" ) ) { + vit.weight_per_unit = read_from_json_string( jo.get_member( "weight_per_unit" ), + vitamin_units::mass_units ); + } if( !jo.has_string( "vit_type" ) ) { jo.throw_error_at( "vit_type", "vitamin must have a vitamin type" ); @@ -124,14 +128,14 @@ float vitamin::RDA_to_default( int percent ) const return ( 24_hours / rate_ ) * ( static_cast( percent ) / 100.0f ); } -int vitamin::units_from_mass( units::mass mass ) const +int vitamin::units_from_mass( vitamin_units::mass val ) const { if( !weight_per_unit.has_value() ) { debugmsg( "Tried to convert vitamin in mass to units, but %s doesn't support mass for vitamins", id_.str() ); return 1; } - return mass / *weight_per_unit; + return val / *weight_per_unit; } namespace io diff --git a/src/vitamin.h b/src/vitamin.h index 9809216216c2a..229d0a9ee6d5c 100644 --- a/src/vitamin.h +++ b/src/vitamin.h @@ -10,6 +10,7 @@ #include #include "calendar.h" +#include "stomach.h" #include "translations.h" #include "type_id.h" #include "units.h" @@ -109,13 +110,13 @@ class vitamin */ float RDA_to_default( int percent ) const; - int units_from_mass( units::mass mass ) const; + int units_from_mass( vitamin_units::mass val ) const; private: vitamin_id id_; vitamin_type type_ = vitamin_type::num_vitamin_types; translation name_; - std::optional weight_per_unit; + std::optional weight_per_unit; efftype_id deficiency_; efftype_id excess_; int min_ = 0;