diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 3cec373e7..412631a3d 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -152,7 +152,7 @@ if (BUILD_SUPPORTED) DFHACK_PLUGIN(sort sort.cpp LINK_LIBRARIES lua) DFHACK_PLUGIN(steam-engine steam-engine.cpp) DFHACK_PLUGIN(stockflow stockflow.cpp LINK_LIBRARIES lua) - DFHACK_PLUGIN(stockpiles stockpiles.cpp PROTOBUFS stockpiles) + add_subdirectory(stockpiles) DFHACK_PLUGIN(stocks stocks.cpp) DFHACK_PLUGIN(strangemood strangemood.cpp) DFHACK_PLUGIN(tiletypes tiletypes.cpp Brushes.h) diff --git a/plugins/stockpiles.cpp b/plugins/stockpiles.cpp deleted file mode 100644 index ebf4b0d40..000000000 --- a/plugins/stockpiles.cpp +++ /dev/null @@ -1,2835 +0,0 @@ -#include "Core.h" -#include "Console.h" -#include "Export.h" -#include "PluginManager.h" - -#include "MiscUtils.h" - -#include "modules/Materials.h" -#include "modules/Items.h" - -#include "DataDefs.h" -#include "df/world.h" -#include "df/world_data.h" -#include "df/inorganic_raw.h" -#include "df/organic_mat_category.h" -#include "df/furniture_type.h" -#include "df/item_quality.h" -#include "df/item_type.h" -#include "df/creature_raw.h" -#include "df/caste_raw.h" -#include "df/material.h" -#include "df/inorganic_raw.h" -#include "df/plant_raw.h" -#include "df/stockpile_group_set.h" - -#include "df/ui.h" -#include "df/building_stockpilest.h" -#include "df/stockpile_settings.h" -#include "df/global_objects.h" -#include "df/viewscreen_dwarfmodest.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -// protobuf -#include "proto/stockpiles.pb.h" -#include - -// os -#include - -// stl -#include -#include - -using std::vector; -using std::string; -using std::endl; -using namespace DFHack; -using namespace df::enums; -using namespace google::protobuf; -using namespace dfstockpiles; - -using df::global::world; -using df::global::ui; -using df::global::selection_rect; - -using df::building_stockpilest; -using std::placeholders::_1; - -static command_result copystock ( color_ostream &out, vector & parameters ); -static bool copystock_guard ( df::viewscreen *top ); - -static command_result savestock ( color_ostream &out, vector & parameters ); -static bool savestock_guard ( df::viewscreen *top ); - -static command_result loadstock ( color_ostream &out, vector & parameters ); -static bool loadstock_guard ( df::viewscreen *top ); - -DFHACK_PLUGIN ( "stockpiles" ); - -DFhackCExport command_result plugin_init ( color_ostream &out, std::vector &commands ) -{ - if ( world && ui ) - { - commands.push_back ( - PluginCommand ( - "copystock", "Copy stockpile under cursor.", - copystock, copystock_guard, - " - In 'q' or 't' mode: select a stockpile and invoke in order\n" - " to switch to the 'p' stockpile creation mode, and initialize\n" - " the custom settings from the selected stockpile.\n" - " - In 'p': invoke in order to switch back to 'q'.\n" - ) - ); - commands.push_back ( - PluginCommand ( - "savestock", "Save the active stockpile's settings to a file.", - savestock, savestock_guard, - "Must be in 'q' mode and have a stockpile selected.\n" - "example: 'savestock food.dfstock' will save the settings to 'food.dfstock'\n" - "in your stockpile folder.\n" - "Omitting the filename will result in text output directly to the console\n\n" - " -d, --debug: enable debug output\n" - " : filename to save stockpile settings to (will be overwritten!)\n" - ) - ); - commands.push_back ( - PluginCommand ( - "loadstock", "Load settings from a file and apply them to the active stockpile.", - loadstock, loadstock_guard, - "Must be in 'q' mode and have a stockpile selected.\n" - "example: 'loadstock food.dfstock' will load the settings from 'food.dfstock'\n" - "in your stockpile folder and apply them to the selected stockpile.\n" - " -d, --debug: enable debug output\n" - " : filename to load stockpile settings from\n" - ) - ); - } - std::cerr << "world: " << sizeof ( df::world ) << " ui: " << sizeof ( df::ui ) - << " b_stock: " << sizeof ( building_stockpilest ) << endl; - return CR_OK; -} - -DFhackCExport command_result plugin_shutdown ( color_ostream &out ) -{ - return CR_OK; -} - -static bool copystock_guard ( df::viewscreen *top ) -{ - using namespace ui_sidebar_mode; - - if ( !Gui::dwarfmode_hotkey ( top ) ) - return false; - - switch ( ui->main.mode ) - { - case Stockpiles: - return true; - case BuildingItems: - case QueryBuilding: - return !!virtual_cast ( world->selected_building ); - default: - return false; - } -} - -static command_result copystock ( color_ostream &out, vector & parameters ) -{ - // HOTKEY COMMAND: CORE ALREADY SUSPENDED - - // For convenience: when used in the stockpiles mode, switch to 'q' - if ( ui->main.mode == ui_sidebar_mode::Stockpiles ) - { - world->selected_building = NULL; // just in case it contains some kind of garbage - ui->main.mode = ui_sidebar_mode::QueryBuilding; - selection_rect->start_x = -30000; - - out << "Switched back to query building." << endl; - return CR_OK; - } - - building_stockpilest *sp = virtual_cast ( world->selected_building ); - if ( !sp ) - { - out.printerr ( "Selected building isn't a stockpile.\n" ); - return CR_WRONG_USAGE; - } - - ui->stockpile.custom_settings = sp->settings; - ui->main.mode = ui_sidebar_mode::Stockpiles; - world->selected_stockpile_type = stockpile_category::Custom; - - out << "Stockpile options copied." << endl; - return CR_OK; -} - - -static bool savestock_guard ( df::viewscreen *top ) -{ - using namespace ui_sidebar_mode; - - if ( !Gui::dwarfmode_hotkey ( top ) ) - return false; - - switch ( ui->main.mode ) - { - case Stockpiles: - return true; - case BuildingItems: - case QueryBuilding: - return !!virtual_cast ( world->selected_building ); - default: - return false; - } -} - -static bool loadstock_guard ( df::viewscreen *top ) -{ - using namespace ui_sidebar_mode; - - if ( !Gui::dwarfmode_hotkey ( top ) ) - return false; - - switch ( ui->main.mode ) - { - case Stockpiles: - return true; - case BuildingItems: - case QueryBuilding: - return !!virtual_cast ( world->selected_building ); - default: - return false; - } -} - -// Utility Functions {{{ -// A set of convenience functions for doing common lookups - - -/** - * Retrieve creature raw from index - */ -static df::creature_raw* find_creature ( int32_t idx ) -{ - return world->raws.creatures.all[idx]; -} - -/** - * Retrieve creature index from id string - * @return -1 if not found - */ -static int16_t find_creature ( const std::string &creature_id ) -{ - return linear_index ( world->raws.creatures.all, &df::creature_raw::creature_id, creature_id ); -} - -/** - * Retrieve plant raw from index -*/ -static df::plant_raw* find_plant ( size_t idx ) -{ - return world->raws.plants.all[idx]; -} - -/** - * Retrieve plant index from id string - * @return -1 if not found - */ -static size_t find_plant ( const std::string &plant_id ) -{ - return linear_index ( world->raws.plants.all, &df::plant_raw::id, plant_id ); -} - -// }}} utility Functions - - - - -/** - * Helper class for mapping the various organic mats between their material indices - * and their index in the stockpile_settings structures. - */ -class OrganicMatLookup -{ - -// pair of material type and index - typedef std::pair FoodMatPair; -// map for using type,index pairs to find the food index - typedef std::map FoodMatMap; - -public: - struct FoodMat - { - MaterialInfo material; - df::creature_raw *creature; - df::caste_raw * caste; - FoodMat() : material ( -1 ), creature ( 0 ), caste ( 0 ) {} - }; - static void food_mat_by_idx ( std::ostream &out, organic_mat_category::organic_mat_category mat_category, std::vector::size_type food_idx, FoodMat & food_mat ) - { - out << "food_lookup: food_idx(" << food_idx << ") "; - df::world_raws &raws = world->raws; - df::special_mat_table table = raws.mat_table; - int32_t main_idx = table.organic_indexes[mat_category][food_idx]; - int16_t type = table.organic_types[mat_category][food_idx]; - if ( mat_category == organic_mat_category::Fish || - mat_category == organic_mat_category::UnpreparedFish || - mat_category == organic_mat_category::Eggs ) - { - food_mat.creature = raws.creatures.all[type]; - food_mat.caste = food_mat.creature->caste[main_idx]; - out << " special creature type(" << type << ") caste("<< main_idx <<")" <::size_type idx ) - { - FoodMat food_mat; - food_mat_by_idx ( out, mat_category, idx, food_mat ); - if ( food_mat.material.isValid() ) - { - return food_mat.material.getToken(); - } - else if ( food_mat.creature ) - { - return food_mat.creature->creature_id + ":" + food_mat.caste->caste_id; - } - return std::string(); - } - - static size_t food_max_size ( organic_mat_category::organic_mat_category mat_category ) - { - return world->raws.mat_table.organic_types[mat_category].size(); - } - - static void food_build_map ( std::ostream &out ) - { - if ( index_built ) - return; - df::world_raws &raws = world->raws; - df::special_mat_table table = raws.mat_table; - using df::enums::organic_mat_category::organic_mat_category; - df::enum_traits traits; - for ( int32_t mat_category = traits.first_item_value; mat_category <= traits.last_item_value; ++mat_category ) - { - for ( size_t i = 0; i < table.organic_indexes[mat_category].size(); ++i ) - { - int16_t type = table.organic_types[mat_category].at ( i ); - int32_t index = table.organic_indexes[mat_category].at ( i ); - food_index[mat_category].insert ( std::make_pair ( std::make_pair ( type,index ), i ) ); // wtf.. only in c++ - } - } - index_built = true; - } - - static int16_t food_idx_by_token ( std::ostream &out, organic_mat_category::organic_mat_category mat_category, const std::string & token ) - { - int16_t food_idx = -1; - df::world_raws &raws = world->raws; - df::special_mat_table table = raws.mat_table; - out << "food_idx_by_token: "; - if ( mat_category == organic_mat_category::Fish || - mat_category == organic_mat_category::UnpreparedFish || - mat_category == organic_mat_category::Eggs ) - { - std::vector tokens; - split_string ( &tokens, token, ":" ); - if ( tokens.size() != 2 ) - { - out << "creature " << "invalid CREATURE:CASTE token: " << token << endl; - } - else - { - int16_t creature_idx = find_creature ( tokens[0] ); - if ( creature_idx < 0 ) - { - out << " creature invalid token " << tokens[0]; - } - else - { - food_idx = linear_index ( table.organic_types[mat_category], creature_idx ); - if ( tokens[1] == "MALE" ) - food_idx += 1; - if (table.organic_types[mat_category][food_idx] == creature_idx ) - out << "creature " << token << " caste " << tokens[1] << " creature_idx(" << creature_idx << ") food_idx("<< food_idx << ")" << endl; - else { - out << "ERROR creature caste not found: " << token << " caste " << tokens[1] << " creature_idx(" << creature_idx << ") food_idx("<< food_idx << ")" << endl; - food_idx = -1; - } - } - } - } - else - { - if ( !index_built ) - food_build_map ( out ); - MaterialInfo mat_info = food_mat_by_token ( out, token ); - int16_t type = mat_info.type; - int32_t index = mat_info.index; - int16_t food_idx2 = -1; - auto it = food_index[mat_category].find ( std::make_pair ( type, index ) ); - if ( it != food_index[mat_category].end() ) - { - out << "matinfo: " << token << " type(" << mat_info.type << ") idx(" << mat_info.index << ") food_idx(" << it->second << ")" << endl; - food_idx = it->second; - } - else - { - out << "matinfo: " << token << " type(" << mat_info.type << ") idx(" << mat_info.index << ") food_idx not found :(" << endl; - } - } - return food_idx; - } - - static MaterialInfo food_mat_by_token ( std::ostream &out, const std::string & token ) - { - MaterialInfo mat_info; - mat_info.find ( token ); - return mat_info; - } - - static bool index_built; - static std::vector food_index; -private: - OrganicMatLookup() {} -}; - -bool OrganicMatLookup::index_built = false; -std::vector OrganicMatLookup::food_index = std::vector ( 37 ); - - -/** - * Null buffer that acts like /dev/null for when debug is disabled - */ -class NullBuffer : public std::streambuf -{ -public: - int overflow ( int c ) - { - return c; - } -}; - -class NullStream : public std::ostream -{ -public: - NullStream() : std::ostream ( &m_sb ) {} -private: - NullBuffer m_sb; -}; - -/** - * Class for serializing the stockpile_settings structure into a Google protobuf - */ -class StockpileSerializer -{ -public: - /** - * @param out for debugging - * @param stockpile stockpile to read or write settings to - */ - StockpileSerializer ( building_stockpilest * stockpile ) - : mDebug ( false ) - , mOut ( 0 ) - , mNull() - , mPile ( stockpile ) - { - - // build other_mats indices - furniture_setup_other_mats(); - bars_blocks_setup_other_mats(); - finished_goods_setup_other_mats(); - weapons_armor_setup_other_mats(); - } - - ~StockpileSerializer() {} - - void enable_debug ( color_ostream&out ) - { - mDebug = true; - mOut = &out; - } - - /** - * Since we depend on protobuf-lite, not the full lib, we copy this function from - * protobuf message.cc - */ - bool serialize_to_ostream(ostream* output) - { - mBuffer.Clear(); - write(); - { - io::OstreamOutputStream zero_copy_output(output); - if (!mBuffer.SerializeToZeroCopyStream(&zero_copy_output)) return false; - } - return output->good(); - } - - /** - * Will serialize stockpile settings to a file (overwrites existing files) - * @return success/failure - */ - bool serialize_to_file ( const std::string & file ) - { - std::fstream output ( file, std::ios::out | std::ios::binary | std::ios::trunc ); - return serialize_to_ostream(&output); - } - - /** - * Again, copied from message.cc - */ - bool parse_from_istream(istream* input) - { - mBuffer.Clear(); - io::IstreamInputStream zero_copy_input(input); - const bool res = mBuffer.ParseFromZeroCopyStream(&zero_copy_input) && input->eof(); - if( res ) read(); - return res; - } - - - /** - * Read stockpile settings from file - */ - bool unserialize_from_file ( const std::string & file ) - { - std::fstream input ( file, std::ios::in | std::ios::binary ); - return parse_from_istream(&input); - } - -private: - - bool mDebug; - std::ostream * mOut; - NullStream mNull; - building_stockpilest * mPile; - StockpileSettings mBuffer; - std::map mOtherMatsFurniture; - std::map mOtherMatsFinishedGoods; - std::map mOtherMatsBars; - std::map mOtherMatsBlocks; - std::map mOtherMatsWeaponsArmor; - - - std::ostream & debug() - { - if ( mDebug ) - return *mOut; - return mNull; - } - - /** - read memory structures and serialize to protobuf - */ - void write() - { -// debug() << "GROUP SET " << bitfield_to_string(mPile->settings.flags) << endl; - write_general(); - if ( mPile->settings.flags.bits.animals ) - write_animals(); - if ( mPile->settings.flags.bits.food ) - write_food(); - if ( mPile->settings.flags.bits.furniture ) - write_furniture(); - if ( mPile->settings.flags.bits.refuse ) - write_refuse(); - if ( mPile->settings.flags.bits.stone ) - write_stone(); - if ( mPile->settings.flags.bits.ammo ) - write_ammo(); - if ( mPile->settings.flags.bits.coins ) - write_coins(); - if ( mPile->settings.flags.bits.bars_blocks ) - write_bars_blocks(); - if ( mPile->settings.flags.bits.gems ) - write_gems(); - if ( mPile->settings.flags.bits.finished_goods ) - write_finished_goods(); - if ( mPile->settings.flags.bits.leather ) - write_leather(); - if ( mPile->settings.flags.bits.cloth ) - write_cloth(); - if ( mPile->settings.flags.bits.wood ) - write_wood(); - if ( mPile->settings.flags.bits.weapons ) - write_weapons(); - if ( mPile->settings.flags.bits.armor ) - write_armor(); - } - - // parse serialized data into ui indices - void read () - { - debug() << endl << "==READ==" << endl; - read_general(); - read_animals(); - read_food(); - read_furniture(); - read_refuse(); - read_stone(); - read_ammo(); - read_coins(); - read_bars_blocks(); - read_gems(); - read_finished_goods(); - read_leather(); - read_cloth(); - read_wood(); - read_weapons(); - read_armor(); - } - - /** - * Find an enum's value based off the string label. - * @param traits the enum's trait struct - * @param token the string value in key_table - * @return the enum's value, -1 if not found - */ - template - static typename df::enum_traits::base_type linear_index ( std::ostream & out, df::enum_traits traits, const std::string &token ) - { - auto j = traits.first_item_value; - auto limit = traits.last_item_value; - // sometimes enums start at -1, which is bad news for array indexing - if ( j < 0 ) - { - j += abs ( traits.first_item_value ); - limit += abs ( traits.first_item_value ); - } - for ( ; j <= limit; ++j ) - { -// out << " linear_index("<< token <<") = table["< FuncReadImport; - // add the token to the serialized list during export - typedef std::function FuncWriteExport; - // are item's of item_type allowed? - typedef std::function FuncItemAllowed; - // is this material allowed? - typedef std::function FuncMaterialAllowed; - - // convenient struct for parsing food stockpile items - struct food_pair - { - // exporting - FuncWriteExport set_value; - std::vector * stockpile_values; - // importing - FuncReadImport get_value; - size_t serialized_count; - bool valid; - - food_pair ( FuncWriteExport s, std::vector* sp_v, FuncReadImport g, size_t count ) - : set_value ( s ) - , stockpile_values ( sp_v ) - , get_value ( g ) - , serialized_count ( count ) - , valid ( true ) - {} - food_pair(): valid( false ) {} - }; - - /** - * There are many repeated (un)serialization cases throughout the stockpile_settings structure, - * so the most common cases have been generalized into generic functions using lambdas. - * - * The basic process to serialize a stockpile_settings structure is: - * 1. loop through the list - * 2. for every element that is TRUE: - * 3. map the specific stockpile_settings index into a general material, creature, etc index - * 4. verify that type is allowed in the list (e.g., no stone in gems stockpiles) - * 5. add it to the protobuf using FuncWriteExport - * - * The unserialization process is the same in reverse. - */ - void serialize_list_organic_mat ( FuncWriteExport add_value, const std::vector * list, organic_mat_category::organic_mat_category cat ) - { - if ( !list ) - { - debug() << "serialize_list_organic_mat: list null" << endl; - } - for ( size_t i = 0; i < list->size(); ++i ) - { - if ( list->at ( i ) ) - { - std::string token = OrganicMatLookup::food_token_by_idx ( debug(), cat, i ); - if ( !token.empty() ) - { - add_value ( token ); - debug() << " organic_material " << i << " is " << token << endl; - } - else - { - debug() << "food mat invalid :(" << endl; - } - } - } - } - - /** - * @see serialize_list_organic_mat - */ - void unserialize_list_organic_mat ( FuncReadImport get_value, size_t list_size, std::vector *pile_list, organic_mat_category::organic_mat_category cat ) - { - pile_list->clear(); - pile_list->resize ( OrganicMatLookup::food_max_size ( cat ), '\0' ); - for ( size_t i = 0; i < list_size; ++i ) - { - std::string token = get_value ( i ); - int16_t idx = OrganicMatLookup::food_idx_by_token ( debug(), cat, token ); - debug() << " organic_material " << idx << " is " << token << endl; - if ( idx >= pile_list->size() ) - { - debug() << "error organic mat index too large! idx[" << idx << "] max_size[" << pile_list->size() << "]" << endl; - continue; - } - pile_list->at ( idx ) = 1; - } - } - - /** - * @see serialize_list_organic_mat - */ - void serialize_list_item_type ( FuncItemAllowed is_allowed, FuncWriteExport add_value, const std::vector &list ) - { - using df::enums::item_type::item_type; - df::enum_traits type_traits; - debug() << "item_type size = " << list.size() << " size limit = " << type_traits.last_item_value << " typecasted: " << ( size_t ) type_traits.last_item_value << endl; - for ( size_t i = 0; i <= ( size_t ) type_traits.last_item_value; ++i ) - { - if ( list.at ( i ) ) - { - const item_type type = ( item_type ) ( ( df::enum_traits::base_type ) i ); - std::string r_type ( type_traits.key_table[i+1] ); - if ( !is_allowed ( type ) ) continue; - add_value ( r_type ); - debug() << "item_type key_table[" << i+1 << "] type[" << ( int16_t ) type << "] is " << r_type < *pile_list ) - { - pile_list->clear(); - pile_list->resize ( 112, '\0' ); // TODO remove hardcoded list size value - for ( int i = 0; i < pile_list->size(); ++i ) - { - pile_list->at ( i ) = is_allowed ( ( item_type::item_type ) i ) ? 0 : 1; - } - using df::enums::item_type::item_type; - df::enum_traits type_traits; - for ( int32_t i = 0; i < list_size; ++i ) - { - const std::string token = read_value ( i ); - // subtract one because item_type starts at -1 - const df::enum_traits::base_type idx = linear_index ( debug(), type_traits, token ) - 1; - const item_type type = ( item_type ) idx; - if ( !is_allowed ( type ) ) continue; - debug() << " item_type " << idx << " is " << token << endl; - if ( idx >= pile_list->size() ) - { - debug() << "error item_type index too large! idx[" << idx << "] max_size[" << pile_list->size() << "]" << endl; - continue; - } - pile_list->at ( idx ) = 1; - } - } - - /** - * @see serialize_list_organic_mat - */ - void serialize_list_material ( FuncMaterialAllowed is_allowed, FuncWriteExport add_value, const std::vector &list ) - { - MaterialInfo mi; - for ( size_t i = 0; i < list.size(); ++i ) - { - if ( list.at ( i ) ) - { - mi.decode ( 0, i ); - if ( !is_allowed ( mi ) ) continue; - debug() << " material " << i << " is " << mi.getToken() << endl; - add_value ( mi.getToken() ); - } - } - } - - /** - * @see serialize_list_organic_mat - */ - void unserialize_list_material ( FuncMaterialAllowed is_allowed, FuncReadImport read_value, int32_t list_size, std::vector *pile_list ) - { - // we initialize all possible (allowed) values to 0, - // then all other not-allowed values to 1 - // why? because that's how the memory is in DF before - // we muck with it. - std::set idx_set; - pile_list->clear(); - pile_list->resize ( world->raws.inorganics.size(), 0 ); - for ( int i = 0; i < pile_list->size(); ++i ) - { - MaterialInfo mi ( 0, i ); - pile_list->at ( i ) = is_allowed ( mi ) ? 0 : 1; - } - for ( int i = 0; i < list_size; ++i ) - { - const std::string token = read_value ( i ); - MaterialInfo mi; - mi.find ( token ); - if ( !is_allowed ( mi ) ) continue; - debug() << " material " << mi.index << " is " << token << endl; - if ( mi.index >= pile_list->size() ) - { - debug() << "error material index too large! idx[" << mi.index << "] max_size[" << pile_list->size() << "]" << endl; - continue; - } - pile_list->at ( mi.index ) = 1; - } - } - - /** - * @see serialize_list_organic_mat - */ - void serialize_list_quality ( FuncWriteExport add_value, const bool ( &quality_list ) [7] ) - { - using df::enums::item_quality::item_quality; - df::enum_traits quality_traits; - for ( size_t i = 0; i < 7; ++i ) - { - if ( quality_list[i] ) - { - const std::string f_type ( quality_traits.key_table[i] ); - add_value ( f_type ); - debug() << " quality: " << i << " is " << f_type < 0 && list_size <= 7 ) - { - using df::enums::item_quality::item_quality; - df::enum_traits quality_traits; - for ( int i = 0; i < list_size; ++i ) - { - const std::string quality = read_value ( i ); - df::enum_traits::base_type idx = linear_index ( debug(), quality_traits, quality ); - if ( idx < 0 ) - { - debug() << " invalid quality token " << quality << endl; - continue; - } - debug() << " quality: " << idx << " is " << quality << endl; - pile_list[idx] = true; - } - } - else - { - quality_clear ( pile_list ); - } - } - - /** - * @see serialize_list_organic_mat - */ - void serialize_list_other_mats ( const std::map other_mats, FuncWriteExport add_value, std::vector list ) - { - for ( size_t i = 0; i < list.size(); ++i ) - { - if ( list.at ( i ) ) - { - const std::string token = other_mats_index ( other_mats, i ); - if ( token.empty() ) - { - debug() << " invalid other material with index " << i << endl; - continue; - } - add_value ( token ); - debug() << " other mats " << i << " is " << token << endl; - } - } - } - - /** - * @see serialize_list_organic_mat - */ - void unserialize_list_other_mats ( const std::map other_mats, FuncReadImport read_value, int32_t list_size, std::vector *pile_list ) - { - pile_list->clear(); - pile_list->resize ( other_mats.size(), '\0' ); - for ( int i = 0; i < list_size; ++i ) - { - const std::string token = read_value ( i ); - size_t idx = other_mats_token ( other_mats, token ); - if ( idx < 0 ) - { - debug() << "invalid other mat with token " << token; - continue; - } - debug() << " other_mats " << idx << " is " << token << endl; - if ( idx >= pile_list->size() ) - { - debug() << "error other_mats index too large! idx[" << idx << "] max_size[" << pile_list->size() << "]" << endl; - continue; - } - pile_list->at ( idx ) = 1; - } - } - - - /** - * @see serialize_list_organic_mat - */ - void serialize_list_itemdef ( FuncWriteExport add_value, std::vector list, std::vector items, item_type::item_type type ) - { - for ( size_t i = 0; i < list.size(); ++i ) - { - if ( list.at ( i ) ) - { - const df::itemdef *a = items.at ( i ); - // skip procedurally generated items - if ( a->base_flags.is_set ( 0 ) ) continue; - ItemTypeInfo ii; - if ( !ii.decode ( type, i ) ) continue; - add_value ( ii.getToken() ); - debug() << " itemdef type" << i << " is " << ii.getToken() << endl; - } - } - } - - /** - * @see serialize_list_organic_mat - */ - void unserialize_list_itemdef ( FuncReadImport read_value, int32_t list_size, std::vector *pile_list, item_type::item_type type ) - { - pile_list->clear(); - pile_list->resize ( Items::getSubtypeCount ( type ), '\0' ); - for ( int i = 0; i < list_size; ++i ) - { - std::string token = read_value ( i ); - ItemTypeInfo ii; - if ( !ii.find ( token ) ) continue; - debug() << " itemdef " << ii.subtype << " is " << token << endl; - if ( ii.subtype >= pile_list->size() ) - { - debug() << "error itemdef index too large! idx[" << ii.subtype << "] max_size[" << pile_list->size() << "]" << endl; - continue; - } - pile_list->at ( ii.subtype ) = 1; - } - } - - /** - * Given a list of other_materials and an index, return its corresponding token - * @return empty string if not found - * @see other_mats_token - */ - std::string other_mats_index ( const std::map other_mats, int idx ) - { - auto it = other_mats.find ( idx ); - if ( it == other_mats.end() ) - return std::string(); - return it->second; - } - /** - * Given a list of other_materials and a token, return its corresponding index - * @return -1 if not found - * @see other_mats_index - */ - int other_mats_token ( const std::map other_mats, const std::string & token ) - { - for ( auto it = other_mats.begin(); it != other_mats.end(); ++it ) - { - if ( it->second == token ) - return it->first; - } - return -1; - } - - void write_general() - { - mBuffer.set_max_bins ( mPile->max_barrels ); - mBuffer.set_max_wheelbarrows ( mPile->max_wheelbarrows ); - mBuffer.set_use_links_only ( mPile->use_links_only ); - mBuffer.set_unknown1 ( mPile->settings.unk1 ); - mBuffer.set_allow_inorganic ( mPile->settings.allow_inorganic ); - mBuffer.set_allow_organic ( mPile->settings.allow_organic ); - mBuffer.set_corpses ( mPile->settings.flags.bits.corpses ); - } - - void read_general() - { - if ( mBuffer.has_max_bins() ) - mPile->max_bins = mBuffer.max_bins(); - if ( mBuffer.has_max_wheelbarrows() ) - mPile->max_wheelbarrows = mBuffer.max_wheelbarrows(); - if ( mBuffer.has_max_barrels() ) - mPile->max_barrels = mBuffer.max_barrels(); - if ( mBuffer.has_use_links_only() ) - mPile->use_links_only = mBuffer.use_links_only(); - if ( mBuffer.has_unknown1() ) - mPile->settings.unk1 = mBuffer.unknown1(); - if ( mBuffer.has_allow_inorganic() ) - mPile->settings.allow_inorganic = mBuffer.allow_inorganic(); - if ( mBuffer.has_allow_organic() ) - mPile->settings.allow_organic = mBuffer.allow_organic(); - if ( mBuffer.has_corpses() ) - mPile->settings.flags.bits.corpses = mBuffer.corpses(); - } - - void write_animals() - { - StockpileSettings::AnimalsSet *animals= mBuffer.mutable_animals(); - animals->set_empty_cages ( mPile->settings.animals.empty_cages ); - animals->set_empty_traps ( mPile->settings.animals.empty_traps ); - for ( size_t i = 0; i < mPile->settings.animals.enabled.size(); ++i ) - { - if ( mPile->settings.animals.enabled.at ( i ) == 1 ) - { - df::creature_raw* r = find_creature ( i ); - debug() << "creature "<< r->creature_id << " " << i << endl; - animals->add_enabled ( r->creature_id ); - } - } - } - - void read_animals() - { - if ( mBuffer.has_animals() ) - { - mPile->settings.flags.bits.animals = 1; - debug() << "animals:" << endl; - mPile->settings.animals.empty_cages = mBuffer.animals().empty_cages(); - mPile->settings.animals.empty_traps = mBuffer.animals().empty_traps(); - - mPile->settings.animals.enabled.clear(); - mPile->settings.animals.enabled.resize ( world->raws.creatures.all.size(), '\0' ); - debug() << " pile has " << mPile->settings.animals.enabled.size() << endl; - for ( auto i = 0; i < mBuffer.animals().enabled_size(); ++i ) - { - std::string id = mBuffer.animals().enabled ( i ); - int idx = find_creature ( id ); - debug() << id << " " << idx << endl; - if ( idx < 0 || idx >= mPile->settings.animals.enabled.size() ) - { - debug() << "WARNING: animal index invalid: " << idx << endl; - continue; - } - mPile->settings.animals.enabled.at ( idx ) = ( char ) 1; - } - } - else - { - mPile->settings.animals.enabled.clear(); - mPile->settings.flags.bits.animals = 0; - mPile->settings.animals.empty_cages = false; - mPile->settings.animals.empty_traps = false; - } - } - - food_pair food_map ( organic_mat_category::organic_mat_category cat ) - { - using df::enums::organic_mat_category::organic_mat_category; - using namespace std::placeholders; - switch ( cat ) - { - case organic_mat_category::Meat: - { - FuncWriteExport setter = [=] ( const std::string &id ) - { - mBuffer.mutable_food()->add_meat ( id ); - }; - FuncReadImport getter = [=] ( size_t idx ) -> std::string { return mBuffer.food().meat ( idx ); }; - return food_pair ( setter, &mPile->settings.food.meat, getter, mBuffer.food().meat_size() ); - } - case organic_mat_category::Fish: - { - FuncWriteExport setter = [=] ( const std::string &id ) - { - mBuffer.mutable_food()->add_fish ( id ); - }; - FuncReadImport getter = [=] ( size_t idx ) -> std::string { return mBuffer.food().fish ( idx ); }; - return food_pair ( setter, &mPile->settings.food.fish, getter, mBuffer.food().fish_size() ); - } - case organic_mat_category::UnpreparedFish: - { - FuncWriteExport setter = [=] ( const std::string &id ) - { - mBuffer.mutable_food()->add_unprepared_fish ( id ); - }; - FuncReadImport getter = [=] ( size_t idx ) -> std::string { return mBuffer.food().unprepared_fish ( idx ); }; - return food_pair ( setter, &mPile->settings.food.unprepared_fish, getter, mBuffer.food().unprepared_fish_size() ); - } - case organic_mat_category::Eggs: - { - FuncWriteExport setter = [=] ( const std::string &id ) - { - mBuffer.mutable_food()->add_egg ( id ); - }; - FuncReadImport getter = [=] ( size_t idx ) -> std::string { return mBuffer.food().egg ( idx ); }; - return food_pair ( setter, &mPile->settings.food.egg, getter, mBuffer.food().egg_size() ); - } - case organic_mat_category::Plants: - { - FuncWriteExport setter = [=] ( const std::string &id ) - { - mBuffer.mutable_food()->add_plants ( id ); - }; - FuncReadImport getter = [=] ( size_t idx ) -> std::string { return mBuffer.food().plants ( idx ); }; - return food_pair ( setter, &mPile->settings.food.plants, getter, mBuffer.food().plants_size() ); - } - case organic_mat_category::PlantDrink: - { - FuncWriteExport setter = [=] ( const std::string &id ) - { - mBuffer.mutable_food()->add_drink_plant ( id ); - }; - FuncReadImport getter = [=] ( size_t idx ) -> std::string { return mBuffer.food().drink_plant ( idx ); }; - return food_pair ( setter, &mPile->settings.food.drink_plant, getter, mBuffer.food().drink_plant_size() ); - } - case organic_mat_category::CreatureDrink: - { - FuncWriteExport setter = [=] ( const std::string &id ) - { - mBuffer.mutable_food()->add_drink_animal ( id ); - }; - FuncReadImport getter = [=] ( size_t idx ) -> std::string { return mBuffer.food().drink_animal ( idx ); }; - return food_pair ( setter, &mPile->settings.food.drink_animal, getter, mBuffer.food().drink_animal_size() ); - } - case organic_mat_category::PlantCheese: - { - FuncWriteExport setter = [=] ( const std::string &id ) - { - mBuffer.mutable_food()->add_cheese_plant ( id ); - }; - FuncReadImport getter = [=] ( size_t idx ) -> std::string { return mBuffer.food().cheese_plant ( idx ); }; - return food_pair ( setter, &mPile->settings.food.cheese_plant, getter, mBuffer.food().cheese_plant_size() ); - } - case organic_mat_category::CreatureCheese: - { - FuncWriteExport setter = [=] ( const std::string &id ) - { - mBuffer.mutable_food()->add_cheese_animal ( id ); - }; - FuncReadImport getter = [=] ( size_t idx ) -> std::string { return mBuffer.food().cheese_animal ( idx ); }; - return food_pair ( setter, &mPile->settings.food.cheese_animal, getter, mBuffer.food().cheese_animal_size() ); - } - case organic_mat_category::Seed: - { - FuncWriteExport setter = [=] ( const std::string &id ) - { - mBuffer.mutable_food()->add_seeds ( id ); - }; - FuncReadImport getter = [=] ( size_t idx ) -> std::string { return mBuffer.food().seeds ( idx ); }; - return food_pair ( setter, &mPile->settings.food.seeds, getter, mBuffer.food().seeds_size() ); - } - case organic_mat_category::Leaf: - { - FuncWriteExport setter = [=] ( const std::string &id ) - { - mBuffer.mutable_food()->add_leaves ( id ); - }; - FuncReadImport getter = [=] ( size_t idx ) -> std::string { return mBuffer.food().leaves ( idx ); }; - return food_pair ( setter, &mPile->settings.food.leaves, getter, mBuffer.food().leaves_size() ); - } - case organic_mat_category::PlantPowder: - { - FuncWriteExport setter = [=] ( const std::string &id ) - { - mBuffer.mutable_food()->add_powder_plant ( id ); - }; - FuncReadImport getter = [=] ( size_t idx ) -> std::string { return mBuffer.food().powder_plant ( idx ); }; - return food_pair ( setter, &mPile->settings.food.powder_plant, getter, mBuffer.food().powder_plant_size() ); - } - case organic_mat_category::CreaturePowder: - { - FuncWriteExport setter = [=] ( const std::string &id ) - { - mBuffer.mutable_food()->add_powder_creature ( id ); - }; - FuncReadImport getter = [=] ( size_t idx ) -> std::string { return mBuffer.food().powder_creature ( idx ); }; - return food_pair ( setter, &mPile->settings.food.powder_creature, getter, mBuffer.food().powder_creature_size() ); - } - case organic_mat_category::Glob: - { - FuncWriteExport setter = [=] ( const std::string &id ) - { - mBuffer.mutable_food()->add_glob ( id ); - }; - FuncReadImport getter = [=] ( size_t idx ) -> std::string { return mBuffer.food().glob ( idx ); }; - return food_pair ( setter, &mPile->settings.food.glob, getter, mBuffer.food().glob_size() ); - } - case organic_mat_category::PlantLiquid: - { - FuncWriteExport setter = [=] ( const std::string &id ) - { - mBuffer.mutable_food()->add_liquid_plant ( id ); - }; - FuncReadImport getter = [=] ( size_t idx ) -> std::string { return mBuffer.food().liquid_plant ( idx ); }; - return food_pair ( setter, &mPile->settings.food.liquid_plant, getter, mBuffer.food().liquid_plant_size() ); - } - case organic_mat_category::CreatureLiquid: - { - FuncWriteExport setter = [=] ( const std::string &id ) - { - mBuffer.mutable_food()->add_liquid_animal ( id ); - }; - FuncReadImport getter = [=] ( size_t idx ) -> std::string { return mBuffer.food().liquid_animal ( idx ); }; - return food_pair ( setter, &mPile->settings.food.liquid_animal, getter, mBuffer.food().liquid_animal_size() ); - } - case organic_mat_category::MiscLiquid: - { - FuncWriteExport setter = [=] ( const std::string &id ) - { - mBuffer.mutable_food()->add_liquid_misc ( id ); - }; - FuncReadImport getter = [=] ( size_t idx ) -> std::string { return mBuffer.food().liquid_misc ( idx ); }; - return food_pair ( setter, &mPile->settings.food.liquid_misc, getter, mBuffer.food().liquid_misc_size() ); - } - - case organic_mat_category::Paste: - { - FuncWriteExport setter = [=] ( const std::string &id ) - { - mBuffer.mutable_food()->add_glob_paste ( id ); - }; - FuncReadImport getter = [=] ( size_t idx ) -> std::string { return mBuffer.food().glob_paste ( idx ); }; - return food_pair ( setter, &mPile->settings.food.glob_paste, getter, mBuffer.food().glob_paste_size() ); - } - case organic_mat_category::Pressed: - { - FuncWriteExport setter = [=] ( const std::string &id ) - { - mBuffer.mutable_food()->add_glob_pressed ( id ); - }; - FuncReadImport getter = [=] ( size_t idx ) -> std::string { return mBuffer.food().glob_pressed ( idx ); }; - return food_pair ( setter, &mPile->settings.food.glob_pressed, getter, mBuffer.food().glob_pressed_size() ); - } - case organic_mat_category::Leather: - case organic_mat_category::Silk: - case organic_mat_category::PlantFiber: - case organic_mat_category::Bone: - case organic_mat_category::Shell: - case organic_mat_category::Wood: - case organic_mat_category::Horn: - case organic_mat_category::Pearl: - case organic_mat_category::Tooth: - case organic_mat_category::EdibleCheese: - case organic_mat_category::AnyDrink: - case organic_mat_category::EdiblePlant: - case organic_mat_category::CookableLiquid: - case organic_mat_category::CookablePowder: - case organic_mat_category::CookableSeed: - case organic_mat_category::CookableLeaf: - case organic_mat_category::Yarn: - case organic_mat_category::MetalThread: - default: - // not used in stockpile food menu - break; - } - return food_pair(); - } - - - void write_food() - { - StockpileSettings::FoodSet *food = mBuffer.mutable_food(); - debug() << " food: " << endl; - food->set_prepared_meals ( mPile->settings.food.prepared_meals ); - - using df::enums::organic_mat_category::organic_mat_category; - df::enum_traits traits; - for ( int32_t mat_category = traits.first_item_value; mat_category traits; - if ( mBuffer.has_food() ) - { - mPile->settings.flags.bits.food = 1; - const StockpileSettings::FoodSet food = mBuffer.food(); - debug() << "food:" <settings.food.prepared_meals = food.prepared_meals(); - else - mPile->settings.food.prepared_meals = true; - - debug() << " prepared_meals: " << mPile->settings.food.prepared_meals << endl; - - for ( int32_t mat_category = traits.first_item_value; mat_category clear(); - } - mPile->settings.flags.bits.food = 0; - mPile->settings.food.prepared_meals = false; - } - } - - void furniture_setup_other_mats() - { - mOtherMatsFurniture.insert ( std::make_pair ( 0,"WOOD" ) ); - mOtherMatsFurniture.insert ( std::make_pair ( 1,"PLANT_CLOTH" ) ); - mOtherMatsFurniture.insert ( std::make_pair ( 2,"BONE" ) ); - mOtherMatsFurniture.insert ( std::make_pair ( 3,"TOOTH" ) ); - mOtherMatsFurniture.insert ( std::make_pair ( 4,"HORN" ) ); - mOtherMatsFurniture.insert ( std::make_pair ( 5,"PEARL" ) ); - mOtherMatsFurniture.insert ( std::make_pair ( 6,"SHELL" ) ); - mOtherMatsFurniture.insert ( std::make_pair ( 7,"LEATHER" ) ); - mOtherMatsFurniture.insert ( std::make_pair ( 8,"SILK" ) ); - mOtherMatsFurniture.insert ( std::make_pair ( 9,"AMBER" ) ); - mOtherMatsFurniture.insert ( std::make_pair ( 10,"CORAL" ) ); - mOtherMatsFurniture.insert ( std::make_pair ( 11,"GREEN_GLASS" ) ); - mOtherMatsFurniture.insert ( std::make_pair ( 12,"CLEAR_GLASS" ) ); - mOtherMatsFurniture.insert ( std::make_pair ( 13,"CRYSTAL_GLASS" ) ); - mOtherMatsFurniture.insert ( std::make_pair ( 14,"YARN" ) ); - } - - void write_furniture() - { - StockpileSettings::FurnitureSet *furniture= mBuffer.mutable_furniture(); - furniture->set_sand_bags ( mPile->settings.furniture.sand_bags ); - - // FURNITURE type - using df::enums::furniture_type::furniture_type; - df::enum_traits type_traits; - for ( size_t i = 0; i < mPile->settings.furniture.type.size(); ++i ) - { - if ( mPile->settings.furniture.type.at ( i ) ) - { - std::string f_type ( type_traits.key_table[i] ); - furniture->add_type ( f_type ); - debug() << "furniture_type " << i << " is " << f_type <add_mats ( token ); - }, mPile->settings.furniture.mats ); - - // other mats - serialize_list_other_mats ( mOtherMatsFurniture, [=] ( const std::string &token ) - { - furniture->add_other_mats ( token ); - }, mPile->settings.furniture.other_mats ); - - serialize_list_quality ( [=] ( const std::string &token ) - { - furniture->add_quality_core ( token ); - }, mPile->settings.furniture.quality_core ); - serialize_list_quality ( [=] ( const std::string &token ) - { - furniture->add_quality_total ( token ); - }, mPile->settings.furniture.quality_total ); - } - - bool furniture_mat_is_allowed ( const MaterialInfo &mi ) - { - return mi.isValid() && mi.material - && ( mi.material->flags.is_set ( material_flags::IS_METAL) - || mi.material->flags.is_set ( material_flags::IS_STONE )); - } - - void read_furniture() - { - if ( mBuffer.has_furniture() ) - { - mPile->settings.flags.bits.furniture = 1; - const StockpileSettings::FurnitureSet furniture = mBuffer.furniture(); - debug() << "furniture:" <settings.furniture.sand_bags = mBuffer.furniture().sand_bags(); - else - mPile->settings.furniture.sand_bags = false; - - // type - using df::enums::furniture_type::furniture_type; - df::enum_traits type_traits; - mPile->settings.furniture.type.clear(); - mPile->settings.furniture.type.resize ( type_traits.last_item_value+1, '\0' ); - if ( furniture.type_size() > 0 ) - { - for ( int i = 0; i < furniture.type_size(); ++i ) - { - const std::string type = furniture.type ( i ); - df::enum_traits::base_type idx = linear_index ( debug(), type_traits, type ); - debug() << " type " << idx << " is " << type << endl; - if ( idx < 0 || idx >= mPile->settings.furniture.type.size() ) - { - debug() << "WARNING: furniture type index invalid " << type << ", idx=" << idx << endl; - continue; - } - mPile->settings.furniture.type.at ( idx ) = 1; - } - } - - FuncMaterialAllowed filter = std::bind ( &StockpileSerializer::furniture_mat_is_allowed, this, _1 ); - unserialize_list_material ( filter, [=] ( const size_t & idx ) -> const std::string& - { - return furniture.mats ( idx ); - }, furniture.mats_size(), &mPile->settings.furniture.mats ); - - // other materials - unserialize_list_other_mats ( mOtherMatsFurniture, [=] ( const size_t & idx ) -> const std::string& - { - return furniture.other_mats ( idx ); - }, furniture.other_mats_size(), &mPile->settings.furniture.other_mats ); - - // core quality - unserialize_list_quality ( [=] ( const size_t & idx ) -> const std::string& - { - return furniture.quality_core ( idx ); - }, furniture.quality_core_size(), mPile->settings.furniture.quality_core ); - - // total quality - unserialize_list_quality ( [=] ( const size_t & idx ) -> const std::string& - { - return furniture.quality_total ( idx ); - }, furniture.quality_total_size(), mPile->settings.furniture.quality_total ); - - } - else - { - mPile->settings.flags.bits.furniture = 0; - mPile->settings.furniture.type.clear(); - mPile->settings.furniture.other_mats.clear(); - mPile->settings.furniture.mats.clear(); - quality_clear ( mPile->settings.furniture.quality_core ); - quality_clear ( mPile->settings.furniture.quality_total ); - } - } - - bool refuse_creature_is_allowed ( const df::creature_raw *raw ) - { - if ( !raw ) return false; - // wagon and generated creatures not allowed, except angels - const bool is_wagon = raw->creature_id == "EQUIPMENT_WAGON"; - const bool is_generated = raw->flags.is_set ( creature_raw_flags::GENERATED ); - const bool is_angel = is_generated && raw->creature_id.find ( "DIVINE_" ) != std::string::npos; - return !is_wagon && ! ( is_generated && !is_angel ); - } - - void refuse_write_helper ( std::function add_value, const vector & list ) - { - for ( size_t i = 0; i < list.size(); ++i ) - { - if ( list.at ( i ) == 1 ) - { - df::creature_raw* r = find_creature ( i ); - // skip forgotten beasts, titans, demons, and night creatures - if ( !refuse_creature_is_allowed ( r ) ) continue; - debug() << "creature "<< r->creature_id << " " << i << endl; - add_value ( r->creature_id ); - } - } - } - - bool refuse_type_is_allowed ( item_type::item_type type ) - { - if ( type == item_type::NONE - || type == item_type::BAR - || type == item_type::SMALLGEM - || type == item_type::BLOCKS - || type == item_type::ROUGH - || type == item_type::BOULDER - || type == item_type::CORPSE - || type == item_type::CORPSEPIECE - || type == item_type::ROCK - || type == item_type::ORTHOPEDIC_CAST - ) return false; - return true; - } - - - void write_refuse() - { - StockpileSettings::RefuseSet *refuse = mBuffer.mutable_refuse(); - refuse->set_fresh_raw_hide ( mPile->settings.refuse.fresh_raw_hide ); - refuse->set_rotten_raw_hide ( mPile->settings.refuse.rotten_raw_hide ); - - // type - FuncItemAllowed filter = std::bind ( &StockpileSerializer::refuse_type_is_allowed, this, _1 ); - serialize_list_item_type ( filter, [=] ( const std::string &token ) - { - refuse->add_type ( token ); - }, mPile->settings.refuse.type ); - - // corpses - refuse_write_helper ( [=] ( const std::string &id ) - { - refuse->add_corpses ( id ); - }, mPile->settings.refuse.corpses ); - // body_parts - refuse_write_helper ( [=] ( const std::string &id ) - { - refuse->add_body_parts ( id ); - }, mPile->settings.refuse.body_parts ); - // skulls - refuse_write_helper ( [=] ( const std::string &id ) - { - refuse->add_skulls ( id ); - }, mPile->settings.refuse.skulls ); - // bones - refuse_write_helper ( [=] ( const std::string &id ) - { - refuse->add_bones ( id ); - }, mPile->settings.refuse.bones ); - // hair - refuse_write_helper ( [=] ( const std::string &id ) - { - refuse->add_hair ( id ); - }, mPile->settings.refuse.hair ); - // shells - refuse_write_helper ( [=] ( const std::string &id ) - { - refuse->add_shells ( id ); - }, mPile->settings.refuse.shells ); - // teeth - refuse_write_helper ( [=] ( const std::string &id ) - { - refuse->add_teeth ( id ); - }, mPile->settings.refuse.teeth ); - // horns - refuse_write_helper ( [=] ( const std::string &id ) - { - refuse->add_horns ( id ); - }, mPile->settings.refuse.horns ); - } - - void refuse_read_helper ( std::function get_value, size_t list_size, std::vector* pile_list ) - { - pile_list->clear(); - pile_list->resize ( world->raws.creatures.all.size(), '\0' ); - if ( list_size > 0 ) - { - for ( size_t i = 0; i < list_size; ++i ) - { - const std::string creature_id = get_value ( i ); - const int idx = find_creature ( creature_id ); - const df::creature_raw* creature = find_creature ( idx ); - if ( idx < 0 || !refuse_creature_is_allowed ( creature ) || idx >= pile_list->size() ) - { - debug() << "WARNING invalid refuse creature " << creature_id << ", idx=" << idx << endl; - continue; - } - debug() << " creature " << idx << " is " << creature_id << endl; - pile_list->at ( idx ) = 1; - } - } - } - - - - void read_refuse() - { - if ( mBuffer.has_refuse() ) - { - mPile->settings.flags.bits.refuse = 1; - const StockpileSettings::RefuseSet refuse = mBuffer.refuse(); - debug() << "refuse: " <settings.refuse.fresh_raw_hide = refuse.fresh_raw_hide(); - mPile->settings.refuse.rotten_raw_hide = refuse.rotten_raw_hide(); - - // type - FuncItemAllowed filter = std::bind ( &StockpileSerializer::refuse_type_is_allowed, this, _1 ); - unserialize_list_item_type ( filter, [=] ( const size_t & idx ) -> const std::string& - { - return refuse.type ( idx ); - }, refuse.type_size(), &mPile->settings.refuse.type ); - - // corpses - debug() << " corpses" << endl; - refuse_read_helper ( [=] ( const size_t & idx ) -> const std::string& - { - return refuse.corpses ( idx ); - }, refuse.corpses_size(), &mPile->settings.refuse.corpses ); - // body_parts - debug() << " body_parts" << endl; - refuse_read_helper ( [=] ( const size_t & idx ) -> const std::string& - { - return refuse.body_parts ( idx ); - }, refuse.body_parts_size(), &mPile->settings.refuse.body_parts ); - // skulls - debug() << " skulls" << endl; - refuse_read_helper ( [=] ( const size_t & idx ) -> const std::string& - { - return refuse.skulls ( idx ); - }, refuse.skulls_size(), &mPile->settings.refuse.skulls ); - // bones - debug() << " bones" << endl; - refuse_read_helper ( [=] ( const size_t & idx ) -> const std::string& - { - return refuse.bones ( idx ); - }, refuse.bones_size(), &mPile->settings.refuse.bones ); - // hair - debug() << " hair" << endl; - refuse_read_helper ( [=] ( const size_t & idx ) -> const std::string& - { - return refuse.hair ( idx ); - }, refuse.hair_size(), &mPile->settings.refuse.hair ); - // shells - debug() << " shells" << endl; - refuse_read_helper ( [=] ( const size_t & idx ) -> const std::string& - { - return refuse.shells ( idx ); - }, refuse.shells_size(), &mPile->settings.refuse.shells ); - // teeth - debug() << " teeth" << endl; - refuse_read_helper ( [=] ( const size_t & idx ) -> const std::string& - { - return refuse.teeth ( idx ); - }, refuse.teeth_size(), &mPile->settings.refuse.teeth ); - // horns - debug() << " horns" << endl; - refuse_read_helper ( [=] ( const size_t & idx ) -> const std::string& - { - return refuse.horns ( idx ); - }, refuse.horns_size(), &mPile->settings.refuse.horns ); - } - else - { - mPile->settings.flags.bits.refuse = 0; - mPile->settings.refuse.type.clear(); - mPile->settings.refuse.corpses.clear(); - mPile->settings.refuse.body_parts.clear(); - mPile->settings.refuse.skulls.clear(); - mPile->settings.refuse.bones.clear(); - mPile->settings.refuse.hair.clear(); - mPile->settings.refuse.shells.clear(); - mPile->settings.refuse.teeth.clear(); - mPile->settings.refuse.horns.clear(); - mPile->settings.refuse.fresh_raw_hide = false; - mPile->settings.refuse.rotten_raw_hide = false; - } - } - - bool stone_is_allowed ( const MaterialInfo &mi ) - { - if ( !mi.isValid() ) return false; - const bool is_allowed_soil = mi.inorganic->flags.is_set ( inorganic_flags::SOIL ) && !mi.inorganic->flags.is_set ( inorganic_flags::AQUIFER ); - const bool is_allowed_stone = mi.material->flags.is_set ( material_flags::IS_STONE ) && !mi.material->flags.is_set ( material_flags::NO_STONE_STOCKPILE ); - return is_allowed_soil || is_allowed_stone; - } - - void write_stone() - { - StockpileSettings::StoneSet *stone= mBuffer.mutable_stone(); - - FuncMaterialAllowed filter = std::bind ( &StockpileSerializer::stone_is_allowed, this, _1 ); - serialize_list_material ( filter, [=] ( const std::string &token ) - { - stone->add_mats ( token ); - }, mPile->settings.stone.mats ); - } - - void read_stone() - { - if ( mBuffer.has_stone() ) - { - mPile->settings.flags.bits.stone = 1; - const StockpileSettings::StoneSet stone = mBuffer.stone(); - debug() << "stone: " < const std::string& - { - return stone.mats ( idx ); - }, stone.mats_size(), &mPile->settings.stone.mats ); - } - else - { - mPile->settings.flags.bits.stone = 0; - mPile->settings.stone.mats.clear(); - } - } - - bool ammo_mat_is_allowed ( const MaterialInfo &mi ) - { - return mi.isValid() && mi.material && mi.material->flags.is_set ( material_flags::IS_METAL ); - } - - void write_ammo() - { - StockpileSettings::AmmoSet *ammo = mBuffer.mutable_ammo(); - - // ammo type - serialize_list_itemdef ( [=] ( const std::string &token ) - { - ammo->add_type ( token ); - }, mPile->settings.ammo.type, - std::vector ( world->raws.itemdefs.ammo.begin(),world->raws.itemdefs.ammo.end() ), - item_type::AMMO ); - - // metal - FuncMaterialAllowed filter = std::bind ( &StockpileSerializer::ammo_mat_is_allowed, this, _1 ); - serialize_list_material ( filter, [=] ( const std::string &token ) - { - ammo->add_mats ( token ); - }, mPile->settings.ammo.mats ); - - // other mats - only wood and bone - if ( mPile->settings.ammo.other_mats.size() > 2 ) - { - debug() << "WARNING: ammo other materials > 2! " << mPile->settings.ammo.other_mats.size() << endl; - } - - for ( size_t i = 0; i < std::min ( size_t ( 2 ), mPile->settings.ammo.other_mats.size() ); ++i ) - { - if ( !mPile->settings.ammo.other_mats.at ( i ) ) - continue; - const std::string token = i == 0 ? "WOOD" : "BONE"; - ammo->add_other_mats ( token ); - debug() << " other mats " << i << " is " << token << endl; - } - - // quality core - serialize_list_quality ( [=] ( const std::string &token ) - { - ammo->add_quality_core ( token ); - }, mPile->settings.ammo.quality_core ); - - // quality total - serialize_list_quality ( [=] ( const std::string &token ) - { - ammo->add_quality_total ( token ); - }, mPile->settings.ammo.quality_total ); - } - - void read_ammo() - { - if ( mBuffer.has_ammo() ) - { - mPile->settings.flags.bits.ammo = 1; - const StockpileSettings::AmmoSet ammo = mBuffer.ammo(); - debug() << "ammo: " < const std::string& - { - return ammo.type ( idx ); - }, ammo.type_size(), &mPile->settings.ammo.type, item_type::AMMO ); - - // materials metals - FuncMaterialAllowed filter = std::bind ( &StockpileSerializer::ammo_mat_is_allowed, this, _1 ); - unserialize_list_material ( filter, [=] ( const size_t & idx ) -> const std::string& - { - return ammo.mats ( idx ); - }, ammo.mats_size(), &mPile->settings.ammo.mats ); - - // others - mPile->settings.ammo.other_mats.clear(); - mPile->settings.ammo.other_mats.resize ( 2, '\0' ); - if ( ammo.other_mats_size() > 0 ) - { - // TODO remove hardcoded value - for ( int i = 0; i < ammo.other_mats_size(); ++i ) - { - const std::string token = ammo.other_mats ( i ); - const int32_t idx = token == "WOOD" ? 0 : token == "BONE" ? 1 : -1; - debug() << " other mats " << idx << " is " << token << endl; - if ( idx != -1 ) - mPile->settings.ammo.other_mats.at ( idx ) = 1; - } - } - - // core quality - unserialize_list_quality ( [=] ( const size_t & idx ) -> const std::string& - { - return ammo.quality_core ( idx ); - }, ammo.quality_core_size(), mPile->settings.ammo.quality_core ); - - // total quality - unserialize_list_quality ( [=] ( const size_t & idx ) -> const std::string& - { - return ammo.quality_total ( idx ); - }, ammo.quality_total_size(), mPile->settings.ammo.quality_total ); - } - else - { - mPile->settings.flags.bits.ammo = 0; - mPile->settings.ammo.type.clear(); - mPile->settings.ammo.mats.clear(); - mPile->settings.ammo.other_mats.clear(); - quality_clear ( mPile->settings.ammo.quality_core ); - quality_clear ( mPile->settings.ammo.quality_total ); - } - } - - bool coins_mat_is_allowed ( const MaterialInfo &mi ) - { - return mi.isValid(); - } - - void write_coins() - { - StockpileSettings::CoinSet *coins = mBuffer.mutable_coin(); - FuncMaterialAllowed filter = std::bind ( &StockpileSerializer::coins_mat_is_allowed, this, _1 ); - serialize_list_material ( filter, [=] ( const std::string &token ) - { - coins->add_mats ( token ); - }, mPile->settings.coins.mats ); - } - - void read_coins() - { - if ( mBuffer.has_coin() ) - { - mPile->settings.flags.bits.coins = 1; - const StockpileSettings::CoinSet coins = mBuffer.coin(); - debug() << "coins: " < const std::string& - { - return coins.mats ( idx ); - }, coins.mats_size(), &mPile->settings.coins.mats ); - } - else - { - mPile->settings.flags.bits.coins = 0; - mPile->settings.coins.mats.clear(); - } - } - - void bars_blocks_setup_other_mats() - { - mOtherMatsBars.insert ( std::make_pair ( 0,"COAL" ) ); - mOtherMatsBars.insert ( std::make_pair ( 1,"POTASH" ) ); - mOtherMatsBars.insert ( std::make_pair ( 2,"ASH" ) ); - mOtherMatsBars.insert ( std::make_pair ( 3,"PEARLASH" ) ); - mOtherMatsBars.insert ( std::make_pair ( 4,"SOAP" ) ); - - mOtherMatsBlocks.insert ( std::make_pair ( 0,"GREEN_GLASS" ) ); - mOtherMatsBlocks.insert ( std::make_pair ( 1,"CLEAR_GLASS" ) ); - mOtherMatsBlocks.insert ( std::make_pair ( 2,"CRYSTAL_GLASS" ) ); - mOtherMatsBlocks.insert ( std::make_pair ( 3,"WOOD" ) ); - } - - bool bars_mat_is_allowed ( const MaterialInfo &mi ) - { - return mi.isValid() && mi.material && mi.material->flags.is_set ( material_flags::IS_METAL ); - } - - bool blocks_mat_is_allowed ( const MaterialInfo &mi ) - { - return mi.isValid() && mi.material - && ( mi.material->flags.is_set ( material_flags::IS_METAL) - || mi.material->flags.is_set ( material_flags::IS_STONE )); - } - - - void write_bars_blocks() - { - StockpileSettings::BarsBlocksSet *bars_blocks = mBuffer.mutable_barsblocks(); - MaterialInfo mi; - FuncMaterialAllowed filter = std::bind ( &StockpileSerializer::bars_mat_is_allowed, this, _1 ); - serialize_list_material ( filter, [=] ( const std::string &token ) - { - bars_blocks->add_bars_mats ( token ); - }, mPile->settings.bars_blocks.bars_mats ); - - // blocks mats - filter = std::bind ( &StockpileSerializer::blocks_mat_is_allowed, this, _1 ); - serialize_list_material ( filter, [=] ( const std::string &token ) - { - bars_blocks->add_blocks_mats ( token ); - }, mPile->settings.bars_blocks.blocks_mats ); - - // bars other mats - serialize_list_other_mats ( mOtherMatsBars, [=] ( const std::string &token ) - { - bars_blocks->add_bars_other_mats ( token ); - }, mPile->settings.bars_blocks.bars_other_mats ); - - // blocks other mats - serialize_list_other_mats ( mOtherMatsBlocks, [=] ( const std::string &token ) - { - bars_blocks->add_blocks_other_mats ( token ); - }, mPile->settings.bars_blocks.blocks_other_mats ); - } - - void read_bars_blocks() - { - if ( mBuffer.has_barsblocks() ) - { - mPile->settings.flags.bits.bars_blocks = 1; - const StockpileSettings::BarsBlocksSet bars_blocks = mBuffer.barsblocks(); - debug() << "bars_blocks: " < const std::string& - { - return bars_blocks.bars_mats ( idx ); - }, bars_blocks.bars_mats_size(), &mPile->settings.bars_blocks.bars_mats ); - - // blocks - filter = std::bind ( &StockpileSerializer::blocks_mat_is_allowed, this, _1 ); - unserialize_list_material ( filter, [=] ( const size_t & idx ) -> const std::string& - { - return bars_blocks.blocks_mats ( idx ); - }, bars_blocks.blocks_mats_size(), &mPile->settings.bars_blocks.blocks_mats ); - // bars other mats - unserialize_list_other_mats ( mOtherMatsBars, [=] ( const size_t & idx ) -> const std::string& - { - return bars_blocks.bars_other_mats ( idx ); - }, bars_blocks.bars_other_mats_size(), &mPile->settings.bars_blocks.bars_other_mats ); - - - // blocks other mats - unserialize_list_other_mats ( mOtherMatsBlocks, [=] ( const size_t & idx ) -> const std::string& - { - return bars_blocks.blocks_other_mats ( idx ); - }, bars_blocks.blocks_other_mats_size(), &mPile->settings.bars_blocks.blocks_other_mats ); - - } - else - { - mPile->settings.flags.bits.bars_blocks = 0; - mPile->settings.bars_blocks.bars_other_mats.clear(); - mPile->settings.bars_blocks.bars_mats.clear(); - mPile->settings.bars_blocks.blocks_other_mats.clear(); - mPile->settings.bars_blocks.blocks_mats.clear(); - } - } - - bool gem_mat_is_allowed ( const MaterialInfo &mi ) - { - return mi.isValid() && mi.material && mi.material->flags.is_set ( material_flags::IS_GEM ); - } - bool gem_cut_mat_is_allowed ( const MaterialInfo &mi ) - { - return mi.isValid() && mi.material && ( mi.material->flags.is_set ( material_flags::IS_GEM ) || mi.material->flags.is_set ( material_flags::IS_STONE ) ) ; - } - bool gem_other_mat_is_allowed(MaterialInfo &mi ) { - return mi.isValid() && (mi.getToken() == "GLASS_GREEN" || mi.getToken() == "GLASS_CLEAR" || mi.getToken() == "GLASS_CRYSTAL"); - } - - void write_gems() - { - StockpileSettings::GemsSet *gems = mBuffer.mutable_gems(); - MaterialInfo mi; - // rough mats - FuncMaterialAllowed filter_rough = std::bind ( &StockpileSerializer::gem_mat_is_allowed, this, _1 ); - serialize_list_material ( filter_rough, [=] ( const std::string &token ) - { - gems->add_rough_mats ( token ); - }, mPile->settings.gems.rough_mats ); - // cut mats - FuncMaterialAllowed filter_cut = std::bind ( &StockpileSerializer::gem_cut_mat_is_allowed, this, _1 ); - serialize_list_material ( filter_cut, [=] ( const std::string &token ) - { - gems->add_cut_mats ( token ); - }, mPile->settings.gems.cut_mats ); - // rough other - for ( size_t i = 0; i < mPile->settings.gems.rough_other_mats.size(); ++i ) - { - if ( mPile->settings.gems.rough_other_mats.at ( i ) ) - { - mi.decode ( i, -1 ); - if ( !gem_other_mat_is_allowed(mi) ) continue; - debug() << " gem rough_other mat" << i << " is " << mi.getToken() << endl; - gems->add_rough_other_mats ( mi.getToken() ); - } - } - // cut other - for ( size_t i = 0; i < mPile->settings.gems.cut_other_mats.size(); ++i ) - { - if ( mPile->settings.gems.cut_other_mats.at ( i ) ) - { - mi.decode ( i, -1 ); - if ( !mi.isValid() ) mi.decode ( 0, i ); - if ( !gem_other_mat_is_allowed(mi) ) continue; - debug() << " gem cut_other mat" << i << " is " << mi.getToken() << endl; - gems->add_cut_other_mats ( mi.getToken() ); - } - } - } - - void read_gems() - { - if ( mBuffer.has_gems() ) - { - mPile->settings.flags.bits.gems = 1; - const StockpileSettings::GemsSet gems = mBuffer.gems(); - debug() << "gems: " < const std::string& - { - return gems.rough_mats ( idx ); - }, gems.rough_mats_size(), &mPile->settings.gems.rough_mats ); - - // cut - FuncMaterialAllowed filter_cut = std::bind ( &StockpileSerializer::gem_cut_mat_is_allowed, this, _1 ); - unserialize_list_material ( filter_cut, [=] ( const size_t & idx ) -> const std::string& - { - return gems.cut_mats ( idx ); - }, gems.cut_mats_size(), &mPile->settings.gems.cut_mats ); - - const size_t builtin_size = std::extentraws.mat_table.builtin ) >::value; - // rough other - mPile->settings.gems.rough_other_mats.clear(); - mPile->settings.gems.rough_other_mats.resize ( builtin_size, '\0' ); - for ( int i = 0; i < gems.rough_other_mats_size(); ++i ) - { - const std::string token = gems.rough_other_mats ( i ); - MaterialInfo mi; - mi.find ( token ); - if ( !mi.isValid() || mi.type >= builtin_size ) - { - debug() << "WARNING: invalid gem mat " << token << ". idx=" << mi.type << endl; - continue; - } - debug() << " rough_other mats " << mi.type << " is " << token << endl; - mPile->settings.gems.rough_other_mats.at ( mi.type ) = 1; - } - - // cut other - mPile->settings.gems.cut_other_mats.clear(); - mPile->settings.gems.cut_other_mats.resize ( builtin_size, '\0' ); - for ( int i = 0; i < gems.cut_other_mats_size(); ++i ) - { - const std::string token = gems.cut_other_mats ( i ); - MaterialInfo mi; - mi.find ( token ); - if ( !mi.isValid() || mi.type >= builtin_size ) - { - debug() << "WARNING: invalid gem mat " << token << ". idx=" << mi.type << endl; - continue; - } - debug() << " cut_other mats " << mi.type << " is " << token << endl; - mPile->settings.gems.cut_other_mats.at ( mi.type ) = 1; } - } - else - { - mPile->settings.flags.bits.gems = 0; - mPile->settings.gems.cut_other_mats.clear(); - mPile->settings.gems.cut_mats.clear(); - mPile->settings.gems.rough_other_mats.clear(); - mPile->settings.gems.rough_mats.clear(); - } - } - - bool finished_goods_type_is_allowed ( item_type::item_type type ) - { - switch ( type ) - { - case item_type::CHAIN: - case item_type::FLASK: - case item_type::GOBLET: - case item_type::INSTRUMENT: - case item_type::TOY: - case item_type::ARMOR: - case item_type::SHOES: - case item_type::HELM: - case item_type::GLOVES: - case item_type::FIGURINE: - case item_type::AMULET: - case item_type::SCEPTER: - case item_type::CROWN: - case item_type::RING: - case item_type::EARRING: - case item_type::BRACELET: - case item_type::GEM: - case item_type::TOTEM: - case item_type::PANTS: - case item_type::BACKPACK: - case item_type::QUIVER: - case item_type::SPLINT: - case item_type::CRUTCH: - case item_type::TOOL: - case item_type::BOOK: - return true; - default: - return false; - } - - } - - void finished_goods_setup_other_mats() - { - mOtherMatsFinishedGoods.insert ( std::make_pair ( 0,"WOOD" ) ); - mOtherMatsFinishedGoods.insert ( std::make_pair ( 1,"PLANT_CLOTH" ) ); - mOtherMatsFinishedGoods.insert ( std::make_pair ( 2,"BONE" ) ); - mOtherMatsFinishedGoods.insert ( std::make_pair ( 3,"TOOTH" ) ); - mOtherMatsFinishedGoods.insert ( std::make_pair ( 4,"HORN" ) ); - mOtherMatsFinishedGoods.insert ( std::make_pair ( 5,"PEARL" ) ); - mOtherMatsFinishedGoods.insert ( std::make_pair ( 6,"SHELL" ) ); - mOtherMatsFinishedGoods.insert ( std::make_pair ( 7,"LEATHER" ) ); - mOtherMatsFinishedGoods.insert ( std::make_pair ( 8,"SILK" ) ); - mOtherMatsFinishedGoods.insert ( std::make_pair ( 9,"AMBER" ) ); - mOtherMatsFinishedGoods.insert ( std::make_pair ( 10,"CORAL" ) ); - mOtherMatsFinishedGoods.insert ( std::make_pair ( 11,"GREEN_GLASS" ) ); - mOtherMatsFinishedGoods.insert ( std::make_pair ( 12,"CLEAR_GLASS" ) ); - mOtherMatsFinishedGoods.insert ( std::make_pair ( 13,"CRYSTAL_GLASS" ) ); - mOtherMatsFinishedGoods.insert ( std::make_pair ( 14,"YARN" ) ); - mOtherMatsFinishedGoods.insert ( std::make_pair ( 15,"WAX" ) ); - } - - bool finished_goods_mat_is_allowed ( const MaterialInfo &mi ) - { - return mi.isValid() - && mi.material - && ( mi.material->flags.is_set ( material_flags::IS_GEM ) - || mi.material->flags.is_set ( material_flags::IS_METAL ) - || mi.material->flags.is_set ( material_flags::IS_STONE ) ) ; - } - - void write_finished_goods() - { - StockpileSettings::FinishedGoodsSet *finished_goods = mBuffer.mutable_finished_goods(); - - // type - FuncItemAllowed filter = std::bind ( &StockpileSerializer::finished_goods_type_is_allowed, this, _1 ); - serialize_list_item_type ( filter, [=] ( const std::string &token ) - { - finished_goods->add_type ( token ); - }, mPile->settings.finished_goods.type ); - - // materials - FuncMaterialAllowed mat_filter = std::bind ( &StockpileSerializer::finished_goods_mat_is_allowed, this, _1 ); - serialize_list_material ( mat_filter, [=] ( const std::string &token ) - { - finished_goods->add_mats ( token ); - }, mPile->settings.finished_goods.mats ); - - // other mats - serialize_list_other_mats ( mOtherMatsFinishedGoods, [=] ( const std::string &token ) - { - finished_goods->add_other_mats ( token ); - }, mPile->settings.finished_goods.other_mats ); - - // quality core - serialize_list_quality ( [=] ( const std::string &token ) - { - finished_goods->add_quality_core ( token ); - }, mPile->settings.finished_goods.quality_core ); - - // quality total - serialize_list_quality ( [=] ( const std::string &token ) - { - finished_goods->add_quality_total ( token ); - }, mPile->settings.finished_goods.quality_total ); - } - - void read_finished_goods() - { - if ( mBuffer.has_finished_goods() ) - { - mPile->settings.flags.bits.finished_goods = 1; - const StockpileSettings::FinishedGoodsSet finished_goods = mBuffer.finished_goods(); - debug() << "finished_goods: " < const std::string& - { - return finished_goods.type ( idx ); - }, finished_goods.type_size(), &mPile->settings.finished_goods.type ); - - // materials - FuncMaterialAllowed mat_filter = std::bind ( &StockpileSerializer::finished_goods_mat_is_allowed, this, _1 ); - unserialize_list_material ( mat_filter, [=] ( const size_t & idx ) -> const std::string& - { - return finished_goods.mats ( idx ); - }, finished_goods.mats_size(), &mPile->settings.finished_goods.mats ); - - // other mats - unserialize_list_other_mats ( mOtherMatsFinishedGoods, [=] ( const size_t & idx ) -> const std::string& - { - return finished_goods.other_mats ( idx ); - }, finished_goods.other_mats_size(), &mPile->settings.finished_goods.other_mats ); - - // core quality - unserialize_list_quality ( [=] ( const size_t & idx ) -> const std::string& - { - return finished_goods.quality_core ( idx ); - }, finished_goods.quality_core_size(), mPile->settings.finished_goods.quality_core ); - - // total quality - unserialize_list_quality ( [=] ( const size_t & idx ) -> const std::string& - { - return finished_goods.quality_total ( idx ); - }, finished_goods.quality_total_size(), mPile->settings.finished_goods.quality_total ); - - } - else - { - mPile->settings.flags.bits.finished_goods = 0; - mPile->settings.finished_goods.type.clear(); - mPile->settings.finished_goods.other_mats.clear(); - mPile->settings.finished_goods.mats.clear(); - quality_clear ( mPile->settings.finished_goods.quality_core ); - quality_clear ( mPile->settings.finished_goods.quality_total ); - } - } - - void write_leather() - { - StockpileSettings::LeatherSet *leather = mBuffer.mutable_leather(); - - FuncWriteExport setter = [=] ( const std::string &id ) - { - leather->add_mats ( id ); - }; - serialize_list_organic_mat ( setter, &mPile->settings.leather.mats, organic_mat_category::Leather ); - } - void read_leather() - { - if ( mBuffer.has_leather() ) - { - mPile->settings.flags.bits.leather = 1; - const StockpileSettings::LeatherSet leather = mBuffer.leather(); - debug() << "leather: " < std::string - { - return leather.mats ( idx ); - }, leather.mats_size(), &mPile->settings.leather.mats, organic_mat_category::Leather ); - } - else - { - mPile->settings.flags.bits.leather = 0; - mPile->settings.leather.mats.clear(); - } - } - - void write_cloth() - { - StockpileSettings::ClothSet * cloth = mBuffer.mutable_cloth(); - - serialize_list_organic_mat ( [=] ( const std::string &token ) - { - cloth->add_thread_silk ( token ); - }, &mPile->settings.cloth.thread_silk, organic_mat_category::Silk ); - - serialize_list_organic_mat ( [=] ( const std::string &token ) - { - cloth->add_thread_plant ( token ); - }, &mPile->settings.cloth.thread_plant, organic_mat_category::PlantFiber ); - - serialize_list_organic_mat ( [=] ( const std::string &token ) - { - cloth->add_thread_yarn ( token ); - }, &mPile->settings.cloth.thread_yarn, organic_mat_category::Yarn ); - - serialize_list_organic_mat ( [=] ( const std::string &token ) - { - cloth->add_thread_metal ( token ); - }, &mPile->settings.cloth.thread_metal, organic_mat_category::MetalThread ); - - serialize_list_organic_mat ( [=] ( const std::string &token ) - { - cloth->add_cloth_silk ( token ); - }, &mPile->settings.cloth.cloth_silk, organic_mat_category::Silk ); - - serialize_list_organic_mat ( [=] ( const std::string &token ) - { - cloth->add_cloth_plant ( token ); - }, &mPile->settings.cloth.cloth_plant, organic_mat_category::PlantFiber ); - - serialize_list_organic_mat ( [=] ( const std::string &token ) - { - cloth->add_cloth_yarn ( token ); - }, &mPile->settings.cloth.cloth_yarn, organic_mat_category::Yarn ); - - serialize_list_organic_mat ( [=] ( const std::string &token ) - { - cloth->add_cloth_metal ( token ); - }, &mPile->settings.cloth.cloth_metal, organic_mat_category::MetalThread ); - - } - void read_cloth() - { - if ( mBuffer.has_cloth() ) - { - mPile->settings.flags.bits.cloth = 1; - const StockpileSettings::ClothSet cloth = mBuffer.cloth(); - debug() << "cloth: " < std::string - { - return cloth.thread_silk ( idx ); - }, cloth.thread_silk_size(), &mPile->settings.cloth.thread_silk, organic_mat_category::Silk ); - - unserialize_list_organic_mat ( [=] ( size_t idx ) -> std::string - { - return cloth.thread_plant ( idx ); - }, cloth.thread_plant_size(), &mPile->settings.cloth.thread_plant, organic_mat_category::PlantFiber ); - - unserialize_list_organic_mat ( [=] ( size_t idx ) -> std::string - { - return cloth.thread_yarn ( idx ); - }, cloth.thread_yarn_size(), &mPile->settings.cloth.thread_yarn, organic_mat_category::Yarn ); - - unserialize_list_organic_mat ( [=] ( size_t idx ) -> std::string - { - return cloth.thread_metal ( idx ); - }, cloth.thread_metal_size(), &mPile->settings.cloth.thread_metal, organic_mat_category::MetalThread ); - - unserialize_list_organic_mat ( [=] ( size_t idx ) -> std::string - { - return cloth.cloth_silk ( idx ); - }, cloth.cloth_silk_size(), &mPile->settings.cloth.cloth_silk, organic_mat_category::Silk ); - - unserialize_list_organic_mat ( [=] ( size_t idx ) -> std::string - { - return cloth.cloth_plant ( idx ); - }, cloth.cloth_plant_size(), &mPile->settings.cloth.cloth_plant, organic_mat_category::PlantFiber ); - - unserialize_list_organic_mat ( [=] ( size_t idx ) -> std::string - { - return cloth.cloth_yarn ( idx ); - }, cloth.cloth_yarn_size(), &mPile->settings.cloth.cloth_yarn, organic_mat_category::Yarn ); - - unserialize_list_organic_mat ( [=] ( size_t idx ) -> std::string - { - return cloth.cloth_metal ( idx ); - }, cloth.cloth_metal_size(), &mPile->settings.cloth.cloth_metal, organic_mat_category::MetalThread ); - } - else - { - mPile->settings.cloth.thread_metal.clear(); - mPile->settings.cloth.thread_plant.clear(); - mPile->settings.cloth.thread_silk.clear(); - mPile->settings.cloth.thread_yarn.clear(); - mPile->settings.cloth.cloth_metal.clear(); - mPile->settings.cloth.cloth_plant.clear(); - mPile->settings.cloth.cloth_silk.clear(); - mPile->settings.cloth.cloth_yarn.clear(); - mPile->settings.flags.bits.cloth = 0; - } - } - - bool wood_mat_is_allowed ( const df::plant_raw * plant ) - { - return plant && plant->flags.is_set ( plant_raw_flags::TREE ); - } - - void write_wood() - { - StockpileSettings::WoodSet * wood = mBuffer.mutable_wood(); - for ( size_t i = 0; i < mPile->settings.wood.mats.size(); ++i ) - { - if ( mPile->settings.wood.mats.at ( i ) ) - { - const df::plant_raw * plant = find_plant ( i ); - if ( !wood_mat_is_allowed ( plant ) ) continue; - wood->add_mats ( plant->id ); - debug() << " plant " << i << " is " << plant->id << endl; - } - } - } - void read_wood() - { - if ( mBuffer.has_wood() ) - { - mPile->settings.flags.bits.wood = 1; - const StockpileSettings::WoodSet wood = mBuffer.wood(); - debug() << "wood: " <settings.wood.mats.clear(); - mPile->settings.wood.mats.resize ( world->raws.plants.all.size(), '\0' ); - for ( int i = 0; i < wood.mats_size(); ++i ) - { - const std::string token = wood.mats ( i ); - const size_t idx = find_plant ( token ); - if ( idx < 0 || idx >= mPile->settings.wood.mats.size() ) - { - debug() << "WARNING wood mat index invalid " << token << ", idx=" << idx << endl; - continue; - } - debug() << " plant " << idx << " is " << token << endl; - mPile->settings.wood.mats.at ( idx ) = 1; - } - } - else - { - mPile->settings.flags.bits.wood = 0; - mPile->settings.wood.mats.clear(); - } - } - - bool weapons_mat_is_allowed ( const MaterialInfo &mi ) - { - return mi.isValid() && mi.material && ( - mi.material->flags.is_set ( material_flags::IS_METAL ) || - mi.material->flags.is_set ( material_flags::IS_STONE ) ); - - } - - void write_weapons() - { - StockpileSettings::WeaponsSet * weapons = mBuffer.mutable_weapons(); - - weapons->set_unusable ( mPile->settings.weapons.unusable ); - weapons->set_usable ( mPile->settings.weapons.usable ); - - // weapon type - serialize_list_itemdef ( [=] ( const std::string &token ) - { - weapons->add_weapon_type ( token ); - }, mPile->settings.weapons.weapon_type, - std::vector ( world->raws.itemdefs.weapons.begin(),world->raws.itemdefs.weapons.end() ), - item_type::WEAPON ); - - // trapcomp type - serialize_list_itemdef ( [=] ( const std::string &token ) - { - weapons->add_trapcomp_type ( token ); - }, mPile->settings.weapons.trapcomp_type, - std::vector ( world->raws.itemdefs.trapcomps.begin(),world->raws.itemdefs.trapcomps.end() ), - item_type::TRAPCOMP ); - - // materials - FuncMaterialAllowed mat_filter = std::bind ( &StockpileSerializer::weapons_mat_is_allowed, this, _1 ); - serialize_list_material ( mat_filter, [=] ( const std::string &token ) - { - weapons->add_mats ( token ); - }, mPile->settings.weapons.mats ); - - // other mats - serialize_list_other_mats ( mOtherMatsWeaponsArmor, [=] ( const std::string &token ) - { - weapons->add_other_mats ( token ); - }, mPile->settings.weapons.other_mats ); - - // quality core - serialize_list_quality ( [=] ( const std::string &token ) - { - weapons->add_quality_core ( token ); - }, mPile->settings.weapons.quality_core ); - - // quality total - serialize_list_quality ( [=] ( const std::string &token ) - { - weapons->add_quality_total ( token ); - }, mPile->settings.weapons.quality_total ); - } - - void read_weapons() - { - if ( mBuffer.has_weapons() ) - { - mPile->settings.flags.bits.weapons = 1; - const StockpileSettings::WeaponsSet weapons = mBuffer.weapons(); - debug() << "weapons: " < const std::string& - { - return weapons.weapon_type ( idx ); - }, weapons.weapon_type_size(), &mPile->settings.weapons.weapon_type, item_type::WEAPON ); - - // trapcomp type - unserialize_list_itemdef ( [=] ( const size_t & idx ) -> const std::string& - { - return weapons.trapcomp_type ( idx ); - }, weapons.trapcomp_type_size(), &mPile->settings.weapons.trapcomp_type, item_type::TRAPCOMP ); - - // materials - FuncMaterialAllowed mat_filter = std::bind ( &StockpileSerializer::weapons_mat_is_allowed, this, _1 ); - unserialize_list_material ( mat_filter, [=] ( const size_t & idx ) -> const std::string& - { - return weapons.mats ( idx ); - }, weapons.mats_size(), &mPile->settings.weapons.mats ); - - // other mats - unserialize_list_other_mats ( mOtherMatsWeaponsArmor, [=] ( const size_t & idx ) -> const std::string& - { - return weapons.other_mats ( idx ); - }, weapons.other_mats_size(), &mPile->settings.weapons.other_mats ); - - - // core quality - unserialize_list_quality ( [=] ( const size_t & idx ) -> const std::string& - { - return weapons.quality_core ( idx ); - }, weapons.quality_core_size(), mPile->settings.weapons.quality_core ); - // total quality - unserialize_list_quality ( [=] ( const size_t & idx ) -> const std::string& - { - return weapons.quality_total ( idx ); - }, weapons.quality_total_size(), mPile->settings.weapons.quality_total ); - } - else - { - mPile->settings.flags.bits.weapons = 0; - mPile->settings.weapons.weapon_type.clear(); - mPile->settings.weapons.trapcomp_type.clear(); - mPile->settings.weapons.other_mats.clear(); - mPile->settings.weapons.mats.clear(); - quality_clear ( mPile->settings.weapons.quality_core ); - quality_clear ( mPile->settings.weapons.quality_total ); - } - - } - - void weapons_armor_setup_other_mats() - { - mOtherMatsWeaponsArmor.insert ( std::make_pair ( 0,"WOOD" ) ); - mOtherMatsWeaponsArmor.insert ( std::make_pair ( 1,"PLANT_CLOTH" ) ); - mOtherMatsWeaponsArmor.insert ( std::make_pair ( 2,"BONE" ) ); - mOtherMatsWeaponsArmor.insert ( std::make_pair ( 3,"SHELL" ) ); - mOtherMatsWeaponsArmor.insert ( std::make_pair ( 4,"LEATHER" ) ); - mOtherMatsWeaponsArmor.insert ( std::make_pair ( 5,"SILK" ) ); - mOtherMatsWeaponsArmor.insert ( std::make_pair ( 6,"GREEN_GLASS" ) ); - mOtherMatsWeaponsArmor.insert ( std::make_pair ( 7,"CLEAR_GLASS" ) ); - mOtherMatsWeaponsArmor.insert ( std::make_pair ( 8,"CRYSTAL_GLASS" ) ); - mOtherMatsWeaponsArmor.insert ( std::make_pair ( 9,"YARN" ) ); - } - - bool armor_mat_is_allowed ( const MaterialInfo &mi ) - { - return mi.isValid() && mi.material && mi.material->flags.is_set ( material_flags::IS_METAL ); - } - - void write_armor() - { - StockpileSettings::ArmorSet * armor = mBuffer.mutable_armor(); - - armor->set_unusable ( mPile->settings.armor.unusable ); - armor->set_usable ( mPile->settings.armor.usable ); - - // armor type - serialize_list_itemdef ( [=] ( const std::string &token ) - { - armor->add_body ( token ); - }, mPile->settings.armor.body, - std::vector ( world->raws.itemdefs.armor.begin(),world->raws.itemdefs.armor.end() ), - item_type::ARMOR ); - - // helm type - serialize_list_itemdef ( [=] ( const std::string &token ) - { - armor->add_head ( token ); - }, mPile->settings.armor.head, - std::vector ( world->raws.itemdefs.helms.begin(),world->raws.itemdefs.helms.end() ), - item_type::HELM ); - - // shoes type - serialize_list_itemdef ( [=] ( const std::string &token ) - { - armor->add_feet ( token ); - }, mPile->settings.armor.feet, - std::vector ( world->raws.itemdefs.shoes.begin(),world->raws.itemdefs.shoes.end() ), - item_type::SHOES ); - - // gloves type - serialize_list_itemdef ( [=] ( const std::string &token ) - { - armor->add_hands ( token ); - }, mPile->settings.armor.hands, - std::vector ( world->raws.itemdefs.gloves.begin(),world->raws.itemdefs.gloves.end() ), - item_type::GLOVES ); - - // pant type - serialize_list_itemdef ( [=] ( const std::string &token ) - { - armor->add_legs ( token ); - }, mPile->settings.armor.legs, - std::vector ( world->raws.itemdefs.pants.begin(),world->raws.itemdefs.pants.end() ), - item_type::PANTS ); - - // shield type - serialize_list_itemdef ( [=] ( const std::string &token ) - { - armor->add_shield ( token ); - }, mPile->settings.armor.shield, - std::vector ( world->raws.itemdefs.shields.begin(),world->raws.itemdefs.shields.end() ), - item_type::SHIELD ); - - // materials - FuncMaterialAllowed mat_filter = std::bind ( &StockpileSerializer::armor_mat_is_allowed, this, _1 ); - serialize_list_material ( mat_filter, [=] ( const std::string &token ) - { - armor->add_mats ( token ); - }, mPile->settings.armor.mats ); - - // other mats - serialize_list_other_mats ( mOtherMatsWeaponsArmor, [=] ( const std::string &token ) - { - armor->add_other_mats ( token ); - }, mPile->settings.armor.other_mats ); - - // quality core - serialize_list_quality ( [=] ( const std::string &token ) - { - armor->add_quality_core ( token ); - }, mPile->settings.armor.quality_core ); - - // quality total - serialize_list_quality ( [=] ( const std::string &token ) - { - armor->add_quality_total ( token ); - }, mPile->settings.armor.quality_total ); - } - - void read_armor() - { - if ( mBuffer.has_armor() ) - { - mPile->settings.flags.bits.armor = 1; - const StockpileSettings::ArmorSet armor = mBuffer.armor(); - debug() << "armor: " < const std::string& - { - return armor.body ( idx ); - }, armor.body_size(), &mPile->settings.armor.body, item_type::ARMOR ); - - // head type - unserialize_list_itemdef ( [=] ( const size_t & idx ) -> const std::string& - { - return armor.head ( idx ); - }, armor.head_size(), &mPile->settings.armor.head, item_type::HELM ); - - // feet type - unserialize_list_itemdef ( [=] ( const size_t & idx ) -> const std::string& - { - return armor.feet ( idx ); - }, armor.feet_size(), &mPile->settings.armor.feet, item_type::SHOES ); - - // hands type - unserialize_list_itemdef ( [=] ( const size_t & idx ) -> const std::string& - { - return armor.hands ( idx ); - }, armor.hands_size(), &mPile->settings.armor.hands, item_type::GLOVES ); - - // legs type - unserialize_list_itemdef ( [=] ( const size_t & idx ) -> const std::string& - { - return armor.legs ( idx ); - }, armor.legs_size(), &mPile->settings.armor.legs, item_type::PANTS ); - - // shield type - unserialize_list_itemdef ( [=] ( const size_t & idx ) -> const std::string& - { - return armor.shield ( idx ); - }, armor.shield_size(), &mPile->settings.armor.shield, item_type::SHIELD ); - - // materials - FuncMaterialAllowed mat_filter = std::bind ( &StockpileSerializer::armor_mat_is_allowed, this, _1 ); - unserialize_list_material ( mat_filter, [=] ( const size_t & idx ) -> const std::string& - { - return armor.mats ( idx ); - }, armor.mats_size(), &mPile->settings.armor.mats ); - - // other mats - unserialize_list_other_mats ( mOtherMatsWeaponsArmor, [=] ( const size_t & idx ) -> const std::string& - { - return armor.other_mats ( idx ); - }, armor.other_mats_size(), &mPile->settings.armor.other_mats ); - - // core quality - unserialize_list_quality ( [=] ( const size_t & idx ) -> const std::string& - { - return armor.quality_core ( idx ); - }, armor.quality_core_size(), mPile->settings.armor.quality_core ); - // total quality - unserialize_list_quality ( [=] ( const size_t & idx ) -> const std::string& - { - return armor.quality_total ( idx ); - }, armor.quality_total_size(), mPile->settings.armor.quality_total ); - } - else - { - mPile->settings.flags.bits.armor = 0; - mPile->settings.armor.body.clear(); - mPile->settings.armor.head.clear(); - mPile->settings.armor.feet.clear(); - mPile->settings.armor.hands.clear(); - mPile->settings.armor.legs.clear(); - mPile->settings.armor.shield.clear(); - mPile->settings.armor.other_mats.clear(); - mPile->settings.armor.mats.clear(); - quality_clear ( mPile->settings.armor.quality_core ); - quality_clear ( mPile->settings.armor.quality_total ); - } - } -}; - -static bool file_exists ( const std::string& filename ) -{ - struct stat buf; - if ( stat ( filename.c_str(), &buf ) != -1 ) - { - return true; - } - return false; -} - -static bool is_dfstockfile ( const std::string& filename ) -{ - return filename.rfind ( ".dfstock" ) != std::string::npos; -} - -// exporting -static command_result savestock ( color_ostream &out, vector & parameters ) -{ - building_stockpilest *sp = virtual_cast ( world->selected_building ); - if ( !sp ) - { - out.printerr ( "Selected building isn't a stockpile.\n" ); - return CR_WRONG_USAGE; - } - - if ( parameters.size() > 2 ) - { - out.printerr ( "Invalid parameters\n" ); - return CR_WRONG_USAGE; - } - - bool debug = false; - std::string file; - for( size_t i = 0; i < parameters.size(); ++i ) - { - const std::string o = parameters.at(i); - if ( o == "--debug" || o == "-d" ) - debug = true; - else if ( !o.empty() && o[0] != '-' ) - { - file = o; - } - } - if ( file.empty() ) - { - out.printerr("You must supply a valid filename.\n"); - return CR_WRONG_USAGE; - } - - StockpileSerializer cereal ( sp ); - if ( debug ) - cereal.enable_debug ( out ); - - if ( !is_dfstockfile ( file ) ) file += ".dfstock"; - if ( !cereal.serialize_to_file ( file ) ) - { - out.printerr ( "serialize failed\n" ); - return CR_FAILURE; - } - return CR_OK; -} - - -// importing -static command_result loadstock ( color_ostream &out, vector & parameters ) -{ - building_stockpilest *sp = virtual_cast ( world->selected_building ); - if ( !sp ) - { - out.printerr ( "Selected building isn't a stockpile.\n" ); - return CR_WRONG_USAGE; - } - - if ( parameters.size() < 1 || parameters.size() > 2 ) - { - out.printerr ( "Invalid parameters\n" ); - return CR_WRONG_USAGE; - } - - bool debug = false; - std::string file; - for( size_t i = 0; i < parameters.size(); ++i ) - { - const std::string o = parameters.at(i); - if ( o == "--debug" || o == "-d" ) - debug = true; - else if ( !o.empty() && o[0] != '-' ) - { - file = o; - } - } - - if ( !is_dfstockfile ( file ) ) file += ".dfstock"; - if ( file.empty() || !file_exists ( file ) ) - { - out.printerr ( "loadstock: a .dfstock file is required to import\n" ); - return CR_WRONG_USAGE; - } - - StockpileSerializer cereal ( sp ); - if ( debug ) - cereal.enable_debug ( out ); - if ( !cereal.unserialize_from_file ( file ) ) - { - out.printerr ( "unserialization failed\n" ); - return CR_FAILURE; - } - return CR_OK; -} - - - - - diff --git a/plugins/stockpiles/CMakeLists.txt b/plugins/stockpiles/CMakeLists.txt new file mode 100644 index 000000000..2a2ac5740 --- /dev/null +++ b/plugins/stockpiles/CMakeLists.txt @@ -0,0 +1,44 @@ +PROJECT(stockpiles) + +# add *our* headers here. +SET(PROJECT_HDRS +StockpileUtils.h +OrganicMatLookup.h +StockpileSerializer.h +) + +SET(PROJECT_SRCS +OrganicMatLookup.cpp +StockpileSerializer.cpp +stockpiles.cpp +) + +SET(PROJECT_PROTOS +${CMAKE_CURRENT_SOURCE_DIR}/proto/stockpiles.proto +) + +#Create new lists of what sources and headers protoc will output after we invoke it +STRING(REPLACE ".proto" ".pb.cc;" PROJECT_PROTO_SRCS ${PROJECT_PROTOS}) +STRING(REPLACE ".proto" ".pb.h;" PROJECT_PROTO_HDRS ${PROJECT_PROTOS}) + +SET_SOURCE_FILES_PROPERTIES( ${PROJECT_PROTO_HDRS} PROPERTIES GENERATED TRUE) +SET_SOURCE_FILES_PROPERTIES( ${PROJECT_PROTO_SRCS} PROPERTIES GENERATED TRUE) + +LIST(APPEND PROJECT_HDRS ${PROJECT_PROTO_HDRS}) +LIST(APPEND PROJECT_SRCS ${PROJECT_PROTO_SRCS}) + +SET_SOURCE_FILES_PROPERTIES( ${PROJECT_HDRS} PROPERTIES HEADER_FILE_ONLY TRUE) +LIST(APPEND PROJECT_SRCS ${PROJECT_HDRS}) + +#Generate sources from our proto files and store them in the source tree +ADD_CUSTOM_COMMAND( +OUTPUT ${PROJECT_PROTO_SRCS} ${PROJECT_PROTO_HDRS} +COMMAND protoc-bin -I=${CMAKE_CURRENT_SOURCE_DIR}/proto/ --cpp_out=${CMAKE_CURRENT_SOURCE_DIR}/proto/ ${PROJECT_PROTOS} +DEPENDS protoc-bin ${PROJECT_PROTOS} +) + +IF(WIN32) + DFHACK_PLUGIN(stockpiles ${PROJECT_SRCS} ${PROJECT_HDRS} LINK_LIBRARIES protobuf-lite) +ELSE() + DFHACK_PLUGIN(stockpiles ${PROJECT_SRCS} ${PROJECT_HDRS} LINK_LIBRARIES protobuf-lite) +ENDIF() diff --git a/plugins/stockpiles/OrganicMatLookup.cpp b/plugins/stockpiles/OrganicMatLookup.cpp new file mode 100644 index 000000000..ca9670a96 --- /dev/null +++ b/plugins/stockpiles/OrganicMatLookup.cpp @@ -0,0 +1,155 @@ +#include "OrganicMatLookup.h" + +#include "StockpileUtils.h" + +#include "modules/Materials.h" +#include "MiscUtils.h" + +#include "df/world.h" +#include "df/world_data.h" + +#include "df/creature_raw.h" +#include "df/caste_raw.h" +#include "df/material.h" + +using namespace DFHack; +using namespace df::enums; +using df::global::world; + +using std::endl; + +/** + * Helper class for mapping the various organic mats between their material indices + * and their index in the stockpile_settings structures. + */ + +void OrganicMatLookup::food_mat_by_idx ( std::ostream &out, organic_mat_category::organic_mat_category mat_category, std::vector::size_type food_idx, FoodMat & food_mat ) +{ + out << "food_lookup: food_idx(" << food_idx << ") "; + df::world_raws &raws = world->raws; + df::special_mat_table table = raws.mat_table; + int32_t main_idx = table.organic_indexes[mat_category][food_idx]; + int16_t type = table.organic_types[mat_category][food_idx]; + if ( mat_category == organic_mat_category::Fish || + mat_category == organic_mat_category::UnpreparedFish || + mat_category == organic_mat_category::Eggs ) + { + food_mat.creature = raws.creatures.all[type]; + food_mat.caste = food_mat.creature->caste[main_idx]; + out << " special creature type(" << type << ") caste("<< main_idx <<")" <::size_type idx ) +{ + FoodMat food_mat; + food_mat_by_idx ( out, mat_category, idx, food_mat ); + if ( food_mat.material.isValid() ) + { + return food_mat.material.getToken(); + } + else if ( food_mat.creature ) + { + return food_mat.creature->creature_id + ":" + food_mat.caste->caste_id; + } + return std::string(); +} + +size_t OrganicMatLookup::food_max_size ( organic_mat_category::organic_mat_category mat_category ) +{ + return world->raws.mat_table.organic_types[mat_category].size(); +} + +void OrganicMatLookup::food_build_map ( std::ostream &out ) +{ + if ( index_built ) + return; + df::world_raws &raws = world->raws; + df::special_mat_table table = raws.mat_table; + using df::enums::organic_mat_category::organic_mat_category; + df::enum_traits traits; + for ( int32_t mat_category = traits.first_item_value; mat_category <= traits.last_item_value; ++mat_category ) + { + for ( size_t i = 0; i < table.organic_indexes[mat_category].size(); ++i ) + { + int16_t type = table.organic_types[mat_category].at ( i ); + int32_t index = table.organic_indexes[mat_category].at ( i ); + food_index[mat_category].insert ( std::make_pair ( std::make_pair ( type,index ), i ) ); // wtf.. only in c++ + } + } + index_built = true; +} + +int16_t OrganicMatLookup::food_idx_by_token ( std::ostream &out, organic_mat_category::organic_mat_category mat_category, const std::string & token ) +{ + int16_t food_idx = -1; + df::world_raws &raws = world->raws; + df::special_mat_table table = raws.mat_table; + out << "food_idx_by_token: "; + if ( mat_category == organic_mat_category::Fish || + mat_category == organic_mat_category::UnpreparedFish || + mat_category == organic_mat_category::Eggs ) + { + std::vector tokens; + split_string ( &tokens, token, ":" ); + if ( tokens.size() != 2 ) + { + out << "creature " << "invalid CREATURE:CASTE token: " << token << endl; + } + else + { + int16_t creature_idx = find_creature ( tokens[0] ); + if ( creature_idx < 0 ) + { + out << " creature invalid token " << tokens[0]; + } + else + { + food_idx = linear_index ( table.organic_types[mat_category], creature_idx ); + if ( tokens[1] == "MALE" ) + food_idx += 1; + if ( table.organic_types[mat_category][food_idx] == creature_idx ) + out << "creature " << token << " caste " << tokens[1] << " creature_idx(" << creature_idx << ") food_idx("<< food_idx << ")" << endl; + else + { + out << "ERROR creature caste not found: " << token << " caste " << tokens[1] << " creature_idx(" << creature_idx << ") food_idx("<< food_idx << ")" << endl; + food_idx = -1; + } + } + } + } + else + { + if ( !index_built ) + food_build_map ( out ); + MaterialInfo mat_info = food_mat_by_token ( out, token ); + int16_t type = mat_info.type; + int32_t index = mat_info.index; + int16_t food_idx2 = -1; + auto it = food_index[mat_category].find ( std::make_pair ( type, index ) ); + if ( it != food_index[mat_category].end() ) + { + out << "matinfo: " << token << " type(" << mat_info.type << ") idx(" << mat_info.index << ") food_idx(" << it->second << ")" << endl; + food_idx = it->second; + } + else + { + out << "matinfo: " << token << " type(" << mat_info.type << ") idx(" << mat_info.index << ") food_idx not found :(" << endl; + } + } + return food_idx; +} + +MaterialInfo OrganicMatLookup::food_mat_by_token ( std::ostream &out, const std::string & token ) +{ + MaterialInfo mat_info; + mat_info.find ( token ); + return mat_info; +} + +bool OrganicMatLookup::index_built = false; +std::vector OrganicMatLookup::food_index = std::vector ( 37 ); diff --git a/plugins/stockpiles/OrganicMatLookup.h b/plugins/stockpiles/OrganicMatLookup.h new file mode 100644 index 000000000..aca10b004 --- /dev/null +++ b/plugins/stockpiles/OrganicMatLookup.h @@ -0,0 +1,47 @@ +#pragma once + +#include "modules/Materials.h" + +#include "df/organic_mat_category.h" + +namespace df { +struct creature_raw; +struct caste_raw; +} + +/** + * Helper class for mapping the various organic mats between their material indices + * and their index in the stockpile_settings structures. + */ +class OrganicMatLookup +{ + +// pair of material type and index + typedef std::pair FoodMatPair; +// map for using type,index pairs to find the food index + typedef std::map FoodMatMap; + +public: + struct FoodMat + { + DFHack::MaterialInfo material; + df::creature_raw *creature; + df::caste_raw * caste; + FoodMat() : material ( -1 ), creature ( 0 ), caste ( 0 ) {} + }; + static void food_mat_by_idx ( std::ostream &out, df::enums::organic_mat_category::organic_mat_category mat_category, std::vector::size_type food_idx, FoodMat & food_mat ); + static std::string food_token_by_idx ( std::ostream &out, df::enums::organic_mat_category::organic_mat_category mat_category, std::vector::size_type idx ); + + static size_t food_max_size ( df::enums::organic_mat_category::organic_mat_category mat_category ); + static void food_build_map ( std::ostream &out ); + + static int16_t food_idx_by_token ( std::ostream &out, df::enums::organic_mat_category::organic_mat_category mat_category, const std::string & token ); + + static DFHack::MaterialInfo food_mat_by_token ( std::ostream &out, const std::string & token ); + + static bool index_built; + static std::vector food_index; +private: + OrganicMatLookup(); +}; + diff --git a/plugins/stockpiles/StockpileSerializer.cpp b/plugins/stockpiles/StockpileSerializer.cpp new file mode 100644 index 000000000..52bd82591 --- /dev/null +++ b/plugins/stockpiles/StockpileSerializer.cpp @@ -0,0 +1,2195 @@ +#include "StockpileSerializer.h" + +// stockpiles plugin +#include "OrganicMatLookup.h" +#include "StockpileUtils.h" + +// dfhack +#include "MiscUtils.h" + +// df +#include "df/building_stockpilest.h" +#include "df/inorganic_raw.h" +#include "df/creature_raw.h" +#include "df/caste_raw.h" +#include "df/material.h" +#include "df/inorganic_raw.h" +#include "df/plant_raw.h" +#include "df/stockpile_group_set.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// protobuf +#include + +using std::endl; +using namespace DFHack; +using namespace df::enums; +using namespace google::protobuf; +using namespace dfstockpiles; +using df::global::world; + +using std::placeholders::_1; + + + +int NullBuffer::overflow ( int c ) +{ + return c; +} + +NullStream::NullStream() : std::ostream ( &m_sb ) {} + +StockpileSerializer::StockpileSerializer ( df::building_stockpilest * stockpile ) + : mDebug ( false ) + , mOut ( 0 ) + , mNull() + , mPile ( stockpile ) +{ + + // build other_mats indices + furniture_setup_other_mats(); + bars_blocks_setup_other_mats(); + finished_goods_setup_other_mats(); + weapons_armor_setup_other_mats(); +} + +StockpileSerializer::~StockpileSerializer() {} + +void StockpileSerializer::enable_debug ( std::ostream&out ) +{ + mDebug = true; + mOut = &out; +} + +bool StockpileSerializer::serialize_to_ostream ( std::ostream* output ) +{ + mBuffer.Clear(); + write(); + { + io::OstreamOutputStream zero_copy_output ( output ); + if ( !mBuffer.SerializeToZeroCopyStream ( &zero_copy_output ) ) return false; + } + return output->good(); +} + +bool StockpileSerializer::serialize_to_file ( const std::string & file ) +{ + std::fstream output ( file, std::ios::out | std::ios::binary | std::ios::trunc ); + return serialize_to_ostream ( &output ); +} + +bool StockpileSerializer::parse_from_istream ( std::istream* input ) +{ + mBuffer.Clear(); + io::IstreamInputStream zero_copy_input ( input ); + const bool res = mBuffer.ParseFromZeroCopyStream ( &zero_copy_input ) && input->eof(); + if ( res ) read(); + return res; +} + + +bool StockpileSerializer::unserialize_from_file ( const std::string & file ) +{ + std::fstream input ( file, std::ios::in | std::ios::binary ); + return parse_from_istream ( &input ); +} + +std::ostream & StockpileSerializer::debug() +{ + if ( mDebug ) + return *mOut; + return mNull; +} + + +void StockpileSerializer::write() +{ + // debug() << "GROUP SET " << bitfield_to_string(mPile->settings.flags) << endl; + write_general(); + if ( mPile->settings.flags.bits.animals ) + write_animals(); + if ( mPile->settings.flags.bits.food ) + write_food(); + if ( mPile->settings.flags.bits.furniture ) + write_furniture(); + if ( mPile->settings.flags.bits.refuse ) + write_refuse(); + if ( mPile->settings.flags.bits.stone ) + write_stone(); + if ( mPile->settings.flags.bits.ammo ) + write_ammo(); + if ( mPile->settings.flags.bits.coins ) + write_coins(); + if ( mPile->settings.flags.bits.bars_blocks ) + write_bars_blocks(); + if ( mPile->settings.flags.bits.gems ) + write_gems(); + if ( mPile->settings.flags.bits.finished_goods ) + write_finished_goods(); + if ( mPile->settings.flags.bits.leather ) + write_leather(); + if ( mPile->settings.flags.bits.cloth ) + write_cloth(); + if ( mPile->settings.flags.bits.wood ) + write_wood(); + if ( mPile->settings.flags.bits.weapons ) + write_weapons(); + if ( mPile->settings.flags.bits.armor ) + write_armor(); +} + +void StockpileSerializer::read () +{ + debug() << endl << "==READ==" << endl; + read_general(); + read_animals(); + read_food(); + read_furniture(); + read_refuse(); + read_stone(); + read_ammo(); + read_coins(); + read_bars_blocks(); + read_gems(); + read_finished_goods(); + read_leather(); + read_cloth(); + read_wood(); + read_weapons(); + read_armor(); +} + + +void StockpileSerializer::serialize_list_organic_mat ( FuncWriteExport add_value, const std::vector * list, organic_mat_category::organic_mat_category cat ) +{ + if ( !list ) + { + debug() << "serialize_list_organic_mat: list null" << endl; + } + for ( size_t i = 0; i < list->size(); ++i ) + { + if ( list->at ( i ) ) + { + std::string token = OrganicMatLookup::food_token_by_idx ( debug(), cat, i ); + if ( !token.empty() ) + { + add_value ( token ); + debug() << " organic_material " << i << " is " << token << endl; + } + else + { + debug() << "food mat invalid :(" << endl; + } + } + } +} + +void StockpileSerializer::unserialize_list_organic_mat ( FuncReadImport get_value, size_t list_size, std::vector *pile_list, organic_mat_category::organic_mat_category cat ) +{ + pile_list->clear(); + pile_list->resize ( OrganicMatLookup::food_max_size ( cat ), '\0' ); + for ( size_t i = 0; i < list_size; ++i ) + { + std::string token = get_value ( i ); + int16_t idx = OrganicMatLookup::food_idx_by_token ( debug(), cat, token ); + debug() << " organic_material " << idx << " is " << token << endl; + if ( idx >= pile_list->size() ) + { + debug() << "error organic mat index too large! idx[" << idx << "] max_size[" << pile_list->size() << "]" << endl; + continue; + } + pile_list->at ( idx ) = 1; + } +} + + +void StockpileSerializer::serialize_list_item_type ( FuncItemAllowed is_allowed, FuncWriteExport add_value, const std::vector &list ) +{ + using df::enums::item_type::item_type; + df::enum_traits type_traits; + debug() << "item_type size = " << list.size() << " size limit = " << type_traits.last_item_value << " typecasted: " << ( size_t ) type_traits.last_item_value << endl; + for ( size_t i = 0; i <= ( size_t ) type_traits.last_item_value; ++i ) + { + if ( list.at ( i ) ) + { + const item_type type = ( item_type ) ( ( df::enum_traits::base_type ) i ); + std::string r_type ( type_traits.key_table[i+1] ); + if ( !is_allowed ( type ) ) continue; + add_value ( r_type ); + debug() << "item_type key_table[" << i+1 << "] type[" << ( int16_t ) type << "] is " << r_type < *pile_list ) +{ + pile_list->clear(); + pile_list->resize ( 112, '\0' ); // TODO remove hardcoded list size value + for ( int i = 0; i < pile_list->size(); ++i ) + { + pile_list->at ( i ) = is_allowed ( ( item_type::item_type ) i ) ? 0 : 1; + } + using df::enums::item_type::item_type; + df::enum_traits type_traits; + for ( int32_t i = 0; i < list_size; ++i ) + { + const std::string token = read_value ( i ); + // subtract one because item_type starts at -1 + const df::enum_traits::base_type idx = linear_index ( debug(), type_traits, token ) - 1; + const item_type type = ( item_type ) idx; + if ( !is_allowed ( type ) ) continue; + debug() << " item_type " << idx << " is " << token << endl; + if ( idx >= pile_list->size() ) + { + debug() << "error item_type index too large! idx[" << idx << "] max_size[" << pile_list->size() << "]" << endl; + continue; + } + pile_list->at ( idx ) = 1; + } +} + + +void StockpileSerializer::serialize_list_material ( FuncMaterialAllowed is_allowed, FuncWriteExport add_value, const std::vector &list ) +{ + MaterialInfo mi; + for ( size_t i = 0; i < list.size(); ++i ) + { + if ( list.at ( i ) ) + { + mi.decode ( 0, i ); + if ( !is_allowed ( mi ) ) continue; + debug() << " material " << i << " is " << mi.getToken() << endl; + add_value ( mi.getToken() ); + } + } +} + + +void StockpileSerializer::unserialize_list_material ( FuncMaterialAllowed is_allowed, FuncReadImport read_value, int32_t list_size, std::vector *pile_list ) +{ + // we initialize all possible (allowed) values to 0, + // then all other not-allowed values to 1 + // why? because that's how the memory is in DF before + // we muck with it. + std::set idx_set; + pile_list->clear(); + pile_list->resize ( world->raws.inorganics.size(), 0 ); + for ( int i = 0; i < pile_list->size(); ++i ) + { + MaterialInfo mi ( 0, i ); + pile_list->at ( i ) = is_allowed ( mi ) ? 0 : 1; + } + for ( int i = 0; i < list_size; ++i ) + { + const std::string token = read_value ( i ); + MaterialInfo mi; + mi.find ( token ); + if ( !is_allowed ( mi ) ) continue; + debug() << " material " << mi.index << " is " << token << endl; + if ( mi.index >= pile_list->size() ) + { + debug() << "error material index too large! idx[" << mi.index << "] max_size[" << pile_list->size() << "]" << endl; + continue; + } + pile_list->at ( mi.index ) = 1; + } +} + + +void StockpileSerializer::serialize_list_quality ( FuncWriteExport add_value, const bool ( &quality_list ) [7] ) +{ + using df::enums::item_quality::item_quality; + df::enum_traits quality_traits; + for ( size_t i = 0; i < 7; ++i ) + { + if ( quality_list[i] ) + { + const std::string f_type ( quality_traits.key_table[i] ); + add_value ( f_type ); + debug() << " quality: " << i << " is " << f_type < 0 && list_size <= 7 ) + { + using df::enums::item_quality::item_quality; + df::enum_traits quality_traits; + for ( int i = 0; i < list_size; ++i ) + { + const std::string quality = read_value ( i ); + df::enum_traits::base_type idx = linear_index ( debug(), quality_traits, quality ); + if ( idx < 0 ) + { + debug() << " invalid quality token " << quality << endl; + continue; + } + debug() << " quality: " << idx << " is " << quality << endl; + pile_list[idx] = true; + } + } + else + { + quality_clear ( pile_list ); + } +} + + +void StockpileSerializer::serialize_list_other_mats ( const std::map other_mats, FuncWriteExport add_value, std::vector list ) +{ + for ( size_t i = 0; i < list.size(); ++i ) + { + if ( list.at ( i ) ) + { + const std::string token = other_mats_index ( other_mats, i ); + if ( token.empty() ) + { + debug() << " invalid other material with index " << i << endl; + continue; + } + add_value ( token ); + debug() << " other mats " << i << " is " << token << endl; + } + } +} + + +void StockpileSerializer::unserialize_list_other_mats ( const std::map other_mats, FuncReadImport read_value, int32_t list_size, std::vector *pile_list ) +{ + pile_list->clear(); + pile_list->resize ( other_mats.size(), '\0' ); + for ( int i = 0; i < list_size; ++i ) + { + const std::string token = read_value ( i ); + size_t idx = other_mats_token ( other_mats, token ); + if ( idx < 0 ) + { + debug() << "invalid other mat with token " << token; + continue; + } + debug() << " other_mats " << idx << " is " << token << endl; + if ( idx >= pile_list->size() ) + { + debug() << "error other_mats index too large! idx[" << idx << "] max_size[" << pile_list->size() << "]" << endl; + continue; + } + pile_list->at ( idx ) = 1; + } +} + + + +void StockpileSerializer::serialize_list_itemdef ( FuncWriteExport add_value, std::vector list, std::vector items, item_type::item_type type ) +{ + for ( size_t i = 0; i < list.size(); ++i ) + { + if ( list.at ( i ) ) + { + const df::itemdef *a = items.at ( i ); + // skip procedurally generated items + if ( a->base_flags.is_set ( 0 ) ) continue; + ItemTypeInfo ii; + if ( !ii.decode ( type, i ) ) continue; + add_value ( ii.getToken() ); + debug() << " itemdef type" << i << " is " << ii.getToken() << endl; + } + } +} + + +void StockpileSerializer::unserialize_list_itemdef ( FuncReadImport read_value, int32_t list_size, std::vector *pile_list, item_type::item_type type ) +{ + pile_list->clear(); + pile_list->resize ( Items::getSubtypeCount ( type ), '\0' ); + for ( int i = 0; i < list_size; ++i ) + { + std::string token = read_value ( i ); + ItemTypeInfo ii; + if ( !ii.find ( token ) ) continue; + debug() << " itemdef " << ii.subtype << " is " << token << endl; + if ( ii.subtype >= pile_list->size() ) + { + debug() << "error itemdef index too large! idx[" << ii.subtype << "] max_size[" << pile_list->size() << "]" << endl; + continue; + } + pile_list->at ( ii.subtype ) = 1; + } +} + + +std::string StockpileSerializer::other_mats_index ( const std::map other_mats, int idx ) +{ + auto it = other_mats.find ( idx ); + if ( it == other_mats.end() ) + return std::string(); + return it->second; +} + +int StockpileSerializer::other_mats_token ( const std::map other_mats, const std::string & token ) +{ + for ( auto it = other_mats.begin(); it != other_mats.end(); ++it ) + { + if ( it->second == token ) + return it->first; + } + return -1; +} + +void StockpileSerializer::write_general() +{ + mBuffer.set_max_bins ( mPile->max_barrels ); + mBuffer.set_max_wheelbarrows ( mPile->max_wheelbarrows ); + mBuffer.set_use_links_only ( mPile->use_links_only ); + mBuffer.set_unknown1 ( mPile->settings.unk1 ); + mBuffer.set_allow_inorganic ( mPile->settings.allow_inorganic ); + mBuffer.set_allow_organic ( mPile->settings.allow_organic ); + mBuffer.set_corpses ( mPile->settings.flags.bits.corpses ); +} + +void StockpileSerializer::read_general() +{ + if ( mBuffer.has_max_bins() ) + mPile->max_bins = mBuffer.max_bins(); + if ( mBuffer.has_max_wheelbarrows() ) + mPile->max_wheelbarrows = mBuffer.max_wheelbarrows(); + if ( mBuffer.has_max_barrels() ) + mPile->max_barrels = mBuffer.max_barrels(); + if ( mBuffer.has_use_links_only() ) + mPile->use_links_only = mBuffer.use_links_only(); + if ( mBuffer.has_unknown1() ) + mPile->settings.unk1 = mBuffer.unknown1(); + if ( mBuffer.has_allow_inorganic() ) + mPile->settings.allow_inorganic = mBuffer.allow_inorganic(); + if ( mBuffer.has_allow_organic() ) + mPile->settings.allow_organic = mBuffer.allow_organic(); + if ( mBuffer.has_corpses() ) + mPile->settings.flags.bits.corpses = mBuffer.corpses(); +} + +void StockpileSerializer::write_animals() +{ + StockpileSettings::AnimalsSet *animals= mBuffer.mutable_animals(); + animals->set_empty_cages ( mPile->settings.animals.empty_cages ); + animals->set_empty_traps ( mPile->settings.animals.empty_traps ); + for ( size_t i = 0; i < mPile->settings.animals.enabled.size(); ++i ) + { + if ( mPile->settings.animals.enabled.at ( i ) == 1 ) + { + df::creature_raw* r = find_creature ( i ); + debug() << "creature "<< r->creature_id << " " << i << endl; + animals->add_enabled ( r->creature_id ); + } + } +} + +void StockpileSerializer::read_animals() +{ + if ( mBuffer.has_animals() ) + { + mPile->settings.flags.bits.animals = 1; + debug() << "animals:" << endl; + mPile->settings.animals.empty_cages = mBuffer.animals().empty_cages(); + mPile->settings.animals.empty_traps = mBuffer.animals().empty_traps(); + + mPile->settings.animals.enabled.clear(); + mPile->settings.animals.enabled.resize ( world->raws.creatures.all.size(), '\0' ); + debug() << " pile has " << mPile->settings.animals.enabled.size() << endl; + for ( auto i = 0; i < mBuffer.animals().enabled_size(); ++i ) + { + std::string id = mBuffer.animals().enabled ( i ); + int idx = find_creature ( id ); + debug() << id << " " << idx << endl; + if ( idx < 0 || idx >= mPile->settings.animals.enabled.size() ) + { + debug() << "WARNING: animal index invalid: " << idx << endl; + continue; + } + mPile->settings.animals.enabled.at ( idx ) = ( char ) 1; + } + } + else + { + mPile->settings.animals.enabled.clear(); + mPile->settings.flags.bits.animals = 0; + mPile->settings.animals.empty_cages = false; + mPile->settings.animals.empty_traps = false; + } +} + +StockpileSerializer::food_pair StockpileSerializer::food_map ( organic_mat_category::organic_mat_category cat ) +{ + using df::enums::organic_mat_category::organic_mat_category; + using namespace std::placeholders; + switch ( cat ) + { + case organic_mat_category::Meat: + { + FuncWriteExport setter = [=] ( const std::string &id ) + { + mBuffer.mutable_food()->add_meat ( id ); + }; + FuncReadImport getter = [=] ( size_t idx ) -> std::string { return mBuffer.food().meat ( idx ); }; + return food_pair ( setter, &mPile->settings.food.meat, getter, mBuffer.food().meat_size() ); + } + case organic_mat_category::Fish: + { + FuncWriteExport setter = [=] ( const std::string &id ) + { + mBuffer.mutable_food()->add_fish ( id ); + }; + FuncReadImport getter = [=] ( size_t idx ) -> std::string { return mBuffer.food().fish ( idx ); }; + return food_pair ( setter, &mPile->settings.food.fish, getter, mBuffer.food().fish_size() ); + } + case organic_mat_category::UnpreparedFish: + { + FuncWriteExport setter = [=] ( const std::string &id ) + { + mBuffer.mutable_food()->add_unprepared_fish ( id ); + }; + FuncReadImport getter = [=] ( size_t idx ) -> std::string { return mBuffer.food().unprepared_fish ( idx ); }; + return food_pair ( setter, &mPile->settings.food.unprepared_fish, getter, mBuffer.food().unprepared_fish_size() ); + } + case organic_mat_category::Eggs: + { + FuncWriteExport setter = [=] ( const std::string &id ) + { + mBuffer.mutable_food()->add_egg ( id ); + }; + FuncReadImport getter = [=] ( size_t idx ) -> std::string { return mBuffer.food().egg ( idx ); }; + return food_pair ( setter, &mPile->settings.food.egg, getter, mBuffer.food().egg_size() ); + } + case organic_mat_category::Plants: + { + FuncWriteExport setter = [=] ( const std::string &id ) + { + mBuffer.mutable_food()->add_plants ( id ); + }; + FuncReadImport getter = [=] ( size_t idx ) -> std::string { return mBuffer.food().plants ( idx ); }; + return food_pair ( setter, &mPile->settings.food.plants, getter, mBuffer.food().plants_size() ); + } + case organic_mat_category::PlantDrink: + { + FuncWriteExport setter = [=] ( const std::string &id ) + { + mBuffer.mutable_food()->add_drink_plant ( id ); + }; + FuncReadImport getter = [=] ( size_t idx ) -> std::string { return mBuffer.food().drink_plant ( idx ); }; + return food_pair ( setter, &mPile->settings.food.drink_plant, getter, mBuffer.food().drink_plant_size() ); + } + case organic_mat_category::CreatureDrink: + { + FuncWriteExport setter = [=] ( const std::string &id ) + { + mBuffer.mutable_food()->add_drink_animal ( id ); + }; + FuncReadImport getter = [=] ( size_t idx ) -> std::string { return mBuffer.food().drink_animal ( idx ); }; + return food_pair ( setter, &mPile->settings.food.drink_animal, getter, mBuffer.food().drink_animal_size() ); + } + case organic_mat_category::PlantCheese: + { + FuncWriteExport setter = [=] ( const std::string &id ) + { + mBuffer.mutable_food()->add_cheese_plant ( id ); + }; + FuncReadImport getter = [=] ( size_t idx ) -> std::string { return mBuffer.food().cheese_plant ( idx ); }; + return food_pair ( setter, &mPile->settings.food.cheese_plant, getter, mBuffer.food().cheese_plant_size() ); + } + case organic_mat_category::CreatureCheese: + { + FuncWriteExport setter = [=] ( const std::string &id ) + { + mBuffer.mutable_food()->add_cheese_animal ( id ); + }; + FuncReadImport getter = [=] ( size_t idx ) -> std::string { return mBuffer.food().cheese_animal ( idx ); }; + return food_pair ( setter, &mPile->settings.food.cheese_animal, getter, mBuffer.food().cheese_animal_size() ); + } + case organic_mat_category::Seed: + { + FuncWriteExport setter = [=] ( const std::string &id ) + { + mBuffer.mutable_food()->add_seeds ( id ); + }; + FuncReadImport getter = [=] ( size_t idx ) -> std::string { return mBuffer.food().seeds ( idx ); }; + return food_pair ( setter, &mPile->settings.food.seeds, getter, mBuffer.food().seeds_size() ); + } + case organic_mat_category::Leaf: + { + FuncWriteExport setter = [=] ( const std::string &id ) + { + mBuffer.mutable_food()->add_leaves ( id ); + }; + FuncReadImport getter = [=] ( size_t idx ) -> std::string { return mBuffer.food().leaves ( idx ); }; + return food_pair ( setter, &mPile->settings.food.leaves, getter, mBuffer.food().leaves_size() ); + } + case organic_mat_category::PlantPowder: + { + FuncWriteExport setter = [=] ( const std::string &id ) + { + mBuffer.mutable_food()->add_powder_plant ( id ); + }; + FuncReadImport getter = [=] ( size_t idx ) -> std::string { return mBuffer.food().powder_plant ( idx ); }; + return food_pair ( setter, &mPile->settings.food.powder_plant, getter, mBuffer.food().powder_plant_size() ); + } + case organic_mat_category::CreaturePowder: + { + FuncWriteExport setter = [=] ( const std::string &id ) + { + mBuffer.mutable_food()->add_powder_creature ( id ); + }; + FuncReadImport getter = [=] ( size_t idx ) -> std::string { return mBuffer.food().powder_creature ( idx ); }; + return food_pair ( setter, &mPile->settings.food.powder_creature, getter, mBuffer.food().powder_creature_size() ); + } + case organic_mat_category::Glob: + { + FuncWriteExport setter = [=] ( const std::string &id ) + { + mBuffer.mutable_food()->add_glob ( id ); + }; + FuncReadImport getter = [=] ( size_t idx ) -> std::string { return mBuffer.food().glob ( idx ); }; + return food_pair ( setter, &mPile->settings.food.glob, getter, mBuffer.food().glob_size() ); + } + case organic_mat_category::PlantLiquid: + { + FuncWriteExport setter = [=] ( const std::string &id ) + { + mBuffer.mutable_food()->add_liquid_plant ( id ); + }; + FuncReadImport getter = [=] ( size_t idx ) -> std::string { return mBuffer.food().liquid_plant ( idx ); }; + return food_pair ( setter, &mPile->settings.food.liquid_plant, getter, mBuffer.food().liquid_plant_size() ); + } + case organic_mat_category::CreatureLiquid: + { + FuncWriteExport setter = [=] ( const std::string &id ) + { + mBuffer.mutable_food()->add_liquid_animal ( id ); + }; + FuncReadImport getter = [=] ( size_t idx ) -> std::string { return mBuffer.food().liquid_animal ( idx ); }; + return food_pair ( setter, &mPile->settings.food.liquid_animal, getter, mBuffer.food().liquid_animal_size() ); + } + case organic_mat_category::MiscLiquid: + { + FuncWriteExport setter = [=] ( const std::string &id ) + { + mBuffer.mutable_food()->add_liquid_misc ( id ); + }; + FuncReadImport getter = [=] ( size_t idx ) -> std::string { return mBuffer.food().liquid_misc ( idx ); }; + return food_pair ( setter, &mPile->settings.food.liquid_misc, getter, mBuffer.food().liquid_misc_size() ); + } + + case organic_mat_category::Paste: + { + FuncWriteExport setter = [=] ( const std::string &id ) + { + mBuffer.mutable_food()->add_glob_paste ( id ); + }; + FuncReadImport getter = [=] ( size_t idx ) -> std::string { return mBuffer.food().glob_paste ( idx ); }; + return food_pair ( setter, &mPile->settings.food.glob_paste, getter, mBuffer.food().glob_paste_size() ); + } + case organic_mat_category::Pressed: + { + FuncWriteExport setter = [=] ( const std::string &id ) + { + mBuffer.mutable_food()->add_glob_pressed ( id ); + }; + FuncReadImport getter = [=] ( size_t idx ) -> std::string { return mBuffer.food().glob_pressed ( idx ); }; + return food_pair ( setter, &mPile->settings.food.glob_pressed, getter, mBuffer.food().glob_pressed_size() ); + } + case organic_mat_category::Leather: + case organic_mat_category::Silk: + case organic_mat_category::PlantFiber: + case organic_mat_category::Bone: + case organic_mat_category::Shell: + case organic_mat_category::Wood: + case organic_mat_category::Horn: + case organic_mat_category::Pearl: + case organic_mat_category::Tooth: + case organic_mat_category::EdibleCheese: + case organic_mat_category::AnyDrink: + case organic_mat_category::EdiblePlant: + case organic_mat_category::CookableLiquid: + case organic_mat_category::CookablePowder: + case organic_mat_category::CookableSeed: + case organic_mat_category::CookableLeaf: + case organic_mat_category::Yarn: + case organic_mat_category::MetalThread: + default: + // not used in stockpile food menu + break; + } + return food_pair(); +} + + +void StockpileSerializer::write_food() +{ + StockpileSettings::FoodSet *food = mBuffer.mutable_food(); + debug() << " food: " << endl; + food->set_prepared_meals ( mPile->settings.food.prepared_meals ); + + using df::enums::organic_mat_category::organic_mat_category; + df::enum_traits traits; + for ( int32_t mat_category = traits.first_item_value; mat_category traits; + if ( mBuffer.has_food() ) + { + mPile->settings.flags.bits.food = 1; + const StockpileSettings::FoodSet food = mBuffer.food(); + debug() << "food:" <settings.food.prepared_meals = food.prepared_meals(); + else + mPile->settings.food.prepared_meals = true; + + debug() << " prepared_meals: " << mPile->settings.food.prepared_meals << endl; + + for ( int32_t mat_category = traits.first_item_value; mat_category clear(); + } + mPile->settings.flags.bits.food = 0; + mPile->settings.food.prepared_meals = false; + } +} + +void StockpileSerializer::furniture_setup_other_mats() +{ + mOtherMatsFurniture.insert ( std::make_pair ( 0,"WOOD" ) ); + mOtherMatsFurniture.insert ( std::make_pair ( 1,"PLANT_CLOTH" ) ); + mOtherMatsFurniture.insert ( std::make_pair ( 2,"BONE" ) ); + mOtherMatsFurniture.insert ( std::make_pair ( 3,"TOOTH" ) ); + mOtherMatsFurniture.insert ( std::make_pair ( 4,"HORN" ) ); + mOtherMatsFurniture.insert ( std::make_pair ( 5,"PEARL" ) ); + mOtherMatsFurniture.insert ( std::make_pair ( 6,"SHELL" ) ); + mOtherMatsFurniture.insert ( std::make_pair ( 7,"LEATHER" ) ); + mOtherMatsFurniture.insert ( std::make_pair ( 8,"SILK" ) ); + mOtherMatsFurniture.insert ( std::make_pair ( 9,"AMBER" ) ); + mOtherMatsFurniture.insert ( std::make_pair ( 10,"CORAL" ) ); + mOtherMatsFurniture.insert ( std::make_pair ( 11,"GREEN_GLASS" ) ); + mOtherMatsFurniture.insert ( std::make_pair ( 12,"CLEAR_GLASS" ) ); + mOtherMatsFurniture.insert ( std::make_pair ( 13,"CRYSTAL_GLASS" ) ); + mOtherMatsFurniture.insert ( std::make_pair ( 14,"YARN" ) ); +} + +void StockpileSerializer::write_furniture() +{ + StockpileSettings::FurnitureSet *furniture= mBuffer.mutable_furniture(); + furniture->set_sand_bags ( mPile->settings.furniture.sand_bags ); + + // FURNITURE type + using df::enums::furniture_type::furniture_type; + df::enum_traits type_traits; + for ( size_t i = 0; i < mPile->settings.furniture.type.size(); ++i ) + { + if ( mPile->settings.furniture.type.at ( i ) ) + { + std::string f_type ( type_traits.key_table[i] ); + furniture->add_type ( f_type ); + debug() << "furniture_type " << i << " is " << f_type <add_mats ( token ); + }, mPile->settings.furniture.mats ); + + // other mats + serialize_list_other_mats ( mOtherMatsFurniture, [=] ( const std::string &token ) + { + furniture->add_other_mats ( token ); + }, mPile->settings.furniture.other_mats ); + + serialize_list_quality ( [=] ( const std::string &token ) + { + furniture->add_quality_core ( token ); + }, mPile->settings.furniture.quality_core ); + serialize_list_quality ( [=] ( const std::string &token ) + { + furniture->add_quality_total ( token ); + }, mPile->settings.furniture.quality_total ); +} + +bool StockpileSerializer::furniture_mat_is_allowed ( const MaterialInfo &mi ) +{ + return mi.isValid() && mi.material + && ( mi.material->flags.is_set ( material_flags::IS_METAL ) + || mi.material->flags.is_set ( material_flags::IS_STONE ) ); +} + +void StockpileSerializer::read_furniture() +{ + if ( mBuffer.has_furniture() ) + { + mPile->settings.flags.bits.furniture = 1; + const StockpileSettings::FurnitureSet furniture = mBuffer.furniture(); + debug() << "furniture:" <settings.furniture.sand_bags = mBuffer.furniture().sand_bags(); + else + mPile->settings.furniture.sand_bags = false; + + // type + using df::enums::furniture_type::furniture_type; + df::enum_traits type_traits; + mPile->settings.furniture.type.clear(); + mPile->settings.furniture.type.resize ( type_traits.last_item_value+1, '\0' ); + if ( furniture.type_size() > 0 ) + { + for ( int i = 0; i < furniture.type_size(); ++i ) + { + const std::string type = furniture.type ( i ); + df::enum_traits::base_type idx = linear_index ( debug(), type_traits, type ); + debug() << " type " << idx << " is " << type << endl; + if ( idx < 0 || idx >= mPile->settings.furniture.type.size() ) + { + debug() << "WARNING: furniture type index invalid " << type << ", idx=" << idx << endl; + continue; + } + mPile->settings.furniture.type.at ( idx ) = 1; + } + } + + FuncMaterialAllowed filter = std::bind ( &StockpileSerializer::furniture_mat_is_allowed, this, _1 ); + unserialize_list_material ( filter, [=] ( const size_t & idx ) -> const std::string& + { + return furniture.mats ( idx ); + }, furniture.mats_size(), &mPile->settings.furniture.mats ); + + // other materials + unserialize_list_other_mats ( mOtherMatsFurniture, [=] ( const size_t & idx ) -> const std::string& + { + return furniture.other_mats ( idx ); + }, furniture.other_mats_size(), &mPile->settings.furniture.other_mats ); + + // core quality + unserialize_list_quality ( [=] ( const size_t & idx ) -> const std::string& + { + return furniture.quality_core ( idx ); + }, furniture.quality_core_size(), mPile->settings.furniture.quality_core ); + + // total quality + unserialize_list_quality ( [=] ( const size_t & idx ) -> const std::string& + { + return furniture.quality_total ( idx ); + }, furniture.quality_total_size(), mPile->settings.furniture.quality_total ); + + } + else + { + mPile->settings.flags.bits.furniture = 0; + mPile->settings.furniture.type.clear(); + mPile->settings.furniture.other_mats.clear(); + mPile->settings.furniture.mats.clear(); + quality_clear ( mPile->settings.furniture.quality_core ); + quality_clear ( mPile->settings.furniture.quality_total ); + } +} + +bool StockpileSerializer::refuse_creature_is_allowed ( const df::creature_raw *raw ) +{ + if ( !raw ) return false; + // wagon and generated creatures not allowed, except angels + const bool is_wagon = raw->creature_id == "EQUIPMENT_WAGON"; + const bool is_generated = raw->flags.is_set ( creature_raw_flags::GENERATED ); + const bool is_angel = is_generated && raw->creature_id.find ( "DIVINE_" ) != std::string::npos; + return !is_wagon && ! ( is_generated && !is_angel ); +} + +void StockpileSerializer::refuse_write_helper ( std::function add_value, const vector & list ) +{ + for ( size_t i = 0; i < list.size(); ++i ) + { + if ( list.at ( i ) == 1 ) + { + df::creature_raw* r = find_creature ( i ); + // skip forgotten beasts, titans, demons, and night creatures + if ( !refuse_creature_is_allowed ( r ) ) continue; + debug() << "creature "<< r->creature_id << " " << i << endl; + add_value ( r->creature_id ); + } + } +} + +bool StockpileSerializer::refuse_type_is_allowed ( item_type::item_type type ) +{ + if ( type == item_type::NONE + || type == item_type::BAR + || type == item_type::SMALLGEM + || type == item_type::BLOCKS + || type == item_type::ROUGH + || type == item_type::BOULDER + || type == item_type::CORPSE + || type == item_type::CORPSEPIECE + || type == item_type::ROCK + || type == item_type::ORTHOPEDIC_CAST + ) return false; + return true; +} + + +void StockpileSerializer::write_refuse() +{ + StockpileSettings::RefuseSet *refuse = mBuffer.mutable_refuse(); + refuse->set_fresh_raw_hide ( mPile->settings.refuse.fresh_raw_hide ); + refuse->set_rotten_raw_hide ( mPile->settings.refuse.rotten_raw_hide ); + + // type + FuncItemAllowed filter = std::bind ( &StockpileSerializer::refuse_type_is_allowed, this, _1 ); + serialize_list_item_type ( filter, [=] ( const std::string &token ) + { + refuse->add_type ( token ); + }, mPile->settings.refuse.type ); + + // corpses + refuse_write_helper ( [=] ( const std::string &id ) + { + refuse->add_corpses ( id ); + }, mPile->settings.refuse.corpses ); + // body_parts + refuse_write_helper ( [=] ( const std::string &id ) + { + refuse->add_body_parts ( id ); + }, mPile->settings.refuse.body_parts ); + // skulls + refuse_write_helper ( [=] ( const std::string &id ) + { + refuse->add_skulls ( id ); + }, mPile->settings.refuse.skulls ); + // bones + refuse_write_helper ( [=] ( const std::string &id ) + { + refuse->add_bones ( id ); + }, mPile->settings.refuse.bones ); + // hair + refuse_write_helper ( [=] ( const std::string &id ) + { + refuse->add_hair ( id ); + }, mPile->settings.refuse.hair ); + // shells + refuse_write_helper ( [=] ( const std::string &id ) + { + refuse->add_shells ( id ); + }, mPile->settings.refuse.shells ); + // teeth + refuse_write_helper ( [=] ( const std::string &id ) + { + refuse->add_teeth ( id ); + }, mPile->settings.refuse.teeth ); + // horns + refuse_write_helper ( [=] ( const std::string &id ) + { + refuse->add_horns ( id ); + }, mPile->settings.refuse.horns ); +} + +void StockpileSerializer::refuse_read_helper ( std::function get_value, size_t list_size, std::vector* pile_list ) +{ + pile_list->clear(); + pile_list->resize ( world->raws.creatures.all.size(), '\0' ); + if ( list_size > 0 ) + { + for ( size_t i = 0; i < list_size; ++i ) + { + const std::string creature_id = get_value ( i ); + const int idx = find_creature ( creature_id ); + const df::creature_raw* creature = find_creature ( idx ); + if ( idx < 0 || !refuse_creature_is_allowed ( creature ) || idx >= pile_list->size() ) + { + debug() << "WARNING invalid refuse creature " << creature_id << ", idx=" << idx << endl; + continue; + } + debug() << " creature " << idx << " is " << creature_id << endl; + pile_list->at ( idx ) = 1; + } + } +} + + + +void StockpileSerializer::read_refuse() +{ + if ( mBuffer.has_refuse() ) + { + mPile->settings.flags.bits.refuse = 1; + const StockpileSettings::RefuseSet refuse = mBuffer.refuse(); + debug() << "refuse: " <settings.refuse.fresh_raw_hide = refuse.fresh_raw_hide(); + mPile->settings.refuse.rotten_raw_hide = refuse.rotten_raw_hide(); + + // type + FuncItemAllowed filter = std::bind ( &StockpileSerializer::refuse_type_is_allowed, this, _1 ); + unserialize_list_item_type ( filter, [=] ( const size_t & idx ) -> const std::string& + { + return refuse.type ( idx ); + }, refuse.type_size(), &mPile->settings.refuse.type ); + + // corpses + debug() << " corpses" << endl; + refuse_read_helper ( [=] ( const size_t & idx ) -> const std::string& + { + return refuse.corpses ( idx ); + }, refuse.corpses_size(), &mPile->settings.refuse.corpses ); + // body_parts + debug() << " body_parts" << endl; + refuse_read_helper ( [=] ( const size_t & idx ) -> const std::string& + { + return refuse.body_parts ( idx ); + }, refuse.body_parts_size(), &mPile->settings.refuse.body_parts ); + // skulls + debug() << " skulls" << endl; + refuse_read_helper ( [=] ( const size_t & idx ) -> const std::string& + { + return refuse.skulls ( idx ); + }, refuse.skulls_size(), &mPile->settings.refuse.skulls ); + // bones + debug() << " bones" << endl; + refuse_read_helper ( [=] ( const size_t & idx ) -> const std::string& + { + return refuse.bones ( idx ); + }, refuse.bones_size(), &mPile->settings.refuse.bones ); + // hair + debug() << " hair" << endl; + refuse_read_helper ( [=] ( const size_t & idx ) -> const std::string& + { + return refuse.hair ( idx ); + }, refuse.hair_size(), &mPile->settings.refuse.hair ); + // shells + debug() << " shells" << endl; + refuse_read_helper ( [=] ( const size_t & idx ) -> const std::string& + { + return refuse.shells ( idx ); + }, refuse.shells_size(), &mPile->settings.refuse.shells ); + // teeth + debug() << " teeth" << endl; + refuse_read_helper ( [=] ( const size_t & idx ) -> const std::string& + { + return refuse.teeth ( idx ); + }, refuse.teeth_size(), &mPile->settings.refuse.teeth ); + // horns + debug() << " horns" << endl; + refuse_read_helper ( [=] ( const size_t & idx ) -> const std::string& + { + return refuse.horns ( idx ); + }, refuse.horns_size(), &mPile->settings.refuse.horns ); + } + else + { + mPile->settings.flags.bits.refuse = 0; + mPile->settings.refuse.type.clear(); + mPile->settings.refuse.corpses.clear(); + mPile->settings.refuse.body_parts.clear(); + mPile->settings.refuse.skulls.clear(); + mPile->settings.refuse.bones.clear(); + mPile->settings.refuse.hair.clear(); + mPile->settings.refuse.shells.clear(); + mPile->settings.refuse.teeth.clear(); + mPile->settings.refuse.horns.clear(); + mPile->settings.refuse.fresh_raw_hide = false; + mPile->settings.refuse.rotten_raw_hide = false; + } +} + +bool StockpileSerializer::stone_is_allowed ( const MaterialInfo &mi ) +{ + if ( !mi.isValid() ) return false; + const bool is_allowed_soil = mi.inorganic->flags.is_set ( inorganic_flags::SOIL ) && !mi.inorganic->flags.is_set ( inorganic_flags::AQUIFER ); + const bool is_allowed_stone = mi.material->flags.is_set ( material_flags::IS_STONE ) && !mi.material->flags.is_set ( material_flags::NO_STONE_STOCKPILE ); + return is_allowed_soil || is_allowed_stone; +} + +void StockpileSerializer::write_stone() +{ + StockpileSettings::StoneSet *stone= mBuffer.mutable_stone(); + + FuncMaterialAllowed filter = std::bind ( &StockpileSerializer::stone_is_allowed, this, _1 ); + serialize_list_material ( filter, [=] ( const std::string &token ) + { + stone->add_mats ( token ); + }, mPile->settings.stone.mats ); +} + +void StockpileSerializer::read_stone() +{ + if ( mBuffer.has_stone() ) + { + mPile->settings.flags.bits.stone = 1; + const StockpileSettings::StoneSet stone = mBuffer.stone(); + debug() << "stone: " < const std::string& + { + return stone.mats ( idx ); + }, stone.mats_size(), &mPile->settings.stone.mats ); + } + else + { + mPile->settings.flags.bits.stone = 0; + mPile->settings.stone.mats.clear(); + } +} + +bool StockpileSerializer::ammo_mat_is_allowed ( const MaterialInfo &mi ) +{ + return mi.isValid() && mi.material && mi.material->flags.is_set ( material_flags::IS_METAL ); +} + +void StockpileSerializer::write_ammo() +{ + StockpileSettings::AmmoSet *ammo = mBuffer.mutable_ammo(); + + // ammo type + serialize_list_itemdef ( [=] ( const std::string &token ) + { + ammo->add_type ( token ); + }, mPile->settings.ammo.type, + std::vector ( world->raws.itemdefs.ammo.begin(),world->raws.itemdefs.ammo.end() ), + item_type::AMMO ); + + // metal + FuncMaterialAllowed filter = std::bind ( &StockpileSerializer::ammo_mat_is_allowed, this, _1 ); + serialize_list_material ( filter, [=] ( const std::string &token ) + { + ammo->add_mats ( token ); + }, mPile->settings.ammo.mats ); + + // other mats - only wood and bone + if ( mPile->settings.ammo.other_mats.size() > 2 ) + { + debug() << "WARNING: ammo other materials > 2! " << mPile->settings.ammo.other_mats.size() << endl; + } + + for ( size_t i = 0; i < std::min ( size_t ( 2 ), mPile->settings.ammo.other_mats.size() ); ++i ) + { + if ( !mPile->settings.ammo.other_mats.at ( i ) ) + continue; + const std::string token = i == 0 ? "WOOD" : "BONE"; + ammo->add_other_mats ( token ); + debug() << " other mats " << i << " is " << token << endl; + } + + // quality core + serialize_list_quality ( [=] ( const std::string &token ) + { + ammo->add_quality_core ( token ); + }, mPile->settings.ammo.quality_core ); + + // quality total + serialize_list_quality ( [=] ( const std::string &token ) + { + ammo->add_quality_total ( token ); + }, mPile->settings.ammo.quality_total ); +} + +void StockpileSerializer::read_ammo() +{ + if ( mBuffer.has_ammo() ) + { + mPile->settings.flags.bits.ammo = 1; + const StockpileSettings::AmmoSet ammo = mBuffer.ammo(); + debug() << "ammo: " < const std::string& + { + return ammo.type ( idx ); + }, ammo.type_size(), &mPile->settings.ammo.type, item_type::AMMO ); + + // materials metals + FuncMaterialAllowed filter = std::bind ( &StockpileSerializer::ammo_mat_is_allowed, this, _1 ); + unserialize_list_material ( filter, [=] ( const size_t & idx ) -> const std::string& + { + return ammo.mats ( idx ); + }, ammo.mats_size(), &mPile->settings.ammo.mats ); + + // others + mPile->settings.ammo.other_mats.clear(); + mPile->settings.ammo.other_mats.resize ( 2, '\0' ); + if ( ammo.other_mats_size() > 0 ) + { + // TODO remove hardcoded value + for ( int i = 0; i < ammo.other_mats_size(); ++i ) + { + const std::string token = ammo.other_mats ( i ); + const int32_t idx = token == "WOOD" ? 0 : token == "BONE" ? 1 : -1; + debug() << " other mats " << idx << " is " << token << endl; + if ( idx != -1 ) + mPile->settings.ammo.other_mats.at ( idx ) = 1; + } + } + + // core quality + unserialize_list_quality ( [=] ( const size_t & idx ) -> const std::string& + { + return ammo.quality_core ( idx ); + }, ammo.quality_core_size(), mPile->settings.ammo.quality_core ); + + // total quality + unserialize_list_quality ( [=] ( const size_t & idx ) -> const std::string& + { + return ammo.quality_total ( idx ); + }, ammo.quality_total_size(), mPile->settings.ammo.quality_total ); + } + else + { + mPile->settings.flags.bits.ammo = 0; + mPile->settings.ammo.type.clear(); + mPile->settings.ammo.mats.clear(); + mPile->settings.ammo.other_mats.clear(); + quality_clear ( mPile->settings.ammo.quality_core ); + quality_clear ( mPile->settings.ammo.quality_total ); + } +} + +bool StockpileSerializer::coins_mat_is_allowed ( const MaterialInfo &mi ) +{ + return mi.isValid(); +} + +void StockpileSerializer::write_coins() +{ + StockpileSettings::CoinSet *coins = mBuffer.mutable_coin(); + FuncMaterialAllowed filter = std::bind ( &StockpileSerializer::coins_mat_is_allowed, this, _1 ); + serialize_list_material ( filter, [=] ( const std::string &token ) + { + coins->add_mats ( token ); + }, mPile->settings.coins.mats ); +} + +void StockpileSerializer::read_coins() +{ + if ( mBuffer.has_coin() ) + { + mPile->settings.flags.bits.coins = 1; + const StockpileSettings::CoinSet coins = mBuffer.coin(); + debug() << "coins: " < const std::string& + { + return coins.mats ( idx ); + }, coins.mats_size(), &mPile->settings.coins.mats ); + } + else + { + mPile->settings.flags.bits.coins = 0; + mPile->settings.coins.mats.clear(); + } +} + +void StockpileSerializer::bars_blocks_setup_other_mats() +{ + mOtherMatsBars.insert ( std::make_pair ( 0,"COAL" ) ); + mOtherMatsBars.insert ( std::make_pair ( 1,"POTASH" ) ); + mOtherMatsBars.insert ( std::make_pair ( 2,"ASH" ) ); + mOtherMatsBars.insert ( std::make_pair ( 3,"PEARLASH" ) ); + mOtherMatsBars.insert ( std::make_pair ( 4,"SOAP" ) ); + + mOtherMatsBlocks.insert ( std::make_pair ( 0,"GREEN_GLASS" ) ); + mOtherMatsBlocks.insert ( std::make_pair ( 1,"CLEAR_GLASS" ) ); + mOtherMatsBlocks.insert ( std::make_pair ( 2,"CRYSTAL_GLASS" ) ); + mOtherMatsBlocks.insert ( std::make_pair ( 3,"WOOD" ) ); +} + +bool StockpileSerializer::bars_mat_is_allowed ( const MaterialInfo &mi ) +{ + return mi.isValid() && mi.material && mi.material->flags.is_set ( material_flags::IS_METAL ); +} + +bool StockpileSerializer::blocks_mat_is_allowed ( const MaterialInfo &mi ) +{ + return mi.isValid() && mi.material + && ( mi.material->flags.is_set ( material_flags::IS_METAL ) + || mi.material->flags.is_set ( material_flags::IS_STONE ) ); +} + + +void StockpileSerializer::write_bars_blocks() +{ + StockpileSettings::BarsBlocksSet *bars_blocks = mBuffer.mutable_barsblocks(); + MaterialInfo mi; + FuncMaterialAllowed filter = std::bind ( &StockpileSerializer::bars_mat_is_allowed, this, _1 ); + serialize_list_material ( filter, [=] ( const std::string &token ) + { + bars_blocks->add_bars_mats ( token ); + }, mPile->settings.bars_blocks.bars_mats ); + + // blocks mats + filter = std::bind ( &StockpileSerializer::blocks_mat_is_allowed, this, _1 ); + serialize_list_material ( filter, [=] ( const std::string &token ) + { + bars_blocks->add_blocks_mats ( token ); + }, mPile->settings.bars_blocks.blocks_mats ); + + // bars other mats + serialize_list_other_mats ( mOtherMatsBars, [=] ( const std::string &token ) + { + bars_blocks->add_bars_other_mats ( token ); + }, mPile->settings.bars_blocks.bars_other_mats ); + + // blocks other mats + serialize_list_other_mats ( mOtherMatsBlocks, [=] ( const std::string &token ) + { + bars_blocks->add_blocks_other_mats ( token ); + }, mPile->settings.bars_blocks.blocks_other_mats ); +} + +void StockpileSerializer::read_bars_blocks() +{ + if ( mBuffer.has_barsblocks() ) + { + mPile->settings.flags.bits.bars_blocks = 1; + const StockpileSettings::BarsBlocksSet bars_blocks = mBuffer.barsblocks(); + debug() << "bars_blocks: " < const std::string& + { + return bars_blocks.bars_mats ( idx ); + }, bars_blocks.bars_mats_size(), &mPile->settings.bars_blocks.bars_mats ); + + // blocks + filter = std::bind ( &StockpileSerializer::blocks_mat_is_allowed, this, _1 ); + unserialize_list_material ( filter, [=] ( const size_t & idx ) -> const std::string& + { + return bars_blocks.blocks_mats ( idx ); + }, bars_blocks.blocks_mats_size(), &mPile->settings.bars_blocks.blocks_mats ); + // bars other mats + unserialize_list_other_mats ( mOtherMatsBars, [=] ( const size_t & idx ) -> const std::string& + { + return bars_blocks.bars_other_mats ( idx ); + }, bars_blocks.bars_other_mats_size(), &mPile->settings.bars_blocks.bars_other_mats ); + + + // blocks other mats + unserialize_list_other_mats ( mOtherMatsBlocks, [=] ( const size_t & idx ) -> const std::string& + { + return bars_blocks.blocks_other_mats ( idx ); + }, bars_blocks.blocks_other_mats_size(), &mPile->settings.bars_blocks.blocks_other_mats ); + + } + else + { + mPile->settings.flags.bits.bars_blocks = 0; + mPile->settings.bars_blocks.bars_other_mats.clear(); + mPile->settings.bars_blocks.bars_mats.clear(); + mPile->settings.bars_blocks.blocks_other_mats.clear(); + mPile->settings.bars_blocks.blocks_mats.clear(); + } +} + +bool StockpileSerializer::gem_mat_is_allowed ( const MaterialInfo &mi ) +{ + return mi.isValid() && mi.material && mi.material->flags.is_set ( material_flags::IS_GEM ); +} +bool StockpileSerializer::gem_cut_mat_is_allowed ( const MaterialInfo &mi ) +{ + return mi.isValid() && mi.material && ( mi.material->flags.is_set ( material_flags::IS_GEM ) || mi.material->flags.is_set ( material_flags::IS_STONE ) ) ; +} +bool StockpileSerializer::gem_other_mat_is_allowed ( MaterialInfo &mi ) +{ + return mi.isValid() && ( mi.getToken() == "GLASS_GREEN" || mi.getToken() == "GLASS_CLEAR" || mi.getToken() == "GLASS_CRYSTAL" ); +} + +void StockpileSerializer::write_gems() +{ + StockpileSettings::GemsSet *gems = mBuffer.mutable_gems(); + MaterialInfo mi; + // rough mats + FuncMaterialAllowed filter_rough = std::bind ( &StockpileSerializer::gem_mat_is_allowed, this, _1 ); + serialize_list_material ( filter_rough, [=] ( const std::string &token ) + { + gems->add_rough_mats ( token ); + }, mPile->settings.gems.rough_mats ); + // cut mats + FuncMaterialAllowed filter_cut = std::bind ( &StockpileSerializer::gem_cut_mat_is_allowed, this, _1 ); + serialize_list_material ( filter_cut, [=] ( const std::string &token ) + { + gems->add_cut_mats ( token ); + }, mPile->settings.gems.cut_mats ); + // rough other + for ( size_t i = 0; i < mPile->settings.gems.rough_other_mats.size(); ++i ) + { + if ( mPile->settings.gems.rough_other_mats.at ( i ) ) + { + mi.decode ( i, -1 ); + if ( !gem_other_mat_is_allowed ( mi ) ) continue; + debug() << " gem rough_other mat" << i << " is " << mi.getToken() << endl; + gems->add_rough_other_mats ( mi.getToken() ); + } + } + // cut other + for ( size_t i = 0; i < mPile->settings.gems.cut_other_mats.size(); ++i ) + { + if ( mPile->settings.gems.cut_other_mats.at ( i ) ) + { + mi.decode ( i, -1 ); + if ( !mi.isValid() ) mi.decode ( 0, i ); + if ( !gem_other_mat_is_allowed ( mi ) ) continue; + debug() << " gem cut_other mat" << i << " is " << mi.getToken() << endl; + gems->add_cut_other_mats ( mi.getToken() ); + } + } +} + +void StockpileSerializer::read_gems() +{ + if ( mBuffer.has_gems() ) + { + mPile->settings.flags.bits.gems = 1; + const StockpileSettings::GemsSet gems = mBuffer.gems(); + debug() << "gems: " < const std::string& + { + return gems.rough_mats ( idx ); + }, gems.rough_mats_size(), &mPile->settings.gems.rough_mats ); + + // cut + FuncMaterialAllowed filter_cut = std::bind ( &StockpileSerializer::gem_cut_mat_is_allowed, this, _1 ); + unserialize_list_material ( filter_cut, [=] ( const size_t & idx ) -> const std::string& + { + return gems.cut_mats ( idx ); + }, gems.cut_mats_size(), &mPile->settings.gems.cut_mats ); + + const size_t builtin_size = std::extentraws.mat_table.builtin ) >::value; + // rough other + mPile->settings.gems.rough_other_mats.clear(); + mPile->settings.gems.rough_other_mats.resize ( builtin_size, '\0' ); + for ( int i = 0; i < gems.rough_other_mats_size(); ++i ) + { + const std::string token = gems.rough_other_mats ( i ); + MaterialInfo mi; + mi.find ( token ); + if ( !mi.isValid() || mi.type >= builtin_size ) + { + debug() << "WARNING: invalid gem mat " << token << ". idx=" << mi.type << endl; + continue; + } + debug() << " rough_other mats " << mi.type << " is " << token << endl; + mPile->settings.gems.rough_other_mats.at ( mi.type ) = 1; + } + + // cut other + mPile->settings.gems.cut_other_mats.clear(); + mPile->settings.gems.cut_other_mats.resize ( builtin_size, '\0' ); + for ( int i = 0; i < gems.cut_other_mats_size(); ++i ) + { + const std::string token = gems.cut_other_mats ( i ); + MaterialInfo mi; + mi.find ( token ); + if ( !mi.isValid() || mi.type >= builtin_size ) + { + debug() << "WARNING: invalid gem mat " << token << ". idx=" << mi.type << endl; + continue; + } + debug() << " cut_other mats " << mi.type << " is " << token << endl; + mPile->settings.gems.cut_other_mats.at ( mi.type ) = 1; + } + } + else + { + mPile->settings.flags.bits.gems = 0; + mPile->settings.gems.cut_other_mats.clear(); + mPile->settings.gems.cut_mats.clear(); + mPile->settings.gems.rough_other_mats.clear(); + mPile->settings.gems.rough_mats.clear(); + } +} + +bool StockpileSerializer::finished_goods_type_is_allowed ( item_type::item_type type ) +{ + switch ( type ) + { + case item_type::CHAIN: + case item_type::FLASK: + case item_type::GOBLET: + case item_type::INSTRUMENT: + case item_type::TOY: + case item_type::ARMOR: + case item_type::SHOES: + case item_type::HELM: + case item_type::GLOVES: + case item_type::FIGURINE: + case item_type::AMULET: + case item_type::SCEPTER: + case item_type::CROWN: + case item_type::RING: + case item_type::EARRING: + case item_type::BRACELET: + case item_type::GEM: + case item_type::TOTEM: + case item_type::PANTS: + case item_type::BACKPACK: + case item_type::QUIVER: + case item_type::SPLINT: + case item_type::CRUTCH: + case item_type::TOOL: + case item_type::BOOK: + return true; + default: + return false; + } + +} + +void StockpileSerializer::finished_goods_setup_other_mats() +{ + mOtherMatsFinishedGoods.insert ( std::make_pair ( 0,"WOOD" ) ); + mOtherMatsFinishedGoods.insert ( std::make_pair ( 1,"PLANT_CLOTH" ) ); + mOtherMatsFinishedGoods.insert ( std::make_pair ( 2,"BONE" ) ); + mOtherMatsFinishedGoods.insert ( std::make_pair ( 3,"TOOTH" ) ); + mOtherMatsFinishedGoods.insert ( std::make_pair ( 4,"HORN" ) ); + mOtherMatsFinishedGoods.insert ( std::make_pair ( 5,"PEARL" ) ); + mOtherMatsFinishedGoods.insert ( std::make_pair ( 6,"SHELL" ) ); + mOtherMatsFinishedGoods.insert ( std::make_pair ( 7,"LEATHER" ) ); + mOtherMatsFinishedGoods.insert ( std::make_pair ( 8,"SILK" ) ); + mOtherMatsFinishedGoods.insert ( std::make_pair ( 9,"AMBER" ) ); + mOtherMatsFinishedGoods.insert ( std::make_pair ( 10,"CORAL" ) ); + mOtherMatsFinishedGoods.insert ( std::make_pair ( 11,"GREEN_GLASS" ) ); + mOtherMatsFinishedGoods.insert ( std::make_pair ( 12,"CLEAR_GLASS" ) ); + mOtherMatsFinishedGoods.insert ( std::make_pair ( 13,"CRYSTAL_GLASS" ) ); + mOtherMatsFinishedGoods.insert ( std::make_pair ( 14,"YARN" ) ); + mOtherMatsFinishedGoods.insert ( std::make_pair ( 15,"WAX" ) ); +} + +bool StockpileSerializer::finished_goods_mat_is_allowed ( const MaterialInfo &mi ) +{ + return mi.isValid() + && mi.material + && ( mi.material->flags.is_set ( material_flags::IS_GEM ) + || mi.material->flags.is_set ( material_flags::IS_METAL ) + || mi.material->flags.is_set ( material_flags::IS_STONE ) ) ; +} + +void StockpileSerializer::write_finished_goods() +{ + StockpileSettings::FinishedGoodsSet *finished_goods = mBuffer.mutable_finished_goods(); + + // type + FuncItemAllowed filter = std::bind ( &StockpileSerializer::finished_goods_type_is_allowed, this, _1 ); + serialize_list_item_type ( filter, [=] ( const std::string &token ) + { + finished_goods->add_type ( token ); + }, mPile->settings.finished_goods.type ); + + // materials + FuncMaterialAllowed mat_filter = std::bind ( &StockpileSerializer::finished_goods_mat_is_allowed, this, _1 ); + serialize_list_material ( mat_filter, [=] ( const std::string &token ) + { + finished_goods->add_mats ( token ); + }, mPile->settings.finished_goods.mats ); + + // other mats + serialize_list_other_mats ( mOtherMatsFinishedGoods, [=] ( const std::string &token ) + { + finished_goods->add_other_mats ( token ); + }, mPile->settings.finished_goods.other_mats ); + + // quality core + serialize_list_quality ( [=] ( const std::string &token ) + { + finished_goods->add_quality_core ( token ); + }, mPile->settings.finished_goods.quality_core ); + + // quality total + serialize_list_quality ( [=] ( const std::string &token ) + { + finished_goods->add_quality_total ( token ); + }, mPile->settings.finished_goods.quality_total ); +} + +void StockpileSerializer::read_finished_goods() +{ + if ( mBuffer.has_finished_goods() ) + { + mPile->settings.flags.bits.finished_goods = 1; + const StockpileSettings::FinishedGoodsSet finished_goods = mBuffer.finished_goods(); + debug() << "finished_goods: " < const std::string& + { + return finished_goods.type ( idx ); + }, finished_goods.type_size(), &mPile->settings.finished_goods.type ); + + // materials + FuncMaterialAllowed mat_filter = std::bind ( &StockpileSerializer::finished_goods_mat_is_allowed, this, _1 ); + unserialize_list_material ( mat_filter, [=] ( const size_t & idx ) -> const std::string& + { + return finished_goods.mats ( idx ); + }, finished_goods.mats_size(), &mPile->settings.finished_goods.mats ); + + // other mats + unserialize_list_other_mats ( mOtherMatsFinishedGoods, [=] ( const size_t & idx ) -> const std::string& + { + return finished_goods.other_mats ( idx ); + }, finished_goods.other_mats_size(), &mPile->settings.finished_goods.other_mats ); + + // core quality + unserialize_list_quality ( [=] ( const size_t & idx ) -> const std::string& + { + return finished_goods.quality_core ( idx ); + }, finished_goods.quality_core_size(), mPile->settings.finished_goods.quality_core ); + + // total quality + unserialize_list_quality ( [=] ( const size_t & idx ) -> const std::string& + { + return finished_goods.quality_total ( idx ); + }, finished_goods.quality_total_size(), mPile->settings.finished_goods.quality_total ); + + } + else + { + mPile->settings.flags.bits.finished_goods = 0; + mPile->settings.finished_goods.type.clear(); + mPile->settings.finished_goods.other_mats.clear(); + mPile->settings.finished_goods.mats.clear(); + quality_clear ( mPile->settings.finished_goods.quality_core ); + quality_clear ( mPile->settings.finished_goods.quality_total ); + } +} + +void StockpileSerializer::write_leather() +{ + StockpileSettings::LeatherSet *leather = mBuffer.mutable_leather(); + + FuncWriteExport setter = [=] ( const std::string &id ) + { + leather->add_mats ( id ); + }; + serialize_list_organic_mat ( setter, &mPile->settings.leather.mats, organic_mat_category::Leather ); +} +void StockpileSerializer::read_leather() +{ + if ( mBuffer.has_leather() ) + { + mPile->settings.flags.bits.leather = 1; + const StockpileSettings::LeatherSet leather = mBuffer.leather(); + debug() << "leather: " < std::string + { + return leather.mats ( idx ); + }, leather.mats_size(), &mPile->settings.leather.mats, organic_mat_category::Leather ); + } + else + { + mPile->settings.flags.bits.leather = 0; + mPile->settings.leather.mats.clear(); + } +} + +void StockpileSerializer::write_cloth() +{ + StockpileSettings::ClothSet * cloth = mBuffer.mutable_cloth(); + + serialize_list_organic_mat ( [=] ( const std::string &token ) + { + cloth->add_thread_silk ( token ); + }, &mPile->settings.cloth.thread_silk, organic_mat_category::Silk ); + + serialize_list_organic_mat ( [=] ( const std::string &token ) + { + cloth->add_thread_plant ( token ); + }, &mPile->settings.cloth.thread_plant, organic_mat_category::PlantFiber ); + + serialize_list_organic_mat ( [=] ( const std::string &token ) + { + cloth->add_thread_yarn ( token ); + }, &mPile->settings.cloth.thread_yarn, organic_mat_category::Yarn ); + + serialize_list_organic_mat ( [=] ( const std::string &token ) + { + cloth->add_thread_metal ( token ); + }, &mPile->settings.cloth.thread_metal, organic_mat_category::MetalThread ); + + serialize_list_organic_mat ( [=] ( const std::string &token ) + { + cloth->add_cloth_silk ( token ); + }, &mPile->settings.cloth.cloth_silk, organic_mat_category::Silk ); + + serialize_list_organic_mat ( [=] ( const std::string &token ) + { + cloth->add_cloth_plant ( token ); + }, &mPile->settings.cloth.cloth_plant, organic_mat_category::PlantFiber ); + + serialize_list_organic_mat ( [=] ( const std::string &token ) + { + cloth->add_cloth_yarn ( token ); + }, &mPile->settings.cloth.cloth_yarn, organic_mat_category::Yarn ); + + serialize_list_organic_mat ( [=] ( const std::string &token ) + { + cloth->add_cloth_metal ( token ); + }, &mPile->settings.cloth.cloth_metal, organic_mat_category::MetalThread ); + +} +void StockpileSerializer::read_cloth() +{ + if ( mBuffer.has_cloth() ) + { + mPile->settings.flags.bits.cloth = 1; + const StockpileSettings::ClothSet cloth = mBuffer.cloth(); + debug() << "cloth: " < std::string + { + return cloth.thread_silk ( idx ); + }, cloth.thread_silk_size(), &mPile->settings.cloth.thread_silk, organic_mat_category::Silk ); + + unserialize_list_organic_mat ( [=] ( size_t idx ) -> std::string + { + return cloth.thread_plant ( idx ); + }, cloth.thread_plant_size(), &mPile->settings.cloth.thread_plant, organic_mat_category::PlantFiber ); + + unserialize_list_organic_mat ( [=] ( size_t idx ) -> std::string + { + return cloth.thread_yarn ( idx ); + }, cloth.thread_yarn_size(), &mPile->settings.cloth.thread_yarn, organic_mat_category::Yarn ); + + unserialize_list_organic_mat ( [=] ( size_t idx ) -> std::string + { + return cloth.thread_metal ( idx ); + }, cloth.thread_metal_size(), &mPile->settings.cloth.thread_metal, organic_mat_category::MetalThread ); + + unserialize_list_organic_mat ( [=] ( size_t idx ) -> std::string + { + return cloth.cloth_silk ( idx ); + }, cloth.cloth_silk_size(), &mPile->settings.cloth.cloth_silk, organic_mat_category::Silk ); + + unserialize_list_organic_mat ( [=] ( size_t idx ) -> std::string + { + return cloth.cloth_plant ( idx ); + }, cloth.cloth_plant_size(), &mPile->settings.cloth.cloth_plant, organic_mat_category::PlantFiber ); + + unserialize_list_organic_mat ( [=] ( size_t idx ) -> std::string + { + return cloth.cloth_yarn ( idx ); + }, cloth.cloth_yarn_size(), &mPile->settings.cloth.cloth_yarn, organic_mat_category::Yarn ); + + unserialize_list_organic_mat ( [=] ( size_t idx ) -> std::string + { + return cloth.cloth_metal ( idx ); + }, cloth.cloth_metal_size(), &mPile->settings.cloth.cloth_metal, organic_mat_category::MetalThread ); + } + else + { + mPile->settings.cloth.thread_metal.clear(); + mPile->settings.cloth.thread_plant.clear(); + mPile->settings.cloth.thread_silk.clear(); + mPile->settings.cloth.thread_yarn.clear(); + mPile->settings.cloth.cloth_metal.clear(); + mPile->settings.cloth.cloth_plant.clear(); + mPile->settings.cloth.cloth_silk.clear(); + mPile->settings.cloth.cloth_yarn.clear(); + mPile->settings.flags.bits.cloth = 0; + } +} + +bool StockpileSerializer::wood_mat_is_allowed ( const df::plant_raw * plant ) +{ + return plant && plant->flags.is_set ( plant_raw_flags::TREE ); +} + +void StockpileSerializer::write_wood() +{ + StockpileSettings::WoodSet * wood = mBuffer.mutable_wood(); + for ( size_t i = 0; i < mPile->settings.wood.mats.size(); ++i ) + { + if ( mPile->settings.wood.mats.at ( i ) ) + { + const df::plant_raw * plant = find_plant ( i ); + if ( !wood_mat_is_allowed ( plant ) ) continue; + wood->add_mats ( plant->id ); + debug() << " plant " << i << " is " << plant->id << endl; + } + } +} +void StockpileSerializer::read_wood() +{ + if ( mBuffer.has_wood() ) + { + mPile->settings.flags.bits.wood = 1; + const StockpileSettings::WoodSet wood = mBuffer.wood(); + debug() << "wood: " <settings.wood.mats.clear(); + mPile->settings.wood.mats.resize ( world->raws.plants.all.size(), '\0' ); + for ( int i = 0; i < wood.mats_size(); ++i ) + { + const std::string token = wood.mats ( i ); + const size_t idx = find_plant ( token ); + if ( idx < 0 || idx >= mPile->settings.wood.mats.size() ) + { + debug() << "WARNING wood mat index invalid " << token << ", idx=" << idx << endl; + continue; + } + debug() << " plant " << idx << " is " << token << endl; + mPile->settings.wood.mats.at ( idx ) = 1; + } + } + else + { + mPile->settings.flags.bits.wood = 0; + mPile->settings.wood.mats.clear(); + } +} + +bool StockpileSerializer::weapons_mat_is_allowed ( const MaterialInfo &mi ) +{ + return mi.isValid() && mi.material && ( + mi.material->flags.is_set ( material_flags::IS_METAL ) || + mi.material->flags.is_set ( material_flags::IS_STONE ) ); + +} + +void StockpileSerializer::write_weapons() +{ + StockpileSettings::WeaponsSet * weapons = mBuffer.mutable_weapons(); + + weapons->set_unusable ( mPile->settings.weapons.unusable ); + weapons->set_usable ( mPile->settings.weapons.usable ); + + // weapon type + serialize_list_itemdef ( [=] ( const std::string &token ) + { + weapons->add_weapon_type ( token ); + }, mPile->settings.weapons.weapon_type, + std::vector ( world->raws.itemdefs.weapons.begin(),world->raws.itemdefs.weapons.end() ), + item_type::WEAPON ); + + // trapcomp type + serialize_list_itemdef ( [=] ( const std::string &token ) + { + weapons->add_trapcomp_type ( token ); + }, mPile->settings.weapons.trapcomp_type, + std::vector ( world->raws.itemdefs.trapcomps.begin(),world->raws.itemdefs.trapcomps.end() ), + item_type::TRAPCOMP ); + + // materials + FuncMaterialAllowed mat_filter = std::bind ( &StockpileSerializer::weapons_mat_is_allowed, this, _1 ); + serialize_list_material ( mat_filter, [=] ( const std::string &token ) + { + weapons->add_mats ( token ); + }, mPile->settings.weapons.mats ); + + // other mats + serialize_list_other_mats ( mOtherMatsWeaponsArmor, [=] ( const std::string &token ) + { + weapons->add_other_mats ( token ); + }, mPile->settings.weapons.other_mats ); + + // quality core + serialize_list_quality ( [=] ( const std::string &token ) + { + weapons->add_quality_core ( token ); + }, mPile->settings.weapons.quality_core ); + + // quality total + serialize_list_quality ( [=] ( const std::string &token ) + { + weapons->add_quality_total ( token ); + }, mPile->settings.weapons.quality_total ); +} + +void StockpileSerializer::read_weapons() +{ + if ( mBuffer.has_weapons() ) + { + mPile->settings.flags.bits.weapons = 1; + const StockpileSettings::WeaponsSet weapons = mBuffer.weapons(); + debug() << "weapons: " < const std::string& + { + return weapons.weapon_type ( idx ); + }, weapons.weapon_type_size(), &mPile->settings.weapons.weapon_type, item_type::WEAPON ); + + // trapcomp type + unserialize_list_itemdef ( [=] ( const size_t & idx ) -> const std::string& + { + return weapons.trapcomp_type ( idx ); + }, weapons.trapcomp_type_size(), &mPile->settings.weapons.trapcomp_type, item_type::TRAPCOMP ); + + // materials + FuncMaterialAllowed mat_filter = std::bind ( &StockpileSerializer::weapons_mat_is_allowed, this, _1 ); + unserialize_list_material ( mat_filter, [=] ( const size_t & idx ) -> const std::string& + { + return weapons.mats ( idx ); + }, weapons.mats_size(), &mPile->settings.weapons.mats ); + + // other mats + unserialize_list_other_mats ( mOtherMatsWeaponsArmor, [=] ( const size_t & idx ) -> const std::string& + { + return weapons.other_mats ( idx ); + }, weapons.other_mats_size(), &mPile->settings.weapons.other_mats ); + + + // core quality + unserialize_list_quality ( [=] ( const size_t & idx ) -> const std::string& + { + return weapons.quality_core ( idx ); + }, weapons.quality_core_size(), mPile->settings.weapons.quality_core ); + // total quality + unserialize_list_quality ( [=] ( const size_t & idx ) -> const std::string& + { + return weapons.quality_total ( idx ); + }, weapons.quality_total_size(), mPile->settings.weapons.quality_total ); + } + else + { + mPile->settings.flags.bits.weapons = 0; + mPile->settings.weapons.weapon_type.clear(); + mPile->settings.weapons.trapcomp_type.clear(); + mPile->settings.weapons.other_mats.clear(); + mPile->settings.weapons.mats.clear(); + quality_clear ( mPile->settings.weapons.quality_core ); + quality_clear ( mPile->settings.weapons.quality_total ); + } + +} + +void StockpileSerializer::weapons_armor_setup_other_mats() +{ + mOtherMatsWeaponsArmor.insert ( std::make_pair ( 0,"WOOD" ) ); + mOtherMatsWeaponsArmor.insert ( std::make_pair ( 1,"PLANT_CLOTH" ) ); + mOtherMatsWeaponsArmor.insert ( std::make_pair ( 2,"BONE" ) ); + mOtherMatsWeaponsArmor.insert ( std::make_pair ( 3,"SHELL" ) ); + mOtherMatsWeaponsArmor.insert ( std::make_pair ( 4,"LEATHER" ) ); + mOtherMatsWeaponsArmor.insert ( std::make_pair ( 5,"SILK" ) ); + mOtherMatsWeaponsArmor.insert ( std::make_pair ( 6,"GREEN_GLASS" ) ); + mOtherMatsWeaponsArmor.insert ( std::make_pair ( 7,"CLEAR_GLASS" ) ); + mOtherMatsWeaponsArmor.insert ( std::make_pair ( 8,"CRYSTAL_GLASS" ) ); + mOtherMatsWeaponsArmor.insert ( std::make_pair ( 9,"YARN" ) ); +} + +bool StockpileSerializer::armor_mat_is_allowed ( const MaterialInfo &mi ) +{ + return mi.isValid() && mi.material && mi.material->flags.is_set ( material_flags::IS_METAL ); +} + +void StockpileSerializer::write_armor() +{ + StockpileSettings::ArmorSet * armor = mBuffer.mutable_armor(); + + armor->set_unusable ( mPile->settings.armor.unusable ); + armor->set_usable ( mPile->settings.armor.usable ); + + // armor type + serialize_list_itemdef ( [=] ( const std::string &token ) + { + armor->add_body ( token ); + }, mPile->settings.armor.body, + std::vector ( world->raws.itemdefs.armor.begin(),world->raws.itemdefs.armor.end() ), + item_type::ARMOR ); + + // helm type + serialize_list_itemdef ( [=] ( const std::string &token ) + { + armor->add_head ( token ); + }, mPile->settings.armor.head, + std::vector ( world->raws.itemdefs.helms.begin(),world->raws.itemdefs.helms.end() ), + item_type::HELM ); + + // shoes type + serialize_list_itemdef ( [=] ( const std::string &token ) + { + armor->add_feet ( token ); + }, mPile->settings.armor.feet, + std::vector ( world->raws.itemdefs.shoes.begin(),world->raws.itemdefs.shoes.end() ), + item_type::SHOES ); + + // gloves type + serialize_list_itemdef ( [=] ( const std::string &token ) + { + armor->add_hands ( token ); + }, mPile->settings.armor.hands, + std::vector ( world->raws.itemdefs.gloves.begin(),world->raws.itemdefs.gloves.end() ), + item_type::GLOVES ); + + // pant type + serialize_list_itemdef ( [=] ( const std::string &token ) + { + armor->add_legs ( token ); + }, mPile->settings.armor.legs, + std::vector ( world->raws.itemdefs.pants.begin(),world->raws.itemdefs.pants.end() ), + item_type::PANTS ); + + // shield type + serialize_list_itemdef ( [=] ( const std::string &token ) + { + armor->add_shield ( token ); + }, mPile->settings.armor.shield, + std::vector ( world->raws.itemdefs.shields.begin(),world->raws.itemdefs.shields.end() ), + item_type::SHIELD ); + + // materials + FuncMaterialAllowed mat_filter = std::bind ( &StockpileSerializer::armor_mat_is_allowed, this, _1 ); + serialize_list_material ( mat_filter, [=] ( const std::string &token ) + { + armor->add_mats ( token ); + }, mPile->settings.armor.mats ); + + // other mats + serialize_list_other_mats ( mOtherMatsWeaponsArmor, [=] ( const std::string &token ) + { + armor->add_other_mats ( token ); + }, mPile->settings.armor.other_mats ); + + // quality core + serialize_list_quality ( [=] ( const std::string &token ) + { + armor->add_quality_core ( token ); + }, mPile->settings.armor.quality_core ); + + // quality total + serialize_list_quality ( [=] ( const std::string &token ) + { + armor->add_quality_total ( token ); + }, mPile->settings.armor.quality_total ); +} + +void StockpileSerializer::read_armor() +{ + if ( mBuffer.has_armor() ) + { + mPile->settings.flags.bits.armor = 1; + const StockpileSettings::ArmorSet armor = mBuffer.armor(); + debug() << "armor: " < const std::string& + { + return armor.body ( idx ); + }, armor.body_size(), &mPile->settings.armor.body, item_type::ARMOR ); + + // head type + unserialize_list_itemdef ( [=] ( const size_t & idx ) -> const std::string& + { + return armor.head ( idx ); + }, armor.head_size(), &mPile->settings.armor.head, item_type::HELM ); + + // feet type + unserialize_list_itemdef ( [=] ( const size_t & idx ) -> const std::string& + { + return armor.feet ( idx ); + }, armor.feet_size(), &mPile->settings.armor.feet, item_type::SHOES ); + + // hands type + unserialize_list_itemdef ( [=] ( const size_t & idx ) -> const std::string& + { + return armor.hands ( idx ); + }, armor.hands_size(), &mPile->settings.armor.hands, item_type::GLOVES ); + + // legs type + unserialize_list_itemdef ( [=] ( const size_t & idx ) -> const std::string& + { + return armor.legs ( idx ); + }, armor.legs_size(), &mPile->settings.armor.legs, item_type::PANTS ); + + // shield type + unserialize_list_itemdef ( [=] ( const size_t & idx ) -> const std::string& + { + return armor.shield ( idx ); + }, armor.shield_size(), &mPile->settings.armor.shield, item_type::SHIELD ); + + // materials + FuncMaterialAllowed mat_filter = std::bind ( &StockpileSerializer::armor_mat_is_allowed, this, _1 ); + unserialize_list_material ( mat_filter, [=] ( const size_t & idx ) -> const std::string& + { + return armor.mats ( idx ); + }, armor.mats_size(), &mPile->settings.armor.mats ); + + // other mats + unserialize_list_other_mats ( mOtherMatsWeaponsArmor, [=] ( const size_t & idx ) -> const std::string& + { + return armor.other_mats ( idx ); + }, armor.other_mats_size(), &mPile->settings.armor.other_mats ); + + // core quality + unserialize_list_quality ( [=] ( const size_t & idx ) -> const std::string& + { + return armor.quality_core ( idx ); + }, armor.quality_core_size(), mPile->settings.armor.quality_core ); + // total quality + unserialize_list_quality ( [=] ( const size_t & idx ) -> const std::string& + { + return armor.quality_total ( idx ); + }, armor.quality_total_size(), mPile->settings.armor.quality_total ); + } + else + { + mPile->settings.flags.bits.armor = 0; + mPile->settings.armor.body.clear(); + mPile->settings.armor.head.clear(); + mPile->settings.armor.feet.clear(); + mPile->settings.armor.hands.clear(); + mPile->settings.armor.legs.clear(); + mPile->settings.armor.shield.clear(); + mPile->settings.armor.other_mats.clear(); + mPile->settings.armor.mats.clear(); + quality_clear ( mPile->settings.armor.quality_core ); + quality_clear ( mPile->settings.armor.quality_total ); + } +} diff --git a/plugins/stockpiles/StockpileSerializer.h b/plugins/stockpiles/StockpileSerializer.h new file mode 100644 index 000000000..ff0a38241 --- /dev/null +++ b/plugins/stockpiles/StockpileSerializer.h @@ -0,0 +1,349 @@ +#pragma once + +// stockpiles plugin +#include "proto/stockpiles.pb.h" + +// dfhack +#include "modules/Materials.h" +#include "modules/Items.h" + +// df +#include "df/world.h" +#include "df/world_data.h" +#include "df/organic_mat_category.h" +#include "df/furniture_type.h" +#include "df/item_quality.h" +#include "df/item_type.h" + +// stl +#include +#include +#include +#include + +namespace df { +struct building_stockpilest; +} + + +/** + * Null buffer that acts like /dev/null for when debug is disabled + */ +class NullBuffer : public std::streambuf +{ +public: + int overflow ( int c ); +}; + +class NullStream : public std::ostream +{ +public: + NullStream(); +private: + NullBuffer m_sb; +}; + + +/** + * Class for serializing the stockpile_settings structure into a Google protobuf + */ +class StockpileSerializer +{ +public: + /** + * @param out for debugging + * @param stockpile stockpile to read or write settings to + */ + + StockpileSerializer ( df::building_stockpilest * stockpile ); + + ~StockpileSerializer(); + + void enable_debug ( std::ostream &out ); + + /** + * Since we depend on protobuf-lite, not the full lib, we copy this function from + * protobuf message.cc + */ + bool serialize_to_ostream(std::ostream* output); + + /** + * Will serialize stockpile settings to a file (overwrites existing files) + * @return success/failure + */ + bool serialize_to_file ( const std::string & file ); + + /** + * Again, copied from message.cc + */ + bool parse_from_istream(std::istream* input); + + + /** + * Read stockpile settings from file + */ + bool unserialize_from_file ( const std::string & file ); + +private: + + bool mDebug; + std::ostream * mOut; + NullStream mNull; + df::building_stockpilest * mPile; + dfstockpiles::StockpileSettings mBuffer; + std::map mOtherMatsFurniture; + std::map mOtherMatsFinishedGoods; + std::map mOtherMatsBars; + std::map mOtherMatsBlocks; + std::map mOtherMatsWeaponsArmor; + + + std::ostream & debug(); + + /** + read memory structures and serialize to protobuf + */ + void write(); + + // parse serialized data into ui indices + void read (); + + /** + * Find an enum's value based off the string label. + * @param traits the enum's trait struct + * @param token the string value in key_table + * @return the enum's value, -1 if not found + */ + template + static typename df::enum_traits::base_type linear_index ( std::ostream & out, df::enum_traits traits, const std::string &token ) + { + auto j = traits.first_item_value; + auto limit = traits.last_item_value; + // sometimes enums start at -1, which is bad news for array indexing + if ( j < 0 ) + { + j += abs ( traits.first_item_value ); + limit += abs ( traits.first_item_value ); + } + for ( ; j <= limit; ++j ) + { + // out << " linear_index("<< token <<") = table["< FuncReadImport; + // add the token to the serialized list during export + typedef std::function FuncWriteExport; + // are item's of item_type allowed? + typedef std::function FuncItemAllowed; + // is this material allowed? + typedef std::function FuncMaterialAllowed; + + // convenient struct for parsing food stockpile items + struct food_pair + { + // exporting + FuncWriteExport set_value; + std::vector * stockpile_values; + // importing + FuncReadImport get_value; + size_t serialized_count; + bool valid; + + food_pair ( FuncWriteExport s, std::vector* sp_v, FuncReadImport g, size_t count ) + : set_value ( s ) + , stockpile_values ( sp_v ) + , get_value ( g ) + , serialized_count ( count ) + , valid ( true ) + {} + food_pair(): valid( false ) {} + }; + + /** + * There are many repeated (un)serialization cases throughout the stockpile_settings structure, + * so the most common cases have been generalized into generic functions using lambdas. + * + * The basic process to serialize a stockpile_settings structure is: + * 1. loop through the list + * 2. for every element that is TRUE: + * 3. map the specific stockpile_settings index into a general material, creature, etc index + * 4. verify that type is allowed in the list (e.g., no stone in gems stockpiles) + * 5. add it to the protobuf using FuncWriteExport + * + * The unserialization process is the same in reverse. + */ + void serialize_list_organic_mat ( FuncWriteExport add_value, const std::vector * list, df::enums::organic_mat_category::organic_mat_category cat ); + + /** + * @see serialize_list_organic_mat + */ + void unserialize_list_organic_mat ( FuncReadImport get_value, size_t list_size, std::vector *pile_list, df::enums::organic_mat_category::organic_mat_category cat ); + + + /** + * @see serialize_list_organic_mat + */ + void serialize_list_item_type ( FuncItemAllowed is_allowed, FuncWriteExport add_value, const std::vector &list ); + + + /** + * @see serialize_list_organic_mat + */ + void unserialize_list_item_type ( FuncItemAllowed is_allowed, FuncReadImport read_value, int32_t list_size, std::vector *pile_list ); + + + /** + * @see serialize_list_organic_mat + */ + void serialize_list_material ( FuncMaterialAllowed is_allowed, FuncWriteExport add_value, const std::vector &list ); + + /** + * @see serialize_list_organic_mat + */ + void unserialize_list_material ( FuncMaterialAllowed is_allowed, FuncReadImport read_value, int32_t list_size, std::vector *pile_list ); + + /** + * @see serialize_list_organic_mat + */ + void serialize_list_quality ( FuncWriteExport add_value, const bool ( &quality_list ) [7] ); + + /** + * Set all values in a bool[7] to false + */ + void quality_clear ( bool ( &pile_list ) [7] ); + + + /** + * @see serialize_list_organic_mat + */ + void unserialize_list_quality ( FuncReadImport read_value, int32_t list_size, bool ( &pile_list ) [7] ); + + + /** + * @see serialize_list_organic_mat + */ + void serialize_list_other_mats ( const std::map other_mats, FuncWriteExport add_value, std::vector list ); + + /** + * @see serialize_list_organic_mat + */ + void unserialize_list_other_mats ( const std::map other_mats, FuncReadImport read_value, int32_t list_size, std::vector *pile_list ); + + + /** + * @see serialize_list_organic_mat + */ + void serialize_list_itemdef ( FuncWriteExport add_value, std::vector list, std::vector items, df::enums::item_type::item_type type ); + + + /** + * @see serialize_list_organic_mat + */ + void unserialize_list_itemdef ( FuncReadImport read_value, int32_t list_size, std::vector *pile_list, df::enums::item_type::item_type type ); + + + /** + * Given a list of other_materials and an index, return its corresponding token + * @return empty string if not found + * @see other_mats_token + */ + std::string other_mats_index ( const std::map other_mats, int idx ); + + /** + * Given a list of other_materials and a token, return its corresponding index + * @return -1 if not found + * @see other_mats_index + */ + int other_mats_token ( const std::map other_mats, const std::string & token ); + + void write_general(); + void read_general(); + + + void write_animals(); + void read_animals(); + + + food_pair food_map ( df::enums::organic_mat_category::organic_mat_category cat ); + + + void write_food(); + void read_food(); + + void furniture_setup_other_mats(); + void write_furniture(); + bool furniture_mat_is_allowed ( const DFHack::MaterialInfo &mi ); + void read_furniture(); + + bool refuse_creature_is_allowed ( const df::creature_raw *raw ); + + + void refuse_write_helper ( std::function add_value, const std::vector & list ); + + + bool refuse_type_is_allowed ( df::enums::item_type::item_type type ); + + + void write_refuse(); + void refuse_read_helper ( std::function get_value, size_t list_size, std::vector* pile_list ); + + void read_refuse(); + + bool stone_is_allowed ( const DFHack::MaterialInfo &mi ); + + void write_stone(); + + void read_stone(); + + bool ammo_mat_is_allowed ( const DFHack::MaterialInfo &mi ); + + void write_ammo(); + void read_ammo(); + bool coins_mat_is_allowed ( const DFHack::MaterialInfo &mi ); + + void write_coins(); + + void read_coins(); + + void bars_blocks_setup_other_mats(); + + bool bars_mat_is_allowed ( const DFHack::MaterialInfo &mi ); + + bool blocks_mat_is_allowed ( const DFHack::MaterialInfo &mi ); + + void write_bars_blocks(); + void read_bars_blocks(); + bool gem_mat_is_allowed ( const DFHack::MaterialInfo &mi ); + bool gem_cut_mat_is_allowed ( const DFHack::MaterialInfo &mi ); + bool gem_other_mat_is_allowed(DFHack::MaterialInfo &mi ); + + void write_gems(); + + void read_gems(); + + bool finished_goods_type_is_allowed ( df::enums::item_type::item_type type ); + void finished_goods_setup_other_mats(); + bool finished_goods_mat_is_allowed ( const DFHack::MaterialInfo &mi ); + void write_finished_goods(); + void read_finished_goods(); + void write_leather(); + void read_leather(); + void write_cloth(); + void read_cloth(); + bool wood_mat_is_allowed ( const df::plant_raw * plant ); + void write_wood(); + void read_wood(); + bool weapons_mat_is_allowed ( const DFHack::MaterialInfo &mi ); + void write_weapons(); + void read_weapons(); + void weapons_armor_setup_other_mats(); + bool armor_mat_is_allowed ( const DFHack::MaterialInfo &mi ); + void write_armor(); + void read_armor(); + +}; diff --git a/plugins/stockpiles/StockpileUtils.h b/plugins/stockpiles/StockpileUtils.h new file mode 100644 index 000000000..b7cfd93d9 --- /dev/null +++ b/plugins/stockpiles/StockpileUtils.h @@ -0,0 +1,53 @@ +#pragma once + +#include "MiscUtils.h" + +#include "df/world.h" +#include "df/world_data.h" +#include "df/creature_raw.h" +#include "df/plant_raw.h" + + + + +// Utility Functions {{{ +// A set of convenience functions for doing common lookups + + +/** + * Retrieve creature raw from index + */ +static df::creature_raw* find_creature ( int32_t idx ) +{ + return df::global::world->raws.creatures.all[idx]; +} + +/** + * Retrieve creature index from id string + * @return -1 if not found + */ +static int16_t find_creature ( const std::string &creature_id ) +{ + return linear_index ( df::global::world->raws.creatures.all, &df::creature_raw::creature_id, creature_id ); +} + +/** + * Retrieve plant raw from index +*/ +static df::plant_raw* find_plant ( size_t idx ) +{ + return df::global::world->raws.plants.all[idx]; +} + +/** + * Retrieve plant index from id string + * @return -1 if not found + */ +static size_t find_plant ( const std::string &plant_id ) +{ + return linear_index ( df::global::world->raws.plants.all, &df::plant_raw::id, plant_id ); +} + +// }}} utility Functions + + diff --git a/plugins/stockpiles/proto/.gitignore b/plugins/stockpiles/proto/.gitignore new file mode 100644 index 000000000..befabf79d --- /dev/null +++ b/plugins/stockpiles/proto/.gitignore @@ -0,0 +1,3 @@ +*.pb.cc +*.pb.cc.rule +*.pb.h diff --git a/plugins/proto/stockpiles.proto b/plugins/stockpiles/proto/stockpiles.proto similarity index 100% rename from plugins/proto/stockpiles.proto rename to plugins/stockpiles/proto/stockpiles.proto diff --git a/plugins/stockpiles/stockpiles.cpp b/plugins/stockpiles/stockpiles.cpp new file mode 100644 index 000000000..6553ae6dd --- /dev/null +++ b/plugins/stockpiles/stockpiles.cpp @@ -0,0 +1,303 @@ +#include "Core.h" +#include "Console.h" +#include "Export.h" +#include "PluginManager.h" + +#include "StockpileSerializer.h" + +#include "df/world.h" +#include "df/world_data.h" + +#include "df/ui.h" +#include "df/building_stockpilest.h" +#include "df/stockpile_settings.h" +#include "df/global_objects.h" +#include "df/viewscreen_dwarfmodest.h" + + + +// os +#include + +// stl +#include +#include + +using std::vector; +using std::string; +using std::endl; +using namespace DFHack; +using namespace df::enums; +using namespace google::protobuf; +using namespace dfstockpiles; + +using df::global::world; +using df::global::ui; +using df::global::selection_rect; + +using df::building_stockpilest; +using std::placeholders::_1; + +static command_result copystock ( color_ostream &out, vector & parameters ); +static bool copystock_guard ( df::viewscreen *top ); + +static command_result savestock ( color_ostream &out, vector & parameters ); +static bool savestock_guard ( df::viewscreen *top ); + +static command_result loadstock ( color_ostream &out, vector & parameters ); +static bool loadstock_guard ( df::viewscreen *top ); + +DFHACK_PLUGIN ( "stockpiles" ); + +DFhackCExport command_result plugin_init ( color_ostream &out, std::vector &commands ) +{ + if ( world && ui ) + { + commands.push_back ( + PluginCommand ( + "copystock", "Copy stockpile under cursor.", + copystock, copystock_guard, + " - In 'q' or 't' mode: select a stockpile and invoke in order\n" + " to switch to the 'p' stockpile creation mode, and initialize\n" + " the custom settings from the selected stockpile.\n" + " - In 'p': invoke in order to switch back to 'q'.\n" + ) + ); + commands.push_back ( + PluginCommand ( + "savestock", "Save the active stockpile's settings to a file.", + savestock, savestock_guard, + "Must be in 'q' mode and have a stockpile selected.\n" + "example: 'savestock food.dfstock' will save the settings to 'food.dfstock'\n" + "in your stockpile folder.\n" + "Omitting the filename will result in text output directly to the console\n\n" + " -d, --debug: enable debug output\n" + " : filename to save stockpile settings to (will be overwritten!)\n" + ) + ); + commands.push_back ( + PluginCommand ( + "loadstock", "Load settings from a file and apply them to the active stockpile.", + loadstock, loadstock_guard, + "Must be in 'q' mode and have a stockpile selected.\n" + "example: 'loadstock food.dfstock' will load the settings from 'food.dfstock'\n" + "in your stockpile folder and apply them to the selected stockpile.\n" + " -d, --debug: enable debug output\n" + " : filename to load stockpile settings from\n" + ) + ); + } + std::cerr << "world: " << sizeof ( df::world ) << " ui: " << sizeof ( df::ui ) + << " b_stock: " << sizeof ( building_stockpilest ) << endl; + return CR_OK; +} + +DFhackCExport command_result plugin_shutdown ( color_ostream &out ) +{ + return CR_OK; +} + +static bool copystock_guard ( df::viewscreen *top ) +{ + using namespace ui_sidebar_mode; + + if ( !Gui::dwarfmode_hotkey ( top ) ) + return false; + + switch ( ui->main.mode ) + { + case Stockpiles: + return true; + case BuildingItems: + case QueryBuilding: + return !!virtual_cast ( world->selected_building ); + default: + return false; + } +} + +static command_result copystock ( color_ostream &out, vector & parameters ) +{ + // HOTKEY COMMAND: CORE ALREADY SUSPENDED + + // For convenience: when used in the stockpiles mode, switch to 'q' + if ( ui->main.mode == ui_sidebar_mode::Stockpiles ) + { + world->selected_building = NULL; // just in case it contains some kind of garbage + ui->main.mode = ui_sidebar_mode::QueryBuilding; + selection_rect->start_x = -30000; + + out << "Switched back to query building." << endl; + return CR_OK; + } + + building_stockpilest *sp = virtual_cast ( world->selected_building ); + if ( !sp ) + { + out.printerr ( "Selected building isn't a stockpile.\n" ); + return CR_WRONG_USAGE; + } + + ui->stockpile.custom_settings = sp->settings; + ui->main.mode = ui_sidebar_mode::Stockpiles; + world->selected_stockpile_type = stockpile_category::Custom; + + out << "Stockpile options copied." << endl; + return CR_OK; +} + + +static bool savestock_guard ( df::viewscreen *top ) +{ + using namespace ui_sidebar_mode; + + if ( !Gui::dwarfmode_hotkey ( top ) ) + return false; + + switch ( ui->main.mode ) + { + case Stockpiles: + return true; + case BuildingItems: + case QueryBuilding: + return !!virtual_cast ( world->selected_building ); + default: + return false; + } +} + +static bool loadstock_guard ( df::viewscreen *top ) +{ + using namespace ui_sidebar_mode; + + if ( !Gui::dwarfmode_hotkey ( top ) ) + return false; + + switch ( ui->main.mode ) + { + case Stockpiles: + return true; + case BuildingItems: + case QueryBuilding: + return !!virtual_cast ( world->selected_building ); + default: + return false; + } +} + + + +static bool file_exists ( const std::string& filename ) +{ + struct stat buf; + if ( stat ( filename.c_str(), &buf ) != -1 ) + { + return true; + } + return false; +} + +static bool is_dfstockfile ( const std::string& filename ) +{ + return filename.rfind ( ".dfstock" ) != std::string::npos; +} + +// exporting +static command_result savestock ( color_ostream &out, vector & parameters ) +{ + building_stockpilest *sp = virtual_cast ( world->selected_building ); + if ( !sp ) + { + out.printerr ( "Selected building isn't a stockpile.\n" ); + return CR_WRONG_USAGE; + } + + if ( parameters.size() > 2 ) + { + out.printerr ( "Invalid parameters\n" ); + return CR_WRONG_USAGE; + } + + bool debug = false; + std::string file; + for ( size_t i = 0; i < parameters.size(); ++i ) + { + const std::string o = parameters.at ( i ); + if ( o == "--debug" || o == "-d" ) + debug = true; + else if ( !o.empty() && o[0] != '-' ) + { + file = o; + } + } + if ( file.empty() ) + { + out.printerr ( "You must supply a valid filename.\n" ); + return CR_WRONG_USAGE; + } + + StockpileSerializer cereal ( sp ); + if ( debug ) + cereal.enable_debug ( out ); + + if ( !is_dfstockfile ( file ) ) file += ".dfstock"; + if ( !cereal.serialize_to_file ( file ) ) + { + out.printerr ( "serialize failed\n" ); + return CR_FAILURE; + } + return CR_OK; +} + + +// importing +static command_result loadstock ( color_ostream &out, vector & parameters ) +{ + building_stockpilest *sp = virtual_cast ( world->selected_building ); + if ( !sp ) + { + out.printerr ( "Selected building isn't a stockpile.\n" ); + return CR_WRONG_USAGE; + } + + if ( parameters.size() < 1 || parameters.size() > 2 ) + { + out.printerr ( "Invalid parameters\n" ); + return CR_WRONG_USAGE; + } + + bool debug = false; + std::string file; + for ( size_t i = 0; i < parameters.size(); ++i ) + { + const std::string o = parameters.at ( i ); + if ( o == "--debug" || o == "-d" ) + debug = true; + else if ( !o.empty() && o[0] != '-' ) + { + file = o; + } + } + + if ( !is_dfstockfile ( file ) ) file += ".dfstock"; + if ( file.empty() || !file_exists ( file ) ) + { + out.printerr ( "loadstock: a .dfstock file is required to import\n" ); + return CR_WRONG_USAGE; + } + + StockpileSerializer cereal ( sp ); + if ( debug ) + cereal.enable_debug ( out ); + if ( !cereal.unserialize_from_file ( file ) ) + { + out.printerr ( "unserialization failed\n" ); + return CR_FAILURE; + } + return CR_OK; +} + + + + +