diff --git a/src/character.h b/src/character.h index b7ca12b00358f..f21a1a2189efd 100644 --- a/src/character.h +++ b/src/character.h @@ -390,6 +390,12 @@ struct needs_rates { float kcal = 0.0f; }; +struct pocket_data_with_parent { + const item_pocket *pocket_ptr; + item_location parent; + int nested_level; +}; + class Character : public Creature, public visitable { public: @@ -2037,6 +2043,18 @@ class Character : public Creature, public visitable std::pair best_pocket( const item &it, const item *avoid = nullptr, bool ignore_settings = false ); + /** + * Collect all pocket data (with parent and nest levels added) that the character has. + * @param filter only collect pockets that match the filter. + * @param sort_func customizable sorting function. + * + * @returns pocket_data_with_parent vector to return. + */ + std::vector get_all_pocket_with_parent( + const std::function &filter = return_true, + const std::function + *sort_func = nullptr ); + /** * Checks if character stats and skills meet minimum requirements for the item. * Prints an appropriate message if requirements not met. diff --git a/src/character_attire.cpp b/src/character_attire.cpp index 47a98225f7208..6d6ac645e4e04 100644 --- a/src/character_attire.cpp +++ b/src/character_attire.cpp @@ -2230,6 +2230,71 @@ void outfit::pickup_stash( const item &newit, int &remaining_charges, bool ignor } } +static std::vector get_child_pocket_with_parent( + const item_pocket *pocket, const item_location &parent, item_location it, const int nested_level, + const std::function &filter = return_true ) +{ + std::vector ret; + if( pocket != nullptr ) { + pocket_data_with_parent pocket_data = { pocket, item_location::nowhere, nested_level }; + const item_location new_parent = item_location( it ); + + if( parent != item_location::nowhere ) { + pocket_data.parent = item_location( parent, it.get_item() ); + } + if( filter( pocket_data.pocket_ptr ) ) { + ret.emplace_back( pocket_data ); + } + + for( const item *contained : pocket->all_items_top() ) { + const item_location poc_loc = item_location( it, const_cast( contained ) ); + for( const item_pocket *pocket_nest : contained->get_all_contained_pockets() ) { + std::vector child = + get_child_pocket_with_parent( pocket_nest, new_parent, + poc_loc, nested_level + 1, filter ); + ret.insert( ret.end(), child.begin(), child.end() ); + } + } + } + return ret; +} + +std::vector Character::get_all_pocket_with_parent( + const std::function &filter, + const std::function + *sort_func ) +{ + std::vector ret; + const auto sort_pockets_func = [ sort_func ] + ( const pocket_data_with_parent & l, const pocket_data_with_parent & r ) { + const auto sort_pockets = *sort_func; + return sort_pockets( l, r ); + }; + + std::list locs; + item_location carried_item = get_wielded_item(); + if( carried_item != item_location::nowhere ) { + locs.emplace_back( carried_item ); + } + for( item_location &worn_loc : top_items_loc( ) ) { + if( worn_loc != item_location::nowhere ) { + locs.emplace_back( worn_loc ); + } + } + + for( item_location &loc : locs ) { + for( const item_pocket *pocket : loc->get_all_contained_pockets() ) { + std::vector child = + get_child_pocket_with_parent( pocket, item_location::nowhere, loc, 0, filter ); + ret.insert( ret.end(), child.begin(), child.end() ); + } + } + if( sort_func ) { + std::sort( ret.begin(), ret.end(), sort_pockets_func ); + } + return ret; +} + void outfit::add_stash( Character &guy, const item &newit, int &remaining_charges, bool ignore_pkt_settings ) { @@ -2245,52 +2310,50 @@ void outfit::add_stash( Character &guy, const item &newit, int &remaining_charge // Crawl Next : worn items pickup_stash( newit, remaining_charges, ignore_pkt_settings ); } else { - item_pocket *found_pocket; - std::vector pockets; + //item copy for test can contain item temp_it = item( newit ); temp_it.charges = 1; // Collect all pockets - std::list items; - item_location carried_item = guy.get_wielded_item(); - if( carried_item != item_location::nowhere ) { - items.emplace_back( &*carried_item ); - } - for( item &i : worn ) { - items.emplace_back( &i ); - } - for( item *i : items ) { - for( const item_pocket *pocket : i->get_all_contained_pockets() ) { - if( pocket->can_contain( temp_it ).success() ) { - // Top-level( wielded, worn ) pockets may be stored - found_pocket = const_cast( pocket ); - pockets.emplace_back( found_pocket ); - } - for( const item *contained : pocket->all_items_ptr( item_pocket::pocket_type::CONTAINER ) ) { - for( const item_pocket *pocket_nest : contained->get_all_contained_pockets() ) { - if( pocket_nest->can_contain( temp_it ).success() && pocket_nest->rigid() ) { - // Nested pocket with rigid() can only be stored - found_pocket = const_cast( pocket_nest ); - pockets.emplace_back( found_pocket ); - } - } - } - } - } - - // Sort by item_pocket::better_pocket - std::sort( pockets.begin(), pockets.end(), [newit, temp_it]( item_pocket * lhs, - item_pocket * rhs ) { - return rhs->better_pocket( *lhs, newit, false ); - } ); - - int amount = remaining_charges; + std::vector pockets_with_parent; + auto const pocket_filter = [&temp_it]( item_pocket const * pck ) { + return pck->can_contain( temp_it ).success(); + }; + const std::function + &sort_f = [&temp_it]( const pocket_data_with_parent & a, const pocket_data_with_parent & b ) { + return b.pocket_ptr->better_pocket( *a.pocket_ptr, temp_it, false ); + }; + pockets_with_parent = guy.get_all_pocket_with_parent( pocket_filter, &sort_f ); + + const int amount = remaining_charges; int num_contained = 0; - for( item_pocket *&pocket : pockets ) { + for( const pocket_data_with_parent &pocket_data_ptr : pockets_with_parent ) { if( amount <= num_contained || remaining_charges <= 0 ) { break; } - const int filled_count = pocket->fill_with( newit, guy, remaining_charges, false, false ); + int filled_count = 0; + item_pocket *pocke = const_cast( pocket_data_ptr.pocket_ptr ); + if( pocke == nullptr ) { + continue; + } + if( pocke->rigid() ) { + // Rigid container allow to fill unconditionally till volume limit + // because do not depend on the capacity of the parent's pocket. + filled_count = pocke->fill_with( newit, guy, remaining_charges, false, false ); + } else { + int max_contain_value = pocke->remaining_capacity_for_item( newit ); + const item_location parent_data = pocket_data_ptr.parent; + + if( parent_data.has_parent() ) { + if( parent_data.parents_can_contain_recursive( &temp_it ) ) { + max_contain_value = parent_data.max_charges_by_parent_recursive( temp_it ); + } else { + max_contain_value = 0; + } + } + const int charges = std::min( max_contain_value, remaining_charges ) ; + filled_count = pocke->fill_with( newit, guy, charges, false, false ); + } num_contained += filled_count; remaining_charges -= filled_count; }