diff --git a/src/item_factory.cpp b/src/item_factory.cpp index 25950481cf429..e752a291e737b 100644 --- a/src/item_factory.cpp +++ b/src/item_factory.cpp @@ -457,33 +457,28 @@ void Item_factory::finalize() void Item_factory::finalize_item_blacklist() { - for( t_string_set::const_iterator a = item_blacklist.begin(); a != item_blacklist.end(); ++a ) { - if( !has_template( *a ) ) { - debugmsg( "item on blacklist %s does not exist", a->c_str() ); - } - - } - - for( auto &e : m_templates ) { - if( !item_is_blacklisted( e.first ) ) { + for( const std::string &blackout : item_blacklist ) { + std::unordered_map::iterator candidate = m_templates.find( blackout ); + if( candidate == m_templates.end() ) { + debugmsg( "item on blacklist %s does not exist", blackout.c_str() ); continue; } - for( auto &g : m_template_groups ) { - g.second->remove_item( e.first ); + + for( std::pair> &g : m_template_groups ) { + g.second->remove_item( candidate->first ); } // remove any blacklisted items from requirements - for( auto &r : requirement_data::all() ) { - const_cast( r.second ).blacklist_item( e.first ); + for( const std::pair &r : requirement_data::all() ) { + const_cast( r.second ).blacklist_item( candidate->first ); } // remove any recipes used to craft the blacklisted item - recipe_dictionary::delete_if( [&]( const recipe & r ) { - return r.result() == e.first; + recipe_dictionary::delete_if( [&candidate]( const recipe & r ) { + return r.result() == candidate->first; } ); } - - for( auto &vid : vehicle_prototype::get_all() ) { + for( vproto_id &vid : vehicle_prototype::get_all() ) { vehicle_prototype &prototype = const_cast( vid.obj() ); for( vehicle_item_spawn &vis : prototype.item_spawns ) { auto &vec = vis.item_ids; @@ -491,6 +486,41 @@ void Item_factory::finalize_item_blacklist() vec.erase( iter, vec.end() ); } } + + for( const std::pair &migrate : migrations ) { + if( m_templates.find( migrate.second.replace ) == m_templates.end() ) { + debugmsg( "Replacement item for migration %s does not exist", migrate.first.c_str() ); + continue; + } + + for( std::pair> &g : m_template_groups ) { + g.second->replace_item( migrate.first, migrate.second.replace ); + } + + // replace migrated items in requirements + for( const std::pair &r : requirement_data::all() ) { + const_cast( r.second ).replace_item( migrate.first, + migrate.second.replace ); + } + + // remove any recipes used to craft the migrated item + // if there's a valid recipe, it will be for the replacement + recipe_dictionary::delete_if( [&migrate]( const recipe & r ) { + return r.result() == migrate.first; + } ); + } + for( vproto_id &vid : vehicle_prototype::get_all() ) { + vehicle_prototype &prototype = const_cast( vid.obj() ); + for( vehicle_item_spawn &vis : prototype.item_spawns ) { + for( itype_id &type_to_spawn : vis.item_ids ) { + std::map::iterator replacement = + migrations.find( type_to_spawn ); + if( replacement != migrations.end() ) { + type_to_spawn = replacement->second.replace; + } + } + } + } } void Item_factory::load_item_blacklist( const JsonObject &json ) diff --git a/src/item_group.cpp b/src/item_group.cpp index aa7d1b3e8dc70..16331294f7c14 100644 --- a/src/item_group.cpp +++ b/src/item_group.cpp @@ -157,6 +157,27 @@ bool Single_item_creator::remove_item( const Item_tag &itemid ) return type == S_NONE; } +bool Single_item_creator::replace_item( const Item_tag &itemid, const Item_tag &replacementid ) +{ + if( modifier ) { + if( modifier->replace_item( itemid, replacementid ) ) { + return true; + } + } + if( type == S_ITEM ) { + if( itemid == id ) { + id = replacementid; + return true; + } + } else if( type == S_ITEM_GROUP ) { + Item_spawn_data *isd = item_controller->get_group( id ); + if( isd != nullptr ) { + isd->replace_item( itemid, replacementid ); + } + } + return type == S_NONE; +} + bool Single_item_creator::has_item( const Item_tag &itemid ) const { return type == S_ITEM && itemid == id; @@ -377,6 +398,19 @@ bool Item_modifier::remove_item( const Item_tag &itemid ) return false; } +bool Item_modifier::replace_item( const Item_tag &itemid, const Item_tag &replacementid ) +{ + if( ammo != nullptr ) { + ammo->replace_item( itemid, replacementid ); + } + if( container != nullptr ) { + if( container->replace_item( itemid, replacementid ) ) { + return true; + } + } + return false; +} + Item_group::Item_group( Type t, int probability, int ammo_chance, int magazine_chance ) : Item_spawn_data( probability ) , type( t ) @@ -496,9 +530,17 @@ bool Item_group::remove_item( const Item_tag &itemid ) return items.empty(); } +bool Item_group::replace_item( const Item_tag &itemid, const Item_tag &replacementid ) +{ + for( const std::unique_ptr &elem : items ) { + ( elem )->replace_item( itemid, replacementid ); + } + return items.empty(); +} + bool Item_group::has_item( const Item_tag &itemid ) const { - for( const auto &elem : items ) { + for( const std::unique_ptr &elem : items ) { if( ( elem )->has_item( itemid ) ) { return true; } diff --git a/src/item_group.h b/src/item_group.h index 7e1c72f74f94e..066849040f4e4 100644 --- a/src/item_group.h +++ b/src/item_group.h @@ -134,6 +134,7 @@ class Item_spawn_data * all linked groups. */ virtual bool remove_item( const Item_tag &itemid ) = 0; + virtual bool replace_item( const Item_tag &itemid, const Item_tag &replacementid ) = 0; virtual bool has_item( const Item_tag &itemid ) const = 0; virtual std::set every_item() const = 0; @@ -189,6 +190,7 @@ class Item_modifier void modify( item &new_item ) const; void check_consistency( const std::string &context ) const; bool remove_item( const Item_tag &itemid ); + bool replace_item( const Item_tag &itemid, const Item_tag &replacementid ); // Currently these always have the same chance as the item group it's part of, but // theoretically it could be defined per-item / per-group. @@ -236,6 +238,8 @@ class Single_item_creator : public Item_spawn_data item create_single( const time_point &birthday, RecursionList &rec ) const override; void check_consistency( const std::string &context ) const override; bool remove_item( const Item_tag &itemid ) override; + bool replace_item( const Item_tag &itemid, const Item_tag &replacementid ) override; + bool has_item( const Item_tag &itemid ) const override; std::set every_item() const override; }; @@ -280,6 +284,7 @@ class Item_group : public Item_spawn_data item create_single( const time_point &birthday, RecursionList &rec ) const override; void check_consistency( const std::string &context ) const override; bool remove_item( const Item_tag &itemid ) override; + bool replace_item( const Item_tag &itemid, const Item_tag &replacementid ) override; bool has_item( const Item_tag &itemid ) const override; std::set every_item() const override; diff --git a/src/requirements.cpp b/src/requirements.cpp index e2f5f44a9412a..7cc5968eeb4c4 100644 --- a/src/requirements.cpp +++ b/src/requirements.cpp @@ -823,6 +823,42 @@ void requirement_data::blacklist_item( const std::string &id ) blacklisted |= apply_blacklist( components, id ); } +template +static void apply_replacement( std::vector> &vec, const std::string &id, + const std::string &replacement ) +{ + // If the target and replacement are both present, remove the target. + // If only the target is present, replace it. + for( auto &opts : vec ) { + typename std::vector::iterator target = opts.end(); + typename std::vector::iterator replacement_target = opts.end(); + for( typename std::vector::iterator iter = opts.begin(); iter != opts.end(); ++iter ) { + if( iter->type == id ) { + target = iter; + } else if( iter->type == replacement ) { + replacement_target = iter; + } + } + // No target to replace, do nothing. + if( target == opts.end() ) { + continue; + } + // Target but no replacement, replace. + if( replacement_target == opts.end() ) { + target->type = replacement; + continue; + } + // Both target and replacement, remove the target entry and leave the existing replacement. + opts.erase( target ); + } +} + +void requirement_data::replace_item( const itype_id &id, const itype_id &replacement ) +{ + apply_replacement( tools, id, replacement ); + apply_replacement( components, id, replacement ); +} + const requirement_data::alter_tool_comp_vector &requirement_data::get_tools() const { return tools; diff --git a/src/requirements.h b/src/requirements.h index fba89c452d513..36d2215fe77a5 100644 --- a/src/requirements.h +++ b/src/requirements.h @@ -208,7 +208,6 @@ struct requirement_data { return tools.empty() && components.empty() && qualities.empty(); } - /** check if removal of items via @ref blacklist_item left no alternatives in group */ bool is_blacklisted() const { return blacklisted; } @@ -270,6 +269,10 @@ struct requirement_data { * will be marked as @ref blacklisted */ void blacklist_item( const itype_id &id ); + /** + * Replace tools or components of the given type. + */ + void replace_item( const itype_id &id, const itype_id &replacement ); const alter_tool_comp_vector &get_tools() const; const alter_quali_req_vector &get_qualities() const;