diff --git a/src/avatar_action.cpp b/src/avatar_action.cpp index f96e480949572..85cfe123b4145 100644 --- a/src/avatar_action.cpp +++ b/src/avatar_action.cpp @@ -870,7 +870,8 @@ void avatar_action::plthrow( avatar &you, int pos, } if( pos == INT_MIN ) { - pos = g->inv_for_all( _( "Throw item" ), _( "You don't have any items to throw." ) ); + pos = you.get_item_position( game_menus::inv::titled_menu( you, _( "Throw item" ), + _( "You don't have any items to throw." ) ).get_item() ); g->refresh_all(); } diff --git a/src/computer.cpp b/src/computer.cpp index d7c38aae5817a..c750beb13cbec 100644 --- a/src/computer.cpp +++ b/src/computer.cpp @@ -45,6 +45,7 @@ #include "creature.h" #include "enums.h" #include "game_constants.h" +#include "game_inventory.h" #include "int_id.h" #include "item.h" #include "omdata.h" @@ -366,9 +367,13 @@ void computer::load_data( const std::string &data ) static item *pick_usb() { - const int pos = g->inv_for_id( itype_id( "usb_drive" ), _( "Choose drive:" ) ); - if( pos != INT_MIN ) { - return &g->u.i_at( pos ); + auto filter = []( const item & it ) { + return it.typeId() == "usb_drive"; + }; + + item_location loc = game_menus::inv::titled_filter_menu( filter, g->u, _( "Choose drive:" ) ); + if( loc ) { + return &*loc; } return nullptr; } diff --git a/src/debug_menu.cpp b/src/debug_menu.cpp index c1815a79ab64e..96334e212ba0a 100644 --- a/src/debug_menu.cpp +++ b/src/debug_menu.cpp @@ -27,6 +27,7 @@ #include "faction.h" #include "filesystem.h" #include "game.h" +#include "game_inventory.h" #include "map_extras.h" #include "messages.h" #include "mission.h" @@ -518,8 +519,11 @@ void character_edit_menu() p.weapon = item(); break; case D_ITEM_WORN: { - int item_pos = g->inv_for_all( _( "Make target equip" ) ); - item &to_wear = g->u.i_at( item_pos ); + item_location loc = game_menus::inv::titled_menu( g->u, _( "Make target equip" ) ); + if( !loc ) { + break; + } + item &to_wear = *loc; if( to_wear.is_armor() ) { p.on_item_wear( to_wear ); p.worn.push_back( to_wear ); diff --git a/src/game.cpp b/src/game.cpp index 2d028619c2dd6..653eb581bb81f 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -5346,13 +5346,13 @@ bool game::npc_menu( npc &who ) actor->head_power >= 0 && actor->torso_power >= 0; }; - const int pos = inv_for_filter( _( "Use which item?" ), will_accept ); + item_location loc = game_menus::inv::titled_filter_menu( will_accept, u, _( "Use which item?" ) ); - if( pos == INT_MIN ) { + if( !loc ) { add_msg( _( "Never mind" ) ); return false; } - item &used = u.i_at( pos ); + item &used = *loc; bool did_use = u.invoke_item( &used, heal_string, who.pos() ); if( did_use ) { // Note: exiting a body part selection menu counts as use here @@ -8446,9 +8446,11 @@ void game::eat( item_location( *menu )( player &p ), int pos ) void game::change_side( int pos ) { if( pos == INT_MIN ) { - pos = inv_for_filter( _( "Change side for item" ), [&]( const item & it ) { + auto filter = [&]( const item & it ) { return u.is_worn( it ) && it.is_sided(); - }, _( "You don't have sided items worn." ) ); + }; + pos = u.get_item_position( game_menus::inv::titled_filter_menu( filter, u, + _( "Change side for item" ), _( "You don't have sided items worn." ) ).get_item() ); } if( pos == INT_MIN ) { add_msg( _( "Never mind." ) ); diff --git a/src/game.h b/src/game.h index 95fc12ae0d675..92e01179e7733 100644 --- a/src/game.h +++ b/src/game.h @@ -562,13 +562,6 @@ class game void draw_trail_to_square( const tripoint &t, bool bDrawX ); - // TODO: Move these functions to game_menus::inv and isolate them. - int inv_for_filter( const std::string &title, item_filter filter, - const std::string &none_message = "" ); - int inv_for_all( const std::string &title, const std::string &none_message = "" ); - int inv_for_flag( const std::string &flag, const std::string &title ); - int inv_for_id( const itype_id &id, const std::string &title ); - enum inventory_item_menu_positon { RIGHT_TERMINAL_EDGE, LEFT_OF_INFO, diff --git a/src/game_inventory.cpp b/src/game_inventory.cpp index ce674e09f7b66..da130b8cceef1 100644 --- a/src/game_inventory.cpp +++ b/src/game_inventory.cpp @@ -207,31 +207,18 @@ void game_menus::inv::common( avatar &you ) } while( loop_options.count( res ) != 0 ); } -int game::inv_for_filter( const std::string &title, item_filter filter, - const std::string &none_message ) +item_location game_menus::inv::titled_filter_menu( item_filter filter, avatar &you, + const std::string &title, const std::string &none_message ) { - return u.get_item_position( inv_map_splice( filter, title, -1, none_message ).get_item() ); + return inv_internal( you, inventory_filter_preset( convert_filter( filter ) ), + title, -1, none_message ); } -int game::inv_for_all( const std::string &title, const std::string &none_message ) +item_location game_menus::inv::titled_menu( avatar &you, const std::string &title, + const std::string &none_message ) { const std::string msg = none_message.empty() ? _( "Your inventory is empty." ) : none_message; - return u.get_item_position( inv_internal( u, inventory_selector_preset(), title, -1, - msg ).get_item() ); -} - -int game::inv_for_flag( const std::string &flag, const std::string &title ) -{ - return inv_for_filter( title, [ &flag ]( const item & it ) { - return it.has_flag( flag ); - } ); -} - -int game::inv_for_id( const itype_id &id, const std::string &title ) -{ - return inv_for_filter( title, [ &id ]( const item & it ) { - return it.typeId() == id; - }, string_format( _( "You don't have a %s." ), item::nname( id ) ) ); + return inv_internal( you, inventory_selector_preset(), title, -1, msg ); } class armor_inventory_preset: public inventory_selector_preset diff --git a/src/game_inventory.h b/src/game_inventory.h index 5fc752b97975f..069605dc183c9 100644 --- a/src/game_inventory.h +++ b/src/game_inventory.h @@ -22,6 +22,7 @@ class player; class salvage_actor; class repair_item_actor; +using item_filter = std::function; using item_location_filter = std::function; class inventory_filter_preset : public inventory_selector_preset @@ -39,6 +40,12 @@ namespace game_menus namespace inv { +// item selector for all items in @you's inventory. +item_location titled_menu( avatar &you, const std::string &title, + const std::string &none_message = "" ); +// item selector for items in @you's inventory with a filter +item_location titled_filter_menu( item_filter filter, avatar &you, + const std::string &title, const std::string &none_message = "" ); /** * @name Customized inventory menus diff --git a/src/iuse.cpp b/src/iuse.cpp index 520087d67e003..e3c060e016871 100644 --- a/src/iuse.cpp +++ b/src/iuse.cpp @@ -1597,15 +1597,19 @@ int iuse::radio_mod( player *p, item *, bool, const tripoint & ) return 0; } - int inventory_index = g->inv_for_filter( _( "Modify what?" ), []( const item & itm ) { + auto filter = []( const item & itm ) { return itm.has_flag( "RADIO_MODABLE" ); - } ); - item &modded = p->i_at( inventory_index ); + }; + + // note: if !p->is_npc() then p is avatar + item_location loc = game_menus::inv::titled_filter_menu( + filter, *p->as_avatar(), _( "Modify what?" ) ); - if( modded.is_null() ) { + if( !loc ) { p->add_msg_if_player( _( "You do not have that item!" ) ); return 0; } + item &modded = *loc; int choice = uilist( _( "Which signal should activate the item?" ), { _( "\"Red\"" ), _( "\"Blue\"" ), _( "\"Green\"" ) @@ -4623,12 +4627,20 @@ int iuse::mind_splicer( player *p, item *it, bool, const tripoint & ) if( map_it.typeId() == "rmi2_corpse" && query_yn( _( "Use the mind splicer kit on the %s?" ), colorize( map_it.tname(), map_it.color_in_inventory() ) ) ) { - int pos = g->inv_for_id( itype_id( "data_card" ), _( "Select storage media" ) ); - item &data_card = p->i_at( pos ); - if( data_card.is_null() ) { + + auto filter = []( const item & it ) { + return it.typeId() == "data_card"; + }; + avatar *you = p->as_avatar(); + item_location loc; + if( you != nullptr ) { + loc = game_menus::inv::titled_filter_menu( filter, *you, _( "Select storage media" ) ); + } + if( !loc ) { add_msg( m_info, _( "Nevermind." ) ); return 0; } + item &data_card = *loc; ///\EFFECT_DEX makes using the mind splicer faster ///\EFFECT_FIRSTAID makes using the mind splicer faster const time_duration time = std::max( 150_minutes - 20_minutes * ( p->get_skill_level( @@ -4676,15 +4688,20 @@ int iuse::lumber( player *p, item *it, bool t, const tripoint & ) } // If the player is not standing on a log, check inventory - int pos = g->inv_for_id( itype_id( "log" ), _( "Cut up what?" ) ); - - item &cut = p->i_at( pos ); + avatar *you = p->as_avatar(); + item_location loc; + auto filter = []( const item & it ) { + return it.typeId() == "log"; + }; + if( you != nullptr ) { + loc = game_menus::inv::titled_filter_menu( filter, *you, _( "Cut up what?" ) ); + } - if( cut.is_null() ) { + if( !loc ) { p->add_msg_if_player( m_info, _( "You do not have that item!" ) ); return 0; } - p->i_rem( &cut ); + p->i_rem( &*loc ); cut_log_into_planks( *p ); return it->type->charges_to_use(); } @@ -5865,12 +5882,12 @@ int iuse::gun_repair( player *p, item *it, bool, const tripoint & ) p->add_msg_if_player( m_info, _( "You need a mechanics skill of 2 to use this repair kit." ) ); return 0; } - int inventory_index = g->inv_for_all( _( "Select the firearm to repair" ) ); - item &fix = p->i_at( inventory_index ); - if( fix.is_null() ) { + item_location loc = game_menus::inv::titled_menu( g->u, ( "Select the firearm to repair" ) ); + if( !loc ) { p->add_msg_if_player( m_info, _( "You do not have that item!" ) ); return 0; } + item &fix = *loc; if( !fix.is_firearm() ) { p->add_msg_if_player( m_info, _( "That isn't a firearm!" ) ); return 0; @@ -6011,21 +6028,31 @@ int iuse::misc_repair( player *p, item *it, bool, const tripoint & ) p->add_msg_if_player( m_info, _( "You need a fabrication skill of 1 to use this repair kit." ) ); return 0; } - static const std::set repairable { + static const std::set repairable{ material_id( "wood" ), material_id( "paper" ), material_id( "bone" ), material_id( "chitin" ), material_id( "acidchitin" ) }; - int inventory_index = g->inv_for_filter( _( "Select the item to repair" ), []( const item & itm ) { + + auto filter = []( const item & itm ) { return !itm.is_firearm() && itm.made_of_any( repairable ) && !itm.count_by_charges(); - } ); - item &fix = p->i_at( inventory_index ); - if( fix.is_null() ) { + }; + + item_location loc; + avatar *you = p->as_avatar(); + if( you != nullptr ) { + loc = game_menus::inv::titled_filter_menu( + filter, *you, _( "Select the item to repair" ) ); + } + + if( !loc ) { p->add_msg_if_player( m_info, _( "You do not have that item!" ) ); return 0; } + item &fix = *loc; + if( fix.damage() <= fix.min_damage() ) { p->add_msg_if_player( m_info, _( "You cannot improve your %s any more this way." ), fix.tname() ); @@ -6687,17 +6714,26 @@ int iuse::einktabletpc( player *p, item *it, bool t, const tripoint &pos ) return it->type->charges_to_use(); } + avatar *you = p->as_avatar(); + item_location loc; + auto filter = []( const item & it ) { + return it.has_flag( "MC_MOBILE" ); + }; + const std::string title = _( "Insert memory card" ); + if( ei_download == choice ) { p->moves -= to_moves( 2_seconds ); - const int inventory_index = g->inv_for_flag( "MC_MOBILE", _( "Insert memory card" ) ); - item &mc = p->i_at( inventory_index ); - - if( mc.is_null() ) { + if( you != nullptr ) { + loc = game_menus::inv::titled_filter_menu( filter, *you, title ); + } + if( !loc ) { p->add_msg_if_player( m_info, _( "You do not have that item!" ) ); return it->type->charges_to_use(); } + item &mc = *loc; + if( !mc.has_flag( "MC_MOBILE" ) ) { p->add_msg_if_player( m_info, _( "This is not a compatible memory card." ) ); return it->type->charges_to_use(); @@ -6721,13 +6757,15 @@ int iuse::einktabletpc( player *p, item *it, bool t, const tripoint &pos ) if( ei_decrypt == choice ) { p->moves -= to_moves( 2_seconds ); - const int inventory_index = g->inv_for_flag( "MC_MOBILE", _( "Insert memory card" ) ); - item &mc = p->i_at( inventory_index ); - - if( mc.is_null() ) { + if( you != nullptr ) { + loc = game_menus::inv::titled_filter_menu( filter, *you, title ); + } + if( !loc ) { p->add_msg_if_player( m_info, _( "You do not have that item!" ) ); return it->type->charges_to_use(); } + item &mc = *loc; + if( !mc.has_flag( "MC_MOBILE" ) ) { p->add_msg_if_player( m_info, _( "This is not a compatible memory card." ) ); return it->type->charges_to_use(); @@ -7829,13 +7867,19 @@ int iuse::camera( player *p, item *it, bool, const tripoint & ) p->moves -= to_moves( 2_seconds ); - const int inventory_index = g->inv_for_flag( "MC_MOBILE", _( "Insert memory card" ) ); - item &mc = p->i_at( inventory_index ); - - if( mc.is_null() ) { + avatar *you = p->as_avatar(); + item_location loc; + if( you != nullptr ) { + loc = game_menus::inv::titled_filter_menu( []( const item & it ) { + return it.has_flag( "MC_MOBILE" ); + }, *you, _( "Insert memory card" ) ); + } + if( !loc ) { p->add_msg_if_player( m_info, _( "You do not have that item!" ) ); return it->type->charges_to_use(); } + item &mc = *loc; + if( !mc.has_flag( "MC_MOBILE" ) ) { p->add_msg_if_player( m_info, _( "This is not a compatible memory card." ) ); return it->type->charges_to_use(); @@ -8022,19 +8066,26 @@ int iuse::radiocar( player *p, item *it, bool, const tripoint & ) if( choice == 1 ) { if( bomb_it == it->contents.end() ) { //arming car with bomb - int inventory_index = g->inv_for_flag( "RADIOCARITEM", _( "Arm what?" ) ); - item &put = p->i_at( inventory_index ); - if( put.is_null() ) { + + avatar *you = p->as_avatar(); + item_location loc; + if( you != nullptr ) { + loc = game_menus::inv::titled_filter_menu( []( const item & it ) { + return it.has_flag( "RADIOCARITEM" ); + }, *you, _( "Arm what?" ) ); + } + if( !loc ) { p->add_msg_if_player( m_info, _( "You do not have that item!" ) ); return 0; } + item &put = *loc; if( put.has_flag( "RADIOCARITEM" ) && ( put.volume() <= 1250_ml || ( put.weight() <= 2_kilogram ) ) ) { p->moves -= to_moves( 3_seconds ); p->add_msg_if_player( _( "You armed your RC car with %s." ), put.tname() ); - it->put_in( p->i_rem( inventory_index ) ); + it->put_in( p->i_rem( &put ) ); } else if( !put.has_flag( "RADIOCARITEM" ) ) { p->add_msg_if_player( _( "RC car with %s? How?" ), put.tname() ); @@ -8859,6 +8910,16 @@ int iuse::cable_attach( player *p, item *it, bool, const tripoint & ) const bool wearing_solar_pack = has_solar_pack || has_solar_pack_on; const bool has_ups = p->has_charges( "UPS_off", 1 ) || p->has_charges( "adv_UPS_off", 1 ); + item_location loc; + avatar *you = p->as_avatar(); + + auto filter = [&]( const item & itm ) { + return itm.has_flag( "IS_UPS" ); + }; + + const std::string choose_ups = _( "Choose UPS:" ); + const std::string dont_have_ups = _( "You don't have any UPS." ); + const auto set_cable_active = []( player * p, item * it, const std::string & state ) { it->set_var( "state", state ); it->active = true; @@ -8891,14 +8952,14 @@ int iuse::cable_attach( player *p, item *it, bool, const tripoint & ) p->add_msg_if_player( m_info, _( "You attach the cable to the solar pack." ) ); return 0; } else if( choice == 3 ) { - int pos = g->inv_for_filter( _( "Choose UPS:" ), [&]( const item & itm ) { - return itm.has_flag( "IS_UPS" ); - }, _( "You don't have any UPS." ) ); - if( pos == INT_MIN ) { + if( you != nullptr ) { + loc = game_menus::inv::titled_filter_menu( filter, *you, choose_ups, dont_have_ups ); + } + if( !loc ) { add_msg( _( "Never mind" ) ); return 0; } - item &chosen = p->i_at( pos ); + item &chosen = *loc; chosen.set_var( "cable", "plugged_in" ); chosen.activate(); set_cable_active( p, it, "UPS" ); @@ -8996,15 +9057,13 @@ int iuse::cable_attach( player *p, item *it, bool, const tripoint & ) p->add_msg_if_player( m_good, _( "You are now plugged to the solar backpack." ) ); return 0; } else if( choice == 4 ) { + loc = game_menus::inv::titled_filter_menu( filter, *you, choose_ups, dont_have_ups ); // connecting self to UPS - int pos = g->inv_for_filter( _( "Choose UPS:" ), [&]( const item & itm ) { - return itm.has_flag( "IS_UPS" ); - }, _( "You don't have any UPS." ) ); - if( pos == INT_MIN ) { + if( !loc ) { add_msg( _( "Never mind" ) ); return 0; } - item &chosen = p->i_at( pos ); + item &chosen = *loc; chosen.set_var( "cable", "plugged_in" ); chosen.activate(); set_cable_active( p, it, "UPS_link" ); diff --git a/src/iuse_actor.cpp b/src/iuse_actor.cpp index 408343dd93e40..61f1664eb71c3 100644 --- a/src/iuse_actor.cpp +++ b/src/iuse_actor.cpp @@ -1700,12 +1700,12 @@ int inscribe_actor::use( player &p, item &it, bool t, const tripoint & ) const return iuse::handle_ground_graffiti( p, &it, string_format( _( "%s what?" ), verb ), p.pos() ); } - int pos = g->inv_for_all( _( "Inscribe which item?" ) ); - item &cut = p.i_at( pos ); - if( cut.is_null() ) { + item_location loc = game_menus::inv::titled_menu( g->u, _( "Inscribe which item?" ) ); + if( !loc ) { p.add_msg_if_player( m_info, _( "Never mind." ) ); return 0; } + item &cut = *loc; if( &cut == &it ) { p.add_msg_if_player( _( "You try to bend your %s, but fail." ), it.tname() ); return 0; @@ -4511,15 +4511,18 @@ int sew_advanced_actor::use( player &p, item &it, bool, const tripoint & ) const return 0; } - int pos = g->inv_for_filter( _( "Enhance which clothing?" ), [ this ]( const item & itm ) { + auto filter = [this]( const item & itm ) { return itm.is_armor() && !itm.is_firearm() && !itm.is_power_armor() && itm.made_of_any( materials ); - } ); - item &mod = p.i_at( pos ); - if( mod.is_null() ) { + }; + // note: if !p.is_npc() then p is avatar. + item_location loc = game_menus::inv::titled_filter_menu( + filter, *p.as_avatar(), _( "Enhance which clothing?" ) ); + if( !loc ) { p.add_msg_if_player( m_info, _( "You do not have that item!" ) ); return 0; } + item &mod = *loc; if( &mod == &it ) { p.add_msg_if_player( m_info, _( "This can be used to repair or modify other items, not itself." ) ); @@ -4681,7 +4684,7 @@ int sew_advanced_actor::use( player &p, item &it, bool, const tripoint & ) const mod.tname( 1, false ), startdurability, resultdurability ); if( destroyed ) { p.add_msg_if_player( m_bad, _( "You destroy it!" ) ); - p.i_rem_keep_contents( pos ); + p.i_rem_keep_contents( p.get_item_position( &mod ) ); } return thread_needed / 2; } else if( rn <= 10 ) { diff --git a/src/monexamine.cpp b/src/monexamine.cpp index 3a1db0ae8352e..876617a71117b 100644 --- a/src/monexamine.cpp +++ b/src/monexamine.cpp @@ -253,12 +253,15 @@ bool monexamine::pet_menu( monster &z ) int monexamine::pet_armor_pos( monster &z ) { - int pos = g->inv_for_filter( _( "Pet armor" ), [z]( const item & it ) { + auto filter = [z]( const item & it ) { return z.type->bodytype == it.get_pet_armor_bodytype() && z.get_volume() >= it.get_pet_armor_min_vol() && z.get_volume() <= it.get_pet_armor_max_vol(); - } ); - return pos; + }; + + item_location loc = game_menus::inv::titled_filter_menu( filter, g->u, _( "Pet armor" ) ); + + return g->u.get_item_position( loc.get_item() ); } void monexamine::remove_battery( monster &z ) @@ -461,21 +464,24 @@ void monexamine::rename_pet( monster &z ) void monexamine::attach_bag_to( monster &z ) { std::string pet_name = z.get_name(); - int pos = g->inv_for_filter( _( "Bag item" ), []( const item & it ) { + + auto filter = []( const item & it ) { return it.is_armor() && it.get_storage() > 0_ml; - } ); + }; - if( pos == INT_MIN ) { + item_location loc = game_menus::inv::titled_filter_menu( filter, g->u, _( "Bag item" ) ); + + if( !loc ) { add_msg( _( "Never mind." ) ); return; } - item &it = g->u.i_at( pos ); + item &it = *loc; // force it to the front of the monster's inventory in case they have armor on z.inv.insert( z.inv.begin(), it ); add_msg( _( "You mount the %1$s on your %2$s, ready to store gear." ), it.display_name(), pet_name ); - g->u.i_rem( pos ); + g->u.i_rem( &*loc ); z.add_effect( effect_has_bag, 1_turns, num_bp, true ); // Update encumbrance in case we were wearing it g->u.flag_encumbrance(); diff --git a/src/npctalk.cpp b/src/npctalk.cpp index 06f30a13677cb..83197ebb4bd13 100644 --- a/src/npctalk.cpp +++ b/src/npctalk.cpp @@ -24,6 +24,7 @@ #include "debug.h" #include "faction_camp.h" #include "game.h" +#include "game_inventory.h" #include "help.h" #include "input.h" #include "item.h" @@ -3227,11 +3228,12 @@ std::string give_item_to( npc &p, bool allow_use, bool allow_carry ) if( p.is_hallucination() ) { return _( "No thanks, I'm good." ); } - const int inv_pos = g->inv_for_all( _( "Offer what?" ), _( "You have no items to offer." ) ); - item &given = g->u.i_at( inv_pos ); - if( given.is_null() ) { + item_location loc = game_menus::inv::titled_menu( g->u, _( "Offer what?" ), + _( "You have no items to offer." ) ); + if( !loc ) { return _( "Changed your mind?" ); } + item &given = *loc; if( ( &given == &g->u.weapon && given.has_flag( "NO_UNWIELD" ) ) || ( g->u.is_worn( given ) && given.has_flag( "NO_TAKEOFF" ) ) ) { @@ -3248,7 +3250,7 @@ std::string give_item_to( npc &p, bool allow_use, bool allow_carry ) // Eating first, to avoid evaluating bread as a weapon const auto consume_res = try_consume( p, given, no_consume_reason ); if( consume_res == CONSUMED_ALL ) { - g->u.i_rem( inv_pos ); + g->u.i_rem( &given ); } if( consume_res != REFUSED ) { g->u.moves -= 100; @@ -3288,7 +3290,7 @@ std::string give_item_to( npc &p, bool allow_use, bool allow_carry ) } if( taken ) { - g->u.i_rem( inv_pos ); + g->u.i_rem( &given ); g->u.moves -= 100; p.has_new_items = true; return _( "Thanks!" );