static void arm_shooter( npc &shooter, std::string gun_type, std::vector<std::string> mods = {} ) { shooter.remove_weapon(); itype_id gun_id( gun_type ); // Give shooter a loaded gun of the requested type. item &gun = shooter.i_add( item( gun_id ) ); const itype_id ammo_id = gun.ammo_default(); if( gun.magazine_integral() ) { item &ammo = shooter.i_add( item( ammo_id, calendar::turn, gun.ammo_capacity() ) ); REQUIRE( gun.is_reloadable_with( ammo_id ) ); REQUIRE( shooter.can_reload( gun, ammo_id ) ); gun.reload( shooter, item_location( shooter, &ammo ), gun.ammo_capacity() ); } else { const itype_id magazine_id = gun.magazine_default(); item &magazine = shooter.i_add( item( magazine_id ) ); item &ammo = shooter.i_add( item( ammo_id, calendar::turn, magazine.ammo_capacity() ) ); REQUIRE( magazine.is_reloadable_with( ammo_id ) ); REQUIRE( shooter.can_reload( magazine, ammo_id ) ); magazine.reload( shooter, item_location( shooter, &ammo ), magazine.ammo_capacity() ); gun.reload( shooter, item_location( shooter, &magazine ), magazine.ammo_capacity() ); } for( auto mod : mods ) { gun.contents.push_back( item( itype_id( mod ) ) ); } shooter.wield( gun ); }
static item_location inv_internal( player &u, const inventory_selector_preset &preset, const std::string &title, int radius, const std::string &none_message ) { u.inv.restack( &u ); u.inv.sort(); inventory_pick_selector inv_s( u, preset ); inv_s.set_title( title ); inv_s.set_display_stats( false ); inv_s.add_character_items( u ); inv_s.add_nearby_items( radius ); if( inv_s.empty() ) { const std::string msg = none_message.empty() ? _( "You don't have the necessary item at hand." ) : none_message; popup( msg, PF_GET_KEY ); return item_location(); } return inv_s.execute(); }
item_location inventory_selector::execute_pick_map( const std::string &title, std::unordered_map<item *, item_location> &opts ) { prepare_columns( false ); while( true ) { display( title, SM_PICK ); const std::string action = ctxt.handle_input(); const long ch = ctxt.get_raw_input().get_first_input(); const auto entry = find_entry_by_invlet( ch ); if( entry != nullptr ) { item *it = const_cast<item *>( entry->it ); const auto iter = opts.find( it ); return ( iter != opts.end() ) ? std::move( iter->second ) : item_location( u, it ); } else if( action == "QUIT" ) { return item_location(); } else if( action == "RIGHT" || action == "CONFIRM" ) { const auto selected = get_active_column().get_selected(); const auto it = const_cast<item *>( selected.it ); // Item in inventory if( selected.item_pos != INT_MIN ) { return item_location( u, it ); } // Item on ground or in vehicle auto iter = opts.find( it ); if( iter != opts.end() ) { return std::move( iter->second ); } return item_location(); } else { on_action( action ); } } }
item_location game_menus::inv::saw_barrel( player &p, item &tool ) { const auto actor = dynamic_cast<const saw_barrel_actor *> ( tool.type->get_use( "saw_barrel" )->get_actor_ptr() ); if( !actor ) { debugmsg( "Tried to use a wrong item." ); return item_location(); } return inv_internal( p, saw_barrel_inventory_preset( p, tool, *actor ), _( "Saw barrel" ), 1, _( "You don't have any guns." ), string_format( _( "Choose a weapon to use your %s on" ), tool.tname( 1, false ).c_str() ) ); }
item_location game_menus::inv::holster( player &p, item &holster ) { const std::string holster_name = holster.tname( 1, false ); const auto actor = dynamic_cast<const holster_actor *> ( holster.type->get_use( "holster" )->get_actor_ptr() ); if( !actor ) { const std::string msg = string_format( _( "You can't put anything into your %s." ), holster_name.c_str() ); popup( msg, PF_GET_KEY ); return item_location(); } const std::string title = actor->holster_prompt.empty() ? _( "Holster item" ) : _( actor->holster_prompt.c_str() ); const std::string hint = string_format( _( "Choose a weapon to put into your %s" ), holster_name.c_str() ); return inv_internal( p, holster_inventory_preset( p, *actor ), title, 1, string_format( _( "You have no weapons you could put into your %s." ), holster_name.c_str() ), hint ); }
const item_location turret_data::base() const { return item_location( vehicle_cursor( *veh, veh->index_of_part( part ) ), &part->base ); }
item_location game::inv_map_splice( item_filter inv_filter, item_filter ground_filter, item_filter vehicle_filter, const std::string &title, int radius ) { inventory_selector inv_s( false, false, title ); // first get matching items from the inventory u.inv.restack( &u ); u.inv.sort(); inv_s.make_item_list( u.inv.slice_filter_by( inv_filter ) ); std::list<item_category> categories; int rank = -1000; // items are stacked per tile considering vehicle and map tiles separately // in the below loops identical items on the same tile are grouped into lists // each element of stacks represents one tile and is a vector of such lists std::vector<std::vector<std::list<item>>> stacks; // an indexed_invslice is created for each map or vehicle tile // each list of items created above for the tile will be added to it std::vector<indexed_invslice> slices; // inv_s.first_item will later contain the chosen item as a pointer to first item // of one of the above lists so use this as the key when storing the item location std::unordered_map<item *, item_location> opts; // the closest 10 items also have their location added to the invlets vector const char min_invlet = '0'; const char max_invlet = '9'; char cur_invlet = min_invlet; std::vector<item_location> invlets; for( const auto &pos : closest_tripoints_first( radius, g->u.pos() ) ) { // second get all matching items on the map within radius if( m.accessible_items( g->u.pos(), pos, radius ) ) { auto items = m.i_at( pos ); // create a new slice and stack for the current map tile stacks.emplace_back(); slices.emplace_back(); // reserve sufficient capacity to ensure reallocation is not required auto ¤t_stack = stacks.back(); current_stack.reserve( items.size() ); for( item &it : items ) { if( ground_filter( it ) ) { auto match = std::find_if( current_stack.begin(), current_stack.end(), [&]( const std::list<item> &e ) { return it.stacks_with( e.back() ); } ); if( match != current_stack.end() ) { match->push_back( it ); } else { // item doesn't stack with any previous so start new list and append to current indexed_invslice current_stack.emplace_back( 1, it ); slices.back().emplace_back( ¤t_stack.back(), INT_MIN ); opts.emplace( ¤t_stack.back().front(), item_location( pos, &it ) ); if( cur_invlet <= max_invlet ) { current_stack.back().front().invlet = cur_invlet++; invlets.emplace_back( pos, &it ); } else { current_stack.back().front().invlet = 0; } } } } std::string name = trim( std::string( _( "GROUND" ) ) + " " + direction_suffix( g->u.pos(), pos ) ); categories.emplace_back( name, name, rank-- ); inv_s.make_item_list( slices.back(), &categories.back() ); } // finally get all matching items in vehicle cargo spaces int part = -1; vehicle *veh = m.veh_at( pos, part ); if( veh && part >= 0 ) { part = veh->part_with_feature( part, "CARGO" ); if( part != -1 ) { auto items = veh->get_items( part ); // create a new slice and stack for the current vehicle part stacks.emplace_back(); slices.emplace_back(); // reserve sufficient capacity to ensure reallocation is not required auto ¤t_stack = stacks.back(); current_stack.reserve( items.size() ); for( item &it : items ) { if( vehicle_filter( it ) ) { auto match = std::find_if( current_stack.begin(), current_stack.end(), [&]( const std::list<item> &e ) { return it.stacks_with( e.back() ); } ); if( match != current_stack.end() ) { match->push_back( it ); } else { // item doesn't stack with any previous so start new list and append to current indexed_invslice current_stack.emplace_back( 1, it ); slices.back().emplace_back( ¤t_stack.back(), INT_MIN ); opts.emplace( ¤t_stack.back().front(), item_location( vehicle_cursor( *veh, part ), &it ) ); if( cur_invlet <= max_invlet ) { current_stack.back().front().invlet = cur_invlet++; invlets.emplace_back( vehicle_cursor( *veh, part ), &it ); } else { current_stack.back().front().invlet = 0; } } } } std::string name = trim( std::string( _( "VEHICLE" ) ) + " " + direction_suffix( g->u.pos(), pos ) ); categories.emplace_back( name, name, rank-- ); inv_s.make_item_list( slices.back(), &categories.back() ); } } } inv_s.prepare_paging(); while( true ) { inv_s.display(); const std::string action = inv_s.ctxt.handle_input(); const long ch = inv_s.ctxt.get_raw_input().get_first_input(); const int item_pos = g->u.invlet_to_position( ch ); if( item_pos != INT_MIN ) { // Indexed item in inventory inv_s.set_to_drop( item_pos, 0 ); return item_location( u, inv_s.first_item ); } else if( ch >= min_invlet && ch <= max_invlet ) { // Indexed item on ground or in vehicle if( (long)invlets.size() > ch - min_invlet ) { return std::move( invlets[ch - min_invlet] ); } } else if( inv_s.handle_movement( action ) ) { // continue with comparison below } else if( action == "QUIT" ) { return item_location(); } else if( action == "RIGHT" || action == "CONFIRM" ) { inv_s.set_selected_to_drop( 0 ); // Item in inventory if( inv_s.get_selected_item_position() != INT_MIN ) { return item_location( u, inv_s.first_item ); } // Item on ground or in vehicle auto it = opts.find( inv_s.first_item ); if( it != opts.end() ) { return std::move( it->second ); } return item_location(); } } }
item_location game::inv_map_splice( item_filter inv_filter, item_filter ground_filter, item_filter vehicle_filter, const std::string &title, int radius, const std::string &none_message ) { u.inv.restack( &u ); u.inv.sort(); inventory_selector inv_s( u, inv_filter ); std::list<item_category> categories; int rank = -1000; // items are stacked per tile considering vehicle and map tiles separately // in the below loops identical items on the same tile are grouped into lists // each element of stacks represents one tile and is a vector of such lists std::vector<std::vector<std::list<item>>> stacks; // an indexed_invslice is created for each map or vehicle tile // each list of items created above for the tile will be added to it std::vector<indexed_invslice> slices; // of one of the above lists so use this as the key when storing the item location std::unordered_map<item *, item_location> opts; // the closest 10 items also have their location added to the invlets vector const char min_invlet = '0'; const char max_invlet = '9'; char cur_invlet = min_invlet; for( const auto &pos : closest_tripoints_first( radius, g->u.pos() ) ) { // second get all matching items on the map within radius if( m.accessible_items( g->u.pos(), pos, radius ) ) { auto items = m.i_at( pos ); // create a new slice and stack for the current map tile stacks.emplace_back(); slices.emplace_back(); // reserve sufficient capacity to ensure reallocation is not required auto ¤t_stack = stacks.back(); current_stack.reserve( items.size() ); for( item &it : items ) { if( ground_filter( it ) ) { auto match = std::find_if( current_stack.begin(), current_stack.end(), [&]( const std::list<item> &e ) { return it.stacks_with( e.back() ); } ); if( match != current_stack.end() ) { match->push_back( it ); } else { // item doesn't stack with any previous so start new list and append to current indexed_invslice current_stack.emplace_back( 1, it ); slices.back().emplace_back( ¤t_stack.back(), INT_MIN ); opts.emplace( ¤t_stack.back().front(), item_location( pos, &it ) ); current_stack.back().front().invlet = ( cur_invlet <= max_invlet ) ? cur_invlet++ : 0; } } } std::string name = trim( std::string( _( "GROUND" ) ) + " " + direction_suffix( g->u.pos(), pos ) ); categories.emplace_back( name, name, rank++ ); inv_s.add_entries( slices.back(), &categories.back() ); } // finally get all matching items in vehicle cargo spaces int part = -1; vehicle *veh = m.veh_at( pos, part ); if( veh && part >= 0 ) { part = veh->part_with_feature( part, "CARGO" ); if( part != -1 ) { auto items = veh->get_items( part ); // create a new slice and stack for the current vehicle part stacks.emplace_back(); slices.emplace_back(); // reserve sufficient capacity to ensure reallocation is not required auto ¤t_stack = stacks.back(); current_stack.reserve( items.size() ); for( item &it : items ) { if( vehicle_filter( it ) ) { auto match = std::find_if( current_stack.begin(), current_stack.end(), [&]( const std::list<item> &e ) { return it.stacks_with( e.back() ); } ); if( match != current_stack.end() ) { match->push_back( it ); } else { // item doesn't stack with any previous so start new list and append to current indexed_invslice current_stack.emplace_back( 1, it ); slices.back().emplace_back( ¤t_stack.back(), INT_MIN ); opts.emplace( ¤t_stack.back().front(), item_location( vehicle_cursor( *veh, part ), &it ) ); current_stack.back().front().invlet = ( cur_invlet <= max_invlet ) ? cur_invlet++ : 0; } } } std::string name = trim( std::string( _( "VEHICLE" ) ) + " " + direction_suffix( g->u.pos(), pos ) ); categories.emplace_back( name, name, rank-- ); inv_s.add_entries( slices.back(), &categories.back() ); } } } if( inv_s.empty() ) { const std::string msg = ( none_message.empty() ) ? _( "You don't have the necessary item at hand." ) : none_message; popup( msg, PF_GET_KEY ); return item_location(); } return inv_s.execute_pick_map( title, opts ); }
CHECK( mag.is_reloadable_with( bad_ammo ) == false ); CHECK( p.can_reload( mag ) == true ); CHECK( p.can_reload( mag, ammo_id ) == true ); CHECK( p.can_reload( mag, alt_ammo ) == true ); CHECK( p.can_reload( mag, bad_ammo ) == false ); CHECK( mag.ammo_type() == gun_ammo ); CHECK( mag.ammo_capacity() == mag_cap ); CHECK( mag.ammo_current() == "null" ); CHECK( mag.ammo_data() == nullptr ); GIVEN( "An empty magazine" ) { CHECK( mag.ammo_remaining() == 0 ); WHEN( "the magazine is reloaded with incompatible ammo" ) { item& ammo = p.i_add( item( bad_ammo ) ); bool ok = mag.reload( g->u, item_location( p, &ammo ), mag.ammo_capacity() ); THEN( "reloading should fail" ) { REQUIRE_FALSE( ok ); REQUIRE( mag.ammo_remaining() == 0 ); } } WHEN( "the magazine is loaded with an excess of ammo" ) { item& ammo = p.i_add( item( ammo_id, calendar::turn, mag_cap + 5 ) ); REQUIRE( ammo.charges == mag_cap + 5 ); bool ok = mag.reload( g->u, item_location( p, &ammo ), mag.ammo_capacity() ); THEN( "reloading is sucessful" ) { REQUIRE( ok ); AND_THEN( "the current ammo is updated" ) {