Skip to content

Commit

Permalink
refactor drop activity and associated functions (#36205)
Browse files Browse the repository at this point in the history
  • Loading branch information
KorGgenT authored and kevingranade committed Dec 30, 2019
1 parent 63f1be8 commit 1c9dafc
Show file tree
Hide file tree
Showing 16 changed files with 202 additions and 172 deletions.
127 changes: 69 additions & 58 deletions src/activity_item_handling.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -70,21 +70,22 @@ const int ACTIVITY_SEARCH_DISTANCE = 60;

/** Activity-associated item */
struct act_item {
/// Pointer to the inventory item
const item *it;
/// inventory item
item_location loc;
/// How many items need to be processed
int count;
/// Amount of moves that processing will consume
int consumed_moves;

act_item( const item *it, int count, int consumed_moves )
: it( it ),
act_item( const item_location &loc, int count, int consumed_moves )
: loc( loc ),
count( count ),
consumed_moves( consumed_moves ) {}
};

// TODO: Deliberately unified with multidrop. Unify further.
using drop_indexes = std::list<std::pair<int, int>>;
using drop_location = std::pair<item_location, int>;
using drop_locations = std::list<std::pair<item_location, int>>;

static bool same_type( const std::list<item> &items )
{
Expand Down Expand Up @@ -351,30 +352,28 @@ void put_into_vehicle_or_drop( Character &c, item_drop_reason reason, const std:
drop_on_map( c, reason, items, where );
}

static drop_indexes convert_to_indexes( const player_activity &act )
static drop_locations convert_to_locations( const player_activity &act )
{
drop_indexes res;
drop_locations res;

if( act.values.size() % 2 != 0 ) {
if( act.values.size() != act.targets.size() ) {
debugmsg( "Drop/stash activity contains an odd number of values." );
return res;
}
for( auto iter = act.values.begin(); iter != act.values.end(); iter += 2 ) {
res.emplace_back( *iter, *std::next( iter ) );
for( size_t i = 0; i < act.values.size(); i++ ) {
res.emplace_back( act.targets[i], act.values[i] );
}
return res;
}

static drop_indexes convert_to_indexes( const player &p, const std::list<act_item> &items )
static drop_locations convert_to_locations( const std::list<act_item> &items )
{
drop_indexes res;
drop_locations res;

for( const auto &ait : items ) {
const int pos = p.get_item_position( ait.it );

if( pos != INT_MIN && ait.count > 0 ) {
if( res.empty() || res.back().first != pos ) {
res.emplace_back( pos, ait.count );
for( const act_item &ait : items ) {
if( ait.loc && ait.count > 0 ) {
if( res.empty() || res.back().first != ait.loc ) {
res.emplace_back( ait.loc, ait.count );
} else {
res.back().second += ait.count;
}
Expand All @@ -383,31 +382,30 @@ static drop_indexes convert_to_indexes( const player &p, const std::list<act_ite
return res;
}

static std::list<act_item> convert_to_items( const player &p, const drop_indexes &drop,
int min_pos, int max_pos )
static std::list<act_item> convert_to_items( Character &p, const drop_locations &drop,
std::function<bool( item_location loc )> filter )
{
std::list<act_item> res;

for( const auto &rec : drop ) {
const auto pos = rec.first;
const auto count = rec.second;
for( const drop_location &rec : drop ) {
const item_location loc = rec.first;
const int count = rec.second;

if( pos < min_pos || pos > max_pos ) {
if( !filter( loc ) ) {
continue;
} else if( pos >= 0 ) {
} else if( !p.is_worn( *loc ) && !p.is_wielding( *loc ) ) {
int obtained = 0;
for( const auto &it : p.inv.const_stack( pos ) ) {
for( const item &it : p.inv.const_stack( p.get_item_position( &*loc ) ) ) {
if( obtained >= count ) {
break;
}
const int qty = it.count_by_charges() ? std::min<int>( it.charges, count - obtained ) : 1;
obtained += qty;
// TODO: Use a calculated cost
res.emplace_back( &it, qty, 100 );
item_location loc( p, const_cast<item *>( &it ) );
res.emplace_back( loc, qty, loc.obtain_cost( p, qty ) );
}
} else {
// TODO: Use a calculated cost
res.emplace_back( &p.i_at( pos ), count, pos == -1 ? 0 : 100 );
res.emplace_back( loc, count, p.is_wielding( *loc ) ? 0 : loc.obtain_cost( p ) );
}
}

Expand All @@ -417,35 +415,46 @@ static std::list<act_item> convert_to_items( const player &p, const drop_indexes
// Prepares items for dropping by reordering them so that the drop
// cost is minimal and "dependent" items get taken off first.
// Implements the "backpack" logic.
static std::list<act_item> reorder_for_dropping( const player &p, const drop_indexes &drop )
static std::list<act_item> reorder_for_dropping( Character &p, const drop_locations &drop )
{
auto res = convert_to_items( p, drop, -1, -1 );
auto inv = convert_to_items( p, drop, 0, INT_MAX );
auto worn = convert_to_items( p, drop, INT_MIN, -2 );
std::list<act_item> res = convert_to_items( p, drop,
[&p]( item_location loc ) {
return p.is_wielding( *loc );
} );
std::list<act_item> inv = convert_to_items( p, drop,
[&p]( item_location loc ) {
return !p.is_wielding( *loc ) && !p.is_worn( *loc );
} );
std::list<act_item> worn = convert_to_items( p, drop,
[&p]( item_location loc ) {
return p.is_worn( *loc );
} );

// Sort inventory items by volume in ascending order
inv.sort( []( const act_item & first, const act_item & second ) {
return first.it->volume() < second.it->volume();
return first.loc->volume() < second.loc->volume();
} );
// Add missing dependent worn items (if any).
for( const auto &wait : worn ) {
for( const auto dit : p.get_dependent_worn_items( *wait.it ) ) {
for( item *dit : p.get_dependent_worn_items( *wait.loc ) ) {
const auto iter = std::find_if( worn.begin(), worn.end(),
[dit]( const act_item & ait ) {
return ait.it == dit;
return &*ait.loc == dit;
} );

if( iter == worn.end() ) {
// TODO: Use a calculated cost
worn.emplace_front( dit, dit->count(), 100 );
const item_location loc( p, dit );
act_item act( loc, loc->count(), loc.obtain_cost( p, loc->count() ) );
worn.emplace_front( loc, loc->count(), loc.obtain_cost( p ) );
}
}
}
// Sort worn items by storage in descending order, but dependent items always go first.
worn.sort( []( const act_item & first, const act_item & second ) {
return first.it->is_worn_only_with( *second.it )
|| ( first.it->get_storage() > second.it->get_storage()
&& !second.it->is_worn_only_with( *first.it ) );
return first.loc->is_worn_only_with( *second.loc )
|| ( first.loc->get_storage() > second.loc->get_storage()
&& !second.loc->is_worn_only_with( *first.loc ) );
} );

// Cumulatively increases
Expand All @@ -454,9 +463,9 @@ static std::list<act_item> reorder_for_dropping( const player &p, const drop_ind
units::volume remaining_storage = p.volume_capacity();

while( !worn.empty() && !inv.empty() ) {
storage_loss += worn.front().it->get_storage();
storage_loss += worn.front().loc->get_storage();
remaining_storage -= p.volume_capacity_reduced_by( storage_loss );
units::volume inventory_item_volume = inv.front().it->volume();
units::volume inventory_item_volume = inv.front().loc->volume();
// Does not fit
if( remaining_storage < inventory_item_volume ) {
break;
Expand Down Expand Up @@ -492,7 +501,7 @@ static void debug_drop_list( const std::list<act_item> &list )
std::string res( "Items ordered to drop:\n" );
for( const auto &ait : list ) {
res += string_format( "Drop %d %s for %d moves\n",
ait.count, ait.it->display_name( ait.count ), ait.consumed_moves );
ait.count, ait.loc->display_name( ait.count ), ait.consumed_moves );
}
popup( res, PF_GET_KEY );
}
Expand All @@ -501,21 +510,21 @@ static std::list<item> obtain_activity_items( player_activity &act, player &p )
{
std::list<item> res;

auto items = reorder_for_dropping( p, convert_to_indexes( act ) );
std::list<act_item> items = reorder_for_dropping( p, convert_to_locations( act ) );

debug_drop_list( items );

while( !items.empty() && ( p.is_npc() || p.moves > 0 || items.front().consumed_moves == 0 ) ) {
const auto &ait = items.front();
act_item &ait = items.front();

p.mod_moves( -ait.consumed_moves );

if( p.is_worn( *ait.it ) ) {
p.takeoff( *ait.it, &res );
} else if( ait.it->count_by_charges() ) {
res.push_back( p.reduce_charges( const_cast<item *>( ait.it ), ait.count ) );
if( p.is_worn( *ait.loc ) ) {
p.takeoff( *ait.loc, &res );
} else if( ait.loc->count_by_charges() ) {
res.push_back( p.reduce_charges( const_cast<item *>( &*ait.loc ), ait.count ) );
} else {
res.push_back( p.i_rem( ait.it ) );
res.push_back( p.i_rem( &*ait.loc ) );
}

items.pop_front();
Expand All @@ -527,10 +536,11 @@ static std::list<item> obtain_activity_items( player_activity &act, player &p )
res.insert( res.begin(), excess.begin(), excess.end() );
}
// Load anything that remains (if any) into the activity
act.targets.clear();
act.values.clear();
if( !items.empty() ) {
for( const auto &drop : convert_to_indexes( p, items ) ) {
act.values.push_back( drop.first );
for( const drop_location &drop : convert_to_locations( items ) ) {
act.targets.push_back( drop.first );
act.values.push_back( drop.second );
}
}
Expand Down Expand Up @@ -607,14 +617,14 @@ void activity_on_turn_wear( player_activity &act, player &p )

void activity_handlers::washing_finish( player_activity *act, player *p )
{
auto items = reorder_for_dropping( *p, convert_to_indexes( *act ) );
std::list<act_item> items = reorder_for_dropping( *p, convert_to_locations( *act ) );

// Check again that we have enough water and soap incase the amount in our inventory changed somehow
// Consume the water and soap
units::volume total_volume = 0_ml;

for( const act_item &filthy_item : items ) {
total_volume += filthy_item.it->volume();
total_volume += filthy_item.loc->volume();
}
washing_requirements required = washing_requirements_for_volume( total_volume );

Expand All @@ -638,7 +648,7 @@ void activity_handlers::washing_finish( player_activity *act, player *p )
}

for( const auto &ait : items ) {
item *filthy_item = const_cast<item *>( ait.it );
item *filthy_item = const_cast<item *>( &*ait.loc );
filthy_item->item_tags.erase( "FILTHY" );
p->on_worn_item_washed( *filthy_item );
}
Expand Down Expand Up @@ -1926,10 +1936,11 @@ static bool tidy_activity( player &p, const tripoint &src_loc,
}
// we are adjacent to an unsorted zone, we came here to just drop items we are carrying
if( mgr.has( zone_type_id( z_loot_unsorted ), g->m.getabs( src_loc ) ) ) {
for( auto inv_elem : p.inv_dump() ) {
for( item *inv_elem : p.inv_dump() ) {
if( inv_elem->has_var( "activity_var" ) ) {
inv_elem->erase_var( "activity_var" );
p.drop( p.get_item_position( inv_elem ), src_loc );
item_location loc( p, inv_elem );
p.drop( loc, src_loc );
}
}
}
Expand Down
54 changes: 33 additions & 21 deletions src/advanced_inv.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -759,8 +759,8 @@ enum aim_entry {

bool advanced_inventory::move_all_items( bool nested_call )
{
auto &spane = panes[src];
auto &dpane = panes[dest];
advanced_inventory_pane &spane = panes[src];
advanced_inventory_pane &dpane = panes[dest];

// AIM_ALL source area routine
if( spane.get_area() == AIM_ALL ) {
Expand All @@ -775,8 +775,8 @@ bool advanced_inventory::move_all_items( bool nested_call )
return false;
}

auto &sarea = squares[spane.get_area()];
auto &darea = squares[dpane.get_area()];
advanced_inv_area &sarea = squares[spane.get_area()];
advanced_inv_area &darea = squares[dpane.get_area()];

// Check first if the destination area still have enough room for moving all.
if( !is_processing() && sarea.volume > darea.free_volume( dpane.in_vehicle() ) &&
Expand All @@ -787,12 +787,12 @@ bool advanced_inventory::move_all_items( bool nested_call )
// make sure that there are items to be moved
bool done = false;
// copy the current pane, to be restored after the move is queued
auto shadow = panes[src];
advanced_inventory_pane shadow = panes[src];
// here we recursively call this function with each area in order to
// put all items in the proper destination area, with minimal fuss
auto &loc = uistate.adv_inv_aim_all_location;
int &loc = uistate.adv_inv_aim_all_location;
// re-entry nonsense
auto &entry = uistate.adv_inv_re_enter_move_all;
int &entry = uistate.adv_inv_re_enter_move_all;
// if we are just starting out, set entry to initial value
switch( static_cast<aim_entry>( entry++ ) ) {
case ENTRY_START:
Expand Down Expand Up @@ -855,8 +855,8 @@ bool advanced_inventory::move_all_items( bool nested_call )
popup( _( "You can't put items there!" ) );
return false;
}
auto &sarea = squares[spane.get_area()];
auto &darea = squares[dpane.get_area()];
advanced_inv_area &sarea = squares[spane.get_area()];
advanced_inv_area &darea = squares[dpane.get_area()];

// Make sure source and destination are different, otherwise items will disappear
// Need to check actual position to account for dragged vehicles
Expand All @@ -882,31 +882,43 @@ bool advanced_inventory::move_all_items( bool nested_call )
}

if( spane.get_area() == AIM_INVENTORY || spane.get_area() == AIM_WORN ) {
std::list<std::pair<int, int>> dropped;
drop_locations dropped;
// keep a list of favorites separated, only drop non-fav first if they exist
std::list<std::pair<int, int>> dropped_favorite;
drop_locations dropped_favorite;

if( spane.get_area() == AIM_INVENTORY ) {
for( size_t index = 0; index < g->u.inv.size(); ++index ) {
const auto &stack = g->u.inv.const_stack( index );
const auto &it = stack.front();
const std::list<item> &stack = g->u.inv.const_stack( index );
const item &it = stack.front();
item_location indexed_item( g->u, const_cast<item *>( &it ) );

if( !spane.is_filtered( it ) ) {
( it.is_favorite ? dropped_favorite : dropped ).emplace_back( static_cast<int>( index ),
it.count_by_charges() ? static_cast<int>( it.charges ) : static_cast<int>( stack.size() ) );
int count;
if( it.count_by_charges() ) {
count = it.charges;
} else {
count = stack.size();
}
if( it.is_favorite ) {
dropped_favorite.emplace_back( indexed_item, count );
} else {
dropped.emplace_back( indexed_item, count );
}
}
}
} else if( spane.get_area() == AIM_WORN ) {
// do this in reverse, to account for vector item removal messing with future indices
auto iter = g->u.worn.rbegin();
for( size_t idx = 0; idx < g->u.worn.size(); ++idx, ++iter ) {
const size_t index = g->u.worn.size() - idx - 1;
const auto &it = *iter;
item &it = *iter;

if( !spane.is_filtered( it ) ) {
( it.is_favorite ? dropped_favorite : dropped ).emplace_back( player::worn_position_to_index(
index ),
it.count() );
item_location loc( g->u, &it );
if( it.is_favorite ) {
dropped_favorite.emplace_back( loc, it.count() );
} else {
dropped.emplace_back( loc, it.count() );
}
}
}
}
Expand Down Expand Up @@ -1264,7 +1276,7 @@ void advanced_inventory::display()
g->u.activity.str_values.push_back( "force_ground" );
}

g->u.activity.values.push_back( idx );
g->u.activity.targets.push_back( item_location( g->u, &g->u.i_at( idx ) ) );
g->u.activity.values.push_back( amount_to_move );

// exit so that the activity can be carried out
Expand Down
Loading

0 comments on commit 1c9dafc

Please sign in to comment.