1 /*
2 * Researching what seem to be generally agreed on values
3 * for bow power required for guaranteed game kills, these are some breakpoints.
4 * While many source use KE values, there is a broad consensus that momentum is related to damage.
5 * FPS ft-lbs slug-ft-s Game
6 * 136 19.01 0.28 Turkeys
7 * 153 24 0.315 Whitetails at close range
8 * 195 39 0.401 Whitetails
9 * 249 63.73 0.512 Elk
10 * 278 79.44 0.572 Moose
11 *
12 * The concept is to bracket these threshods with various bows using standard hunting loadouts.
13 */
14 #include <iosfwd>
15 #include <memory>
16 #include <set>
17 #include <string>
18
19 #include "catch/catch.hpp"
20 #include "damage.h"
21 #include "game_constants.h"
22 #include "item.h"
23 #include "itype.h"
24 #include "map.h"
25 #include "mapdata.h"
26 #include "monster.h"
27 #include "point.h"
28 #include "projectile.h"
29 #include "type_id.h"
30 #include "value_ptr.h"
31
32 // In short, a bow should never destroy a wall, pretty simple.
test_projectile_hitting_wall(const std::string & target_type,bool smashable,dealt_projectile_attack & attack,const std::string & weapon_type)33 static void test_projectile_hitting_wall( const std::string &target_type, bool smashable,
34 dealt_projectile_attack &attack, const std::string &weapon_type )
35 {
36 static const tripoint target_point{ 5, 5, 0 };
37 map &here = get_map();
38 for( int i = 0; i < 10; ++i ) {
39 projectile projectile_copy = attack.proj;
40 here.set( target_point, ter_id( target_type ), furn_id( "f_null" ) );
41 CAPTURE( projectile_copy.impact.total_damage() );
42 here.shoot( target_point, projectile_copy, false );
43 CAPTURE( target_type );
44 CAPTURE( weapon_type );
45 CAPTURE( ter_id( target_type ).obj().name() );
46 CAPTURE( here.ter( target_point ).obj().name() );
47 if( smashable ) {
48 CHECK( here.ter( target_point ) != ter_id( target_type ) );
49 } else {
50 CHECK( here.ter( target_point ) == ter_id( target_type ) );
51 }
52 }
53 }
54
test_projectile_attack(const std::string & target_type,bool killable,dealt_projectile_attack & attack,const std::string & weapon_type)55 static void test_projectile_attack( const std::string &target_type, bool killable,
56 dealt_projectile_attack &attack, const std::string &weapon_type )
57 {
58 for( int i = 0; i < 10; ++i ) {
59 monster target{ mtype_id( target_type ), tripoint_zero };
60 target.deal_projectile_attack( nullptr, attack, false );
61 CAPTURE( target_type );
62 CAPTURE( target.get_hp() );
63 CAPTURE( target.get_hp_max() );
64 CAPTURE( attack.proj.impact.total_damage() );
65 CAPTURE( weapon_type );
66 CHECK( target.is_dead() == killable );
67 }
68 }
69
test_archery_balance(const std::string & weapon_type,const std::string & ammo_type,const std::string & killable,const std::string & unkillable)70 static void test_archery_balance( const std::string &weapon_type, const std::string &ammo_type,
71 const std::string &killable, const std::string &unkillable )
72 {
73 item weapon( weapon_type );
74 // The standard modern hunting arrow, make this a parameter if we extend to crossbows.
75 weapon.ammo_set( itype_id( ammo_type ), 1 );
76
77 projectile test_projectile;
78 test_projectile.speed = 1000;
79 test_projectile.impact = weapon.gun_damage();
80 test_projectile.proj_effects = weapon.ammo_effects();
81 test_projectile.critical_multiplier = weapon.ammo_data()->ammo->critical_multiplier;
82
83 dealt_projectile_attack attack {
84 test_projectile, nullptr, dealt_damage_instance(), tripoint_zero, accuracy_critical - 0.05
85 };
86 if( !killable.empty() ) {
87 test_projectile_attack( killable, true, attack, weapon_type );
88 }
89 if( !unkillable.empty() ) {
90 test_projectile_attack( unkillable, false, attack, weapon_type );
91 }
92 test_projectile_hitting_wall( "t_wall", false, attack, weapon_type );
93 // Use "can't kill anything" as an indication that it can't break a window either.
94 if( !killable.empty() ) {
95 test_projectile_hitting_wall( "t_window", true, attack, weapon_type );
96 }
97 }
98
99 TEST_CASE( "archery_damage_thresholds", "[balance],[archery]" )
100 {
101 // Selfbow can't kill a turkey
102 test_archery_balance( "selfbow", "arrow_metal", "", "mon_turkey" );
103 test_archery_balance( "rep_crossbow", "bolt_steel", "", "mon_turkey" );
104 // Shortbow can kill turkeys, but not deer
105 test_archery_balance( "shortbow", "arrow_metal", "mon_turkey", "mon_deer" );
106 test_archery_balance( "hand_crossbow", "bolt_steel", "mon_turkey", "mon_deer" );
107 // Fiberglass recurve can kill deer, but not bear
108 test_archery_balance( "recurbow", "arrow_metal", "mon_deer", "mon_bear" );
109 test_archery_balance( "compositecrossbow", "bolt_steel", "mon_deer", "mon_bear" );
110 // Medium setting compound bow can kill Bear
111 test_archery_balance( "compbow", "arrow_metal", "mon_bear", "" );
112 // High setting modern compund bow can kill Moose
113 test_archery_balance( "compcrossbow", "bolt_steel", "mon_moose", "" );
114 test_archery_balance( "compbow_high", "arrow_metal", "mon_moose", "" );
115 }
116