Skip to content

Commit

Permalink
Add code for applying migrations to downstream item lists. (#35545)
Browse files Browse the repository at this point in the history
* Add replacement methods to item groups

* Add replacement method to requirements

* Refactor item blacklist application code.

* Apply migrations to template groups, requirements, recipes and vehicle contents.
  • Loading branch information
kevingranade authored and ZhilkinSerg committed Jan 14, 2020
1 parent 9a45f6f commit 2e3b2f5
Show file tree
Hide file tree
Showing 5 changed files with 135 additions and 19 deletions.
64 changes: 47 additions & 17 deletions src/item_factory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -457,40 +457,70 @@ 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<itype_id, itype>::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<const Group_tag, std::unique_ptr<Item_spawn_data>> &g : m_template_groups ) {
g.second->remove_item( candidate->first );
}

// remove any blacklisted items from requirements
for( auto &r : requirement_data::all() ) {
const_cast<requirement_data &>( r.second ).blacklist_item( e.first );
for( const std::pair<const requirement_id, requirement_data> &r : requirement_data::all() ) {
const_cast<requirement_data &>( 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<vehicle_prototype &>( vid.obj() );
for( vehicle_item_spawn &vis : prototype.item_spawns ) {
auto &vec = vis.item_ids;
const auto iter = std::remove_if( vec.begin(), vec.end(), item_is_blacklisted );
vec.erase( iter, vec.end() );
}
}

for( const std::pair<itype_id, migration> &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<const Group_tag, std::unique_ptr<Item_spawn_data>> &g : m_template_groups ) {
g.second->replace_item( migrate.first, migrate.second.replace );
}

// replace migrated items in requirements
for( const std::pair<const requirement_id, requirement_data> &r : requirement_data::all() ) {
const_cast<requirement_data &>( 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<vehicle_prototype &>( vid.obj() );
for( vehicle_item_spawn &vis : prototype.item_spawns ) {
for( itype_id &type_to_spawn : vis.item_ids ) {
std::map<itype_id, migration>::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 )
Expand Down
44 changes: 43 additions & 1 deletion src/item_group.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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 )
Expand Down Expand Up @@ -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<Item_spawn_data> &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<Item_spawn_data> &elem : items ) {
if( ( elem )->has_item( itemid ) ) {
return true;
}
Expand Down
5 changes: 5 additions & 0 deletions src/item_group.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<const itype *> every_item() const = 0;
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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<const itype *> every_item() const override;
};
Expand Down Expand Up @@ -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<const itype *> every_item() const override;

Expand Down
36 changes: 36 additions & 0 deletions src/requirements.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -823,6 +823,42 @@ void requirement_data::blacklist_item( const std::string &id )
blacklisted |= apply_blacklist( components, id );
}

template <typename T>
static void apply_replacement( std::vector<std::vector<T>> &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<T>::iterator target = opts.end();
typename std::vector<T>::iterator replacement_target = opts.end();
for( typename std::vector<T>::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;
Expand Down
5 changes: 4 additions & 1 deletion src/requirements.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down Expand Up @@ -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;
Expand Down

0 comments on commit 2e3b2f5

Please sign in to comment.