diff --git a/library/include/DataDefs.h b/library/include/DataDefs.h index d869f12aa..05be18e31 100644 --- a/library/include/DataDefs.h +++ b/library/include/DataDefs.h @@ -124,6 +124,12 @@ namespace DFHack template T *ifnull(T *a, T *b) { return a ? a : b; } + + template + inline T next_enum_item_(T v) { + v = T(int(v) + 1); + return isvalid(v) ? v : start; + } } namespace df @@ -171,6 +177,11 @@ namespace df #define ENUM_FIRST_ITEM(enum) (df::enums::enum::_first_item_of_##enum) #define ENUM_LAST_ITEM(enum) (df::enums::enum::_last_item_of_##enum) +#define ENUM_NEXT_ITEM(enum,val) \ + (DFHack::next_enum_item_(val)) +#define FOR_ENUM_ITEMS(enum,iter) \ + for(df::enum iter = ENUM_FIRST_ITEM(enum); iter < ENUM_LAST_ITEM(enum); iter = df::enum(1+int(iter))) + namespace df { #define DF_KNOWN_GLOBALS \ GLOBAL(cursor,cursor) \ diff --git a/library/include/modules/Materials.h b/library/include/modules/Materials.h index 8c3ce6846..372d9d491 100644 --- a/library/include/modules/Materials.h +++ b/library/include/modules/Materials.h @@ -34,11 +34,80 @@ distribution. #include "Types.h" #include "BitArray.h" +#include "DataDefs.h" +#include "df/material.h" + #include #include +namespace df +{ + struct item; + struct inorganic_raw; + struct plant_raw; + struct creature_raw; + struct historical_figure; + union job_material_category; +} + namespace DFHack { + struct DFHACK_EXPORT MaterialInfo { + static const int NUM_BUILTIN = 19; + static const int GROUP_SIZE = 200; + static const int CREATURE_BASE = NUM_BUILTIN; + static const int FIGURE_BASE = NUM_BUILTIN + GROUP_SIZE; + static const int PLANT_BASE = NUM_BUILTIN + GROUP_SIZE*2; + static const int END_BASE = NUM_BUILTIN + GROUP_SIZE*3; + + int16_t type; + int32_t index; + + df::material *material; + + enum Mode { + Builtin, + Inorganic, + Creature, + Plant + }; + Mode mode; + + int16_t subtype; + df::inorganic_raw *inorganic; + df::creature_raw *creature; + df::plant_raw *plant; + + df::historical_figure *figure; + + public: + MaterialInfo(int16_t type = -1, int32_t index = -1) { decode(type, index); } + MaterialInfo(df::item *item) { decode(item); } + + bool isValid() const { return material != NULL; } + + bool decode(int16_t type, int32_t index = -1); + bool decode(df::item *item); + + bool find(const std::string &token, const std::string &subtoken = std::string()); + bool findBuiltin(const std::string &token); + bool findInorganic(const std::string &token); + bool findPlant(const std::string &token, const std::string &subtoken); + bool findCreature(const std::string &token, const std::string &subtoken); + + std::string toString(uint16_t temp = 10015, bool named = true); + + df::craft_material_class getCraftClass(); + bool matches(const df::job_material_category &cat); + }; + + inline bool operator== (const MaterialInfo &a, const MaterialInfo &b) { + return a.type == b.type && a.index == b.index; + } + inline bool operator!= (const MaterialInfo &a, const MaterialInfo &b) { + return a.type != b.type || a.index != b.index; + } + typedef int32_t t_materialIndex; typedef int16_t t_materialType, t_itemType, t_itemSubtype; diff --git a/library/modules/Materials.cpp b/library/modules/Materials.cpp index c77a62bd7..2e333f5f9 100644 --- a/library/modules/Materials.cpp +++ b/library/modules/Materials.cpp @@ -40,7 +40,239 @@ using namespace std; #include "ModuleFactory.h" #include "Core.h" +#include "MiscUtils.h" + +#include "df/world.h" +#include "df/item.h" +#include "df/inorganic_raw.h" +#include "df/plant_raw.h" +#include "df/creature_raw.h" +#include "df/historical_figure.h" + +#include "df/job_material_category.h" +#include "df/matter_state.h" + using namespace DFHack; +using namespace df::enums; + +bool MaterialInfo::decode(df::item *item) +{ + if (!item) + decode(-1); + else + decode(item->getActualMaterial(), item->getActualMaterialIndex()); +} + +bool MaterialInfo::decode(int16_t type, int32_t index) +{ + this->type = type; + this->index = index; + + material = NULL; + mode = Builtin; subtype = 0; + inorganic = NULL; plant = NULL; creature = NULL; + figure = NULL; + + df::world_raws &raws = df::global::world->raws; + + if (type < 0 || type >= sizeof(raws.mat_table.builtin)/sizeof(void*)) + return false; + + if (index < 0) + { + material = raws.mat_table.builtin[type]; + } + else if (type == 0) + { + mode = Inorganic; + inorganic = df::inorganic_raw::find(index); + if (!inorganic) + return false; + material = &inorganic->material; + } + else if (type < CREATURE_BASE) + { + material = raws.mat_table.builtin[type]; + } + else if (type < FIGURE_BASE) + { + mode = Creature; + subtype = type-CREATURE_BASE; + creature = df::creature_raw::find(index); + if (!creature || subtype >= creature->material.size()) + return false; + material = creature->material[subtype]; + } + else if (type < PLANT_BASE) + { + mode = Creature; + subtype = type-FIGURE_BASE; + figure = df::historical_figure::find(index); + if (!figure) + return false; + creature = df::creature_raw::find(figure->race); + if (!creature || subtype >= creature->material.size()) + return false; + material = creature->material[subtype]; + } + else if (type < END_BASE) + { + mode = Plant; + subtype = type-PLANT_BASE; + plant = df::plant_raw::find(index); + if (!plant || subtype >= plant->material.size()) + return false; + material = plant->material[subtype]; + } + else + { + material = raws.mat_table.builtin[type]; + } + + return (material != NULL); +} + +bool MaterialInfo::find(const std::string &token, const std::string &subtoken) +{ + if (findBuiltin(token)) + return true; + if (subtoken.empty()) + { + if (findInorganic(token)) + return true; + } + else + { + if (findPlant(token, subtoken)) + return true; + if (findCreature(token, subtoken)) + return true; + } + return false; +} + +bool MaterialInfo::findBuiltin(const std::string &token) +{ + df::world_raws &raws = df::global::world->raws; + for (int i = 1; i < NUM_BUILTIN; i++) + if (raws.mat_table.builtin[i]->id == token) + return decode(i, -1); + return decode(-1); +} + +bool MaterialInfo::findInorganic(const std::string &token) +{ + df::world_raws &raws = df::global::world->raws; + for (unsigned i = 0; i < raws.inorganics.size(); i++) + { + df::inorganic_raw *p = raws.inorganics[i]; + if (p->id == token) + return decode(0, i); + } + return decode(-1); +} + +bool MaterialInfo::findPlant(const std::string &token, const std::string &subtoken) +{ + df::world_raws &raws = df::global::world->raws; + for (unsigned i = 0; i < raws.plants.all.size(); i++) + { + df::plant_raw *p = raws.plants.all[i]; + if (p->id != token) + continue; + + for (unsigned j = 0; j < p->material.size(); j++) + if (p->material[j]->id == subtoken) + return decode(PLANT_BASE+j, i); + + break; + } + return decode(-1); +} + +bool MaterialInfo::findCreature(const std::string &token, const std::string &subtoken) +{ + df::world_raws &raws = df::global::world->raws; + for (unsigned i = 0; i < raws.creatures.all.size(); i++) + { + df::creature_raw *p = raws.creatures.all[i]; + if (p->creature_id != token) + continue; + + for (unsigned j = 0; j < p->material.size(); j++) + if (p->material[j]->id == subtoken) + return decode(CREATURE_BASE+j, i); + + break; + } + return decode(-1); +} + +std::string MaterialInfo::toString(uint16_t temp, bool named) +{ + if (type == -1) + return "NONE"; + if (!material) + return stl_sprintf("INVALID %d:%d", type, index); + + df::matter_state state = matter_state::Solid; + if (temp >= material->heat.melting_point) + state = matter_state::Liquid; + if (temp >= material->heat.boiling_point) + state = matter_state::Gas; + + std::string name = material->state_name[state]; + if (!material->prefix.empty()) + name = material->prefix + " " + name; + + if (named && figure) + name += stl_sprintf(" of HF %d", index); + return name; +} + +df::craft_material_class MaterialInfo::getCraftClass() +{ + if (!material) + return craft_material_class::None; + + if (type == 0 && index == -1) + return craft_material_class::Stone; + + FOR_ENUM_ITEMS(material_flags, i) + { + df::craft_material_class ccv = ENUM_ATTR(material_flags, type, i); + if (ccv == craft_material_class::None) + continue; + if (material->flags.is_set(i)) + return ccv; + } + + return craft_material_class::None; +} + +bool MaterialInfo::matches(const df::job_material_category &cat) +{ + if (!material) + return false; + + using namespace df::enums::material_flags; +#define TEST(bit,flag) if (cat.bits.bit && material->flags.is_set(flag)) return true; + TEST(plant, STRUCTURAL_PLANT_MAT); + TEST(wood, WOOD); + TEST(cloth, THREAD_PLANT); + TEST(silk, SILK); + TEST(leather, LEATHER); + TEST(bone, BONE); + TEST(shell, SHELL); + TEST(wood2, WOOD); + TEST(soap, SOAP); + TEST(tooth, TOOTH); + TEST(horn, HORN); + TEST(pearl, PEARL); + TEST(yarn, YARN); +#undef TEST + return false; +} Module* DFHack::createMaterials() { diff --git a/library/xml b/library/xml index c99be2349..f50bf32d3 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit c99be2349fef32db026af7541676e2e4f24e4636 +Subproject commit f50bf32d3d4d29667720d648bf455896072c77f9 diff --git a/plugins/initflags.cpp b/plugins/initflags.cpp index 5671619e8..c7a634b78 100644 --- a/plugins/initflags.cpp +++ b/plugins/initflags.cpp @@ -51,10 +51,7 @@ DFhackCExport command_result twaterlvl(Core * c, vector & parameters) DFhackCExport command_result tidlers(Core * c, vector & parameters) { // HOTKEY COMMAND: CORE ALREADY SUSPENDED - df::d_init_idlers iv = df::d_init_idlers(int(d_init->idlers) + 1); - if (!d_init_idlers::is_valid(iv)) - iv = ENUM_FIRST_ITEM(d_init_idlers); - d_init->idlers = iv; - c->con << "Toggled the display of idlers to " << ENUM_KEY_STR(d_init_idlers, iv) << endl; + d_init->idlers = ENUM_NEXT_ITEM(d_init_idlers, d_init->idlers); + c->con << "Toggled the display of idlers to " << ENUM_KEY_STR(d_init_idlers, d_init->idlers) << endl; return CR_OK; } diff --git a/plugins/jobutils.cpp b/plugins/jobutils.cpp index 1e0971dff..005797ae7 100644 --- a/plugins/jobutils.cpp +++ b/plugins/jobutils.cpp @@ -4,6 +4,8 @@ #include #include +#include + #include #include #include @@ -13,11 +15,6 @@ #include #include #include -#include -#include -#include -#include -#include #include #include #include @@ -28,156 +25,6 @@ using std::endl; using namespace DFHack; using namespace df::enums; -/* - * TODO: This should be in core: - */ - -struct MaterialInfo { - int16_t type; - int32_t index; - - df::material *material; - - enum Mode { - Builtin, - Inorganic, - Creature, - Plant - }; - Mode mode; - - int16_t subtype; - df::inorganic_raw *inorganic; - df::creature_raw *creature; - df::plant_raw *plant; - - df::historical_figure *figure; - - MaterialInfo(int16_t type = -1, int32_t index = -1) { - decode(type, index); - } - - bool isValid() const { return material != NULL; } - bool decode(int16_t type, int32_t index = -1); - - bool findInorganic(const std::string &token); - - std::string toString(uint16_t temp = 10015, bool named = true); -}; - -inline bool operator== (const MaterialInfo &a, const MaterialInfo &b) { - return a.type == b.type && a.index == b.index; -} -inline bool operator!= (const MaterialInfo &a, const MaterialInfo &b) { - return a.type != b.type || a.index != b.index; -} - -bool MaterialInfo::decode(int16_t type, int32_t index) -{ - this->type = type; - this->index = index; - - material = NULL; - mode = Builtin; subtype = 0; - inorganic = NULL; plant = NULL; creature = NULL; - figure = NULL; - - if (type < 0 || type >= 659) - return false; - - df::world_raws &raws = df::global::world->raws; - - if (index < 0) - { - material = raws.mat_table.builtin[type]; - } - else if (type == 0) - { - mode = Inorganic; - inorganic = df::inorganic_raw::find(index); - if (!inorganic) - return false; - material = &inorganic->material; - } - else if (type < 19) - { - material = raws.mat_table.builtin[type]; - } - else if (type < 219) - { - mode = Creature; - subtype = type-19; - creature = df::creature_raw::find(index); - if (!creature || subtype >= creature->material.size()) - return false; - material = creature->material[subtype]; - } - else if (type < 419) - { - mode = Creature; - subtype = type-219; - figure = df::historical_figure::find(index); - if (!figure) - return false; - creature = df::creature_raw::find(figure->race); - if (!creature || subtype >= creature->material.size()) - return false; - material = creature->material[subtype]; - } - else if (type < 619) - { - mode = Plant; - subtype = type-419; - plant = df::plant_raw::find(index); - if (!plant || subtype >= plant->material.size()) - return false; - material = plant->material[subtype]; - } - else - { - material = raws.mat_table.builtin[type]; - } - - return (material != NULL); -} - -bool MaterialInfo::findInorganic(const std::string &token) -{ - df::world_raws &raws = df::global::world->raws; - for (unsigned i = 0; i < raws.inorganics.size(); i++) - { - df::inorganic_raw *p = raws.inorganics[i]; - if (p->id == token) - return decode(0, i); - } - return decode(-1); -} - -std::string MaterialInfo::toString(uint16_t temp, bool named) -{ - if (type == -1) - return "NONE"; - if (!material) - return stl_sprintf("INVALID %d:%d", type, index); - - int idx = 0; - if (temp >= material->heat.melting_point) - idx = 1; - if (temp >= material->heat.boiling_point) - idx = 2; - std::string name = material->state_name[idx]; - if (!material->prefix.empty()) - name = material->prefix + " " + name; - - if (named && figure) - name += stl_sprintf(" of HF %d", index); - return name; -} - -/* - * Plugin-specific code starts here. - */ - using df::global::world; using df::global::ui; using df::global::ui_build_selector; @@ -277,6 +124,7 @@ static command_result job_material_in_job(Core *c, MaterialInfo &new_mat) } df::job *job = selected->jobs[idx]; + MaterialInfo cur_mat(job->matType, job->matIndex); if (!cur_mat.isValid() || cur_mat.type != 0) @@ -286,26 +134,17 @@ static command_result job_material_in_job(Core *c, MaterialInfo &new_mat) return CR_FAILURE; } - // Verify that the new material matches the old one - df::material_flags rq_flag = material_flags::IS_STONE; - if (cur_mat.mode != MaterialInfo::Builtin) + df::craft_material_class old_class = cur_mat.getCraftClass(); + if (old_class == craft_material_class::None) { - if (cur_mat.material->flags.is_set(material_flags::IS_GEM)) - rq_flag = material_flags::IS_GEM; - else if (cur_mat.material->flags.is_set(material_flags::IS_METAL)) - rq_flag = material_flags::IS_METAL; - else if (!cur_mat.material->flags.is_set(rq_flag)) - { - c->con.printerr("Unexpected current material type: %s\n", - cur_mat.toString().c_str()); - return CR_FAILURE; - } + c->con.printerr("Unexpected current material type: %s\n", + cur_mat.toString().c_str()); + return CR_FAILURE; } - - if (!new_mat.material->flags.is_set(rq_flag)) + if (new_mat.getCraftClass() != old_class) { c->con.printerr("New material %s does not satisfy requirement: %s\n", - new_mat.toString().c_str(), ENUM_KEY_STR(material_flags, rq_flag)); + new_mat.toString().c_str(), ENUM_KEY_STR(craft_material_class, old_class)); return CR_FAILURE; } @@ -334,40 +173,43 @@ static command_result job_material_in_job(Core *c, MaterialInfo &new_mat) } c->con << "Applied material '" << new_mat.toString() - << " jo job " << ENUM_KEY_STR(job_type,job->job_id) << endl; + << "' to job " << ENUM_KEY_STR(job_type,job->job_id) << endl; return CR_OK; } -static command_result job_material_in_build(Core *c, MaterialInfo &new_mat) +static bool build_choice_matches(df::ui_build_item_req *req, df::build_req_choicest *choice, + MaterialInfo &new_mat) { - df::ui_build_item_req *req = ui_build_selector->requirements[ui_build_selector->req_index]; - - for (unsigned i = 0; i < ui_build_selector->choices.size(); i++) + if (VIRTUAL_CAST_VAR(gen, df::build_req_choice_genst, choice)) { - df::build_req_choicest *choice = ui_build_selector->choices[i]; - bool found = false; - - if (VIRTUAL_CAST_VAR(gen, df::build_req_choice_genst, choice)) + if (gen->matType == new_mat.type && + gen->matIndex == new_mat.index && + gen->used_count < gen->candidates.size()) { - if (gen->matType == new_mat.type && - gen->matIndex == new_mat.index && - gen->used_count < gen->candidates.size()) - { - found = true; - } + return true; } - else if (VIRTUAL_CAST_VAR(spec, df::build_req_choice_specst, choice)) + } + else if (VIRTUAL_CAST_VAR(spec, df::build_req_choice_specst, choice)) + { + if (spec->candidate && + spec->candidate->getActualMaterial() == new_mat.type && + spec->candidate->getActualMaterialIndex() == new_mat.index && + !req->candidate_selected[spec->candidate_id]) { - if (spec->candidate && - spec->candidate->getActualMaterial() == new_mat.type && - spec->candidate->getActualMaterialIndex() == new_mat.index && - !req->candidate_selected[spec->candidate_id]) - { - found = true; - } + return true; } + } + + return false; +} - if (found) +static command_result job_material_in_build(Core *c, MaterialInfo &new_mat) +{ + df::ui_build_item_req *req = ui_build_selector->requirements[ui_build_selector->req_index]; + + for (unsigned i = 0; i < ui_build_selector->choices.size(); i++) + { + if (build_choice_matches(req, ui_build_selector->choices[i], new_mat)) { ui_build_selector->sel_index = i; return CR_OK;