From bfcaca92663bdabd9621384abeaabb0edaa13761 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Mon, 2 Jan 2012 15:18:23 +0400 Subject: [PATCH 1/9] Update xml defs with more ui info. --- Memory.xml | 3 +++ library/include/DataDefs.h | 1 + library/xml | 2 +- 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/Memory.xml b/Memory.xml index 5360d6481..729759445 100644 --- a/Memory.xml +++ b/Memory.xml @@ -1093,6 +1093,7 @@
+
@@ -2343,6 +2344,7 @@
+
cmake @@ -3227,6 +3229,7 @@
+
diff --git a/library/include/DataDefs.h b/library/include/DataDefs.h index 75b2ab2e5..9f3bcf34d 100644 --- a/library/include/DataDefs.h +++ b/library/include/DataDefs.h @@ -178,6 +178,7 @@ namespace df { GLOBAL(d_init,d_init) \ SIMPLE_GLOBAL(ui_look_cursor,int) \ SIMPLE_GLOBAL(ui_workshop_job_cursor,int) \ + SIMPLE_GLOBAL(ui_workshop_in_add,bool) \ GLOBAL(ui_sidebar_menus,ui_sidebar_menus) \ GLOBAL(ui_build_selector,ui_build_selector) \ GLOBAL(ui_look_list,ui_look_list) diff --git a/library/xml b/library/xml index 7a730830c..7b89e5958 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit 7a730830c515fea701ff73b33cb604f48a4531fc +Subproject commit 7b89e595811ed81b012a805131aa10a89d451e76 From 326c58f7935cb39206c52646a38ff19502ebb966 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Mon, 2 Jan 2012 18:46:24 +0400 Subject: [PATCH 2/9] Add a plugin to tweak inorganic materials in jobs. It also auto-seeks in the build item list if used in that context. --- library/MiscUtils.cpp | 27 ++- library/include/DataDefs.h | 4 + library/include/MiscUtils.h | 3 + plugins/CMakeLists.txt | 1 + plugins/jobutils.cpp | 402 ++++++++++++++++++++++++++++++++++++ 5 files changed, 435 insertions(+), 2 deletions(-) create mode 100644 plugins/jobutils.cpp diff --git a/library/MiscUtils.cpp b/library/MiscUtils.cpp index 47deea04c..72e3eae28 100644 --- a/library/MiscUtils.cpp +++ b/library/MiscUtils.cpp @@ -27,8 +27,6 @@ distribution. #include "Core.h" #include "MiscUtils.h" - - #ifndef LINUX_BUILD #include #else @@ -36,6 +34,31 @@ distribution. #include #endif +#include + +std::string stl_sprintf(const char *fmt, ...) { + va_list lst; + va_start(lst, fmt); + std::string rv = stl_vsprintf(fmt, lst); + va_end(lst); + return rv; +} + +std::string stl_vsprintf(const char *fmt, va_list args) { + std::vector buf; + buf.resize(4096); + for (;;) { + int rsz = vsnprintf(&buf[0], buf.size(), fmt, args); + + if (rsz < 0) + buf.resize(buf.size()*2); + else if (unsigned(rsz) > buf.size()) + buf.resize(rsz+1); + else + return std::string(&buf[0], rsz); + } +} + #ifdef LINUX_BUILD // Linux uint64_t GetTimeMs64() { diff --git a/library/include/DataDefs.h b/library/include/DataDefs.h index 9f3bcf34d..d869f12aa 100644 --- a/library/include/DataDefs.h +++ b/library/include/DataDefs.h @@ -111,11 +111,15 @@ namespace DFHack return T::_identity.is_instance(ptr) ? static_cast(ptr) : NULL; } +#define VIRTUAL_CAST_VAR(var,type,input) type *var = virtual_cast(input) + template inline T *strict_virtual_cast(virtual_ptr ptr) { return T::_identity.is_direct_instance(ptr) ? static_cast(ptr) : NULL; } +#define STRICT_VIRTUAL_CAST_VAR(var,type,input) type *var = strict_virtual_cast(input) + void InitDataDefGlobals(Core *core); template diff --git a/library/include/MiscUtils.h b/library/include/MiscUtils.h index 833e74104..8959a766b 100644 --- a/library/include/MiscUtils.h +++ b/library/include/MiscUtils.h @@ -102,3 +102,6 @@ CT *binsearch_in_vector(std::vector &vec, FT CT::*field, AT value) * source: http://stackoverflow.com/questions/1861294/how-to-calculate-execution-time-of-a-code-snippet-in-c */ DFHACK_EXPORT uint64_t GetTimeMs64(); + +DFHACK_EXPORT std::string stl_sprintf(const char *fmt, ...); +DFHACK_EXPORT std::string stl_vsprintf(const char *fmt, va_list args); diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 133905727..d28baeb6f 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -61,6 +61,7 @@ DFHACK_PLUGIN(initflags initflags.cpp) DFHACK_PLUGIN(stockpiles stockpiles.cpp) DFHACK_PLUGIN(rename rename.cpp) DFHACK_PLUGIN(fixwagons fixwagons.cpp) +DFHACK_PLUGIN(jobutils jobutils.cpp) #DFHACK_PLUGIN(versionosd versionosd.cpp) # this is the skeleton plugin. If you want to make your own, make a copy and then change it diff --git a/plugins/jobutils.cpp b/plugins/jobutils.cpp new file mode 100644 index 000000000..1e0971dff --- /dev/null +++ b/plugins/jobutils.cpp @@ -0,0 +1,402 @@ +#include "Core.h" +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using std::vector; +using std::string; +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; +using df::global::ui_workshop_job_cursor; + +static bool job_material_hotkey(Core *c, df::viewscreen *top); +static command_result job_material(Core *c, vector & parameters); + +DFhackCExport const char * plugin_name ( void ) +{ + return "jobutils"; +} + +DFhackCExport command_result plugin_init (Core *c, std::vector &commands) +{ + commands.clear(); + if (world && ui && (ui_workshop_job_cursor || ui_build_selector)) { + commands.push_back( + PluginCommand( + "job-material", "Alter the material of the selected job.", + job_material, job_material_hotkey, + " job-material \n" + "Intended to be used as a keybinding:\n" + " - In 'q' mode, when a job is highlighted within a workshop\n" + " or furnace, changes the material of the job.\n" + " - In 'b' mode, during selection of building components\n" + " positions the cursor over the first available choice\n" + " with the matching material.\n" + ) + ); + } + return CR_OK; +} + +DFhackCExport command_result plugin_shutdown ( Core * c ) +{ + return CR_OK; +} + +static bool job_material_hotkey(Core *c, df::viewscreen *top) +{ + using namespace ui_sidebar_mode; + + if (!dwarfmode_hotkey(c,top)) + return false; + + switch (ui->main.mode) { + case QueryBuilding: + { + if (!ui_workshop_job_cursor) // allow missing + return false; + + df::building *selected = world->selected_building; + if (!virtual_cast(selected) && + !virtual_cast(selected)) + return false; + + // No jobs? + if (selected->jobs.empty() || + selected->jobs[0]->job_id == job_type::DestroyBuilding) + return false; + + // Add job gui activated? + if (df::global::ui_workshop_in_add && // allow missing + *df::global::ui_workshop_in_add) + return false; + + return true; + }; + case Build: + { + if (!ui_build_selector) // allow missing + return false; + + // Not selecting, or no choices? + if (ui_build_selector->building_type < 0 || + ui_build_selector->stage != 2 || + ui_build_selector->choices.empty()) + return false; + + return true; + }; + default: + return false; + } +} + +static command_result job_material_in_job(Core *c, MaterialInfo &new_mat) +{ + df::building *selected = world->selected_building; + int idx = *ui_workshop_job_cursor; + + if (idx < 0 || idx >= selected->jobs.size()) + { + c->con.printerr("Invalid job cursor index: %d\n", idx); + return CR_FAILURE; + } + + df::job *job = selected->jobs[idx]; + MaterialInfo cur_mat(job->matType, job->matIndex); + + if (!cur_mat.isValid() || cur_mat.type != 0) + { + c->con.printerr("Current job material isn't inorganic: %s\n", + cur_mat.toString().c_str()); + 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) + { + 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; + } + } + + if (!new_mat.material->flags.is_set(rq_flag)) + { + c->con.printerr("New material %s does not satisfy requirement: %s\n", + new_mat.toString().c_str(), ENUM_KEY_STR(material_flags, rq_flag)); + return CR_FAILURE; + } + + for (unsigned i = 0; i < job->job_items.size(); i++) + { + df::job_item *item = job->job_items[i]; + MaterialInfo item_mat(item->matType, item->matIndex); + + if (item_mat != cur_mat) + { + c->con.printerr("Job item %d has different material: %s\n", + i, item_mat.toString().c_str()); + return CR_FAILURE; + } + } + + // Apply the substitution + job->matType = new_mat.type; + job->matIndex = new_mat.index; + + for (unsigned i = 0; i < job->job_items.size(); i++) + { + df::job_item *item = job->job_items[i]; + item->matType = new_mat.type; + item->matIndex = new_mat.index; + } + + c->con << "Applied material '" << new_mat.toString() + << " jo 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) +{ + 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++) + { + 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()) + { + found = true; + } + } + 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]) + { + found = true; + } + } + + if (found) + { + ui_build_selector->sel_index = i; + return CR_OK; + } + } + + c->con.printerr("Could not find material in list: %s\n", new_mat.toString().c_str()); + return CR_FAILURE; +} + +static command_result job_material(Core * c, vector & parameters) +{ + // HOTKEY COMMAND: CORE ALREADY SUSPENDED + + MaterialInfo new_mat; + if (parameters.size() == 1) + { + if (!new_mat.findInorganic(parameters[0])) { + c->con.printerr("Could not find inorganic material: %s\n", parameters[0].c_str()); + return CR_WRONG_USAGE; + } + } + else + return CR_WRONG_USAGE; + + if (ui->main.mode == ui_sidebar_mode::QueryBuilding) + return job_material_in_job(c, new_mat); + if (ui->main.mode == ui_sidebar_mode::Build) + return job_material_in_build(c, new_mat); + + return CR_WRONG_USAGE; +} From fb736a85567b643c900c5057490c3c568455de0b Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Tue, 3 Jan 2012 13:07:49 +0400 Subject: [PATCH 3/9] Support more ways to binsearch in a vector. --- library/include/MiscUtils.h | 110 +++++++++++++++++++++++++++++++----- library/xml | 2 +- 2 files changed, 96 insertions(+), 16 deletions(-) diff --git a/library/include/MiscUtils.h b/library/include/MiscUtils.h index 8959a766b..05be881ee 100644 --- a/library/include/MiscUtils.h +++ b/library/include/MiscUtils.h @@ -66,36 +66,116 @@ void print_bits ( T val, DFHack::Console& out ) out.print(strs.str().c_str()); } -//FIXME: Error 8 error C4519: default template arguments are only allowed on a class template -template -CT *binsearch_in_vector(std::vector &vec, FT CT::*field, AT value) +/* + * Binary search in vectors. + */ + +template +int binsearch_index(const std::vector &vec, FT key, bool exact = true) +{ + // Returns the index of the value >= the key + int min = -1, max = (int)vec.size(); + const FT *p = vec.data(); + for (;;) + { + int mid = (min + max)>>1; + if (mid == min) + return exact ? -1 : max; + FT midv = p[mid]; + if (midv == key) + return mid; + else if (midv < key) + min = mid; + else + max = mid; + } +} + +template +int binsearch_index(const std::vector &vec, FT CT::*field, FT key, bool exact = true) { + // Returns the index of the value >= the key int min = -1, max = (int)vec.size(); - CT **p = vec.data(); - FT key = (FT)value; + CT *const *p = vec.data(); for (;;) { int mid = (min + max)>>1; if (mid == min) - { - return NULL; - } + return exact ? -1 : max; FT midv = p[mid]->*field; if (midv == key) - { - return p[mid]; - } + return mid; else if (midv < key) - { min = mid; - } else - { max = mid; - } } } +template +inline int binsearch_index(const std::vector &vec, typename CT::key_field_type key, bool exact = true) +{ + return CT::binsearch_index(vec, key, exact); +} + +template +inline int binsearch_index(const std::vector &vec, typename CT::key_pointer_type key, bool exact = true) +{ + return CT::binsearch_index(vec, key, exact); +} + +template +inline bool vector_contains(const std::vector &vec, KT key) +{ + return binsearch_index(vec, key) >= 0; +} + +template +inline bool vector_contains(const std::vector &vec, FT CT::*field, FT key) +{ + return binsearch_index(vec, field, key) >= 0; +} + +template +unsigned insert_into_vector(std::vector &vec, FT key, bool *inserted = NULL) +{ + unsigned pos = (unsigned)binsearch_index(vec, key, false); + bool to_ins = (pos >= vec.size() || vec[pos] != key); + if (inserted) *inserted = to_ins; + if (to_ins) + vec.insert(vec.begin()+pos,key); + return pos; +} + +template +unsigned insert_into_vector(std::vector &vec, FT CT::*field, CT *obj, bool *inserted = NULL) +{ + unsigned pos = (unsigned)binsearch_index(vec, field, obj->*field, false); + bool to_ins = (pos >= vec.size() || vec[pos] != obj); + if (inserted) *inserted = to_ins; + if (to_ins) + vec.insert(vec.begin()+pos,obj); + return pos; +} + +template +CT *binsearch_in_vector(const std::vector &vec, KT value) +{ + int idx = binsearch_index(vec, value); + return idx < 0 ? NULL : vec[idx]; +} + +template +CT *binsearch_in_vector(const std::vector &vec, FT CT::*field, FT value) +{ + int idx = binsearch_index(vec, field, value); + return idx < 0 ? NULL : vec[idx]; +} + +/* + * MISC + */ + /** * Returns the amount of milliseconds elapsed since the UNIX epoch. * Works on both windows and linux. diff --git a/library/xml b/library/xml index 7b89e5958..c99be2349 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit 7b89e595811ed81b012a805131aa10a89d451e76 +Subproject commit c99be2349fef32db026af7541676e2e4f24e4636 From f8814909a95597ac05380f274361651a9ce8e499 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Tue, 3 Jan 2012 19:25:55 +0400 Subject: [PATCH 4/9] Move the MaterialInfo class to the main library. --- library/include/DataDefs.h | 11 ++ library/include/modules/Materials.h | 69 +++++++++ library/modules/Materials.cpp | 232 ++++++++++++++++++++++++++++ library/xml | 2 +- plugins/initflags.cpp | 7 +- plugins/jobutils.cpp | 232 +++++----------------------- 6 files changed, 352 insertions(+), 201 deletions(-) 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; From 1d1cd638003a74d4543ff7348ee29c27f03ed78a Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Tue, 3 Jan 2012 22:56:05 +0400 Subject: [PATCH 5/9] Implement printing job details. --- library/DataDefs.cpp | 37 ++++++++++ library/include/DataDefs.h | 13 ++++ library/xml | 2 +- plugins/jobutils.cpp | 139 +++++++++++++++++++++++++++++++++++-- 4 files changed, 185 insertions(+), 6 deletions(-) diff --git a/library/DataDefs.cpp b/library/DataDefs.cpp index bcdb388b5..eb05038ef 100644 --- a/library/DataDefs.cpp +++ b/library/DataDefs.cpp @@ -35,6 +35,8 @@ distribution. // must be last due to MS stupidity #include "DataDefs.h" +#include "MiscUtils.h" + using namespace DFHack; /* The order of global object constructor calls is @@ -160,6 +162,41 @@ void virtual_identity::Init(Core *core) } } +std::string DFHack::bitfieldToString(const void *p, int size, const bitfield_item_info *items) +{ + std::string res; + const char *data = (const char*)p; + + for (int i = 0; i < size*8; i++) { + unsigned v; + + if (items[i].size > 1) { + unsigned pdv = *(unsigned*)&data[i/8]; + v = (pdv >> (i%8)) & ((1 << items[i].size)-1); + } else { + v = (data[i/8]>>(i%8)) & 1; + } + + if (v) { + if (!res.empty()) + res += ' '; + + if (items[i].name) + res += items[i].name; + else + res += stl_sprintf("UNK_%d", i); + + if (items[i].size > 1) + res += stl_sprintf("=%u", v); + } + + if (items[i].size > 1) + i += items[i].size-1; + } + + return res; +} + #define SIMPLE_GLOBAL(name,tname) \ tname *df::global::name = NULL; #define GLOBAL(name,tname) SIMPLE_GLOBAL(name,df::tname) diff --git a/library/include/DataDefs.h b/library/include/DataDefs.h index 05be18e31..d3899854d 100644 --- a/library/include/DataDefs.h +++ b/library/include/DataDefs.h @@ -130,6 +130,18 @@ namespace DFHack v = T(int(v) + 1); return isvalid(v) ? v : start; } + + struct bitfield_item_info { + const char *name; + int size; + }; + + DFHACK_EXPORT std::string bitfieldToString(const void *p, int size, const bitfield_item_info *items); + + template + inline std::string bitfieldToString(const T &val) { + return bitfieldToString(&val.whole, sizeof(val.whole), val.get_items()); + } } namespace df @@ -137,6 +149,7 @@ namespace df using DFHack::virtual_ptr; using DFHack::virtual_identity; using DFHack::virtual_class; + using DFHack::bitfield_item_info; using DFHack::BitArray; template diff --git a/library/xml b/library/xml index f50bf32d3..3455acedc 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit f50bf32d3d4d29667720d648bf455896072c77f9 +Subproject commit 3455acedc525473cd8c5403d225a6078b661dfc8 diff --git a/plugins/jobutils.cpp b/plugins/jobutils.cpp index 005797ae7..83503ddc9 100644 --- a/plugins/jobutils.cpp +++ b/plugins/jobutils.cpp @@ -30,9 +30,14 @@ using df::global::ui; using df::global::ui_build_selector; using df::global::ui_workshop_job_cursor; +static bool wokshop_job_hotkey(Core *c, df::viewscreen *top); +static bool build_selector_hotkey(Core *c, df::viewscreen *top); + static bool job_material_hotkey(Core *c, df::viewscreen *top); static command_result job_material(Core *c, vector & parameters); +static command_result job_cmd(Core *c, vector & parameters); + DFhackCExport const char * plugin_name ( void ) { return "jobutils"; @@ -41,7 +46,19 @@ DFhackCExport const char * plugin_name ( void ) DFhackCExport command_result plugin_init (Core *c, std::vector &commands) { commands.clear(); - if (world && ui && (ui_workshop_job_cursor || ui_build_selector)) { + if (!world || !ui) + return CR_FAILURE; + + commands.push_back( + PluginCommand( + "job", "General job query and manipulation.", + job_cmd, false, + " job query - Print details of the current job.\n" + " job list - Print details of all jobs in the workshop.\n" + ) + ); + + if (ui_workshop_job_cursor || ui_build_selector) { commands.push_back( PluginCommand( "job-material", "Alter the material of the selected job.", @@ -56,6 +73,7 @@ DFhackCExport command_result plugin_init (Core *c, std::vector & ) ); } + return CR_OK; } @@ -64,7 +82,7 @@ DFhackCExport command_result plugin_shutdown ( Core * c ) return CR_OK; } -static bool job_material_hotkey(Core *c, df::viewscreen *top) +static bool workshop_job_hotkey(Core *c, df::viewscreen *top) { using namespace ui_sidebar_mode; @@ -94,6 +112,19 @@ static bool job_material_hotkey(Core *c, df::viewscreen *top) return true; }; + default: + return false; + } +} + +static bool build_selector_hotkey(Core *c, df::viewscreen *top) +{ + using namespace ui_sidebar_mode; + + if (!dwarfmode_hotkey(c,top)) + return false; + + switch (ui->main.mode) { case Build: { if (!ui_build_selector) // allow missing @@ -112,7 +143,13 @@ static bool job_material_hotkey(Core *c, df::viewscreen *top) } } -static command_result job_material_in_job(Core *c, MaterialInfo &new_mat) +static bool job_material_hotkey(Core *c, df::viewscreen *top) +{ + return workshop_job_hotkey(c, top) || + build_selector_hotkey(c, top); +} + +static df::job *getWorkshopJob(Core *c) { df::building *selected = world->selected_building; int idx = *ui_workshop_job_cursor; @@ -120,10 +157,17 @@ static command_result job_material_in_job(Core *c, MaterialInfo &new_mat) if (idx < 0 || idx >= selected->jobs.size()) { c->con.printerr("Invalid job cursor index: %d\n", idx); - return CR_FAILURE; + return NULL; } - df::job *job = selected->jobs[idx]; + return selected->jobs[idx]; +} + +static command_result job_material_in_job(Core *c, MaterialInfo &new_mat) +{ + df::job *job = getWorkshopJob(c); + if (!job) + return CR_FAILURE; MaterialInfo cur_mat(job->matType, job->matIndex); @@ -242,3 +286,88 @@ static command_result job_material(Core * c, vector & parameters) return CR_WRONG_USAGE; } + +static void print_job_item_details(Core *c, df::job *job, df::job_item *item) +{ + c->con << " Input Item: " << ENUM_KEY_STR(item_type,item->itemType); + if (item->itemSubtype != -1) + c->con << " [" << item->itemSubtype << "]"; + c->con << "; count=" << item->count << endl; + + MaterialInfo mat(item->matType, item->matIndex); + if (mat.isValid()) + c->con << " material: " << mat.toString() << endl; + + if (item->flags1.whole) + c->con << " flags1: " << bitfieldToString(item->flags1) << endl; + if (item->flags2.whole) + c->con << " flags2: " << bitfieldToString(item->flags2) << endl; + if (item->flags3.whole) + c->con << " flags3: " << bitfieldToString(item->flags3) << endl; + + if (!item->reaction_class.empty()) + c->con << " reaction class: " << item->reaction_class << endl; + if (!item->has_material_reaction_product.empty()) + c->con << " reaction product: " << item->has_material_reaction_product << endl; +} + +static void print_job_details(Core *c, df::job *job) +{ + c->con << "Job " << job->id << ": " << ENUM_KEY_STR(job_type,job->job_id) + << " [" << job->x << "," << job->y << "," << job->z << "] (" + << bitfieldToString(job->flags) << ")" << endl; + + MaterialInfo mat(job->matType, job->matIndex); + if (mat.isValid() || job->material_category.whole) + { + c->con << " material: " << mat.toString(); + if (job->material_category.whole) + c->con << " (" << bitfieldToString(job->material_category) << ")"; + c->con << endl; + } + + if (job->item_subtype >= 0 || job->item_category.whole) + c->con << " item: " << job->item_subtype + << " (" << bitfieldToString(job->item_category) << ")" << endl; + + if (job->hist_figure_id >= 0) + c->con << " figure: " << job->hist_figure_id << endl; + + if (!job->reaction_name.empty()) + c->con << " reaction: " << job->reaction_name << endl; + + for (unsigned i = 0; i < job->job_items.size(); i++) + print_job_item_details(c, job, job->job_items[i]); +} + +static command_result job_cmd(Core * c, vector & parameters) +{ + CoreSuspender suspend(c); + + if (parameters.empty()) + return CR_WRONG_USAGE; + + std::string cmd = parameters[0]; + if (cmd == "query" || cmd == "list") + { + if (!workshop_job_hotkey(c, c->getTopViewscreen())) { + c->con.printerr("No job is highlighted.\n"); + return CR_WRONG_USAGE; + } + + if (cmd == "query") { + df::job *job = getWorkshopJob(c); + if (!job) + return CR_FAILURE; + print_job_details(c, job); + } else { + df::building *selected = world->selected_building; + for (unsigned i = 0; i < selected->jobs.size(); i++) + print_job_details(c, selected->jobs[i]); + } + } + else + return CR_WRONG_USAGE; + + return CR_OK; +} From 99dda069de241920b19ad99ba8dbc88f166f1d91 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Wed, 4 Jan 2012 18:46:39 +0400 Subject: [PATCH 6/9] Update data definitions. --- library/include/modules/Materials.h | 9 ++++- library/modules/Materials.cpp | 13 +++++-- library/xml | 2 +- plugins/jobutils.cpp | 54 +++++++++++++++++------------ 4 files changed, 52 insertions(+), 26 deletions(-) diff --git a/library/include/modules/Materials.h b/library/include/modules/Materials.h index 372d9d491..c5f54cd91 100644 --- a/library/include/modules/Materials.h +++ b/library/include/modules/Materials.h @@ -47,6 +47,7 @@ namespace df struct plant_raw; struct creature_raw; struct historical_figure; + struct material_vec_ref; union job_material_category; } @@ -82,12 +83,18 @@ namespace DFHack public: MaterialInfo(int16_t type = -1, int32_t index = -1) { decode(type, index); } - MaterialInfo(df::item *item) { decode(item); } + template MaterialInfo(T *ptr) { decode(ptr); } bool isValid() const { return material != NULL; } bool decode(int16_t type, int32_t index = -1); bool decode(df::item *item); + bool decode(const df::material_vec_ref &vr, int idx); + + template bool decode(T *ptr) { + // Assume and exploit a certain naming convention + return ptr ? decode(ptr->mat_type, ptr->mat_index) : decode(-1); + } bool find(const std::string &token, const std::string &subtoken = std::string()); bool findBuiltin(const std::string &token); diff --git a/library/modules/Materials.cpp b/library/modules/Materials.cpp index 2e333f5f9..0faf26030 100644 --- a/library/modules/Materials.cpp +++ b/library/modules/Materials.cpp @@ -51,6 +51,7 @@ using namespace std; #include "df/job_material_category.h" #include "df/matter_state.h" +#include "df/material_vec_ref.h" using namespace DFHack; using namespace df::enums; @@ -58,9 +59,17 @@ using namespace df::enums; bool MaterialInfo::decode(df::item *item) { if (!item) - decode(-1); + return decode(-1); else - decode(item->getActualMaterial(), item->getActualMaterialIndex()); + return decode(item->getActualMaterial(), item->getActualMaterialIndex()); +} + +bool MaterialInfo::decode(const df::material_vec_ref &vr, int idx) +{ + if (idx < 0 || idx >= vr.mat_type.size() || idx >= vr.mat_index.size()) + return decode(-1); + else + return decode(vr.mat_type[idx], vr.mat_index[idx]); } bool MaterialInfo::decode(int16_t type, int32_t index) diff --git a/library/xml b/library/xml index 3455acedc..4857a4f5a 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit 3455acedc525473cd8c5403d225a6078b661dfc8 +Subproject commit 4857a4f5af1a4174e5478f1221552826a60b36c5 diff --git a/plugins/jobutils.cpp b/plugins/jobutils.cpp index 83503ddc9..71ecc2b2c 100644 --- a/plugins/jobutils.cpp +++ b/plugins/jobutils.cpp @@ -18,6 +18,7 @@ #include #include #include +#include using std::vector; using std::string; @@ -102,7 +103,7 @@ static bool workshop_job_hotkey(Core *c, df::viewscreen *top) // No jobs? if (selected->jobs.empty() || - selected->jobs[0]->job_id == job_type::DestroyBuilding) + selected->jobs[0]->job_type == job_type::DestroyBuilding) return false; // Add job gui activated? @@ -169,7 +170,7 @@ static command_result job_material_in_job(Core *c, MaterialInfo &new_mat) if (!job) return CR_FAILURE; - MaterialInfo cur_mat(job->matType, job->matIndex); + MaterialInfo cur_mat(job); if (!cur_mat.isValid() || cur_mat.type != 0) { @@ -195,7 +196,7 @@ static command_result job_material_in_job(Core *c, MaterialInfo &new_mat) for (unsigned i = 0; i < job->job_items.size(); i++) { df::job_item *item = job->job_items[i]; - MaterialInfo item_mat(item->matType, item->matIndex); + MaterialInfo item_mat(item); if (item_mat != cur_mat) { @@ -206,18 +207,18 @@ static command_result job_material_in_job(Core *c, MaterialInfo &new_mat) } // Apply the substitution - job->matType = new_mat.type; - job->matIndex = new_mat.index; + job->mat_type = new_mat.type; + job->mat_index = new_mat.index; for (unsigned i = 0; i < job->job_items.size(); i++) { df::job_item *item = job->job_items[i]; - item->matType = new_mat.type; - item->matIndex = new_mat.index; + item->mat_type = new_mat.type; + item->mat_index = new_mat.index; } c->con << "Applied material '" << new_mat.toString() - << "' to job " << ENUM_KEY_STR(job_type,job->job_id) << endl; + << "' to job " << ENUM_KEY_STR(job_type,job->job_type) << endl; return CR_OK; } @@ -226,8 +227,8 @@ static bool build_choice_matches(df::ui_build_item_req *req, df::build_req_choic { if (VIRTUAL_CAST_VAR(gen, df::build_req_choice_genst, choice)) { - if (gen->matType == new_mat.type && - gen->matIndex == new_mat.index && + if (gen->mat_type == new_mat.type && + gen->mat_index == new_mat.index && gen->used_count < gen->candidates.size()) { return true; @@ -289,14 +290,22 @@ static command_result job_material(Core * c, vector & parameters) static void print_job_item_details(Core *c, df::job *job, df::job_item *item) { - c->con << " Input Item: " << ENUM_KEY_STR(item_type,item->itemType); - if (item->itemSubtype != -1) - c->con << " [" << item->itemSubtype << "]"; - c->con << "; count=" << item->count << endl; - - MaterialInfo mat(item->matType, item->matIndex); - if (mat.isValid()) - c->con << " material: " << mat.toString() << endl; + c->con << " Input Item: " << ENUM_KEY_STR(item_type,item->item_type); + if (item->item_subtype != -1) + c->con << " [" << item->item_subtype << "]"; + if (item->quantity != 1) + c->con << "; quantity=" << item->quantity; + if (item->min_dimension >= 0) + c->con << "; min_dimension=" << item->min_dimension; + c->con << endl; + + MaterialInfo mat(item); + if (mat.isValid() || item->metal_ore >= 0) { + c->con << " material: " << mat.toString(); + if (item->metal_ore >= 0) + c->con << "; ore of " << MaterialInfo(0,item->metal_ore).toString(); + c->con << endl; + } if (item->flags1.whole) c->con << " flags1: " << bitfieldToString(item->flags1) << endl; @@ -309,15 +318,16 @@ static void print_job_item_details(Core *c, df::job *job, df::job_item *item) c->con << " reaction class: " << item->reaction_class << endl; if (!item->has_material_reaction_product.empty()) c->con << " reaction product: " << item->has_material_reaction_product << endl; + if (item->has_tool_use >= 0) + c->con << " tool use: " << ENUM_KEY_STR(tool_uses, item->has_tool_use) << endl; } static void print_job_details(Core *c, df::job *job) { - c->con << "Job " << job->id << ": " << ENUM_KEY_STR(job_type,job->job_id) - << " [" << job->x << "," << job->y << "," << job->z << "] (" - << bitfieldToString(job->flags) << ")" << endl; + c->con << "Job " << job->id << ": " << ENUM_KEY_STR(job_type,job->job_type) + << " (" << bitfieldToString(job->flags) << ")" << endl; - MaterialInfo mat(job->matType, job->matIndex); + MaterialInfo mat(job); if (mat.isValid() || job->material_category.whole) { c->con << " material: " << mat.toString(); From 284009e873b95eed32fae62703ab3bc59e8e905b Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Wed, 4 Jan 2012 19:39:38 +0400 Subject: [PATCH 7/9] Add a hotkey command to duplicate jobs in workshops. --- Memory.xml | 5 ++ library/include/DataDefs.h | 1 + library/include/MiscUtils.h | 14 +++++ plugins/jobutils.cpp | 100 ++++++++++++++++++++++++++++++++++-- 4 files changed, 117 insertions(+), 3 deletions(-) diff --git a/Memory.xml b/Memory.xml index 729759445..62f20f9ab 100644 --- a/Memory.xml +++ b/Memory.xml @@ -1094,6 +1094,7 @@
+
@@ -2345,6 +2346,8 @@
+ +
cmake @@ -3230,6 +3233,8 @@
+ +
diff --git a/library/include/DataDefs.h b/library/include/DataDefs.h index d3899854d..ff61d08f5 100644 --- a/library/include/DataDefs.h +++ b/library/include/DataDefs.h @@ -204,6 +204,7 @@ namespace df { GLOBAL(gview,interface) \ GLOBAL(init,init) \ GLOBAL(d_init,d_init) \ + SIMPLE_GLOBAL(job_next_id,int) \ SIMPLE_GLOBAL(ui_look_cursor,int) \ SIMPLE_GLOBAL(ui_workshop_job_cursor,int) \ SIMPLE_GLOBAL(ui_workshop_in_add,bool) \ diff --git a/library/include/MiscUtils.h b/library/include/MiscUtils.h index 05be881ee..961904e5c 100644 --- a/library/include/MiscUtils.h +++ b/library/include/MiscUtils.h @@ -172,6 +172,20 @@ CT *binsearch_in_vector(const std::vector &vec, FT CT::*field, FT value) return idx < 0 ? NULL : vec[idx]; } +/* + * List + */ + +template +Link *linked_list_append(Link *head, Link *tail) +{ + while (head->next) + head = head->next; + head->next = tail; + tail->prev = head; + return tail; +} + /* * MISC */ diff --git a/plugins/jobutils.cpp b/plugins/jobutils.cpp index 71ecc2b2c..bca8696f5 100644 --- a/plugins/jobutils.cpp +++ b/plugins/jobutils.cpp @@ -17,8 +17,11 @@ #include #include #include +#include #include #include +#include +#include using std::vector; using std::string; @@ -30,13 +33,16 @@ using df::global::world; using df::global::ui; using df::global::ui_build_selector; using df::global::ui_workshop_job_cursor; +using df::global::job_next_id; -static bool wokshop_job_hotkey(Core *c, df::viewscreen *top); -static bool build_selector_hotkey(Core *c, df::viewscreen *top); +/* Plugin registration */ +static bool workshop_job_hotkey(Core *c, df::viewscreen *top); +static bool build_selector_hotkey(Core *c, df::viewscreen *top); static bool job_material_hotkey(Core *c, df::viewscreen *top); -static command_result job_material(Core *c, vector & parameters); +static command_result job_material(Core *c, vector & parameters); +static command_result job_duplicate(Core *c, vector & parameters); static command_result job_cmd(Core *c, vector & parameters); DFhackCExport const char * plugin_name ( void ) @@ -75,6 +81,17 @@ DFhackCExport command_result plugin_init (Core *c, std::vector & ); } + if (ui_workshop_job_cursor && job_next_id) { + commands.push_back( + PluginCommand( + "job-duplicate", "Duplicate the selected job in a workshop.", + job_duplicate, workshop_job_hotkey, + " - In 'q' mode, when a job is highlighted within a workshop\n" + " or furnace building, instantly duplicates the job.\n" + ) + ); + } + return CR_OK; } @@ -83,6 +100,8 @@ DFhackCExport command_result plugin_shutdown ( Core * c ) return CR_OK; } +/* UI state guards */ + static bool workshop_job_hotkey(Core *c, df::viewscreen *top) { using namespace ui_sidebar_mode; @@ -150,6 +169,8 @@ static bool job_material_hotkey(Core *c, df::viewscreen *top) build_selector_hotkey(c, top); } +/* job-material implementation */ + static df::job *getWorkshopJob(Core *c) { df::building *selected = world->selected_building; @@ -288,6 +309,79 @@ static command_result job_material(Core * c, vector & parameters) return CR_WRONG_USAGE; } +/* job-duplicate implementation */ + +static df::job *clone_job(df::job *job) +{ + df::job *pnew = new df::job(*job); + + pnew->id = (*job_next_id)++; + + // Clean out transient fields + pnew->flags.whole = 0; + pnew->flags.bits.repeat = job->flags.bits.repeat; + + pnew->completion_timer = -1; + pnew->items.clear(); + pnew->misc_links.clear(); + + // Link the job into the global list + pnew->list_link = new df::job_list_link(); + pnew->list_link->item = pnew; + + linked_list_append(&world->job_list, pnew->list_link); + + // Clone refs + for (int i = pnew->references.size()-1; i >= 0; i--) + { + df::general_ref *ref = pnew->references[i]; + + if (virtual_cast(ref)) + pnew->references.erase(pnew->references.begin()+i); + else + pnew->references[i] = ref->clone(); + } + + // Clone items + for (int i = pnew->job_items.size()-1; i >= 0; i--) + pnew->job_items[i] = new df::job_item(*pnew->job_items[i]); + + return pnew; +} + +static command_result job_duplicate(Core * c, vector & parameters) +{ + if (!parameters.empty()) + return CR_WRONG_USAGE; + + df::job *job = getWorkshopJob(c); + if (!job) + return CR_FAILURE; + + if (!job->misc_links.empty() || job->job_items.empty()) + { + c->con.printerr("Cannot duplicate job %s\n", ENUM_KEY_STR(job_type,job->job_type)); + return CR_FAILURE; + } + + df::building *building = world->selected_building; + if (building->jobs.size() >= 10) + { + c->con.printerr("Job list is already full.\n"); + return CR_FAILURE; + } + + // Actually clone + df::job *pnew = clone_job(job); + + int pos = ++*ui_workshop_job_cursor; + building->jobs.insert(building->jobs.begin()+pos, pnew); + + return CR_OK; +} + +/* Main job command implementation */ + static void print_job_item_details(Core *c, df::job *job, df::job_item *item) { c->con << " Input Item: " << ENUM_KEY_STR(item_type,item->item_type); From 4a596909684b261f9d22f2911abf86e7e9f5b6fc Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Wed, 4 Jan 2012 20:22:56 +0400 Subject: [PATCH 8/9] Don't print empty parentheses without any flags to fill them. --- library/xml | 2 +- plugins/jobutils.cpp | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/library/xml b/library/xml index 4857a4f5a..e61e696e2 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit 4857a4f5af1a4174e5478f1221552826a60b36c5 +Subproject commit e61e696e2a2bb6b24a86c9c01a26f3133509585e diff --git a/plugins/jobutils.cpp b/plugins/jobutils.cpp index bca8696f5..c89ca2f40 100644 --- a/plugins/jobutils.cpp +++ b/plugins/jobutils.cpp @@ -418,8 +418,10 @@ static void print_job_item_details(Core *c, df::job *job, df::job_item *item) static void print_job_details(Core *c, df::job *job) { - c->con << "Job " << job->id << ": " << ENUM_KEY_STR(job_type,job->job_type) - << " (" << bitfieldToString(job->flags) << ")" << endl; + c->con << "Job " << job->id << ": " << ENUM_KEY_STR(job_type,job->job_type); + if (job->flags.whole) + c->con << " (" << bitfieldToString(job->flags) << ")"; + c->con << endl; MaterialInfo mat(job); if (mat.isValid() || job->material_category.whole) From 687245abd92f11db0f6fdff405c77f708344eb07 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Thu, 5 Jan 2012 22:04:05 +0400 Subject: [PATCH 9/9] Allow setting job item materials. --- library/include/MiscUtils.h | 27 +++++++ library/include/modules/Materials.h | 13 ++++ library/modules/Materials.cpp | 111 ++++++++++++++++++++++++++++ library/xml | 2 +- plugins/jobutils.cpp | 77 ++++++++++++++++--- 5 files changed, 218 insertions(+), 12 deletions(-) diff --git a/library/include/MiscUtils.h b/library/include/MiscUtils.h index 961904e5c..5897994e3 100644 --- a/library/include/MiscUtils.h +++ b/library/include/MiscUtils.h @@ -70,6 +70,24 @@ void print_bits ( T val, DFHack::Console& out ) * Binary search in vectors. */ +template +int linear_index(const std::vector &vec, FT key) +{ + for (unsigned i = 0; i < vec.size(); i++) + if (vec[i] == key) + return i; + return -1; +} + +template +int linear_index(const std::vector &vec, const FT &key) +{ + for (unsigned i = 0; i < vec.size(); i++) + if (vec[i] && *vec[i] == key) + return i; + return -1; +} + template int binsearch_index(const std::vector &vec, FT key, bool exact = true) { @@ -91,6 +109,15 @@ int binsearch_index(const std::vector &vec, FT key, bool exact = true) } } +template +int linear_index(const std::vector &vec, FT CT::*field, FT key) +{ + for (unsigned i = 0; i < vec.size(); i++) + if (vec[i]->*field == key) + return i; + return -1; +} + template int binsearch_index(const std::vector &vec, FT CT::*field, FT key, bool exact = true) { diff --git a/library/include/modules/Materials.h b/library/include/modules/Materials.h index c5f54cd91..25becc400 100644 --- a/library/include/modules/Materials.h +++ b/library/include/modules/Materials.h @@ -48,7 +48,12 @@ namespace df struct creature_raw; struct historical_figure; struct material_vec_ref; + struct job_item; + union job_material_category; + union job_item_flags1; + union job_item_flags2; + union job_item_flags3; } namespace DFHack @@ -104,8 +109,16 @@ namespace DFHack std::string toString(uint16_t temp = 10015, bool named = true); + bool isAnyCloth(); + + void getMatchBits(df::job_item_flags1 &ok, df::job_item_flags1 &mask); + void getMatchBits(df::job_item_flags2 &ok, df::job_item_flags2 &mask); + void getMatchBits(df::job_item_flags3 &ok, df::job_item_flags3 &mask); + df::craft_material_class getCraftClass(); + bool matches(const df::job_material_category &cat); + bool matches(const df::job_item &item); }; inline bool operator== (const MaterialInfo &a, const MaterialInfo &b) { diff --git a/library/modules/Materials.cpp b/library/modules/Materials.cpp index 0faf26030..8e9d30cc1 100644 --- a/library/modules/Materials.cpp +++ b/library/modules/Materials.cpp @@ -43,12 +43,15 @@ using namespace std; #include "MiscUtils.h" #include "df/world.h" +#include "df/ui.h" #include "df/item.h" #include "df/inorganic_raw.h" #include "df/plant_raw.h" +#include "df/plant_raw_flags.h" #include "df/creature_raw.h" #include "df/historical_figure.h" +#include "df/job_item.h" #include "df/job_material_category.h" #include "df/matter_state.h" #include "df/material_vec_ref.h" @@ -259,6 +262,17 @@ df::craft_material_class MaterialInfo::getCraftClass() return craft_material_class::None; } +bool MaterialInfo::isAnyCloth() +{ + using namespace df::enums::material_flags; + + return material && ( + material->flags.is_set(THREAD_PLANT) || + material->flags.is_set(SILK) || + material->flags.is_set(YARN) + ); +} + bool MaterialInfo::matches(const df::job_material_category &cat) { if (!material) @@ -283,6 +297,103 @@ bool MaterialInfo::matches(const df::job_material_category &cat) return false; } +bool MaterialInfo::matches(const df::job_item &item) +{ + if (!isValid()) return false; + + df::job_item_flags1 ok1, mask1; + getMatchBits(ok1, mask1); + + df::job_item_flags2 ok2, mask2; + getMatchBits(ok2, mask2); + + df::job_item_flags3 ok3, mask3; + getMatchBits(ok3, mask3); + + return ((item.flags1.whole & mask1.whole) == (item.flags1.whole & ok1.whole)) && + ((item.flags2.whole & mask2.whole) == (item.flags2.whole & ok2.whole)) && + ((item.flags3.whole & mask3.whole) == (item.flags3.whole & ok3.whole)); +} + +void MaterialInfo::getMatchBits(df::job_item_flags1 &ok, df::job_item_flags1 &mask) +{ + ok.whole = mask.whole = 0; + if (!isValid()) return; + +#define MAT_FLAG(name) material->flags.is_set(df::enums::material_flags::name) +#define FLAG(field, name) (field && field->flags.is_set(name)) +#define TEST(bit, check) \ + mask.bits.bit = true; ok.bits.bit = !!(check); + + bool structural = MAT_FLAG(STRUCTURAL_PLANT_MAT); + + TEST(millable, structural && FLAG(plant, plant_raw_flags::MILL)); + TEST(sharpenable, MAT_FLAG(IS_STONE)); + TEST(distillable, structural && FLAG(plant, plant_raw_flags::DRINK)); + TEST(processable, structural && FLAG(plant, plant_raw_flags::THREAD)); + TEST(bag, isAnyCloth()); + TEST(cookable, MAT_FLAG(EDIBLE_COOKED)); + TEST(extract_bearing_plant, structural && FLAG(plant, plant_raw_flags::EXTRACT_STILL_VIAL)); + TEST(extract_bearing_fish, false); + TEST(extract_bearing_vermin, false); + TEST(processable_to_vial, structural && FLAG(plant, plant_raw_flags::EXTRACT_VIAL)); + TEST(processable_to_bag, structural && FLAG(plant, plant_raw_flags::LEAVES)); + TEST(processable_to_barrel, structural && FLAG(plant, plant_raw_flags::EXTRACT_BARREL)); + TEST(solid, !(MAT_FLAG(ALCOHOL_PLANT) || + MAT_FLAG(ALCOHOL_CREATURE) || + MAT_FLAG(LIQUID_MISC_PLANT) || + MAT_FLAG(LIQUID_MISC_CREATURE) || + MAT_FLAG(LIQUID_MISC_OTHER))); + TEST(tameable_vermin, false); + TEST(sharpenable, MAT_FLAG(IS_GLASS)); + TEST(milk, linear_index(material->reaction_product.id, std::string("CHEESE_MAT")) >= 0); + //04000000 - "milkable" - vtable[107],1,1 +} + +void MaterialInfo::getMatchBits(df::job_item_flags2 &ok, df::job_item_flags2 &mask) +{ + ok.whole = mask.whole = 0; + if (!isValid()) return; + + bool is_cloth = isAnyCloth(); + + TEST(dye, MAT_FLAG(IS_DYE)); + TEST(dyeable, is_cloth); + TEST(dyed, is_cloth); + TEST(sewn_imageless, is_cloth); + TEST(glass_making, MAT_FLAG(CRYSTAL_GLASSABLE)); + + TEST(fire_safe, material->heat.melting_point > 11000); + TEST(magma_safe, material->heat.melting_point > 12000); + TEST(deep_material, FLAG(inorganic, df::enums::inorganic_flags::DEEP_ANY)); + TEST(non_economic, inorganic && !(df::global::ui && df::global::ui->economic_stone[index])); + + TEST(plant, plant); + TEST(silk, MAT_FLAG(SILK)); + TEST(leather, MAT_FLAG(LEATHER)); + TEST(bone, MAT_FLAG(BONE)); + TEST(shell, MAT_FLAG(SHELL)); + TEST(totemable, false); + TEST(horn, MAT_FLAG(HORN)); + TEST(pearl, MAT_FLAG(PEARL)); + TEST(soap, MAT_FLAG(SOAP)); + TEST(ivory_tooth, MAT_FLAG(TOOTH)); + //TEST(hair_wool, MAT_FLAG(YARN)); + TEST(yarn, MAT_FLAG(YARN)); +} + +void MaterialInfo::getMatchBits(df::job_item_flags3 &ok, df::job_item_flags3 &mask) +{ + ok.whole = mask.whole = 0; + if (!isValid()) return; + + TEST(hard, MAT_FLAG(ITEMS_HARD)); +} + +#undef MAT_FLAG +#undef FLAG +#undef TEST + Module* DFHack::createMaterials() { return new Materials(); diff --git a/library/xml b/library/xml index e61e696e2..f20758b0a 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit e61e696e2a2bb6b24a86c9c01a26f3133509585e +Subproject commit f20758b0a3ef5aebcbf5896cbb7a824cbdf7b218 diff --git a/plugins/jobutils.cpp b/plugins/jobutils.cpp index c89ca2f40..66ed00024 100644 --- a/plugins/jobutils.cpp +++ b/plugins/jobutils.cpp @@ -60,8 +60,12 @@ DFhackCExport command_result plugin_init (Core *c, std::vector & PluginCommand( "job", "General job query and manipulation.", job_cmd, false, - " job query - Print details of the current job.\n" - " job list - Print details of all jobs in the workshop.\n" + " job query\n" + " Print details of the current job.\n" + " job list\n" + " Print details of all jobs in the workshop.\n" + " job item-material [submaterial]\n" + " Replace the exact material id in the job item.\n" ) ); @@ -382,9 +386,9 @@ static command_result job_duplicate(Core * c, vector & parameters) /* Main job command implementation */ -static void print_job_item_details(Core *c, df::job *job, df::job_item *item) +static void print_job_item_details(Core *c, df::job *job, unsigned idx, df::job_item *item) { - c->con << " Input Item: " << ENUM_KEY_STR(item_type,item->item_type); + c->con << " Input Item " << (idx+1) << ": " << ENUM_KEY_STR(item_type,item->item_type); if (item->item_subtype != -1) c->con << " [" << item->item_subtype << "]"; if (item->quantity != 1) @@ -443,7 +447,17 @@ static void print_job_details(Core *c, df::job *job) c->con << " reaction: " << job->reaction_name << endl; for (unsigned i = 0; i < job->job_items.size(); i++) - print_job_item_details(c, job, job->job_items[i]); + print_job_item_details(c, job, i, job->job_items[i]); +} + +static df::job *getWorkshopJobSafe(Core *c) +{ + if (!workshop_job_hotkey(c, c->getTopViewscreen())) { + c->con.printerr("No job is highlighted.\n"); + return NULL; + } + + return getWorkshopJob(c); } static command_result job_cmd(Core * c, vector & parameters) @@ -456,15 +470,11 @@ static command_result job_cmd(Core * c, vector & parameters) std::string cmd = parameters[0]; if (cmd == "query" || cmd == "list") { - if (!workshop_job_hotkey(c, c->getTopViewscreen())) { - c->con.printerr("No job is highlighted.\n"); + df::job *job = getWorkshopJobSafe(c); + if (!job) return CR_WRONG_USAGE; - } if (cmd == "query") { - df::job *job = getWorkshopJob(c); - if (!job) - return CR_FAILURE; print_job_details(c, job); } else { df::building *selected = world->selected_building; @@ -472,6 +482,51 @@ static command_result job_cmd(Core * c, vector & parameters) print_job_details(c, selected->jobs[i]); } } + else if (cmd == "item-material") + { + if (parameters.size() < 1+1+1) + return CR_WRONG_USAGE; + + df::job *job = getWorkshopJobSafe(c); + if (!job) + return CR_WRONG_USAGE; + + int v = atoi(parameters[1].c_str()); + if (v < 1 || v > job->job_items.size()) { + c->con.printerr("Invalid item index.\n"); + return CR_WRONG_USAGE; + } + + df::job_item *item = job->job_items[v-1]; + + std::string subtoken = (parameters.size()>3 ? parameters[3] : ""); + MaterialInfo info; + if (!info.find(parameters[2], subtoken)) { + c->con.printerr("Could not find the specified material.\n"); + return CR_FAILURE; + } + + if (!info.matches(*item)) { + c->con.printerr("Material does not match the requirements.\n"); + print_job_details(c, job); + return CR_FAILURE; + } + + if (job->mat_type != -1 && + job->mat_type == item->mat_type && + job->mat_index == item->mat_index) + { + job->mat_type = info.type; + job->mat_index = info.index; + } + + item->mat_type = info.type; + item->mat_index = info.index; + + c->con << "Job item " << v << " updated." << endl; + print_job_details(c, job); + return CR_OK; + } else return CR_WRONG_USAGE;