From 1570af3f4dff4a6d8dc20d9d8dca9098708261a1 Mon Sep 17 00:00:00 2001 From: lethosor Date: Sat, 7 Mar 2015 15:56:43 -0500 Subject: [PATCH 1/8] Allow plugins to export additional functions through a class --- library/PluginManager.cpp | 11 +++++++++++ library/include/PluginManager.h | 19 +++++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/library/PluginManager.cpp b/library/PluginManager.cpp index bd376f9aa..50410f9a0 100644 --- a/library/PluginManager.cpp +++ b/library/PluginManager.cpp @@ -268,6 +268,7 @@ bool Plugin::load(color_ostream &con) plugin_enable = (command_result (*)(color_ostream &,bool)) LookupPlugin(plug, "plugin_enable"); plugin_is_enabled = (bool*) LookupPlugin(plug, "plugin_is_enabled"); plugin_eval_ruby = (command_result (*)(color_ostream &, const char*)) LookupPlugin(plug, "plugin_eval_ruby"); + plugin_get_exports = (void* (*)(void)) LookupPlugin(plug, "plugin_get_exports"); index_lua(plug); this->name = *plug_name; plugin_lib = plug; @@ -757,6 +758,16 @@ Plugin *PluginManager::getPluginByCommand(const std::string &command) return NULL; } +void *PluginManager::getPluginExports(const std::string &name) +{ + Plugin *plug = getPluginByName(name); + if (!plug) + return NULL; + if (plug->getState() != Plugin::plugin_state::PS_LOADED) + return NULL; + return plug->getExports(); +} + // FIXME: handle name collisions... command_result PluginManager::InvokeCommand(color_ostream &out, const std::string & command, std::vector & parameters) { diff --git a/library/include/PluginManager.h b/library/include/PluginManager.h index befc030ff..09030664a 100644 --- a/library/include/PluginManager.h +++ b/library/include/PluginManager.h @@ -162,6 +162,12 @@ namespace DFHack command_result invoke(color_ostream &out, const std::string & command, std::vector & parameters); bool can_invoke_hotkey(const std::string & command, df::viewscreen *top ); plugin_state getState () const; + void *getExports() + { + if (plugin_get_exports) + return plugin_get_exports(); + return NULL; + }; RPCService *rpc_connect(color_ostream &out); @@ -224,6 +230,7 @@ namespace DFHack command_result (*plugin_enable)(color_ostream &, bool); RPCService* (*plugin_rpcconnect)(color_ostream &); command_result (*plugin_eval_ruby)(color_ostream &, const char*); + void* (*plugin_get_exports)(void); }; class DFHACK_EXPORT PluginManager { @@ -241,6 +248,7 @@ namespace DFHack public: Plugin *getPluginByName (const std::string & name); Plugin *getPluginByCommand (const std::string &command); + void *getPluginExports(const std::string &name); command_result InvokeCommand(color_ostream &out, const std::string & command, std::vector & parameters); bool CanInvokeHotkey(const std::string &command, df::viewscreen *top); Plugin* operator[] (std::size_t index) @@ -283,6 +291,17 @@ namespace DFHack DFhackDataExport bool plugin_is_enabled = false; \ bool &varname = plugin_is_enabled; +#define DFHACK_PLUGIN_EXPORTS(clsname) \ + DFhackCExport void* plugin_get_exports() \ + { \ + static clsname* instance = NULL; \ + if (!instance) \ + instance = new clsname; \ + return (void*)instance; \ + } +#define GET_PLUGIN_EXPORTS(plugname, clsname) \ + (clsname*)DFHack::Core::getInstance().getPluginManager()->getPluginExports(plugname) + #define DFHACK_PLUGIN_LUA_COMMANDS \ DFhackCExport const DFHack::CommandReg plugin_lua_commands[] = #define DFHACK_PLUGIN_LUA_FUNCTIONS \ From ea1c9fa0dfb42e52b3213d535f2fb5bc53d7499e Mon Sep 17 00:00:00 2001 From: lethosor Date: Sat, 7 Mar 2015 17:25:35 -0500 Subject: [PATCH 2/8] Allow plugin exports to be defined in plugins (and looked up when needed) --- library/PluginManager.cpp | 25 ++++++++++++++++++++++++- library/include/PluginManager.h | 21 ++++++++++++--------- 2 files changed, 36 insertions(+), 10 deletions(-) diff --git a/library/PluginManager.cpp b/library/PluginManager.cpp index 50410f9a0..01b5ea5da 100644 --- a/library/PluginManager.cpp +++ b/library/PluginManager.cpp @@ -268,7 +268,7 @@ bool Plugin::load(color_ostream &con) plugin_enable = (command_result (*)(color_ostream &,bool)) LookupPlugin(plug, "plugin_enable"); plugin_is_enabled = (bool*) LookupPlugin(plug, "plugin_is_enabled"); plugin_eval_ruby = (command_result (*)(color_ostream &, const char*)) LookupPlugin(plug, "plugin_eval_ruby"); - plugin_get_exports = (void* (*)(void)) LookupPlugin(plug, "plugin_get_exports"); + plugin_get_exports = (PluginExports* (*)(void)) LookupPlugin(plug, "plugin_get_exports"); index_lua(plug); this->name = *plug_name; plugin_lib = plug; @@ -523,6 +523,16 @@ Plugin::plugin_state Plugin::getState() const return state; } +PluginExports *Plugin::getExports() +{ + if (!plugin_get_exports) + return NULL; + PluginExports *exports = plugin_get_exports(); + if (!exports->bind(plugin_lib)) + return NULL; + return exports; +}; + void Plugin::index_lua(DFLibrary *lib) { if (auto cmdlist = (CommandReg*)LookupPlugin(lib, "plugin_lua_commands")) @@ -695,6 +705,19 @@ void Plugin::push_function(lua_State *state, LuaFunction *fn) lua_pushcclosure(state, lua_fun_wrapper, 4); } +bool PluginExports::bind(DFLibrary *lib) +{ + for (auto it = bindings.begin(); it != bindings.end(); ++it) + { + std::string name = it->first; + void** dest = it->second; + *dest = LookupPlugin(lib, name.c_str()); + if (!*dest) + return false; + } + return true; +} + PluginManager::PluginManager(Core * core) { cmdlist_mutex = new mutex(); diff --git a/library/include/PluginManager.h b/library/include/PluginManager.h index 09030664a..a8f93b431 100644 --- a/library/include/PluginManager.h +++ b/library/include/PluginManager.h @@ -51,6 +51,7 @@ namespace df namespace DFHack { class Core; + class PluginExports; class PluginManager; class virtual_identity; class RPCService; @@ -162,12 +163,7 @@ namespace DFHack command_result invoke(color_ostream &out, const std::string & command, std::vector & parameters); bool can_invoke_hotkey(const std::string & command, df::viewscreen *top ); plugin_state getState () const; - void *getExports() - { - if (plugin_get_exports) - return plugin_get_exports(); - return NULL; - }; + PluginExports *getExports(); RPCService *rpc_connect(color_ostream &out); @@ -230,8 +226,15 @@ namespace DFHack command_result (*plugin_enable)(color_ostream &, bool); RPCService* (*plugin_rpcconnect)(color_ostream &); command_result (*plugin_eval_ruby)(color_ostream &, const char*); - void* (*plugin_get_exports)(void); + PluginExports* (*plugin_get_exports)(void); + }; + class DFHACK_EXPORT PluginExports { + protected: + friend class Plugin; + std::map bindings; + bool bind(DFLibrary* lib); }; + #define PLUGIN_EXPORT_BIND(name) bindings.insert(std::pair(#name, (void**)&this->name)) class DFHACK_EXPORT PluginManager { // PRIVATE METHODS @@ -292,12 +295,12 @@ namespace DFHack bool &varname = plugin_is_enabled; #define DFHACK_PLUGIN_EXPORTS(clsname) \ - DFhackCExport void* plugin_get_exports() \ + DFhackCExport PluginExports* plugin_get_exports() \ { \ static clsname* instance = NULL; \ if (!instance) \ instance = new clsname; \ - return (void*)instance; \ + return (PluginExports*)instance; \ } #define GET_PLUGIN_EXPORTS(plugname, clsname) \ (clsname*)DFHack::Core::getInstance().getPluginManager()->getPluginExports(plugname) From 82202efb26afa4bd35c1579f92ae6c7dff7fc3a8 Mon Sep 17 00:00:00 2001 From: Ross Morgan-Linial Date: Wed, 6 May 2015 20:57:43 -0700 Subject: [PATCH 3/8] Don't reset disabled labors Autolabor was disabling all labors on dwarves selected for trading or diplomacy, even if those labors were not supposed to be managed by autolabor. This is wrong. --- plugins/autolabor.cpp | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/plugins/autolabor.cpp b/plugins/autolabor.cpp index 31f5aa077..7fda57431 100644 --- a/plugins/autolabor.cpp +++ b/plugins/autolabor.cpp @@ -1214,7 +1214,9 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out ) laborinfo_sorter lasorter; std::sort(labors.begin(), labors.end(), lasorter); - // Handle DISABLED skills (just bookkeeping) + // Handle DISABLED skills (just bookkeeping). + // Note that autolabor should *NEVER* enable or disable a skill that has been marked as DISABLED, for any reason. + // The user has told us that they want manage this skill manually, and we must respect that. for (auto lp = labors.begin(); lp != labors.end(); ++lp) { auto labor = *lp; @@ -1224,12 +1226,6 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out ) for (int dwarf = 0; dwarf < n_dwarfs; dwarf++) { - if ((dwarf_info[dwarf].trader && trader_requested) || - dwarf_info[dwarf].diplomacy) - { - dwarfs[dwarf]->status.labors[labor] = false; - } - if (dwarfs[dwarf]->status.labors[labor]) { if (labor_infos[labor].is_exclusive) From 684ff433d450cb340752dcab84c65f9525f044d8 Mon Sep 17 00:00:00 2001 From: Ross Morgan-Linial Date: Thu, 7 May 2015 18:08:02 -0700 Subject: [PATCH 4/8] Autolabor changes Try harder to concentrate skill on certain dwarves, at the expense of potentially allowing more idle dwarves. Attempt to keep dwarves with tools from running off and doing other things while holding on the tool so much. Add comments explaining the two modes autolabor uses depending on number of idle dwarves. --- plugins/autolabor.cpp | 99 ++++++++++++++++++++++++++++--------------- 1 file changed, 66 insertions(+), 33 deletions(-) diff --git a/plugins/autolabor.cpp b/plugins/autolabor.cpp index 7fda57431..46898dcc9 100644 --- a/plugins/autolabor.cpp +++ b/plugins/autolabor.cpp @@ -109,6 +109,9 @@ enum dwarf_state { // Busy with a useful task BUSY, + // Busy with a useful task that requires a tool + EXCLUSIVE, + // In the military, can't work MILITARY, @@ -119,11 +122,12 @@ enum dwarf_state { OTHER }; -const int NUM_STATE = 5; +const int NUM_STATE = 6; static const char *state_names[] = { "IDLE", "BUSY", + "EXCLUSIVE", "MILITARY", "CHILD", "OTHER", @@ -133,13 +137,13 @@ static const dwarf_state dwarf_states[] = { BUSY /* CarveFortification */, BUSY /* DetailWall */, BUSY /* DetailFloor */, - BUSY /* Dig */, - BUSY /* CarveUpwardStaircase */, - BUSY /* CarveDownwardStaircase */, - BUSY /* CarveUpDownStaircase */, - BUSY /* CarveRamp */, - BUSY /* DigChannel */, - BUSY /* FellTree */, + EXCLUSIVE /* Dig */, + EXCLUSIVE /* CarveUpwardStaircase */, + EXCLUSIVE /* CarveDownwardStaircase */, + EXCLUSIVE /* CarveUpDownStaircase */, + EXCLUSIVE /* CarveRamp */, + EXCLUSIVE /* DigChannel */, + EXCLUSIVE /* FellTree */, BUSY /* GatherPlants */, BUSY /* RemoveConstruction */, BUSY /* CollectWebs */, @@ -154,7 +158,7 @@ static const dwarf_state dwarf_states[] = { OTHER /* Sleep */, BUSY /* CollectSand */, BUSY /* Fish */, - BUSY /* Hunt */, + EXCLUSIVE /* Hunt */, OTHER /* HuntVermin */, BUSY /* Kidnap */, BUSY /* BeatCriminal */, @@ -183,7 +187,7 @@ static const dwarf_state dwarf_states[] = { OTHER /* GoShopping2 */, BUSY /* Clean */, OTHER /* Rest */, - BUSY /* PickupEquipment */, + EXCLUSIVE /* PickupEquipment */, BUSY /* DumpItem */, OTHER /* StrangeMoodCrafter */, OTHER /* StrangeMoodJeweller */, @@ -393,8 +397,15 @@ struct labor_default int active_dwarfs; }; +// The percentage of the dwarves assigned as haulers at any one time. static int hauler_pct = 33; +// The maximum percentage of dwarves who will be allowed to be idle. +// Decreasing this will encourage autolabor to keep dwarves busy, +// at the expense of making it harder for dwarves to specialize in +// specific skills. +static int idler_pct = 10; + static std::vector labor_infos; static const struct labor_default default_labor_infos[] = { @@ -521,7 +532,6 @@ struct dwarf_info_t bool medical; // this dwarf has medical responsibility bool trader; // this dwarf has trade responsibility bool diplomacy; // this dwarf meets with diplomats - int single_labor; // this dwarf will be exclusively assigned to one labor (-1/NONE for none) }; static bool isOptionEnabled(unsigned flag) @@ -738,7 +748,13 @@ struct laborinfo_sorter { bool operator() (int i,int j) { - return labor_infos[i].mode() < labor_infos[j].mode(); + if (labor_infos[i].mode() != labor_infos[j].mode()) + return labor_infos[i].mode() < labor_infos[j].mode(); + if (labor_infos[i].is_exclusive != labor_infos[j].is_exclusive) + return labor_infos[i].is_exclusive; + if (labor_infos[i].maximum_dwarfs() != labor_infos[j].maximum_dwarfs()) + return labor_infos[i].maximum_dwarfs() < labor_infos[j].maximum_dwarfs(); + return false; }; }; @@ -769,6 +785,7 @@ static void assign_labor(unit_labor::unit_labor labor, int best_dwarf = 0; int best_value = -10000; + int best_skill = 0; std::vector values(n_dwarfs); std::vector candidates; @@ -813,6 +830,9 @@ static void assign_labor(unit_labor::unit_labor labor, dwarf_skill[dwarf] = skill_level; dwarf_skillxp[dwarf] = skill_experience; + if (best_skill < skill_level) + best_skill = skill_level; + value += skill_level * 100; value += skill_experience / 20; if (skill_level > 0 || skill_experience > 0) @@ -832,6 +852,9 @@ static void assign_labor(unit_labor::unit_labor labor, value += 350; } + if (dwarf_info[dwarf].has_exclusive_labor) + value -= 500; + // bias by happiness //value += dwarfs[dwarf]->status.happiness; @@ -897,15 +920,28 @@ static void assign_labor(unit_labor::unit_labor labor, if (unit_labor::FISH == labor && !has_fishery) min_dwarfs = max_dwarfs = 0; - bool want_idle_dwarf = true; - if (state_count[IDLE] < 2) - want_idle_dwarf = false; + // If there are enough idle dwarves to choose from, enter an aggressive assignment + // mode. "Enough" idle dwarves is defined as 2 or 10% of the total number of dwarves, + // whichever is higher. + // + // In aggressive mode, we will always pick at least one idle dwarf for each skill, + // in order to try to get the idle dwarves to start doing something. We also pick + // any dwarf more preferable to the idle dwarf, since we'd rather have a more + // preferable dwarf do a new job if one becomes available (probably because that + // dwarf just finished a job). + // + // In non-aggressive mode, only dwarves that are good at a labor will be assigned + // to it. Dwarves good at nothing, or nothing that needs doing, will tend to get + // assigned to hauling by the hauler code. If there are no hauling jobs to do, + // they will sit around idle and when enough build up they will trigger aggressive + // mode again. + bool aggressive_mode = state_count[IDLE] >= 2 && state_count[IDLE] >= n_dwarfs * idler_pct / 100; /* * Assign dwarfs to this labor. We assign at least the minimum number of dwarfs, in * order of preference, and then assign additional dwarfs that meet any of these conditions: - * - The dwarf is idle and there are no idle dwarves assigned to this labor - * - The dwarf has nonzero skill associated with the labor + * - We are in aggressive mode and have not yet assigned an idle dwarf + * - The dwarf is good at this skill * - The labor is mining, hunting, or woodcutting and the dwarf currently has it enabled. * We stop assigning dwarfs when we reach the maximum allowed. * Note that only idle and busy dwarfs count towards the number of dwarfs. "Other" dwarfs @@ -917,24 +953,23 @@ static void assign_labor(unit_labor::unit_labor labor, { int dwarf = candidates[i]; + if (dwarf_info[dwarf].trader && trader_requested) + continue; + if (dwarf_info[dwarf].diplomacy) + continue; + assert(dwarf >= 0); assert(dwarf < n_dwarfs); bool preferred_dwarf = false; - if (want_idle_dwarf && dwarf_info[dwarf].state == IDLE) - preferred_dwarf = true; - if (dwarf_skill[dwarf] > 0) + if (dwarf_skillxp[dwarf] > 0 && dwarf_skill[dwarf] >= best_skill / 2) preferred_dwarf = true; - if (previously_enabled[dwarf] && labor_infos[labor].is_exclusive) + if (previously_enabled[dwarf] && labor_infos[labor].is_exclusive && dwarf_info[dwarf].state == EXCLUSIVE) preferred_dwarf = true; if (dwarf_info[dwarf].medical && labor == df::unit_labor::DIAGNOSE) preferred_dwarf = true; - if (dwarf_info[dwarf].trader && trader_requested) - continue; - if (dwarf_info[dwarf].diplomacy) - continue; - if (labor_infos[labor].active_dwarfs >= min_dwarfs && !preferred_dwarf) + if (labor_infos[labor].active_dwarfs >= min_dwarfs && !preferred_dwarf && !aggressive_mode) continue; if (!dwarfs[dwarf]->status.labors[labor]) @@ -952,11 +987,11 @@ static void assign_labor(unit_labor::unit_labor labor, if (print_debug) out.print("Dwarf %i \"%s\" assigned %s: value %i %s %s\n", dwarf, dwarfs[dwarf]->name.first_name.c_str(), ENUM_KEY_STR(unit_labor, labor).c_str(), values[dwarf], dwarf_info[dwarf].trader ? "(trader)" : "", dwarf_info[dwarf].diplomacy ? "(diplomacy)" : ""); - if (dwarf_info[dwarf].state == IDLE || dwarf_info[dwarf].state == BUSY) + if (dwarf_info[dwarf].state == IDLE || dwarf_info[dwarf].state == BUSY || dwarf_info[dwarf].state == EXCLUSIVE) labor_infos[labor].active_dwarfs++; if (dwarf_info[dwarf].state == IDLE) - want_idle_dwarf = false; + aggressive_mode = false; } } @@ -1049,8 +1084,6 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out ) for (int dwarf = 0; dwarf < n_dwarfs; dwarf++) { - dwarf_info[dwarf].single_labor = -1; - if (dwarfs[dwarf]->status.souls.size() <= 0) continue; @@ -1248,7 +1281,7 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out ) // Set about 1/3 of the dwarfs as haulers. The haulers have all HAULER labors enabled. Having a lot of haulers helps // make sure that hauling jobs are handled quickly rather than building up. - int num_haulers = state_count[IDLE] + state_count[BUSY] * hauler_pct / 100; + int num_haulers = state_count[IDLE] + (state_count[BUSY] + state_count[EXCLUSIVE]) * hauler_pct / 100; if (num_haulers < 1) num_haulers = 1; @@ -1270,7 +1303,7 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out ) continue; } - if (dwarf_info[dwarf].state == IDLE || dwarf_info[dwarf].state == BUSY) + if (dwarf_info[dwarf].state == IDLE || dwarf_info[dwarf].state == BUSY || dwarf_info[dwarf].state == EXCLUSIVE) hauler_ids.push_back(dwarf); } dwarfinfo_sorter sorter(dwarf_info); @@ -1301,7 +1334,7 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out ) dwarfs[dwarf]->status.labors[labor] = true; dwarf_info[dwarf].assigned_jobs++; - if (dwarf_info[dwarf].state == IDLE || dwarf_info[dwarf].state == BUSY) + if (dwarf_info[dwarf].state == IDLE || dwarf_info[dwarf].state == BUSY || dwarf_info[dwarf].state == EXCLUSIVE) labor_infos[labor].active_dwarfs++; if (print_debug) From 3da939bb0208d72d30b3dd8eea2b2ec8a6217f6f Mon Sep 17 00:00:00 2001 From: Ross Morgan-Linial Date: Thu, 7 May 2015 20:41:11 -0700 Subject: [PATCH 5/8] Change tabs to spaces. --- plugins/autolabor.cpp | 66 +++++++++++++++++++++---------------------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/plugins/autolabor.cpp b/plugins/autolabor.cpp index 46898dcc9..0ab28ca65 100644 --- a/plugins/autolabor.cpp +++ b/plugins/autolabor.cpp @@ -109,8 +109,8 @@ enum dwarf_state { // Busy with a useful task BUSY, - // Busy with a useful task that requires a tool - EXCLUSIVE, + // Busy with a useful task that requires a tool + EXCLUSIVE, // In the military, can't work MILITARY, @@ -127,7 +127,7 @@ const int NUM_STATE = 6; static const char *state_names[] = { "IDLE", "BUSY", - "EXCLUSIVE", + "EXCLUSIVE", "MILITARY", "CHILD", "OTHER", @@ -748,13 +748,13 @@ struct laborinfo_sorter { bool operator() (int i,int j) { - if (labor_infos[i].mode() != labor_infos[j].mode()) - return labor_infos[i].mode() < labor_infos[j].mode(); - if (labor_infos[i].is_exclusive != labor_infos[j].is_exclusive) - return labor_infos[i].is_exclusive; - if (labor_infos[i].maximum_dwarfs() != labor_infos[j].maximum_dwarfs()) - return labor_infos[i].maximum_dwarfs() < labor_infos[j].maximum_dwarfs(); - return false; + if (labor_infos[i].mode() != labor_infos[j].mode()) + return labor_infos[i].mode() < labor_infos[j].mode(); + if (labor_infos[i].is_exclusive != labor_infos[j].is_exclusive) + return labor_infos[i].is_exclusive; + if (labor_infos[i].maximum_dwarfs() != labor_infos[j].maximum_dwarfs()) + return labor_infos[i].maximum_dwarfs() < labor_infos[j].maximum_dwarfs(); + return false; }; }; @@ -785,7 +785,7 @@ static void assign_labor(unit_labor::unit_labor labor, int best_dwarf = 0; int best_value = -10000; - int best_skill = 0; + int best_skill = 0; std::vector values(n_dwarfs); std::vector candidates; @@ -830,8 +830,8 @@ static void assign_labor(unit_labor::unit_labor labor, dwarf_skill[dwarf] = skill_level; dwarf_skillxp[dwarf] = skill_experience; - if (best_skill < skill_level) - best_skill = skill_level; + if (best_skill < skill_level) + best_skill = skill_level; value += skill_level * 100; value += skill_experience / 20; @@ -852,8 +852,8 @@ static void assign_labor(unit_labor::unit_labor labor, value += 350; } - if (dwarf_info[dwarf].has_exclusive_labor) - value -= 500; + if (dwarf_info[dwarf].has_exclusive_labor) + value -= 500; // bias by happiness @@ -920,28 +920,28 @@ static void assign_labor(unit_labor::unit_labor labor, if (unit_labor::FISH == labor && !has_fishery) min_dwarfs = max_dwarfs = 0; - // If there are enough idle dwarves to choose from, enter an aggressive assignment - // mode. "Enough" idle dwarves is defined as 2 or 10% of the total number of dwarves, - // whichever is higher. - // - // In aggressive mode, we will always pick at least one idle dwarf for each skill, - // in order to try to get the idle dwarves to start doing something. We also pick - // any dwarf more preferable to the idle dwarf, since we'd rather have a more - // preferable dwarf do a new job if one becomes available (probably because that - // dwarf just finished a job). - // - // In non-aggressive mode, only dwarves that are good at a labor will be assigned - // to it. Dwarves good at nothing, or nothing that needs doing, will tend to get - // assigned to hauling by the hauler code. If there are no hauling jobs to do, - // they will sit around idle and when enough build up they will trigger aggressive - // mode again. + // If there are enough idle dwarves to choose from, enter an aggressive assignment + // mode. "Enough" idle dwarves is defined as 2 or 10% of the total number of dwarves, + // whichever is higher. + // + // In aggressive mode, we will always pick at least one idle dwarf for each skill, + // in order to try to get the idle dwarves to start doing something. We also pick + // any dwarf more preferable to the idle dwarf, since we'd rather have a more + // preferable dwarf do a new job if one becomes available (probably because that + // dwarf just finished a job). + // + // In non-aggressive mode, only dwarves that are good at a labor will be assigned + // to it. Dwarves good at nothing, or nothing that needs doing, will tend to get + // assigned to hauling by the hauler code. If there are no hauling jobs to do, + // they will sit around idle and when enough build up they will trigger aggressive + // mode again. bool aggressive_mode = state_count[IDLE] >= 2 && state_count[IDLE] >= n_dwarfs * idler_pct / 100; /* * Assign dwarfs to this labor. We assign at least the minimum number of dwarfs, in * order of preference, and then assign additional dwarfs that meet any of these conditions: * - We are in aggressive mode and have not yet assigned an idle dwarf - * - The dwarf is good at this skill + * - The dwarf is good at this skill * - The labor is mining, hunting, or woodcutting and the dwarf currently has it enabled. * We stop assigning dwarfs when we reach the maximum allowed. * Note that only idle and busy dwarfs count towards the number of dwarfs. "Other" dwarfs @@ -1248,8 +1248,8 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out ) std::sort(labors.begin(), labors.end(), lasorter); // Handle DISABLED skills (just bookkeeping). - // Note that autolabor should *NEVER* enable or disable a skill that has been marked as DISABLED, for any reason. - // The user has told us that they want manage this skill manually, and we must respect that. + // Note that autolabor should *NEVER* enable or disable a skill that has been marked as DISABLED, for any reason. + // The user has told us that they want manage this skill manually, and we must respect that. for (auto lp = labors.begin(); lp != labors.end(); ++lp) { auto labor = *lp; From 7aac9a8fb81481a9ab37377ae6a4dc92b6f84a1d Mon Sep 17 00:00:00 2001 From: Ross Morgan-Linial Date: Thu, 7 May 2015 11:03:10 -0700 Subject: [PATCH 6/8] Improve workflow handling of plant reactions Makes workflow understand "seeds of any plant", "thread of any plant", "drink of any plant", "powder of any plant", and "liquid of any plant" when counting items. Changes workflow's handling of the built-in plant reactions to understand that they always produce plant products, and that they also produce seeds. This means that you can set a target for, for example, "powder of any plant", and workflow will use a milling job to produce that. --- library/modules/Materials.cpp | 5 +++++ plugins/lua/workflow.lua | 10 +++++++++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/library/modules/Materials.cpp b/library/modules/Materials.cpp index 20b5a913c..e0337bf78 100644 --- a/library/modules/Materials.cpp +++ b/library/modules/Materials.cpp @@ -396,6 +396,11 @@ bool MaterialInfo::matches(const df::job_material_category &cat) using namespace df::enums::material_flags; TEST(plant, STRUCTURAL_PLANT_MAT); + TEST(plant, SEED_MAT); + TEST(plant, THREAD_PLANT); + TEST(plant, ALCOHOL_PLANT); + TEST(plant, POWDER_MISC_PLANT); + TEST(plant, LIQUID_MISC_PLANT); TEST(wood, WOOD); TEST(cloth, THREAD_PLANT); TEST(silk, SILK); diff --git a/plugins/lua/workflow.lua b/plugins/lua/workflow.lua index b7f6406a9..7f1d5a08d 100644 --- a/plugins/lua/workflow.lua +++ b/plugins/lua/workflow.lua @@ -203,12 +203,20 @@ for job,flag in pairs(plant_products) do local itag = 'idx_'..string.lower(flag) job_outputs[job] = function(callback, job) local mat_type, mat_index = -1, -1 + local seed_type, seed_index = -1, -1 local mat = dfhack.matinfo.decode(job.job_items[0]) if mat and mat.plant and mat.plant.flags[flag] then mat_type = mat.plant.material_defs[ttag] mat_index = mat.plant.material_defs[itag] + seed_type = mat.plant.material_defs['type_seed'] + seed_index = mat.plant.material_defs['idx_seed'] end - default_output(callback, job, mat_type, mat_index) + local mat_mask = { } + if flag ~= 'LEAVES' then + mat_mask.plant = true + end + default_output(callback, job, mat_type, mat_index, mat_mask) + callback{ item_type = df.item_type.SEEDS, mat_type = seed_type, mat_index = seed_index } end end From 51d61d91cb97dbe1ba2b8fc8464de62414538122 Mon Sep 17 00:00:00 2001 From: Ross Morgan-Linial Date: Thu, 7 May 2015 20:50:19 -0700 Subject: [PATCH 7/8] Change tabs to spaces. --- library/modules/Materials.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/modules/Materials.cpp b/library/modules/Materials.cpp index e0337bf78..b7f9675f5 100644 --- a/library/modules/Materials.cpp +++ b/library/modules/Materials.cpp @@ -399,8 +399,8 @@ bool MaterialInfo::matches(const df::job_material_category &cat) TEST(plant, SEED_MAT); TEST(plant, THREAD_PLANT); TEST(plant, ALCOHOL_PLANT); - TEST(plant, POWDER_MISC_PLANT); - TEST(plant, LIQUID_MISC_PLANT); + TEST(plant, POWDER_MISC_PLANT); + TEST(plant, LIQUID_MISC_PLANT); TEST(wood, WOOD); TEST(cloth, THREAD_PLANT); TEST(silk, SILK); From 3001053cef0ee6616a6d8c9134a63a5263b9a5e3 Mon Sep 17 00:00:00 2001 From: Ross Morgan-Linial Date: Thu, 7 May 2015 21:51:03 -0700 Subject: [PATCH 8/8] Change tabs to spaces. --- plugins/lua/workflow.lua | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/plugins/lua/workflow.lua b/plugins/lua/workflow.lua index 7f1d5a08d..dcec1248d 100644 --- a/plugins/lua/workflow.lua +++ b/plugins/lua/workflow.lua @@ -203,20 +203,20 @@ for job,flag in pairs(plant_products) do local itag = 'idx_'..string.lower(flag) job_outputs[job] = function(callback, job) local mat_type, mat_index = -1, -1 - local seed_type, seed_index = -1, -1 + local seed_type, seed_index = -1, -1 local mat = dfhack.matinfo.decode(job.job_items[0]) if mat and mat.plant and mat.plant.flags[flag] then mat_type = mat.plant.material_defs[ttag] mat_index = mat.plant.material_defs[itag] - seed_type = mat.plant.material_defs['type_seed'] - seed_index = mat.plant.material_defs['idx_seed'] + seed_type = mat.plant.material_defs['type_seed'] + seed_index = mat.plant.material_defs['idx_seed'] end - local mat_mask = { } - if flag ~= 'LEAVES' then - mat_mask.plant = true - end - default_output(callback, job, mat_type, mat_index, mat_mask) - callback{ item_type = df.item_type.SEEDS, mat_type = seed_type, mat_index = seed_index } + local mat_mask = { } + if flag ~= 'LEAVES' then + mat_mask.plant = true + end + default_output(callback, job, mat_type, mat_index, mat_mask) + callback{ item_type = df.item_type.SEEDS, mat_type = seed_type, mat_index = seed_index } end end