diff --git a/src/game.cpp b/src/game.cpp index 5039ea825517c..8ca25e9d7a96f 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -9392,81 +9392,76 @@ void game::reload( item_location &loc, bool prompt, bool empty ) refresh_all(); } -void game::reload() -{ - // general reload item menu will popup if: - // - user is unarmed; - // - weapon wielded can't be reloaded (bows can, they just reload before shooting automatically) - // - weapon wielded reloads before shooting (like bows) - if( !u.is_armed() || !u.can_reload( u.weapon ) || u.weapon.has_flag( "RELOAD_AND_SHOOT" ) ) { - vehicle *veh = veh_pointer_or_null( m.veh_at( u.pos() ) ); - turret_data turret; - if( veh && ( turret = veh->turret_query( u.pos() ) ) && turret.can_reload() ) { - item::reload_option opt = g->u.select_ammo( *turret.base(), true ); - if( opt ) { - g->u.assign_activity( activity_id( "ACT_RELOAD" ), opt.moves(), opt.qty() ); - g->u.activity.targets.emplace_back( turret.base() ); - g->u.activity.targets.push_back( std::move( opt.ammo ) ); - } - return; +void game::reload( bool try_everything ) +{ + // As a special streamlined activity, hitting reload repeatedly should: + // Reload wielded gun + // First reload a magazine if necessary. + // Then load said magazine into gun. + // Reload magazines that are compatible with the current gun. + // Reload other guns in inventory. + // Reload misc magazines in inventory. + std::vector reloadables = u.find_reloadables(); + std::sort( reloadables.begin(), reloadables.end(), + [this]( const item_location & a, const item_location & b ) { + const item *ap = a.get_item(); + const item *bp = b.get_item(); + // Current wielded weapon comes first. + if( this->u.is_wielding( *ap ) ) { + return true; } - - item_location item_loc = inv_map_splice( [&]( const item & it ) { - return u.rate_action_reload( it ) == HINT_GOOD; - }, _( "Reload item" ), 1, _( "You have nothing to reload." ) ); - - if( !item_loc ) { - add_msg( _( "Never mind." ) ); + if( this->u.is_wielding( *bp ) ) { + return false; + } + // Second sort by afiliation with wielded gun + const std::set compatible_magazines = this->u.weapon.magazine_compatible(); + const bool mag_ap = compatible_magazines.count( ap->typeId() ) > 0; + const bool mag_bp = compatible_magazines.count( bp->typeId() ) > 0; + if( mag_ap != mag_bp ) { + return mag_ap; + } + // Third sort by gun vs magazine, + if( ap->is_gun() != bp->is_gun() ) { + return ap->is_gun(); + } + // Finally sort by speed to reload. + return ( ap->get_reload_time() * ( ap->ammo_capacity() - ap->ammo_remaining() ) ) < + ( bp->get_reload_time() * ( bp->ammo_capacity() - bp->ammo_remaining() ) ); + } ); + for( item_location &candidate : reloadables ) { + std::vector ammo_list; + u.list_ammo( *candidate.get_item(), ammo_list, false ); + if( !ammo_list.empty() ) { + reload( candidate, false, false ); return; } - - reload( item_loc ); - - } else { - // As a special streamlined activity, hitting reload repeatedly should: - // Reload wielded gun - // First reload a magazine if necessary. - // Then load said magazine into gun. - // Reload magazines that are compatible with the current gun. - // Reload other guns in inventory. - // Reload misc magazines in inventory. - std::vector reloadables = u.find_reloadables(); - std::sort( reloadables.begin(), reloadables.end(), - [this]( const item_location & a, const item_location & b ) { - const item *ap = a.get_item(); - const item *bp = b.get_item(); - // Current wielded weapon comes first. - if( this->u.is_wielding( *ap ) ) { - return true; - } - if( this->u.is_wielding( *bp ) ) { - return false; - } - // Second sort by afiliation with wielded gun - const std::set compatible_magazines = this->u.weapon.magazine_compatible(); - const bool mag_ap = compatible_magazines.count( ap->typeId() ) > 0; - const bool mag_bp = compatible_magazines.count( bp->typeId() ) > 0; - if( mag_ap != mag_bp ) { - return mag_ap; - } - // Third sort by gun vs magazine, - if( ap->is_gun() != bp->is_gun() ) { - return ap->is_gun(); - } - // Finally sort by speed to reload. - return ( ap->get_reload_time() * ( ap->ammo_capacity() - ap->ammo_remaining() ) ) < - ( bp->get_reload_time() * ( bp->ammo_capacity() - bp->ammo_remaining() ) ); - } ); - for( item_location &candidate : reloadables ) { - std::vector ammo_list; - u.list_ammo( *candidate.get_item(), ammo_list, false ); - if( !ammo_list.empty() ) { - reload( candidate, false, false ); - return; - } - add_msg( _( "Nothing to reload." ) ); + } + // Just for testing, bail out here to avoid unwanted side effects. + if( !try_everything ) { + return; + } + // If we make it here and haven't found anything to reload, start looking elsewhere. + vehicle *veh = veh_pointer_or_null( m.veh_at( u.pos() ) ); + turret_data turret; + if( veh && ( turret = veh->turret_query( u.pos() ) ) && turret.can_reload() ) { + item::reload_option opt = g->u.select_ammo( *turret.base(), true ); + if( opt ) { + g->u.assign_activity( activity_id( "ACT_RELOAD" ), opt.moves(), opt.qty() ); + g->u.activity.targets.emplace_back( turret.base() ); + g->u.activity.targets.push_back( std::move( opt.ammo ) ); } + return; + } + item_location item_loc = inv_map_splice( [&]( const item & it ) { + return u.rate_action_reload( it ) == HINT_GOOD; + }, _( "Reload item" ), 1, _( "You have nothing to reload." ) ); + + if( !item_loc ) { + add_msg( _( "Never mind." ) ); + return; } + + reload( item_loc ); } // Unload a container, gun, or tool diff --git a/src/game.h b/src/game.h index d75e162bf2d3a..02a151c74867a 100644 --- a/src/game.h +++ b/src/game.h @@ -970,7 +970,7 @@ class game void mend( int pos = INT_MIN ); void autoattack(); public: - void reload(); // Reload a wielded gun/tool 'r' + void reload( bool try_everything = true ); // Reload a wielded gun/tool 'r' // Places the player at the specified point; hurts feet, lists items etc. void place_player( const tripoint &dest ); void place_player_overmap( const tripoint &om_dest ); diff --git a/tests/reloading_test.cpp b/tests/reloading_test.cpp index 6b1abac424cff..bda9306a59ca5 100644 --- a/tests/reloading_test.cpp +++ b/tests/reloading_test.cpp @@ -112,6 +112,17 @@ TEST_CASE( "reload_gun_with_swappable_magazine", "[reload],[gun]" ) REQUIRE( gun.ammo_remaining() == gun.ammo_capacity() ); } +void reload_a_revolver( player &dummy, item &gun, item &ammo ) +{ + while( gun.ammo_remaining() < gun.ammo_capacity() ) { + g->reload( false ); + REQUIRE( dummy.activity ); + process_activity( dummy ); + CHECK( gun.ammo_remaining() > 0 ); + CHECK( gun.ammo_current() == ammo.type->get_id() ); + } +} + TEST_CASE( "automatic_reloading_action", "[reload],[gun]" ) { player &dummy = g->u; @@ -123,7 +134,7 @@ TEST_CASE( "automatic_reloading_action", "[reload],[gun]" ) GIVEN( "an unarmed player" ) { REQUIRE( !dummy.is_armed() ); WHEN( "the player triggers auto reload" ) { - g->reload(); + g->reload( false ); THEN( "No activity is generated" ) { CHECK( !dummy.activity ); } @@ -139,20 +150,31 @@ TEST_CASE( "automatic_reloading_action", "[reload],[gun]" ) REQUIRE( dummy.weapon.can_reload_with( ammo.type->get_id() ) ); WHEN( "the player triggers auto reload until the revolver is full" ) { - while( dummy.weapon.ammo_remaining() < dummy.weapon.ammo_capacity() ) { - g->reload(); - REQUIRE( dummy.activity ); - process_activity( dummy ); - CHECK( dummy.weapon.ammo_remaining() > 0 ); - CHECK( dummy.weapon.ammo_current() == ammo.type->get_id() ); - } + reload_a_revolver( dummy, dummy.weapon, ammo ); WHEN( "the player triggers auto reload again" ) { - g->reload(); + g->reload( false ); THEN( "no activity is generated" ) { CHECK( !dummy.activity ); } } } + GIVEN( "the player has another gun with ammo" ) { + item &gun2 = dummy.i_add( item( "sw_610", 0, 0 ) ); + REQUIRE( gun2.ammo_remaining() == 0 ); + REQUIRE( gun2.can_reload_with( ammo.type->get_id() ) ); + WHEN( "the player triggers auto reload until the first revolver is full" ) { + reload_a_revolver( dummy, dummy.weapon, ammo ); + WHEN( "the player triggers auto reload until the second revolver is full" ) { + reload_a_revolver( dummy, gun2, ammo ); + WHEN( "the player triggers auto reload again" ) { + g->reload( false ); + THEN( "no activity is generated" ) { + CHECK( !dummy.activity ); + } + } + } + } + } } GIVEN( "a player wielding an unloaded gun, carrying an unloaded magazine, and carrying ammo for the magazine" ) { @@ -170,7 +192,7 @@ TEST_CASE( "automatic_reloading_action", "[reload],[gun]" ) REQUIRE( dummy.weapon.ammo_remaining() == 0 ); WHEN( "the player triggers auto reload" ) { - g->reload(); + g->reload( false ); REQUIRE( dummy.activity ); process_activity( dummy ); @@ -179,7 +201,7 @@ TEST_CASE( "automatic_reloading_action", "[reload],[gun]" ) CHECK( mag.contents.front().type == ammo.type ); } WHEN( "the player triggers auto reload again" ) { - g->reload(); + g->reload( false ); REQUIRE( dummy.activity ); process_activity( dummy ); @@ -187,7 +209,7 @@ TEST_CASE( "automatic_reloading_action", "[reload],[gun]" ) CHECK( dummy.weapon.ammo_remaining() > 0 ); } WHEN( "the player triggers auto reload again" ) { - g->reload(); + g->reload( false ); THEN( "No activity is generated" ) { CHECK( !dummy.activity ); } @@ -202,7 +224,7 @@ TEST_CASE( "automatic_reloading_action", "[reload],[gun]" ) REQUIRE( mag2.ammo_remaining() == 0 ); WHEN( "the player triggers auto reload" ) { - g->reload(); + g->reload( false ); REQUIRE( dummy.activity ); process_activity( dummy ); @@ -211,7 +233,7 @@ TEST_CASE( "automatic_reloading_action", "[reload],[gun]" ) CHECK( mag.contents.front().type == ammo.type ); } WHEN( "the player triggers auto reload again" ) { - g->reload(); + g->reload( false ); REQUIRE( dummy.activity ); process_activity( dummy ); @@ -219,7 +241,7 @@ TEST_CASE( "automatic_reloading_action", "[reload],[gun]" ) CHECK( dummy.weapon.ammo_remaining() > 0 ); } WHEN( "the player triggers auto reload again" ) { - g->reload(); + g->reload( false ); REQUIRE( dummy.activity ); process_activity( dummy ); @@ -228,7 +250,7 @@ TEST_CASE( "automatic_reloading_action", "[reload],[gun]" ) CHECK( mag2.contents.front().type == ammo.type ); } WHEN( "the player triggers auto reload again" ) { - g->reload(); + g->reload( false ); THEN( "No activity is generated" ) { CHECK( !dummy.activity ); }