-
Notifications
You must be signed in to change notification settings - Fork 4.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
f94cfe1
commit 0b3ba14
Showing
1 changed file
with
163 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,163 @@ | ||
#include <cstdlib> | ||
|
||
#include "avatar.h" | ||
#include "calendar.h" | ||
#include "creature.h" | ||
#include "game.h" | ||
#include "item.h" | ||
#include "player.h" | ||
#include "type_id.h" | ||
|
||
#include "catch/catch.hpp" | ||
#include "player_helpers.h" | ||
|
||
// Run a large number of trials of a player attacking a monster with a given weapon, | ||
// and return the average damage done per second. | ||
static double weapon_dps_trials( avatar &attacker, monster &defender, item &weapon ) | ||
{ | ||
constexpr int trials = 1000; | ||
|
||
int total_damage = 0; | ||
int total_moves = 0; | ||
|
||
clear_character( attacker ); | ||
REQUIRE( attacker.can_wield( weapon ).success() ); | ||
REQUIRE( attacker.wield( weapon ) ); | ||
|
||
// FIXME: This is_wielding check always fails | ||
//REQUIRE( attacker.is_wielding( weapon ) ); | ||
|
||
for( int i = 0; i < trials; i++ ) { | ||
// Keep track of attacker's moves and defender's HP | ||
const int before_moves = attacker.get_moves(); | ||
const int starting_hp = defender.get_hp(); | ||
// Attack once | ||
attacker.melee_attack( defender, false ); | ||
// Tally total damage and moves | ||
total_damage += std::max( 0, starting_hp - defender.get_hp() ); | ||
total_moves += std::abs( attacker.get_moves() - before_moves ); | ||
// Reset for the next attack | ||
attacker.empty_skills(); // Don't learn | ||
defender.set_hp( starting_hp ); // Don't die | ||
} | ||
|
||
// Scale damage-per-move to damage-per-second | ||
// 1 second == 100 moves | ||
return 100.0f * total_damage / total_moves; | ||
} | ||
|
||
static void check_actual_dps( avatar &attacker, monster &defender, item &weapon ) | ||
{ | ||
clear_character( attacker ); | ||
double expect_dps = weapon.effective_dps( attacker, defender ); | ||
double actual_dps = weapon_dps_trials( attacker, defender, weapon ); | ||
CHECK( actual_dps == Approx( expect_dps ).margin( 0.01f ) ); | ||
} | ||
|
||
TEST_CASE( "effective damage per second", "[effective][dps]" ) | ||
{ | ||
avatar &dummy = g->u; | ||
clear_character( dummy ); | ||
|
||
item rock( "test_rock" ); | ||
item plank( "test_2x4" ); | ||
item knife( "knife_trench" ); | ||
|
||
SECTION( "against a debug monster with no armor or dodge" ) { | ||
monster mummy( mtype_id( "debug_mon" ) ); | ||
|
||
CHECK( plank.effective_dps( dummy, mummy ) == Approx( 4.79f ).margin( 0.01f ) ); | ||
CHECK( knife.effective_dps( dummy, mummy ) == Approx( 20.16f ).margin( 0.01f ) ); | ||
} | ||
|
||
SECTION( "against an agile target" ) { | ||
monster smoker( mtype_id( "mon_zombie_smoker" ) ); | ||
REQUIRE( smoker.get_dodge() >= 4 ); | ||
|
||
CHECK( plank.effective_dps( dummy, smoker ) == Approx( 2.51f ).margin( 0.01f ) ); | ||
CHECK( knife.effective_dps( dummy, smoker ) == Approx( 10.57f ).margin( 0.01f ) ); | ||
} | ||
|
||
SECTION( "against an armored target" ) { | ||
monster soldier( mtype_id( "mon_zombie_soldier" ) ); | ||
|
||
CHECK( plank.effective_dps( dummy, soldier ) == Approx( 1.11f ).margin( 0.01f ) ); | ||
CHECK( knife.effective_dps( dummy, soldier ) == Approx( 2.22f ).margin( 0.01f ) ); | ||
} | ||
|
||
SECTION( "effect of STR and DEX on damage per second" ) { | ||
monster mummy( mtype_id( "debug_mon" ) ); | ||
|
||
SECTION( "STR 6, DEX 6" ) { | ||
dummy.str_max = 6; | ||
dummy.dex_max = 6; | ||
|
||
CHECK( rock.effective_dps( dummy, mummy ) == Approx( 6.55f ).margin( 0.01f ) ); | ||
CHECK( plank.effective_dps( dummy, mummy ) == Approx( 3.60f ).margin( 0.01f ) ); | ||
CHECK( knife.effective_dps( dummy, mummy ) == Approx( 16.66f ).margin( 0.01f ) ); | ||
} | ||
|
||
SECTION( "STR 8, DEX 8" ) { | ||
dummy.str_max = 8; | ||
dummy.dex_max = 8; | ||
|
||
CHECK( rock.effective_dps( dummy, mummy ) == Approx( 8.84f ).margin( 0.01f ) ); | ||
CHECK( plank.effective_dps( dummy, mummy ) == Approx( 4.79f ).margin( 0.01f ) ); | ||
CHECK( knife.effective_dps( dummy, mummy ) == Approx( 20.16f ).margin( 0.01f ) ); | ||
} | ||
|
||
SECTION( "STR 10, DEX 10" ) { | ||
dummy.str_max = 10; | ||
dummy.dex_max = 10; | ||
|
||
CHECK( rock.effective_dps( dummy, mummy ) == Approx( 10.42f ).margin( 0.01f ) ); | ||
CHECK( plank.effective_dps( dummy, mummy ) == Approx( 5.55f ).margin( 0.01f ) ); | ||
CHECK( knife.effective_dps( dummy, mummy ) == Approx( 21.61f ).margin( 0.01f ) ); | ||
} | ||
} | ||
} | ||
|
||
TEST_CASE( "effective vs actual damage per second", "[actual][dps][!mayfail]" ) | ||
{ | ||
avatar &dummy = g->u; | ||
clear_character( dummy ); | ||
|
||
monster mummy( mtype_id( "debug_mon" ) ); | ||
monster soldier( mtype_id( "mon_zombie_soldier" ) ); | ||
monster smoker( mtype_id( "mon_zombie_smoker" ) ); | ||
monster survivor( mtype_id( "mon_zombie_survivor" ) ); | ||
|
||
item rock( "test_rock" ); | ||
item plank( "test_2x4" ); | ||
item knife( "knife_trench" ); | ||
item halligan( "test_halligan" ); | ||
|
||
SECTION( "debug monster" ) { | ||
check_actual_dps( dummy, mummy, rock ); | ||
check_actual_dps( dummy, mummy, plank ); | ||
check_actual_dps( dummy, mummy, knife ); | ||
check_actual_dps( dummy, mummy, halligan ); | ||
} | ||
|
||
SECTION( "soldier zombie" ) { | ||
check_actual_dps( dummy, soldier, rock ); | ||
check_actual_dps( dummy, soldier, plank ); | ||
check_actual_dps( dummy, soldier, knife ); | ||
check_actual_dps( dummy, soldier, halligan ); | ||
} | ||
|
||
SECTION( "smoker zombie" ) { | ||
check_actual_dps( dummy, smoker, rock ); | ||
check_actual_dps( dummy, smoker, plank ); | ||
check_actual_dps( dummy, smoker, knife ); | ||
check_actual_dps( dummy, smoker, halligan ); | ||
} | ||
|
||
SECTION( "survivor zombie" ) { | ||
check_actual_dps( dummy, survivor, rock ); | ||
check_actual_dps( dummy, survivor, plank ); | ||
check_actual_dps( dummy, survivor, knife ); | ||
check_actual_dps( dummy, survivor, halligan ); | ||
} | ||
} | ||
|