Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[RDY]Make ranged cap a soft cap, add hard cap at 60 #20535

Merged
merged 2 commits into from
Mar 21, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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 @@ -90,8 +90,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 @@ -1829,7 +1837,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 @@ -1856,6 +1864,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 @@ -1964,7 +1977,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