Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: NPCs drop excess items if overburdened, merchants pick up and renew inventory if restocking #3386

Merged
merged 3 commits into from
Oct 7, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 53 additions & 0 deletions src/character.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3208,6 +3208,52 @@ ret_val<bool> Character::can_swap( const item &it ) const
return ret_val<bool>::make_success();
}

// pretty much the same as inventory::remove_randomly_by_volume but I didn't see a point in
// adding it to the inventory class when it's only called here in Character::drop_invalid_inventory
std::list<item> remove_randomly_by_weight( inventory &, const units::mass & );
std::list<item> remove_randomly_by_weight( inventory &inv, const units::mass &weight )
{
std::list<item> result;
struct entry {
decltype( inv.slice().begin() ) stack;
decltype( ( *stack )->begin() ) stack_it;
};
std::vector<entry> vals;

auto slice = inv.slice();
size_t ndx = 0;
for( auto stack = slice.begin(); stack != slice.end(); ++stack ) {
for( auto stack_it = ( *stack )->begin(); stack_it != ( *stack )->end(); ++stack_it ) {
vals.push_back( { stack, stack_it } );
}
++ndx;
}
// shuffle the vector
std::shuffle( vals.begin(), vals.end(), rng_get_engine() );
// iterate through until we have dropped enough items
auto dropped_weight = 0_gram;
for( auto &e : vals ) {
if( dropped_weight >= weight ) {
break;
}
dropped_weight += e.stack_it->weight();
result.push_back( std::move( *e.stack_it ) );
e.stack_it = ( *e.stack )->erase( e.stack_it );
if( e.stack_it == ( *e.stack )->begin() && !( *e.stack )->empty() ) {
e.stack_it->invlet = result.back().invlet;
}
}
// iterate through items again so that we can remove any empty groups
ndx = slice.size() - 1;
for( auto stack = slice.rbegin(); stack != slice.rend(); ++stack ) {
if( ( *stack )->empty() ) {
inv.reduce_stack( ndx, -1 );
}
--ndx;
}
return result;
}

void Character::drop_invalid_inventory()
{
bool dropped_liquid = false;
Expand All @@ -3228,6 +3274,13 @@ void Character::drop_invalid_inventory()
auto items_to_drop = inv.remove_randomly_by_volume( volume_carried() - volume_capacity() );
put_into_vehicle_or_drop( *this, item_drop_reason::tumbling, items_to_drop );
}
// Also drop excess weight
auto wt_carried = weight_carried();
auto wt_capacity = weight_capacity();
if( wt_carried > wt_capacity ) {
auto items_to_drop = remove_randomly_by_weight( inv, wt_carried - wt_capacity );
put_into_vehicle_or_drop( *this, item_drop_reason::too_heavy, items_to_drop );
}
}

bool Character::has_artifact_with( const art_effect_passive effect ) const
Expand Down
24 changes: 21 additions & 3 deletions src/npc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
#include "magic.h"
#include "map.h"
#include "map_iterator.h"
#include "map_selector.h"
#include "mapdata.h"
#include "math_defines.h"
#include "messages.h"
Expand Down Expand Up @@ -1708,9 +1709,26 @@ void npc::shop_restock()
}
}

has_new_items = true;
inv.clear();
inv.push_back( ret );
// we have items to restock with, so go ahead and pick up everything so we can clear out properly
// If we don't restock for some reason don't clear out inventory since we'd end up not having anything
// to trade
if( !ret.empty() ) {
// Pick up nearby items as a free action since we'll be immediately deleting these items
const auto pickup_item_filter = [this]( const item & it ) -> bool {
return it.is_owned_by( *this );
};
auto old_moves = moves;
for( map_cursor &cursor : map_selector( pos(), PICKUP_RANGE ) ) {
auto pickups = cursor.remove_items_with( pickup_item_filter );
inv.push_back( pickups );
}
set_moves( old_moves );

// clear out inventory and add in restocked items
has_new_items = true;
inv.clear();
inv.push_back( ret );
}
}

int npc::minimum_item_value() const
Expand Down
Loading