diff --git a/data/json/items/basecamp.json b/data/json/items/basecamp.json index d1fe1f06c19e6..1412d4901b3e5 100644 --- a/data/json/items/basecamp.json +++ b/data/json/items/basecamp.json @@ -3,11 +3,10 @@ "id": "fake_char_kiln", "//": "The stationary object needs a larger capacity than the portable base object", "type": "TOOL", - "copy-from": "fake_item", + "copy-from": "fake_crafting_tool", "name": { "str": "basecamp kiln" }, "description": "A fake kiln used for basecamps.", "sub": "char_kiln", - "extend": { "flags": [ "ALLOWS_REMOTE_USE" ] }, "ammo": [ "charcoal" ], "//1": "250 liters of materials can produce 100 liters of charcoal.", "pocket_data": [ { "pocket_type": "MAGAZINE", "rigid": true, "ammo_restriction": { "charcoal": 20000 } } ] @@ -16,11 +15,10 @@ "id": "fake_char_smoker", "//": "The stationary object needs a larger capacity than the portable base object", "type": "TOOL", - "copy-from": "fake_item", + "copy-from": "fake_crafting_tool", "name": { "str": "basecamp charcoal smoker" }, "description": "A fake charcoal smoker used for basecamps.", "sub": "char_smoker", - "extend": { "flags": [ "ALLOWS_REMOTE_USE" ] }, "ammo": [ "charcoal" ], "//1": "holds 450 liters of charcoal. a full smoking rack cycle of 20L of stuff would consume 15,000 charcoal (75 liters, 15.6kg of charcoal)", "pocket_data": [ { "pocket_type": "MAGAZINE", "rigid": true, "ammo_restriction": { "charcoal": 90000 } } ] @@ -32,29 +30,27 @@ "name": { "str": "basecamp forge" }, "copy-from": "char_forge", "description": "A fake charcoal forge used for basecamps.", - "extend": { "flags": [ "ALLOWS_REMOTE_USE", "ZERO_WEIGHT" ] }, + "extend": { "flags": [ "ALLOWS_REMOTE_USE", "ZERO_WEIGHT", "PSEUDO" ] }, "pocket_data": [ { "pocket_type": "MAGAZINE", "rigid": true, "ammo_restriction": { "charcoal": 2000, "coal": 10000 } } ] }, { "id": "fake_clay_kiln", "//": "The stationary object needs a larger capacity than the portable base object", "type": "TOOL", - "copy-from": "fake_item", + "copy-from": "fake_crafting_tool", "name": { "str": "basecamp clay kiln" }, "description": "A fake clay kiln used for basecamps.", "sub": "brick_kiln", - "extend": { "flags": [ "ALLOWS_REMOTE_USE" ] }, "ammo": [ "charcoal", "coal" ], "pocket_data": [ { "pocket_type": "MAGAZINE", "ammo_restriction": { "charcoal": 2000, "coal": 10000 } } ] }, { "id": "fake_fireplace", "type": "TOOL", - "copy-from": "fake_item", + "copy-from": "fake_crafting_tool", "name": { "str": "basecamp fireplace" }, "description": "A fake fireplace used for basecamps.", "sub": "hotplate", - "extend": { "flags": [ "ALLOWS_REMOTE_USE" ] }, "ammo": [ "tinder" ], "pocket_data": [ { "pocket_type": "MAGAZINE", "rigid": true, "ammo_restriction": { "tinder": 50000 } } ], "charge_factor": 25 @@ -62,11 +58,10 @@ { "id": "fake_woodstove", "type": "TOOL", - "copy-from": "fake_item", + "copy-from": "fake_crafting_tool", "name": { "str": "basecamp stove" }, "description": "A fake stove used for basecamps.", "sub": "hotplate", - "extend": { "flags": [ "ALLOWS_REMOTE_USE" ] }, "ammo": [ "tinder" ], "pocket_data": [ { "pocket_type": "MAGAZINE", "rigid": true, "ammo_restriction": { "tinder": 50000 } } ], "charge_factor": 10 diff --git a/src/game_inventory.cpp b/src/game_inventory.cpp index 9d17affe6c7fa..84a99eaa9b10b 100644 --- a/src/game_inventory.cpp +++ b/src/game_inventory.cpp @@ -2773,8 +2773,11 @@ class select_ammo_inventory_preset : public inventory_selector_preset { public: select_ammo_inventory_preset( Character &you, const item_location &target, - bool empty ) : you( you ), - target( target ), empty( empty ) { + bool empty, + item_location_filter filter = []( const item_location & ) { + return true; + } ) : you( you ), + target( target ), empty( empty ), filter( std::move( filter ) ) { _indent_entries = false; _collate_entries = true; @@ -2836,6 +2839,10 @@ class select_ammo_inventory_preset : public inventory_selector_preset } bool is_shown( const item_location &loc ) const override { + if( !filter( loc ) ) { + return false; + } + // todo: allow to reload a magazine/magazine well from a container pocket on the same item if( loc.parent_item() == target ) { return false; @@ -2896,12 +2903,14 @@ class select_ammo_inventory_preset : public inventory_selector_preset Character &you; const item_location target; bool empty; + item_location_filter filter; }; item::reload_option game_menus::inv::select_ammo( Character &you, const item_location &loc, - bool prompt, bool empty ) + bool prompt, + bool empty, item_location_filter filter ) { - const select_ammo_inventory_preset preset( you, loc, empty ); + const select_ammo_inventory_preset preset( you, loc, empty, std::move( filter ) ); ammo_inventory_selector inv_s( you, loc, preset ); inv_s.set_title( string_format( loc->is_watertight_container() ? _( "Refill %s" ) : diff --git a/src/game_inventory.h b/src/game_inventory.h index 7fedebd2e3fbb..2fbb315797ba7 100644 --- a/src/game_inventory.h +++ b/src/game_inventory.h @@ -157,7 +157,11 @@ item_location change_sprite( Character &you ); /** Unload item menu **/ std::pair unload( Character &you ); item::reload_option select_ammo( Character &you, const item_location &loc, bool prompt = false, - bool empty = true ); + bool empty = true, + item_location_filter filter = []( const item_location & ) +{ + return true; +} ); /*@}*/ } // namespace inv diff --git a/src/iexamine.cpp b/src/iexamine.cpp index fed39ce2b5f08..3943d2ed02d80 100644 --- a/src/iexamine.cpp +++ b/src/iexamine.cpp @@ -46,6 +46,7 @@ #include "harvest.h" #include "input_context.h" #include "inventory.h" +#include "inventory_ui.h" #include "item.h" #include "item_location.h" #include "item_stack.h" @@ -4329,15 +4330,6 @@ const itype *furn_t::crafting_pseudo_item_type() const return item::find_type( crafting_pseudo_item ); } -const itype *furn_t::crafting_ammo_item_type() const -{ - const itype *pseudo = crafting_pseudo_item_type(); - if( pseudo && pseudo->tool && !pseudo->tool->ammo_id.empty() ) { - return item::find_type( ammotype( *pseudo->tool->ammo_id.begin() )->default_ammotype() ); - } - return nullptr; -} - /** * Finds the number of charges of the first item that matches type. * @@ -4356,90 +4348,85 @@ static int count_charges_in_list( const itype *type, const map_stack &items ) return 0; } -/** -* Finds the number of charges of the first item that matches ammotype. -* -* @param ammotype Search target. -* @param items Stack of items. Search stops at first match. -* @param [out] item_type Matching type. -* -* @return Number of charges. -* */ -static int count_charges_in_list( const ammotype *ammotype, const map_stack &items, - itype_id &item_type ) -{ - for( const item &candidate : items ) { - if( candidate.is_ammo() && candidate.type->ammo->type == *ammotype ) { - item_type = candidate.typeId(); - return candidate.charges; - } - } - return 0; -} - static void reload_furniture( Character &you, const tripoint &examp, bool allow_unload ) { map &here = get_map(); const furn_t &f = here.furn( examp ).obj(); - const itype *pseudo_type = f.crafting_pseudo_item_type(); - const itype *ammo = f.crafting_ammo_item_type(); - bool use_ammotype = f.has_flag( ter_furn_flag::TFLAG_AMMOTYPE_RELOAD ); - if( pseudo_type == nullptr || ammo == nullptr || !ammo->ammo ) { - add_msg( m_info, _( "This %s can not be reloaded!" ), f.name() ); - return; - } - itype_id ammo_itypeID( ammo->get_id() ); - int amount_in_furn = use_ammotype ? - count_charges_in_list( &ammo->ammo->type, here.i_at( examp ), ammo_itypeID ) : - count_charges_in_list( ammo, here.i_at( examp ) ); - if( allow_unload && amount_in_furn > 0 ) { + map_stack items = here.i_at( examp ); + if( allow_unload && !items.empty() ) { + // Hopefully the furniture only ever has one itype in it at a time, but + // if something goes wrong, let's let the player remove whatever's in + // there. + const item &first_item = *items.begin(); + int amount_in_furn = count_charges_in_list( first_item.type, items ); if( you.query_yn( _( "The %1$s contains %2$d %3$s. Unload?" ), f.name(), amount_in_furn, - ammo_itypeID->nname( amount_in_furn ) ) ) { - map_stack items = here.i_at( examp ); - for( map_stack::iterator itm = items.begin(); itm != items.end(); ) { - if( itm->typeId() == ammo_itypeID ) { - if( you.can_stash( *itm ) ) { - std::vector target_items{ item_location( map_cursor( examp ), &*itm ) }; - you.assign_activity( pickup_activity_actor( target_items, { 0 }, you.pos(), false ) ); - return; - } else { - // get handling cost before the item reference is invalidated - const int handling_cost = -you.item_handling_cost( *itm ); - - add_msg( _( "You remove %1$s from the %2$s." ), itm->tname(), f.name() ); - here.add_item_or_charges( you.pos(), *itm ); - itm = items.erase( itm ); - you.mod_moves( handling_cost ); - return; - } + first_item.type->nname( amount_in_furn ) ) ) { + for( map_stack::iterator itm = items.begin(); itm != items.end(); itm++ ) { + if( itm->type != first_item.type ) { + continue; + } + if( you.can_stash( *itm ) ) { + std::vector target_items{ item_location( map_cursor( examp ), &*itm ) }; + you.assign_activity( pickup_activity_actor( target_items, { 0 }, you.pos(), false ) ); + return; } else { - itm++; + // get handling cost before the item reference is invalidated + const int handling_cost = -you.item_handling_cost( *itm ); + + add_msg( _( "You remove %1$s from the %2$s." ), itm->tname(), f.name() ); + here.add_item_or_charges( you.pos(), *itm ); + itm = items.erase( itm ); + you.mod_moves( handling_cost ); + return; } } + debugmsg( "Tried to unload %s, but there was nothing to unload.", f.name() ); } } - const int max_amount_in_furn = item( pseudo_type ).ammo_capacity( ammo->ammo->type ); - const int max_reload_amount = max_amount_in_furn - amount_in_furn; - if( max_reload_amount <= 0 ) { + const itype *pseudo_type = f.crafting_pseudo_item_type(); + + if( !pseudo_type || !pseudo_type->tool || pseudo_type->tool->ammo_id.empty() ) { + add_msg( m_info, _( "This %s can not be reloaded!" ), f.name() ); return; } + const std::set &allowed_ammotypes = pseudo_type->tool->ammo_id; + item pseudo( pseudo_type ); // maybe at some point we need a pseudo item_location or something // but for now this should at least work as intended item_location pseudo_loc( map_cursor( examp ), &pseudo ); - // used to only allow one type of ammo, changed with move to inventory_selector - // todo: use furniture name instead of pseudo item name - item::reload_option opt = game_menus::inv::select_ammo( you, pseudo_loc ); + // If the furniture already has ammo in it, we'll only reload that exact itype. + const itype *ammo_to_load = nullptr; + for( const item &i : items ) { + if( i.type->ammo ) { + if( allowed_ammotypes.find( i.type->ammo->type ) != allowed_ammotypes.end() ) { + ammo_to_load = i.type; + break; + } + } + } + // todo: use furniture name instead of pseudo item name + item::reload_option opt = game_menus::inv::select_ammo( you, + pseudo_loc, false, true, [&]( const item_location & loc ) { + return !ammo_to_load || loc.get_item()->type->get_id() == ammo_to_load->get_id(); + } ); if( !opt ) { return; } - const itype *opt_type = opt.ammo->type; + ammo_to_load = opt.ammo->type; + const int amount_in_furn = count_charges_in_list( ammo_to_load, items ); + const int max_amount_in_furn = item( pseudo_type ).ammo_capacity( ammo_to_load->ammo->type ); + const int max_reload_amount = max_amount_in_furn - amount_in_furn; + if( max_reload_amount <= 0 ) { + return; + } + const int max_amount = std::min( opt.qty(), max_reload_amount ); const std::string popupmsg = string_format( _( "Put how many of the %1$s into the %2$s?" ), - opt_type->nname( max_amount ), f.name() ); + ammo_to_load->nname( max_amount ), f.name() ); int amount = string_input_popup() .title( popupmsg ) .width( 20 ) @@ -4460,20 +4447,20 @@ static void reload_furniture( Character &you, const tripoint &examp, bool allow_ here.add_item( examp, it ); }; - item moved( opt_type, opt.ammo.get_item()->birthday(), amount ); + item moved( ammo_to_load, opt.ammo.get_item()->birthday(), amount ); place_item( moved ); you.mod_moves( -you.item_handling_cost( moved ) ); std::listused; - if( opt.ammo.get_item()->use_charges( opt_type->get_id(), amount, used, + if( opt.ammo.get_item()->use_charges( ammo_to_load->get_id(), amount, used, opt.ammo.position() ) ) { opt.ammo.remove_item(); } - const int amount_in_furn_after_placing = count_charges_in_list( opt_type, + const int amount_in_furn_after_placing = count_charges_in_list( ammo_to_load, here.i_at( examp ) ); //~ %1$s - furniture, %2$d - number, %3$s items. add_msg( _( "The %1$s contains %2$d %3$s." ), f.name(), amount_in_furn_after_placing, - opt_type->nname( amount_in_furn_after_placing ) ); + ammo_to_load->nname( amount_in_furn_after_placing ) ); add_msg( _( "You reload the %s." ), here.furnname( examp ) ); @@ -6600,7 +6587,6 @@ void iexamine::smoker_options( Character &you, const tripoint &examp ) const furn_t &f = here.furn( examp ).obj(); const itype *type = f.crafting_pseudo_item_type(); - const itype *ammo = f.crafting_ammo_item_type(); const bool empty = f_volume == 0_ml; const bool full = f_volume >= sm_rack::MAX_FOOD_VOLUME; const bool full_portable = f_volume >= sm_rack::MAX_FOOD_VOLUME_PORTABLE; @@ -6609,8 +6595,10 @@ void iexamine::smoker_options( Character &you, const tripoint &examp ) const bool has_coal_in_inventory = you.crafting_inventory().charges_of( itype_charcoal ) > 0; const int coal_charges = count_charges_in_list( item::find_type( itype_charcoal ), items_here ); const int need_charges = get_charcoal_charges( f_volume ); - const int max_charges = type == nullptr || ammo == nullptr || - !ammo->ammo ? 0 : item( type ).ammo_capacity( ammo->ammo->type ); + + const ammotype ammo = type && type->tool && + !type->tool->ammo_id.empty() ? *type->tool->ammo_id.begin() : ammotype::NULL_ID(); + const int max_charges = ammo ? item( type ).ammo_capacity( ammo ) : 0; const bool has_coal = coal_charges > 0; const bool has_enough_coal = coal_charges >= need_charges; diff --git a/src/inventory.cpp b/src/inventory.cpp index f13ca9dfab2c2..dc12a9cbac961 100644 --- a/src/inventory.cpp +++ b/src/inventory.cpp @@ -449,37 +449,6 @@ void inventory::restack( Character &p ) #endif } -static int count_charges_in_list( const itype *type, const map_stack &items ) -{ - for( const item &candidate : items ) { - if( candidate.type == type ) { - return candidate.charges; - } - } - return 0; -} - -/** -* Finds the number of charges of the first item that matches ammotype. -* -* @param ammotype Search target. -* @param items Stack of items. Search stops at first match. -* @param [out] item_type Matching type. -* -* @return Number of charges. -* */ -static int count_charges_in_list( const ammotype *ammotype, const map_stack &items, - itype_id &item_type ) -{ - for( const item &candidate : items ) { - if( candidate.is_ammo() && candidate.type->ammo->type == *ammotype ) { - item_type = candidate.typeId(); - return candidate.charges; - } - } - return 0; -} - void inventory::form_from_map( const tripoint &origin, int range, const Character *pl, bool assign_invlet, bool clear_path ) @@ -535,20 +504,12 @@ void inventory::form_from_map( map &m, std::vector pts, const Characte } const furn_t &f = m.furn( p ).obj(); if( item *furn_item = provide_pseudo_item( f.crafting_pseudo_item ) ) { - const itype *ammo = f.crafting_ammo_item_type(); if( furn_item->has_pocket_type( pocket_type::MAGAZINE ) ) { - // NOTE: This only works if the pseudo item has a MAGAZINE pocket, not a MAGAZINE_WELL! - const bool using_ammotype = f.has_flag( ter_furn_flag::TFLAG_AMMOTYPE_RELOAD ); - int amount = 0; - itype_id ammo_id = ammo->get_id(); - // Some furniture can consume more than one item type. - if( using_ammotype ) { - amount = count_charges_in_list( &ammo->ammo->type, m.i_at( p ), ammo_id ); - } else { - amount = count_charges_in_list( ammo, m.i_at( p ) ); + // Whatever's in the furniture goes in the pseudo. Assuming only + // valid types of ammo have been inserted, this should be safe. + for( const item &i : m.i_at( p ) ) { + furn_item->put_in( i, pocket_type::MAGAZINE ); } - item furn_ammo( ammo_id, calendar::turn, amount ); - furn_item->put_in( furn_ammo, pocket_type::MAGAZINE ); } } if( m.accessible_items( p ) ) { diff --git a/src/item.cpp b/src/item.cpp index 304fd43d19277..74e7d8ab2606d 100644 --- a/src/item.cpp +++ b/src/item.cpp @@ -11497,7 +11497,7 @@ void item::reload_option::qty( int val ) // This gets rounded up to 1 later. int remaining_capacity = target->is_watertight_container() ? target->get_remaining_capacity_for_liquid( ammo_obj, true ) : - target->remaining_ammo_capacity(); + target->ammo_capacity( ammo->ammo_type() ) - target->ammo_remaining(); if( target->has_flag( flag_RELOAD_ONE ) && !( ammo->has_flag( flag_SPEEDLOADER ) || ammo->has_flag( flag_SPEEDLOADER_CLIP ) ) ) { remaining_capacity = 1; diff --git a/src/mapdata.h b/src/mapdata.h index e2140b89de39b..fc41263c9d042 100644 --- a/src/mapdata.h +++ b/src/mapdata.h @@ -669,8 +669,6 @@ struct furn_t : map_data_common_t { // May return NULL const itype *crafting_pseudo_item_type() const; - // May return NULL - const itype *crafting_ammo_item_type() const; furn_t();