static item_location inv_internal( player &u, const inventory_selector_preset &preset, const std::string &title, int radius, const std::string &none_message ) { u.inv.restack( &u ); u.inv.sort(); inventory_pick_selector inv_s( u, preset ); inv_s.set_title( title ); inv_s.set_display_stats( false ); inv_s.add_character_items( u ); inv_s.add_nearby_items( radius ); if( inv_s.empty() ) { const std::string msg = none_message.empty() ? _( "You don't have the necessary item at hand." ) : none_message; popup( msg, PF_GET_KEY ); return item_location(); } return inv_s.execute(); }
void game::interactive_inv() { static const std::set<int> allowed_selections = { { ' ', '.', 'q', '=', '\n', KEY_LEFT, KEY_ESCAPE } }; u.inv.restack( &u ); u.inv.sort(); inventory_pick_selector inv_s( u ); inv_s.add_character_items( u ); inv_s.set_title( _( "Inventory" ) ); int res; do { inv_s.set_hint( string_format( _( "Item hotkeys assigned: <color_ltgray>%d</color>/<color_ltgray>%d</color>" ), u.allocated_invlets().size(), inv_chars.size() - u.allocated_invlets().size() ) ); const item_location &location = inv_s.execute(); if( location == item_location::nowhere ) { break; } refresh_all(); res = inventory_item_menu( u.get_item_position( location.get_item() ) ); refresh_all(); } while( allowed_selections.count( res ) != 0 ); }
int game::display_slice(indexed_invslice const &slice, const std::string &title, bool show_worn, const int position) { inventory_selector inv_s(false, false, title); inv_s.make_item_list(slice); inv_s.prepare_paging(); inv_s.select_item_by_position(position); while(true) { inv_s.display(show_worn); const std::string action = inv_s.ctxt.handle_input(); const long ch = inv_s.ctxt.get_raw_input().get_first_input(); const int item_pos = g->u.invlet_to_position( ch ); if (item_pos != INT_MIN) { return item_pos; } else if (inv_s.handle_movement(action)) { continue; } else if (action == "CONFIRM" || action == "RIGHT") { return inv_s.get_selected_item_position(); } else if (action == "QUIT") { return INT_MIN; } else { return INT_MIN; } } }
std::list<std::pair<int, int>> game::multidrop() { u.inv.restack( &u ); u.inv.sort(); inventory_selector inv_s( u, [ this ]( const item &it ) -> bool { return u.can_unwield( it, false ); } ); if( inv_s.empty() ) { popup( std::string( _( "You have nothing to drop." ) ), PF_GET_KEY ); return std::list<std::pair<int, int> >(); } return inv_s.execute_multidrop( _( "Multidrop:" ) ); }
int game::inv_for_filter( const std::string &title, item_filter filter, const std::string &none_message ) { u.inv.restack( &u ); u.inv.sort(); inventory_selector inv_s( u, filter ); if( inv_s.empty() ) { const std::string msg = ( none_message.empty() ) ? _( "You don't have the necessary item." ) : none_message; popup( msg, PF_GET_KEY ); return INT_MIN; } return inv_s.execute_pick( title ); }
void game::compare( const tripoint &offset ) { const tripoint examp = u.pos() + offset; std::vector<std::list<item>> grounditems; indexed_invslice grounditems_slice; if( !m.has_flag( "SEALED", u.pos() ) ) { auto here = m.i_at( examp ); //Filter out items with the same name (keep only one of them) std::set<std::string> dups; for (size_t i = 0; i < here.size(); i++) { if (dups.count(here[i].tname()) == 0) { grounditems.push_back(std::list<item>(1, here[i])); //Only the first 10 items get a invlet if ( grounditems.size() <= 10 ) { // invlet: '0' ... '9' grounditems.back().front().invlet = '0' + grounditems.size() - 1; } dups.insert(here[i].tname()); } } for (size_t a = 0; a < grounditems.size(); a++) { // avoid INT_MIN, as it can be confused with "no item at all" grounditems_slice.push_back(indexed_invslice::value_type(&grounditems[a], INT_MIN + a + 1)); } } static const item_category category_on_ground( "GROUND", _( "GROUND" ), -1000 ); u.inv.restack(&u); u.inv.sort(); inventory_selector inv_s( u ); inv_s.add_entries( grounditems_slice, &category_on_ground ); if( inv_s.empty() ) { popup( std::string( _( "There are no items to compare." ) ), PF_GET_KEY ); return; } inv_s.execute_compare( _( "Compare:" ) ); }
std::list<std::pair<int, int>> game::multidrop() { u.inv.restack(&u); u.inv.sort(); const indexed_invslice stacks = u.inv.slice_filter(); inventory_selector inv_s(true, false, _("Multidrop:")); inv_s.make_item_list(stacks); inv_s.prepare_paging(); int count = 0; while(true) { inv_s.display(); const std::string action = inv_s.ctxt.handle_input(); const long ch = inv_s.ctxt.get_raw_input().get_first_input(); const int item_pos = g->u.invlet_to_position( ch ); if (ch >= '0' && ch <= '9') { count = std::max( 0, count * 10 + ((char)ch - '0') ); } else if (item_pos != INT_MIN) { inv_s.set_to_drop(item_pos, count); count = 0; } else if (inv_s.handle_movement(action)) { count = 0; continue; } else if (action == "CONFIRM") { break; } else if (action == "QUIT") { return std::list<std::pair<int, int> >(); } else if (action == "RIGHT") { inv_s.set_selected_to_drop(count); count = 0; } } std::list<std::pair<int, int>> dropped_pos_and_qty; for( auto drop_pair : inv_s.dropping ) { int num_to_drop = drop_pair.second; if( num_to_drop == -1 ) { num_to_drop = inventory::num_items_at_position( drop_pair.first ); } dropped_pos_and_qty.push_back( std::make_pair( drop_pair.first, num_to_drop ) ); } return dropped_pos_and_qty; }
void game::interactive_inv() { static const std::set<int> allowed_selections = { { ' ', '.', 'q', '=', '\n', KEY_LEFT, KEY_ESCAPE } }; u.inv.restack( &u ); u.inv.sort(); inventory_selector inv_s( u ); int res; do { const int pos = inv_s.execute_pick( _( "Inventory:" ) ); if( pos == INT_MIN ) { break; } refresh_all(); res = inventory_item_menu( pos ); } while( allowed_selections.count( res ) != 0 ); }
std::list<std::pair<int, int>> game::multidrop() { u.inv.restack( &u ); u.inv.sort(); const inventory_filter_preset preset( [ this ]( const item_location & location ) { return u.can_unwield( *location, false ); } ); inventory_drop_selector inv_s( u, preset ); inv_s.add_character_items( u ); inv_s.set_title( _( "Multidrop" ) ); inv_s.set_hint( _( "To drop x items, type a number before selecting." ) ); if( inv_s.empty() ) { popup( std::string( _( "You have nothing to drop." ) ), PF_GET_KEY ); return std::list<std::pair<int, int> >(); } return inv_s.execute(); }
void game_menus::inv::swap_letters( player &p ) { p.inv.restack( &p ); p.inv.sort(); inventory_pick_selector inv_s( p ); inv_s.add_character_items( p ); inv_s.set_title( _( "Swap Inventory Letters" ) ); inv_s.set_display_stats( false ); if( inv_s.empty() ) { popup( std::string( _( "Your inventory is empty." ) ), PF_GET_KEY ); return; } while( true ) { const std::string invlets = colorize_symbols( inv_chars.get_allowed_chars(), [ &p ]( const std::string::value_type & elem ) { if( p.assigned_invlet.count( elem ) ) { return c_yellow; } else if( p.invlet_to_position( elem ) != INT_MIN ) { return c_white; } else { return c_dkgray; } } ); inv_s.set_hint( invlets ); auto loc = inv_s.execute(); if( !loc ) { break; } reassign_letter( p, *loc ); g->refresh_all(); } }
std::list<std::pair<int, int>> game_menus::inv::multidrop( player &p ) { p.inv.restack( &p ); p.inv.sort(); const inventory_filter_preset preset( [ &p ]( const item_location & location ) { return p.can_unwield( *location ).success(); } ); inventory_drop_selector inv_s( p, preset ); inv_s.add_character_items( p ); inv_s.set_title( _( "Multidrop" ) ); inv_s.set_hint( _( "To drop x items, type a number before selecting." ) ); if( inv_s.empty() ) { popup( std::string( _( "You have nothing to drop." ) ), PF_GET_KEY ); return std::list<std::pair<int, int> >(); } return inv_s.execute(); }
void game::compare( const tripoint &offset ) { u.inv.restack( &u ); u.inv.sort(); inventory_compare_selector inv_s( u ); inv_s.add_character_items( u ); inv_s.set_title( _( "Compare" ) ); inv_s.set_hint( _( "Select two items to compare them." ) ); if( offset != tripoint_min ) { inv_s.add_map_items( u.pos() + offset ); inv_s.add_vehicle_items( u.pos() + offset ); } else { inv_s.add_nearby_items(); } if( inv_s.empty() ) { popup( std::string( _( "There are no items to compare." ) ), PF_GET_KEY ); return; } do { const auto to_compare = inv_s.execute(); if( to_compare.first == nullptr || to_compare.second == nullptr ) { break; } std::vector<iteminfo> vItemLastCh, vItemCh; std::string sItemLastCh, sItemCh, sItemLastTn, sItemTn; to_compare.first->info( true, vItemCh ); sItemCh = to_compare.first->tname(); sItemTn = to_compare.first->type_name(); to_compare.second->info( true, vItemLastCh ); sItemLastCh = to_compare.second->tname(); sItemLastTn = to_compare.second->type_name(); int iScrollPos = 0; int iScrollPosLast = 0; int ch = ( int ) ' '; do { draw_item_info( 0, ( TERMX - VIEW_OFFSET_X * 2 ) / 2, 0, TERMY - VIEW_OFFSET_Y * 2, sItemLastCh, sItemLastTn, vItemLastCh, vItemCh, iScrollPosLast, true ); //without getch( ch = draw_item_info( ( TERMX - VIEW_OFFSET_X * 2 ) / 2, ( TERMX - VIEW_OFFSET_X * 2 ) / 2, 0, TERMY - VIEW_OFFSET_Y * 2, sItemCh, sItemTn, vItemCh, vItemLastCh, iScrollPos ); if( ch == KEY_PPAGE ) { iScrollPos--; iScrollPosLast--; } else if( ch == KEY_NPAGE ) { iScrollPos++; iScrollPosLast++; } refresh_all(); } while( ch == KEY_PPAGE || ch == KEY_NPAGE ); } while( true ); }
void game::compare(int iCompareX, int iCompareY) { int examx, examy; if (iCompareX != -999 && iCompareY != -999) { examx = u.posx + iCompareX; examy = u.posy + iCompareY; } else if (!choose_adjacent(_("Compare where?"), examx, examy)) { return; } std::vector <item> &here = m.i_at(examx, examy); typedef std::vector< std::list<item> > pseudo_inventory; pseudo_inventory grounditems; indexed_invslice grounditems_slice; //Filter out items with the same name (keep only one of them) //Only the first 10 Items due to numbering 0-9 std::set<std::string> dups; for (size_t i = 0; i < here.size() && grounditems.size() < 10; i++) { if (dups.count(here[i].tname()) == 0) { grounditems.push_back(std::list<item>(1, here[i])); // invlet: '0' ... '9' grounditems.back().front().invlet = '0' + grounditems.size() - 1; dups.insert(here[i].tname()); } } for (size_t a = 0; a < grounditems.size(); a++) { // avoid INT_MIN, as it can be confused with "no item at all" grounditems_slice.push_back(indexed_invslice::value_type(&grounditems[a], INT_MIN + a + 1)); } static const item_category category_on_ground( "GROUND:", _("GROUND:"), -1000 ); u.inv.restack(&u); u.inv.sort(); const indexed_invslice stacks = u.inv.slice_filter(); inventory_selector inv_s(false, true, _("Compare:")); inv_s.make_item_list(grounditems_slice, &category_on_ground); inv_s.make_item_list(stacks); inv_s.prepare_paging(); inventory_selector::drop_map prev_droppings; while(true) { inv_s.display(); const std::string action = inv_s.ctxt.handle_input(); const long ch = inv_s.ctxt.get_raw_input().get_first_input(); const int item_pos = g->u.invlet_to_position(static_cast<char>(ch)); if (item_pos != INT_MIN) { inv_s.set_to_drop(item_pos, 0); } else if (ch >= '0' && ch <= '9' && (size_t) (ch - '0') < grounditems_slice.size()) { const int ip = ch - '0'; inv_s.set_drop_count(INT_MIN + 1 + ip, 0, grounditems_slice[ip].first->front()); } else if (inv_s.handle_movement(action)) { // continue with comparison below } else if (action == "QUIT") { break; } else if (action == "RIGHT") { inv_s.set_selected_to_drop(0); } if (inv_s.second_item != NULL) { std::vector<iteminfo> vItemLastCh, vItemCh; std::string sItemLastCh, sItemCh; inv_s.first_item->info(true, &vItemCh); sItemCh = inv_s.first_item->tname(); inv_s.second_item->info(true, &vItemLastCh); sItemLastCh = inv_s.second_item->tname(); draw_item_info(0, (TERMX - VIEW_OFFSET_X * 2) / 2, 0, TERMY - VIEW_OFFSET_Y * 2, sItemLastCh, vItemLastCh, vItemCh, -1, true); //without getch() draw_item_info((TERMX - VIEW_OFFSET_X * 2) / 2, (TERMX - VIEW_OFFSET_X * 2) / 2, 0, TERMY - VIEW_OFFSET_Y * 2, sItemCh, vItemCh, vItemLastCh); inv_s.dropping = prev_droppings; inv_s.second_item = NULL; } else { prev_droppings = inv_s.dropping; } } }
std::vector<item> game::multidrop(std::vector<item> &dropped_worn, int &freed_volume_capacity) { freed_volume_capacity = 0; u.inv.restack(&u); u.inv.sort(); const indexed_invslice stacks = u.inv.slice_filter(); inventory_selector inv_s(true, false, _("Multidrop:")); inv_s.make_item_list(stacks); inv_s.prepare_paging(); int count = 0; while(true) { inv_s.display(); const std::string action = inv_s.ctxt.handle_input(); const long ch = inv_s.ctxt.get_raw_input().get_first_input(); const int item_pos = g->u.invlet_to_position(static_cast<char>(ch)); if (ch >= '0' && ch <= '9') { count = count * 10 + ((char)ch - '0'); } else if (item_pos != INT_MIN) { inv_s.set_to_drop(item_pos, count); count = 0; } else if (inv_s.handle_movement(action)) { count = 0; continue; } else if (action == "CONFIRM") { break; } else if (action == "QUIT") { return std::vector<item>(); } else if (action == "RIGHT") { inv_s.set_selected_to_drop(count); count = 0; } } std::vector<item> ret; ret = inv_s.remove_dropping_items(g->u); const inventory_selector::drop_map &dropping = inv_s.dropping; const inventory_selector::drop_map::const_iterator dit = dropping.find(-1); if (dit != dropping.end()) { if (dit->second == -1) { ret.push_back(u.remove_weapon()); } else { ret.push_back(u.weapon); u.weapon.charges -= dit->second; ret.back().charges = dit->second; } } // We iterate backwards because deletion will invalidate later indices. for( int k = u.worn.size() - 1; k >= 0; k-- ) { const int wornpos = player::worn_position_to_index( k ); if( dropping.count( wornpos ) == 0 ) { continue; } const it_armor *ita = dynamic_cast<const it_armor *>( u.worn[k].type ); const char invlet = u.worn[k].invlet; // TODO: might be 0 if( !u.takeoff( wornpos, true ) ) { continue; } u.moves -= 250; // same as in game::takeoff if( ita != NULL ) { freed_volume_capacity += ita->storage; } // Item could have been dropped after taking it off if( !u.inv.item_by_letter( invlet ).is_null() ) { dropped_worn.push_back( u.i_rem( invlet ) ); } } return ret; }
item_location game::inv_map_splice( item_filter inv_filter, item_filter ground_filter, item_filter vehicle_filter, const std::string &title, int radius, const std::string &none_message ) { u.inv.restack( &u ); u.inv.sort(); inventory_selector inv_s( u, inv_filter ); std::list<item_category> categories; int rank = -1000; // items are stacked per tile considering vehicle and map tiles separately // in the below loops identical items on the same tile are grouped into lists // each element of stacks represents one tile and is a vector of such lists std::vector<std::vector<std::list<item>>> stacks; // an indexed_invslice is created for each map or vehicle tile // each list of items created above for the tile will be added to it std::vector<indexed_invslice> slices; // of one of the above lists so use this as the key when storing the item location std::unordered_map<item *, item_location> opts; // the closest 10 items also have their location added to the invlets vector const char min_invlet = '0'; const char max_invlet = '9'; char cur_invlet = min_invlet; for( const auto &pos : closest_tripoints_first( radius, g->u.pos() ) ) { // second get all matching items on the map within radius if( m.accessible_items( g->u.pos(), pos, radius ) ) { auto items = m.i_at( pos ); // create a new slice and stack for the current map tile stacks.emplace_back(); slices.emplace_back(); // reserve sufficient capacity to ensure reallocation is not required auto ¤t_stack = stacks.back(); current_stack.reserve( items.size() ); for( item &it : items ) { if( ground_filter( it ) ) { auto match = std::find_if( current_stack.begin(), current_stack.end(), [&]( const std::list<item> &e ) { return it.stacks_with( e.back() ); } ); if( match != current_stack.end() ) { match->push_back( it ); } else { // item doesn't stack with any previous so start new list and append to current indexed_invslice current_stack.emplace_back( 1, it ); slices.back().emplace_back( ¤t_stack.back(), INT_MIN ); opts.emplace( ¤t_stack.back().front(), item_location( pos, &it ) ); current_stack.back().front().invlet = ( cur_invlet <= max_invlet ) ? cur_invlet++ : 0; } } } std::string name = trim( std::string( _( "GROUND" ) ) + " " + direction_suffix( g->u.pos(), pos ) ); categories.emplace_back( name, name, rank++ ); inv_s.add_entries( slices.back(), &categories.back() ); } // finally get all matching items in vehicle cargo spaces int part = -1; vehicle *veh = m.veh_at( pos, part ); if( veh && part >= 0 ) { part = veh->part_with_feature( part, "CARGO" ); if( part != -1 ) { auto items = veh->get_items( part ); // create a new slice and stack for the current vehicle part stacks.emplace_back(); slices.emplace_back(); // reserve sufficient capacity to ensure reallocation is not required auto ¤t_stack = stacks.back(); current_stack.reserve( items.size() ); for( item &it : items ) { if( vehicle_filter( it ) ) { auto match = std::find_if( current_stack.begin(), current_stack.end(), [&]( const std::list<item> &e ) { return it.stacks_with( e.back() ); } ); if( match != current_stack.end() ) { match->push_back( it ); } else { // item doesn't stack with any previous so start new list and append to current indexed_invslice current_stack.emplace_back( 1, it ); slices.back().emplace_back( ¤t_stack.back(), INT_MIN ); opts.emplace( ¤t_stack.back().front(), item_location( vehicle_cursor( *veh, part ), &it ) ); current_stack.back().front().invlet = ( cur_invlet <= max_invlet ) ? cur_invlet++ : 0; } } } std::string name = trim( std::string( _( "VEHICLE" ) ) + " " + direction_suffix( g->u.pos(), pos ) ); categories.emplace_back( name, name, rank-- ); inv_s.add_entries( slices.back(), &categories.back() ); } } } if( inv_s.empty() ) { const std::string msg = ( none_message.empty() ) ? _( "You don't have the necessary item at hand." ) : none_message; popup( msg, PF_GET_KEY ); return item_location(); } return inv_s.execute_pick_map( title, opts ); }
void game::compare( const tripoint &offset ) { const tripoint examp = u.pos3() + offset; std::vector<std::list<item>> grounditems; indexed_invslice grounditems_slice; if( !m.has_flag( "SEALED", g->u.pos() ) ) { auto here = m.i_at( examp ); //Filter out items with the same name (keep only one of them) std::set<std::string> dups; for (size_t i = 0; i < here.size(); i++) { if (dups.count(here[i].tname()) == 0) { grounditems.push_back(std::list<item>(1, here[i])); //Only the first 10 items get a invlet if ( grounditems.size() <= 10 ) { // invlet: '0' ... '9' grounditems.back().front().invlet = '0' + grounditems.size() - 1; } dups.insert(here[i].tname()); } } for (size_t a = 0; a < grounditems.size(); a++) { // avoid INT_MIN, as it can be confused with "no item at all" grounditems_slice.push_back(indexed_invslice::value_type(&grounditems[a], INT_MIN + a + 1)); } } static const item_category category_on_ground( "GROUND:", _("GROUND:"), -1000 ); u.inv.restack(&u); u.inv.sort(); const indexed_invslice stacks = u.inv.slice_filter(); inventory_selector inv_s(false, true, _("Compare:")); inv_s.make_item_list(grounditems_slice, &category_on_ground); inv_s.make_item_list(stacks); inv_s.prepare_paging(); inventory_selector::drop_map prev_droppings; while(true) { inv_s.display(); const std::string action = inv_s.ctxt.handle_input(); const long ch = inv_s.ctxt.get_raw_input().get_first_input(); const int item_pos = g->u.invlet_to_position( ch ); if (item_pos != INT_MIN) { inv_s.set_to_drop(item_pos, 0); } else if (ch >= '0' && ch <= '9' && (size_t) (ch - '0') < grounditems_slice.size()) { const int ip = ch - '0'; inv_s.set_drop_count(INT_MIN + 1 + ip, 0, grounditems_slice[ip].first->front()); } else if (inv_s.handle_movement(action)) { // continue with comparison below } else if (action == "QUIT") { break; } else if (action == "RIGHT") { inv_s.set_selected_to_drop(0); } if (inv_s.second_item != NULL) { std::vector<iteminfo> vItemLastCh, vItemCh; std::string sItemLastCh, sItemCh, sItemTn; inv_s.first_item->info(true, vItemCh); sItemCh = inv_s.first_item->tname(); sItemTn = inv_s.first_item->type_name(); inv_s.second_item->info(true, vItemLastCh); sItemLastCh = inv_s.second_item->tname(); int iScrollPos = 0; int iScrollPosLast = 0; int ch = (int)' '; do { draw_item_info(0, (TERMX - VIEW_OFFSET_X * 2) / 2, 0, TERMY - VIEW_OFFSET_Y * 2, sItemLastCh, sItemTn, vItemLastCh, vItemCh, iScrollPosLast, true); //without getch() ch = draw_item_info((TERMX - VIEW_OFFSET_X * 2) / 2, (TERMX - VIEW_OFFSET_X * 2) / 2, 0, TERMY - VIEW_OFFSET_Y * 2, sItemCh, sItemTn, vItemCh, vItemLastCh, iScrollPos); if ( ch == KEY_PPAGE ) { iScrollPos--; iScrollPosLast--; } else if ( ch == KEY_NPAGE ) { iScrollPos++; iScrollPosLast++; } } while (ch == KEY_PPAGE || ch == KEY_NPAGE); inv_s.dropping = prev_droppings; inv_s.second_item = NULL; } else { prev_droppings = inv_s.dropping; } } }
item *game::inv_map_for_liquid(const item &liquid, const std::string title) { std::vector <item> &here = m.i_at(g->u.posx, g->u.posy); typedef std::vector< std::list<item> > pseudo_inventory; pseudo_inventory grounditems; indexed_invslice grounditems_slice; std::vector<item *> ground_containers; LIQUID_FILL_ERROR error; std::set<std::string> dups; for( auto item_iter = here.begin(); item_iter != here.end(); ++item_iter ) { if( item_iter->get_remaining_capacity_for_liquid(liquid, error) > 0 ) { if( dups.count( item_iter->tname()) == 0 ) { grounditems.push_back( std::list<item>(1, *item_iter) ); if( grounditems.size() <= 10 ) { grounditems.back().front().invlet = '0' + grounditems.size() - 1; } else { grounditems.back().front().invlet = ' '; } dups.insert( item_iter->tname() ); ground_containers.push_back( &*item_iter ); } } } for (size_t a = 0; a < grounditems.size(); a++) { // avoid INT_MIN, as it can be confused with "no item at all" grounditems_slice.push_back(indexed_invslice::value_type(&grounditems[a], INT_MIN + a + 1)); } static const item_category category_on_ground( "GROUND:", _("GROUND:"), -1000 ); u.inv.restack(&u); u.inv.sort(); const indexed_invslice stacks = u.inv.slice_filter_by_capacity_for_liquid(liquid); inventory_selector inv_s(false, true, title); inv_s.make_item_list(grounditems_slice, &category_on_ground); inv_s.make_item_list(stacks); inv_s.prepare_paging(); inventory_selector::drop_map prev_droppings; while (true) { inv_s.display(); const std::string action = inv_s.ctxt.handle_input(); const long ch = inv_s.ctxt.get_raw_input().get_first_input(); const int item_pos = g->u.invlet_to_position(static_cast<char>(ch)); if (item_pos != INT_MIN) { inv_s.set_to_drop(item_pos, 0); return inv_s.first_item; } else if (ch >= '0' && ch <= '9' && (size_t)(ch - '0') < grounditems_slice.size()) { const int ip = ch - '0'; return ground_containers[ip]; } else if (inv_s.handle_movement(action)) { // continue with comparison below } else if (action == "QUIT") { return NULL; } else if (action == "RIGHT" || action == "CONFIRM") { inv_s.set_selected_to_drop(0); for( size_t i = 0; i < grounditems_slice.size(); i++) { if( &grounditems_slice[i].first->front() == inv_s.first_item ) { return ground_containers[i]; } } return inv_s.first_item; } } }
item_location game::inv_map_splice( item_filter inv_filter, item_filter ground_filter, item_filter vehicle_filter, const std::string &title, int radius ) { inventory_selector inv_s( false, false, title ); // first get matching items from the inventory u.inv.restack( &u ); u.inv.sort(); inv_s.make_item_list( u.inv.slice_filter_by( inv_filter ) ); // items are stacked per tile considering vehicle and map tiles separately static const item_category ground_cat( "GROUND:", _( "GROUND:" ), -1000 ); static const item_category nearby_cat( "NEARBY:", _( "NEARBY:" ), -2000 ); static const item_category vehicle_cat( "VEHICLE:", _( "VEHICLE:" ), -3000 ); // in the below loops identical items on the same tile are grouped into lists // each element of stacks represents one tile and is a vector of such lists std::vector<std::vector<std::list<item>>> stacks; // an indexed_invslice is created for each map or vehicle tile // each list of items created above for the tile will be added to it std::vector<indexed_invslice> slices; // inv_s.first_item will later contain the chosen item as a pointer to first item // of one of the above lists so use this as the key when storing the item location std::unordered_map<item *, item_location> opts; // the closest 10 items also have their location added to the invlets vector const char min_invlet = '0'; const char max_invlet = '9'; char cur_invlet = min_invlet; std::vector<item_location> invlets; for( const auto &pos : closest_tripoints_first( radius, g->u.pos() ) ) { // second get all matching items on the map within radius if( m.accessible_items( g->u.pos(), pos, radius ) ) { auto items = m.i_at( pos ); // create a new slice and stack for the current map tile stacks.emplace_back(); slices.emplace_back(); // reserve sufficient capacity to ensure reallocation is not required auto ¤t_stack = stacks.back(); current_stack.reserve( items.size() ); for( item &it : items ) { if( ground_filter( it ) ) { auto match = std::find_if( current_stack.begin(), current_stack.end(), [&]( const std::list<item> &e ) { return it.stacks_with( e.back() ); } ); if( match != current_stack.end() ) { match->push_back( it ); } else { // item doesn't stack with any previous so start new list and append to current indexed_invslice current_stack.emplace_back( 1, it ); slices.back().emplace_back( ¤t_stack.back(), INT_MIN ); opts.emplace( ¤t_stack.back().front(), item_location::on_map( pos, &it ) ); if( cur_invlet <= max_invlet ) { current_stack.back().front().invlet = cur_invlet++; invlets.emplace_back( item_location::on_map( pos, &it ) ); } else { current_stack.back().front().invlet = 0; } } } } inv_s.make_item_list( slices.back(), pos == g->u.pos() ? &ground_cat : &nearby_cat ); } // finally get all matching items in vehicle cargo spaces int part = -1; vehicle *veh = m.veh_at( pos, part ); if( veh && part >= 0 ) { part = veh->part_with_feature( part, "CARGO" ); if( part != -1 ) { auto items = veh->get_items( part ); // create a new slice and stack for the current vehicle part stacks.emplace_back(); slices.emplace_back(); // reserve sufficient capacity to ensure reallocation is not required auto ¤t_stack = stacks.back(); current_stack.reserve( items.size() ); for( item &it : items ) { if( vehicle_filter( it ) ) { auto match = std::find_if( current_stack.begin(), current_stack.end(), [&]( const std::list<item> &e ) { return it.stacks_with( e.back() ); } ); if( match != current_stack.end() ) { match->push_back( it ); } else { // item doesn't stack with any previous so start new list and append to current indexed_invslice current_stack.emplace_back( 1, it ); slices.back().emplace_back( ¤t_stack.back(), INT_MIN ); opts.emplace( ¤t_stack.back().front(), item_location::on_vehicle( *veh, veh->parts[part].mount, &it ) ); if( cur_invlet <= max_invlet ) { current_stack.back().front().invlet = cur_invlet++; invlets.emplace_back( item_location::on_vehicle( *veh, veh->parts[part].mount, &it ) ); } else { current_stack.back().front().invlet = 0; } } } } inv_s.make_item_list( slices.back(), &vehicle_cat ); } } } inv_s.prepare_paging(); while( true ) { inv_s.display(); const std::string action = inv_s.ctxt.handle_input(); const long ch = inv_s.ctxt.get_raw_input().get_first_input(); const int item_pos = g->u.invlet_to_position( ch ); if( item_pos != INT_MIN ) { // Indexed item in inventory inv_s.set_to_drop( item_pos, 0 ); return item_location::on_character( u, inv_s.first_item ); } else if( ch >= min_invlet && ch <= max_invlet ) { // Indexed item on ground or in vehicle if( (long)invlets.size() > ch - min_invlet ) { return std::move( invlets[ch - min_invlet] ); } } else if( inv_s.handle_movement( action ) ) { // continue with comparison below } else if( action == "QUIT" ) { return item_location::nowhere(); } else if( action == "RIGHT" || action == "CONFIRM" ) { inv_s.set_selected_to_drop( 0 ); // Item in inventory if( inv_s.get_selected_item_position() != INT_MIN ) { return item_location::on_character( u, inv_s.first_item ); } // Item on ground or in vehicle auto it = opts.find( inv_s.first_item ); if( it != opts.end() ) { return std::move( it->second ); } return item_location::nowhere(); } } }
item_location game::inv_map_splice( item_filter inv_filter, item_filter ground_filter, item_filter vehicle_filter, const std::string &title ) { char cur_invlet = '0'; pseudo_inventory ground_items; pseudo_inventory vehicle_items; std::vector<item *> ground_selectables; std::vector<item *> vehicle_selectables; indexed_invslice ground_items_slice; indexed_invslice veh_items_slice; pseudo_inv_to_slice( m.i_at( g->u.pos() ), ground_filter, ground_items, ground_items_slice, ground_selectables, cur_invlet ); int part = -1; vehicle *veh = m.veh_at( g->u.pos(), part ); point veh_pt( INT_MIN, INT_MIN ); if( veh != nullptr && part >= 0 ) { part = veh->part_with_feature( part, "CARGO" ); if( part != -1 ) { veh_pt = veh->parts[part].mount; pseudo_inv_to_slice( veh->get_items( part ), vehicle_filter, vehicle_items, veh_items_slice, vehicle_selectables, cur_invlet ); } } static const item_category category_on_ground( "GROUND:", _("GROUND:"), -1000 ); static const item_category category_on_veh( "VEHICLE:", _("VEHICLE:"), -2000 ); u.inv.restack(&u); u.inv.sort(); const indexed_invslice stacks = u.inv.slice_filter_by( inv_filter ); inventory_selector inv_s(false, false, title); inv_s.make_item_list(stacks); inv_s.make_item_list(ground_items_slice, &category_on_ground); inv_s.make_item_list(veh_items_slice, &category_on_veh); inv_s.prepare_paging(); inventory_selector::drop_map prev_droppings; while( true ) { inv_s.display(); const std::string action = inv_s.ctxt.handle_input(); const long ch = inv_s.ctxt.get_raw_input().get_first_input(); const int item_pos = g->u.invlet_to_position( static_cast<char>( ch ) ); if( item_pos != INT_MIN ) { inv_s.set_to_drop(item_pos, 0); // In the inventory return item_location::on_character( u, inv_s.first_item ); } else if( ch >= first_invlet && ch <= last_invlet ) { // Indexed results on the ground or vehicle const size_t index = (size_t)(ch - first_invlet); if( index < ground_items_slice.size() ) { // Ground item return item_location::on_map( u.pos(), ground_selectables[index] ); } else if( index < ground_items_slice.size() + veh_items_slice.size() ) { // Vehicle item return item_location::on_vehicle( *veh, veh_pt, vehicle_selectables[index] ); } } else if( inv_s.handle_movement( action ) ) { // continue with comparison below } else if( action == "QUIT" ) { return item_location::nowhere(); } else if( action == "RIGHT" || action == "CONFIRM" ) { inv_s.set_selected_to_drop(0); for( size_t i = 0; i < ground_items_slice.size(); i++) { if( &ground_items_slice[i].first->front() == inv_s.first_item ) { // Ground item, may be unindexed return item_location::on_map( u.pos(), ground_selectables[i] ); } } for( size_t i = 0; i < veh_items_slice.size(); i++) { if( &veh_items_slice[i].first->front() == inv_s.first_item ) { // Vehicle item return item_location::on_vehicle( *veh, veh_pt, vehicle_selectables[i] ); } } // Inventory item or possibly nothing int inv_pos = inv_s.get_selected_item_position(); if( inv_pos == INT_MIN ) { return item_location::nowhere(); } else { return item_location::on_character( u, inv_s.first_item ); } } } }