Skip to content

Commit

Permalink
Merge pull request #68988 from ehughsbaird/units-weight-more
Browse files Browse the repository at this point in the history
Use microgram (μg) resolution mass for food vitamins
  • Loading branch information
Maleclypse authored Nov 7, 2023
2 parents 9dbc11f + 2f9bc68 commit 7fecb62
Show file tree
Hide file tree
Showing 9 changed files with 65 additions and 37 deletions.
2 changes: 1 addition & 1 deletion data/json/items/comestibles/protein.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
8 changes: 4 additions & 4 deletions data/json/vitamin.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
},
{
Expand All @@ -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 ] ]
},
Expand All @@ -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 ] ]
},
Expand Down
2 changes: 1 addition & 1 deletion doc/JSON_INFO.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 }
Expand Down
4 changes: 2 additions & 2 deletions src/item_factory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<units::mass>( 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 );
}
}
Expand All @@ -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<units::mass>( 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 );
}
}
Expand Down
44 changes: 23 additions & 21 deletions src/stomach.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<int, units::mass> &val = vitamins_[vit];
std::variant<int, vitamin_units::mass> &val = vitamins_[vit];
// We must be finalized because we're calling vitamin::all()
val = std::max( std::get<int>( val ), other );
}
Expand All @@ -51,13 +51,13 @@ std::map<vitamin_id, int> nutrients::vitamins() const
}

