File: tool_quality_test.cpp

package info (click to toggle)
cataclysm-dda 0.H-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 710,808 kB
  • sloc: cpp: 524,019; python: 11,580; sh: 1,228; makefile: 1,169; xml: 507; javascript: 150; sql: 56; exp: 41; perl: 37
file content (208 lines) | stat: -rw-r--r-- 9,322 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
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
#include "avatar.h"
#include "cata_catch.h"
#include "itype.h"
#include "player_helpers.h"
#include "type_id.h"

static const quality_id qual_BOIL( "BOIL" );
static const quality_id qual_DRILL( "DRILL" );
static const quality_id qual_LOCKPICK( "LOCKPICK" );
static const quality_id qual_PRY( "PRY" );
static const quality_id qual_SCREW( "SCREW" );
static const quality_id qual_SCREW_FINE( "SCREW_FINE" );
static const quality_id qual_WRENCH( "WRENCH" );

// item::get_quality
//
// Returns the best quality level provided by:
//
// - inherent qualities from the "qualities" field
// - charged qualities from the "charged_qualities" field (only if item has sufficient charge)
// - inherent or charged qualities of any items contained within this item
//
// The BOIL quality requires the item (container) to be empty, unless strict_boiling = false.
//
TEST_CASE( "get_quality", "[tool][quality]" )
{
    SECTION( "get_quality returns numeric level of given quality" ) {
        item sonic( "test_sonic_screwdriver" );
        CHECK( sonic.get_quality( qual_LOCKPICK ) == 30 );
        CHECK( sonic.get_quality( qual_PRY ) == 2 );
        CHECK( sonic.get_quality( qual_SCREW ) == 2 );
        CHECK( sonic.get_quality( qual_SCREW_FINE ) == 1 );
        CHECK( sonic.get_quality( qual_WRENCH ) == 1 );
    }

    SECTION( "charged_qualities" ) {
        // Without charges, the cordless drill cannot drill, but with charges, it can drill
        SECTION( "with 0 charge, get_quality returns 0" ) {
            CHECK( tool_with_ammo( "test_cordless_drill", 0 ).get_quality( qual_DRILL ) == 0 );
        }
        SECTION( "with sufficient charge, get_quality returns quality level" ) {
            CHECK( tool_with_ammo( "test_cordless_drill", 20 ).get_quality( qual_DRILL ) == 3 );
        }
    }

    SECTION( "BOIL quality" ) {
        // Tools that can BOIL and have a CONTAINER pocket have BOIL quality only when empty
        item tin_can( "test_can_food" );
        SECTION( "get_quality returns BOIL quality if container is empty" ) {
            REQUIRE( tin_can.empty_container() );

            CHECK( tin_can.get_quality( qual_BOIL ) == 1 );
        }
        SECTION( "get_quality returns INT_MIN for BOIL quality if container is not empty" ) {
            item broth( "test_liquid" );
            tin_can.put_in( broth, pocket_type::CONTAINER );
            REQUIRE_FALSE( tin_can.empty_container() );

            CHECK( tin_can.get_quality( qual_BOIL ) == INT_MIN );
            // Passing strict_boiling = false to get_quality ignores the emptiness rule
            CHECK( tin_can.get_quality( qual_BOIL, false ) == 1 );
        }
    }
}

