diff --git a/src/init.cpp b/src/init.cpp index 9c74b777bbbb4..7753b118bb74b 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -193,6 +193,8 @@ void DynamicDataLoader::initialize() type_function_map["monitems"] = new ClassFunctionAccessor(g, &game::load_monitem); type_function_map["region_settings"] = new StaticFunctionAccessor(&load_region_settings); + type_function_map["ITEM_BLACKLIST"] = new ClassFunctionAccessor(item_controller, + &Item_factory::load_item_blacklist); // ...unimplemented? type_function_map["INSTRUMENT"] = new StaticFunctionAccessor(&load_ingored_type); @@ -388,6 +390,7 @@ void DynamicDataLoader::finalize_loaded_data() { calculate_mapgen_weights(); MonsterGenerator::generator().finalize_mtypes(); g->finalize_vehicles(); + item_controller->finialize_item_blacklist(); finalize_recipes(); check_consistency(); } diff --git a/src/item_factory.cpp b/src/item_factory.cpp index 831bbec252d60..ca0f55f33afc9 100644 --- a/src/item_factory.cpp +++ b/src/item_factory.cpp @@ -5,6 +5,7 @@ #include "addiction.h" #include "translations.h" #include "bodypart.h" +#include "crafting.h" #include #include #include @@ -30,6 +31,103 @@ static const std::string category_id_other("other"); Item_factory* item_controller = new Item_factory(); +extern std::map > > recipe_booksets; + +typedef std::set t_string_set; +static t_string_set item_blacklist; + +bool remove_item(const std::string &itm, std::vector& com) { + std::vector::iterator a = com.begin(); + while(a != com.end()) { + if(a->type == itm) { + a = com.erase(a); + } else { + ++a; + } + } + return com.empty(); +} + +bool remove_item(const std::string &itm, std::vector >& com) { + for(size_t i = 0; i < com.size(); i++) { + // Note: this assumes that coms[i] is never an empty vector + if(remove_item(itm, com[i])) { + return true; + } + } + return false; +} + +void remove_item(const std::string &itm, std::vector& vec) { + for(size_t i = 0; i < vec.size(); i++) { + if(vec[i].itemtype == itm) { + vec.erase(vec.begin() + i); + i--; + } + } +} + +void Item_factory::finialize_item_blacklist() { + std::set deleted_recipes; + for(t_string_set::const_iterator a = item_blacklist.begin(); a != item_blacklist.end(); ++a) { + const std::string &itm = *a; + for(std::map::iterator b = m_template_groups.begin(); b != m_template_groups.end(); ++b) { + b->second->remove_item(itm); + } + for(recipe_map::iterator b = recipes.begin(); b != recipes.end(); ++b) { + for(size_t c = 0; c < b->second.size(); c++) { + recipe *r = b->second[c]; + if(r->result == itm || remove_item(itm, r->components) || remove_item(itm, r->tools)) { + deleted_recipes.insert(r); + delete r; + b->second.erase(b->second.begin() + c); + c--; + continue; + } + } + } + for(size_t i = 0; i < constructions.size(); i++) { + construction *c = constructions[i]; + if(remove_item(itm, c->components) || remove_item(itm, c->tools)) { + delete c; + constructions.erase(constructions.begin() + i); + i--; + } + } + for(size_t i = 0; i < terlist.size(); i++) { + remove_item(itm, terlist[i].bash.items); + } + for(size_t i = 0; i < furnlist.size(); i++) { + remove_item(itm, furnlist[i].bash.items); + } + } + // look through the recipe-to-book mapping and remove any mapping + // to recipes that have been removed (and already deleted! - dangling pointers ahead) + for (std::map > >::iterator book_ref_it = + recipe_booksets.begin(); book_ref_it != recipe_booksets.end(); ++book_ref_it) { + // std::queue doesn't support erase, have to make a copy + // without the delete recipes and use that + std::queue > copy; + while (!book_ref_it->second.empty()) { + std::pair rec_pair = book_ref_it->second.front(); + book_ref_it->second.pop(); + if (deleted_recipes.count(rec_pair.first) == 0) { + copy.push(rec_pair); // not delete, recipe still valid + } + } + // std::queue doesn't even has a swap function, have to use slow copy assignment + book_ref_it->second = copy; + } + item_blacklist.clear(); +} + +void Item_factory::load_item_blacklist(JsonObject &json) { + JsonArray jarr = json.get_array("items"); + while(jarr.has_more()) { + item_blacklist.insert(jarr.next_string()); + } +} + //Every item factory comes with a missing item Item_factory::Item_factory(){ init(); diff --git a/src/item_factory.h b/src/item_factory.h index da71c5097743e..f1afe5cd77377 100644 --- a/src/item_factory.h +++ b/src/item_factory.h @@ -83,6 +83,9 @@ class Item_factory void load_bionic (JsonObject &jo); void load_veh_part (JsonObject &jo); + void load_item_blacklist(JsonObject &jo); + void finialize_item_blacklist(); + // Check that all items referenced in the groups // do actually exist (are defined) void check_items_of_groups_exist() const; diff --git a/src/item_group.cpp b/src/item_group.cpp index f7226c57b9fde..403b045173914 100644 --- a/src/item_group.cpp +++ b/src/item_group.cpp @@ -114,3 +114,45 @@ bool Item_group::has_item(const Item_tag item_id) { return 0; } + +void Item_group::remove_item(const Item_tag &item_id) { + std::set rec; + remove_item(item_id, rec); +} + +void Item_group::remove_item(const Item_tag &item_id, std::set &rec) { + if(rec.count(m_id) > 0) { + return; + } + rec.insert(m_id); + // If this removes an item/group, m_max_odds must be decreased + // by the chance of the removed item/group. But the chance of + // that is not directly stored, but as offset from the previous + // item/group (see add_entry/add_group) + int delta_max_odds = 0; + int prev_upper_bound = 0; + for(size_t i = 0; i < m_groups.size(); i++) { + m_groups[i]->m_group->remove_item(item_id, rec); + if(m_groups[i]->m_group->m_max_odds == 0) { + delta_max_odds += (m_groups[i]->m_upper_bound - prev_upper_bound); + delete m_groups[i]; + m_groups.erase(m_groups.begin() + i); + i--; + } else { + m_groups[i]->m_upper_bound -= delta_max_odds; + prev_upper_bound = m_groups[i]->m_upper_bound; + } + } + for(size_t i = 0; i < m_entries.size(); i++) { + if(m_entries[i]->m_id == item_id) { + delta_max_odds += (m_entries[i]->m_upper_bound - prev_upper_bound); + delete m_entries[i]; + m_entries.erase(m_entries.begin() + i); + i--; + } else { + m_entries[i]->m_upper_bound -= delta_max_odds; + prev_upper_bound = m_entries[i]->m_upper_bound; + } + } + m_max_odds -= delta_max_odds; +} diff --git a/src/item_group.h b/src/item_group.h index cbbb1bd98fd21..30f1d7060a40c 100644 --- a/src/item_group.h +++ b/src/item_group.h @@ -2,6 +2,8 @@ #define _ITEM_GROUP_H_ #include +#include +#include typedef std::string Item_tag; @@ -19,6 +21,7 @@ friend class Item_factory; bool guns_have_ammo(); void add_entry(const Item_tag item_id, int chance); void add_group(Item_group*, int chance); + void remove_item(const Item_tag &item_id); // Does this item group contain the given item? bool has_item(const Item_tag item_id); @@ -26,6 +29,7 @@ friend class Item_factory; // this is not recursive void check_items_exist() const; private: + void remove_item(const Item_tag &item_id, std::set &rec); const Item_tag m_id; int m_max_odds; @@ -39,6 +43,7 @@ friend class Item_factory; //to add a lower bound value. class Item_group_entry { +friend class Item_group; public: Item_group_entry(const Item_tag id, int upper_bound); @@ -51,6 +56,7 @@ class Item_group_entry class Item_group_group { +friend class Item_group; public: Item_group_group(Item_group* group, int upper_bound);