Skip to content

Commit fe15810

Browse files
authored
[CR] Fix nested pockets pickup (#54941)
* Fix nested pockets pickup There are quite some discrepancies between can_contain and best_pocket functionality. This commit looks to fix that and actually allow nested pockets to work properly as long as there is space. fixes #54725
1 parent 1d48bbb commit fe15810

File tree

9 files changed

+237
-51
lines changed

9 files changed

+237
-51
lines changed

src/character.cpp

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2945,16 +2945,7 @@ bool Character::can_pickVolume_partial( const item &it, bool, const item *avoid
29452945
copy.charges = 1;
29462946
}
29472947

2948-
const item weapon = get_wielded_item();
2949-
if( ( avoid == nullptr || &weapon != avoid ) && weapon.can_contain( copy ).success() ) {
2950-
return true;
2951-
}
2952-
for( const item &w : worn ) {
2953-
if( ( avoid == nullptr || &w != avoid ) && w.can_contain( copy ).success() ) {
2954-
return true;
2955-
}
2956-
}
2957-
return false;
2948+
return can_pickVolume( copy, avoid );
29582949
}
29592950

29602951
bool Character::can_pickWeight( const item &it, bool safe ) const

src/character.h

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1234,11 +1234,6 @@ class Character : public Creature, public visitable
12341234
int get_mod( const trait_id &mut, const std::string &arg ) const;
12351235
/** Applies skill-based boosts to stats **/
12361236
void apply_skill_boost();
1237-
/**
1238-
* What is the best pocket to put @it into?
1239-
* the pockets in @avoid do not count
1240-
*/
1241-
std::pair<item_location, item_pocket *> best_pocket( const item &it, const item *avoid );
12421237
protected:
12431238

12441239
void on_move( const tripoint_abs_ms &old_pos ) override;
@@ -1950,6 +1945,16 @@ class Character : public Creature, public visitable
19501945
bool can_pickVolume_partial( const item &it, bool safe = false, const item *avoid = nullptr ) const;
19511946
bool can_pickWeight( const item &it, bool safe = true ) const;
19521947
bool can_pickWeight_partial( const item &it, bool safe = true ) const;
1948+
1949+
/**
1950+
* What is the best pocket to put @it into?
1951+
* @param it the item to try and find a pocket for.
1952+
* @param avoid pockets in this item are not taken into account.
1953+
*
1954+
* @returns nullptr in the value of the returned pair if no valid pocket was found.
1955+
*/
1956+
std::pair<item_location, item_pocket *> best_pocket( const item &it, const item *avoid = nullptr );
1957+
19531958
/**
19541959
* Checks if character stats and skills meet minimum requirements for the item.
19551960
* Prints an appropriate message if requirements not met.

src/item.cpp

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9324,24 +9324,27 @@ ret_val<bool> item::is_compatible( const item &it ) const
93249324
return contents.is_compatible( it );
93259325
}
93269326

9327-
ret_val<bool> item::can_contain( const item &it ) const
9327+
ret_val<bool> item::can_contain( const item &it, const bool nested ) const
93289328
{
93299329
if( this == &it ) {
93309330
// does the set of all sets contain itself?
93319331
return ret_val<bool>::make_failure();
93329332
}
9333+
if( nested && !this->is_container() ) {
9334+
return ret_val<bool>::make_failure();
9335+
}
93339336
// disallow putting portable holes into bags of holding
93349337
if( contents.bigger_on_the_inside( volume() ) &&
93359338
it.contents.bigger_on_the_inside( it.volume() ) ) {
93369339
return ret_val<bool>::make_failure();
93379340
}
93389341
for( const item *internal_it : contents.all_items_top( item_pocket::pocket_type::CONTAINER ) ) {
9339-
if( internal_it->contents.can_contain_rigid( it ).success() ) {
9342+
if( internal_it->can_contain( it, true ).success() ) {
93409343
return ret_val<bool>::make_success();
93419344
}
93429345
}
93439346

9344-
return contents.can_contain( it );
9347+
return nested ? contents.can_contain_rigid( it ) : contents.can_contain( it );
93459348
}
93469349

93479350
bool item::can_contain( const itype &tp ) const
@@ -9359,10 +9362,10 @@ bool item::can_contain_partial( const item &it ) const
93599362
}
93609363

93619364
std::pair<item_location, item_pocket *> item::best_pocket( const item &it, item_location &parent,
9362-
const item *avoid, const bool allow_sealed, const bool ignore_settings )
9365+
const item *avoid, const bool allow_sealed, const bool ignore_settings, const bool nested )
93639366
{
93649367
item_location nested_location( parent, this );
9365-
return contents.best_pocket( it, nested_location, avoid, allow_sealed, ignore_settings );
9368+
return contents.best_pocket( it, nested_location, avoid, allow_sealed, ignore_settings, nested );
93669369
}
93679370

93689371
bool item::spill_contents( Character &c )

src/item.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1489,14 +1489,16 @@ class item : public visitable
14891489
/**
14901490
* Can the pocket contain the specified item?
14911491
* @param it the item being put in
1492+
* @param nested whether or not the current call is nested (used recursively).
14921493
*/
14931494
/*@{*/
1494-
ret_val<bool> can_contain( const item &it ) const;
1495+
ret_val<bool> can_contain( const item &it, const bool nested = false ) const;
14951496
bool can_contain( const itype &tp ) const;
14961497
bool can_contain_partial( const item &it ) const;
14971498
/*@}*/
14981499
std::pair<item_location, item_pocket *> best_pocket( const item &it, item_location &parent,
1499-
const item *avoid = nullptr, bool allow_sealed = false, bool ignore_settings = false );
1500+
const item *avoid = nullptr, bool allow_sealed = false, bool ignore_settings = false,
1501+
bool nested = false );
15001502

