Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor item components to be stored as a map #62498

Merged
merged 1 commit into from
Mar 10, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/activity_item_handling.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1342,7 +1342,7 @@ static activity_reason_info can_do_activity_there( const activity_id &act, Chara
if( i.is_disassemblable() ) {
// Are the requirements fulfilled?
const recipe &r = recipe_dictionary::get_uncraft( ( i.typeId() == itype_disassembly ) ?
i.components.front().typeId() : i.typeId() );
i.components.only_item().typeId() : i.typeId() );
req = r.disassembly_requirements();
if( !std::all_of( req.get_qualities().begin(),
req.get_qualities().end(), [&inv]( const std::vector<quality_requirement> &cur ) {
Expand Down
16 changes: 9 additions & 7 deletions src/consumption.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -276,13 +276,15 @@ nutrients Character::compute_effective_nutrients( const item &comest ) const
// Avoid division by zero
return tally;
}
for( const item &component : comest.components ) {
nutrients component_value =
compute_effective_nutrients( component ) * component.charges;
if( component.has_flag( flag_BYPRODUCT ) ) {
tally -= component_value;
} else {
tally += component_value;
for( const item_components::type_vector_pair &tvp : comest.components ) {
for( const item &component : tvp.second ) {
nutrients component_value =
compute_effective_nutrients( component ) * component.charges;
if( component.has_flag( flag_BYPRODUCT ) ) {
tally -= component_value;
} else {
tally += component_value;
}
}
}
return tally / comest.recipe_charges;
Expand Down
6 changes: 4 additions & 2 deletions src/craft_command.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -414,7 +414,7 @@ bool craft_command::safe_to_unload_comp( const item &it )
item craft_command::create_in_progress_craft()
{
// Use up the components and tools
std::list<item> used;
item_components used;
std::vector<item_comp> comps_used;
if( crafter->has_trait( trait_DEBUG_HS ) ) {
return item( rec, batch_size, used, comps_used );
Expand Down Expand Up @@ -449,7 +449,9 @@ item craft_command::create_in_progress_craft()
unload_activity_actor::unload( *crafter, tmp_loc );
}
}
used.splice( used.end(), tmp );
for( item &it : tmp ) {
used.add( it );
}
}

for( const comp_selection<item_comp> &selection : item_selections ) {
Expand Down
196 changes: 81 additions & 115 deletions src/crafting.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -650,32 +650,6 @@ void Character::make_craft_with_command( const recipe_id &id_to_make, int batch_
last_craft->execute();
}

// @param offset is the index of the created item in the range [0, batch_size-1],
// it makes sure that the used items are distributed equally among the new items.
static void set_components( std::list<item> &components, const std::list<item> &used,
const int batch_size, const size_t offset )
{
if( batch_size <= 1 ) {
components.insert( components.begin(), used.begin(), used.end() );
return;
}
// This count does *not* include items counted by charges!
size_t non_charges_counter = 0;
for( const item &tmp : used ) {
if( tmp.count_by_charges() ) {
components.push_back( tmp );
// This assumes all (count-by-charges) items of the same type have been merged into one,
// which has a charges value that can be evenly divided by batch_size.
components.back().charges = tmp.charges / batch_size;
} else {
if( ( non_charges_counter + offset ) % batch_size == 0 ) {
components.push_back( tmp );
}
non_charges_counter++;
}
}
}

static cata::optional<item_location> wield_craft( Character &p, item &craft )
{
if( p.wield( craft ) ) {
Expand Down Expand Up @@ -1288,7 +1262,7 @@ static void destroy_random_component( item &craft, const Character &crafter )
return;
}

item destroyed = random_entry_removed( craft.components );
item destroyed = craft.components.get_and_remove_random_entry();

crafter.add_msg_player_or_npc( game_message_params( game_message_type::m_bad ),
_( "You mess up and destroy the %s." ),
Expand Down Expand Up @@ -1379,10 +1353,12 @@ void item::inherit_flags( const item &parent, const recipe &making )
}
}

void item::inherit_flags( const std::list<item> &parents, const recipe &making )
void item::inherit_flags( const item_components &parents, const recipe &making )
{
for( const item &parent : parents ) {
inherit_flags( parent, making );
for( const item_components::type_vector_pair &tvp : parents ) {
for( const item &parent : tvp.second ) {
inherit_flags( parent, making );
}
}
}

Expand All @@ -1395,7 +1371,7 @@ void Character::complete_craft( item &craft, const cata::optional<tripoint> &loc

const recipe &making = craft.get_making();
const int batch_size = craft.get_making_batch_size();
std::list<item> &used = craft.components;
item_components &used = craft.components;
const double relative_rot = craft.get_relative_rot();
const bool should_heat = making.hot_result();
const bool remove_raw = making.removes_raw();
Expand Down Expand Up @@ -1457,49 +1433,39 @@ void Character::complete_craft( item &craft, const cata::optional<tripoint> &loc
// Setting this for items counted by charges gives only problems:
// those items are automatically merged everywhere (map/vehicle/inventory),
// which would either lose this information or merge it somehow.
set_components( food_contained.components, used, batch_size, newit_counter );
food_contained.components = used.split( batch_size, newit_counter );
newit_counter++;
} else if( food_contained.is_food() && !food_contained.has_flag( flag_NUTRIENT_OVERRIDE ) ) {
// use a copy of the used list so that the byproducts don't build up over iterations (#38071)
std::list<item> usedbp;
item_components usedbp;

// 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.get_comestible()->cooks_like.is_empty() ) {
const double relative_rot = comp.get_relative_rot();
comp = item( comp.get_comestible()->cooks_like, comp.birthday(), comp.charges );
comp.set_relative_rot( relative_rot );
}
// If this recipe is cooked, components are no longer raw.
if( should_heat || remove_raw ) {
comp.set_flag_recursive( flag_COOKED );
}

// when batch crafting, set_components depends on components being merged, so merge any unmerged ones here
if( comp.count_by_charges() ) {
auto it = std::find_if( usedbp.begin(), usedbp.end(), [&comp]( const item & usedit ) {
return usedit.type == comp.type;
} );

if( it != usedbp.end() ) {
it->charges += comp.charges;
continue;
for( item_components::type_vector_pair &tvp : used ) {
for( item &comp : tvp.second ) {
// only comestibles have cooks_like. any other type of item will throw an exception, so filter those out
if( comp.is_comestible() && !comp.get_comestible()->cooks_like.is_empty() ) {
const double relative_rot = comp.get_relative_rot();
comp = item( comp.get_comestible()->cooks_like, comp.birthday(), comp.charges );
comp.set_relative_rot( relative_rot );
}
// If this recipe is cooked, components are no longer raw.
if( should_heat || remove_raw ) {
comp.set_flag_recursive( flag_COOKED );
}
}

usedbp.emplace_back( comp );
usedbp.add( comp );
}
}

// byproducts get stored as a "component" but with a byproduct flag for consumption purposes
if( making.has_byproducts() ) {
for( item &byproduct : making.create_byproducts( batch_size ) ) {
byproduct.set_flag( flag_BYPRODUCT );
usedbp.push_back( byproduct );
usedbp.add( byproduct );
}
}
// store components for food recipes that do not have the override flag
set_components( food_contained.components, usedbp, batch_size, newit_counter );
food_contained.components = usedbp.split( batch_size, newit_counter );

// store the number of charges the recipe would create with batch size 1.
if( &newit != &food_contained ) { // If a canned/contained item was crafted…
Expand Down Expand Up @@ -1674,7 +1640,9 @@ bool Character::can_continue_craft( item &craft, const requirement_data &continu
item_selections.push_back( is );
}
for( const auto &it : item_selections ) {
craft.components.splice( craft.components.end(), consume_items( it, batch_size, filter ) );
for( item &itm : consume_items( it, batch_size, filter ) ) {
craft.components.add( itm );
}
}
}

Expand Down Expand Up @@ -2410,7 +2378,7 @@ ret_val<void> Character::can_disassemble( const item &obj, const read_only_visit
}

const recipe &r = recipe_dictionary::get_uncraft( ( obj.typeId() == itype_disassembly ) ?
obj.components.front().typeId() : obj.typeId() );
obj.components.only_item().typeId() : obj.typeId() );

// check sufficient light
if( lighting_craft_speed_multiplier( r ) == 0.0f ) {
Expand Down Expand Up @@ -2671,7 +2639,7 @@ void Character::complete_disassemble( item_location target )
{
// Disassembly has 2 parallel vectors:
// item location, and recipe id
item temp = target.get_item()->components.front();
item temp = target.get_item()->components.only_item();
const recipe &rec = recipe_dictionary::get_uncraft( temp.typeId() );

if( rec ) {
Expand Down Expand Up @@ -2702,7 +2670,7 @@ void Character::complete_disassemble( item_location target )
}
// Set get and set duration of next uncraft
const recipe &next_recipe = recipe_dictionary::get_uncraft( ( next_item->typeId() ==
itype_disassembly ) ? next_item->components.front().typeId() : next_item->typeId() );
itype_disassembly ) ? next_item->components.only_item().typeId() : next_item->typeId() );

if( !next_recipe ) {
debugmsg( "bad disassembly recipe" );
Expand Down Expand Up @@ -2745,7 +2713,7 @@ void Character::complete_disassemble( item_location &target, const recipe &dis )

// Get the item to disassemble, and make a copy to keep its data (damage/components)
// after the original has been removed.
item org_item = target.get_item()->components.front();
item org_item = target.get_item()->components.only_item();
item dis_item = org_item;

if( this->is_avatar() ) {
Expand All @@ -2767,7 +2735,7 @@ void Character::complete_disassemble( item_location &target, const recipe &dis )

// If the components aren't empty, we want items exactly identical to them
// Even if the best-fit recipe does not involve those items
std::list<item> components = dis_item.components;
item_components components = dis_item.components;

// If the components are empty, item is the default kind and made of default components
if( components.empty() ) {
Expand Down Expand Up @@ -2807,7 +2775,7 @@ void Character::complete_disassemble( item_location &target, const recipe &dis )
}

for( ; compcount > 0; compcount-- ) {
components.emplace_back( newit );
components.add( newit );
}
}
}
Expand Down Expand Up @@ -2838,65 +2806,63 @@ void Character::complete_disassemble( item_location &target, const recipe &dis )
std::map<itype_id, int> destroy_tally;

// Roll skill and damage checks for successful recovery of each component
for( const item &newit : components ) {
// Use item type to index recover/destroy tallies
const itype_id it_type_id = newit.typeId();
// Chance of failure based on character skill and recipe difficulty
const bool comp_success = dice( skill_dice, skill_sides ) > dice( diff_dice, diff_sides );
// If original item was damaged, there is another chance for recovery to fail
const bool dmg_success = component_success_chance > rng_float( 0, 1 );

// If component recovery failed, tally it and continue with the next component
if( ( dis.difficulty != 0 && !comp_success ) || !dmg_success ) {
// Count destroyed items
if( destroy_tally.count( it_type_id ) == 0 ) {
destroy_tally[it_type_id] = newit.count();
} else {
destroy_tally[it_type_id] += newit.count();
for( const item_components::type_vector_pair &tvp : components ) {
for( const item &newit : tvp.second ) {
// Use item type to index recover/destroy tallies
const itype_id it_type_id = newit.typeId();
// Chance of failure based on character skill and recipe difficulty
const bool comp_success = dice( skill_dice, skill_sides ) > dice( diff_dice, diff_sides );
// If original item was damaged, there is another chance for recovery to fail
const bool dmg_success = component_success_chance > rng_float( 0, 1 );

// If component recovery failed, tally it and continue with the next component
if( ( dis.difficulty != 0 && !comp_success ) || !dmg_success ) {
// Count destroyed items
if( destroy_tally.count( it_type_id ) == 0 ) {
destroy_tally[it_type_id] = newit.count();
} else {
destroy_tally[it_type_id] += newit.count();
}
continue;
}
continue;
}

// Component recovered successfully; add to the tally
if( recover_tally.count( it_type_id ) == 0 ) {
recover_tally[it_type_id] = newit.count();
} else {
recover_tally[it_type_id] += newit.count();
}
// Component recovered successfully; add to the tally
if( recover_tally.count( it_type_id ) == 0 ) {
recover_tally[it_type_id] = newit.count();
} else {
recover_tally[it_type_id] += newit.count();
}

// Use item from components list, or (if not contained)
// use newit, the default constructed.
item act_item = newit;
// Use item from components list, or (if not contained)
// use newit, the default constructed.
item act_item = newit;

if( act_item.has_temperature() ) {
// TODO: fix point types
act_item.set_item_temperature( get_weather().get_temperature( loc.raw() ) );
}
if( act_item.has_temperature() ) {
// TODO: fix point types
act_item.set_item_temperature( get_weather().get_temperature( loc.raw() ) );
}

// Refitted clothing disassembles into refitted components (when applicable)
if( dis_item.has_flag( flag_FIT ) && act_item.has_flag( flag_VARSIZE ) ) {
act_item.set_flag( flag_FIT );
}
// Refitted clothing disassembles into refitted components (when applicable)
if( dis_item.has_flag( flag_FIT ) && act_item.has_flag( flag_VARSIZE ) ) {
act_item.set_flag( flag_FIT );
}

// Filthy items yield filthy components
if( dis_item.is_filthy() ) {
act_item.set_flag( flag_FILTHY );
}
// Filthy items yield filthy components
if( dis_item.is_filthy() ) {
act_item.set_flag( flag_FILTHY );
}

for( std::list<item>::iterator a = dis_item.components.begin(); a != dis_item.components.end();
++a ) {
if( a->type == newit.type ) {
act_item = *a;
dis_item.components.erase( a );
break;
ret_val<item> removed = dis_item.components.remove( newit.typeId() );
if( removed.success() ) {
act_item = removed.value();
}
}

//NPCs are too dumb to be able to handle liquid (for now)
if( act_item.made_of( phase_id::LIQUID ) && !is_npc() ) {
liquid_handler::handle_all_liquid( act_item, PICKUP_RANGE );
} else {
drop_items.push_back( act_item );
//NPCs are too dumb to be able to handle liquid (for now)
if( act_item.made_of( phase_id::LIQUID ) && !is_npc() ) {
liquid_handler::handle_all_liquid( act_item, PICKUP_RANGE );
} else {
drop_items.push_back( act_item );
}
}
}

Expand Down
4 changes: 2 additions & 2 deletions src/iexamine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5859,7 +5859,7 @@ void iexamine::mill_finalize( Character &, const tripoint &examp, const time_poi
iter = items.erase( iter );
} else {
item result( mdata.into_, start_time + milling_time, charges );
result.components.push_back( it );
result.components.add( it );
// copied from item::inherit_flags, which can not be called here because it requires a recipe.
for( const flag_id &f : it.type->get_flags() ) {
if( f->craft_inherit() ) {
Expand Down Expand Up @@ -5924,7 +5924,7 @@ static void smoker_finalize( Character &, const tripoint &examp, const time_poin
// Set charges to 1 for stacking purposes.
it = item( it.get_comestible()->cooks_like, it.birthday(), 1 );
}
result.components.push_back( it );
result.components.add( it );
// Smoking is always 1:1, so these must be equal for correct kcal/vitamin calculation.
result.recipe_charges = it.charges;
result.set_flag_recursive( flag_COOKED );
Expand Down
Loading