// Tools that run on battery power (or are otherwise "charged") may have "charged_qualities"
// that are only available when the item is charged with at least "charges_per_use" charges.
TEST_CASE( "battery-powered_tool_qualities", "[tool][battery][quality]" )
{
    item drill( "test_cordless_drill" );
    item battery( "medium_battery_cell" );

    // This cordless drill is a battery-powered tool needing some charges for each use
    REQUIRE( drill.is_tool() );
    REQUIRE( drill.type->charges_to_use() > 0 );

    // It has one "inherent" quality, SCREW 1, that always works regardless of charges
    REQUIRE( drill.type->qualities.size() == 1 );
    // and it has one "charged" quality, DRILL 3, that only works with enough charges
    REQUIRE( drill.type->charged_qualities.size() == 1 );
    // the idea being that the screwdriver bit can be used to awkwardly turn screws, even without
    // powered assistance, while drilling holes with an unpowered drill bit is nigh impossible.

    // TODO: Eventually, battery power should also give a significant advantage to the speed or
    // effort of screw-turning, compared with manual wrist-twisting.

    WHEN( "tool has no battery" ) {
        REQUIRE_FALSE( drill.magazine_current() );

        // Screwing should work, but drilling should not
        THEN( "inherent qualities can be used" ) {
            CHECK( drill.has_quality( qual_SCREW, 1, 1 ) );
        }
        THEN( "charged tool qualities cannot be used" ) {
            CHECK_FALSE( drill.has_quality( qual_DRILL, 3, 1 ) );
        }
    }

    WHEN( "tool has a battery with zero charge" ) {
        // Get a dead battery
        battery.ammo_set( battery.ammo_default(), 0 );
        REQUIRE( battery.ammo_remaining() == 0 );
        // Install the battery in the drill
        drill.put_in( battery, pocket_type::MAGAZINE_WELL );
        REQUIRE( drill.magazine_current() );
        REQUIRE( drill.ammo_remaining() == 0 );

        // Screwing should work, but drilling should not
        THEN( "inherent qualities can be used" ) {
            CHECK( drill.has_quality( qual_SCREW, 1, 1 ) );
        }
        THEN( "charged tool qualities cannot be used" ) {
            CHECK_FALSE( drill.has_quality( qual_DRILL, 3, 1 ) );
        }
    }

    WHEN( "tool has a battery with enough charge for one use, equal to charges_per_use" ) {
        // Get a battery with exactly enough charge for one use
        int bat_charges = drill.type->charges_to_use();
        battery.ammo_set( battery.ammo_default(), bat_charges );
        REQUIRE( battery.ammo_remaining() == bat_charges );
        // Install the battery in the drill
        drill.put_in( battery, pocket_type::MAGAZINE_WELL );
        REQUIRE( drill.magazine_current() );
        REQUIRE( drill.ammo_remaining() == bat_charges );

        // Ensure there is enough charge
        REQUIRE( drill.type->charges_to_use() <= bat_charges );

        // Both screwing and drilling should work
        THEN( "both inherent and charged tool qualities can be used" ) {
            CHECK( drill.has_quality( qual_SCREW, 1, 1 ) );
            CHECK( drill.has_quality( qual_DRILL, 3, 1 ) );
        }
    }

    WHEN( "tool has a battery with insufficient charge, less than charges_per_use" ) {
        // Get a battery with too few charges by 1
        int bat_charges = drill.type->charges_to_use() - 1;
        battery.ammo_set( battery.ammo_default(), bat_charges );
        REQUIRE( battery.ammo_remaining() == bat_charges );
        // Install the battery in the drill
        drill.put_in( battery, pocket_type::MAGAZINE_WELL );
        REQUIRE( drill.magazine_current() );
        REQUIRE( drill.ammo_remaining() == bat_charges );

        // Ensure there is not enough charge
        REQUIRE( drill.type->charges_to_use() > bat_charges );

        // Screwing should still work, but drilling should not
        THEN( "inherent qualities can be used" ) {
            CHECK( drill.has_quality( qual_SCREW, 1, 1 ) );
        }
        THEN( "charged tool qualities cannot be used" ) {
            CHECK_FALSE( drill.has_quality( qual_DRILL, 3, 1 ) );
        }
    }

    SECTION( "charged tool qualities with UPS conversion mod" ) {
        // Need avatar as "carrier" for the UPS
        Character &they = get_player_character();
        clear_character( they );
        they.worn.wear_item( they, item( "debug_backpack" ), false, false );

        // Use i_add to place everything in the avatar's inventory (backpack)
        // so the UPS power will be available to the cordless drill after modding
        item_location drill = they.i_add( item( "test_cordless_drill" ) );
        item_location bat_cell = they.i_add( item( "heavy_battery_cell" ) );
        item_location ups_mod = they.i_add( item( "battery_ups" ) );
        item_location ups = they.i_add( item( "UPS_ON" ) );

        GIVEN( "UPS has battery with enough charge, equal to drill's charges_per_use" ) {
            // Charge the battery
            int bat_charges = drill->type->charges_to_use();
            REQUIRE( bat_charges > 0 );
            bat_cell->ammo_set( bat_cell->ammo_default(), bat_charges );
            REQUIRE( bat_cell->ammo_remaining() == bat_charges );
            // Install heavy battery into UPS
            REQUIRE( ups->put_in( *bat_cell, pocket_type::MAGAZINE_WELL ).success() );
            REQUIRE( ups->ammo_remaining( &they ) == bat_charges );

            WHEN( "UPS battery mod is installed into the drill" ) {
                // Ensure drill currently has no mods
                REQUIRE( drill->toolmods().empty() );
                REQUIRE( drill->tname() == "test cordless drill" );
                // Install the UPS mod and ensure it worked
                drill->put_in( *ups_mod, pocket_type::MOD );
                REQUIRE_FALSE( drill->toolmods().empty() );
                REQUIRE( drill->tname() == "test cordless drill+1 (UPS)" );
                // Ensure avatar actually has the drill and UPS in possession
                CHECK( they.has_item( *drill ) );
                CHECK( they.has_item( *ups ) );

                THEN( "the drill has the same charge as the UPS" ) {
                    CHECK( drill->ammo_remaining( &they ) == bat_charges );
                }
                THEN( "inherent qualities of the drill can be used" ) {
                    CHECK( drill->has_quality( qual_SCREW, 1, 1 ) );
                }
                // Regression test for #54471
                THEN( "charged qualities of the drill can be used" ) {
                    CHECK( drill->has_quality( qual_DRILL, 3, 1 ) );
                }
            }
        }
    }
}