15011503
units::length max_containable_length( bool unrestricted_pockets_only = false ) const;
15021504
units::length min_containable_length() const;

src/item_contents.cpp

Lines changed: 18 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -550,10 +550,12 @@ void item_contents::force_insert_item( const item &it, item_pocket::pocket_type
550550
}
551551

552552
std::pair<item_location, item_pocket *> item_contents::best_pocket( const item &it,
553-
item_location &parent, const item *avoid, const bool allow_sealed, const bool ignore_settings )
553+
item_location &parent, const item *avoid, const bool allow_sealed, const bool ignore_settings,
554+
const bool nested )
554555
{
555-
std::pair<item_location, item_pocket *> ret;
556-
ret.second = nullptr;
556+
// @TODO: this could be made better by doing a plain preliminary volume check.
557+
// if the total volume of the parent is not sufficient, a child won't have enough either.
558+
std::pair<item_location, item_pocket *> ret = { parent, nullptr };
557559
for( item_pocket &pocket : contents ) {
558560
if( !pocket.is_type( item_pocket::pocket_type::CONTAINER ) ) {
559561
// best pocket is for picking stuff up.
@@ -565,29 +567,24 @@ std::pair<item_location, item_pocket *> item_contents::best_pocket( const item &
565567
// that needs to be something a player explicitly does
566568
continue;
567569
}
568-
if( !pocket.can_contain( it ).success() ) {
569-
continue;
570-
}
571570
if( !ignore_settings && !pocket.settings.accepts_item( it ) ) {
572571
// Item forbidden by whitelist / blacklist
573572
continue;
574573
}
575-
if( ret.second == nullptr || ret.second->better_pocket( pocket, it ) ) {
576-
// this pocket is the new candidate for "best"
577-
ret.first = parent;
574+
item_pocket *const nested_content_pocket =
575+
pocket.best_pocket_in_contents( parent, it, avoid, allow_sealed, ignore_settings );
576+
if( nested_content_pocket != nullptr ) {
577+
// item fits in pockets contents, no need to check the pocket itself.
578+
// this gives nested pockets priority over parent pockets.
579+
ret.second = nested_content_pocket;
580+
continue;
581+
}
582+
if( !pocket.can_contain( it ).success() || ( nested && !pocket.rigid() ) ) {
583+
// non-rigid nested pocket makes no sense, item should also be able to fit in parent.
584+
continue;
585+
}
586+
if( ret.second == nullptr || ret.second->better_pocket( pocket, it, /*nested=*/nested ) ) {
578587
ret.second = &pocket;
579-
// check all pockets within to see if they are better
580-
for( item *contained : pocket.all_items_top() ) {
581-
if( contained == avoid ) {
582-
continue;
583-
}
584-
std::pair<item_location, item_pocket *> internal_pocket =
585-
contained->best_pocket( it, parent, avoid, /*allow_sealed=*/false, /*ignore_settings=*/false );
586-
if( internal_pocket.second != nullptr &&
587-
ret.second->better_pocket( *internal_pocket.second, it, true ) ) {
588-
ret = internal_pocket;
589-
}
590-
}
591588
}
592589
}
593590
return ret;

src/item_contents.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,10 +36,12 @@ class item_contents
3636

3737
/**
3838
* returns an item_location and pointer to the best pocket that can contain the item @it
39+
* checks all items contained in every pocket
3940
* only checks CONTAINER pocket type
4041
*/
4142
std::pair<item_location, item_pocket *> best_pocket( const item &it, item_location &parent,
42-
const item *avoid = nullptr, bool allow_sealed = false, bool ignore_settings = false );
43+
const item *avoid = nullptr, bool allow_sealed = false, bool ignore_settings = false,
44+
bool nested = false );
4345

4446
units::length max_containable_length( bool unrestricted_pockets_only = false ) const;
4547
units::length min_containable_length() const;

src/item_pocket.cpp

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1274,7 +1274,7 @@ ret_val<item_pocket::contain_code> item_pocket::is_compatible( const item &it )
12741274

12751275
if( it.length() < data->min_item_length ) {
12761276
return ret_val<item_pocket::contain_code>::make_failure(
1277-
contain_code::ERR_TOO_BIG, _( "item is too short" ) );
1277+
contain_code::ERR_TOO_SMALL, _( "item is too short" ) );
12781278
}
12791279

12801280
if( it.volume() < data->min_item_volume ) {
@@ -1349,7 +1349,7 @@ ret_val<item_pocket::contain_code> item_pocket::can_contain( const item &it ) co
13491349
}
13501350
} else if( size() == 1 && contents.front().made_of( phase_id::GAS ) ) {
13511351
return ret_val<item_pocket::contain_code>::make_failure(
1352-
contain_code::ERR_LIQUID, _( "can't put non gas into pocket with gas" ) );
1352+
contain_code::ERR_GAS, _( "can't put non gas into pocket with gas" ) );
13531353
}
13541354

