forked from CleverRaven/Cataclysm-DDA
-
Notifications
You must be signed in to change notification settings - Fork 0
/
item_pickup_test.cpp
190 lines (166 loc) · 8.32 KB
/
item_pickup_test.cpp
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
#include "avatar.h"
#include "cata_catch.h"
#include "item.h"
#include "map.h"
#include "map_helpers.h"
#include "player_helpers.h"
#include "rng.h"
static const itype_id itype_backpack_hiking( "backpack_hiking" );
static const itype_id itype_m4_carbine( "m4_carbine" );
static const itype_id itype_rope_6( "rope_6" );
// This test case exists by way of documenting and exhibiting some potentially unexpected behavior
// of the following functions for transferring items into inventory:
//
// - Character::wear_item
// - Character::pick_up
// - Character::i_add
// - item::put_in
//
// namely, that these functions create *copies* of the items, and the original item
// references will not refer to the items placed in inventory.
TEST_CASE( "putting items into inventory with put_in or i_add", "[pickup][inventory]" )
{
avatar &they = get_avatar();
map &here = get_map();
clear_avatar();
clear_map();
// Spawn items on the map at this location
const tripoint ground = they.pos();
item &rope_map = here.add_item( ground, item( itype_rope_6 ) );
item &backpack_map = here.add_item( ground, item( itype_backpack_hiking ) );
// Set unique IDs on the items, to verify their copies later
std::string backpack_uid = random_string( 10 );
std::string rope_uid = random_string( 10 );
backpack_map.set_var( "uid", backpack_uid );
rope_map.set_var( "uid", rope_uid );
// Ensure avatar does not currently possess these items, or items with their uid
REQUIRE_FALSE( they.has_item( backpack_map ) );
REQUIRE_FALSE( they.has_item( rope_map ) );
REQUIRE_FALSE( character_has_item_with_var_val( they, "uid", backpack_uid ) );
REQUIRE_FALSE( character_has_item_with_var_val( they, "uid", rope_uid ) );
WHEN( "avatar wears a hiking backpack from the ground with wear_item" ) {
they.worn.clear();
// Get the backpack from the iterator returned by wear_item,
// for the reference to the backpack that the avatar is wearing now
cata::optional<std::list<item>::iterator> worn = they.wear_item( backpack_map );
item &backpack = **worn;
THEN( "they have a copy of the backpack" ) {
// They have the same type
CHECK( backpack.typeId() == backpack_map.typeId() );
// They have the same uid
CHECK( backpack.get_var( "uid" ) == backpack_uid );
CHECK( character_has_item_with_var_val( they, "uid", backpack_uid ) );
// New one is in avatar's possession
CHECK( they.has_item( backpack ) );
// Original backpack from the ground is not
CHECK_FALSE( they.has_item( backpack_map ) );
}
WHEN( "using put_in to put a rope directly into the backpack" ) {
REQUIRE( backpack.put_in( rope_map, item_pocket::pocket_type::CONTAINER ).success() );
THEN( "the original rope is not in inventory or the backpack" ) {
CHECK_FALSE( they.has_item( rope_map ) );
CHECK_FALSE( backpack.has_item( rope_map ) );
}
THEN( "they have a copy of the rope in inventory" ) {
CHECK( character_has_item_with_var_val( they, "uid", rope_uid ) );
}
// FIXME: After put_in, there is no way to get the new copied item reference(?)
}
WHEN( "using i_add to put the rope into inventory" ) {
// Add the rope to the inventory (goes in backpack, as it's the only thing worn)
item_location rope_new = they.i_add( rope_map );
THEN( "a copy of the rope item is in inventory and in the backpack" ) {
CHECK( they.has_item( *rope_new ) );
CHECK( backpack.has_item( *rope_new ) );
CHECK( character_has_item_with_var_val( they, "uid", rope_uid ) );
}
THEN( "the original rope is not in inventory or the backpack" ) {
CHECK_FALSE( they.has_item( rope_map ) );
CHECK_FALSE( backpack.has_item( rope_map ) );
}
}
}
// The Character::pick_up function assigns an item pick-up activity to the character,
// which can be executed with the process_activity() helper.
// But Character::pick_up cannot wield or wear items in the act of picking them up;
// the available storage needs to be worn ahead of time.
GIVEN( "avatar is not wearing anything that can store items" ) {
they.worn.clear();
WHEN( "avatar tries to get the backpack with pick_up" ) {
item_location backpack_loc( map_cursor( ground ), &backpack_map );
const drop_locations &pack_droplocs = { std::make_pair( backpack_loc, 1 ) };
they.pick_up( pack_droplocs );
process_activity( they );
THEN( "they fail to acquire the backpack" ) {
CHECK_FALSE( character_has_item_with_var_val( they, "uid", backpack_uid ) );
}
}
WHEN( "avatar tries to get the rope with pick_up" ) {
item_location rope_loc( map_cursor( ground ), &rope_map );
const drop_locations &rope_droplocs = { std::make_pair( rope_loc, 1 ) };
they.pick_up( rope_droplocs );
process_activity( they );
THEN( "they fail to acquire the rope" ) {
CHECK_FALSE( character_has_item_with_var_val( they, "uid", rope_uid ) );
}
}
}
}
// The below incredibly-specific test case is designed as a regression test for #52422 in which
// picking up items from the ground could result in inventory items being dropped.
//
// One such case is when an "inner container" (container within a container) would be selected as
// the "best pocket" for a picked up item, but inserting the item makes it too big or heavy for its
// outer container, forcing it to be dropped on the ground (along with whatever was inserted).
//
// The reproduction use case here is: Wearing only a backpack containing a rope, when picking up
// an M4 from the ground, the M4 should go into the backpack, not into the rope, and neither the
// rope nor the M4 should be dropped.
TEST_CASE( "pickup m4 with a rope in a hiking backpack", "[pickup][container]" )
{
avatar &they = get_avatar();
map &here = get_map();
clear_avatar();
clear_map();
// Spawn items on the map at this location
const tripoint ground = they.pos();
item &m4a1 = here.add_item( ground, item( itype_m4_carbine ) );
item &rope_map = here.add_item( ground, item( itype_rope_6 ) );
item &backpack_map = here.add_item( ground, item( itype_backpack_hiking ) );
// Ensure that rope and backpack are containers, both capable of holding the M4
REQUIRE( rope_map.is_container() );
REQUIRE( backpack_map.is_container() );
REQUIRE( rope_map.can_contain( m4a1 ).success() );
REQUIRE( backpack_map.can_contain( m4a1 ).success() );
REQUIRE( backpack_map.can_contain( rope_map ).success() );
// Give the M4 a serial number (and the rope too, it's also a deadly weapon)
std::string m4_uid = random_string( 10 );
std::string rope_uid = random_string( 10 );
m4a1.set_var( "uid", m4_uid );
rope_map.set_var( "uid", rope_uid );
GIVEN( "avatar is wearing a backpack with a short rope in it" ) {
// What happens to the stuff on the ground?
CAPTURE( here.i_at( ground ).size() );
// Wear backpack from map and get the new item reference
cata::optional<std::list<item>::iterator> worn = they.wear_item( backpack_map );
item &backpack = **worn;
REQUIRE( they.has_item( backpack ) );
// Put the rope in
item_location rope = they.i_add( rope_map );
REQUIRE( they.has_item( *rope ) );
WHEN( "they pick up the M4" ) {
// Get item_location for m4 on the map
item_location m4_loc( map_cursor( they.pos() ), &m4a1 );
const drop_locations &thing = { std::make_pair( m4_loc, 1 ) };
CHECK_FALSE( backpack.has_item( m4a1 ) );
// Now pick up the M4
they.pick_up( thing );
process_activity( they );
// Neither the rope nor the M4 should have been dropped
THEN( "they should have the rope and M4 still in possession" ) {
CHECK( character_has_item_with_var_val( they, "uid", rope_uid ) );
CHECK( character_has_item_with_var_val( they, "uid", m4_uid ) );
}
}
}
}