diff --git a/src/ranged.cpp b/src/ranged.cpp index 39b1cb0f459cb..c4d0e2cab404e 100644 --- a/src/ranged.cpp +++ b/src/ranged.cpp @@ -838,13 +838,13 @@ static int find_target( const std::vector &t, const tripoint &tpos ) return -1; } -static void do_aim( player &p, const item &relevant ) +static void do_aim( player &p, const item &relevant, const double min_recoil ) { const double aim_amount = p.aim_per_move( relevant, p.recoil ); - if( aim_amount > 0 ) { + if( aim_amount > 0 && p.recoil > min_recoil ) { // Increase aim at the cost of moves p.mod_moves( -1 ); - p.recoil = std::max( 0.0, p.recoil - aim_amount ); + p.recoil = std::max( min_recoil, p.recoil - aim_amount ); } else { // If aim is already maxed, we're just waiting, so pass the turn. p.set_moves( 0 ); @@ -1022,6 +1022,21 @@ static int print_ranged_chance( const player &p, const catacurses::window &w, in return line_number; } +// Handle capping aim level when the player cannot see the target tile or there is nothing to aim at. +static double calculate_aim_cap( const player &p, const tripoint &target ) +{ + double min_recoil = 0.0; + const Creature *victim = g->critter_at( target, true ); + if( victim == nullptr || !p.sees( *victim ) ) { + const int range = rl_dist( p.pos(), target ); + // Get angle of triangle that spans the target square. + const double angle = atan2( 1, range ); + // Convert from radians to arcmin. + min_recoil = 60 * 180 * angle / M_PI; + } + return min_recoil; +} + static int print_aim( const player &p, const catacurses::window &w, int line_number, input_context &ctxt, item *weapon, const double target_size, const tripoint &pos, double predicted_recoil ) @@ -1034,7 +1049,9 @@ static int print_aim( const player &p, const catacurses::window &w, int line_num dispersion_sources dispersion = p.get_weapon_dispersion( *weapon ); dispersion.add_range( p.recoil_vehicle() ); - const double min_dispersion = p.effective_dispersion( p.weapon.sight_dispersion() ); + const double min_recoil = calculate_aim_cap( p, pos ); + const double effective_recoil = p.effective_dispersion( p.weapon.sight_dispersion() ); + const double min_dispersion = std::max( min_recoil, effective_recoil ); const double steadiness_range = MAX_RECOIL - min_dispersion; // This is a relative measure of how steady the player's aim is, // 0 is the best the player can do. @@ -1597,8 +1614,9 @@ std::vector target_handler::target_ui( player &pc, target_mode mode, recoil_pos = dst; set_last_target( dst ); + const double min_recoil = calculate_aim_cap( pc, dst ); for( int i = 0; i < 10; ++i ) { - do_aim( pc, *relevant ); + do_aim( pc, *relevant, min_recoil ); } if( pc.moves <= 0 ) { // We've run out of moves, clear target vector, but leave target selected. @@ -1659,12 +1677,13 @@ std::vector target_handler::target_ui( player &pc, target_mode mode, } int aim_threshold = it->threshold; set_last_target( dst ); + const double min_recoil = calculate_aim_cap( pc, dst ); do { - do_aim( pc, relevant ? *relevant : null_item_reference() ); - } while( pc.moves > 0 && pc.recoil > aim_threshold && pc.recoil - sight_dispersion > 0 ); + do_aim( pc, relevant ? *relevant : null_item_reference(), min_recoil ); + } while( pc.moves > 0 && pc.recoil > aim_threshold && pc.recoil - sight_dispersion > min_recoil ); if( pc.recoil <= aim_threshold || - pc.recoil - sight_dispersion == 0 ) { + pc.recoil - sight_dispersion == min_recoil ) { // If we made it under the aim threshold, go ahead and fire. // Also fire if we're at our best aim level already. pc.view_offset = old_offset;