Skip to content

Commit

Permalink
Refactor reload to only handle non-guns if everything else is reloaded
Browse files Browse the repository at this point in the history
  • Loading branch information
kevingranade committed Mar 22, 2019
1 parent 3428b13 commit 331f3b4
Show file tree
Hide file tree
Showing 3 changed files with 104 additions and 87 deletions.
135 changes: 65 additions & 70 deletions src/game.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<item_location> 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<itype_id> 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<item::reload_option> 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<item_location> 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<itype_id> 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<item::reload_option> 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
Expand Down
2 changes: 1 addition & 1 deletion src/game.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 );
Expand Down
54 changes: 38 additions & 16 deletions tests/reloading_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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 );
}
Expand All @@ -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" ) {
Expand All @@ -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 );

Expand All @@ -179,15 +201,15 @@ 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 );

THEN( "The magazine is loaded into the 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 );
}
Expand All @@ -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 );

Expand All @@ -211,15 +233,15 @@ 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 );

THEN( "The magazine is loaded into the 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 );

Expand All @@ -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 );
}
Expand Down

0 comments on commit 331f3b4

Please sign in to comment.