13551355

@@ -1801,6 +1801,26 @@ ret_val<item_pocket::contain_code> item_pocket::insert_item( const item &it )
18011801
return ret;
18021802
}
18031803

1804+
item_pocket *item_pocket::best_pocket_in_contents(
1805+
item_location &parent, const item &it, const item *avoid,
1806+
const bool allow_sealed, const bool ignore_settings )
1807+
{
1808+
item_pocket *ret = nullptr;
1809+
1810+
for( item &contained_item : contents ) {
1811+
if( &contained_item == &it || &contained_item == avoid ) {
1812+
continue;
1813+
}
1814+
item_pocket *nested_pocket = contained_item.best_pocket( it, parent, avoid,
1815+
allow_sealed, ignore_settings, /*nested=*/true ).second;
1816+
if( nested_pocket != nullptr &&
1817+
( ret == nullptr || ret->better_pocket( *nested_pocket, it, /*nested=*/true ) ) ) {
1818+
ret = nested_pocket;
1819+
}
1820+
}
1821+
return ret;
1822+
}
1823+
18041824
int item_pocket::obtain_cost( const item &it ) const
18051825
{
18061826
if( has_item( it ) ) {

src/item_pocket.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,20 @@ class item_pocket
317317
void add( const item &it, item **ret = nullptr );
318318
bool can_unload_liquid() const;
319319

320+
/**
321+
* @brief Check contents of pocket to see if it contains a valid item/pocket to store the given item.
322+
* @param ret Used to cache and return a pocket if a valid one was found.
323+
* @param pocket The @a item_pocket pocket to recursively look through.
324+
* @param parent The parent item location, most likely provided by initial call to @a best_pocket.
325+
* @param it The item to try and store.
326+
* @param avoid Item to avoid trying to search for/inside of.
327+
*
328+
* @returns A non-nullptr if a suitable pocket is found.
329+
*/
330+
item_pocket *best_pocket_in_contents(
331+
item_location &parent, const item &it, const item *avoid,
332+
const bool allow_sealed, const bool ignore_settings );
333+
320334
// only available to help with migration from previous usage of std::list<item>
321335
std::list<item> &edit_contents();
322336

0 commit comments

Comments
 (0)