std::map<vitamin_id, int> ret;
for( const std::pair<const vitamin_id, std::variant<int, units::mass>> &vit : vitamins_ ) {
for( const std::pair<const vitamin_id, std::variant<int, vitamin_units::mass>> &vit : vitamins_ ) {
ret.emplace( vit.first, std::get<int>( 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 ) );
Expand All @@ -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<units::mass>( iter->second ) ) {
auto iter = vitamins_.emplace( vit, vitamin_units::mass( 0, {} ) ).first;
if( !std::holds_alternative<vitamin_units::mass>( iter->second ) ) {
debugmsg( "Tried to add mass vitamin to units vitamin before vitamins were finalized!" );
return;
}
iter->second = std::get<units::mass>( iter->second ) + mass;
iter->second = std::get<vitamin_units::mass>( iter->second ) + mass;
}

void nutrients::set_vitamin( const vitamin_id &vit, int units )
Expand All @@ -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<units::mass>( iter->second ) ) {
if( std::holds_alternative<vitamin_units::mass>( iter->second ) ) {
debugmsg( "Tried to add mass vitamin to units vitamin before vitamins were finalized!" );
return;
}
Expand All @@ -107,7 +107,7 @@ int nutrients::get_vitamin( const vitamin_id &vit ) const
if( it == vitamins_.end() ) {
return 0;
}
if( !finalized && std::holds_alternative<units::mass>( it->second ) ) {
if( !finalized && std::holds_alternative<vitamin_units::mass>( it->second ) ) {
debugmsg( "Called get_vitamin on a mass vitamin before vitamins were finalized!" );
return 0;
}
Expand All @@ -121,11 +121,11 @@ int nutrients::kcal() const

void nutrients::finalize_vitamins()
{
for( std::pair<const vitamin_id, std::variant<int, units::mass> > &vit : vitamins_ ) {
if( std::holds_alternative<units::mass>( vit.second ) ) {
vit.second = vit.first->units_from_mass( std::get<units::mass>( vit.second ) );
for( std::pair<const vitamin_id, std::variant<int, vitamin_units::mass> > &vit : vitamins_ ) {
if( std::holds_alternative<vitamin_units::mass>( vit.second ) ) {
vit.second = vit.first->units_from_mass( std::get<vitamin_units::mass>( vit.second ) );
}
if( std::holds_alternative<units::mass>( vit.second ) ) {
if( std::holds_alternative<vitamin_units::mass>( vit.second ) ) {
debugmsg( "Error occured during vitamin finalization!" );
}
}
Expand Down Expand Up @@ -155,8 +155,9 @@ nutrients &nutrients::operator+=( const nutrients &r )
debugmsg( "Nutrients not finalized when += called!" );
}
calories += r.calories;
for( const std::pair<const vitamin_id, std::variant<int, units::mass>> &vit : r.vitamins_ ) {
std::variant<int, units::mass> &here = vitamins_[vit.first];
for( const std::pair<const vitamin_id, std::variant<int, vitamin_units::mass>> &vit :
r.vitamins_ ) {
std::variant<int, vitamin_units::mass> &here = vitamins_[vit.first];
here = std::get<int>( here ) + std::get<int>( vit.second );
}
return *this;
Expand All @@ -168,8 +169,9 @@ nutrients &nutrients::operator-=( const nutrients &r )
debugmsg( "Nutrients not finalized when -= called!" );
}
calories -= r.calories;
for( const std::pair<const vitamin_id, std::variant<int, units::mass>> &vit : r.vitamins_ ) {
std::variant<int, units::mass> &here = vitamins_[vit.first];
for( const std::pair<const vitamin_id, std::variant<int, vitamin_units::mass>> &vit :
r.vitamins_ ) {
std::variant<int, vitamin_units::mass> &here = vitamins_[vit.first];
here = std::get<int>( here ) - std::get<int>( vit.second );
}
return *this;
Expand All @@ -181,8 +183,8 @@ nutrients &nutrients::operator*=( int r )
debugmsg( "Nutrients not finalized when *= called!" );
}
calories *= r;
for( const std::pair<const vitamin_id, std::variant<int, units::mass>> &vit : vitamins_ ) {
std::variant<int, units::mass> &here = vitamins_[vit.first];
for( const std::pair<const vitamin_id, std::variant<int, vitamin_units::mass>> &vit : vitamins_ ) {
std::variant<int, vitamin_units::mass> &here = vitamins_[vit.first];
here = std::get<int>( here ) * r;
}
return *this;
Expand All @@ -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<const vitamin_id, std::variant<int, units::mass>> &vit : vitamins_ ) {
std::variant<int, units::mass> &here = vitamins_[vit.first];
for( const std::pair<const vitamin_id, std::variant<int, vitamin_units::mass>> &vit : vitamins_ ) {
std::variant<int, vitamin_units::mass> &here = vitamins_[vit.first];
here = divide_round_up( std::get<int>( here ), r );
}
return *this;
Expand Down
23 changes: 20 additions & 3 deletions src/stomach.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,23 @@ class JsonObject;
class JsonOut;
struct needs_rates;

namespace vitamin_units
{
using mass = units::quantity<int, units::mass_in_microgram_tag>;

constexpr mass microgram = units::quantity<int, units::mass_in_microgram_tag>( 1, {} );
constexpr mass milligram = units::quantity<int, units::mass_in_microgram_tag>( 1000, {} );
constexpr mass gram = units::quantity<int, units::mass_in_microgram_tag>( 1'000'000, {} );
const std::vector<std::pair<std::string, mass>> 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 {
Expand All @@ -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 );
Expand Down Expand Up @@ -72,7 +89,7 @@ struct nutrients {

private:
/** vitamins potentially provided by this comestible (if any) */
std::map<vitamin_id, std::variant<int, units::mass>> vitamins_;
std::map<vitamin_id, std::variant<int, vitamin_units::mass>> vitamins_;
};

// Contains all information that can pass out of (or into) a stomach
Expand Down
4 changes: 4 additions & 0 deletions src/units_fwd.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ class volume_in_milliliter_tag

using volume = quantity<int, volume_in_milliliter_tag>;

class mass_in_microgram_tag
{
};

class mass_in_milligram_tag
{
};
Expand Down
10 changes: 7 additions & 3 deletions src/vitamin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<time_duration>( 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" );
Expand Down Expand Up @@ -124,14 +128,14 @@ float vitamin::RDA_to_default( int percent ) const
return ( 24_hours / rate_ ) * ( static_cast<float>( 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
Expand Down
5 changes: 3 additions & 2 deletions src/vitamin.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include <vector>

#include "calendar.h"
#include "stomach.h"
#include "translations.h"
#include "type_id.h"
#include "units.h"
Expand Down Expand Up @@ -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<units::mass> weight_per_unit;
std::optional<vitamin_units::mass> weight_per_unit;
efftype_id deficiency_;
efftype_id excess_;
int min_ = 0;
Expand Down

0 comments on commit 7fecb62

Please sign in to comment.