Skip to content

Commit

Permalink
Merge pull request #20535 from Coolthulhu/range-uncap
Browse files Browse the repository at this point in the history
[RDY]Make ranged cap a soft cap, add hard cap at 60
  • Loading branch information
Rivet-the-Zombie committed Mar 21, 2017
2 parents 0d7bf2c + ff95f0d commit e5f5d30
Show file tree
Hide file tree
Showing 8 changed files with 45 additions and 31 deletions.
2 changes: 1 addition & 1 deletion lua/class_definitions.lua
Original file line number Diff line number Diff line change
Expand Up @@ -553,7 +553,7 @@ classes = {
{ name = "get_sick", rval = nil, args = { } },
{ name = "get_size", rval = "m_size", args = { } },
{ name = "get_stamina_max", rval = "int", args = { } },
{ name = "get_weapon_dispersion", rval = "float", args = { "item" } },
{ name = "get_weapon_dispersion", rval = "float", args = { "item", "float" } },
{ name = "get_wind_resistance", rval = "int", args = { "body_part" } },
{ name = "global_omt_location", rval = "tripoint", args = { } },
{ name = "global_sm_location", rval = "tripoint", args = { } },
Expand Down
8 changes: 4 additions & 4 deletions src/dump.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -337,7 +337,7 @@ bool game::dump_stats( const std::string& what, dump_mode mode, const std::vecto
scol = -1; // unsorted output so graph columns have predictable ordering

header = { "Name" };
for( int range = 1; range <= MAX_RANGE; ++range ) {
for( int range = 1; range <= RANGE_HARD_CAP; ++range ) {
header.push_back( to_string( range ) );
}

Expand All @@ -353,17 +353,17 @@ bool game::dump_stats( const std::string& what, dump_mode mode, const std::vecto
recoil -= adj;
}

double dispersion = who.get_weapon_dispersion( gun ) + recoil;

std::vector<std::string> r;
r.push_back( string_format( "%s %s (%dmv aiming)", who.get_name().c_str(), gun.tname().c_str(), aim_moves ) );

for( int range = 1; range <= MAX_RANGE; ++range ) {
for( int range = 1; range <= RANGE_HARD_CAP; ++range ) {
if( range > gun.gun_range( &who ) ) {
r.push_back( "0" );
continue;
}

double dispersion = who.get_weapon_dispersion( gun, range ) + recoil;

// We want to find the long-run average damage per shot for this dispersion.

// The base damage is the same for every shot. The damage multiplier varies depending
Expand Down
6 changes: 4 additions & 2 deletions src/game_constants.h
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,10 @@
/** Maximum (effective) level for a stat */
#define MAX_STAT 20

/** Maximum range for aimed weapons */
#define MAX_RANGE 30
/** Maximum range at which only standard dispersion applies */
#define RANGE_SOFT_CAP 30
/** Maximum range at which ranged attacks can be executed */
#define RANGE_HARD_CAP 60

/** Accuracy levels which a shots tangent must be below */
constexpr double accuracy_headshot = 0.1;
Expand Down
2 changes: 1 addition & 1 deletion src/item.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4051,7 +4051,7 @@ int item::gun_range( bool with_ammo ) const
if( with_ammo && ammo_data() ) {
ret += ammo_data()->ammo->range;
}
return std::min( std::max( 0, ret ), MAX_RANGE );
return std::min( std::max( 0, ret ), RANGE_HARD_CAP );
}

int item::gun_range( const player *p ) const
Expand Down
13 changes: 3 additions & 10 deletions src/npcmove.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1261,19 +1261,12 @@ int npc::confident_gun_mode_range( const item::gun_mode &gun, int at_recoil ) co
return 0;
}

double deviation = get_weapon_dispersion( *gun.target ) + at_recoil;
// Halve to get expected values
deviation /= 2;
// Convert from MoA back to quarter-degrees.
deviation /= 15;

int ret = std::min( int( confidence_mult() * 360 / deviation ), gun->gun_range( this ) );

double ret = gun_current_range( *gun.target, at_recoil, 50 / confidence_mult(), accuracy_goodhit );
// 5 round burst equivalent to ~2 individually aimed shots
ret /= std::max( sqrt( gun.qty / 1.5 ), 1.0 );

add_msg( m_debug, "confident_gun_mode_range (%s=%d)", gun.mode.c_str(), ret );
return std::max( ret, 1 );
add_msg( m_debug, "confident_gun_mode_range (%s=%d)", gun.mode.c_str(), (int)ret );
return std::max<int>( ret, 1 );
}

int npc::confident_throw_range( const item &thrown ) const
Expand Down
7 changes: 5 additions & 2 deletions src/player.h
Original file line number Diff line number Diff line change
Expand Up @@ -504,8 +504,11 @@ class player : public Character, public JsonSerializer, public JsonDeserializer
*/
void melee_attack( Creature &t, bool allow_special, const matec_id &force_technique, int hitspread ) override;

/** Returns a weapon's modified dispersion value */
double get_weapon_dispersion( const item &obj ) const;
/**
* Returns a weapon's modified dispersion value.
* @param range Distance to target against which we're calculating the dispersion
*/
double get_weapon_dispersion( const item &obj, float range ) const;

/** Returns true if a gun misfires, jams, or has other problems, else returns false */
bool handle_gun_damage( item &firing );
Expand Down
27 changes: 20 additions & 7 deletions src/ranged.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -451,7 +451,8 @@ double player::gun_current_range( const item& gun, double penalty, unsigned chan
}

// calculate base dispersion
double dispersion = get_weapon_dispersion( gun ) + ( penalty < 0 ? recoil_total() : penalty );
double dispersion = get_weapon_dispersion( gun, RANGE_SOFT_CAP ) + ( penalty < 0 ? recoil_total() : penalty );

double sigma = dispersion / dispersion_sigmas;

// angle such that chance% of dispersion rolls are <= that angle
Expand All @@ -466,7 +467,14 @@ double player::gun_current_range( const item& gun, double penalty, unsigned chan
// D = (0.5*T**2 / (1 - cos V)) ** 0.5
double range = sqrt( 0.5 * missed_by_tiles * missed_by_tiles / ( 1 - cos( ARCMIN( max_dispersion ) ) ) );

return std::min( range, ( double )gun.gun_range( this ) );
range = std::min( range, ( double )gun.gun_range( this ) );
if( range > RANGE_SOFT_CAP ) {
// This is a special case that is a pain to calculate since dispersion depends on range
// Just interpolate linearly - the actual function is linear enough
return lerp_clamped( RANGE_SOFT_CAP, RANGE_HARD_CAP, 1.0 - dispersion / 180.0 );
}

return range;
}

double player::gun_engagement_range( const item &gun, engagement opt ) const
Expand Down Expand Up @@ -615,7 +623,7 @@ int player::fire_gun( const tripoint &target, int shots, item& gun )
break;
}

double dispersion = get_weapon_dispersion( gun ) + recoil_total();
double dispersion = get_weapon_dispersion( gun, rl_dist( pos(), aim ) ) + recoil_total();
int range = rl_dist( pos(), aim );

auto shot = projectile_attack( make_gun_projectile( gun ), aim, dispersion );
Expand Down Expand Up @@ -646,7 +654,7 @@ int player::fire_gun( const tripoint &target, int shots, item& gun )

if( shot.hit_critter ) {
hits++;
if( range > double( get_skill_level( skill_gun ) ) / MAX_SKILL * MAX_RANGE ) {
if( range > double( get_skill_level( skill_gun ) ) / MAX_SKILL * RANGE_SOFT_CAP ) {
xp += range; // shots at sufficient distance train marksmanship
}
}
Expand Down Expand Up @@ -1065,8 +1073,8 @@ static int print_aim( const player &p, WINDOW *w, int line_number, item *weapon,
// Creature::projectile_attack() into shared methods.
// Dodge is intentionally not accounted for.

const double dispersion = p.get_weapon_dispersion( *weapon ) + predicted_recoil + p.recoil_vehicle();
const double range = rl_dist( p.pos(), target.pos() );
const double dispersion = p.get_weapon_dispersion( *weapon, range ) + predicted_recoil + p.recoil_vehicle();

// This is a relative measure of how steady the player's aim is,
// 0 it is the best the player can do.
Expand Down Expand Up @@ -1838,7 +1846,7 @@ static bool is_driving( const player &p )


// utility functions for projectile_attack
double player::get_weapon_dispersion( const item &obj ) const
double player::get_weapon_dispersion( const item &obj, float range ) const
{
double dispersion = obj.gun_dispersion();

Expand All @@ -1865,6 +1873,11 @@ double player::get_weapon_dispersion( const item &obj ) const
dispersion *= 4;
}

// @todo Scale the range penalty with something (but don't allow it to get below 2.0*range)
if( range > RANGE_SOFT_CAP ) {
dispersion += ( range - RANGE_SOFT_CAP ) * 3.0f;
}

return std::max( dispersion, 0.0 );
}

Expand Down Expand Up @@ -1973,7 +1986,7 @@ double player::gun_value( const item &weap, long ammo ) const

item tmp = weap;
tmp.ammo_set( weap.ammo_default() );
int total_dispersion = get_weapon_dispersion( tmp ) + effective_dispersion( tmp.sight_dispersion() );
int total_dispersion = get_weapon_dispersion( tmp, RANGE_SOFT_CAP ) + effective_dispersion( tmp.sight_dispersion() );

if( def_ammo_i != nullptr && def_ammo_i->ammo != nullptr ) {
const islot_ammo &def_ammo = *def_ammo_i->ammo;
Expand Down
11 changes: 7 additions & 4 deletions tests/gun_aiming.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ static void test_internal( const npc& who, const std::vector<item> &guns )
for( int chance = 10; chance < 100; chance += 20 ) {
for( double recoil = 0; recoil < 1000; recoil += 50 ) {
double range = who.gun_current_range( gun, recoil, chance, accuracy );
double dispersion = who.get_weapon_dispersion( gun ) + recoil;
double dispersion = who.get_weapon_dispersion( gun, RANGE_SOFT_CAP ) + recoil;

CAPTURE( gun.tname() );
CAPTURE( accuracy );
Expand All @@ -48,7 +48,9 @@ static void test_internal( const npc& who, const std::vector<item> &guns )

// Aiming at human
double target_size = who.ranged_target_size();
if( range == gun.gun_range( &who ) ) {
if( range > RANGE_SOFT_CAP ) {
// No good approximation for this edge case yet
} else if( range == gun.gun_range( &who ) ) {
CHECK( who.projectile_attack_chance( dispersion, range, accuracy, target_size ) >= chance / 100.0 );
} else {
CHECK( who.projectile_attack_chance( dispersion, range, accuracy, target_size ) == Approx( chance / 100.0 ).epsilon( 0.0005 ) );
Expand Down Expand Up @@ -128,7 +130,7 @@ static void test_internal( const npc& who, const std::vector<item> &guns )
double recoil = MIN_RECOIL;
double chance = 0.5;
double range = who.gun_current_range( gun, recoil, chance, accuracy );
double dispersion = who.get_weapon_dispersion( gun ) + recoil;
double dispersion = who.get_weapon_dispersion( gun, RANGE_SOFT_CAP ) + recoil;
CHECK( who.projectile_attack_chance( dispersion, range, accuracy, target_size + 1 ) >=
who.projectile_attack_chance( dispersion, range, accuracy, target_size ) );
}
Expand All @@ -153,7 +155,8 @@ TEST_CASE( "gun_aiming", "[gun] [aim]" ) {
WHEN( "many shots are fired at human-sized target" ) {
THEN( "the distribution of accuracies is as expected" ) {
double target_size = who.ranged_target_size();
for( int range = 0; range <= MAX_RANGE; ++range ) {
// Don't test range above soft cap - there is no good approxmation for that yet
for( int range = 0; range <= RANGE_SOFT_CAP; ++range ) {
for( int dispersion = 0; dispersion < 1200; dispersion += 50 ) {
test_distribution( who, dispersion, range, target_size );
}
Expand Down

0 comments on commit e5f5d30

Please sign in to comment.