diff --git a/src/clzones.cpp b/src/clzones.cpp index 5ef78f01cef27..f2e635df5fa7c 100644 --- a/src/clzones.cpp +++ b/src/clzones.cpp @@ -517,7 +517,7 @@ zone_type_id zone_manager::get_near_zone_type_for_item( const item &it, const auto &it_food = it.is_food_container() ? it.contents.front() : it; if( it_food.is_food() ) { // skip food without comestible, like MREs - if( it_food.type->comestible->comesttype == "DRINK" ) { + if( it_food.get_comestible()->comesttype == "DRINK" ) { if( !preserves && it_food.goes_bad() && has_near( zone_type_id( "LOOT_PDRINK" ), where ) ) { return zone_type_id( "LOOT_PDRINK" ); } else if( has_near( zone_type_id( "LOOT_DRINK" ), where ) ) { diff --git a/src/consumption.cpp b/src/consumption.cpp index f5ed012b1a474..422a1616ec30a 100644 --- a/src/consumption.cpp +++ b/src/consumption.cpp @@ -115,7 +115,7 @@ int player::kcal_for( const item &comest ) const } kcal /= comest.recipe_charges; } else { - kcal = comest.type->comestible->get_calories(); + kcal = comest.get_comestible()->get_calories(); } if( has_trait( trait_GIZZARD ) ) { @@ -165,7 +165,7 @@ std::pair player::fun_for( const item &comest ) const } // As float to avoid rounding too many times - float fun = comest.type->comestible->fun; + float fun = comest.get_comestible()->fun; if( comest.has_flag( flag_MUSHY ) && fun > -5.0f ) { fun = -5.0f; // defrosted MUSHY food is practicaly tastless or tastes off } @@ -218,8 +218,8 @@ std::pair player::fun_for( const item &comest ) const } if( has_active_bionic( bio_taste_blocker ) && - power_level > abs( comest.type->comestible->fun ) && - comest.type->comestible->fun < 0 ) { + power_level > abs( comest.get_comestible()->fun ) && + comest.get_comestible()->fun < 0 ) { fun = 0; } @@ -259,7 +259,7 @@ std::map player::vitamins_from( const item &it ) const { std::map res; - if( !it.type->comestible ) { + if( !it.get_comestible() ) { return res; } @@ -277,7 +277,7 @@ std::map player::vitamins_from( const item &it ) const } } else { // if we're here, whatever is returned is going to be based on the item's defined stats - res = it.type->comestible->vitamins; + res = it.get_comestible()->vitamins; std::list traits = mut_vitamin_absorb_modify( *this ); // traits modify the absorption of vitamins here if( !traits.empty() ) { @@ -420,7 +420,7 @@ morale_type player::allergy_type( const item &food ) const ret_val player::can_eat( const item &food ) const { - const auto &comest = food.type->comestible; + const auto &comest = food.get_comestible(); if( !comest ) { return ret_val::make_failure( _( "That doesn't look edible." ) ); } @@ -511,7 +511,7 @@ ret_val player::will_eat( const item &food, bool interactive ) co }; const bool saprophage = has_trait( trait_id( "SAPROPHAGE" ) ); - const auto &comest = food.type->comestible; + const auto &comest = food.get_comestible(); if( food.rotten() ) { const bool saprovore = has_trait( trait_id( "SAPROVORE" ) ); @@ -612,13 +612,13 @@ bool player::eat( item &food, bool force ) const bool hibernate = has_active_mutation( trait_id( "HIBERNATE" ) ); const int nutr = nutrition_for( food ); - const int quench = food.type->comestible->quench; + const int quench = food.get_comestible()->quench; const bool spoiled = food.rotten(); // The item is solid food - const bool chew = food.type->comestible->comesttype == "FOOD" || food.has_flag( "USE_EAT_VERB" ); + const bool chew = food.get_comestible()->comesttype == "FOOD" || food.has_flag( "USE_EAT_VERB" ); // This item is a drink and not a solid food (and not a thick soup) - const bool drinkable = !chew && food.type->comestible->comesttype == "DRINK"; + const bool drinkable = !chew && food.get_comestible()->comesttype == "DRINK"; // If neither of the above is true then it's a drug and shouldn't get mealtime penalty/bonus if( hibernate && @@ -750,9 +750,9 @@ bool player::eat( item &food, bool force ) } } - if( item::find_type( food.type->comestible->tool )->tool ) { + if( item::find_type( food.get_comestible()->tool )->tool ) { // Tools like lighters get used - use_charges( food.type->comestible->tool, 1 ); + use_charges( food.get_comestible()->tool, 1 ); } if( has_bionic( bio_ethanol ) && food.type->can_use( "ALCOHOL" ) ) { @@ -766,7 +766,7 @@ bool player::eat( item &food, bool force ) } if( has_active_bionic( bio_taste_blocker ) ) { - charge_power( -abs( food.type->comestible->fun ) ); + charge_power( -abs( food.get_comestible()->fun ) ); } if( food.has_flag( "CANNIBALISM" ) ) { @@ -874,8 +874,8 @@ bool player::eat( item &food, bool force ) // chance to become parasitised if( !( has_bionic( bio_digestion ) || has_trait( trait_id( "PARAIMMUNE" ) ) ) ) { - if( food.type->comestible->parasites > 0 && !food.has_flag( "NO_PARASITES" ) && - one_in( food.type->comestible->parasites ) ) { + if( food.get_comestible()->parasites > 0 && !food.has_flag( "NO_PARASITES" ) && + one_in( food.get_comestible()->parasites ) ) { switch( rng( 0, 3 ) ) { case 0: if( !has_trait( trait_id( "EATHEALTH" ) ) ) { @@ -940,7 +940,7 @@ void player::consume_effects( const item &food ) debugmsg( "called player::consume_effects with non-comestible" ); return; } - const auto &comest = *food.type->comestible; + const auto &comest = *food.get_comestible(); const int capacity = stomach_capacity(); if( has_trait( trait_id( "THRESH_PLANT" ) ) && food.type->can_use( "PLANTBLECH" ) ) { diff --git a/src/crafting.cpp b/src/crafting.cpp index 7b7aaf1e302cb..00266e671b488 100644 --- a/src/crafting.cpp +++ b/src/crafting.cpp @@ -467,21 +467,29 @@ static item *set_item_inventory( player &p, item &newit ) return ret_val; } -static void return_all_components_for_craft( player &p, std::list &used ) +static void return_all_components_for_craft( player &p, std::list &used, + const double &relative_rot ) { - // Force add here since the craft item is removed immediately after for( item &it : used ) { + // If the product doesn't rot, don't touch component rot. + if( relative_rot != 0.0 ) { + it.set_relative_rot( relative_rot ); + } set_item_inventory( p, it ); } } -static void return_some_components_for_craft( player &p, std::list &used ) +static void return_some_components_for_craft( player &p, std::list &used, + const double &relative_rot ) { for( std::list::iterator it = used.begin(); it != used.end(); ++it ) { - // Force add here since the craft item is removed in the same turn // Each component has a 50% chance of being returned // Never return the first component if( it != used.begin() && one_in( 2 ) ) { + // If the product doesn't rot, don't touch component rot. + if( relative_rot != 0.0 ) { + it->set_relative_rot( relative_rot ); + } set_item_inventory( p, *it ); } } @@ -630,29 +638,21 @@ void player::complete_craft( item &craft ) } std::list &used = craft.components; + const double relative_rot = craft.get_relative_rot(); // Messed up badly; waste some components. if( making.difficulty != 0 && diff_roll > skill_roll * ( 1 + 0.1 * rng( 1, 5 ) ) ) { add_msg( m_bad, _( "You fail to make the %s, and waste some materials." ), making.result_name() ); - return_some_components_for_craft( *this, used ); + return_some_components_for_craft( *this, used, relative_rot ); return; // Messed up slightly; no components wasted. } else if( diff_roll > skill_roll ) { add_msg( m_neutral, _( "You fail to make the %s, but don't waste any materials." ), making.result_name() ); - return_all_components_for_craft( *this, used ); + return_all_components_for_craft( *this, used, relative_rot ); return; } - // Take the relative rot of the most rotten component - double max_relative_rot = 0.0; - for( const item &it : used ) { - if( !it.goes_bad() ) { - continue; - } - max_relative_rot = std::max( max_relative_rot, it.get_relative_rot() ); - } - // Set up the new item, and assign an inventory letter if available std::vector newits = making.create_results( batch_size ); @@ -736,8 +736,8 @@ void player::complete_craft( item &craft ) // if a component item has "cooks_like" it will be replaced by that item as a component for( item &comp : used ) { // only comestibles have cooks_like. any other type of item will throw an exception, so filter those out - if( comp.is_comestible() && !comp.type->comestible->cooks_like.empty() ) { - comp = item( comp.type->comestible->cooks_like, comp.birthday(), comp.charges ); + if( comp.is_comestible() && !comp.get_comestible()->cooks_like.empty() ) { + comp = item( comp.get_comestible()->cooks_like, comp.birthday(), comp.charges ); } } // byproducts get stored as a "component" but with a byproduct flag for consumption purposes @@ -755,12 +755,12 @@ void player::complete_craft( item &craft ) } if( newit.goes_bad() ) { - newit.set_relative_rot( max_relative_rot ); + newit.set_relative_rot( relative_rot ); } else { if( newit.is_container() ) { for( item &in : newit.contents ) { if( in.goes_bad() ) { - in.set_relative_rot( max_relative_rot ); + in.set_relative_rot( relative_rot ); } } } @@ -790,7 +790,7 @@ void player::complete_craft( item &craft ) std::vector bps = making.create_byproducts( batch_size ); for( auto &bp : bps ) { if( bp.goes_bad() ) { - bp.set_relative_rot( max_relative_rot ); + bp.set_relative_rot( relative_rot ); } if( bp.is_food() ) { if( should_heat ) { diff --git a/src/dump.cpp b/src/dump.cpp index f34c311e40957..6fb49519fdcbf 100644 --- a/src/dump.cpp +++ b/src/dump.cpp @@ -131,9 +131,9 @@ bool game::dump_stats( const std::string &what, dump_mode mode, r.push_back( to_string( obj.volume() / units::legacy_volume_factor ) ); r.push_back( to_string( to_gram( obj.weight() ) ) ); r.push_back( to_string( obj.type->stack_size ) ); - r.push_back( to_string( obj.type->comestible->get_calories() ) ); - r.push_back( to_string( obj.type->comestible->quench ) ); - r.push_back( to_string( obj.type->comestible->healthy ) ); + r.push_back( to_string( obj.get_comestible()->get_calories() ) ); + r.push_back( to_string( obj.get_comestible()->quench ) ); + r.push_back( to_string( obj.get_comestible()->healthy ) ); auto vits = g->u.vitamins_from( obj ); for( const auto &v : vitamin::all() ) { r.push_back( to_string( vits[ v.first ] ) ); diff --git a/src/faction_camp.cpp b/src/faction_camp.cpp index c0b19eb9cf240..1f99aa2f93ed1 100644 --- a/src/faction_camp.cpp +++ b/src/faction_camp.cpp @@ -3434,7 +3434,7 @@ bool basecamp::distribute_food() g->m.add_item_or_charges( litter_spread, i, false ); i = comest; } - if( i.is_comestible() && ( i.rotten() || i.type->comestible->fun < -6 ) ) { + if( i.is_comestible() && ( i.rotten() || i.get_comestible()->fun < -6 ) ) { keep_me.push_back( i ); } else if( i.is_food() ) { double rot_multip; @@ -3446,7 +3446,7 @@ bool basecamp::distribute_food() } else { rot_multip = quick_rot; } - total += i.type->comestible->get_calories() * rot_multip * i.count(); + total += i.get_comestible()->get_calories() * rot_multip * i.count(); } else { keep_me.push_back( i ); } diff --git a/src/game_inventory.cpp b/src/game_inventory.cpp index 814a12dcb4910..100bc5903d331 100644 --- a/src/game_inventory.cpp +++ b/src/game_inventory.cpp @@ -500,7 +500,7 @@ class comestible_inventory_preset : public inventory_selector_preset const islot_comestible &get_edible_comestible( const item &it ) const { if( it.is_comestible() && p.can_eat( it ).success() ) { - return *it.type->comestible; + return *it.get_comestible(); } static const islot_comestible dummy {}; return dummy; diff --git a/src/iexamine.cpp b/src/iexamine.cpp index ed0ea8dfc98b5..f41a78b1f21e6 100644 --- a/src/iexamine.cpp +++ b/src/iexamine.cpp @@ -4203,11 +4203,11 @@ void smoker_finalize( player &, const tripoint &examp, const time_point &start_t for( size_t i = 0; i < items.size(); i++ ) { auto &item_it = items[i]; - if( item_it.type->comestible ) { - if( item_it.type->comestible->smoking_result.empty() ) { + if( item_it.get_comestible() ) { + if( item_it.get_comestible()->smoking_result.empty() ) { item_it.unset_flag( "SMOKING" ); } else { - item result( item_it.type->comestible->smoking_result, start_time + 6_hours, item_it.charges ); + item result( item_it.get_comestible()->smoking_result, start_time + 6_hours, item_it.charges ); // Set flag to tell set_relative_rot() to calc from bday not now result.set_flag( "SMOKING_RESULT" ); diff --git a/src/item.cpp b/src/item.cpp index 730eb6d3c0277..1599a2cbf902e 100644 --- a/src/item.cpp +++ b/src/item.cpp @@ -171,7 +171,7 @@ item::item( const itype *type, time_point turn, long qty ) : type( type ), bday( emplace_back( type->magazine->default_ammo, calendar::turn, type->magazine->count ); } - } else if( type->comestible ) { + } else if( get_comestible() ) { active = is_food(); last_temp_check = bday; @@ -209,14 +209,32 @@ item::item( const itype *type, time_point turn, solitary_tag ) item::item( const itype_id &id, time_point turn, solitary_tag tag ) : item( find_type( id ), turn, tag ) {} +static const item *get_most_rotten_component( const item &craft ) +{ + const item *most_rotten = nullptr; + for( const item &it : craft.components ) { + if( it.goes_bad() ) { + if( !most_rotten || it.get_relative_rot() > most_rotten->get_relative_rot() ) { + most_rotten = ⁢ + } + } + } + return most_rotten; +} + item::item( const recipe *rec, long qty, std::list items ) : item( "craft", calendar::turn, qty ) { making = rec; components = items; - // Process this item to apply rot to components - active = true; + if( is_food() ) { + active = true; + last_temp_check = bday; + if( goes_bad() ) { + set_relative_rot( get_most_rotten_component( *this )->get_relative_rot() ); + } + } for( const item &it : components ) { if( it.has_flag( "HIDDEN_POISON" ) ) { @@ -602,9 +620,9 @@ bool item::stacks_with( const item &rhs, bool check_components ) const // Stack items that fall into the same "bucket" of freshness. // Distant buckets are larger than near ones. std::pair my_clipped_time_to_rot = - clipped_time( type->comestible->spoils - rot ); + clipped_time( get_comestible()->spoils - rot ); std::pair other_clipped_time_to_rot = - clipped_time( rhs.type->comestible->spoils - rhs.rot ); + clipped_time( rhs.get_comestible()->spoils - rhs.rot ); if( my_clipped_time_to_rot != other_clipped_time_to_rot ) { return false; } @@ -817,7 +835,7 @@ std::string get_freshness_description( const item &food_item ) // can guess its age as one of {quite fresh,midlife,past midlife,old soon}, and also // guess about how long until it spoils. const double rot_progress = food_item.get_relative_rot(); - const time_duration shelf_life = food_item.type->comestible->spoils; + const time_duration shelf_life = food_item.get_comestible()->spoils; time_duration time_left = shelf_life - ( shelf_life * rot_progress ); // Correct for an estimate that exceeds shelf life -- this happens especially with @@ -1040,7 +1058,7 @@ std::string item::info( std::vector &info, const iteminfo_query *parts to_turns( food->rot ) ) ); info.push_back( iteminfo( "BASE", space + _( "max rot: " ), "", iteminfo::lower_is_better, - to_turns( food->type->comestible->spoils ) ) ); + to_turns( food->get_comestible()->spoils ) ) ); info.push_back( iteminfo( "BASE", _( "last rot: " ), "", iteminfo::lower_is_better, to_turn( food->last_rot_check ) ) ); @@ -1054,11 +1072,11 @@ std::string item::info( std::vector &info, const iteminfo_query *parts info.push_back( iteminfo( "BASE", _( "Spec ener: " ), "", iteminfo::lower_is_better, food->specific_energy ) ); info.push_back( iteminfo( "BASE", _( "Spec heat lq: " ), "", iteminfo::lower_is_better, - 1000 * food->type->comestible->specific_heat_liquid ) ); + 1000 * food->get_comestible()->specific_heat_liquid ) ); info.push_back( iteminfo( "BASE", _( "Spec heat sld: " ), "", iteminfo::lower_is_better, - 1000 * food->type->comestible->specific_heat_solid ) ); + 1000 * food->get_comestible()->specific_heat_solid ) ); info.push_back( iteminfo( "BASE", _( "latent heat: " ), "", iteminfo::lower_is_better, - food->type->comestible->latent_heat ) ); + food->get_comestible()->latent_heat ) ); } } info.push_back( iteminfo( "BASE", _( "burn: " ), "", iteminfo::lower_is_better, @@ -1073,7 +1091,7 @@ std::string item::info( std::vector &info, const iteminfo_query *parts med_item = &contents.front(); } if( med_item != nullptr ) { - const auto &med_com = med_item->type->comestible; + const auto &med_com = med_item->get_comestible(); if( med_com->quench != 0 && parts->test( iteminfo_parts::MED_QUENCH ) ) { info.push_back( iteminfo( "MED", _( "Quench: " ), med_com->quench ) ); } @@ -1106,7 +1124,7 @@ std::string item::info( std::vector &info, const iteminfo_query *parts food_item = &contents.front(); } if( food_item != nullptr ) { - if( g->u.kcal_for( *food_item ) != 0 || food_item->type->comestible->quench != 0 ) { + if( g->u.kcal_for( *food_item ) != 0 || food_item->get_comestible()->quench != 0 ) { if( parts->test( iteminfo_parts::FOOD_NUTRITION ) ) { auto value = g->u.kcal_for( *food_item ); info.push_back( iteminfo( "FOOD", _( "Calories (kcal): " ), @@ -1114,11 +1132,11 @@ std::string item::info( std::vector &info, const iteminfo_query *parts } if( parts->test( iteminfo_parts::FOOD_QUENCH ) ) { info.push_back( iteminfo( "FOOD", space + _( "Quench: " ), - food_item->type->comestible->quench ) ); + food_item->get_comestible()->quench ) ); } } - if( food_item->type->comestible->fun != 0 && parts->test( iteminfo_parts::FOOD_JOY ) ) { + if( food_item->get_comestible()->fun != 0 && parts->test( iteminfo_parts::FOOD_JOY ) ) { info.push_back( iteminfo( "FOOD", _( "Enjoyability: " ), g->u.fun_for( *food_item ).first ) ); } @@ -1174,7 +1192,7 @@ std::string item::info( std::vector &info, const iteminfo_query *parts } if( food_item->goes_bad() && parts->test( iteminfo_parts::FOOD_ROT ) ) { - const std::string rot_time = to_string_clipped( food_item->type->comestible->spoils ); + const std::string rot_time = to_string_clipped( food_item->get_comestible()->spoils ); info.emplace_back( "DESCRIPTION", string_format( _( "* This food is perishable, and at room temperature has an estimated nominal shelf life of %s." ), @@ -2612,22 +2630,6 @@ const std::string &item::symbol() const return type->sym; } -// Used for craft entity -static const item *get_most_rotten_component( const item &craft ) -{ - const item *most_rotten = nullptr; - for( const item &it : craft.components ) { - if( it.goes_bad() ) { - if( most_rotten == nullptr ) { - most_rotten = ⁢ - } else if( it.get_relative_rot() > most_rotten->get_relative_rot() ) { - most_rotten = ⁢ - } - } - } - return most_rotten; -} - nc_color item::color_in_inventory() const { player &u = g->u; // TODO: make a const reference @@ -2644,7 +2646,7 @@ nc_color item::color_in_inventory() const } else if( has_flag( "LEAK_DAM" ) && has_flag( "RADIOACTIVE" ) && damage() > 0 ) { ret = c_light_green; - } else if( active && !is_food() && !is_food_container() && !is_craft() ) { + } else if( active && !is_food() && !is_food_container() ) { // Active items show up as yellow ret = c_yellow; } else if( is_food() || is_food_container() ) { @@ -2687,11 +2689,6 @@ nc_color item::color_in_inventory() const case NO_TOOL: break; } - } else if( is_craft() ) { - const item *most_rotten = get_most_rotten_component( *this ); - if( most_rotten ) { - ret = most_rotten->color_in_inventory(); - } } else if( is_gun() ) { // Guns are green if you are carrying ammo for them // ltred if you have ammo but no mags @@ -2886,26 +2883,6 @@ void item::on_damage( int, damage_type ) } -static std::string get_dirt_rot_suffixes( const item &it ) -{ - std::stringstream ret; - ret.str( "" ); - - if( it.item_tags.count( "DIRTY" ) ) { - ret << _( " (dirty)" ); - } else if( it.rotten() ) { - ret << _( " (rotten)" ); - } else if( it.has_flag( "MUSHY" ) ) { - ret << _( " (mushy)" ); - } else if( it.is_going_bad() ) { - ret << _( " (old)" ); - } else if( it.is_fresh() ) { - ret << _( " (fresh)" ); - } - - return ret.str(); -} - std::string item::tname( unsigned int quantity, bool with_prefix, unsigned int truncate ) const { std::stringstream ret; @@ -3003,7 +2980,7 @@ std::string item::tname( unsigned int quantity, bool with_prefix, unsigned int t ret << " (" << charges << ")"; } const int percent_progress = 100 * item_counter / making->time; - ret << " (" << percent_progress << "%)"; + ret << " (" << percent_progress << "%)"; maintext = ret.str(); } else if( contents.size() == 1 ) { const item &contents_item = contents.front(); @@ -3032,7 +3009,17 @@ std::string item::tname( unsigned int quantity, bool with_prefix, unsigned int t ret << _( " (hallucinogenic)" ); } - ret << get_dirt_rot_suffixes( *this ); + if( item_tags.count( "DIRTY" ) ) { + ret << _( " (dirty)" ); + } else if( rotten() ) { + ret << _( " (rotten)" ); + } else if( has_flag( "MUSHY" ) ) { + ret << _( " (mushy)" ); + } else if( is_going_bad() ) { + ret << _( " (old)" ); + } else if( is_fresh() ) { + ret << _( " (fresh)" ); + } if( has_flag( "HOT" ) ) { ret << _( " (hot)" ); @@ -3047,18 +3034,6 @@ std::string item::tname( unsigned int quantity, bool with_prefix, unsigned int t } } - if( is_craft() ) { - if( has_flag( "HIDDEN_POISON" ) && g->u.get_skill_level( skill_survival ) >= 3 ) { - ret << _( " (poisonous)" ); - } else if( has_flag( "HIDDEN_HALLU" ) && g->u.get_skill_level( skill_survival ) >= 5 ) { - ret << _( " (hallucinogenic)" ); - } - const item *most_rotten = get_most_rotten_component( *this ); - if( most_rotten ) { - ret << get_dirt_rot_suffixes( *most_rotten ); - } - } - const bool small = g->u.has_trait( trait_id( "SMALL2" ) ) || g->u.has_trait( trait_id( "SMALL_OK" ) ); const bool fits = has_flag( "FIT" ); @@ -3107,7 +3082,7 @@ std::string item::tname( unsigned int quantity, bool with_prefix, unsigned int t } if( active && ( has_flag( "WATER_EXTINGUISH" ) || has_flag( "LITCIG" ) ) ) { ret << _( " (lit)" ); - } else if( active && !is_food() && !is_corpse() && !is_craft() && ( typeId().length() < 3 || + } else if( active && !is_food() && !is_corpse() && ( typeId().length() < 3 || typeId().compare( typeId().length() - 3, 3, "_on" ) != 0 ) ) { // Usually the items whose ids end in "_on" have the "active" or "on" string already contained // in their name, also food is active while it rots. @@ -3276,15 +3251,15 @@ units::mass item::weight( bool include_contents ) const return 0_gram; } + units::mass ret = 0_gram; + if( is_craft() ) { - units::mass ret = 0_gram; - for( auto it : components ) { - ret += it.weight(); - } - return ret; + ret = units::from_gram( get_var( "weight", to_gram( find_type( making->result() )->weight ) ) ); + } else { + ret = units::from_gram( get_var( "weight", to_gram( type->weight ) ) ); } - units::mass ret = units::from_gram( get_var( "weight", to_gram( type->weight ) ) ); + if( has_flag( "REDUCED_WEIGHT" ) ) { ret *= 0.75; } @@ -3371,11 +3346,7 @@ units::volume item::base_volume() const return corpse_volume( corpse ); } if( is_craft() ) { - units::volume ret = 0_ml; - for( auto it : components ) { - ret += it.base_volume(); - } - return ret; + return find_type( making->result() )->volume; } if( count_by_charges() ) { @@ -3400,11 +3371,7 @@ units::volume item::volume( bool integral ) const } if( is_craft() ) { - units::volume ret = 0_ml; - for( auto it : components ) { - ret += it.volume(); - } - return ret; + return find_type( making->result() )->volume; } const int local_volume = get_var( "volume", -1 ); @@ -3730,18 +3697,18 @@ std::set item::get_techniques() const bool item::goes_bad() const { - return is_food() && type->comestible->spoils != 0_turns; + return is_food() && get_comestible()->spoils != 0_turns; } double item::get_relative_rot() const { - return goes_bad() ? rot / type->comestible->spoils : 0; + return goes_bad() ? rot / get_comestible()->spoils : 0; } void item::set_relative_rot( double val ) { if( goes_bad() ) { - rot = type->comestible->spoils * val; + rot = get_comestible()->spoils * val; // calc_rot uses last_rot_check (when it's not time_of_cataclysm) instead of bday. // this makes sure the rotting starts from now, not from bday. // if this item is the result of smoking don't do this, we want to start from bday. @@ -3767,10 +3734,10 @@ int item::spoilage_sort_order() } if( subject->goes_bad() ) { - return to_turns( subject->type->comestible->spoils - subject->rot ); + return to_turns( subject->get_comestible()->spoils - subject->rot ); } - if( subject->type->comestible ) { + if( subject->get_comestible() ) { if( subject->get_category().id() == "food" ) { return bottom - 3; } else if( subject->get_category().id() == "drugs" ) { @@ -3816,7 +3783,7 @@ void item::calc_rot( const tripoint &location ) // positive = food was produced some time before calendar::start and/or bad storage // negative = food was stored in good conditions before calendar::start if( since <= calendar::start && goes_bad() ) { - time_duration spoil_variation = type->comestible->spoils * 0.2f; + time_duration spoil_variation = get_comestible()->spoils * 0.2f; rot += factor * rng( -spoil_variation, spoil_variation ); } @@ -4577,18 +4544,18 @@ bool item::is_ammo() const bool item::is_comestible() const { - return type->comestible.has_value(); + return get_comestible().has_value(); } bool item::is_food() const { - return is_comestible() && ( type->comestible->comesttype == "FOOD" || - type->comestible->comesttype == "DRINK" ); + return is_comestible() && ( get_comestible()->comesttype == "FOOD" || + get_comestible()->comesttype == "DRINK" ); } bool item::is_medication() const { - return is_comestible() && type->comestible->comesttype == "MED"; + return is_comestible() && get_comestible()->comesttype == "MED"; } bool item::is_brewable() const @@ -4598,7 +4565,8 @@ bool item::is_brewable() const bool item::is_food_container() const { - return !contents.empty() && contents.front().is_food(); + return ( !contents.empty() && contents.front().is_food() ) || ( is_craft() && + making->create_result().is_food_container() ); } bool item::is_med_container() const @@ -6368,10 +6336,10 @@ bool item::allow_crafting_component() const void item::set_item_specific_energy( const float new_specific_energy ) { - const float specific_heat_liquid = type->comestible->specific_heat_liquid; // J/g K - const float specific_heat_solid = type->comestible->specific_heat_solid; // J/g K - const float latent_heat = type->comestible->latent_heat; // J/kg - const float freezing_temperature = temp_to_kelvin( type->comestible->freeze_point ); // K + const float specific_heat_liquid = get_comestible()->specific_heat_liquid; // J/g K + const float specific_heat_solid = get_comestible()->specific_heat_solid; // J/g K + const float latent_heat = get_comestible()->latent_heat; // J/kg + const float freezing_temperature = temp_to_kelvin( get_comestible()->freeze_point ); // K const float completely_frozen_specific_energy = specific_heat_solid * freezing_temperature; // Energy that the item would have if it was completely solid at freezing temperature const float completely_liquid_specific_energy = completely_frozen_specific_energy + @@ -6418,7 +6386,7 @@ void item::set_item_specific_energy( const float new_specific_energy ) item_tags.insert( "FROZEN" ); current_phase = SOLID; // If below freezing temp AND the food may have parasites AND food does not have "NO_PARASITES" tag then add the "NO_PARASITES" tag. - if( new_item_temperature < freezing_temperature && type->comestible->parasites > 0 ) { + if( new_item_temperature < freezing_temperature && get_comestible()->parasites > 0 ) { if( !( item_tags.count( "NO_PARASITES" ) ) ) { item_tags.insert( "NO_PARASITES" ); } @@ -6433,10 +6401,10 @@ void item::set_item_specific_energy( const float new_specific_energy ) float item::get_specific_energy_from_temperature( const float new_temperature ) { - const float specific_heat_liquid = type->comestible->specific_heat_liquid; // J/g K - const float specific_heat_solid = type->comestible->specific_heat_solid; // J/g K - const float latent_heat = type->comestible->latent_heat; // J/kg - const float freezing_temperature = temp_to_kelvin( type->comestible->freeze_point ); // K + const float specific_heat_liquid = get_comestible()->specific_heat_liquid; // J/g K + const float specific_heat_solid = get_comestible()->specific_heat_solid; // J/g K + const float latent_heat = get_comestible()->latent_heat; // J/kg + const float freezing_temperature = temp_to_kelvin( get_comestible()->freeze_point ); // K const float completely_frozen_energy = specific_heat_solid * freezing_temperature; // Energy that the item would have if it was completely solid at freezing temperature const float completely_liquid_energy = completely_frozen_energy + @@ -6454,9 +6422,9 @@ float item::get_specific_energy_from_temperature( const float new_temperature ) void item::set_item_temperature( float new_temperature ) { - const float freezing_temperature = temp_to_kelvin( type->comestible->freeze_point ); // K - const float specific_heat_solid = type->comestible->specific_heat_solid; // J/g K - const float latent_heat = type->comestible->latent_heat; // J/kg + const float freezing_temperature = temp_to_kelvin( get_comestible()->freeze_point ); // K + const float specific_heat_solid = get_comestible()->specific_heat_solid; // J/g K + const float latent_heat = get_comestible()->latent_heat; // J/kg float new_specific_energy = get_specific_energy_from_temperature( new_temperature ); float freeze_percentage = 0; @@ -6495,7 +6463,7 @@ void item::set_item_temperature( float new_temperature ) item_tags.insert( "FROZEN" ); current_phase = SOLID; // If below freezing temp AND the food may have parasites AND food does not have "NO_PARASITES" tag then add the "NO_PARASITES" tag. - if( new_temperature < freezing_temperature && type->comestible->parasites > 0 ) { + if( new_temperature < freezing_temperature && get_comestible()->parasites > 0 ) { if( !( item_tags.count( "NO_PARASITES" ) ) ) { item_tags.insert( "NO_PARASITES" ); } @@ -6825,12 +6793,12 @@ bool item::needs_processing() const { return active || has_flag( "RADIO_ACTIVATION" ) || ( is_container() && !contents.empty() && contents.front().needs_processing() ) || - is_artifact() || is_food() || is_craft(); + is_artifact() || is_food(); } int item::processing_speed() const { - if( is_corpse() || is_food() || is_food_container() || is_craft() ) { + if( is_corpse() || is_food() || is_food_container() ) { return 100; } // Unless otherwise indicated, update every turn. @@ -6895,10 +6863,10 @@ void item::calc_temp( const int temp, const float insulation, const time_duratio // temperature = item temperature (10e-5 K). Stored in the item const float conductivity_term = 0.046 * std::pow( to_milliliter( volume() ), 2.0 / 3.0 ) / insulation; - const float specific_heat_liquid = type->comestible->specific_heat_liquid; - const float specific_heat_solid = type->comestible->specific_heat_solid; - const float latent_heat = type->comestible->latent_heat; - const float freezing_temperature = temp_to_kelvin( type->comestible->freeze_point ); // K + const float specific_heat_liquid = get_comestible()->specific_heat_liquid; + const float specific_heat_solid = get_comestible()->specific_heat_solid; + const float latent_heat = get_comestible()->latent_heat; + const float freezing_temperature = temp_to_kelvin( get_comestible()->freeze_point ); // K const float completely_frozen_specific_energy = specific_heat_solid * freezing_temperature; // Energy that the item would have if it was completely solid at freezing temperature const float completely_liquid_specific_energy = completely_frozen_specific_energy + @@ -7032,7 +7000,7 @@ void item::calc_temp( const int temp, const float insulation, const time_duratio item_tags.insert( "FROZEN" ); current_phase = SOLID; // If below freezing temp AND the food may have parasites AND food does not have "NO_PARASITES" tag then add the "NO_PARASITES" tag. - if( new_item_temperature < freezing_temperature && type->comestible->parasites > 0 ) { + if( new_item_temperature < freezing_temperature && get_comestible()->parasites > 0 ) { if( !( item_tags.count( "NO_PARASITES" ) ) ) { item_tags.insert( "NO_PARASITES" ); } @@ -7097,21 +7065,6 @@ bool item::process_food( const player *carrier, const tripoint &p, int temp, flo return false; } -bool item::process_craft( const player *carrier, const tripoint &p, int temp, float insulation ) -{ - if( carrier != nullptr && carrier->has_item( *this ) ) { - temp += 5; // body heat increases inventory temperature - insulation *= 1.5; // clothing provides inventory some level of insulation - } - - // Process all stored components - // Already applied body heat, so pass nullptr instead of carrier - for( item &it : components ) { - it.process( nullptr, p, false, temp, insulation ); - } - return false; -} - void item::process_artifact( player *carrier, const tripoint & /*pos*/ ) { if( !is_artifact() ) { @@ -7460,7 +7413,7 @@ bool item::process_tool( player *carrier, const tripoint &pos ) bool item::process( player *carrier, const tripoint &pos, bool activate ) { - if( is_food() || is_food_container() || is_craft() ) { + if( is_food() || is_food_container() ) { return process( carrier, pos, activate, g->get_temperature( pos ), 1 ); } else { return process( carrier, pos, activate, 0, 1 ); @@ -7500,7 +7453,7 @@ bool item::process( player *carrier, const tripoint &pos, bool activate, int tem return false; } - if( !is_food() && !is_craft() && item_counter > 0 ) { + if( !is_food() && item_counter > 0 ) { item_counter--; } @@ -7521,9 +7474,6 @@ bool item::process( player *carrier, const tripoint &pos, bool activate, int tem if( is_food() && process_food( carrier, pos, temp, insulation ) ) { return true; } - if( is_craft() && process_craft( carrier, pos, temp, insulation ) ) { - return true; - } if( is_corpse() && process_corpse( carrier, pos ) ) { return true; } @@ -7903,3 +7853,9 @@ const recipe &item::get_making() const } return *making; } + +cata::optional item::get_comestible() const +{ + return is_craft() ? find_type( making->result() )->comestible : + type->comestible; +} diff --git a/src/item.h b/src/item.h index a411ee7ef13b8..b97c101fb5141 100644 --- a/src/item.h +++ b/src/item.h @@ -42,6 +42,7 @@ class player; class npc; class recipe; struct itype; +struct islot_comestible; struct mtype; using mtype_id = string_id; using bodytype_id = std::string; @@ -1813,6 +1814,8 @@ class item : public visitable const recipe &get_making() const; + cata::optional get_comestible() const; + private: /** * Calculate the thermal energy and temperature change of the item @@ -1841,7 +1844,6 @@ class item : public visitable // processing types, just to make the process function cleaner. // The interface is the same as for @ref process. bool process_food( const player *carrier, const tripoint &p, int temp, float insulation ); - bool process_craft( const player *carrier, const tripoint &p, int temp, float insulation ); bool process_corpse( player *carrier, const tripoint &pos ); bool process_wet( player *carrier, const tripoint &pos ); bool process_litcig( player *carrier, const tripoint &pos ); diff --git a/src/iuse.cpp b/src/iuse.cpp index 48830644a8974..37a184f2ae6c8 100644 --- a/src/iuse.cpp +++ b/src/iuse.cpp @@ -287,14 +287,14 @@ int iuse::xanax( player *p, item *it, bool, const tripoint & ) int iuse::caff( player *p, item *it, bool, const tripoint & ) { - p->mod_fatigue( -( it->type->comestible ? it->type->comestible->stim : 0 ) * 3 ); + p->mod_fatigue( -( it->get_comestible() ? it->get_comestible()->stim : 0 ) * 3 ); return it->type->charges_to_use(); } int iuse::atomic_caff( player *p, item *it, bool, const tripoint & ) { p->add_msg_if_player( m_good, _( "Wow! This %s has a kick." ), it->tname().c_str() ); - p->mod_fatigue( -( it->type->comestible ? it->type->comestible->stim : 0 ) * 12 ); + p->mod_fatigue( -( it->get_comestible() ? it->get_comestible()->stim : 0 ) * 12 ); p->irradiate( 8, true ); return it->type->charges_to_use(); } @@ -316,9 +316,9 @@ int alcohol( player &p, const item &it, const int strength ) 6_turns, 10_turns, 10_turns ) * p.str_max ); // Metabolizing the booze improves the nutritional value; // might not be healthy, and still causes Thirst problems, though - p.mod_hunger( -( abs( it.type->comestible ? it.type->comestible->stim : 0 ) ) ); + p.mod_hunger( -( abs( it.get_comestible() ? it.get_comestible()->stim : 0 ) ) ); // Metabolizing it cancels out the depressant - p.stim += abs( it.type->comestible ? it.type->comestible->stim : 0 ); + p.stim += abs( it.get_comestible() ? it.get_comestible()->stim : 0 ); } else if( p.has_trait( trait_TOLERANCE ) ) { duration -= alc_strength( strength, 12_minutes, 30_minutes, 45_minutes ); } else if( p.has_trait( trait_LIGHTWEIGHT ) ) { @@ -885,12 +885,12 @@ int iuse::blech( player *p, item *it, bool, const tripoint & ) //reverse the harmful values of drinking this acid. double multiplier = -1; p->mod_hunger( -p->nutrition_for( *it ) * multiplier ); - p->mod_thirst( -it->type->comestible->quench * multiplier ); + p->mod_thirst( -it->get_comestible()->quench * multiplier ); p->mod_thirst( -20 ); //acidproof people can drink acids like diluted water. p->mod_stomach_water( 20 ); - p->mod_healthy_mod( it->type->comestible->healthy * multiplier, - it->type->comestible->healthy * multiplier ); - p->add_morale( MORALE_FOOD_BAD, it->type->comestible->fun * multiplier, 60, 1_hours, 30_minutes, + p->mod_healthy_mod( it->get_comestible()->healthy * multiplier, + it->get_comestible()->healthy * multiplier ); + p->add_morale( MORALE_FOOD_BAD, it->get_comestible()->fun * multiplier, 60, 1_hours, 30_minutes, false, it->type ); } else { p->add_msg_if_player( m_bad, _( "Blech, that burns your throat!" ) ); @@ -915,9 +915,9 @@ int iuse::plantblech( player *p, item *it, bool, const tripoint &pos ) //reverses the harmful values of drinking fertilizer p->mod_hunger( p->nutrition_for( *it ) * multiplier ); - p->mod_thirst( -it->type->comestible->quench * multiplier ); - p->mod_healthy_mod( it->type->comestible->healthy * multiplier, - it->type->comestible->healthy * multiplier ); + p->mod_thirst( -it->get_comestible()->quench * multiplier ); + p->mod_healthy_mod( it->get_comestible()->healthy * multiplier, + it->get_comestible()->healthy * multiplier ); p->add_morale( MORALE_FOOD_GOOD, -10 * multiplier, 60, 1_hours, 30_minutes, false, it->type ); return it->type->charges_to_use(); } else { diff --git a/src/map.cpp b/src/map.cpp index 20695a2f00c4a..ec41f45e80d4a 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -6632,7 +6632,7 @@ void map::rotten_item_spawn( const item &item, const tripoint &pnt ) if( g->critter_at( pnt ) != nullptr ) { return; } - auto &comest = item.type->comestible; + const auto &comest = item.get_comestible(); mongroup_id mgroup = comest->rot_spawn; if( mgroup == "GROUP_NULL" ) { return; diff --git a/src/monattack.cpp b/src/monattack.cpp index 3ea741b30bc19..f77f422128a5c 100644 --- a/src/monattack.cpp +++ b/src/monattack.cpp @@ -239,7 +239,7 @@ bool mattack::eat_food( monster *z ) auto items = g->m.i_at( p ); for( auto &item : items ) { //Fun limit prevents scavengers from eating feces - if( !item.is_food() || item.type->comestible->fun < -20 ) { + if( !item.is_food() || item.get_comestible()->fun < -20 ) { continue; } //Don't eat own eggs diff --git a/src/npc.cpp b/src/npc.cpp index b3816d6a0ee7e..6604e57394aa2 100644 --- a/src/npc.cpp +++ b/src/npc.cpp @@ -1386,10 +1386,10 @@ void npc::decide_needs() item inventory_item = i->front(); if( inventory_item.is_food( ) ) { needrank[ need_food ] += nutrition_for( inventory_item ) / 4; - needrank[ need_drink ] += inventory_item.type->comestible->quench / 4; + needrank[ need_drink ] += inventory_item.get_comestible()->quench / 4; } else if( inventory_item.is_food_container() ) { needrank[ need_food ] += nutrition_for( inventory_item.contents.front() ) / 4; - needrank[ need_drink ] += inventory_item.contents.front().type->comestible->quench / 4; + needrank[ need_drink ] += inventory_item.contents.front().get_comestible()->quench / 4; } } needs.clear(); @@ -1550,14 +1550,14 @@ int npc::value( const item &it, int market_price ) const if( it.is_food() ) { int comestval = 0; - if( nutrition_for( it ) > 0 || it.type->comestible->quench > 0 ) { + if( nutrition_for( it ) > 0 || it.get_comestible()->quench > 0 ) { comestval++; } if( get_hunger() > 40 ) { comestval += ( nutrition_for( it ) + get_hunger() - 40 ) / 6; } if( get_thirst() > 40 ) { - comestval += ( it.type->comestible->quench + get_thirst() - 40 ) / 4; + comestval += ( it.get_comestible()->quench + get_thirst() - 40 ) / 4; } if( comestval > 0 && will_eat( it ).success() ) { ret += comestval; @@ -1593,11 +1593,11 @@ int npc::value( const item &it, int market_price ) const // TODO: Artifact hunting from relevant factions // ALSO TODO: Bionics hunting from relevant factions - if( fac_has_job( FACJOB_DRUGS ) && it.is_food() && it.type->comestible->addict >= 5 ) { + if( fac_has_job( FACJOB_DRUGS ) && it.is_food() && it.get_comestible()->addict >= 5 ) { ret += 10; } - if( fac_has_job( FACJOB_DOCTORS ) && it.is_food() && it.type->comestible->comesttype == "MED" ) { + if( fac_has_job( FACJOB_DOCTORS ) && it.is_food() && it.get_comestible()->comesttype == "MED" ) { ret += 10; } @@ -2389,8 +2389,8 @@ bool npc::will_accept_from_player( const item &it ) const return false; } - if( const auto &comest = it.is_container() ? it.get_contained().type->comestible : - it.type->comestible ) { + if( const auto &comest = it.is_container() ? it.get_contained().get_comestible() : + it.get_comestible() ) { if( comest->fun < 0 || it.poison > 0 ) { return false; } diff --git a/src/npcmove.cpp b/src/npcmove.cpp index 9b6cccd18597c..b221abb305f4c 100644 --- a/src/npcmove.cpp +++ b/src/npcmove.cpp @@ -2776,7 +2776,7 @@ void npc::use_painkiller() // Be eaten before it rots (favor soon-to-rot perishables) float rate_food( const item &it, int want_nutr, int want_quench ) { - const auto &food = it.type->comestible; + const auto &food = it.get_comestible(); if( !food ) { return 0.0f; } diff --git a/src/npctalk.cpp b/src/npctalk.cpp index 8b04a4f6ba9af..4998a10078928 100644 --- a/src/npctalk.cpp +++ b/src/npctalk.cpp @@ -2998,7 +2998,7 @@ consumption_result try_consume( npc &p, item &it, std::string &reason ) // TODO: Unify this with 'player::consume_item()' bool consuming_contents = it.is_container() && !it.contents.empty(); item &to_eat = consuming_contents ? it.contents.front() : it; - const auto &comest = to_eat.type->comestible; + const auto &comest = to_eat.get_comestible(); if( !comest ) { // Don't inform the player that we don't want to eat the lighter return REFUSED; diff --git a/src/player.cpp b/src/player.cpp index 2813fc6f0fd57..1b742d42ec26e 100644 --- a/src/player.cpp +++ b/src/player.cpp @@ -7271,7 +7271,7 @@ bool player::consume_med( item &target ) return false; } - const itype_id tool_type = target.type->comestible->tool; + const itype_id tool_type = target.get_comestible()->tool; const auto req_tool = item::find_type( tool_type ); bool tool_override = false; if( tool_type == "syringe" && has_bionic( bio_syringe ) ) { @@ -7315,7 +7315,7 @@ bool player::consume_item( item &target ) item &comest = get_comestible_from( target ); - if( comest.is_null() ) { + if( comest.is_null() || target.is_craft() ) { add_msg_if_player( m_info, _( "You can't eat your %s." ), target.tname().c_str() ); if( is_npc() ) { debugmsg( "%s tried to eat a %s", name.c_str(), target.tname().c_str() ); @@ -9085,10 +9085,10 @@ void player::use( item_location loc ) } invoke_item( &used, loc.position() ); - } else if( used.is_food() || - used.is_medication() || - used.get_contained().is_food() || - used.get_contained().is_medication() ) { + } else if( !used.is_craft() && ( used.is_food() || + used.is_medication() || + used.get_contained().is_food() || + used.get_contained().is_medication() ) ) { consume( inventory_position ); } else if( used.is_book() ) {