From dcc799732943ecc966c7da8ac968eed35149b7e2 Mon Sep 17 00:00:00 2001 From: Aivean Date: Wed, 23 Dec 2020 09:26:48 -0800 Subject: [PATCH 1/4] extract read-only part of visitable interface make visitable a real interface --- src/ballistics.cpp | 2 +- src/character.cpp | 15 +- src/character.h | 16 +- src/consumption.cpp | 2 +- src/iexamine.cpp | 2 +- src/inventory.cpp | 2 +- src/inventory.h | 16 +- src/inventory_ui.cpp | 2 +- src/item.cpp | 10 +- src/item.h | 10 +- src/item_location.cpp | 7 +- src/iuse_actor.cpp | 2 +- src/map.h | 4 +- src/map_selector.h | 18 +- src/npc.cpp | 4 +- src/npcmove.cpp | 4 +- src/npctrade.cpp | 2 +- src/player.cpp | 2 +- src/profession.cpp | 2 +- src/requirements.cpp | 2 +- src/vehicle.h | 4 +- src/vehicle_selector.h | 22 +- src/visitable.cpp | 365 +++++++++++--------------------- src/visitable.h | 72 ++++--- tests/invlet_test.cpp | 2 +- tests/item_location_test.cpp | 2 +- tests/new_character_test.cpp | 2 +- tests/reload_magazine_test.cpp | 14 +- tests/visitable_remove_test.cpp | 4 +- 29 files changed, 282 insertions(+), 329 deletions(-) diff --git a/src/ballistics.cpp b/src/ballistics.cpp index b8535789ddedc..40625a5e83224 100644 --- a/src/ballistics.cpp +++ b/src/ballistics.cpp @@ -53,7 +53,7 @@ static void drop_or_embed_projectile( const dealt_projectile_attack &attack ) if( effects.count( "SHATTER_SELF" ) ) { // Drop the contents, not the thrown item add_msg_if_player_sees( pt, _( "The %s shatters!" ), drop_item.tname() ); - drop_item.visit_items( [&pt]( const item * it ) { + drop_item.visit_items( [&pt]( const item * it, item * ) { get_map().add_item_or_charges( pt, *it ); return VisitResponse::NEXT; } ); diff --git a/src/character.cpp b/src/character.cpp index 8000787779469..18b43dea366cf 100644 --- a/src/character.cpp +++ b/src/character.cpp @@ -422,7 +422,6 @@ std::string enum_to_string( blood_type data ) // *INDENT-OFF* Character::Character() : - visitable(), cached_time( calendar::before_time_starts ), id( -1 ), next_climate_control_check( calendar::before_time_starts ), @@ -2903,7 +2902,7 @@ item *Character::invlet_to_item( const int linvlet ) name; } item *invlet_item = nullptr; - visit_items( [&invlet, &invlet_item]( item * it ) { + visit_items( [&invlet, &invlet_item]( item * it, item * ) { if( it->invlet == invlet ) { invlet_item = it; return VisitResponse::ABORT; @@ -3373,7 +3372,7 @@ std::vector Character::find_reloadables() { std::vector reloadables; - visit_items( [this, &reloadables]( item * node ) { + visit_items( [this, &reloadables]( item * node, item * ) { if( !node->is_gun() && !node->is_magazine() ) { return VisitResponse::NEXT; } @@ -9278,7 +9277,7 @@ void Character::recalculate_enchantment_cache() // start by resetting the cache to all inventory items *enchantment_cache = inv->get_active_enchantment_cache( *this ); - visit_items( [&]( const item * it ) { + visit_items( [&]( const item * it, item * ) { for( const enchantment &ench : it->get_enchantments() ) { if( ench.is_active( *this, *it ) ) { enchantment_cache->force_add( ench ); @@ -10810,7 +10809,7 @@ void Character::fall_asleep( const time_duration &duration ) void Character::migrate_items_to_storage( bool disintegrate ) { - inv->visit_items( [&]( const item * it ) { + inv->visit_items( [&]( const item * it, item * ) { if( disintegrate ) { if( try_add( *it ) == nullptr ) { debugmsg( "ERROR: Could not put %s into inventory. Check if the profession has enough space.", @@ -11182,7 +11181,7 @@ std::list Character::use_charges( const itype_id &what, int qty, const int bool has_tool_with_UPS = false; // Detection of UPS tool - inv.visit_items( [ &what, &qty, &has_tool_with_UPS, &filter]( item * e ) { + inv.visit_items( [ &what, &qty, &has_tool_with_UPS, &filter]( item * e, item * ) { if( filter( *e ) && e->typeId() == what && e->has_flag( flag_USE_UPS ) ) { has_tool_with_UPS = true; return VisitResponse::ABORT; @@ -11194,7 +11193,7 @@ std::list Character::use_charges( const itype_id &what, int qty, const int get_map().use_charges( pos(), radius, what, qty, return_true ); } if( qty > 0 ) { - visit_items( [this, &what, &qty, &res, &del, &filter]( item * e ) { + visit_items( [this, &what, &qty, &res, &del, &filter]( item * e, item * ) { if( e->use_charges( what, qty, res, pos(), filter ) ) { del.push_back( e ); } @@ -12844,7 +12843,7 @@ int Character::item_reload_cost( const item &it, const item &ammo, int qty ) con } else if( ammo.is_ammo_container() ) { int min_clamp = 0; // find the first ammo in the container to get its charges - ammo.visit_items( [&min_clamp]( const item * it ) { + ammo.visit_items( [&min_clamp]( const item * it, item * ) { if( it->is_ammo() ) { min_clamp = it->charges; return VisitResponse::ABORT; diff --git a/src/character.h b/src/character.h index 0d4b6d6cdbd8e..7296cd1d09ce8 100644 --- a/src/character.h +++ b/src/character.h @@ -363,7 +363,7 @@ enum class book_mastery { MASTERED // can no longer increase skill by reading }; -class Character : public Creature, public visitable +class Character : public Creature, public visitable { public: Character( const Character & ) = delete; @@ -2677,6 +2677,20 @@ class Character : public Creature, public visitable /** Handles the still hard-coded effects. */ void hardcoded_effects( effect &it ); + // inherited from visitable + bool has_quality( const quality_id &qual, int level = 1, int qty = 1 ) const override; + int max_quality( const quality_id &qual ) const override; + VisitResponse visit_items( const std::function &func ) const + override; + std::list remove_items_with( const std::function &filter, + int count = INT_MAX ) override; + int charges_of( const itype_id &what, int limit = INT_MAX, + const std::function &filter = return_true, + const std::function &visitor = nullptr ) const override; + int amount_of( const itype_id &what, bool pseudo = true, + int limit = INT_MAX, + const std::function &filter = return_true ) const override; + protected: Character(); Character( Character && ); diff --git a/src/consumption.cpp b/src/consumption.cpp index 733fd1ec21de8..141f8c4c0ff7a 100644 --- a/src/consumption.cpp +++ b/src/consumption.cpp @@ -1507,7 +1507,7 @@ bool Character::can_consume( const item &it ) const item &Character::get_consumable_from( item &it ) const { item *ret = nullptr; - it.visit_items( [&]( item * it ) { + it.visit_items( [&]( item * it, item * ) { if( can_consume_as_is( *it ) ) { ret = it; return VisitResponse::ABORT; diff --git a/src/iexamine.cpp b/src/iexamine.cpp index 76f8343f3d17f..f17edec03714b 100644 --- a/src/iexamine.cpp +++ b/src/iexamine.cpp @@ -3602,7 +3602,7 @@ void iexamine::tree_maple_tapped( player &p, const tripoint &examp ) if( it.will_spill() || it.is_watertight_container() ) { container = ⁢ - it.visit_items( [&charges, &has_sap]( const item * it ) { + it.visit_items( [&charges, &has_sap]( const item * it, item * ) { if( it->typeId() == itype_maple_syrup ) { has_sap = true; charges = it->charges; diff --git a/src/inventory.cpp b/src/inventory.cpp index ffce4f7f5a67a..02dc8d6e3ac59 100644 --- a/src/inventory.cpp +++ b/src/inventory.cpp @@ -1100,7 +1100,7 @@ const itype_bin &inventory::get_binned_items() const // HACK: Hack warning inventory *this_nonconst = const_cast( this ); - this_nonconst->visit_items( [ this ]( item * e ) { + this_nonconst->visit_items( [ this ]( item * e, item * ) { binned_items[ e->typeId() ].push_back( e ); for( const item *it : e->softwares() ) { binned_items[it->typeId()].push_back( it ); diff --git a/src/inventory.h b/src/inventory.h index 75a2751488bce..9354e4aa78682 100644 --- a/src/inventory.h +++ b/src/inventory.h @@ -87,10 +87,9 @@ class invlet_favorites std::array ids_by_invlet; }; -class inventory : public visitable +class inventory : public visitable { public: - friend visitable; invslice slice(); const_invslice const_slice() const; @@ -237,6 +236,19 @@ class inventory : public visitable int count_item( const itype_id &item_type ) const; + // inherited from `visitable` + bool has_quality( const quality_id &qual, int level = 1, int qty = 1 ) const override; + VisitResponse visit_items( const std::function &func ) const + override; + std::list remove_items_with( const std::function &filter, + int count = INT_MAX ) override; + int charges_of( const itype_id &what, int limit = INT_MAX, + const std::function &filter = return_true, + const std::function &visitor = nullptr ) const override; + int amount_of( const itype_id &what, bool pseudo = true, + int limit = INT_MAX, + const std::function &filter = return_true ) const override; + private: invlet_favorites invlet_cache; char find_usable_cached_invlet( const itype_id &item_type ); diff --git a/src/inventory_ui.cpp b/src/inventory_ui.cpp index 490aa0d71c181..f2e0204bdda18 100644 --- a/src/inventory_ui.cpp +++ b/src/inventory_ui.cpp @@ -1313,7 +1313,7 @@ void inventory_selector::add_contained_items( item_location &container, inventor void inventory_selector::add_character_items( Character &character ) { - character.visit_items( [ this, &character ]( item * it ) { + character.visit_items( [ this, &character ]( item * it, item * ) { if( it == &character.weapon ) { add_item( own_gear_column, item_location( character, it ), &item_category_id( "WEAPON_HELD" ).obj() ); diff --git a/src/item.cpp b/src/item.cpp index bd840f055466b..1da4b123d469e 100644 --- a/src/item.cpp +++ b/src/item.cpp @@ -4931,7 +4931,7 @@ int item::price( bool practical ) const { int res = 0; - visit_items( [&res, practical]( const item * e ) { + visit_items( [&res, practical]( const item * e, item * ) { if( e->rotten() ) { // TODO: Special case things that stay useful when rotten return VisitResponse::NEXT; @@ -8017,7 +8017,7 @@ const use_function *item::get_use( const std::string &use_name ) const { const use_function *fun = nullptr; visit_items( - [&fun, &use_name]( const item * it ) { + [&fun, &use_name]( const item * it, auto ) { if( it == nullptr ) { return VisitResponse::SKIP; } @@ -8043,7 +8043,7 @@ item *item::get_usable_item( const std::string &use_name ) { item *ret = nullptr; visit_items( - [&ret, &use_name]( item * it ) { + [&ret, &use_name]( item * it, auto ) { if( it == nullptr ) { return VisitResponse::SKIP; } @@ -8592,7 +8592,7 @@ bool item::allow_crafting_component() const // fixes #18886 - turret installation may require items with irremovable mods if( is_gun() ) { bool valid = true; - visit_items( [&]( const item * it ) { + visit_items( [&]( const item * it, item * ) { if( this == it ) { return VisitResponse::NEXT; } @@ -9126,7 +9126,7 @@ uint64_t item::make_component_hash() const bool item::needs_processing() const { bool need_process = false; - visit_items( [&need_process]( const item * it ) { + visit_items( [&need_process]( const item * it, item * ) { if( it->active || it->ethereal || it->has_flag( flag_RADIO_ACTIVATION ) || it->is_food() || it->has_relic_recharge() ) { need_process = true; diff --git a/src/item.h b/src/item.h index 585fd06199715..5f1f1d57979b8 100644 --- a/src/item.h +++ b/src/item.h @@ -173,7 +173,7 @@ iteminfo weight_to_info( const std::string &type, const std::string &left, inline bool is_crafting_component( const item &component ); -class item : public visitable +class item : public visitable { public: using FlagsSetType = std::set; @@ -208,7 +208,7 @@ class item : public visitable item( itype_id( itype ), std::forward( args )... ) {} - ~item(); + ~item() override; /** Return a pointer-like type that's automatically invalidated if this * item is destroyed or assigned-to */ @@ -2188,6 +2188,12 @@ class item : public visitable */ int get_recursive_disassemble_moves( const Character &guy ) const; + // inherited from visitable + VisitResponse visit_items( const std::function &func ) const + override; + std::list remove_items_with( const std::function &filter, + int count = INT_MAX ) override; + private: /** migrates an item into this item. */ void migrate_content_item( const item &contained ); diff --git a/src/item_location.cpp b/src/item_location.cpp index 915da74e34c19..b011492bdeecc 100644 --- a/src/item_location.cpp +++ b/src/item_location.cpp @@ -34,7 +34,7 @@ template static int find_index( const T &sel, const item *obj ) { int idx = -1; - sel.visit_items( [&idx, &obj]( const item * e ) { + sel.visit_items( [&idx, &obj]( const item * e, item * ) { idx++; if( e == obj ) { return VisitResponse::ABORT; @@ -48,7 +48,7 @@ template static item *retrieve_index( const T &sel, int idx ) { item *obj = nullptr; - sel.visit_items( [&idx, &obj]( const item * e ) { + sel.visit_items( [&idx, &obj]( const item * e, item * ) { if( idx-- == 0 ) { obj = const_cast( e ); return VisitResponse::ABORT; @@ -816,7 +816,8 @@ int item_location::max_charges_by_parent_recursive( const item &it ) const return std::min( { it.charges_per_volume( pocket->remaining_volume() ), it.charges_per_weight( pocket->remaining_weight() ), - pocket->rigid() ? item::INFINITE_CHARGES : parent.max_charges_by_parent_recursive( it ) } ); + pocket->rigid() ? item::INFINITE_CHARGES : parent.max_charges_by_parent_recursive( it ) + } ); } item_location::type item_location::where() const diff --git a/src/iuse_actor.cpp b/src/iuse_actor.cpp index abe2ef87ca60e..a6ef5f2a1ff72 100644 --- a/src/iuse_actor.cpp +++ b/src/iuse_actor.cpp @@ -2507,7 +2507,7 @@ static item_location get_item_location( player &p, item &it, const tripoint &pos if( const optional_vpart_position &vp = get_map().veh_at( pos ) ) { vehicle_cursor vc( vp->vehicle(), vp->part_index() ); bool found_in_vehicle = false; - vc.visit_items( [&]( const item * e ) { + vc.visit_items( [&]( const item * e, item * ) { if( e == &it ) { found_in_vehicle = true; return VisitResponse::ABORT; diff --git a/src/map.h b/src/map.h index 7d44355b84e79..452ec662467b9 100644 --- a/src/map.h +++ b/src/map.h @@ -81,7 +81,6 @@ struct MonsterGroupResult; struct mongroup; struct projectile; struct veh_collision; -template class visitable; struct wrapped_vehicle { @@ -183,7 +182,8 @@ struct bash_params { class map { friend class editmap; - friend class visitable; + friend std::list map_cursor::remove_items_with( const std::function &, + int ); public: // Constructors & Initialization diff --git a/src/map_selector.h b/src/map_selector.h index e9c0db11dff11..1030b3b60fe29 100644 --- a/src/map_selector.h +++ b/src/map_selector.h @@ -7,7 +7,7 @@ #include "point.h" #include "visitable.h" -class map_cursor : public visitable +class map_cursor : public visitable { private: tripoint pos_; @@ -15,12 +15,16 @@ class map_cursor : public visitable public: map_cursor( const tripoint &pos ); operator tripoint() const; + + // inherited from visitable + VisitResponse visit_items( const std::function &func ) const + override; + std::list remove_items_with( const std::function &filter, + int count = INT_MAX ) override; }; -class map_selector : public visitable +class map_selector : public visitable { - friend visitable; - public: using value_type = map_cursor; using size_type = std::vector::size_type; @@ -70,6 +74,12 @@ class map_selector : public visitable return data.back(); } + // inherited from visitable + VisitResponse visit_items( const std::function &func ) const + override; + std::list remove_items_with( const std::function &filter, + int count = INT_MAX ) override; + private: std::vector data; }; diff --git a/src/npc.cpp b/src/npc.cpp index 6241865899c86..b38490951db2d 100644 --- a/src/npc.cpp +++ b/src/npc.cpp @@ -1893,7 +1893,7 @@ healing_options npc::has_healing_options( healing_options try_to_fix ) can_fix.clear_all(); healing_options *fix_p = &can_fix; - visit_items( [&fix_p, try_to_fix]( item * node ) { + visit_items( [&fix_p, try_to_fix]( item * node, item * ) { const use_function *use = node->type->get_use( "heal" ); if( use == nullptr ) { return VisitResponse::NEXT; @@ -1932,7 +1932,7 @@ healing_options npc::has_healing_options( healing_options try_to_fix ) item &npc::get_healing_item( healing_options try_to_fix, bool first_best ) { item *best = &null_item_reference(); - visit_items( [&best, try_to_fix, first_best]( item * node ) { + visit_items( [&best, try_to_fix, first_best]( item * node, item * ) { const use_function *use = node->type->get_use( "heal" ); if( use == nullptr ) { return VisitResponse::NEXT; diff --git a/src/npcmove.cpp b/src/npcmove.cpp index 33bcf92ae3278..3437923c53e4b 100644 --- a/src/npcmove.cpp +++ b/src/npcmove.cpp @@ -1533,7 +1533,7 @@ item &npc::find_reloadable() // TODO: Cache items checked for reloading to avoid re-checking same items every turn // TODO: Make it understand smaller and bigger magazines item *reloadable = nullptr; - visit_items( [this, &reloadable]( item * node ) { + visit_items( [this, &reloadable]( item * node, item * ) { if( !wants_to_reload( *this, *node ) ) { return VisitResponse::NEXT; } @@ -3384,7 +3384,7 @@ bool npc::wield_better_weapon() // Fists aren't checked below compare_weapon( null_item_reference() ); - visit_items( [&compare_weapon]( item * node ) { + visit_items( [&compare_weapon]( item * node, item * ) { // Only compare melee weapons, guns, or holstered items if( node->is_melee() || node->is_gun() ) { compare_weapon( *node ); diff --git a/src/npctrade.cpp b/src/npctrade.cpp index cb67f6f95f705..5f5fd9228838f 100644 --- a/src/npctrade.cpp +++ b/src/npctrade.cpp @@ -136,7 +136,7 @@ double npc_trading::net_price_adjustment( const player &buyer, const player &sel template void buy_helper( T &src, Callback cb ) { - src.visit_items( [&src, &cb]( item * node ) { + src.visit_items( [&src, &cb]( item * node, item * ) { cb( std::move( item_location( src, node ) ), 1 ); return VisitResponse::SKIP; diff --git a/src/player.cpp b/src/player.cpp index b40e56dd7e120..64ae11ece85d3 100644 --- a/src/player.cpp +++ b/src/player.cpp @@ -262,7 +262,7 @@ void player::process_turn() mod_power_level( get_max_power_level() ); } - visit_items( [this]( item * e ) { + visit_items( [this]( item * e, item * ) { e->process_relic( this, pos() ); return VisitResponse::NEXT; } ); diff --git a/src/profession.cpp b/src/profession.cpp index 7e6d7590af670..3160283b5fa58 100644 --- a/src/profession.cpp +++ b/src/profession.cpp @@ -422,7 +422,7 @@ std::list profession::items( bool male, const std::vector &trait } } for( item &it : result ) { - it.visit_items( []( item * it ) { + it.visit_items( []( item * it, item * ) { clear_faults( *it ); return VisitResponse::NEXT; } ); diff --git a/src/requirements.cpp b/src/requirements.cpp index 584bdd7fd1289..cbb50202c7bf5 100644 --- a/src/requirements.cpp +++ b/src/requirements.cpp @@ -1160,7 +1160,7 @@ requirement_data requirement_data::continue_requirements( const std::vector del; - craft_components.visit_items( [&comp, &qty, &del]( item * e ) { + craft_components.visit_items( [&comp, &qty, &del]( item * e, item * ) { std::list used; if( e->use_charges( comp.type, qty, used, tripoint_zero ) ) { del.push_back( e ); diff --git a/src/vehicle.h b/src/vehicle.h index c59b58465c6f9..6be91d7c7da2a 100644 --- a/src/vehicle.h +++ b/src/vehicle.h @@ -55,7 +55,7 @@ class zone_data; struct itype; struct uilist_entry; template struct enum_traits; -template class visitable; +class visitable; enum vpart_bitflags : int; enum ter_bitflags : int; @@ -206,7 +206,7 @@ static constexpr float accel_g = 9.81f; struct vehicle_part { friend vehicle; friend class veh_interact; - friend visitable; + friend class vehicle_cursor; friend item_location; friend class turret_data; diff --git a/src/vehicle_selector.h b/src/vehicle_selector.h index da6b81dabbf6b..13032d16e8705 100644 --- a/src/vehicle_selector.h +++ b/src/vehicle_selector.h @@ -10,18 +10,24 @@ class vehicle; struct tripoint; -class vehicle_cursor : public visitable +class vehicle_cursor : public visitable { public: vehicle_cursor( vehicle &veh, std::ptrdiff_t part ) : veh( veh ), part( part ) {} vehicle &veh; std::ptrdiff_t part; + + // inherited from visitable + bool has_quality( const quality_id &qual, int level = 1, int qty = 1 ) const override; + int max_quality( const quality_id &qual ) const override; + VisitResponse visit_items( const std::function &func ) const + override; + std::list remove_items_with( const std::function &filter, + int count = INT_MAX ) override; }; -class vehicle_selector : public visitable +class vehicle_selector : public visitable { - friend visitable; - public: using value_type = vehicle_cursor; using size_type = std::vector::size_type; @@ -88,6 +94,14 @@ class vehicle_selector : public visitable return data.back(); } + //inherited from visitable + bool has_quality( const quality_id &qual, int level = 1, int qty = 1 ) const override; + int max_quality( const quality_id &qual ) const override; + VisitResponse visit_items( const std::function &func ) const + override; + std::list remove_items_with( const std::function &filter, + int count = INT_MAX ) override; + private: std::vector data; }; diff --git a/src/visitable.cpp b/src/visitable.cpp index 685d8ae28cdbe..5b9d72ddea16a 100644 --- a/src/visitable.cpp +++ b/src/visitable.cpp @@ -47,8 +47,7 @@ static const bionic_id bio_tools( "bio_tools" ); static const bionic_id bio_ups( "bio_ups" ); /** @relates visitable */ -template -item *visitable::find_parent( const item &it ) +item *read_only_visitable::find_parent( const item &it ) const { item *res = nullptr; if( visit_items( [&]( item * node, item * parent ) { @@ -64,15 +63,7 @@ item *visitable::find_parent( const item &it ) } /** @relates visitable */ -template -const item *visitable::find_parent( const item &it ) const -{ - return const_cast *>( this )->find_parent( it ); -} - -/** @relates visitable */ -template -std::vector visitable::parents( const item &it ) +std::vector read_only_visitable::parents( const item &it ) const { std::vector res; for( item *obj = find_parent( it ); obj; obj = find_parent( *obj ) ) { @@ -82,30 +73,17 @@ std::vector visitable::parents( const item &it ) } /** @relates visitable */ -template -std::vector visitable::parents( const item &it ) const +bool read_only_visitable::has_item( const item &it ) const { - std::vector res; - for( const item *obj = find_parent( it ); obj; obj = find_parent( *obj ) ) { - res.push_back( obj ); - } - return res; -} - -/** @relates visitable */ -template -bool visitable::has_item( const item &it ) const -{ - return visit_items( [&it]( const item * node ) { + return visit_items( [&it]( const item * node, item * ) { return node == &it ? VisitResponse::ABORT : VisitResponse::NEXT; } ) == VisitResponse::ABORT; } /** @relates visitable */ -template -bool visitable::has_item_with( const std::function &filter ) const +bool read_only_visitable::has_item_with( const std::function &filter ) const { - return visit_items( [&filter]( const item * node ) { + return visit_items( [&filter]( const item * node, item * ) { return filter( *node ) ? VisitResponse::ABORT : VisitResponse::NEXT; } ) == VisitResponse::ABORT; } @@ -132,7 +110,7 @@ static int has_quality_internal( const T &self, const quality_id &qual, int leve { int qty = 0; - self.visit_items( [&qual, level, &limit, &qty]( const item * e ) { + self.visit_items( [&qual, level, &limit, &qty]( item * e, item * ) { if( e->get_quality( qual ) >= level ) { qty = sum_no_wrap( qty, static_cast( e->count() ) ); if( qty >= limit ) { @@ -167,18 +145,16 @@ static int has_quality_from_vpart( const vehicle &veh, int part, const quality_i return std::min( qty, limit ); } -template -bool visitable::has_quality( const quality_id &qual, int level, int qty ) const +bool read_only_visitable::has_quality( const quality_id &qual, int level, int qty ) const { return has_quality_internal( *this, qual, level, qty ) == qty; } /** @relates visitable */ -template <> -bool visitable::has_quality( const quality_id &qual, int level, int qty ) const +bool inventory::has_quality( const quality_id &qual, int level, int qty ) const { int res = 0; - for( const auto &stack : static_cast( this )->items ) { + for( const auto &stack : this->items ) { res += stack.size() * has_quality_internal( stack.front(), qual, level, qty ); if( res >= qty ) { return true; @@ -188,10 +164,9 @@ bool visitable::has_quality( const quality_id &qual, int level, int q } /** @relates visitable */ -template <> -bool visitable::has_quality( const quality_id &qual, int level, int qty ) const +bool vehicle_selector::has_quality( const quality_id &qual, int level, int qty ) const { - for( const auto &cursor : static_cast( *this ) ) { + for( const auto &cursor : *this ) { qty -= has_quality_from_vpart( cursor.veh, cursor.part, qual, level, qty ); if( qty <= 0 ) { return true; @@ -201,22 +176,16 @@ bool visitable::has_quality( const quality_id &qual, int level } /** @relates visitable */ -template <> -bool visitable::has_quality( const quality_id &qual, int level, int qty ) const +bool vehicle_cursor::has_quality( const quality_id &qual, int level, int qty ) const { - const vehicle_cursor *self = static_cast( this ); - - qty -= has_quality_from_vpart( self->veh, self->part, qual, level, qty ); + qty -= has_quality_from_vpart( veh, part, qual, level, qty ); return qty <= 0 ? true : has_quality_internal( *this, qual, level, qty ) == qty; } /** @relates visitable */ -template <> -bool visitable::has_quality( const quality_id &qual, int level, int qty ) const +bool Character::has_quality( const quality_id &qual, int level, int qty ) const { - const Character *self = static_cast( this ); - - for( const auto &bio : *self->my_bionics ) { + for( const auto &bio : *this->my_bionics ) { if( bio.get_quality( qual ) >= level ) { if( qty <= 1 ) { return true; @@ -233,7 +202,7 @@ template static int max_quality_internal( const T &self, const quality_id &qual ) { int res = INT_MIN; - self.visit_items( [&res, &qual]( const item * e ) { + self.visit_items( [&res, &qual]( item * e, item * ) { res = std::max( res, e->get_quality( qual ) ); return VisitResponse::NEXT; } ); @@ -261,26 +230,22 @@ static int max_quality_from_vpart( const vehicle &veh, int part, const quality_i return res; } -template -int visitable::max_quality( const quality_id &qual ) const +int read_only_visitable::max_quality( const quality_id &qual ) const { return max_quality_internal( *this, qual ); } /** @relates visitable */ -template<> -int visitable::max_quality( const quality_id &qual ) const +int Character::max_quality( const quality_id &qual ) const { int res = INT_MIN; - const Character *self = static_cast( this ); - - for( const bionic &bio : *self->my_bionics ) { + for( const bionic &bio : *my_bionics ) { res = std::max( res, bio.get_quality( qual ) ); } if( qual == qual_BUTCHER ) { - for( const trait_id &mut : self->get_mutations() ) { + for( const trait_id &mut : get_mutations() ) { res = std::max( res, mut->butchering_quality ); } } @@ -289,48 +254,31 @@ int visitable::max_quality( const quality_id &qual ) const } /** @relates visitable */ -template <> -int visitable::max_quality( const quality_id &qual ) const +int vehicle_cursor::max_quality( const quality_id &qual ) const { - const vehicle_cursor *self = static_cast( this ); - return std::max( max_quality_from_vpart( self->veh, self->part, qual ), + return std::max( max_quality_from_vpart( veh, part, qual ), max_quality_internal( *this, qual ) ); } /** @relates visitable */ -template <> -int visitable::max_quality( const quality_id &qual ) const +int vehicle_selector::max_quality( const quality_id &qual ) const { int res = INT_MIN; - for( const auto &e : static_cast( *this ) ) { + for( const auto &e : *this ) { res = std::max( res, e.max_quality( qual ) ); } return res; } -/** @relates visitable */ -template -std::vector visitable::items_with( const std::function &filter ) -{ - std::vector res; - visit_items( [&res, &filter]( item * node, item * ) { - if( filter( *node ) ) { - res.push_back( node ); - } - return VisitResponse::NEXT; - } ); - return res; -} -/** @relates visitable */ -template -std::vector -visitable::items_with( const std::function &filter ) const +template +static inline std::vector items_with_internal( V &self, const std::function + &filter ) { - std::vector res; - visit_items( [&res, &filter]( const item * node, const item * ) { + std::vector res; + self.visit_items( [&res, &filter]( const item * node, item * ) { if( filter( *node ) ) { - res.push_back( node ); + res.push_back( const_cast( node ) ); } return VisitResponse::NEXT; } ); @@ -338,43 +286,30 @@ visitable::items_with( const std::function &filter ) co } /** @relates visitable */ -template -VisitResponse -visitable::visit_items( const std::function &func ) const -{ - return const_cast *>( this )->visit_items( - static_cast&>( func ) ); -} - -/** @relates visitable */ -template VisitResponse -visitable::visit_items( const std::function &func ) const +std::vector read_only_visitable::items_with( + const std::function &filter ) const { - return const_cast *>( this )->visit_items( - static_cast&>( func ) ); + return items_with_internal( *this, filter ); } - /** @relates visitable */ -template -VisitResponse visitable::visit_items( const std::function &func ) +std::vector read_only_visitable::items_with( + const std::function &filter ) { - return visit_items( [&func]( item * it, item * ) { - return func( it ); - } ); + return items_with_internal( *this, filter ); } -// Specialize visitable::visit_items() for each class that will implement the visitable interface - static VisitResponse visit_internal( const std::function &func, - item *node, item *parent = nullptr ) + const item *node, item *parent = nullptr ) { - switch( func( node, parent ) ) { + // hack to avoid repetition + item *m_node = const_cast( node ); + + switch( func( m_node, parent ) ) { case VisitResponse::ABORT: return VisitResponse::ABORT; case VisitResponse::NEXT: - if( node->contents.visit_contents( func, node ) == VisitResponse::ABORT ) { + if( m_node->contents.visit_contents( func, m_node ) == VisitResponse::ABORT ) { return VisitResponse::ABORT; } /* intentional fallthrough */ @@ -420,22 +355,18 @@ VisitResponse item_pocket::visit_contents( const std::function -VisitResponse visitable::visit_items( - const std::function &func ) +VisitResponse item::visit_items( + const std::function &func ) const { - item *it = static_cast( this ); - return visit_internal( func, it ); + return visit_internal( func, this ); } /** @relates visitable */ -template <> -VisitResponse visitable::visit_items( - const std::function &func ) +VisitResponse inventory::visit_items( + const std::function &func ) const { - inventory *inv = static_cast( this ); - for( auto &stack : inv->items ) { - for( auto &it : stack ) { + for( const auto &stack : items ) { + for( const auto &it : stack ) { if( visit_internal( func, &it ) == VisitResponse::ABORT ) { return VisitResponse::ABORT; } @@ -445,39 +376,34 @@ VisitResponse visitable::visit_items( } /** @relates visitable */ -template <> -VisitResponse visitable::visit_items( - const std::function &func ) +VisitResponse Character::visit_items( const std::function &func ) +const { - Character *ch = static_cast( this ); - - if( !ch->weapon.is_null() && - visit_internal( func, &ch->weapon ) == VisitResponse::ABORT ) { + if( !weapon.is_null() && + visit_internal( func, &weapon ) == VisitResponse::ABORT ) { return VisitResponse::ABORT; } - for( auto &e : ch->worn ) { + for( const auto &e : worn ) { if( visit_internal( func, &e ) == VisitResponse::ABORT ) { return VisitResponse::ABORT; } } - return ch->inv->visit_items( func ); + return inv->visit_items( func ); } /** @relates visitable */ -template <> -VisitResponse visitable::visit_items( - const std::function &func ) +VisitResponse map_cursor::visit_items( + const std::function &func ) const { - const map_cursor *cur = static_cast( this ); map &here = get_map(); // skip inaccessible items - if( here.has_flag( "SEALED", *cur ) && !here.has_flag( "LIQUIDCONT", *cur ) ) { + if( here.has_flag( "SEALED", *this ) && !here.has_flag( "LIQUIDCONT", *this ) ) { return VisitResponse::NEXT; } - for( item &e : here.i_at( *cur ) ) { + for( item &e : here.i_at( *this ) ) { if( visit_internal( func, &e ) == VisitResponse::ABORT ) { return VisitResponse::ABORT; } @@ -486,11 +412,10 @@ VisitResponse visitable::visit_items( } /** @relates visitable */ -template <> -VisitResponse visitable::visit_items( - const std::function &func ) +VisitResponse map_selector::visit_items( + const std::function &func ) const { - for( auto &cursor : static_cast( *this ) ) { + for( auto &cursor : * ( const_cast( this ) ) ) { if( cursor.visit_items( func ) == VisitResponse::ABORT ) { return VisitResponse::ABORT; } @@ -499,15 +424,12 @@ VisitResponse visitable::visit_items( } /** @relates visitable */ -template <> -VisitResponse visitable::visit_items( - const std::function &func ) +VisitResponse vehicle_cursor::visit_items( + const std::function &func ) const { - const vehicle_cursor *self = static_cast( this ); - - int idx = self->veh.part_with_feature( self->part, "CARGO", true ); + int idx = veh.part_with_feature( part, "CARGO", true ); if( idx >= 0 ) { - for( auto &e : self->veh.get_items( idx ) ) { + for( auto &e : veh.get_items( idx ) ) { if( visit_internal( func, &e ) == VisitResponse::ABORT ) { return VisitResponse::ABORT; } @@ -517,11 +439,10 @@ VisitResponse visitable::visit_items( } /** @relates visitable */ -template <> -VisitResponse visitable::visit_items( - const std::function &func ) +VisitResponse vehicle_selector::visit_items( + const std::function &func ) const { - for( auto &cursor : static_cast( *this ) ) { + for( const auto &cursor : *this ) { if( cursor.visit_items( func ) == VisitResponse::ABORT ) { return VisitResponse::ABORT; } @@ -529,11 +450,8 @@ VisitResponse visitable::visit_items( return VisitResponse::NEXT; } -// Specialize visitable::remove_items_with() for each class that will implement the visitable interface - /** @relates visitable */ -template -item visitable::remove_item( item &it ) +item visitable::remove_item( item &it ) { auto obj = remove_items_with( [&it]( const item & e ) { return &e == ⁢ @@ -548,11 +466,9 @@ item visitable::remove_item( item &it ) } /** @relates visitable */ -template <> -std::list visitable::remove_items_with( const std::function +std::list item::remove_items_with( const std::function &filter, int count ) { - item *it = static_cast( this ); std::list res; if( count <= 0 ) { @@ -560,16 +476,14 @@ std::list visitable::remove_items_with( const std::functioncontents.remove_internal( filter, count, res ); + contents.remove_internal( filter, count, res ); return res; } /** @relates visitable */ -template <> -std::list visitable::remove_items_with( const +std::list inventory::remove_items_with( const std::function &filter, int count ) { - inventory *inv = static_cast( this ); std::list res; if( count <= 0 ) { @@ -577,7 +491,7 @@ std::list visitable::remove_items_with( const return res; } - for( auto stack = inv->items.begin(); stack != inv->items.end() && count > 0; ) { + for( auto stack = items.begin(); stack != items.end() && count > 0; ) { std::list &istack = *stack; const char original_invlet = istack.front().invlet; @@ -600,24 +514,22 @@ std::list visitable::remove_items_with( const } if( istack.empty() ) { - stack = inv->items.erase( stack ); + stack = items.erase( stack ); } else { ++stack; } } // Invalidate binning cache - inv->binned = false; + binned = false; return res; } /** @relates visitable */ -template <> -std::list visitable::remove_items_with( const +std::list Character::remove_items_with( const std::function &filter, int count ) { - Character *ch = static_cast( this ); std::list res; if( count <= 0 ) { @@ -626,17 +538,17 @@ std::list visitable::remove_items_with( const } // first try and remove items from the inventory - res = ch->inv->remove_items_with( filter, count ); + res = inv->remove_items_with( filter, count ); count -= res.size(); if( count == 0 ) { return res; } // then try any worn items - for( auto iter = ch->worn.begin(); iter != ch->worn.end(); ) { + for( auto iter = worn.begin(); iter != worn.end(); ) { if( filter( *iter ) ) { - iter->on_takeoff( *ch ); - res.splice( res.end(), ch->worn, iter++ ); + iter->on_takeoff( *this ); + res.splice( res.end(), worn, iter++ ); if( --count == 0 ) { return res; } @@ -650,22 +562,20 @@ std::list visitable::remove_items_with( const } // finally try the currently wielded item (if any) - if( filter( ch->weapon ) ) { - res.push_back( ch->remove_weapon() ); + if( filter( weapon ) ) { + res.push_back( remove_weapon() ); count--; } else { - ch->weapon.contents.remove_internal( filter, count, res ); + weapon.contents.remove_internal( filter, count, res ); } return res; } /** @relates visitable */ -template <> -std::list visitable::remove_items_with( const +std::list map_cursor::remove_items_with( const std::function &filter, int count ) { - const map_cursor *cur = static_cast( this ); std::list res; if( count <= 0 ) { @@ -674,14 +584,14 @@ std::list visitable::remove_items_with( const } map &here = get_map(); - if( !here.inbounds( *cur ) ) { + if( !here.inbounds( *this ) ) { debugmsg( "cannot remove items from map: cursor out-of-bounds" ); return res; } // fetch the appropriate item stack point offset; - submap *sub = here.get_submap_at( *cur, offset ); + submap *sub = here.get_submap_at( *this, offset ); cata::colony &stack = sub->get_items( offset ); for( auto iter = stack.begin(); iter != stack.end(); ) { @@ -707,18 +617,17 @@ std::list visitable::remove_items_with( const ++iter; } } - here.update_submap_active_item_status( *cur ); + here.update_submap_active_item_status( *this ); return res; } /** @relates visitable */ -template <> -std::list visitable::remove_items_with( const +std::list map_selector::remove_items_with( const std::function &filter, int count ) { std::list res; - for( auto &cursor : static_cast( *this ) ) { + for( auto &cursor : *this ) { std::list out = cursor.remove_items_with( filter, count ); count -= out.size(); res.splice( res.end(), out ); @@ -728,11 +637,9 @@ std::list visitable::remove_items_with( const } /** @relates visitable */ -template <> -std::list visitable::remove_items_with( const +std::list vehicle_cursor::remove_items_with( const std::function &filter, int count ) { - const vehicle_cursor *cur = static_cast( this ); std::list res; if( count <= 0 ) { @@ -740,19 +647,19 @@ std::list visitable::remove_items_with( const return res; } - int idx = cur->veh.part_with_feature( cur->part, "CARGO", false ); + int idx = veh.part_with_feature( part, "CARGO", false ); if( idx < 0 ) { return res; } - vehicle_part &part = cur->veh.part( idx ); - for( auto iter = part.items.begin(); iter != part.items.end(); ) { + vehicle_part &p = veh.part( idx ); + for( auto iter = p.items.begin(); iter != p.items.end(); ) { if( filter( *iter ) ) { // remove from the active items cache (if it isn't there does nothing) - cur->veh.active_items.remove( &*iter ); + veh.active_items.remove( &*iter ); res.push_back( *iter ); - iter = part.items.erase( iter ); + iter = p.items.erase( iter ); if( --count == 0 ) { return res; @@ -768,20 +675,19 @@ std::list visitable::remove_items_with( const if( !res.empty() ) { // if we removed any items then invalidate the cached mass - cur->veh.invalidate_mass(); + veh.invalidate_mass(); } return res; } /** @relates visitable */ -template <> -std::list visitable::remove_items_with( const +std::list vehicle_selector::remove_items_with( const std::function &filter, int count ) { std::list res; - for( auto &cursor : static_cast( *this ) ) { + for( auto &cursor : *this ) { std::list out = cursor.remove_items_with( filter, count ); count -= out.size(); res.splice( res.end(), out ); @@ -798,7 +704,7 @@ static int charges_of_internal( const T &self, const M &main, const itype_id &id int qty = 0; bool found_tool_with_UPS = false; - self.visit_items( [&]( const item * e ) { + self.visit_items( [&]( const item * e, item * ) { if( filter( *e ) ) { if( e->is_tool() ) { if( e->typeId() == id ) { @@ -835,19 +741,17 @@ static int charges_of_internal( const T &self, const M &main, const itype_id &id } /** @relates visitable */ -template -int visitable::charges_of( const itype_id &what, int limit, - const std::function &filter, - const std::function &visitor ) const +int read_only_visitable::charges_of( const itype_id &what, int limit, + const std::function &filter, + const std::function &visitor ) const { return charges_of_internal( *this, *this, what, limit, filter, visitor ); } /** @relates visitable */ -template <> -int visitable::charges_of( const itype_id &what, int limit, - const std::function &filter, - const std::function &visitor ) const +int inventory::charges_of( const itype_id &what, int limit, + const std::function &filter, + const std::function &visitor ) const { if( what == itype_UPS ) { int qty = 0; @@ -855,7 +759,7 @@ int visitable::charges_of( const itype_id &what, int limit, qty = sum_no_wrap( qty, static_cast( charges_of( itype_adv_UPS_off ) / 0.6 ) ); return std::min( qty, limit ); } - const auto &binned = static_cast( this )->get_binned_items(); + const auto &binned = get_binned_items(); const auto iter = binned.find( what ); if( iter == binned.end() ) { return 0; @@ -872,13 +776,11 @@ int visitable::charges_of( const itype_id &what, int limit, } /** @relates visitable */ -template <> -int visitable::charges_of( const itype_id &what, int limit, - const std::function &filter, - const std::function &visitor ) const +int Character::charges_of( const itype_id &what, int limit, + const std::function &filter, + const std::function &visitor ) const { - const Character *self = static_cast( this ); - const player *p = dynamic_cast( self ); + const player *p = dynamic_cast( this ); if( what == itype_toolset ) { if( p && p->has_active_bionic( bio_tools ) ) { @@ -912,8 +814,8 @@ static int amount_of_internal( const T &self, const itype_id &id, bool pseudo, i const std::function &filter ) { int qty = 0; - self.visit_items( [&qty, &id, &pseudo, &limit, &filter]( const item * e ) { - if( ( id.str() == "any" || e->typeId() == id ) && filter( *e ) && + self.visit_items( [&qty, &id, &pseudo, &limit, &filter]( const item * e, item * ) { + if( ( id == STATIC( itype_id( "any" ) ) || e->typeId() == id ) && filter( *e ) && ( pseudo || !e->has_flag( STATIC( flag_id( "PSEUDO" ) ) ) ) ) { qty = sum_no_wrap( qty, 1 ); } @@ -923,21 +825,19 @@ static int amount_of_internal( const T &self, const itype_id &id, bool pseudo, i } /** @relates visitable */ -template -int visitable::amount_of( const itype_id &what, bool pseudo, int limit, - const std::function &filter ) const +int read_only_visitable::amount_of( const itype_id &what, bool pseudo, int limit, + const std::function &filter ) const { return amount_of_internal( *this, what, pseudo, limit, filter ); } /** @relates visitable */ -template <> -int visitable::amount_of( const itype_id &what, bool pseudo, int limit, - const std::function &filter ) const +int inventory::amount_of( const itype_id &what, bool pseudo, int limit, + const std::function &filter ) const { - const auto &binned = static_cast( this )->get_binned_items(); + const auto &binned = get_binned_items(); const auto iter = binned.find( what ); - if( iter == binned.end() && what != itype_id( "any" ) ) { + if( iter == binned.end() && what != STATIC( itype_id( "any" ) ) ) { return 0; } @@ -958,19 +858,16 @@ int visitable::amount_of( const itype_id &what, bool pseudo, int limi } /** @relates visitable */ -template <> -int visitable::amount_of( const itype_id &what, bool pseudo, int limit, - const std::function &filter ) const +int Character::amount_of( const itype_id &what, bool pseudo, int limit, + const std::function &filter ) const { - const Character *self = static_cast( this ); - - if( what == itype_toolset && pseudo && self->has_active_bionic( bio_tools ) ) { + if( what == itype_toolset && pseudo && has_active_bionic( bio_tools ) ) { return 1; } if( what == itype_apparatus && pseudo ) { int qty = 0; - visit_items( [&qty, &limit, &filter]( const item * e ) { + visit_items( [&qty, &limit, &filter]( const item * e, item * ) { if( e->get_quality( quality_id( "SMOKE_PIPE" ) ) >= 1 && filter( *e ) ) { qty = sum_no_wrap( qty, 1 ); } @@ -983,18 +880,8 @@ int visitable::amount_of( const itype_id &what, bool pseudo, int limi } /** @relates visitable */ -template -bool visitable::has_amount( const itype_id &what, int qty, bool pseudo, - const std::function &filter ) const +bool read_only_visitable::has_amount( const itype_id &what, int qty, bool pseudo, + const std::function &filter ) const { return amount_of( what, pseudo, qty, filter ) == qty; } - -// explicit template initialization for all classes implementing the visitable interface -template class visitable; -template class visitable; -template class visitable; -template class visitable; -template class visitable; -template class visitable; -template class visitable; diff --git a/src/visitable.h b/src/visitable.h index 6135741ec5b1a..6967d64b3113e 100644 --- a/src/visitable.h +++ b/src/visitable.h @@ -19,43 +19,39 @@ enum class VisitResponse : int { SKIP // Skip any child nodes and move directly to the next sibling }; -template -class visitable +/** + * Read-only interface for the "container of items". + * Provides API for the traversal and querying of the items hierarchy. + */ +class read_only_visitable { public: /** * Traverses this object and any child items contained using a visitor pattern + * @note Implement for each class that will implement the visitable interface. + * Other `visit_items` variants rely on this function. * * @param func visitor function called for each node which controls whether traversal continues. * The first argument is the node and the second is the parent node (if any) - * * The visitor function should return VisitResponse::Next to recursively process child items, * VisitResponse::Skip to ignore children of the current node or VisitResponse::Abort to skip all remaining nodes - * * @return This method itself only ever returns VisitResponse::Next or VisitResponse::Abort. */ - VisitResponse visit_items( const std::function &func ); - VisitResponse visit_items( const std::function &func ) - const; - - /** Lightweight version which provides only the current node */ - VisitResponse visit_items( const std::function &func ); - VisitResponse visit_items( const std::function &func ) const; + virtual VisitResponse visit_items( + const std::function &func ) const = 0; /** * Determine the immediate parent container (if any) for an item. * @param it item to search for which must be contained (at any depth) by this object * @return parent container or nullptr if the item is not within a container */ - item *find_parent( const item &it ); - const item *find_parent( const item &it ) const; + item *find_parent( const item &it ) const; /** * Returns vector of parent containers (if any) starting with the innermost * @param it item to search for which must be contained (at any depth) by this object */ - std::vector parents( const item &it ); - std::vector parents( const item &it ) const; + std::vector parents( const item &it ) const; /** Returns true if this visitable instance contains the item */ bool has_item( const item &it ) const; @@ -64,10 +60,10 @@ class visitable bool has_item_with( const std::function &filter ) const; /** Returns true if instance has amount (or more) items of at least quality level */ - bool has_quality( const quality_id &qual, int level = 1, int qty = 1 ) const; + virtual bool has_quality( const quality_id &qual, int level = 1, int qty = 1 ) const; /** Return maximum tool quality level provided by instance or INT_MIN if not found */ - int max_quality( const quality_id &qual ) const; + virtual int max_quality( const quality_id &qual ) const; /** * Count maximum available charges from this instance and any contained items @@ -76,9 +72,9 @@ class visitable * @param filter only count charges of items that match the filter * @param visitor is called when UPS charge is used (parameter is the charge itself) */ - int charges_of( const itype_id &what, int limit = INT_MAX, - const std::function &filter = return_true, - const std::function &visitor = nullptr ) const; + virtual int charges_of( const itype_id &what, int limit = INT_MAX, + const std::function &filter = return_true, + const std::function &visitor = nullptr ) const; /** * Count items matching id including both this instance and any contained items @@ -88,9 +84,9 @@ class visitable * @param filter only count items that match the filter * @note items must be empty to be considered a match */ - int amount_of( const itype_id &what, bool pseudo = true, - int limit = INT_MAX, - const std::function &filter = return_true ) const; + virtual int amount_of( const itype_id &what, bool pseudo = true, + int limit = INT_MAX, + const std::function &filter = return_true ) const; /** Check instance provides at least qty of an item (@see amount_of) */ bool has_amount( const itype_id &what, int qty, bool pseudo = true, @@ -100,15 +96,29 @@ class visitable std::vector items_with( const std::function &filter ); std::vector items_with( const std::function &filter ) const; + virtual ~read_only_visitable() = default; +}; + +/** + * Read-write interface for the "container of items". + * Provides API for the traversal and querying of the items hierarchy. + * Also provides means to modify the hierarchy. + */ +class visitable : public read_only_visitable +{ + public: /** - * Removes items contained by this instance which match the filter - * @note if this instance itself is an item it will not be considered by the filter - * @param filter a UnaryPredicate which should return true if the item is to be removed - * @param count maximum number of items to if unspecified unlimited. A count of zero is a no-op - * @return any items removed (items counted by charges are not guaranteed to be stacked) - */ - std::list remove_items_with( const std::function &filter, - int count = INT_MAX ); + * Removes items contained by this instance which match the filter + * + * @note: Implement for each class that will implement the visitable interface + * @note if this instance itself is an item it will not be considered by the filter + * + * @param filter a UnaryPredicate which should return true if the item is to be removed + * @param count maximum number of items to if unspecified unlimited. A count of zero is a no-op + * @return any items removed (items counted by charges are not guaranteed to be stacked) + */ + virtual std::list remove_items_with( const std::function &filter, + int count = INT_MAX ) = 0; /** Removes and returns the item which must be contained by this instance */ item remove_item( item &it ); diff --git a/tests/invlet_test.cpp b/tests/invlet_test.cpp index 89a8b80ad5c20..93e1cad9d3b03 100644 --- a/tests/invlet_test.cpp +++ b/tests/invlet_test.cpp @@ -68,7 +68,7 @@ template static item *retrieve_item( const T &sel, int id ) { item *obj = nullptr; - sel.visit_items( [&id, &obj]( const item * e ) { + sel.visit_items( [&id, &obj]( const item * e, item * ) { if( get_id( *e ) == id ) { obj = const_cast( e ); return VisitResponse::ABORT; diff --git a/tests/item_location_test.cpp b/tests/item_location_test.cpp index 50cd06327faef..3ad5dba6a69a7 100644 --- a/tests/item_location_test.cpp +++ b/tests/item_location_test.cpp @@ -32,7 +32,7 @@ TEST_CASE( "item_location_can_maintain_reference_despite_item_removal", "[item][ m.add_item( pos, item( "jeans" ) ); map_cursor cursor( pos ); item *tshirt = nullptr; - cursor.visit_items( [&tshirt]( item * i ) { + cursor.visit_items( [&tshirt]( item * i, item * ) { if( i->typeId() == itype_id( "tshirt" ) ) { tshirt = i; return VisitResponse::ABORT; diff --git a/tests/new_character_test.cpp b/tests/new_character_test.cpp index 2174802b5db53..dd05b3828d147 100644 --- a/tests/new_character_test.cpp +++ b/tests/new_character_test.cpp @@ -154,7 +154,7 @@ TEST_CASE( "starting_items", "[slow]" ) player_character.add_profession_items(); std::set items_visited; - const auto visitable_counter = [&items_visited]( const item * it ) { + const auto visitable_counter = [&items_visited]( const item * it, auto ) { items_visited.emplace( it ); return VisitResponse::NEXT; }; diff --git a/tests/reload_magazine_test.cpp b/tests/reload_magazine_test.cpp index dcca1cc670826..4e9140f5df13d 100644 --- a/tests/reload_magazine_test.cpp +++ b/tests/reload_magazine_test.cpp @@ -85,7 +85,7 @@ TEST_CASE( "reload_magazine", "[magazine] [visitable] [item] [item_location]" ) } AND_THEN( "a single correctly sized ammo stack remains in the inventory" ) { std::vector found; - player_character.visit_items( [&ammo_id, &found]( const item * e ) { + player_character.visit_items( [&ammo_id, &found]( const item * e, item * ) { if( e->typeId() == ammo_id ) { found.push_back( e ); } @@ -116,7 +116,7 @@ TEST_CASE( "reload_magazine", "[magazine] [visitable] [item] [item_location]" ) } AND_THEN( "the ammo stack was completely used" ) { std::vector found; - player_character.visit_items( [&ammo_id, &found]( const item * e ) { + player_character.visit_items( [&ammo_id, &found]( const item * e, item * ) { if( e->typeId() == ammo_id ) { found.push_back( e ); } @@ -142,7 +142,7 @@ TEST_CASE( "reload_magazine", "[magazine] [visitable] [item] [item_location]" ) } AND_THEN( "a single correctly sized ammo stack remains in the inventory" ) { std::vector found; - player_character.visit_items( [&ammo_id, &found]( const item * e ) { + player_character.visit_items( [&ammo_id, &found]( const item * e, item * ) { if( e->typeId() == ammo_id ) { found.push_back( e ); } @@ -287,7 +287,7 @@ TEST_CASE( "reload_magazine", "[magazine] [visitable] [item] [item_location]" ) } AND_THEN( "a single correctly sized ammo stack remains in the inventory" ) { std::vector found; - player_character.visit_items( [&ammo_id, &found]( const item * e ) { + player_character.visit_items( [&ammo_id, &found]( const item * e, item * ) { if( e->typeId() == ammo_id ) { found.push_back( e ); } @@ -364,7 +364,7 @@ TEST_CASE( "reload_revolver", "[visitable] [item] [item_location]" ) } AND_THEN( "a single correctly sized ammo stack remains in the inventory" ) { std::vector found; - player_character.visit_items( [&ammo_id, &found]( const item * e ) { + player_character.visit_items( [&ammo_id, &found]( const item * e, item * ) { if( e->typeId() == ammo_id ) { found.push_back( e ); } @@ -395,7 +395,7 @@ TEST_CASE( "reload_revolver", "[visitable] [item] [item_location]" ) } AND_THEN( "the ammo stack was completely used" ) { std::vector found; - player_character.visit_items( [&ammo_id, &found]( const item * e ) { + player_character.visit_items( [&ammo_id, &found]( const item * e, item * ) { if( e->typeId() == ammo_id ) { found.push_back( e ); } @@ -421,7 +421,7 @@ TEST_CASE( "reload_revolver", "[visitable] [item] [item_location]" ) } AND_THEN( "a single correctly sized ammo stack remains in the inventory" ) { std::vector found; - player_character.visit_items( [&ammo_id, &found]( const item * e ) { + player_character.visit_items( [&ammo_id, &found]( const item * e, item * ) { if( e->typeId() == ammo_id ) { found.push_back( e ); } diff --git a/tests/visitable_remove_test.cpp b/tests/visitable_remove_test.cpp index 1fd9a16b5e8fd..a7abda2065afc 100644 --- a/tests/visitable_remove_test.cpp +++ b/tests/visitable_remove_test.cpp @@ -28,7 +28,7 @@ template static int count_items( const T &src, const itype_id &id ) { int n = 0; - src.visit_items( [&n, &id]( const item * e ) { + src.visit_items( [&n, &id]( const item * e, item * ) { n += ( e->typeId() == id ); return VisitResponse::NEXT; } ); @@ -235,7 +235,7 @@ TEST_CASE( "visitable_remove", "[visitable]" ) THEN( "all of the bottles remain in the players possession" ) { REQUIRE( count_items( p, container_id ) == 5 ); AND_THEN( "all of the bottles are now empty" ) { - REQUIRE( p.visit_items( [&container_id]( const item * e ) { + REQUIRE( p.visit_items( [&container_id]( const item * e, item * ) { return ( e->typeId() != container_id || e->contents.empty() ) ? VisitResponse::NEXT : VisitResponse::ABORT; } ) != VisitResponse::ABORT ); From cf05d62c8b31f9bc441bc0ae10f853358dc579d1 Mon Sep 17 00:00:00 2001 From: Aivean Date: Wed, 23 Dec 2020 16:33:20 -0800 Subject: [PATCH 2/4] move several methods from `inventory` into `read_only_visitable` replace `inventory` arguments with `read_only_visitable` where applicable --- src/action.cpp | 3 +-- src/butchery_requirements.cpp | 2 +- src/butchery_requirements.h | 4 ++-- src/character.h | 10 +++++----- src/construction.cpp | 7 ++++--- src/construction.h | 2 +- src/craft_command.cpp | 4 ++-- src/craft_command.h | 6 +++--- src/crafting.cpp | 9 +++++---- src/inventory.cpp | 18 ----------------- src/inventory.h | 7 ------- src/requirements.cpp | 37 ++++++++++++++++++++--------------- src/requirements.h | 37 +++++++++++++++++++---------------- src/visitable.cpp | 18 +++++++++++++++++ src/visitable.h | 9 +++++++++ src/vpart_position.h | 1 + 16 files changed, 93 insertions(+), 81 deletions(-) diff --git a/src/action.cpp b/src/action.cpp index 28a4818c60fd1..76b1f9addbb42 100644 --- a/src/action.cpp +++ b/src/action.cpp @@ -17,6 +17,7 @@ #include "game.h" #include "iexamine.h" #include "input.h" +#include "inventory.h" #include "item.h" #include "map.h" #include "map_iterator.h" @@ -45,8 +46,6 @@ static const std::string flag_GOES_DOWN( "GOES_DOWN" ); static const std::string flag_GOES_UP( "GOES_UP" ); static const std::string flag_SWIMMABLE( "SWIMMABLE" ); -class inventory; - static void parse_keymap( std::istream &keymap_txt, std::map &kmap, std::set &unbound_keymap ); diff --git a/src/butchery_requirements.cpp b/src/butchery_requirements.cpp index b617d5c595283..d55e8af5662b1 100644 --- a/src/butchery_requirements.cpp +++ b/src/butchery_requirements.cpp @@ -93,7 +93,7 @@ void butchery_requirements::check_consistency() } std::pair butchery_requirements::get_fastest_requirements( - const inventory &crafting_inv, creature_size size, butcher_type butcher ) const + const read_only_visitable &crafting_inv, creature_size size, butcher_type butcher ) const { for( const std::pair>> &riter : requirements ) { diff --git a/src/butchery_requirements.h b/src/butchery_requirements.h index 28a0cdf953c23..fbff3f13b3887 100644 --- a/src/butchery_requirements.h +++ b/src/butchery_requirements.h @@ -10,7 +10,7 @@ #include "string_id.h" #include "type_id.h" -class inventory; +class read_only_visitable; class JsonObject; enum class butcher_type : int; @@ -29,7 +29,7 @@ class butchery_requirements // tries to find the requirement with the highest speed bonus. if it fails it returns cata::nullopt std::pair get_fastest_requirements( - const inventory &crafting_inv, creature_size size, butcher_type butcher ) const; + const read_only_visitable &crafting_inv, creature_size size, butcher_type butcher ) const; static void load_butchery_req( const JsonObject &jo, const std::string &src ); void load( const JsonObject &jo, const std::string & ); diff --git a/src/character.h b/src/character.h index 7296cd1d09ce8..1efc4e80bd75e 100644 --- a/src/character.h +++ b/src/character.h @@ -1998,7 +1998,7 @@ class Character : public Creature, public visitable std::vector all_items_with_flag( const flag_id &flag ) const; bool has_charges( const itype_id &it, int quantity, - const std::function &filter = return_true ) const; + const std::function &filter = return_true ) const override; // has_amount works ONLY for quantity. // has_charges works ONLY for charges. @@ -2554,7 +2554,7 @@ class Character : public Creature, public visitable * @param obj Object to check for disassembly * @param inv current crafting inventory */ - ret_val can_disassemble( const item &obj, const inventory &inv ) const; + ret_val can_disassemble( const item &obj, const read_only_visitable &inv ) const; bool disassemble(); bool disassemble( item_location target, bool interactive = true ); @@ -2563,11 +2563,11 @@ class Character : public Creature, public visitable void complete_disassemble( item_location &target, const recipe &dis ); const requirement_data *select_requirements( - const std::vector &, int batch, const inventory &, + const std::vector &, int batch, const read_only_visitable &, const std::function &filter ) const; comp_selection select_item_component( const std::vector &components, - int batch, inventory &map_inv, bool can_cancel = false, + int batch, read_only_visitable &map_inv, bool can_cancel = false, const std::function &filter = return_true, bool player_inv = true ); std::list consume_items( const comp_selection &is, int batch, const std::function &filter = return_true ); @@ -2578,7 +2578,7 @@ class Character : public Creature, public visitable const std::function &filter = return_true ); bool consume_software_container( const itype_id &software_id ); comp_selection - select_tool_component( const std::vector &tools, int batch, inventory &map_inv, + select_tool_component( const std::vector &tools, int batch, read_only_visitable &map_inv, bool can_cancel = false, bool player_inv = true, const std::function &charges_required_modifier = []( int i ) { return i; diff --git a/src/construction.cpp b/src/construction.cpp index 3ef6ff58c6a18..f0f249201b01e 100644 --- a/src/construction.cpp +++ b/src/construction.cpp @@ -141,7 +141,7 @@ static std::map construction_id_map; // Helper functions, nobody but us needs to call these. static bool can_construct( const construction_group_str_id &group ); static bool can_construct( const construction &con ); -static bool player_can_build( player &p, const inventory &inv, +static bool player_can_build( player &p, const read_only_visitable &inv, const construction_group_str_id &group ); static bool player_can_see_to_build( player &p, const construction_group_str_id &group ); static void place_construction( const construction_group_str_id &group ); @@ -837,7 +837,8 @@ construction_id construction_menu( const bool blueprint ) return ret; } -bool player_can_build( player &p, const inventory &inv, const construction_group_str_id &group ) +bool player_can_build( player &p, const read_only_visitable &inv, + const construction_group_str_id &group ) { // check all with the same group to see if player can build any std::vector cons = constructions_by_group( group ); @@ -849,7 +850,7 @@ bool player_can_build( player &p, const inventory &inv, const construction_group return false; } -bool player_can_build( player &p, const inventory &inv, const construction &con ) +bool player_can_build( player &p, const read_only_visitable &inv, const construction &con ) { if( p.has_trait( trait_DEBUG_HS ) ) { return true; diff --git a/src/construction.h b/src/construction.h index 4e61a6676173f..a20a2a1b1e8ad 100644 --- a/src/construction.h +++ b/src/construction.h @@ -116,7 +116,7 @@ void reset_constructions(); construction_id construction_menu( bool blueprint ); void complete_construction( player *p ); bool can_construct( const construction &con, const tripoint &p ); -bool player_can_build( player &p, const inventory &inv, const construction &con ); +bool player_can_build( player &p, const read_only_visitable &inv, const construction &con ); void check_constructions(); void finalize_constructions(); diff --git a/src/craft_command.cpp b/src/craft_command.cpp index 130bab434ef42..5ab4c8fd261b3 100644 --- a/src/craft_command.cpp +++ b/src/craft_command.cpp @@ -290,7 +290,7 @@ skill_id craft_command::get_skill_id() } std::vector> craft_command::check_item_components_missing( - const inventory &map_inv ) const + const read_only_visitable &map_inv ) const { std::vector> missing; @@ -355,7 +355,7 @@ std::vector> craft_command::check_item_components_miss } std::vector> craft_command::check_tool_components_missing( - const inventory &map_inv ) const + const read_only_visitable &map_inv ) const { std::vector> missing; diff --git a/src/craft_command.h b/src/craft_command.h index 370396b995f62..b6d546dd86fbc 100644 --- a/src/craft_command.h +++ b/src/craft_command.h @@ -13,7 +13,7 @@ class Character; class JsonIn; class JsonOut; -class inventory; +class read_only_visitable; class item; struct item_comp; struct tool_comp; @@ -116,10 +116,10 @@ class craft_command /** Checks if tools we selected in a previous call to execute() are still available. */ std::vector> check_item_components_missing( - const inventory &map_inv ) const; + const read_only_visitable &map_inv ) const; /** Checks if items we selected in a previous call to execute() are still available. */ std::vector> check_tool_components_missing( - const inventory &map_inv ) const; + const read_only_visitable &map_inv ) const; /** Creates a continue pop up asking to continue crafting and listing the missing components */ bool query_continue( const std::vector> &missing_items, diff --git a/src/crafting.cpp b/src/crafting.cpp index 077b60039ccbc..383fc54d0d422 100644 --- a/src/crafting.cpp +++ b/src/crafting.cpp @@ -1447,7 +1447,8 @@ bool Character::can_continue_craft( item &craft ) return true; } const requirement_data *Character::select_requirements( - const std::vector &alternatives, int batch, const inventory &inv, + const std::vector &alternatives, int batch, + const read_only_visitable &inv, const std::function &filter ) const { cata_assert( !alternatives.empty() ); @@ -1481,7 +1482,7 @@ const requirement_data *Character::select_requirements( /* selection of component if a recipe requirement has multiple options (e.g. 'duct tap' or 'welder') */ comp_selection Character::select_item_component( const std::vector &components, - int batch, inventory &map_inv, bool can_cancel, + int batch, read_only_visitable &map_inv, bool can_cancel, const std::function &filter, bool player_inv ) { std::vector player_has; @@ -1762,7 +1763,7 @@ bool Character::consume_software_container( const itype_id &software_id ) comp_selection Character::select_tool_component( const std::vector &tools, int batch, - inventory &map_inv, bool can_cancel, bool player_inv, + read_only_visitable &map_inv, bool can_cancel, bool player_inv, const std::function &charges_required_modifier ) { @@ -2031,7 +2032,7 @@ void Character::consume_tools( const std::vector &tools, int batch ) consume_tools( select_tool_component( tools, batch, map_inv ), batch ); } -ret_val Character::can_disassemble( const item &obj, const inventory &inv ) const +ret_val Character::can_disassemble( const item &obj, const read_only_visitable &inv ) const { if( !obj.is_disassemblable() ) { return ret_val::make_failure( _( "You cannot disassemble this." ) ); diff --git a/src/inventory.cpp b/src/inventory.cpp index 02dc8d6e3ac59..66cc7181efe8d 100644 --- a/src/inventory.cpp +++ b/src/inventory.cpp @@ -725,24 +725,6 @@ std::list inventory::use_amount( const itype_id &it, int quantity, return ret; } -bool inventory::has_tools( const itype_id &it, int quantity, - const std::function &filter ) const -{ - return has_amount( it, quantity, true, filter ); -} - -bool inventory::has_components( const itype_id &it, int quantity, - const std::function &filter ) const -{ - return has_amount( it, quantity, false, filter ); -} - -bool inventory::has_charges( const itype_id &it, int quantity, - const std::function &filter ) const -{ - return ( charges_of( it, INT_MAX, filter ) >= quantity ); -} - int inventory::leak_level( const flag_id &flag ) const { int ret = 0; diff --git a/src/inventory.h b/src/inventory.h index 9354e4aa78682..9c69cb9fb3d1f 100644 --- a/src/inventory.h +++ b/src/inventory.h @@ -177,13 +177,6 @@ class inventory : public visitable std::list use_amount( const itype_id &it, int quantity, const std::function &filter = return_true ); - bool has_tools( const itype_id &it, int quantity, - const std::function &filter = return_true ) const; - bool has_components( const itype_id &it, int quantity, - const std::function &filter = return_true ) const; - bool has_charges( const itype_id &it, int quantity, - const std::function &filter = return_true ) const; - int leak_level( const flag_id &flag ) const; // level of leaked bad stuff from items // NPC/AI functions diff --git a/src/requirements.cpp b/src/requirements.cpp index cbb50202c7bf5..3a78f82562a8e 100644 --- a/src/requirements.cpp +++ b/src/requirements.cpp @@ -606,7 +606,8 @@ void requirement_data::reset() } std::vector requirement_data::get_folded_components_list( int width, nc_color col, - const inventory &crafting_inv, const std::function &filter, int batch, + const read_only_visitable &crafting_inv, const std::function &filter, + int batch, const std::string &hilite, requirement_display_flags flags ) const { std::vector out_buffer; @@ -624,7 +625,7 @@ std::vector requirement_data::get_folded_components_list( int width template std::vector requirement_data::get_folded_list( int width, - const inventory &crafting_inv, const std::function &filter, + const read_only_visitable &crafting_inv, const std::function &filter, const std::vector< std::vector > &objs, int batch, const std::string &hilite, requirement_display_flags flags ) const { @@ -684,7 +685,7 @@ std::vector requirement_data::get_folded_list( int width, } std::vector requirement_data::get_folded_tools_list( int width, nc_color col, - const inventory &crafting_inv, int batch ) const + const read_only_visitable &crafting_inv, int batch ) const { std::vector output_buffer; output_buffer.push_back( colorize( _( "Tools required:" ), col ) ); @@ -704,7 +705,7 @@ std::vector requirement_data::get_folded_tools_list( int width, nc_ return output_buffer; } -bool requirement_data::can_make_with_inventory( const inventory &crafting_inv, +bool requirement_data::can_make_with_inventory( const read_only_visitable &crafting_inv, const std::function &filter, int batch, craft_flags flags ) const { if( get_player_character().has_trait( trait_DEBUG_HS ) ) { @@ -729,7 +730,7 @@ bool requirement_data::can_make_with_inventory( const inventory &crafting_inv, } template -bool requirement_data::has_comps( const inventory &crafting_inv, +bool requirement_data::has_comps( const read_only_visitable &crafting_inv, const std::vector< std::vector > &vec, const std::function &filter, int batch, craft_flags flags ) @@ -767,7 +768,7 @@ bool requirement_data::has_comps( const inventory &crafting_inv, } bool quality_requirement::has( - const inventory &crafting_inv, const std::function &, int, + const read_only_visitable &crafting_inv, const std::function &, int, craft_flags, const std::function & ) const { if( get_player_character().has_trait( trait_DEBUG_HS ) ) { @@ -776,7 +777,7 @@ bool quality_requirement::has( return crafting_inv.has_quality( type, level, count ); } -nc_color quality_requirement::get_color( bool has_one, const inventory &, +nc_color quality_requirement::get_color( bool has_one, const read_only_visitable &, const std::function &, int ) const { if( get_player_character().has_trait( trait_DEBUG_HS ) || @@ -787,7 +788,8 @@ nc_color quality_requirement::get_color( bool has_one, const inventory &, } bool tool_comp::has( - const inventory &crafting_inv, const std::function &filter, int batch, + const read_only_visitable &crafting_inv, const std::function &filter, + int batch, craft_flags flags, const std::function &visitor ) const { if( get_player_character().has_trait( trait_DEBUG_HS ) ) { @@ -815,7 +817,7 @@ bool tool_comp::has( } } -nc_color tool_comp::get_color( bool has_one, const inventory &crafting_inv, +nc_color tool_comp::get_color( bool has_one, const read_only_visitable &crafting_inv, const std::function &filter, int batch ) const { if( available == available_status::a_insufficient ) { @@ -827,7 +829,8 @@ nc_color tool_comp::get_color( bool has_one, const inventory &crafting_inv, } bool item_comp::has( - const inventory &crafting_inv, const std::function &filter, int batch, + const read_only_visitable &crafting_inv, const std::function &filter, + int batch, craft_flags, const std::function & ) const { if( get_player_character().has_trait( trait_DEBUG_HS ) ) { @@ -841,7 +844,7 @@ bool item_comp::has( } } -nc_color item_comp::get_color( bool has_one, const inventory &crafting_inv, +nc_color item_comp::get_color( bool has_one, const read_only_visitable &crafting_inv, const std::function &filter, int batch ) const { if( available == available_status::a_insufficient ) { @@ -866,7 +869,7 @@ const T *requirement_data::find_by_type( const std::vector< std::vector > &ve return nullptr; } -bool requirement_data::check_enough_materials( const inventory &crafting_inv, +bool requirement_data::check_enough_materials( const read_only_visitable &crafting_inv, const std::function &filter, int batch ) const { bool retval = true; @@ -884,7 +887,8 @@ bool requirement_data::check_enough_materials( const inventory &crafting_inv, return retval; } -bool requirement_data::check_enough_materials( const item_comp &comp, const inventory &crafting_inv, +bool requirement_data::check_enough_materials( const item_comp &comp, + const read_only_visitable &crafting_inv, const std::function &filter, int batch ) const { if( comp.available != available_status::a_true ) { @@ -1506,7 +1510,7 @@ deduped_requirement_data::deduped_requirement_data( const requirement_data &in, } bool deduped_requirement_data::can_make_with_inventory( - const inventory &crafting_inv, const std::function &filter, + const read_only_visitable &crafting_inv, const std::function &filter, int batch, craft_flags flags ) const { return std::any_of( alternatives().begin(), alternatives().end(), @@ -1516,7 +1520,7 @@ bool deduped_requirement_data::can_make_with_inventory( } std::vector deduped_requirement_data::feasible_alternatives( - const inventory &crafting_inv, const std::function &filter, + const read_only_visitable &crafting_inv, const std::function &filter, int batch, craft_flags flags ) const { std::vector result; @@ -1536,7 +1540,8 @@ const requirement_data *deduped_requirement_data::select_alternative( } const requirement_data *deduped_requirement_data::select_alternative( - Character &crafter, const inventory &inv, const std::function &filter, + Character &crafter, const read_only_visitable &inv, + const std::function &filter, int batch, craft_flags flags ) const { const std::vector all_reqs = diff --git a/src/requirements.h b/src/requirements.h index 8a28c5a9b88b4..556518631857c 100644 --- a/src/requirements.h +++ b/src/requirements.h @@ -23,7 +23,7 @@ class JsonIn; class JsonObject; class JsonOut; class JsonValue; -class inventory; +class read_only_visitable; class item; class nc_color; class player; @@ -93,11 +93,12 @@ struct tool_comp : public component { void load( const JsonValue &value ); void dump( JsonOut &jsout ) const; - bool has( const inventory &crafting_inv, const std::function &filter, + bool has( const read_only_visitable &crafting_inv, + const std::function &filter, int batch = 1, craft_flags = craft_flags::none, const std::function &visitor = std::function() ) const; std::string to_string( int batch = 1, int avail = 0 ) const; - nc_color get_color( bool has_one, const inventory &crafting_inv, + nc_color get_color( bool has_one, const read_only_visitable &crafting_inv, const std::function &filter, int batch = 1 ) const; bool by_charges() const; component_type get_component_type() const { @@ -111,11 +112,12 @@ struct item_comp : public component { void load( const JsonValue &value ); void dump( JsonOut &jsout ) const; - bool has( const inventory &crafting_inv, const std::function &filter, + bool has( const read_only_visitable &crafting_inv, + const std::function &filter, int batch = 1, craft_flags = craft_flags::none, const std::function &visitor = std::function() ) const; std::string to_string( int batch = 1, int avail = 0 ) const; - nc_color get_color( bool has_one, const inventory &crafting_inv, + nc_color get_color( bool has_one, const read_only_visitable &crafting_inv, const std::function &filter, int batch = 1 ) const; component_type get_component_type() const { return component_type::ITEM; @@ -150,13 +152,14 @@ struct quality_requirement { void load( const JsonValue &value ); void dump( JsonOut &jsout ) const; - bool has( const inventory &crafting_inv, const std::function &filter, + bool has( const read_only_visitable &crafting_inv, + const std::function &filter, int = 0, craft_flags = craft_flags::none, const std::function &visitor = std::function() ) const; std::string to_string( int batch = 1, int avail = 0 ) const; std::string to_colored_string() const; void check_consistency( const std::string &display_name ) const; - nc_color get_color( bool has_one, const inventory &crafting_inv, + nc_color get_color( bool has_one, const read_only_visitable &crafting_inv, const std::function &filter, int = 0 ) const; component_type get_component_type() const { return component_type::QUALITY; @@ -335,18 +338,18 @@ struct requirement_data { * @param filter should be recipe::get_component_filter() if used with a recipe * or is_crafting_component otherwise. */ - bool can_make_with_inventory( const inventory &crafting_inv, + bool can_make_with_inventory( const read_only_visitable &crafting_inv, const std::function &filter, int batch = 1, craft_flags = craft_flags::none ) const; /** @param filter see @ref can_make_with_inventory */ std::vector get_folded_components_list( int width, nc_color col, - const inventory &crafting_inv, const std::function &filter, + const read_only_visitable &crafting_inv, const std::function &filter, int batch = 1, const std::string &hilite = "", requirement_display_flags = requirement_display_flags::none ) const; std::vector get_folded_tools_list( int width, nc_color col, - const inventory &crafting_inv, int batch = 1 ) const; + const read_only_visitable &crafting_inv, int batch = 1 ) const; /** * Gets a variant of this recipe with crafting-only tools replaced by their @@ -383,9 +386,9 @@ struct requirement_data { bool blacklisted = false; - bool check_enough_materials( const inventory &crafting_inv, + bool check_enough_materials( const read_only_visitable &crafting_inv, const std::function &filter, int batch = 1 ) const; - bool check_enough_materials( const item_comp &comp, const inventory &crafting_inv, + bool check_enough_materials( const item_comp &comp, const read_only_visitable &crafting_inv, const std::function &filter, int batch = 1 ) const; template @@ -401,12 +404,12 @@ struct requirement_data { const std::vector< std::vector > &objs ); template static bool has_comps( - const inventory &crafting_inv, const std::vector< std::vector > &vec, + const read_only_visitable &crafting_inv, const std::vector< std::vector > &vec, const std::function &filter, int batch = 1, craft_flags = craft_flags::none ); template - std::vector get_folded_list( int width, const inventory &crafting_inv, + std::vector get_folded_list( int width, const read_only_visitable &crafting_inv, const std::function &filter, const std::vector< std::vector > &objs, int batch = 1, const std::string &hilite = "", @@ -456,7 +459,7 @@ class deduped_requirement_data } std::vector feasible_alternatives( - const inventory &crafting_inv, const std::function &filter, + const read_only_visitable &crafting_inv, const std::function &filter, int batch = 1, craft_flags = craft_flags::none ) const; const requirement_data *select_alternative( @@ -464,11 +467,11 @@ class deduped_requirement_data craft_flags = craft_flags::none ) const; const requirement_data *select_alternative( - Character &, const inventory &, const std::function &filter, + Character &, const read_only_visitable &, const std::function &filter, int batch = 1, craft_flags = craft_flags::none ) const; bool can_make_with_inventory( - const inventory &crafting_inv, const std::function &filter, + const read_only_visitable &crafting_inv, const std::function &filter, int batch = 1, craft_flags = craft_flags::none ) const; bool is_too_complex() const { diff --git a/src/visitable.cpp b/src/visitable.cpp index 5b9d72ddea16a..65c1b10745ba4 100644 --- a/src/visitable.cpp +++ b/src/visitable.cpp @@ -198,6 +198,24 @@ bool Character::has_quality( const quality_id &qual, int level, int qty ) const return qty <= 0 ? true : has_quality_internal( *this, qual, level, qty ) == qty; } +bool read_only_visitable::has_tools( const itype_id &it, int quantity, + const std::function &filter ) const +{ + return has_amount( it, quantity, true, filter ); +} + +bool read_only_visitable::has_components( const itype_id &it, int quantity, + const std::function &filter ) const +{ + return has_amount( it, quantity, false, filter ); +} + +bool read_only_visitable::has_charges( const itype_id &it, int quantity, + const std::function &filter ) const +{ + return ( charges_of( it, INT_MAX, filter ) >= quantity ); +} + template static int max_quality_internal( const T &self, const quality_id &qual ) { diff --git a/src/visitable.h b/src/visitable.h index 6967d64b3113e..d45bf31dcc181 100644 --- a/src/visitable.h +++ b/src/visitable.h @@ -97,6 +97,15 @@ class read_only_visitable std::vector items_with( const std::function &filter ) const; virtual ~read_only_visitable() = default; + + bool has_tools( const itype_id &it, int quantity, + const std::function &filter = return_true ) const; + bool has_components( const itype_id &it, int quantity, + const std::function &filter = return_true ) const; + + virtual bool has_charges( const itype_id &it, int quantity, + const std::function &filter = return_true ) const; + }; /** diff --git a/src/vpart_position.h b/src/vpart_position.h index a565a6dfcb4e0..289ca0f6faabf 100644 --- a/src/vpart_position.h +++ b/src/vpart_position.h @@ -13,6 +13,7 @@ class vehicle; struct vehicle_part; class player; class vpart_info; +class inventory; enum vpart_bitflags : int; class vpart_reference; From 8c0324a60ea0cb23c42b810133e7ce7102884d1e Mon Sep 17 00:00:00 2001 From: Aivean Date: Wed, 23 Dec 2020 21:22:05 -0800 Subject: [PATCH 3/4] add `temp_crafting_inventory` - lightweight alternative to the `inventory` for crafting queries --- src/temp_crafting_inventory.cpp | 38 +++++++++++++++++++++++ src/temp_crafting_inventory.h | 53 ++++++++++++++++++++++++++++++++ src/visitable.cpp | 20 ++++++++++++ tests/crafting_test.cpp | 15 +++++++-- tests/temp_crafting_inv_test.cpp | 40 ++++++++++++++++++++++++ 5 files changed, 163 insertions(+), 3 deletions(-) create mode 100644 src/temp_crafting_inventory.cpp create mode 100644 src/temp_crafting_inventory.h create mode 100644 tests/temp_crafting_inv_test.cpp diff --git a/src/temp_crafting_inventory.cpp b/src/temp_crafting_inventory.cpp new file mode 100644 index 0000000000000..c41f45d4b3984 --- /dev/null +++ b/src/temp_crafting_inventory.cpp @@ -0,0 +1,38 @@ + +#include "temp_crafting_inventory.h" + +temp_crafting_inventory::temp_crafting_inventory( const read_only_visitable &v ) +{ + add_all_ref( v ); +} + +size_t temp_crafting_inventory::size() const +{ + return items.size(); +} + +void temp_crafting_inventory::clear() +{ + items.clear(); + temp_owned_items.clear(); +} + +void temp_crafting_inventory::add_item_ref( item &item ) +{ + items.insert( &item ); +} + +item &temp_crafting_inventory::add_item_copy( const item &item ) +{ + const auto iter = temp_owned_items.insert( item ); + items.insert( &( *iter ) ); + return *iter; +} + +void temp_crafting_inventory::add_all_ref( const read_only_visitable &v ) +{ + v.visit_items( [&]( item * it, item * ) { + add_item_ref( *it ); + return VisitResponse::SKIP; + } ); +} diff --git a/src/temp_crafting_inventory.h b/src/temp_crafting_inventory.h new file mode 100644 index 0000000000000..e03aa84c55bc0 --- /dev/null +++ b/src/temp_crafting_inventory.h @@ -0,0 +1,53 @@ +#pragma once +#ifndef CATA_SRC_TEMP_CRAFTING_INVENTORY_H +#define CATA_SRC_TEMP_CRAFTING_INVENTORY_H + +#include "colony.h" +#include "item.h" +#include "visitable.h" + +/** + * A transient add-only list of item references and temporary pseudo-items, that implements `read_only_visitable` + * and thus can answer crafting queries (see crafting.cpp and requirement_data::can_make_with_inventory). + */ +class temp_crafting_inventory : public read_only_visitable +{ + public: + temp_crafting_inventory() = default; + temp_crafting_inventory( const read_only_visitable & ); + + size_t size() const; + + void clear(); + + /** + * Adds item reference to this container. + * @note container doesn't own the added reference, meaning added item should outlive this container. + */ + void add_item_ref( item &item ); + + /** + * Adds all (top-level) items from the given visitable. + * @note container doesn't own the added references, meaning added items should outlive this container. + */ + void add_all_ref( const read_only_visitable &v ); + + /** + * Adds item copy to this container. + * Container will own a copy of the given item. + * @return reference to the added item within the container + */ + item &add_item_copy( const item &item ); + + // inherited from visitable + VisitResponse visit_items( const std::function &func ) const + override; + + private: + // list of all items in this container + cata::colony items; + // copies of "owned" items added by `add_item_copy` + cata::colony temp_owned_items; +}; + +#endif // CATA_SRC_TEMP_CRAFTING_INVENTORY_H diff --git a/src/visitable.cpp b/src/visitable.cpp index 65c1b10745ba4..03995cb30d347 100644 --- a/src/visitable.cpp +++ b/src/visitable.cpp @@ -29,6 +29,7 @@ #include "point.h" #include "string_id.h" #include "submap.h" +#include "temp_crafting_inventory.h" #include "units.h" #include "value_ptr.h" #include "veh_type.h" @@ -393,6 +394,18 @@ VisitResponse inventory::visit_items( return VisitResponse::NEXT; } +/** @relates visitable */ +VisitResponse temp_crafting_inventory::visit_items( + const std::function &func ) const +{ + for( item *it : items ) { + if( visit_internal( func, it ) == VisitResponse::ABORT ) { + return VisitResponse::ABORT; + } + } + return VisitResponse::NEXT; +} + /** @relates visitable */ VisitResponse Character::visit_items( const std::function &func ) const @@ -763,6 +776,13 @@ int read_only_visitable::charges_of( const itype_id &what, int limit, const std::function &filter, const std::function &visitor ) const { + if( what == itype_UPS ) { + int qty = 0; + qty = sum_no_wrap( qty, charges_of( itype_UPS_off ) ); + qty = sum_no_wrap( qty, static_cast( charges_of( itype_adv_UPS_off ) / 0.6 ) ); + return std::min( qty, limit ); + } + return charges_of_internal( *this, *this, what, limit, filter, visitor ); } diff --git a/tests/crafting_test.cpp b/tests/crafting_test.cpp index a19a8dab4fcb6..d471764436d36 100644 --- a/tests/crafting_test.cpp +++ b/tests/crafting_test.cpp @@ -29,6 +29,7 @@ #include "recipe_dictionary.h" #include "requirements.h" #include "ret_val.h" +#include "temp_crafting_inventory.h" #include "type_id.h" #include "value_ptr.h" @@ -319,9 +320,17 @@ static void prep_craft( const recipe_id &rid, const std::vector &tools, player_character.get_skill_level( r.skill_used ) ) ); player_character.moves--; const inventory &crafting_inv = player_character.crafting_inventory(); - bool can_craft = r.deduped_requirements().can_make_with_inventory( - crafting_inv, r.get_component_filter() ); - REQUIRE( can_craft == expect_craftable ); + + SECTION( "can craft with crafting inv" ) { + bool can_craft = r.deduped_requirements().can_make_with_inventory( + crafting_inv, r.get_component_filter() ); + REQUIRE( can_craft == expect_craftable ); + } + SECTION( "can craft with temp inv" ) { + bool can_craft = r.deduped_requirements().can_make_with_inventory( + temp_crafting_inventory( crafting_inv ), r.get_component_filter() ); + REQUIRE( can_craft == expect_craftable ); + } } static time_point midnight = calendar::turn_zero + 0_hours; diff --git a/tests/temp_crafting_inv_test.cpp b/tests/temp_crafting_inv_test.cpp new file mode 100644 index 0000000000000..156538bff13b4 --- /dev/null +++ b/tests/temp_crafting_inv_test.cpp @@ -0,0 +1,40 @@ +#include "catch/catch.hpp" + +#include "temp_crafting_inventory.h" +#include "../src/temp_crafting_inventory.h" + +TEST_CASE( "temp_crafting_inv_test_amount", "[crafting][inventory]" ) +{ + temp_crafting_inventory inv; + CHECK( inv.size() == 0 ); + + item gum( "test_gum", calendar::turn_zero, item::default_charges_tag{} ); + + inv.add_item_ref( gum ); + CHECK( inv.size() == 1 ); + + CHECK( inv.amount_of( itype_id( "test_gum" ) ) == 1 ); + CHECK( inv.has_amount( itype_id( "test_gum" ), 1 ) ); + CHECK( inv.has_charges( itype_id( "test_gum" ), 10 ) ); + CHECK_FALSE( inv.has_charges( itype_id( "test_gum" ), 11 ) ); + + inv.clear(); + CHECK( inv.size() == 0 ); +} + +TEST_CASE( "temp_crafting_inv_test_quality", "[crafting][inventory]" ) +{ + temp_crafting_inventory inv; + inv.add_item_copy( item( "test_halligan" ) ); + + CHECK( inv.has_quality( quality_id( "HAMMER" ), 1 ) ); + CHECK( inv.has_quality( quality_id( "HAMMER" ), 2 ) ); + CHECK_FALSE( inv.has_quality( quality_id( "HAMMER" ), 3 ) ); + CHECK( inv.has_quality( quality_id( "DIG" ), 1 ) ); + CHECK_FALSE( inv.has_quality( quality_id( "AXE" ) ) ); + + inv.add_item_copy( item( "test_fire_ax" ) ); + CHECK( inv.has_quality( quality_id( "AXE" ) ) ); + + CHECK( inv.max_quality( quality_id( "PRY" ) ) == 4 ); +} From c123cc4b79dfc32c652ee4da83a8d9d1f0e5ec0a Mon Sep 17 00:00:00 2001 From: Aivean Date: Wed, 23 Dec 2020 21:22:26 -0800 Subject: [PATCH 4/4] use `temp_crafting_inventory` in `are_requirements_nearby` --- src/activity_item_handling.cpp | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/activity_item_handling.cpp b/src/activity_item_handling.cpp index e0e90704b5334..e1cce655d1221 100644 --- a/src/activity_item_handling.cpp +++ b/src/activity_item_handling.cpp @@ -57,6 +57,7 @@ #include "stomach.h" #include "string_formatter.h" #include "string_id.h" +#include "temp_crafting_inventory.h" #include "translations.h" #include "trap.h" #include "units.h" @@ -899,7 +900,7 @@ static bool are_requirements_nearby( const std::vector &loot_spots, const bool in_loot_zones, const tripoint &src_loc ) { zone_manager &mgr = zone_manager::get_manager(); - inventory temp_inv; + temp_crafting_inventory temp_inv; units::volume volume_allowed = p.volume_capacity() - p.volume_carried(); units::mass weight_allowed = p.weight_capacity() - p.weight_carried(); static const auto check_weight_if = []( const activity_id & id ) { @@ -919,7 +920,7 @@ static bool are_requirements_nearby( const std::vector &loot_spots, if( elem->has_quality( qual_WELD ) ) { found_welder = true; } - temp_inv += *elem; + temp_inv.add_item_ref( *elem ); } map &here = get_map(); for( const tripoint &elem : loot_spots ) { @@ -935,7 +936,7 @@ static bool are_requirements_nearby( const std::vector &loot_spots, continue; } } - for( const item &elem2 : here.i_at( elem ) ) { + for( item &elem2 : here.i_at( elem ) ) { if( in_loot_zones && elem2.made_of_from_type( phase_id::LIQUID ) ) { continue; } @@ -946,7 +947,7 @@ static bool are_requirements_nearby( const std::vector &loot_spots, continue; } } - temp_inv += elem2; + temp_inv.add_item_ref( elem2 ); } if( !in_loot_zones ) { if( const cata::optional vp = here.veh_at( elem ).part_with_feature( "CARGO", @@ -954,7 +955,7 @@ static bool are_requirements_nearby( const std::vector &loot_spots, vehicle &src_veh = vp->vehicle(); int src_part = vp->part_index(); for( item &it : src_veh.get_items( src_part ) ) { - temp_inv += it; + temp_inv.add_item_ref( it ); } } } @@ -970,11 +971,11 @@ static bool are_requirements_nearby( const std::vector &loot_spots, item welder( itype_welder, calendar::turn_zero ); welder.charges = veh_battery; welder.set_flag( flag_PSEUDO ); - temp_inv.add_item( welder ); + temp_inv.add_item_copy( welder ); item soldering_iron( itype_soldering_iron, calendar::turn_zero ); soldering_iron.charges = veh_battery; soldering_iron.set_flag( flag_PSEUDO ); - temp_inv.add_item( soldering_iron ); + temp_inv.add_item_copy( soldering_iron ); } } }