diff --git a/library/xml b/library/xml index 9799fc70b..91b6531e1 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit 9799fc70b8389ddc0e73805f528dbd7b223653e5 +Subproject commit 91b6531e1a66e98f7c0ae7f81b5496ccde584b73 diff --git a/plugins/workflow.cpp b/plugins/workflow.cpp index 369f9947c..e8ac9a4cc 100644 --- a/plugins/workflow.cpp +++ b/plugins/workflow.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -33,6 +34,7 @@ #include #include #include +#include using std::vector; using std::string; @@ -68,9 +70,12 @@ DFhackCExport command_result plugin_init (Core *c, std::vector & PluginCommand( "workflow", "Manage control of repeat jobs.", workflow_cmd, false, - " workflow enable\n" - " workflow disable\n" - " Enable or disable the plugin.\n" + " workflow enable [option...]\n" + " workflow disable [option...]\n" + " If no options are specified, enables or disables the plugin.\n" + " Otherwise, enables or disables any of the following options:\n" + " - drybuckets: Automatically empty abandoned water buckets.\n" + " - auto-melt: Resume melt jobs when there are objects to melt.\n" " workflow jobs\n" " List workflow-controlled jobs (if in a workshop, filtered by it).\n" " workflow list\n" @@ -241,8 +246,6 @@ public: void set_resumed(bool resume) { - want_resumed = resume; - if (resume) { if (world->frame_counter >= resume_time) @@ -250,14 +253,14 @@ public: } else { + resume_time = 0; if (isActuallyResumed()) - { - resume_time = 0; resume_delay = DAY_TICKS; - } actual_job->flags.bits.suspend = true; } + + want_resumed = resume; } }; @@ -326,7 +329,9 @@ static int last_tick_frame_count = 0; static int last_frame_count = 0; enum ConfigFlags { - CF_ENABLED = 1 + CF_ENABLED = 1, + CF_DRYBUCKETS = 2, + CF_AUTOMELT = 4 }; typedef std::map TKnownJobs; @@ -335,6 +340,8 @@ static TKnownJobs known_jobs; static std::vector pending_recover; static std::vector constraints; +static int meltable_count = 0; + /****************************** * MISC FUNCTIONS * ******************************/ @@ -361,6 +368,22 @@ static void enumLiveJobs(std::map &rv) rv[p->item->id] = p->item; } +static bool isOptionEnabled(unsigned flag) +{ + return config.isValid() && (config.ival(0) & flag) != 0; +} + +static void setOptionEnabled(ConfigFlags flag, bool on) +{ + if (!config.isValid()) + return; + + if (on) + config.ival(0) |= flag; + else + config.ival(0) &= ~flag; +} + /****************************** * STATE INITIALIZATION * ******************************/ @@ -403,9 +426,10 @@ static void start_protect(Core *c) static void init_state(Core *c) { config = c->getWorld()->GetPersistentData("workflow/config"); + if (config.isValid() && config.ival(0) == -1) + config.ival(0) = 0; - enabled = config.isValid() && config.ival(0) != -1 && - (config.ival(0) & CF_ENABLED); + enabled = isOptionEnabled(CF_ENABLED); // Parse constraints std::vector items; @@ -436,7 +460,7 @@ static void enable_plugin(Core *c) config.ival(0) = 0; } - config.ival(0) |= CF_ENABLED; + setOptionEnabled(CF_ENABLED, true); enabled = true; c->con << "Enabling the plugin." << endl; @@ -929,15 +953,45 @@ static void map_job_constraints(Core *c) * ITEM-CONSTRAINT MAPPING * ******************************/ -static bool itemNotEmpty(df::item *item) +static void dryBucket(df::item *item) { for (unsigned i = 0; i < item->itemrefs.size(); i++) { df::general_ref *ref = item->itemrefs[i]; if (strict_virtual_cast(ref)) + { + df::item *obj = ref->getItem(); + + if (obj && + obj->getType() == item_type::LIQUID_MISC && + obj->getMaterial() == builtin_mats::WATER) + { + obj->flags.bits.garbage_colect = true; + obj->flags.bits.hidden = true; + } + } + } +} + +static bool itemBusy(df::item *item) +{ + + for (unsigned i = 0; i < item->itemrefs.size(); i++) + { + df::general_ref *ref = item->itemrefs[i]; + if (strict_virtual_cast(ref)) + { + df::item *obj = ref->getItem(); + if (obj && !obj->flags.bits.garbage_colect) + return true; + } + else if (strict_virtual_cast(ref)) return true; - if (strict_virtual_cast(ref)) - return true; + else if (strict_virtual_cast(ref)) + { + if (!item->flags.bits.in_job) + return true; + } } return false; @@ -966,6 +1020,8 @@ static void map_job_items(Core *c) constraints[i]->item_inuse = 0; } + meltable_count = 0; + // Precompute a bitmask with the bad flags df::item_flags bad_flags; bad_flags.whole = 0; @@ -976,6 +1032,8 @@ static void map_job_items(Core *c) F(in_building); F(construction); F(artifact1); #undef F + bool dry_buckets = isOptionEnabled(CF_DRYBUCKETS); + std::vector &items = world->items.other[items_other_id::ANY_FREE]; for (unsigned i = 0; i < items.size(); i++) @@ -984,14 +1042,20 @@ static void map_job_items(Core *c) if (item->flags.whole & bad_flags.whole) continue; - if (itemInRealJob(item)) - continue; df::item_type itype = item->getType(); int16_t isubtype = item->getSubtype(); int16_t imattype = item->getActualMaterial(); int32_t imatindex = item->getActualMaterialIndex(); + // Special handling + if (dry_buckets && itype == item_type::BUCKET && !item->flags.bits.in_job) + dryBucket(item); + + if (item->flags.bits.melt && !item->flags.bits.owned && !itemBusy(item)) + meltable_count++; + + // Match to constraints TMaterialCache::key_type matkey(imattype, imatindex); for (unsigned i = 0; i < constraints.size(); i++) @@ -1019,7 +1083,8 @@ static void map_job_items(Core *c) if (item->flags.bits.owned || item->isAssignedToStockpile() || - itemNotEmpty(item)) + itemInRealJob(item) || + itemBusy(item)) { cv->item_inuse++; } @@ -1041,12 +1106,37 @@ static void map_job_items(Core *c) static std::string shortJobDescription(df::job *job); +static void setJobResumed(Core *c, ProtectedJob *pj, bool goal) +{ + bool current = pj->isResumed(); + + pj->set_resumed(goal); + + if (goal != current) + { + c->con.print("%s %s%s\n", + (goal ? "Resuming" : "Suspending"), + shortJobDescription(pj->actual_job).c_str(), + (!goal || pj->isActuallyResumed() ? "" : " (delayed)")); + } +} + static void update_jobs_by_constraints(Core *c) { for (TKnownJobs::const_iterator it = known_jobs.begin(); it != known_jobs.end(); ++it) { ProtectedJob *pj = it->second; - if (!pj->isLive() || pj->constraints.empty()) + if (!pj->isLive()) + continue; + + if (pj->actual_job->job_type == job_type::MeltMetalObject && + isOptionEnabled(CF_AUTOMELT)) + { + setJobResumed(c, pj, meltable_count > 0); + continue; + } + + if (pj->constraints.empty()) continue; int resume_weight = -1; @@ -1060,29 +1150,21 @@ static void update_jobs_by_constraints(Core *c) suspend_weight = std::max(suspend_weight, pj->constraints[i]->weight); } - bool current = pj->isResumed(); - bool goal = current; + bool goal = pj->isResumed(); if (resume_weight >= 0 && resume_weight >= suspend_weight) goal = true; else if (suspend_weight >= 0 && suspend_weight >= resume_weight) goal = false; - pj->set_resumed(goal); - - if (goal != current) - { - c->con.print("%s %s%s\n", - (goal ? "Resuming" : "Suspending"), - shortJobDescription(pj->actual_job).c_str(), - (!goal || pj->isActuallyResumed() ? "" : " (delayed)")); - } + setJobResumed(c, pj, goal); } } static void process_constraints(Core *c) { - if (constraints.empty()) + if (constraints.empty() && + !isOptionEnabled(CF_DRYBUCKETS | CF_AUTOMELT)) return; map_job_constraints(c); @@ -1203,7 +1285,22 @@ static void print_job(Core *c, ProtectedJob *pj) if (!pj) return; - printJobDetails(c, pj->isLive() ? pj->actual_job : pj->job_copy); + df::job *job = pj->isLive() ? pj->actual_job : pj->job_copy; + + printJobDetails(c, job); + + if (job->job_type == job_type::MeltMetalObject && + isOptionEnabled(CF_AUTOMELT)) + { + if (meltable_count <= 0) + c->con.color(Console::COLOR_CYAN); + else if (pj->want_resumed && !pj->isActuallyResumed()) + c->con.color(Console::COLOR_YELLOW); + else + c->con.color(Console::COLOR_GREEN); + c->con << " Meltable: " << meltable_count << " objects." << endl; + c->con.reset_color(); + } for (int i = 0; i < pj->constraints.size(); i++) print_constraint(c, pj->constraints[i], true, " "); @@ -1233,28 +1330,46 @@ static command_result workflow_cmd(Core *c, vector & parameters) std::string cmd = parameters.empty() ? "list" : parameters[0]; - if (cmd == "enable") + if (cmd == "enable" || cmd == "disable") { - if (enabled) + bool enable = (cmd == "enable"); + if (enable && !enabled) + { + enable_plugin(c); + } + else if (!enable && parameters.size() == 1) { - c->con << "The plugin is already enabled." << endl; + if (enabled) + { + enabled = false; + setOptionEnabled(CF_ENABLED, false); + stop_protect(c); + } + + c->con << "The plugin is disabled." << endl; return CR_OK; } - enable_plugin(c); - return CR_OK; - } - else if (cmd == "disable") - { - if (!enabled) + for (unsigned i = 1; i < parameters.size(); i++) { - c->con << "The plugin is already disabled." << endl; - return CR_OK; + if (parameters[i] == "drybuckets") + setOptionEnabled(CF_DRYBUCKETS, enable); + else if (parameters[i] == "auto-melt") + setOptionEnabled(CF_AUTOMELT, enable); + else + return CR_WRONG_USAGE; } - enabled = false; - config.ival(0) &= ~CF_ENABLED; - stop_protect(c); + if (enabled) + c->con << "The plugin is enabled." << endl; + else + c->con << "The plugin is disabled." << endl; + + if (isOptionEnabled(CF_DRYBUCKETS)) + c->con << "Option drybuckets is enabled." << endl; + if (isOptionEnabled(CF_AUTOMELT)) + c->con << "Option auto-melt is enabled." << endl; + return CR_OK; } else if (cmd == "count" || cmd == "amount")