diff --git a/src/activity_item_handling.cpp b/src/activity_item_handling.cpp index f607bccb99b71..3ddb7080a7861 100644 --- a/src/activity_item_handling.cpp +++ b/src/activity_item_handling.cpp @@ -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 &cur ) { diff --git a/src/consumption.cpp b/src/consumption.cpp index f1fc80c9eadc4..27adc80812be9 100644 --- a/src/consumption.cpp +++ b/src/consumption.cpp @@ -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; diff --git a/src/craft_command.cpp b/src/craft_command.cpp index f4946888fa4bf..cc8f3fc40674d 100644 --- a/src/craft_command.cpp +++ b/src/craft_command.cpp @@ -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 used; + item_components used; std::vector comps_used; if( crafter->has_trait( trait_DEBUG_HS ) ) { return item( rec, batch_size, used, comps_used ); @@ -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 &selection : item_selections ) { diff --git a/src/crafting.cpp b/src/crafting.cpp index 9625ab0039295..15e40f1ad12fc 100644 --- a/src/crafting.cpp +++ b/src/crafting.cpp @@ -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 &components, const std::list &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 wield_craft( Character &p, item &craft ) { if( p.wield( craft ) ) { @@ -1286,7 +1260,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." ), @@ -1377,10 +1351,12 @@ void item::inherit_flags( const item &parent, const recipe &making ) } } -void item::inherit_flags( const std::list &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 ); + } } } @@ -1393,7 +1369,7 @@ void Character::complete_craft( item &craft, const cata::optional &loc const recipe &making = craft.get_making(); const int batch_size = craft.get_making_batch_size(); - std::list &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(); @@ -1455,49 +1431,39 @@ void Character::complete_craft( item &craft, const cata::optional &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 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… @@ -1672,7 +1638,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 ); + } } } @@ -2408,7 +2376,7 @@ ret_val 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 ) { @@ -2669,7 +2637,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 ) { @@ -2700,7 +2668,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" ); @@ -2743,7 +2711,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() ) { @@ -2765,7 +2733,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 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() ) { @@ -2805,7 +2773,7 @@ void Character::complete_disassemble( item_location &target, const recipe &dis ) } for( ; compcount > 0; compcount-- ) { - components.emplace_back( newit ); + components.add( newit ); } } } @@ -2836,65 +2804,63 @@ void Character::complete_disassemble( item_location &target, const recipe &dis ) std::map 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::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 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 ); + } } } diff --git a/src/iexamine.cpp b/src/iexamine.cpp index 07e7e91b9577b..0e4d8231744ac 100644 --- a/src/iexamine.cpp +++ b/src/iexamine.cpp @@ -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() ) { @@ -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 ); diff --git a/src/inventory.cpp b/src/inventory.cpp index c52e50a2fd2c8..fefad7364a3b9 100644 --- a/src/inventory.cpp +++ b/src/inventory.cpp @@ -181,6 +181,16 @@ inventory &inventory::operator+= ( const std::list &rhs ) return *this; } +inventory &inventory::operator+= ( const item_components &rhs ) +{ + for( const item_components::type_vector_pair &tvp : rhs ) { + for( const item &rh : tvp.second ) { + add_item( rh, false, false ); + } + } + return *this; +} + inventory &inventory::operator+= ( const std::vector &rhs ) { for( const item &rh : rhs ) { @@ -215,6 +225,11 @@ inventory inventory::operator+ ( const std::list &rhs ) return inventory( *this ) += rhs; } +inventory inventory::operator+ ( const item_components &rhs ) +{ + return inventory( *this ) += rhs; +} + inventory inventory::operator+ ( const item &rhs ) { return inventory( *this ) += rhs; diff --git a/src/inventory.h b/src/inventory.h index 86cf96e74d05c..d583e05932e11 100644 --- a/src/inventory.h +++ b/src/inventory.h @@ -124,11 +124,13 @@ class inventory : public visitable inventory &operator+= ( const inventory &rhs ); inventory &operator+= ( const item &rhs ); inventory &operator+= ( const std::list &rhs ); + inventory &operator+= ( const item_components &rhs ); inventory &operator+= ( const std::vector &rhs ); inventory &operator+= ( const item_stack &rhs ); inventory operator+ ( const inventory &rhs ); inventory operator+ ( const item &rhs ); inventory operator+ ( const std::list &rhs ); + inventory operator+ ( const item_components &rhs ); void unsort(); // flags the inventory as unsorted void clear(); diff --git a/src/item.cpp b/src/item.cpp index 02c535e3d928e..81c558588e2ae 100644 --- a/src/item.cpp +++ b/src/item.cpp @@ -400,8 +400,12 @@ safe_reference item::get_safe_reference() 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() ) { + for( const item_components::type_vector_pair &tvp : craft.components ) { + if( !tvp.second.front().goes_bad() ) { + // they're all the same type, so this should be the same for all + continue; + } + for( const item &it : tvp.second ) { if( !most_rotten || it.get_relative_rot() > most_rotten->get_relative_rot() ) { most_rotten = ⁢ } @@ -414,8 +418,12 @@ static time_duration get_shortest_lifespan_from_components( const item &craft ) { const item *shortest_lifespan_component = nullptr; time_duration shortest_lifespan = 0_turns; - for( const item &it : craft.components ) { - if( it.goes_bad() ) { + for( const item_components::type_vector_pair &tvp : craft.components ) { + if( !tvp.second.front().goes_bad() ) { + // they're all the same type, so this should be the same for all + continue; + } + for( const item &it : tvp.second ) { time_duration lifespan = it.get_shelf_life() - it.get_rot(); if( !shortest_lifespan_component || ( lifespan < shortest_lifespan ) ) { shortest_lifespan_component = ⁢ @@ -429,9 +437,15 @@ static time_duration get_shortest_lifespan_from_components( const item &craft ) static bool shelf_life_less_than_each_component( const item &craft ) { - for( const item &it : craft.components ) { - if( it.goes_bad() && it.is_comestible() && it.get_shelf_life() < craft.get_shelf_life() ) { - return false; + for( const item_components::type_vector_pair &tvp : craft.components ) { + if( !tvp.second.front().goes_bad() || !tvp.second.front().is_comestible() ) { + // they're all the same type, so this should be the same for all + continue; + } + for( const item &it : tvp.second ) { + if( it.get_shelf_life() < craft.get_shelf_life() ) { + return false; + } } } return true; @@ -463,7 +477,7 @@ static void inherit_rot_from_components( item &it ) } } -item::item( const recipe *rec, int qty, std::list items, std::vector selections ) +item::item( const recipe *rec, int qty, item_components items, std::vector selections ) : item( "craft", calendar::turn ) { craft_data_ = cata::make_value(); @@ -481,15 +495,17 @@ item::item( const recipe *rec, int qty, std::list items, std::vectorcraft_inherit() ) { - set_flag( f ); + for( item_components::type_vector_pair &tvp : components ) { + for( item &component : tvp.second ) { + for( const flag_id &f : component.get_flags() ) { + if( f->craft_inherit() ) { + set_flag( f ); + } } - } - for( const flag_id &f : component.type->get_flags() ) { - if( f->craft_inherit() ) { - set_flag( f ); + for( const flag_id &f : component.type->get_flags() ) { + if( f->craft_inherit() ) { + set_flag( f ); + } } } } @@ -502,7 +518,8 @@ item::item( const recipe *rec, int qty, item &component ) craft_data_->making = rec; craft_data_->disassembly = true; craft_data_->batch_size = qty; - std::listitems( { component } ); + item_components items; + items.add( component ); components = items; if( has_temperature() ) { @@ -513,15 +530,17 @@ item::item( const recipe *rec, int qty, item &component ) } } - for( item &comp : components ) { - for( const flag_id &f : comp.get_flags() ) { - if( f->craft_inherit() ) { - set_flag( f ); + for( item_components::type_vector_pair &tvp : components ) { + for( item &comp : tvp.second ) { + for( const flag_id &f : comp.get_flags() ) { + if( f->craft_inherit() ) { + set_flag( f ); + } } - } - for( const flag_id &f : comp.type->get_flags() ) { - if( f->craft_inherit() ) { - set_flag( f ); + for( const flag_id &f : comp.type->get_flags() ) { + if( f->craft_inherit() ) { + set_flag( f ); + } } } } @@ -6860,8 +6879,10 @@ units::mass item::weight( bool include_contents, bool integral ) const if( is_craft() ) { if( !craft_data_->cached_weight ) { units::mass ret = 0_gram; - for( const item &it : components ) { - ret += it.weight(); + for( const item_components::type_vector_pair &tvp : components ) { + for( const item &it : tvp.second ) { + ret += it.weight(); + } } craft_data_->cached_weight = ret; } @@ -7074,8 +7095,10 @@ units::volume item::base_volume() const if( is_craft() ) { units::volume ret = 0_ml; - for( const item &it : components ) { - ret += it.base_volume(); + for( const item_components::type_vector_pair &tvp : components ) { + for( const item &it : tvp.second ) { + ret += it.base_volume(); + } } return ret; } @@ -7105,8 +7128,10 @@ units::volume item::volume( bool integral, bool ignore_contents, int charges_in_ if( is_craft() ) { if( !craft_data_->cached_volume ) { units::volume ret = 0_ml; - for( const item &it : components ) { - ret += it.volume(); + for( const item_components::type_vector_pair &tvp : components ) { + for( const item &it : tvp.second ) { + ret += it.volume(); + } } // 1 mL minimum craft volume to avoid 0 volume errors from practices or hammerspace craft_data_->cached_volume = std::max( ret, 1_ml ); @@ -7358,8 +7383,10 @@ item &item::unset_flag( const flag_id &flag ) item &item::set_flag_recursive( const flag_id &flag ) { set_flag( flag ); - for( item &comp : components ) { - comp.set_flag_recursive( flag ); + for( item_components::type_vector_pair &tvp : components ) { + for( item &it : tvp.second ) { + it.set_flag_recursive( flag ); + } } return *this; } @@ -11946,10 +11973,12 @@ std::string item::components_to_string() const { using t_count_map = std::map; t_count_map counts; - for( const item &elem : components ) { - if( !elem.has_flag( flag_BYPRODUCT ) ) { - const std::string name = elem.display_name(); - counts[name]++; + for( const item_components::type_vector_pair &tvp : components ) { + for( const item &it : tvp.second ) { + if( !it.has_flag( flag_BYPRODUCT ) ) { + const std::string name = it.display_name(); + counts[name]++; + } } } return enumerate_as_string( counts.begin(), counts.end(), @@ -11966,15 +11995,11 @@ std::string item::components_to_string() const uint64_t item::make_component_hash() const { - // First we need to sort the IDs so that identical ingredients give identical hashes. - std::multiset id_set; - for( const item &it : components ) { - id_set.insert( it.typeId().str() ); - } - std::string concatenated_ids; - for( const std::string &id : id_set ) { - concatenated_ids += id; + for( const item_components::type_vector_pair &tvp : components ) { + for( size_t i = 0; i < tvp.second.size(); ++i ) { + concatenated_ids += tvp.first.str(); + } } std::hash hasher; @@ -13261,22 +13286,26 @@ std::string item::type_name( unsigned int quantity ) const // Apply conditional names, in order. for( const conditional_name &cname : type->conditional_names ) { // Lambda for searching for a item ID among all components. - std::function )> component_id_equals = - [&]( const std::list &components ) { - for( const item &component : components ) { - if( component.typeId().str() == cname.condition ) { - return true; + std::function component_id_equals = + [&]( const item_components & components ) { + for( const item_components::type_vector_pair &tvp : components ) { + for( const item &component : tvp.second ) { + if( component.typeId().str() == cname.condition ) { + return true; + } } } return false; }; // Lambda for recursively searching for a item ID substring among all components. - std::function )> component_id_contains = - [&]( const std::list &components ) { - for( const item &component : components ) { - if( component.typeId().str().find( cname.condition ) != std::string::npos || - component_id_contains( component.components ) ) { - return true; + std::function component_id_contains = + [&]( const item_components & components ) { + for( const item_components::type_vector_pair &tvp : components ) { + for( const item &component : tvp.second ) { + if( component.typeId().str().find( cname.condition ) != std::string::npos || + component_id_contains( component.components ) ) { + return true; + } } } return false; @@ -13486,15 +13515,17 @@ std::vector item::get_uncraft_components() const } } else { //Make a new vector of components from the registered components - for( const item &component : components ) { - auto iter = std::find_if( ret.begin(), ret.end(), [component]( item_comp & obj ) { - return obj.type == component.typeId(); - } ); + for( const item_components::type_vector_pair &tvp : components ) { + for( const item &component : tvp.second ) { + auto iter = std::find_if( ret.begin(), ret.end(), [component]( item_comp & obj ) { + return obj.type == component.typeId(); + } ); - if( iter != ret.end() ) { - iter->count += component.count(); - } else { - ret.emplace_back( component.typeId(), component.count() ); + if( iter != ret.end() ) { + iter->count += component.count(); + } else { + ret.emplace_back( component.typeId(), component.count() ); + } } } } diff --git a/src/item.h b/src/item.h index 2ca90916b9197..e0129bbf7dd01 100644 --- a/src/item.h +++ b/src/item.h @@ -21,6 +21,7 @@ #include "enums.h" #include "gun_mode.h" #include "io_tags.h" +#include "item_components.h" #include "item_contents.h" #include "item_location.h" #include "item_pocket.h" @@ -205,7 +206,7 @@ class item : public visitable item( const itype *type, time_point turn, solitary_tag ); /** For constructing in-progress crafts */ - item( const recipe *rec, int qty, std::list items, std::vector selections ); + item( const recipe *rec, int qty, item_components items, std::vector selections ); /** For constructing in-progress disassemblies */ item( const recipe *rec, int qty, item &component ); @@ -2695,7 +2696,7 @@ class item : public visitable * * @param parents Items to inherit from */ - void inherit_flags( const std::list &parents, const recipe &making ); + void inherit_flags( const item_components &parents, const recipe &making ); void set_tools_to_continue( bool value ); bool has_tools_to_continue() const; @@ -2868,7 +2869,7 @@ class item : public visitable static const int INFINITE_CHARGES; const itype *type; - std::list components; + item_components components; /** What faults (if any) currently apply to this item */ std::set faults; diff --git a/src/item_components.cpp b/src/item_components.cpp new file mode 100644 index 0000000000000..454120c968436 --- /dev/null +++ b/src/item_components.cpp @@ -0,0 +1,162 @@ +#include "item_components.h" + +#include "item.h" +#include "itype.h" +#include "type_id.h" + +std::vector item_components::operator[]( const itype_id it_id ) +{ + return comps[it_id]; +} + +item_components::comp_iterator item_components::begin() +{ + return comps.begin(); +} +item_components::comp_iterator item_components::end() +{ + return comps.end(); +} +item_components::const_comp_iterator item_components::begin() const +{ + return comps.begin(); +} +item_components::const_comp_iterator item_components::end() const +{ + return comps.end(); +} + +bool item_components::empty() +{ + return comps.empty(); +} + +bool item_components::empty() const +{ + return comps.empty(); +} + +void item_components::clear() +{ + comps.clear(); +} + +item item_components::only_item() +{ + if( comps.size() != 1 || comps.begin()->second.size() != 1 ) { + debugmsg( "item_components::only_item called but components don't contain exactly one item" ); + return item(); + } + return *comps.begin()->second.begin(); +} + +item item_components::only_item() const +{ + if( comps.size() != 1 || comps.begin()->second.size() != 1 ) { + debugmsg( "item_components::only_item called but components don't contain exactly one item" ); + return item(); + } + return *comps.begin()->second.begin(); +} + +size_t item_components::size() const +{ + size_t ret = 0; + for( const type_vector_pair &tvp : comps ) { + ret += tvp.second.size(); + } + return ret; +} + +void item_components::add( item &new_it ) +{ + comp_iterator it = comps.find( new_it.typeId() ); + if( it != comps.end() ) { + if( it->first->count_by_charges() ) { + it->second.front().charges += new_it.charges; + } else { + it->second.push_back( new_it ); + } + } else { + comps[new_it.typeId()] = { new_it }; + } +} + +ret_val item_components::remove( itype_id it_id ) +{ + comp_iterator it = comps.find( it_id ); + if( it == comps.end() ) { + return ret_val::make_failure( item() ); + } + item itm = *it->second.begin(); + it->second.erase( it->second.begin() ); + if( it->second.empty() ) { + comps.erase( it ); + } + return ret_val::make_success( itm ); +} + +item item_components::get_and_remove_random_entry() +{ + comp_iterator iter = comps.begin(); + std::advance( iter, rng( 0, comps.size() - 1 ) ); + item ret = random_entry_removed( iter->second ); + if( iter->second.empty() ) { + comps.erase( iter ); + } + return ret; +} + +item_components item_components::split( const int batch_size, const size_t offset ) +{ + item_components ret; + + for( item_components::type_vector_pair &tvp : comps ) { + if( tvp.first->count_by_charges() ) { + if( tvp.second.size() != 1 ) { + debugmsg( "count by charges component %s wasn't merged properly, can't distribute components to resulting items", + tvp.first.str() ); + return item_components(); + } + item new_comp( tvp.second.front() ); + if( new_comp.charges % batch_size != 0 ) { + debugmsg( "component %s can't be evenly distributed to resulting items", tvp.first.str() ); + return item_components(); + } + new_comp.charges /= batch_size; + ret.add( new_comp ); + } else { + + if( tvp.second.size() % batch_size != 0 ) { + debugmsg( "component %s can't be evenly distributed to resulting items", tvp.first.str() ); + return item_components(); + } + + for( size_t i = offset; i < tvp.second.size(); i += batch_size ) { + ret.add( tvp.second[i] ); + } + } + } + + return ret; +} + +void item_components::serialize( JsonOut &jsout ) const +{ + jsout.write( comps ); +} + +void item_components::deserialize( const JsonValue &jv ) +{ + comps.clear(); + // read legacy arrays + if( jv.test_array() ) { + std::list temp; + jv.read( temp ); + for( item &it : temp ) { + add( it ); + } + } else { + jv.read( comps ); + } +} diff --git a/src/item_components.h b/src/item_components.h new file mode 100644 index 0000000000000..e3e96af42fa65 --- /dev/null +++ b/src/item_components.h @@ -0,0 +1,56 @@ +#pragma once +#ifndef CATA_SRC_ITEM_COMPONENTS_H +#define CATA_SRC_ITEM_COMPONENTS_H + +#include +#include +#include + +#include "type_id.h" + +class item; +class JsonOut; +class JsonValue; +template +class ret_val; + +class item_components +{ + private: + std::map> comps; + using comp_iterator = std::map>::iterator; + using const_comp_iterator = std::map>::const_iterator; + + public: + using type_vector_pair = std::pair>; + + comp_iterator begin(); + comp_iterator end(); + const_comp_iterator begin() const; + const_comp_iterator end() const; + bool empty(); + bool empty() const; + void clear(); + + std::vector operator[]( itype_id it_id ); + + // needed for disassembly items + // todo: better way to handle that + item only_item(); + item only_item() const; + + // total number of items in components + size_t size() const; + void add( item &new_it ); + ret_val remove( itype_id it_id ); + // components must not be empty! + item get_and_remove_random_entry(); + + // used to distribute the components of a finished craft to the resulting items + item_components split( int batch_size, size_t offset ); + + void serialize( JsonOut &jsout ) const; + void deserialize( const JsonValue &jv ); +}; + +#endif // CATA_SRC_ITEM_COMPONENTS_H diff --git a/src/iuse_actor.cpp b/src/iuse_actor.cpp index 67a16c9abb563..e3aacf772966f 100644 --- a/src/iuse_actor.cpp +++ b/src/iuse_actor.cpp @@ -1607,8 +1607,10 @@ void salvage_actor::cut_up( Character &p, item_location &cut ) const // All intact components are also cut up and destroyed if( !curr.components.empty() ) { - for( const item &iter : curr.components ) { - cut_up_component( iter, num_adjusted ); + for( const item_components::type_vector_pair &tvp : curr.components ) { + for( const item &iter : tvp.second ) { + cut_up_component( iter, num_adjusted ); + } } return; } diff --git a/src/melee.cpp b/src/melee.cpp index fc22ecde06fd7..4c7e98ad7ce5a 100644 --- a/src/melee.cpp +++ b/src/melee.cpp @@ -234,16 +234,19 @@ bool Character::handle_melee_wear( item_location shield, float wear_multiplier ) // Items that should have no bearing on durability const std::set blacklist = { itype_rag, itype_leather, itype_fur }; - for( item &comp : shield->components ) { - if( blacklist.count( comp.typeId() ) <= 0 ) { + for( item_components::type_vector_pair &tvp : shield->components ) { + if( blacklist.count( tvp.first ) > 0 ) { + continue; + } + for( item &comp : tvp.second ) { if( weak_chip > comp.chip_resistance() ) { weak_chip = comp.chip_resistance(); weak_comp = comp.typeId(); } - } - if( comp.volume() > big_vol ) { - big_vol = comp.volume(); - big_comp = comp.typeId(); + if( comp.volume() > big_vol ) { + big_vol = comp.volume(); + big_comp = comp.typeId(); + } } } material_factor = ( weak_chip < INT_MAX ? weak_chip : shield->chip_resistance() ) / fragile_factor; @@ -292,18 +295,20 @@ bool Character::handle_melee_wear( item_location shield, float wear_multiplier ) _( "'s %s breaks apart!" ), str ); - for( item &comp : temp.components ) { - int break_chance = comp.typeId() == weak_comp ? 2 : 8; + for( item_components::type_vector_pair &tvp : temp.components ) { + for( item &comp : tvp.second ) { + int break_chance = comp.typeId() == weak_comp ? 2 : 8; - if( one_in( break_chance ) ) { - add_msg_if_player( m_bad, _( "The %s is destroyed!" ), comp.tname() ); - continue; - } + if( one_in( break_chance ) ) { + add_msg_if_player( m_bad, _( "The %s is destroyed!" ), comp.tname() ); + continue; + } - if( comp.typeId() == big_comp && !has_wield_conflicts( comp ) ) { - wield( comp ); - } else { - get_map().add_item_or_charges( pos(), comp ); + if( comp.typeId() == big_comp && !has_wield_conflicts( comp ) ) { + wield( comp ); + } else { + get_map().add_item_or_charges( pos(), comp ); + } } } } else { diff --git a/src/requirements.cpp b/src/requirements.cpp index f6f185a78e092..3ae3fec130cab 100644 --- a/src/requirements.cpp +++ b/src/requirements.cpp @@ -1287,7 +1287,7 @@ requirement_data requirement_data::disassembly_requirements() const } requirement_data requirement_data::continue_requirements( const std::vector - &required_comps, const std::list &remaining_comps ) + &required_comps, const item_components &remaining_comps ) { // Create an empty requirement_data requirement_data ret; diff --git a/src/requirements.h b/src/requirements.h index c07aef3aa656e..a9df36f3a3a8d 100644 --- a/src/requirements.h +++ b/src/requirements.h @@ -24,6 +24,7 @@ class JsonObject; class JsonOut; class JsonValue; class item; +class item_components; class nc_color; class read_only_visitable; template struct enum_traits; @@ -365,7 +366,7 @@ struct requirement_data { * Returned requirement_data is for *all* batches at once. */ static requirement_data continue_requirements( const std::vector &required_comps, - const std::list &remaining_comps ); + const item_components &remaining_comps ); /** * Merge similar quality/tool/component lists. diff --git a/tests/crafting_test.cpp b/tests/crafting_test.cpp index 6fff7ed154e1e..4e23cf4cdad5d 100644 --- a/tests/crafting_test.cpp +++ b/tests/crafting_test.cpp @@ -2062,21 +2062,11 @@ TEST_CASE( "tools with charges as components", "[crafting]" ) THEN( "craft uses the free thread instead of tool ammo as component" ) { CHECK( !res.is_null() ); CHECK( res.is_craft() ); + CHECK( res.components[itype_sheet_cotton].size() == cotton_sheets_in_recipe ); + // when threads aren't count by charges anymore, see line above + CHECK( res.components[itype_thread].front().count() == threads_in_recipe ); int cotton_sheets = 0; int threads = 0; - for( const item &comp : res.components ) { - if( comp.typeId() == itype_sheet_cotton ) { - cotton_sheets += comp.count_by_charges() ? comp.charges : 1; - } else if( comp.typeId() == itype_thread ) { - threads += comp.count_by_charges() ? comp.charges : 1; - } else { - FAIL( "found unexpected component " << comp.typeId().str() ); - } - } - CHECK( cotton_sheets == cotton_sheets_in_recipe ); - CHECK( threads == threads_in_recipe ); - cotton_sheets = 0; - threads = 0; int threads_in_tool = 0; for( const item &i : m.i_at( c.pos() ) ) { if( i.typeId() == itype_sheet_cotton ) { @@ -2109,21 +2099,11 @@ TEST_CASE( "tools with charges as components", "[crafting]" ) THEN( "craft uses the free thread instead of tool ammo as component" ) { CHECK( !res.is_null() ); CHECK( res.is_craft() ); + CHECK( res.components[itype_sheet_cotton].size() == cotton_sheets_in_recipe ); + // when threads aren't count by charges anymore, see line above + CHECK( res.components[itype_thread].front().count() == threads_in_recipe ); int cotton_sheets = 0; int threads = 0; - for( const item &comp : res.components ) { - if( comp.typeId() == itype_sheet_cotton ) { - cotton_sheets += comp.count_by_charges() ? comp.charges : 1; - } else if( comp.typeId() == itype_thread ) { - threads += comp.count_by_charges() ? comp.charges : 1; - } else { - FAIL( "found unexpected component " << comp.typeId().str() ); - } - } - CHECK( cotton_sheets == cotton_sheets_in_recipe ); - CHECK( threads == threads_in_recipe ); - cotton_sheets = 0; - threads = 0; int threads_in_tool = 0; for( const item *i : pack_loc->all_items_top() ) { if( i->typeId() == itype_sheet_cotton ) { diff --git a/tests/invlet_test.cpp b/tests/invlet_test.cpp index 3a44116c5d73f..4da5c1bcf69df 100644 --- a/tests/invlet_test.cpp +++ b/tests/invlet_test.cpp @@ -50,23 +50,18 @@ enum test_action { TEST_ACTION_NUM, }; -// This is a massive hack that makes this test work without totally rewriting it after #31406 -// The number of null items in item.components is used as a unique id for the purposes of this test -// -// The reason components is used instead of some other property of items is that this isn't checked -// when determining if two items stack and therefore has no side effects. -static void set_id( item &it, int id ) +static void set_id( item &it, const std::string &id ) { - it.components = std::list( static_cast( id ), item() ); + it.set_var( "id", id ); } -static int get_id( const item &it ) +static std::string get_id( const item &it ) { - return static_cast( it.components.size() ); + return it.get_var( "id" ); } template -static item *retrieve_item( const T &sel, int id ) +static item *retrieve_item( const T &sel, const std::string &id ) { item *obj = nullptr; sel.visit_items( [&id, &obj]( const item * e, item * ) { @@ -239,7 +234,7 @@ static invlet_state check_invlet( Character &you, item &it, const char invlet ) return UNEXPECTED; } -static void drop_at_feet( Character &you, const int id ) +static void drop_at_feet( Character &you, const std::string &id ) { size_t size_before = get_map().i_at( you.pos() ).size(); @@ -253,7 +248,7 @@ static void drop_at_feet( Character &you, const int id ) REQUIRE( get_map().i_at( you.pos() ).size() == size_before + 1 ); } -static void pick_up_from_feet( Character &you, int id ) +static void pick_up_from_feet( Character &you, const std::string &id ) { map_stack items = get_map().i_at( you.pos() ); size_t size_before = items.size(); @@ -269,7 +264,7 @@ static void pick_up_from_feet( Character &you, int id ) REQUIRE( items.size() == size_before - 1 ); } -static void wear_from_feet( Character &you, int id ) +static void wear_from_feet( Character &you, const std::string &id ) { map_stack items = get_map().i_at( you.pos() ); size_t size_before = items.size(); @@ -283,7 +278,7 @@ static void wear_from_feet( Character &you, int id ) REQUIRE( items.size() == size_before - 1 ); } -static void wield_from_feet( Character &you, int id ) +static void wield_from_feet( Character &you, const std::string &id ) { map_stack items = get_map().i_at( you.pos() ); size_t size_before = items.size(); @@ -323,7 +318,7 @@ static void add_item( Character &you, item &it, const inventory_location loc ) } } -static item &item_at( Character &you, const int id, const inventory_location loc ) +static item &item_at( Character &you, const std::string &id, const inventory_location loc ) { switch( loc ) { case GROUND: { @@ -345,7 +340,7 @@ static item &item_at( Character &you, const int id, const inventory_location loc return null_item_reference(); } -static void move_item( Character &you, const int id, const inventory_location from, +static void move_item( Character &you, const std::string &id, const inventory_location from, const inventory_location to ) { switch( from ) { @@ -472,43 +467,43 @@ static void invlet_test( avatar &dummy, const inventory_location from, const inv item tshirt( "tshirt" ); item jeans( "jeans" ); - set_id( tshirt, 1 ); - set_id( jeans, 2 ); + set_id( tshirt, "1" ); + set_id( jeans, "2" ); // add the items to the starting position add_item( dummy, tshirt, to ); add_item( dummy, jeans, to ); // assign invlet to the first item - assign_invlet( dummy, item_at( dummy, 1, to ), invlet, first_invlet_state ); + assign_invlet( dummy, item_at( dummy, "1", to ), invlet, first_invlet_state ); // remove the first item - move_item( dummy, 1, to, from ); + move_item( dummy, "1", to, from ); // assign invlet to the second item - assign_invlet( dummy, item_at( dummy, 2, to ), invlet, second_invlet_state ); + assign_invlet( dummy, item_at( dummy, "2", to ), invlet, second_invlet_state ); item *final_first = nullptr; item *final_second = nullptr; switch( action ) { case REMOVE_1ST_REMOVE_2ND_ADD_1ST_ADD_2ND: - move_item( dummy, 2, to, from ); - move_item( dummy, 1, from, to ); - move_item( dummy, 2, from, to ); - final_first = &item_at( dummy, 1, to ); - final_second = &item_at( dummy, 2, to ); + move_item( dummy, "2", to, from ); + move_item( dummy, "1", from, to ); + move_item( dummy, "2", from, to ); + final_first = &item_at( dummy, "1", to ); + final_second = &item_at( dummy, "2", to ); break; case REMOVE_1ST_REMOVE_2ND_ADD_2ND_ADD_1ST: - move_item( dummy, 2, to, from ); - move_item( dummy, 2, from, to ); - move_item( dummy, 1, from, to ); - final_first = &item_at( dummy, 1, to ); - final_second = &item_at( dummy, 2, to ); + move_item( dummy, "2", to, from ); + move_item( dummy, "2", from, to ); + move_item( dummy, "1", from, to ); + final_first = &item_at( dummy, "1", to ); + final_second = &item_at( dummy, "2", to ); break; case REMOVE_1ST_ADD_1ST: - move_item( dummy, 1, from, to ); - final_first = &item_at( dummy, 1, to ); - final_second = &item_at( dummy, 2, to ); + move_item( dummy, "1", from, to ); + final_first = &item_at( dummy, "1", to ); + final_second = &item_at( dummy, "2", to ); break; default: FAIL( "unimplemented" ); @@ -554,18 +549,18 @@ static void stack_invlet_test( avatar &dummy, inventory_location from, inventory item tshirt1( "tshirt" ); item tshirt2( "tshirt" ); - set_id( tshirt1, 1 ); - set_id( tshirt2, 2 ); + set_id( tshirt1, "1" ); + set_id( tshirt2, "2" ); // add two such items to the starting position add_item( dummy, tshirt1, from ); add_item( dummy, tshirt2, from ); // assign the stack with invlet - assign_invlet( dummy, item_at( dummy, 1, from ), invlet, CACHED ); + assign_invlet( dummy, item_at( dummy, "1", from ), invlet, CACHED ); // wield or wear the first item - move_item( dummy, 1, from, to ); + move_item( dummy, "1", from, to ); std::stringstream ss; ss << "1. add a stack of two same items to " << location_desc( from ) << std::endl; @@ -573,17 +568,18 @@ static void stack_invlet_test( avatar &dummy, inventory_location from, inventory ss << "3. " << move_action_desc( 0, from, to ) << std::endl; ss << "expect the two items to have different invlets" << std::endl; ss << "actually the two items have " << - ( item_at( dummy, 1, to ).invlet != item_at( dummy, 2, from ).invlet ? "different" : "the same" ) << + ( item_at( dummy, "1", to ).invlet != item_at( dummy, "2", + from ).invlet ? "different" : "the same" ) << " invlets" << std::endl; INFO( ss.str() ); - REQUIRE( item_at( dummy, 1, to ).typeId() == tshirt1.typeId() ); - REQUIRE( item_at( dummy, 2, from ).typeId() == tshirt2.typeId() ); + REQUIRE( item_at( dummy, "1", to ).typeId() == tshirt1.typeId() ); + REQUIRE( item_at( dummy, "2", from ).typeId() == tshirt2.typeId() ); // the wielded/worn item should have different invlet from the remaining item - CHECK( item_at( dummy, 1, to ).invlet != item_at( dummy, 2, from ).invlet ); + CHECK( item_at( dummy, "1", to ).invlet != item_at( dummy, "2", from ).invlet ); // clear invlets - assign_invlet( dummy, item_at( dummy, 1, to ), invlet, NONE ); - assign_invlet( dummy, item_at( dummy, 2, from ), invlet, NONE ); + assign_invlet( dummy, item_at( dummy, "1", to ), invlet, NONE ); + assign_invlet( dummy, item_at( dummy, "2", from ), invlet, NONE ); } static void swap_invlet_test( avatar &dummy, inventory_location loc ) @@ -606,28 +602,28 @@ static void swap_invlet_test( avatar &dummy, inventory_location loc ) item tshirt2( "tshirt" ); tshirt2.mod_damage( -1 ); - set_id( tshirt1, 1 ); - set_id( tshirt2, 2 ); + set_id( tshirt1, "1" ); + set_id( tshirt2, "2" ); // add the items add_item( dummy, tshirt1, loc ); add_item( dummy, tshirt2, loc ); // assign the items with invlets - assign_invlet( dummy, item_at( dummy, 1, loc ), invlet_1, CACHED ); - assign_invlet( dummy, item_at( dummy, 2, loc ), invlet_2, CACHED ); + assign_invlet( dummy, item_at( dummy, "1", loc ), invlet_1, CACHED ); + assign_invlet( dummy, item_at( dummy, "2", loc ), invlet_2, CACHED ); // swap the invlets (invoking twice to make the invlet non-player-assigned) - dummy.reassign_item( item_at( dummy, 1, loc ), invlet_2 ); - dummy.reassign_item( item_at( dummy, 1, loc ), invlet_2 ); + dummy.reassign_item( item_at( dummy, "1", loc ), invlet_2 ); + dummy.reassign_item( item_at( dummy, "1", loc ), invlet_2 ); // drop the items - move_item( dummy, 1, loc, GROUND ); - move_item( dummy, 2, loc, GROUND ); + move_item( dummy, "1", loc, GROUND ); + move_item( dummy, "2", loc, GROUND ); // get them again - move_item( dummy, 1, GROUND, loc ); - move_item( dummy, 2, GROUND, loc ); + move_item( dummy, "1", GROUND, loc ); + move_item( dummy, "2", GROUND, loc ); std::stringstream ss; ss << "1. add two items of the same type to " << location_desc( loc ) << @@ -638,23 +634,24 @@ static void swap_invlet_test( avatar &dummy, inventory_location loc ) ss << "4. move the items to " << location_desc( GROUND ) << std::endl; ss << "5. move the items to " << location_desc( loc ) << " again" << std::endl; ss << "expect the items to keep their swapped invlets" << std::endl; - if( item_at( dummy, 1, loc ).invlet == invlet_2 && item_at( dummy, 2, loc ).invlet == invlet_1 ) { + if( item_at( dummy, "1", loc ).invlet == invlet_2 && + item_at( dummy, "2", loc ).invlet == invlet_1 ) { ss << "the items actually keep their swapped invlets" << std::endl; } else { ss << "the items actually does not keep their swapped invlets" << std::endl; } INFO( ss.str() ); - REQUIRE( item_at( dummy, 1, loc ).typeId() == tshirt1.typeId() ); - REQUIRE( item_at( dummy, 2, loc ).typeId() == tshirt2.typeId() ); + REQUIRE( item_at( dummy, "1", loc ).typeId() == tshirt1.typeId() ); + REQUIRE( item_at( dummy, "2", loc ).typeId() == tshirt2.typeId() ); // invlets should not disappear and should still be swapped - CHECK( item_at( dummy, 1, loc ).invlet == invlet_2 ); - CHECK( item_at( dummy, 2, loc ).invlet == invlet_1 ); - CHECK( check_invlet( dummy, item_at( dummy, 1, loc ), invlet_2 ) == CACHED ); - CHECK( check_invlet( dummy, item_at( dummy, 2, loc ), invlet_1 ) == CACHED ); + CHECK( item_at( dummy, "1", loc ).invlet == invlet_2 ); + CHECK( item_at( dummy, "2", loc ).invlet == invlet_1 ); + CHECK( check_invlet( dummy, item_at( dummy, "1", loc ), invlet_2 ) == CACHED ); + CHECK( check_invlet( dummy, item_at( dummy, "2", loc ), invlet_1 ) == CACHED ); // clear invlets - assign_invlet( dummy, item_at( dummy, 1, loc ), invlet_2, NONE ); - assign_invlet( dummy, item_at( dummy, 2, loc ), invlet_1, NONE ); + assign_invlet( dummy, item_at( dummy, "1", loc ), invlet_2, NONE ); + assign_invlet( dummy, item_at( dummy, "2", loc ), invlet_1, NONE ); } static void merge_invlet_test( avatar &dummy, inventory_location from ) @@ -690,21 +687,21 @@ static void merge_invlet_test( avatar &dummy, inventory_location from ) item tshirt1( "tshirt" ); item tshirt2( "tshirt" ); - set_id( tshirt1, 1 ); - set_id( tshirt2, 2 ); + set_id( tshirt1, "1" ); + set_id( tshirt2, "2" ); // add the item add_item( dummy, tshirt1, INVENTORY ); add_item( dummy, tshirt2, from ); // assign the items with invlets - assign_invlet( dummy, item_at( dummy, 1, INVENTORY ), invlet_1, first_invlet_state ); - assign_invlet( dummy, item_at( dummy, 2, from ), invlet_2, second_invlet_state ); + assign_invlet( dummy, item_at( dummy, "1", INVENTORY ), invlet_1, first_invlet_state ); + assign_invlet( dummy, item_at( dummy, "2", from ), invlet_2, second_invlet_state ); // merge the second item into inventory - move_item( dummy, 2, from, INVENTORY ); + move_item( dummy, "2", from, INVENTORY ); - item &merged_item = item_at( dummy, 1, INVENTORY ); + item &merged_item = item_at( dummy, "1", INVENTORY ); invlet_state merged_invlet_state = check_invlet( dummy, merged_item, expected_merged_invlet ); char merged_invlet = merged_item.invlet;