bool requirement_data::has_comps( const inventory &crafting_inv, const std::vector< std::vector<T> > &vec, int batch ) { bool retval = true; int total_UPS_charges_used = 0; for( const auto &set_of_tools : vec ) { bool has_tool_in_set = false; int UPS_charges_used = std::numeric_limits<int>::max(); for( const auto &tool : set_of_tools ) { if( tool.has( crafting_inv, batch, [ &UPS_charges_used ]( int charges ) { UPS_charges_used = std::min( UPS_charges_used, charges ); } ) ) { tool.available = a_true; } else { tool.available = a_false; } has_tool_in_set = has_tool_in_set || tool.available == a_true; } if( !has_tool_in_set ) { retval = false; } if( UPS_charges_used != std::numeric_limits<int>::max() ) { total_UPS_charges_used += UPS_charges_used; } } if( total_UPS_charges_used > 0 && total_UPS_charges_used > crafting_inv.charges_of( "UPS" ) ) { return false; } return retval; }
std::vector<comp_selection<item_comp>> craft_command::check_item_components_missing( const inventory &map_inv ) const { std::vector<comp_selection<item_comp>> missing; for( const auto &item_sel : item_selections ) { itype_id type = item_sel.comp.type; item_comp component = item_sel.comp; long count = ( component.count > 0 ) ? component.count * batch_size : abs( component.count ); if( item::count_by_charges( type ) && count > 0 ) { switch( item_sel.use_from ) { case use_from_player: if( !crafter->has_charges( type, count ) ) { missing.push_back( item_sel ); } break; case use_from_map: if( !map_inv.has_charges( type, count ) ) { missing.push_back( item_sel ); } break; case use_from_both: if( !( crafter->charges_of( type ) + map_inv.charges_of( type ) >= count ) ) { missing.push_back( item_sel ); } break; case use_from_none: case cancel: break; } } else { // Counting by units, not charges. switch( item_sel.use_from ) { case use_from_player: if( !crafter->has_amount( type, count ) ) { missing.push_back( item_sel ); } break; case use_from_map: if( !map_inv.has_components( type, count ) ) { missing.push_back( item_sel ); } break; case use_from_both: if( !( crafter->amount_of( type ) + map_inv.amount_of( type ) >= count ) ) { missing.push_back( item_sel ); } break; case use_from_none: case cancel: break; } } } return missing; }
bool tool_comp::has( const inventory &crafting_inv, int batch, std::function<void( int )> visitor ) const { if( g->u.has_trait( trait_DEBUG_HS ) ) { return true; } if( !by_charges() ) { return crafting_inv.has_tools( type, std::abs( count ) ); } else { int charges_found = crafting_inv.charges_of( type, count * batch ); if( charges_found == count * batch ) { return true; } const auto &binned = crafting_inv.get_binned_items(); const auto iter = binned.find( type ); if( iter == binned.end() ) { return false; } bool has_UPS = false; for( const item *it : iter->second ) { it->visit_items( [&has_UPS]( const item * e ) { if( e->has_flag( "USE_UPS" ) ) { has_UPS = true; return VisitResponse::ABORT; } return VisitResponse::NEXT; } ); } if( has_UPS ) { int UPS_charges_used = crafting_inv.charges_of( "UPS", ( count * batch ) - charges_found ); if( visitor && UPS_charges_used + charges_found >= ( count * batch ) ) { visitor( UPS_charges_used ); } charges_found += UPS_charges_used; } return charges_found == count * batch; } }
/* selection of component if a recipe requirement has multiple options (e.g. 'duct tap' or 'welder') */ comp_selection<item_comp> player::select_item_component( const std::vector<item_comp> &components, int batch, inventory &map_inv, bool can_cancel ) { std::vector<item_comp> player_has; std::vector<item_comp> map_has; std::vector<item_comp> mixed; comp_selection<item_comp> selected; for( const auto &component : components ) { itype_id type = component.type; int count = ( component.count > 0 ) ? component.count * batch : abs( component.count ); bool pl = false, mp = false; if( item::count_by_charges( type ) && count > 0 ) { if( has_charges( type, count ) ) { player_has.push_back( component ); pl = true; } if( map_inv.has_charges( type, count ) ) { map_has.push_back( component ); mp = true; } if( !pl && !mp && charges_of( type ) + map_inv.charges_of( type ) >= count ) { mixed.push_back( component ); } } else { // Counting by units, not charges if( has_amount( type, count ) ) { player_has.push_back( component ); pl = true; } if( map_inv.has_components( type, count ) ) { map_has.push_back( component ); mp = true; } if( !pl && !mp && amount_of( type ) + map_inv.amount_of( type ) >= count ) { mixed.push_back( component ); } } } /* select 1 component to use */ if( player_has.size() + map_has.size() + mixed.size() == 1 ) { // Only 1 choice if( player_has.size() == 1 ) { selected.use_from = use_from_player; selected.comp = player_has[0]; } else if( map_has.size() == 1 ) { selected.use_from = use_from_map; selected.comp = map_has[0]; } else { selected.use_from = use_from_both; selected.comp = mixed[0]; } } else { // Let the player pick which component they want to use uimenu cmenu; // Populate options with the names of the items for( auto &map_ha : map_has ) { std::string tmpStr = item::nname( map_ha.type ) + _( " (nearby)" ); cmenu.addentry( tmpStr ); } for( auto &player_ha : player_has ) { cmenu.addentry( item::nname( player_ha.type ) ); } for( auto &elem : mixed ) { std::string tmpStr = item::nname( elem.type ) + _( " (on person & nearby)" ); cmenu.addentry( tmpStr ); } // Unlike with tools, it's a bad thing if there aren't any components available if( cmenu.entries.empty() ) { if( has_trait( trait_id( "DEBUG_HS" ) ) ) { selected.use_from = use_from_player; return selected; } debugmsg( "Attempted a recipe with no available components!" ); selected.use_from = cancel; return selected; } if( can_cancel ) { cmenu.addentry( -1, true, 'q', _( "Cancel" ) ); } // Get the selection via a menu popup cmenu.title = _( "Use which component?" ); cmenu.query(); if( cmenu.ret == static_cast<int>( map_has.size() + player_has.size() + mixed.size() ) ) { selected.use_from = cancel; return selected; } size_t uselection = static_cast<size_t>( cmenu.ret ); if( uselection < map_has.size() ) { selected.use_from = usage::use_from_map; selected.comp = map_has[uselection]; } else if( uselection < map_has.size() + player_has.size() ) { uselection -= map_has.size(); selected.use_from = usage::use_from_player; selected.comp = player_has[uselection]; } else { uselection -= map_has.size() + player_has.size(); selected.use_from = usage::use_from_both; selected.comp = mixed[uselection]; } } return selected; }
#include "catch/catch.hpp" #include "calendar.h" #include "inventory.h" #include "item.h" TEST_CASE( "visitable_summation" ) { inventory test_inv; item bottle_of_water( "bottle_plastic", calendar::turn ); item water_in_bottle( "water", calendar::turn ); water_in_bottle.charges = bottle_of_water.get_remaining_capacity_for_liquid( water_in_bottle ); bottle_of_water.put_in( water_in_bottle ); test_inv.add_item( bottle_of_water ); item unlimited_water( "water", 0, item::INFINITE_CHARGES ); test_inv.add_item( unlimited_water ); CHECK( test_inv.charges_of( "water", item::INFINITE_CHARGES ) > 1 ); }