|
|
|
@ -31,6 +31,31 @@ using df::global::world;
|
|
|
|
|
|
|
|
|
|
#define ARRAY_COUNT(array) (sizeof(array)/sizeof((array)[0]))
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Autolabor module for dfhack
|
|
|
|
|
*
|
|
|
|
|
* The idea behind this module is to constantly adjust labors so that the right dwarves
|
|
|
|
|
* are assigned to new tasks. The key is that, for almost all labors, once a dwarf begins
|
|
|
|
|
* a job it will finish that job even if the associated labor is removed. Thus the
|
|
|
|
|
* strategy is to frequently decide, for each labor, which dwarves should possibly take
|
|
|
|
|
* a new job for that labor if it comes in and which shouldn't, and then set the labors
|
|
|
|
|
* appropriately. The updating should happen as often as can be reasonably done without
|
|
|
|
|
* causing lag.
|
|
|
|
|
*
|
|
|
|
|
* The obvious thing to do is to just set each labor on a single idle dwarf who is best
|
|
|
|
|
* suited to doing new jobs of that labor. This works in a way, but it leads to a lot
|
|
|
|
|
* of idle dwarves since only one dwarf will be dispatched for each labor in an update
|
|
|
|
|
* cycle, and dwarves that finish tasks will wait for the next update before being
|
|
|
|
|
* dispatched. An improvement is to also set some labors on dwarves that are currently
|
|
|
|
|
* doing a job, so that they will immediately take a new job when they finish. The
|
|
|
|
|
* details of which dwarves should have labors set is mostly a heuristic.
|
|
|
|
|
*
|
|
|
|
|
* A complication to the above simple scheme is labors that have associated equipment.
|
|
|
|
|
* Enabling/disabling these labors causes dwarves to change equipment, and disabling
|
|
|
|
|
* them in the middle of a job may cause the job to be abandoned. Those labors
|
|
|
|
|
* (mining, hunting, and woodcutting) need to be handled carefully to minimize churn.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
static int enable_autolabor = 0;
|
|
|
|
|
|
|
|
|
|
static bool print_debug = 0;
|
|
|
|
@ -659,11 +684,11 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out )
|
|
|
|
|
{
|
|
|
|
|
dwarf_info[dwarf].state = CHILD;
|
|
|
|
|
}
|
|
|
|
|
else if (ENUM_ATTR(profession, military, dwarfs[dwarf]->profession))
|
|
|
|
|
dwarf_info[dwarf].state = MILITARY;
|
|
|
|
|
else if (dwarfs[dwarf]->job.current_job == NULL)
|
|
|
|
|
{
|
|
|
|
|
if (ENUM_ATTR(profession, military, dwarfs[dwarf]->profession))
|
|
|
|
|
dwarf_info[dwarf].state = MILITARY;
|
|
|
|
|
else if (is_on_break)
|
|
|
|
|
if (is_on_break)
|
|
|
|
|
dwarf_info[dwarf].state = OTHER;
|
|
|
|
|
else if (dwarfs[dwarf]->meetings.size() > 0)
|
|
|
|
|
dwarf_info[dwarf].state = OTHER;
|
|
|
|
@ -773,6 +798,7 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out )
|
|
|
|
|
|
|
|
|
|
auto mode = labor_infos[labor].mode;
|
|
|
|
|
|
|
|
|
|
// Find candidate dwarfs, and calculate a preference value for each dwarf
|
|
|
|
|
for (int dwarf = 0; dwarf < n_dwarfs; dwarf++)
|
|
|
|
|
{
|
|
|
|
|
if (dwarf_info[dwarf].state == CHILD)
|
|
|
|
@ -827,9 +853,11 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out )
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Sort candidates by preference value
|
|
|
|
|
values_sorter ivs(values);
|
|
|
|
|
std::sort(candidates.begin(), candidates.end(), ivs);
|
|
|
|
|
|
|
|
|
|
// Disable the labor on everyone
|
|
|
|
|
for (int dwarf = 0; dwarf < n_dwarfs; dwarf++)
|
|
|
|
|
{
|
|
|
|
|
if (dwarf_info[dwarf].state == CHILD)
|
|
|
|
@ -852,6 +880,17 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out )
|
|
|
|
|
if (state_count[IDLE] < 2)
|
|
|
|
|
want_idle_dwarf = false;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* 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
|
|
|
|
|
* - 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
|
|
|
|
|
* (sleeping, eating, on break, etc.) will have labors assigned, but will not be counted.
|
|
|
|
|
* Military and children/nobles will not have labors assigned.
|
|
|
|
|
*/
|
|
|
|
|
for (int i = 0; i < candidates.size() && labor_infos[labor].active_dwarfs < max_dwarfs; i++)
|
|
|
|
|
{
|
|
|
|
|
int dwarf = candidates[i];
|
|
|
|
|