File: explosion_balance.cpp

package info (click to toggle)
cataclysm-dda 0.C%2Bgit20190228.faafa3a-2
  • links: PTS, VCS
  • area: main
  • in suites: buster
  • size: 181,636 kB
  • sloc: cpp: 256,609; python: 2,621; makefile: 862; sh: 495; perl: 37; xml: 33
file content (127 lines) | stat: -rw-r--r-- 4,335 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
#include <sstream>

#include "catch/catch.hpp"
#include "enums.h"
#include "game.h"
#include "item.h"
#include "itype.h"
#include "line.h"
#include "map.h"
#include "map_helpers.h"
#include "monster.h"
#include "test_statistics.h"
#include "vehicle.h"
#include "vpart_position.h"

void check_lethality( const std::string &explosive_id, const int range, float lethality,
                      float margin )
{
    const epsilon_threshold target_lethality{ lethality, margin };
    int num_survivors = 0;
    int num_subjects = 0;
    int num_wounded = 0;
    statistics<bool> deaths;
    std::stringstream survivor_stats;
    int total_hp = 0;
    do {
        // Clear map
        clear_map_and_put_player_underground();
        // Spawn some monsters in a circle.
        tripoint origin( 30, 30, 0 );
        int num_subjects_this_time = 0;
        for( const tripoint monster_position : closest_tripoints_first( range, origin ) ) {
            if( rl_dist( monster_position, origin ) != range ) {
                continue;
            }
            num_subjects++;
            num_subjects_this_time++;
            spawn_test_monster( "mon_zombie", monster_position );
        }
        // Set off an explosion
        item grenade( explosive_id );
        grenade.charges = 0;
        grenade.type->invoke( g->u, grenade, origin );
        // see how many monsters survive
        std::vector<Creature *> survivors = g->get_creatures_if( []( const Creature & critter ) {
            return critter.is_monster();
        } );
        num_survivors += survivors.size();
        for( Creature *survivor : survivors ) {
            survivor_stats << survivor->pos() << " " << survivor->get_hp() << ", ";
            num_wounded += ( survivor->get_hp() < survivor->get_hp_max() ) ? 1 : 0;
            total_hp += survivor->get_hp();
            deaths.add( false );
        }
        if( survivors.size() > 0 ) {
            survivor_stats << std::endl;
        }
        for( int i = survivors.size(); i < num_subjects_this_time; ++i ) {
            deaths.add( true );
        }
    } while( deaths.uncertain_about( target_lethality ) );
    CAPTURE( margin );
    INFO( explosive_id );
    INFO( "range " << range );
    INFO( num_survivors << " survivors out of " << num_subjects << " targets." );
    INFO( survivor_stats.str() );
    INFO( "Wounded survivors: " << num_wounded );
    const int average_hp = num_survivors ? total_hp / num_survivors : 0;
    INFO( "average hp of survivors: " << average_hp );
    CHECK( deaths.avg() == Approx( lethality ).margin( margin ) );
}

std::vector<int> get_part_hp( vehicle *veh )
{
    std::vector<int> part_hp;
    part_hp.reserve( veh->parts.size() );
    for( vehicle_part &part : veh->parts ) {
        part_hp.push_back( part.hp() );
    }
    return part_hp;
}

void check_vehicle_damage( const std::string &explosive_id, const std::string &vehicle_id,
                           const int range )
{
    // Clear map
    clear_map_and_put_player_underground();
    tripoint origin( 30, 30, 0 );

    vehicle *target_vehicle = g->m.add_vehicle( vproto_id( vehicle_id ), origin, 0, -1, 0 );
    std::vector<int> before_hp = get_part_hp( target_vehicle );

    while( g->m.veh_at( origin ) ) {
        origin.x++;
    }
    origin.x += range;

    // Set off an explosion
    item grenade( explosive_id );
    grenade.charges = 0;
    grenade.type->invoke( g->u, grenade, origin );

    std::vector<int> after_hp = get_part_hp( target_vehicle );

    // We don't expect any destroyed parts.
    REQUIRE( before_hp.size() == after_hp.size() );
    for( unsigned int i = 0; i < before_hp.size(); ++i ) {
        INFO( target_vehicle->parts[ i ].name() );
        if( target_vehicle->parts[ i ].name() == "windshield" ||
            target_vehicle->parts[ i ].name() == "headlight" ) {
            CHECK( before_hp[ i ] >= after_hp[ i ] );
        } else if( target_vehicle->parts[ i ].name() != "clock" ) {
            CHECK( before_hp[ i ] == after_hp[ i ] );
        }
    }
}

TEST_CASE( "grenade_lethality", "[grenade],[explosion],[balance]" )
{
    check_lethality( "grenade_act", 5, 0.95, 0.06 );
    check_lethality( "grenade_act", 15, 0.40, 0.06 );
}

TEST_CASE( "grenade_vs_vehicle", "[grenade],[explosion],[balance]" )
{
    check_vehicle_damage( "grenade_act", "car", 5 );
}