From 2072dcc38d9cadf747006223f12018ef16056221 Mon Sep 17 00:00:00 2001 From: Kelly Kinkade Date: Mon, 27 Jun 2016 12:04:51 -0500 Subject: [PATCH 01/22] Rename autolabor2 to labormanager and bring up to date with current --- plugins/devel/CMakeLists.txt | 2 +- .../{autolabor2.cpp => labormanager.cpp} | 487 +++++++++++++----- 2 files changed, 354 insertions(+), 135 deletions(-) rename plugins/devel/{autolabor2.cpp => labormanager.cpp} (86%) diff --git a/plugins/devel/CMakeLists.txt b/plugins/devel/CMakeLists.txt index 1f3ff6f75..a1e5b7f14 100644 --- a/plugins/devel/CMakeLists.txt +++ b/plugins/devel/CMakeLists.txt @@ -3,13 +3,13 @@ DFHACK_PLUGIN(vectors vectors.cpp) endif() ADD_DEFINITIONS(-DDEV_PLUGIN) -#DFHACK_PLUGIN(autolabor2 autolabor2.cpp) DFHACK_PLUGIN(buildprobe buildprobe.cpp) DFHACK_PLUGIN(color-dfhack-text color-dfhack-text.cpp) DFHACK_PLUGIN(counters counters.cpp) DFHACK_PLUGIN(dumpmats dumpmats.cpp) DFHACK_PLUGIN(eventExample eventExample.cpp) DFHACK_PLUGIN(frozen frozen.cpp) +DFHACK_PLUGIN(labormanager labormanager.cpp) DFHACK_PLUGIN(kittens kittens.cpp) DFHACK_PLUGIN(memview memview.cpp) DFHACK_PLUGIN(nestboxes nestboxes.cpp) diff --git a/plugins/devel/autolabor2.cpp b/plugins/devel/labormanager.cpp similarity index 86% rename from plugins/devel/autolabor2.cpp rename to plugins/devel/labormanager.cpp index 4897832d4..12e4e8ca1 100644 --- a/plugins/devel/autolabor2.cpp +++ b/plugins/devel/labormanager.cpp @@ -1,5 +1,5 @@ /* -* Autolabor 2.0 module for dfhack +* Labor manager (formerly Autolabor 2) module for dfhack * * */ @@ -77,7 +77,7 @@ using df::global::world; #define ARRAY_COUNT(array) (sizeof(array)/sizeof((array)[0])) -DFHACK_PLUGIN_IS_ENABLED(enable_autolabor); +DFHACK_PLUGIN_IS_ENABLED(enable_labormanager); static bool print_debug = 0; @@ -94,11 +94,11 @@ enum ConfigFlags { // Here go all the command declarations... // mostly to allow having the mandatory stuff on top of the file and commands on the bottom -command_result autolabor (color_ostream &out, std::vector & parameters); +command_result labormanager (color_ostream &out, std::vector & parameters); // A plugin must be able to return its name and version. -// The name string provided must correspond to the filename - autolabor2.plug.so or autolabor2.plug.dll in this case -DFHACK_PLUGIN("autolabor2"); +// The name string provided must correspond to the filename - labormanager.plug.so or labormanager.plug.dll in this case +DFHACK_PLUGIN("labormanager"); static void generate_labor_to_skill_map(); @@ -486,9 +486,20 @@ static const struct labor_default default_labor_infos[] = { /* PRESSING */ {200, 0, TOOL_NONE}, /* BEEKEEPING */ {200, 0, TOOL_NONE}, /* WAX_WORKING */ {200, 0, TOOL_NONE}, - /* PUSH_HAUL_VEHICLES */ {200, 0, TOOL_NONE} + /* PUSH_HAUL_VEHICLES */ {200, 0, TOOL_NONE}, + /* HAUL_TRADE */ {200, 0, TOOL_NONE}, + /* PULL_LEVER */ {200, 0, TOOL_NONE}, + /* REMOVE_CONSTRUCTION */ {200, 0, TOOL_NONE}, + /* HAUL_WATER */ {200, 0, TOOL_NONE}, + /* GELD */ {200, 0, TOOL_NONE}, + /* BUILD_ROAD */ {200, 0, TOOL_NONE}, + /* BUILD_CONSTRUCTION */ {200, 0, TOOL_NONE}, + /* PAPERMAKING */ {200, 0, TOOL_NONE}, + /* BOOKBINDING */ {200, 0, TOOL_NONE} }; +void debug (char* fmt, ...); + struct dwarf_info_t { df::unit* dwarf; @@ -506,12 +517,19 @@ struct dwarf_info_t df::unit_labor using_labor; dwarf_info_t(df::unit* dw) : dwarf(dw), clear_all(false), - state(OTHER), high_skill(0), has_children(false), armed(false) + state(OTHER), high_skill(0), has_children(false), armed(false), using_labor(df::unit_labor::NONE) { for (int e = TOOL_NONE; e < TOOLS_MAX; e++) has_tool[e] = false; } + ~dwarf_info_t() + { + if (print_debug) + debug("LABORMANAGER: destroying dwarf %p\n", (void*) this); + } + + void set_labor(df::unit_labor labor) { if (labor >= 0 && labor <= ENUM_LAST_ITEM(unit_labor)) @@ -554,10 +572,10 @@ static df::unit_labor hauling_labor_map[] = df::unit_labor::HAUL_ITEM, /* INSTRUMENT */ df::unit_labor::HAUL_ITEM, /* TOY */ df::unit_labor::HAUL_FURNITURE, /* WINDOW */ - df::unit_labor::HAUL_ANIMAL, /* CAGE */ + df::unit_labor::HAUL_ANIMALS, /* CAGE */ df::unit_labor::HAUL_ITEM, /* BARREL */ df::unit_labor::HAUL_ITEM, /* BUCKET */ - df::unit_labor::HAUL_ANIMAL, /* ANIMALTRAP */ + df::unit_labor::HAUL_ANIMALS, /* ANIMALTRAP */ df::unit_labor::HAUL_FURNITURE, /* TABLE */ df::unit_labor::HAUL_FURNITURE, /* COFFIN */ df::unit_labor::HAUL_FURNITURE, /* STATUE */ @@ -850,7 +868,7 @@ private: return df::unit_labor::SIEGECRAFT; } - debug ("AUTOLABOR: Cannot deduce labor for construct building job of type %s\n", + debug ("LABORMANAGER: Cannot deduce labor for construct building job of type %s\n", ENUM_KEY_STR(building_type, bld->getType()).c_str()); return df::unit_labor::NONE; @@ -944,7 +962,7 @@ private: return df::unit_labor::SIEGECRAFT; } - debug ("AUTOLABOR: Cannot deduce labor for destroy building job of type %s\n", + debug ("LABORMANAGER: Cannot deduce labor for destroy building job of type %s\n", ENUM_KEY_STR(building_type, bld->getType()).c_str()); return df::unit_labor::NONE; @@ -979,13 +997,13 @@ private: return df::unit_labor::BONE_CARVE; else { - debug ("AUTOLABOR: Cannot deduce labor for make crafts job (not bone)\n"); + debug ("LABORMANAGER: Cannot deduce labor for make crafts job (not bone)\n"); return df::unit_labor::NONE; } case df::item_type::WOOD: return df::unit_labor::WOOD_CRAFT; default: - debug ("AUTOLABOR: Cannot deduce labor for make crafts job, item type %s\n", + debug ("LABORMANAGER: Cannot deduce labor for make crafts job, item type %s\n", ENUM_KEY_STR(item_type, jobitem).c_str()); return df::unit_labor::NONE; } @@ -1002,7 +1020,7 @@ private: case df::workshop_type::MetalsmithsForge: return metaltype; default: - debug ("AUTOLABOR: Cannot deduce labor for make job, workshop type %s\n", + debug ("LABORMANAGER: Cannot deduce labor for make job, workshop type %s\n", ENUM_KEY_STR(workshop_type, type).c_str()); return df::unit_labor::NONE; } @@ -1016,13 +1034,13 @@ private: case df::furnace_type::GlassFurnace: return df::unit_labor::GLASSMAKER; default: - debug ("AUTOLABOR: Cannot deduce labor for make job, furnace type %s\n", + debug ("LABORMANAGER: Cannot deduce labor for make job, furnace type %s\n", ENUM_KEY_STR(furnace_type, type).c_str()); return df::unit_labor::NONE; } } - debug ("AUTOLABOR: Cannot deduce labor for make job, building type %s\n", + debug ("LABORMANAGER: Cannot deduce labor for make job, building type %s\n", ENUM_KEY_STR(building_type, bld->getType()).c_str()); return df::unit_labor::NONE; @@ -1147,8 +1165,6 @@ public: job_to_labor_table[df::job_type::StoreItemInStockpile] = jlf_hauling; job_to_labor_table[df::job_type::StoreItemInBag] = jlf_hauling; job_to_labor_table[df::job_type::StoreItemInHospital] = jlf_hauling; - job_to_labor_table[df::job_type::StoreItemInChest] = jlf_hauling; - job_to_labor_table[df::job_type::StoreItemInCabinet] = jlf_hauling; job_to_labor_table[df::job_type::StoreWeapon] = jlf_hauling; job_to_labor_table[df::job_type::StoreArmor] = jlf_hauling; job_to_labor_table[df::job_type::StoreItemInBarrel] = jlf_hauling; @@ -1219,7 +1235,6 @@ public: job_to_labor_table[df::job_type::MilkCreature] = jlf_const(df::unit_labor::MILK); job_to_labor_table[df::job_type::MakeCheese] = jlf_const(df::unit_labor::MAKE_CHEESE); job_to_labor_table[df::job_type::ProcessPlants] = jlf_const(df::unit_labor::PROCESS_PLANT); - job_to_labor_table[df::job_type::ProcessPlantsBag] = jlf_const(df::unit_labor::PROCESS_PLANT); job_to_labor_table[df::job_type::ProcessPlantsVial] = jlf_const(df::unit_labor::PROCESS_PLANT); job_to_labor_table[df::job_type::ProcessPlantsBarrel] = jlf_const(df::unit_labor::PROCESS_PLANT); job_to_labor_table[df::job_type::PrepareMeal] = jlf_const(df::unit_labor::COOK); @@ -1256,7 +1271,6 @@ public: job_to_labor_table[df::job_type::CastSpell] = jlf_no_labor; job_to_labor_table[df::job_type::LinkBuildingToTrigger] = jlf_const(df::unit_labor::MECHANIC) ; job_to_labor_table[df::job_type::PullLever] = jlf_no_labor; - job_to_labor_table[df::job_type::BrewDrink] = jlf_const(df::unit_labor::BREWER) ; job_to_labor_table[df::job_type::ExtractFromPlants] = jlf_const(df::unit_labor::HERBALIST) ; job_to_labor_table[df::job_type::ExtractFromRawFish] = jlf_const(df::unit_labor::DISSECT_FISH) ; job_to_labor_table[df::job_type::ExtractFromLandAnimal] = jlf_const(df::unit_labor::DISSECT_VERMIN) ; @@ -1334,9 +1348,9 @@ public: job_to_labor_table[df::job_type::ExecuteCriminal] = jlf_no_labor; job_to_labor_table[df::job_type::TrainAnimal] = jlf_const(df::unit_labor::ANIMALTRAIN); job_to_labor_table[df::job_type::CarveTrack] = jlf_const(df::unit_labor::DETAIL); - job_to_labor_table[df::job_type::PushTrackVehicle] = jlf_const(df::unit_labor::PUSH_HAUL_VEHICLE); - job_to_labor_table[df::job_type::PlaceTrackVehicle] = jlf_const(df::unit_labor::PUSH_HAUL_VEHICLE); - job_to_labor_table[df::job_type::StoreItemInVehicle] = jlf_const(df::unit_labor::PUSH_HAUL_VEHICLE); + job_to_labor_table[df::job_type::PushTrackVehicle] = jlf_const(df::unit_labor::HANDLE_VEHICLES); + job_to_labor_table[df::job_type::PlaceTrackVehicle] = jlf_const(df::unit_labor::HANDLE_VEHICLES); + job_to_labor_table[df::job_type::StoreItemInVehicle] = jlf_const(df::unit_labor::HANDLE_VEHICLES); job_to_labor_table[df::job_type::GeldAnimal] = jlf_const(df::unit_labor::GELD); job_to_labor_table[df::job_type::MakeFigurine] = jlf_make_object; job_to_labor_table[df::job_type::MakeAmulet] = jlf_make_object; @@ -1365,7 +1379,14 @@ public: df::unit_labor labor; - labor = job_to_labor_table[j->job_type]->get_labor(j); + if (job_to_labor_table.count(j->job_type) == 0) + { + debug("LABORMANAGER: job has no job to labor table entry: %s\n", ENUM_KEY_STR(job_type, j->job_type).c_str()); + labor = df::unit_labor::NONE; + } else { + + labor = job_to_labor_table[j->job_type]->get_labor(j); + } return labor; } @@ -1375,6 +1396,8 @@ public: static JobLaborMapper* labor_mapper = 0; +static bool initialized = false; + static bool isOptionEnabled(unsigned flag) { return config.isValid() && (config.ival(0) & flag) != 0; @@ -1393,8 +1416,9 @@ static void setOptionEnabled(ConfigFlags flag, bool on) static void cleanup_state() { - enable_autolabor = false; + enable_labormanager = false; labor_infos.clear(); + initialized = false; } static void reset_labor(df::unit_labor labor) @@ -1405,25 +1429,25 @@ static void reset_labor(df::unit_labor labor) static void init_state() { - config = World::GetPersistentData("autolabor/2.0/config"); + config = World::GetPersistentData("labormanager/2.0/config"); if (config.isValid() && config.ival(0) == -1) config.ival(0) = 0; - enable_autolabor = isOptionEnabled(CF_ENABLED); + enable_labormanager = isOptionEnabled(CF_ENABLED); - if (!enable_autolabor) + if (!enable_labormanager) return; // Load labors from save labor_infos.resize(ARRAY_COUNT(default_labor_infos)); std::vector items; - World::GetPersistentData(&items, "autolabor/2.0/labors/", true); + World::GetPersistentData(&items, "labormanager/2.0/labors/", true); for (auto p = items.begin(); p != items.end(); p++) { string key = p->key(); - df::unit_labor labor = (df::unit_labor) atoi(key.substr(strlen("autolabor/2.0/labors/")).c_str()); + df::unit_labor labor = (df::unit_labor) atoi(key.substr(strlen("labormanager/2.0/labors/")).c_str()); if (labor >= 0 && labor <= labor_infos.size()) { labor_infos[labor].config = *p; @@ -1437,7 +1461,7 @@ static void init_state() continue; std::stringstream name; - name << "autolabor/2.0/labors/" << i; + name << "labormanager/2.0/labors/" << i; labor_infos[i].config = World::AddPersistentData(name.str()); labor_infos[i].mark_assigned(); @@ -1445,6 +1469,8 @@ static void init_state() reset_labor((df::unit_labor) i); } + initialized = true; + } static df::job_skill labor_to_skill[ENUM_LAST_ITEM(unit_labor) + 1]; @@ -1477,12 +1503,12 @@ static void enable_plugin(color_ostream &out) { if (!config.isValid()) { - config = World::AddPersistentData("autolabor/2.0/config"); + config = World::AddPersistentData("labormanager/2.0/config"); config.ival(0) = 0; } setOptionEnabled(CF_ENABLED, true); - enable_autolabor = true; + enable_labormanager = true; out << "Enabling the plugin." << endl; cleanup_state(); @@ -1497,32 +1523,32 @@ DFhackCExport command_result plugin_init ( color_ostream &out, std::vector \n" + " labormanager max \n" " Set max number of dwarves assigned to a labor.\n" - " autolabor2 max none\n" + " labormanager max none\n" " Unrestrict the number of dwarves assigned to a labor.\n" - " autolabor2 priority \n" + " labormanager priority \n" " Change the assignment priority of a labor (default is 100)\n" - " autolabor2 reset \n" + " labormanager reset \n" " Return a labor to the default handling.\n" - " autolabor2 reset-all\n" + " labormanager reset-all\n" " Return all labors to the default handling.\n" - " autolabor2 list\n" + " labormanager list\n" " List current status of all labors.\n" - " autolabor2 status\n" + " labormanager status\n" " Show basic status information.\n" "Function:\n" - " When enabled, autolabor periodically checks your dwarves and enables or\n" + " When enabled, labormanager periodically checks your dwarves and enables or\n" " disables labors. Generally, each dwarf will be assigned exactly one labor.\n" - " Warning: autolabor will override any manual changes you make to labors\n" - " while it is enabled. Do not try to run both autolabor and autolabor2 at\n" - " the same time." + " Warning: labormanager will override any manual changes you make to labors\n" + " while it is enabled. Do not try to run both labormanager and labormanager at\n" + " the same time.\n" )); generate_labor_to_skill_map(); @@ -1594,14 +1620,21 @@ private: int need_food_water; + int priority_food; + std::map labor_needed; std::map labor_outside; std::vector dwarf_info; std::list available_dwarfs; + std::list busy_dwarfs; private: void scan_buildings() { + has_butchers = false; + has_fishery = false; + trader_requested = false; + for (auto b = world->buildings.all.begin(); b != world->buildings.all.end(); b++) { df::building *build = *b; @@ -1658,16 +1691,14 @@ private: if (dig != df::enums::tile_dig_designation::No) { df::tiletype tt = bl->tiletype[x][y]; + df::tiletype_material ttm = ENUM_ATTR(tiletype, material, tt); df::tiletype_shape tts = ENUM_ATTR(tiletype, shape, tt); - switch (tts) - { - case df::enums::tiletype_shape::TREE: - tree_count++; break; - case df::enums::tiletype_shape::SHRUB: - plant_count++; break; - default: - dig_count++; break; - } + if (ttm == df::enums::tiletype_material::TREE) + tree_count++; + else if (tts == df::enums::tiletype_shape::SHRUB) + plant_count++; + else + dig_count++; } if (bl->designation[x][y].bits.smooth != 0) detail_count++; @@ -1684,13 +1715,15 @@ private: for (int e = TOOL_NONE; e < TOOLS_MAX; e++) tool_count[e] = 0; + priority_food = 0; + df::item_flags bad_flags; bad_flags.whole = 0; #define F(x) bad_flags.bits.x = true; F(dump); F(forbid); F(garbage_collect); F(hostile); F(on_fire); F(rotten); F(trader); - F(in_building); F(construction); F(artifact); + F(in_building); F(construction); #undef F auto& v = world->items.all; @@ -1704,6 +1737,11 @@ private: if (item->flags.whole & bad_flags.whole) continue; + df::item_type t = item->getType(); + + if (item->materialRots() && t != df::item_type::CORPSEPIECE && t != df::item_type::CORPSE && item->getRotTimer() > 1) + priority_food++; + if (!item->isWeapon()) continue; @@ -1869,14 +1907,20 @@ private: { state = CHILD; } + else if (ENUM_ATTR(profession, military, dwarf->dwarf->profession)) state = MILITARY; + + else if (dwarf->dwarf->burrows.size() > 0) + state = OTHER; // dwarfs assigned to burrows are treated as if permanently busy + else if (dwarf->dwarf->job.current_job == NULL) { - if (is_on_break) + if (is_on_break || dwarf->dwarf->flags1.bits.chained || dwarf->dwarf->flags1.bits.caged) + { state = OTHER; - else if (dwarf->dwarf->burrows.size() > 0) - state = OTHER; // dwarfs assigned to burrows are treated as if permanently busy + dwarf->clear_all = true; + } else if (dwarf->dwarf->status2.limbs_grasp_count == 0) { state = OTHER; // dwarfs unable to grasp are incapable of nearly all labors @@ -1902,18 +1946,23 @@ private: if (state == BUSY) { df::unit_labor labor = labor_mapper->find_job_labor(dwarf->dwarf->job.current_job); + + dwarf->using_labor = labor; + if (labor != df::unit_labor::NONE) { - dwarf->using_labor = labor; + labor_infos[labor].busy_dwarfs++; - if (!dwarf->dwarf->status.labors[labor] && print_debug) + if (!dwarf->dwarf->status.labors[labor]) { - out.print("AUTOLABOR: dwarf %s (id %d) is doing job %s(%d) but is not enabled for labor %s(%d).\n", + out.print("LABORMANAGER: dwarf %s (id %d) is doing job %s(%d) but is not enabled for labor %s(%d).\n", dwarf->dwarf->name.first_name.c_str(), dwarf->dwarf->id, ENUM_KEY_STR(job_type, job).c_str(), job, ENUM_KEY_STR(unit_labor, labor).c_str(), labor); } } } + if (state == OTHER) + dwarf->clear_all = true; } dwarf->state = state; @@ -1925,8 +1974,6 @@ private: if (dwarf->dwarf->status.labors[l]) if (state == IDLE) labor_infos[l].idle_dwarfs++; - else if (state == BUSY) - labor_infos[l].busy_dwarfs++; } @@ -2022,19 +2069,69 @@ private: dwarf->clear_labor(labor); } + } else { + if (state == IDLE) + available_dwarfs.push_back(dwarf); + + if (state == BUSY) + busy_dwarfs.push_back(dwarf); } - else if (state == IDLE || state == BUSY) - available_dwarfs.push_back(dwarf); + } + + } + } + void release_dwarf_list() + { + while (!dwarf_info.empty()) { + auto d = dwarf_info.begin(); + delete *d; + dwarf_info.erase(d); + } + available_dwarfs.clear(); + busy_dwarfs.clear(); + } + + int score_labor (dwarf_info_t* d, df::unit_labor labor) + { + int skill_level = 0; + int xp = 0; + + if (labor != df::unit_labor::NONE) + { + df::job_skill skill = labor_to_skill[labor]; + if (skill != df::job_skill::NONE) + { + skill_level = Units::getEffectiveSkill(d->dwarf, skill); + xp = Units::getExperience(d->dwarf, skill, false); } + } + int score = skill_level * 1000 - (d->high_skill - skill_level) * 2000 + (xp / (skill_level + 5) * 10); + + if (labor != df::unit_labor::NONE) + { + if (d->dwarf->status.labors[labor]) + if (labor == df::unit_labor::OPERATE_PUMP) + score += 50000; + else + score += 1000; + if (default_labor_infos[labor].tool != TOOL_NONE && + d->has_tool[default_labor_infos[labor].tool]) + score += 5000; + if (d->has_children && labor_outside[labor]) + score -= 15000; + if (d->armed && labor_outside[labor]) + score += 5000; } + + return score; } public: void process() { - dwarf_info.clear(); + release_dwarf_list(); dig_count = tree_count = plant_count = detail_count = 0; cnt_recover_wounded = cnt_diagnosis = cnt_immobilize = cnt_dressing = cnt_cleaning = cnt_surgery = cnt_suture = @@ -2106,13 +2203,27 @@ public: labor_needed[df::unit_labor::HAUL_FOOD] += world->stockpile.num_jobs[6]; labor_needed[df::unit_labor::HAUL_REFUSE] += world->stockpile.num_jobs[7]; labor_needed[df::unit_labor::HAUL_FURNITURE] += world->stockpile.num_jobs[8]; - labor_needed[df::unit_labor::HAUL_ANIMAL] += world->stockpile.num_jobs[9]; + labor_needed[df::unit_labor::HAUL_ANIMALS] += world->stockpile.num_jobs[9]; + + labor_needed[df::unit_labor::HAUL_STONE] += (world->stockpile.num_jobs[1] >= world->stockpile.num_haulers[1]) ? 1 : 0; + labor_needed[df::unit_labor::HAUL_WOOD] += (world->stockpile.num_jobs[2] >= world->stockpile.num_haulers[2]) ? 1 : 0; + labor_needed[df::unit_labor::HAUL_ITEM] += (world->stockpile.num_jobs[3] >= world->stockpile.num_haulers[3]) ? 1 : 0; + labor_needed[df::unit_labor::HAUL_BODY] += (world->stockpile.num_jobs[5] >= world->stockpile.num_haulers[5]) ? 1 : 0; + labor_needed[df::unit_labor::HAUL_FOOD] += (world->stockpile.num_jobs[6] >= world->stockpile.num_haulers[6]) ? 1 : 0; + labor_needed[df::unit_labor::HAUL_REFUSE] += (world->stockpile.num_jobs[7] >= world->stockpile.num_haulers[7]) ? 1 : 0; + labor_needed[df::unit_labor::HAUL_FURNITURE] += (world->stockpile.num_jobs[8] >= world->stockpile.num_haulers[8]) ? 1 : 0; + labor_needed[df::unit_labor::HAUL_ANIMALS] += (world->stockpile.num_jobs[9] >= world->stockpile.num_haulers[9]) ? 1 : 0; + + int binjobs = world->stockpile.num_jobs[4] + ((world->stockpile.num_jobs[4] >= world->stockpile.num_haulers[4]) ? 1 : 0); + + labor_needed[df::unit_labor::HAUL_ITEM] += binjobs; + labor_needed[df::unit_labor::HAUL_FOOD] += priority_food; // add entries for vehicle hauling for (auto v = world->vehicles.all.begin(); v != world->vehicles.all.end(); v++) if ((*v)->route_id != -1) - labor_needed[df::unit_labor::PUSH_HAUL_VEHICLE]++; + labor_needed[df::unit_labor::HANDLE_VEHICLES]++; // add fishing & hunting @@ -2131,33 +2242,101 @@ public: // note: this doesn't test to see if the trainer is actually needed, and thus will overallocate trainers. bleah. } + /* move idle dwarfs ready to be assigned to busy list */ + for (auto d = available_dwarfs.begin(); d != available_dwarfs.end(); ) + { + bool busy = false; + + FOR_ENUM_ITEMS(unit_labor, l) + { + if (l == df::unit_labor::NONE) + continue; + + + if (labor_needed[l] > 0 && (*d)->dwarf->status.labors[l]) + { + busy = true; + labor_needed[l] = max(labor_needed[l]-1, 0); + } + } + + if (busy) + { + busy_dwarfs.push_back(*d); + d = available_dwarfs.erase(d); + } else { + d++; + } + } + /* adjust for over/under */ FOR_ENUM_ITEMS(unit_labor, l) { if (l == df::unit_labor::NONE) continue; - if (l >= df::unit_labor::HAUL_STONE && l <= df::unit_labor::HAUL_ANIMAL) - continue; - if (labor_infos[l].idle_dwarfs > 0 && labor_needed[l] > labor_infos[l].busy_dwarfs) + if (labor_infos[l].idle_dwarfs > 0) + labor_needed[l] = 0; + else + labor_needed[l] = std::max(labor_needed[l] - labor_infos[l].busy_dwarfs, 0); + } + + /* assign food haulers for rotting food items */ + + if (priority_food > 0 && labor_infos[df::unit_labor::HAUL_FOOD].idle_dwarfs > 0) + priority_food = 1; + + if (print_debug) + out.print ("priority food count = %d\n", priority_food); + + while (!available_dwarfs.empty() && priority_food > 0) + { + std::list::iterator bestdwarf = available_dwarfs.begin(); + + int best_score = INT_MIN; + + for (std::list::iterator k = available_dwarfs.begin(); k != available_dwarfs.end(); k++) { - int clawback = labor_infos[l].busy_dwarfs; - if (clawback == 0 && labor_needed[l] > 0) - clawback = 1; + dwarf_info_t* d = (*k); + int score = score_labor(d, df::unit_labor::HAUL_FOOD); + + if (score > best_score) + { + bestdwarf = k; + best_score = score; + } + } + + if (best_score > INT_MIN) + { if (print_debug) - out.print("reducing labor %s to %d (%d needed, %d busy, %d idle)\n", ENUM_KEY_STR(unit_labor, l).c_str(), - clawback, - labor_needed[l], labor_infos[l].busy_dwarfs, labor_infos[l].idle_dwarfs); - labor_needed[l] = clawback; + out.print("LABORMANAGER: assign \"%s\" labor %s score=%d (priority food)\n", (*bestdwarf)->dwarf->name.first_name.c_str(), ENUM_KEY_STR(unit_labor, df::unit_labor::HAUL_FOOD).c_str(), best_score); + + FOR_ENUM_ITEMS(unit_labor, l) + { + if (l == df::unit_labor::NONE) + continue; + + if (l == df::unit_labor::HAUL_FOOD) + (*bestdwarf)->set_labor(l); + else + (*bestdwarf)->clear_labor(l); + } + + available_dwarfs.erase(bestdwarf); + priority_food--; } + else + break; + } if (print_debug) { for (auto i = labor_needed.begin(); i != labor_needed.end(); i++) { - out.print ("labor_needed [%s] = %d, outside = %d, idle = %d\n", ENUM_KEY_STR(unit_labor, i->first).c_str(), i->second, - labor_outside[i->first], labor_infos[i->first].idle_dwarfs); + out.print ("labor_needed [%s] = %d, busy = %d, outside = %d, idle = %d\n", ENUM_KEY_STR(unit_labor, i->first).c_str(), i->second, + labor_infos[i->first].busy_dwarfs, labor_outside[i->first], labor_infos[i->first].idle_dwarfs); } } @@ -2173,6 +2352,8 @@ public: { int priority = labor_infos[l].priority(); priority += labor_infos[l].time_since_last_assigned()/12; + for (int n = 0; n < labor_infos[l].busy_dwarfs; n++) + priority /= 2; pq.push(make_pair(priority, l)); } } @@ -2212,7 +2393,7 @@ public: (1 << df::unit_labor::HAUL_REFUSE) | (1 << df::unit_labor::HAUL_ITEM) | (1 << df::unit_labor::HAUL_FURNITURE) | - (1 << df::unit_labor::HAUL_ANIMAL); + (1 << df::unit_labor::HAUL_ANIMALS); while (!available_dwarfs.empty()) { @@ -2227,36 +2408,11 @@ public: continue; df::unit_labor labor = j->first; - df::job_skill skill = labor_to_skill[labor]; for (std::list::iterator k = available_dwarfs.begin(); k != available_dwarfs.end(); k++) { dwarf_info_t* d = (*k); - int skill_level = 0; - int xp = 0; - if (skill != df::job_skill::NONE) - { - skill_level = Units::getEffectiveSkill(d->dwarf, skill); - xp = Units::getExperience(d->dwarf, skill, false); - } - int score = skill_level * 1000 - (d->high_skill - skill_level) * 2000 + (xp / (skill_level + 5) * 10); - if (d->dwarf->status.labors[labor]) - if (labor == df::unit_labor::OPERATE_PUMP) - score += 50000; - else - score += 1000; - if (default_labor_infos[labor].tool != TOOL_NONE && - d->has_tool[default_labor_infos[labor].tool]) - score += 5000; - if (d->has_children && labor_outside[labor]) - score -= 15000; - if (d->armed && labor_outside[labor]) - score += 5000; - if (d->state == BUSY) - if (d->using_labor == labor) - score += 7500; - else - score -= 7500; + int score = score_labor(d, labor); if (score > best_score) { bestdwarf = k; @@ -2289,11 +2445,15 @@ public: (*bestdwarf)->clear_labor(l); } - if (best_labor >= df::unit_labor::HAUL_STONE && best_labor <= df::unit_labor::HAUL_ANIMAL) + if (best_labor == df::unit_labor::HAUL_FOOD && priority_food > 0) + priority_food--; + + if (best_labor >= df::unit_labor::HAUL_STONE && best_labor <= df::unit_labor::HAUL_ANIMALS) canary &= ~(1 << best_labor); if (best_labor != df::unit_labor::NONE) { + busy_dwarfs.push_back(*bestdwarf); labor_infos[best_labor].active_dwarfs++; to_assign[best_labor]--; } @@ -2301,17 +2461,71 @@ public: available_dwarfs.erase(bestdwarf); } - if (canary != 0) + for (auto d = busy_dwarfs.begin(); d != busy_dwarfs.end(); d++) { - dwarf_info_t* d = dwarf_info.front(); + int current_score = score_labor (*d, (*d)->using_labor); + FOR_ENUM_ITEMS (unit_labor, l) { - if (l >= df::unit_labor::HAUL_STONE && l <= df::unit_labor::HAUL_ANIMAL && - canary & (1 << l)) - d->set_labor(l); + if (l == df::unit_labor::NONE) + continue; + if (l == (*d)->using_labor) + continue; + if (labor_needed[l] <= 0) + continue; + + int score = score_labor (*d, l); + score += labor_infos[l].time_since_last_assigned()/12; + if (l == df::unit_labor::HAUL_FOOD && priority_food > 0) + score += 1000000; + + if (score > current_score) + { + tools_enum t = default_labor_infos[l].tool; + if (t == TOOL_NONE || (*d)->has_tool[t]) + { + (*d)->set_labor (l); + if (print_debug) + out.print("assign \"%s\" extra labor %s score=%d current %s score=%d\n", + (*d)->dwarf->name.first_name.c_str(), + ENUM_KEY_STR(unit_labor, l).c_str(), score, + ENUM_KEY_STR(unit_labor, (*d)->using_labor).c_str(), current_score); + } + } + else + (*d)->clear_labor (l); + } + } + + + if (canary != 0) + { + dwarf_info_t* d = 0; + + for (auto di = busy_dwarfs.begin(); di != busy_dwarfs.end(); di++) + if (!(*di)->clear_all) + { + d = *di; + break; + } + + if (d) + { + + FOR_ENUM_ITEMS (unit_labor, l) + { + if (l >= df::unit_labor::HAUL_STONE && l <= df::unit_labor::HAUL_ANIMALS && + canary & (1 << l)) + d->set_labor(l); + } + if (print_debug) + out.print ("Setting %s as the hauling canary\n", d->dwarf->name.first_name.c_str()); + } + else + { + if (print_debug) + out.print ("No dwarf available to set as the hauling canary!\n"); } - if (print_debug) - out.print ("Setting %s as the hauling canary\n", d->dwarf->name.first_name.c_str()); } /* set reequip on any dwarfs who are carrying tools needed by others */ @@ -2320,6 +2534,9 @@ public: { FOR_ENUM_ITEMS (unit_labor, l) { + if (l == df::unit_labor::NONE) + continue; + tools_enum t = default_labor_infos[l].tool; if (t != TOOL_NONE && tool_count[t] < 0 && (*d)->has_tool[t] && !(*d)->dwarf->status.labors[l]) { @@ -2329,6 +2546,8 @@ public: } } + release_dwarf_list(); + *df::global::process_jobs = true; print_debug = 0; @@ -2359,7 +2578,7 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out ) { static int step_count = 0; // check run conditions - if(!world || !world->map.block_index || !enable_autolabor) + if(!initialized || !world || !world->map.block_index || !enable_labormanager) { // give up if we shouldn't be running' return CR_OK; @@ -2406,26 +2625,26 @@ df::unit_labor lookup_labor_by_name (std::string& name) DFhackCExport command_result plugin_enable ( color_ostream &out, bool enable ) { if (!Core::getInstance().isWorldLoaded()) { - out.printerr("World is not loaded: please load a game first.\n"); + out.printerr("World is not loaded: please load a fort first.\n"); return CR_FAILURE; } - if (enable && !enable_autolabor) + if (enable && !enable_labormanager) { enable_plugin(out); } - else if(!enable && enable_autolabor) + else if(!enable && enable_labormanager) { - enable_autolabor = false; + enable_labormanager = false; setOptionEnabled(CF_ENABLED, false); - out << "Autolabor is disabled." << endl; + out << "LaborManager is disabled." << endl; } return CR_OK; } -command_result autolabor (color_ostream &out, std::vector & parameters) +command_result labormanager (color_ostream &out, std::vector & parameters) { CoreSuspender suspend; @@ -2443,7 +2662,7 @@ command_result autolabor (color_ostream &out, std::vector & parame else if (parameters.size() == 3 && (parameters[0] == "max" || parameters[0] == "priority")) { - if (!enable_autolabor) + if (!enable_labormanager) { out << "Error: The plugin is not enabled." << endl; return CR_FAILURE; @@ -2474,7 +2693,7 @@ command_result autolabor (color_ostream &out, std::vector & parame } else if (parameters.size() == 2 && parameters[0] == "reset") { - if (!enable_autolabor) + if (!enable_labormanager) { out << "Error: The plugin is not enabled." << endl; return CR_FAILURE; @@ -2493,7 +2712,7 @@ command_result autolabor (color_ostream &out, std::vector & parame } else if (parameters.size() == 1 && (parameters[0] == "allow-fishing" || parameters[0] == "forbid-fishing")) { - if (!enable_autolabor) + if (!enable_labormanager) { out << "Error: The plugin is not enabled." << endl; return CR_FAILURE; @@ -2504,7 +2723,7 @@ command_result autolabor (color_ostream &out, std::vector & parame } else if (parameters.size() == 1 && (parameters[0] == "allow-hunting" || parameters[0] == "forbid-hunting")) { - if (!enable_autolabor) + if (!enable_labormanager) { out << "Error: The plugin is not enabled." << endl; return CR_FAILURE; @@ -2515,7 +2734,7 @@ command_result autolabor (color_ostream &out, std::vector & parame } else if (parameters.size() == 1 && parameters[0] == "reset-all") { - if (!enable_autolabor) + if (!enable_labormanager) { out << "Error: The plugin is not enabled." << endl; return CR_FAILURE; @@ -2530,7 +2749,7 @@ command_result autolabor (color_ostream &out, std::vector & parame } else if (parameters.size() == 1 && parameters[0] == "list" || parameters[0] == "status") { - if (!enable_autolabor) + if (!enable_labormanager) { out << "Error: The plugin is not enabled." << endl; return CR_FAILURE; @@ -2563,7 +2782,7 @@ command_result autolabor (color_ostream &out, std::vector & parame } else if (parameters.size() == 1 && parameters[0] == "debug") { - if (!enable_autolabor) + if (!enable_labormanager) { out << "Error: The plugin is not enabled." << endl; return CR_FAILURE; @@ -2576,8 +2795,8 @@ command_result autolabor (color_ostream &out, std::vector & parame else { out.print("Automatically assigns labors to dwarves.\n" - "Activate with 'autolabor enable', deactivate with 'autolabor disable'.\n" - "Current state: %s.\n", enable_autolabor ? "enabled" : "disabled"); + "Activate with 'labormanager enable', deactivate with 'labormanager disable'.\n" + "Current state: %s.\n", enable_labormanager ? "enabled" : "disabled"); return CR_OK; } From 61bcfd4bf3b74a833ec43e4b03b748eaedbd178e Mon Sep 17 00:00:00 2001 From: Kelly Kinkade Date: Mon, 27 Jun 2016 20:58:38 -0500 Subject: [PATCH 02/22] labormanager improvements Add some debugging facilities. Change some hauling, construction, and deconstruction labors to reflect changes in DF since 34.11. --- plugins/devel/labormanager.cpp | 40 +++++++++++++++++++++++++++++----- 1 file changed, 35 insertions(+), 5 deletions(-) diff --git a/plugins/devel/labormanager.cpp b/plugins/devel/labormanager.cpp index 12e4e8ca1..ffabde36a 100644 --- a/plugins/devel/labormanager.cpp +++ b/plugins/devel/labormanager.cpp @@ -80,6 +80,7 @@ using df::global::world; DFHACK_PLUGIN_IS_ENABLED(enable_labormanager); static bool print_debug = 0; +static bool pause_on_error = 1; static std::vector state_count(5); @@ -525,8 +526,6 @@ struct dwarf_info_t ~dwarf_info_t() { - if (print_debug) - debug("LABORMANAGER: destroying dwarf %p\n", (void*) this); } @@ -608,7 +607,7 @@ static df::unit_labor hauling_labor_map[] = df::unit_labor::HAUL_FOOD, /* FISH_RAW */ df::unit_labor::HAUL_REFUSE, /* VERMIN */ df::unit_labor::HAUL_ITEM, /* PET */ - df::unit_labor::HAUL_FOOD, /* SEEDS */ + df::unit_labor::HAUL_ITEM, /* SEEDS */ df::unit_labor::HAUL_FOOD, /* PLANT */ df::unit_labor::HAUL_ITEM, /* SKIN_TANNED */ df::unit_labor::HAUL_FOOD, /* LEAVES */ @@ -716,6 +715,14 @@ void debug (char* fmt, ...) } } +void debug_pause () +{ + if (pause_on_error) + { + debug("LABORMANAGER: Game paused so you can investigate the above message.\nUse 'labormanager pause-on-error no' to disable autopausing.\n"); + *df::global::pause_state = true; + } +} class JobLaborMapper { @@ -804,9 +811,10 @@ private: return workshop_build_labor[ws->type]; } break; + case df::building_type::Construction: + return df::unit_labor::BUILD_CONSTRUCTION; case df::building_type::Furnace: case df::building_type::TradeDepot: - case df::building_type::Construction: case df::building_type::Bridge: case df::building_type::ArcheryTarget: case df::building_type::WaterWheel: @@ -870,6 +878,7 @@ private: debug ("LABORMANAGER: Cannot deduce labor for construct building job of type %s\n", ENUM_KEY_STR(building_type, bld->getType()).c_str()); + debug_pause(); return df::unit_labor::NONE; } @@ -900,9 +909,10 @@ private: return workshop_build_labor[ws->type]; } break; + case df::building_type::Construction: + return df::unit_labor::REMOVE_CONSTRUCTION; case df::building_type::Furnace: case df::building_type::TradeDepot: - case df::building_type::Construction: case df::building_type::Wagon: case df::building_type::Bridge: case df::building_type::ScrewPump: @@ -964,6 +974,7 @@ private: debug ("LABORMANAGER: Cannot deduce labor for destroy building job of type %s\n", ENUM_KEY_STR(building_type, bld->getType()).c_str()); + debug_pause(); return df::unit_labor::NONE; } @@ -998,6 +1009,7 @@ private: else { debug ("LABORMANAGER: Cannot deduce labor for make crafts job (not bone)\n"); + debug_pause(); return df::unit_labor::NONE; } case df::item_type::WOOD: @@ -1005,6 +1017,7 @@ private: default: debug ("LABORMANAGER: Cannot deduce labor for make crafts job, item type %s\n", ENUM_KEY_STR(item_type, jobitem).c_str()); + debug_pause(); return df::unit_labor::NONE; } } @@ -1022,6 +1035,7 @@ private: default: debug ("LABORMANAGER: Cannot deduce labor for make job, workshop type %s\n", ENUM_KEY_STR(workshop_type, type).c_str()); + debug_pause(); return df::unit_labor::NONE; } } @@ -1036,12 +1050,14 @@ private: default: debug ("LABORMANAGER: Cannot deduce labor for make job, furnace type %s\n", ENUM_KEY_STR(furnace_type, type).c_str()); + debug_pause(); return df::unit_labor::NONE; } } debug ("LABORMANAGER: Cannot deduce labor for make job, building type %s\n", ENUM_KEY_STR(building_type, bld->getType()).c_str()); + debug_pause(); return df::unit_labor::NONE; } @@ -1382,6 +1398,7 @@ public: if (job_to_labor_table.count(j->job_type) == 0) { debug("LABORMANAGER: job has no job to labor table entry: %s\n", ENUM_KEY_STR(job_type, j->job_type).c_str()); + debug_pause(); labor = df::unit_labor::NONE; } else { @@ -1941,6 +1958,7 @@ private: else { out.print("Dwarf \"%s\" has unknown job %i\n", dwarf->dwarf->name.first_name.c_str(), job); + debug_pause(); state = OTHER; } if (state == BUSY) @@ -2780,6 +2798,18 @@ command_result labormanager (color_ostream &out, std::vector & par return CR_OK; } + else if (parameters.size() == 2 && parameters[0] == "pause-on-error") + { + if (!enable_labormanager) + { + out << "Error: The plugin is not enabled." << endl; + return CR_FAILURE; + } + + pause_on_error = parameters[1] == "yes" || parameters[1] == "true"; + + return CR_OK; + } else if (parameters.size() == 1 && parameters[0] == "debug") { if (!enable_labormanager) From b516c8e0dcb18d3f1e8cf2e957351803e14bd509 Mon Sep 17 00:00:00 2001 From: Kelly Kinkade Date: Tue, 28 Jun 2016 22:36:45 -0500 Subject: [PATCH 03/22] labormanager: more tweaks to bring up to date This update fixes some labors and attempts to address changes in the way DF maintains the job list. --- plugins/devel/labormanager.cpp | 155 +++++++++++++++------------------ 1 file changed, 72 insertions(+), 83 deletions(-) diff --git a/plugins/devel/labormanager.cpp b/plugins/devel/labormanager.cpp index ffabde36a..256841cb7 100644 --- a/plugins/devel/labormanager.cpp +++ b/plugins/devel/labormanager.cpp @@ -1154,7 +1154,7 @@ public: job_to_labor_table[df::job_type::GatherPlants] = jlf_const(df::unit_labor::HERBALIST); job_to_labor_table[df::job_type::RemoveConstruction] = jlf_no_labor; job_to_labor_table[df::job_type::CollectWebs] = jlf_const(df::unit_labor::WEAVER); - job_to_labor_table[df::job_type::BringItemToDepot] = jlf_no_labor; + job_to_labor_table[df::job_type::BringItemToDepot] = jlf_const(df::unit_labor::HAUL_TRADE); job_to_labor_table[df::job_type::BringItemToShop] = jlf_no_labor; job_to_labor_table[df::job_type::Eat] = jlf_no_labor; job_to_labor_table[df::job_type::GetProvisions] = jlf_no_labor; @@ -1178,13 +1178,13 @@ public: job_to_labor_table[df::job_type::CheckChest] = jlf_no_labor; job_to_labor_table[df::job_type::StoreOwnedItem] = jlf_no_labor; job_to_labor_table[df::job_type::PlaceItemInTomb] = jlf_const(df::unit_labor::HAUL_BODY); - job_to_labor_table[df::job_type::StoreItemInStockpile] = jlf_hauling; - job_to_labor_table[df::job_type::StoreItemInBag] = jlf_hauling; + job_to_labor_table[df::job_type::StoreItemInStockpile] = jlf_no_labor; // Can arise from many different labors, but will never appear in a pending job list + job_to_labor_table[df::job_type::StoreItemInBag] = jlf_no_labor; // Can arise from many different labors, but will never appear in a pending job list job_to_labor_table[df::job_type::StoreItemInHospital] = jlf_hauling; job_to_labor_table[df::job_type::StoreWeapon] = jlf_hauling; job_to_labor_table[df::job_type::StoreArmor] = jlf_hauling; - job_to_labor_table[df::job_type::StoreItemInBarrel] = jlf_hauling; - job_to_labor_table[df::job_type::StoreItemInBin] = jlf_const(df::unit_labor::HAUL_ITEM); + job_to_labor_table[df::job_type::StoreItemInBarrel] = jlf_no_labor; // Can arise from many different labors, but will never appear in a pending job list + job_to_labor_table[df::job_type::StoreItemInBin] = jlf_no_labor; // Can arise from many different labors, but will never appear in a pending job list job_to_labor_table[df::job_type::SeekArtifact] = jlf_no_labor; job_to_labor_table[df::job_type::SeekInfant] = jlf_no_labor; job_to_labor_table[df::job_type::AttendParty] = jlf_no_labor; @@ -1293,14 +1293,14 @@ public: job_to_labor_table[df::job_type::TameVermin] = jlf_const(df::unit_labor::ANIMALTRAIN) ; job_to_labor_table[df::job_type::TameAnimal] = jlf_const(df::unit_labor::ANIMALTRAIN) ; job_to_labor_table[df::job_type::ChainAnimal] = jlf_no_labor; - job_to_labor_table[df::job_type::UnchainAnimal] = jlf_no_labor; + job_to_labor_table[df::job_type::UnchainAnimal] = jlf_const(df::unit_labor::HAUL_ANIMALS); job_to_labor_table[df::job_type::UnchainPet] = jlf_no_labor; - job_to_labor_table[df::job_type::ReleaseLargeCreature] = jlf_no_labor; + job_to_labor_table[df::job_type::ReleaseLargeCreature] = jlf_const(df::unit_labor::HAUL_ANIMALS); job_to_labor_table[df::job_type::ReleasePet] = jlf_no_labor; job_to_labor_table[df::job_type::ReleaseSmallCreature] = jlf_no_labor; job_to_labor_table[df::job_type::HandleSmallCreature] = jlf_no_labor; - job_to_labor_table[df::job_type::HandleLargeCreature] = jlf_no_labor; - job_to_labor_table[df::job_type::CageLargeCreature] = jlf_no_labor; + job_to_labor_table[df::job_type::HandleLargeCreature] = jlf_const(df::unit_labor::HAUL_ANIMALS); + job_to_labor_table[df::job_type::CageLargeCreature] = jlf_const(df::unit_labor::HAUL_ANIMALS); job_to_labor_table[df::job_type::CageSmallCreature] = jlf_no_labor; job_to_labor_table[df::job_type::RecoverWounded] = jlf_const(df::unit_labor::RECOVER_WOUNDED); job_to_labor_table[df::job_type::DiagnosePatient] = jlf_const(df::unit_labor::DIAGNOSE) ; @@ -1640,6 +1640,7 @@ private: int priority_food; std::map labor_needed; + std::map labor_in_use; std::map labor_outside; std::vector dwarf_info; std::list available_dwarfs; @@ -1668,6 +1669,7 @@ private: { df::building_tradedepotst* depot = (df::building_tradedepotst*) build; trader_requested = depot->trade_flags.bits.trader_requested; + if (print_debug) { if (trader_requested) @@ -1819,20 +1821,23 @@ private: } df::unit_labor labor = labor_mapper->find_job_labor (j); + labor_needed[labor]++; if (labor != df::unit_labor::NONE) { - labor_needed[labor]++; - - if (worker != -1) + if (worker == -1) + { + if (j->pos.isValid()) + { + df::tile_designation* d = Maps::getTileDesignation(j->pos); + if (d->bits.outside) + labor_outside[labor] = true; + } + } else { labor_infos[labor].mark_assigned(); + labor_in_use[labor]++; + } - if (j->pos.isValid()) - { - df::tile_designation* d = Maps::getTileDesignation(j->pos); - if (d->bits.outside) - labor_outside[labor] = true; - } } } @@ -1979,8 +1984,6 @@ private: } } } - if (state == OTHER) - dwarf->clear_all = true; } dwarf->state = state; @@ -2199,7 +2202,7 @@ public: // add job entries for health care labor_needed[df::unit_labor::RECOVER_WOUNDED] += cnt_recover_wounded; - labor_needed[df::unit_labor::DIAGNOSE] += cnt_diagnosis; + labor_needed[df::unit_labor::DIAGNOSE] += cnt_diagnosis; labor_needed[df::unit_labor::BONE_SETTING] += cnt_immobilize; labor_needed[df::unit_labor::DRESSING_WOUNDS] += cnt_dressing; labor_needed[df::unit_labor::DRESSING_WOUNDS] += cnt_cleaning; @@ -2260,43 +2263,14 @@ public: // note: this doesn't test to see if the trainer is actually needed, and thus will overallocate trainers. bleah. } - /* move idle dwarfs ready to be assigned to busy list */ - for (auto d = available_dwarfs.begin(); d != available_dwarfs.end(); ) - { - bool busy = false; + /* set requirements to zero for labors with currently idle dwarfs, and remove from requirement dwarfs actually working */ - FOR_ENUM_ITEMS(unit_labor, l) - { - if (l == df::unit_labor::NONE) - continue; - - - if (labor_needed[l] > 0 && (*d)->dwarf->status.labors[l]) - { - busy = true; - labor_needed[l] = max(labor_needed[l]-1, 0); - } - } - - if (busy) - { - busy_dwarfs.push_back(*d); - d = available_dwarfs.erase(d); - } else { - d++; - } - } - - /* adjust for over/under */ - FOR_ENUM_ITEMS(unit_labor, l) - { - if (l == df::unit_labor::NONE) - continue; - if (labor_infos[l].idle_dwarfs > 0) - labor_needed[l] = 0; - else - labor_needed[l] = std::max(labor_needed[l] - labor_infos[l].busy_dwarfs, 0); - } + FOR_ENUM_ITEMS(unit_labor, l) { + if (labor_infos[l].idle_dwarfs > 0) + labor_needed[l] = 0; + else + labor_needed[l] = max(0, labor_needed[l] - labor_in_use[l]); + } /* assign food haulers for rotting food items */ @@ -2401,7 +2375,6 @@ public: priority /= 2; pq.push(make_pair(priority, labor)); } - } int canary = (1 << df::unit_labor::HAUL_STONE) | @@ -2476,6 +2449,7 @@ public: to_assign[best_labor]--; } + busy_dwarfs.push_back(*bestdwarf); available_dwarfs.erase(bestdwarf); } @@ -2510,42 +2484,57 @@ public: ENUM_KEY_STR(unit_labor, (*d)->using_labor).c_str(), current_score); } } - else - (*d)->clear_labor (l); } } - if (canary != 0) - { - dwarf_info_t* d = 0; - - for (auto di = busy_dwarfs.begin(); di != busy_dwarfs.end(); di++) - if (!(*di)->clear_all) - { - d = *di; - break; - } + dwarf_info_t* canary_dwarf = 0; - if (d) + for (auto di = busy_dwarfs.begin(); di != busy_dwarfs.end(); di++) + if (!(*di)->clear_all) { - - FOR_ENUM_ITEMS (unit_labor, l) - { - if (l >= df::unit_labor::HAUL_STONE && l <= df::unit_labor::HAUL_ANIMALS && - canary & (1 << l)) - d->set_labor(l); - } - if (print_debug) - out.print ("Setting %s as the hauling canary\n", d->dwarf->name.first_name.c_str()); + canary_dwarf = *di; + break; } - else + + if (canary_dwarf) + { + + FOR_ENUM_ITEMS (unit_labor, l) { - if (print_debug) - out.print ("No dwarf available to set as the hauling canary!\n"); + if (l >= df::unit_labor::HAUL_STONE && l <= df::unit_labor::HAUL_ANIMALS && + canary & (1 << l)) + canary_dwarf->set_labor(l); } + + /* Also set the canary to remove constructions, because we have no way yet to tell if there are constructions needing removal */ + + canary_dwarf->set_labor(df::unit_labor::REMOVE_CONSTRUCTION); + + if (print_debug) + out.print ("Setting %s as the hauling canary\n", canary_dwarf->dwarf->name.first_name.c_str()); + } + else + { + if (print_debug) + out.print ("No dwarf available to set as the hauling canary!\n"); } + /* Assign any leftover dwarfs to "standard" labors */ + + for (auto d = available_dwarfs.begin(); d != available_dwarfs.end(); d++) + { + FOR_ENUM_ITEMS (unit_labor, l) + { + if (l >= df::unit_labor::HAUL_STONE && l <= df::unit_labor::HAUL_ANIMALS && + canary & (1 << l)) + (*d)->set_labor(l); + } + + (*d)->set_labor(df::unit_labor::CLEAN); + (*d)->set_labor(df::unit_labor::REMOVE_CONSTRUCTION); + } + /* set reequip on any dwarfs who are carrying tools needed by others */ for (auto d = dwarf_info.begin(); d != dwarf_info.end(); d++) From 1927eda6f33aa813da94eaeceb1bfc1db4111d3d Mon Sep 17 00:00:00 2001 From: Kelly Kinkade Date: Tue, 28 Jun 2016 22:57:12 -0500 Subject: [PATCH 04/22] labormanager: whitespace MSVC is evil. --- plugins/devel/labormanager.cpp | 60 +++++++++++++++++----------------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/plugins/devel/labormanager.cpp b/plugins/devel/labormanager.cpp index 256841cb7..8acae7524 100644 --- a/plugins/devel/labormanager.cpp +++ b/plugins/devel/labormanager.cpp @@ -1299,7 +1299,7 @@ public: job_to_labor_table[df::job_type::ReleasePet] = jlf_no_labor; job_to_labor_table[df::job_type::ReleaseSmallCreature] = jlf_no_labor; job_to_labor_table[df::job_type::HandleSmallCreature] = jlf_no_labor; - job_to_labor_table[df::job_type::HandleLargeCreature] = jlf_const(df::unit_labor::HAUL_ANIMALS); + job_to_labor_table[df::job_type::HandleLargeCreature] = jlf_const(df::unit_labor::HAUL_ANIMALS); job_to_labor_table[df::job_type::CageLargeCreature] = jlf_const(df::unit_labor::HAUL_ANIMALS); job_to_labor_table[df::job_type::CageSmallCreature] = jlf_no_labor; job_to_labor_table[df::job_type::RecoverWounded] = jlf_const(df::unit_labor::RECOVER_WOUNDED); @@ -1640,7 +1640,7 @@ private: int priority_food; std::map labor_needed; - std::map labor_in_use; + std::map labor_in_use; std::map labor_outside; std::vector dwarf_info; std::list available_dwarfs; @@ -1821,22 +1821,22 @@ private: } df::unit_labor labor = labor_mapper->find_job_labor (j); - labor_needed[labor]++; + labor_needed[labor]++; if (labor != df::unit_labor::NONE) { - if (worker == -1) - { - if (j->pos.isValid()) - { - df::tile_designation* d = Maps::getTileDesignation(j->pos); - if (d->bits.outside) - labor_outside[labor] = true; - } - } else { + if (worker == -1) + { + if (j->pos.isValid()) + { + df::tile_designation* d = Maps::getTileDesignation(j->pos); + if (d->bits.outside) + labor_outside[labor] = true; + } + } else { labor_infos[labor].mark_assigned(); - labor_in_use[labor]++; - } + labor_in_use[labor]++; + } } } @@ -2263,14 +2263,14 @@ public: // note: this doesn't test to see if the trainer is actually needed, and thus will overallocate trainers. bleah. } - /* set requirements to zero for labors with currently idle dwarfs, and remove from requirement dwarfs actually working */ + /* set requirements to zero for labors with currently idle dwarfs, and remove from requirement dwarfs actually working */ - FOR_ENUM_ITEMS(unit_labor, l) { - if (labor_infos[l].idle_dwarfs > 0) - labor_needed[l] = 0; - else - labor_needed[l] = max(0, labor_needed[l] - labor_in_use[l]); - } + FOR_ENUM_ITEMS(unit_labor, l) { + if (labor_infos[l].idle_dwarfs > 0) + labor_needed[l] = 0; + else + labor_needed[l] = max(0, labor_needed[l] - labor_in_use[l]); + } /* assign food haulers for rotting food items */ @@ -2449,7 +2449,7 @@ public: to_assign[best_labor]--; } - busy_dwarfs.push_back(*bestdwarf); + busy_dwarfs.push_back(*bestdwarf); available_dwarfs.erase(bestdwarf); } @@ -2507,9 +2507,9 @@ public: canary_dwarf->set_labor(l); } - /* Also set the canary to remove constructions, because we have no way yet to tell if there are constructions needing removal */ + /* Also set the canary to remove constructions, because we have no way yet to tell if there are constructions needing removal */ - canary_dwarf->set_labor(df::unit_labor::REMOVE_CONSTRUCTION); + canary_dwarf->set_labor(df::unit_labor::REMOVE_CONSTRUCTION); if (print_debug) out.print ("Setting %s as the hauling canary\n", canary_dwarf->dwarf->name.first_name.c_str()); @@ -2520,10 +2520,10 @@ public: out.print ("No dwarf available to set as the hauling canary!\n"); } - /* Assign any leftover dwarfs to "standard" labors */ + /* Assign any leftover dwarfs to "standard" labors */ - for (auto d = available_dwarfs.begin(); d != available_dwarfs.end(); d++) - { + for (auto d = available_dwarfs.begin(); d != available_dwarfs.end(); d++) + { FOR_ENUM_ITEMS (unit_labor, l) { if (l >= df::unit_labor::HAUL_STONE && l <= df::unit_labor::HAUL_ANIMALS && @@ -2531,9 +2531,9 @@ public: (*d)->set_labor(l); } - (*d)->set_labor(df::unit_labor::CLEAN); - (*d)->set_labor(df::unit_labor::REMOVE_CONSTRUCTION); - } + (*d)->set_labor(df::unit_labor::CLEAN); + (*d)->set_labor(df::unit_labor::REMOVE_CONSTRUCTION); + } /* set reequip on any dwarfs who are carrying tools needed by others */ From b45fb7c5640b1ddc6ef8360ec965876a3f5f6f2e Mon Sep 17 00:00:00 2001 From: Kelly Kinkade Date: Tue, 28 Jun 2016 23:28:44 -0500 Subject: [PATCH 05/22] labormanager: fix stupid --- plugins/devel/labormanager.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/plugins/devel/labormanager.cpp b/plugins/devel/labormanager.cpp index 8acae7524..d78d06b97 100644 --- a/plugins/devel/labormanager.cpp +++ b/plugins/devel/labormanager.cpp @@ -2337,6 +2337,9 @@ public: for (auto i = labor_needed.begin(); i != labor_needed.end(); i++) { df::unit_labor l = i->first; + if (l == df::unit_labor::NONE) + continue; + if (labor_infos[l].maximum_dwarfs() > 0 && i->second > labor_infos[l].maximum_dwarfs()) i->second = labor_infos[l].maximum_dwarfs(); From 857058752ba56321024e08244dd004347ad55cf5 Mon Sep 17 00:00:00 2001 From: Kelly Kinkade Date: Wed, 29 Jun 2016 14:54:03 -0500 Subject: [PATCH 06/22] labormanager: rework for better behavior with 43.03 The main thing here is that the process loop exits if the DF process_job or process_dig flags are set since if these are set the job list is going to change soon anyway. The plugin also sets these flags when it changes any labors, which has the side effect of effectively disabling the process loop while DF is paused, which prevents flapping while editing job preferences in-game, and also allows changing job preferences in game (although such changes may not last when the clock starts up again). --- plugins/devel/labormanager.cpp | 78 +++++++++++++++++++--------------- 1 file changed, 43 insertions(+), 35 deletions(-) diff --git a/plugins/devel/labormanager.cpp b/plugins/devel/labormanager.cpp index d78d06b97..8d614ed36 100644 --- a/plugins/devel/labormanager.cpp +++ b/plugins/devel/labormanager.cpp @@ -528,23 +528,6 @@ struct dwarf_info_t { } - - void set_labor(df::unit_labor labor) - { - if (labor >= 0 && labor <= ENUM_LAST_ITEM(unit_labor)) - { - dwarf->status.labors[labor] = true; - } - } - - void clear_labor(df::unit_labor labor) - { - if (labor >= 0 && labor <= ENUM_LAST_ITEM(unit_labor)) - { - dwarf->status.labors[labor] = false; - } - } - }; /* @@ -1621,6 +1604,8 @@ private: int plant_count; int detail_count; + bool labors_changed; + int tool_count[TOOLS_MAX]; bool reequip_needed[TOOLS_MAX]; @@ -1647,11 +1632,23 @@ private: std::list busy_dwarfs; private: + void set_labor (dwarf_info_t* dwarf, df::unit_labor labor, bool value) + { + if (labor >= 0 && labor <= ENUM_LAST_ITEM(unit_labor)) + { + bool old = dwarf->dwarf->status.labors[labor]; + dwarf->dwarf->status.labors[labor] = value; + if (old != value) + labors_changed = true; + } + } + void scan_buildings() { has_butchers = false; has_fishery = false; trader_requested = false; + labors_changed = false; for (auto b = world->buildings.all.begin(); b != world->buildings.all.end(); b++) { @@ -1821,10 +1818,10 @@ private: } df::unit_labor labor = labor_mapper->find_job_labor (j); - labor_needed[labor]++; if (labor != df::unit_labor::NONE) { + labor_needed[labor]++; if (worker == -1) { if (j->pos.isValid()) @@ -2088,7 +2085,7 @@ private: if (labor == unit_labor::NONE) continue; - dwarf->clear_labor(labor); + set_labor(dwarf, labor, false); } } else { if (state == IDLE) @@ -2152,6 +2149,9 @@ private: public: void process() { + if (*df::global::process_dig || *df::global::process_jobs) + return; + release_dwarf_list(); dig_count = tree_count = plant_count = detail_count = 0; @@ -2266,6 +2266,9 @@ public: /* set requirements to zero for labors with currently idle dwarfs, and remove from requirement dwarfs actually working */ FOR_ENUM_ITEMS(unit_labor, l) { + if (l == df::unit_labor::NONE) + continue; + if (labor_infos[l].idle_dwarfs > 0) labor_needed[l] = 0; else @@ -2309,10 +2312,7 @@ public: if (l == df::unit_labor::NONE) continue; - if (l == df::unit_labor::HAUL_FOOD) - (*bestdwarf)->set_labor(l); - else - (*bestdwarf)->clear_labor(l); + set_labor (*bestdwarf, l, l == df::unit_labor::HAUL_FOOD); } available_dwarfs.erase(bestdwarf); @@ -2337,7 +2337,7 @@ public: for (auto i = labor_needed.begin(); i != labor_needed.end(); i++) { df::unit_labor l = i->first; - if (l == df::unit_labor::NONE) + if (l == df::unit_labor::NONE) continue; if (labor_infos[l].maximum_dwarfs() > 0 && @@ -2394,7 +2394,7 @@ public: std::list::iterator bestdwarf = available_dwarfs.begin(); int best_score = INT_MIN; - df::unit_labor best_labor = df::unit_labor::CLEAN; + df::unit_labor best_labor = df::unit_labor::NONE; for (auto j = to_assign.begin(); j != to_assign.end(); j++) { @@ -2416,6 +2416,9 @@ public: } } + if (best_labor == df::unit_labor::NONE) + break; + if (print_debug) out.print("assign \"%s\" labor %s score=%d\n", (*bestdwarf)->dwarf->name.first_name.c_str(), ENUM_KEY_STR(unit_labor, best_labor).c_str(), best_score); @@ -2426,7 +2429,7 @@ public: if (l == best_labor) { - (*bestdwarf)->set_labor(l); + set_labor(*bestdwarf, l, true); tools_enum t = default_labor_infos[l].tool; if (t != TOOL_NONE) { @@ -2436,7 +2439,7 @@ public: } } else if ((*bestdwarf)->state == IDLE) - (*bestdwarf)->clear_labor(l); + set_labor(*bestdwarf, l, false); } if (best_labor == df::unit_labor::HAUL_FOOD && priority_food > 0) @@ -2479,7 +2482,7 @@ public: tools_enum t = default_labor_infos[l].tool; if (t == TOOL_NONE || (*d)->has_tool[t]) { - (*d)->set_labor (l); + set_labor(*d, l, true); if (print_debug) out.print("assign \"%s\" extra labor %s score=%d current %s score=%d\n", (*d)->dwarf->name.first_name.c_str(), @@ -2507,12 +2510,12 @@ public: { if (l >= df::unit_labor::HAUL_STONE && l <= df::unit_labor::HAUL_ANIMALS && canary & (1 << l)) - canary_dwarf->set_labor(l); + set_labor(canary_dwarf, l, true); } /* Also set the canary to remove constructions, because we have no way yet to tell if there are constructions needing removal */ - canary_dwarf->set_labor(df::unit_labor::REMOVE_CONSTRUCTION); + set_labor(canary_dwarf, df::unit_labor::REMOVE_CONSTRUCTION, true); if (print_debug) out.print ("Setting %s as the hauling canary\n", canary_dwarf->dwarf->name.first_name.c_str()); @@ -2531,11 +2534,12 @@ public: { if (l >= df::unit_labor::HAUL_STONE && l <= df::unit_labor::HAUL_ANIMALS && canary & (1 << l)) - (*d)->set_labor(l); + set_labor(*d, l, true); + else if (l == df::unit_labor::CLEAN || l == df::unit_labor::REMOVE_CONSTRUCTION) + set_labor(*d, l, true); + else + set_labor(*d, l, false); } - - (*d)->set_labor(df::unit_labor::CLEAN); - (*d)->set_labor(df::unit_labor::REMOVE_CONSTRUCTION); } /* set reequip on any dwarfs who are carrying tools needed by others */ @@ -2558,7 +2562,11 @@ public: release_dwarf_list(); - *df::global::process_jobs = true; + if (labors_changed) + { + *df::global::process_dig = true; + *df::global::process_jobs = true; + } print_debug = 0; From d8f4d79b977c126d18201f3875f724731d40acc0 Mon Sep 17 00:00:00 2001 From: Kelly Kinkade Date: Thu, 30 Jun 2016 23:58:56 -0500 Subject: [PATCH 07/22] labormanager: significant restructuring to use job posting list Updated here to get potential jobs off the job posting lists, which is apparently where certain map-designated live after being designated but before they move to the actual job list. Also changes to how tools are handled, and lever pulling is assigned by default to all idle dwarfs. --- plugins/devel/labormanager.cpp | 273 ++++++++++++++++++++------------- 1 file changed, 169 insertions(+), 104 deletions(-) diff --git a/plugins/devel/labormanager.cpp b/plugins/devel/labormanager.cpp index 8d614ed36..545ca7f1c 100644 --- a/plugins/devel/labormanager.cpp +++ b/plugins/devel/labormanager.cpp @@ -184,7 +184,7 @@ static const dwarf_state dwarf_states[] = { OTHER /* GoShopping2 */, BUSY /* Clean */, OTHER /* Rest */, - BUSY /* PickupEquipment */, + OTHER /* PickupEquipment */, BUSY /* DumpItem */, OTHER /* StrangeMoodCrafter */, OTHER /* StrangeMoodJeweller */, @@ -414,7 +414,7 @@ struct labor_default static std::vector labor_infos; static const struct labor_default default_labor_infos[] = { - /* MINE */ {200, 0, TOOL_PICK}, + /* MINEa */ {200, 0, TOOL_PICK}, /* HAUL_STONE */ {100, 0, TOOL_NONE}, /* HAUL_WOOD */ {100, 0, TOOL_NONE}, /* HAUL_BODY */ {200, 0, TOOL_NONE}, @@ -1607,7 +1607,7 @@ private: bool labors_changed; int tool_count[TOOLS_MAX]; - bool reequip_needed[TOOLS_MAX]; + int tool_in_use[TOOLS_MAX]; int cnt_recover_wounded; int cnt_diagnosis; @@ -1639,7 +1639,69 @@ private: bool old = dwarf->dwarf->status.labors[labor]; dwarf->dwarf->status.labors[labor] = value; if (old != value) + { labors_changed = true; + + tools_enum tool = default_labor_infos[labor].tool; + if (tool != TOOL_NONE) + tool_in_use[tool] += value ? 1 : -1; + } + } + } + + void process_job (df::job* j) + { + if (j->flags.bits.suspend || j->flags.bits.item_lost) + return; + + int worker = -1; + int bld = -1; + + for (int r = 0; r < j->general_refs.size(); ++r) + { + if (j->general_refs[r]->getType() == df::general_ref_type::UNIT_WORKER) + worker = ((df::general_ref_unit_workerst *)(j->general_refs[r]))->unit_id; + if (j->general_refs[r]->getType() == df::general_ref_type::BUILDING_HOLDER) + bld = ((df::general_ref_building_holderst *)(j->general_refs[r]))->building_id; + } + + if (bld != -1) + { + df::building* b = binsearch_in_vector(world->buildings.all, bld); + int fjid = -1; + for (int jn = 0; jn < b->jobs.size(); jn++) + { + if (b->jobs[jn]->flags.bits.suspend) + continue; + fjid = b->jobs[jn]->id; + break; + } + // check if this job is the first nonsuspended job on this building; if not, ignore it + // (except for farms and trade depots) + if (fjid != j->id && + b->getType() != df::building_type::FarmPlot && + b->getType() != df::building_type::TradeDepot) + return; + } + + df::unit_labor labor = labor_mapper->find_job_labor (j); + + if (labor != df::unit_labor::NONE) + { + labor_needed[labor]++; + if (worker == -1) + { + if (j->pos.isValid()) + { + df::tile_designation* d = Maps::getTileDesignation(j->pos); + if (d->bits.outside) + labor_outside[labor] = true; + } + } else { + labor_infos[labor].mark_assigned(); + labor_in_use[labor]++; + } + } } @@ -1674,6 +1736,7 @@ private: else out.print("Trade depot found but trader is not requested.\n"); } + } } } @@ -1729,7 +1792,10 @@ private: void count_tools() { for (int e = TOOL_NONE; e < TOOLS_MAX; e++) + { tool_count[e] = 0; + tool_in_use[e] = 0; + } priority_food = 0; @@ -1776,66 +1842,20 @@ private: void collect_job_list() { - labor_needed.clear(); - for (df::job_list_link* jll = world->job_list.next; jll; jll = jll->next) { df::job* j = jll->item; if (!j) continue; + process_job(j); + } - if (j->flags.bits.suspend || j->flags.bits.item_lost) + for (auto jp = world->job_postings.begin(); jp != world->job_postings.end(); jp++) + { + if ((*jp)->flags.bits.dead) continue; - int worker = -1; - int bld = -1; - - for (int r = 0; r < j->general_refs.size(); ++r) - { - if (j->general_refs[r]->getType() == df::general_ref_type::UNIT_WORKER) - worker = ((df::general_ref_unit_workerst *)(j->general_refs[r]))->unit_id; - if (j->general_refs[r]->getType() == df::general_ref_type::BUILDING_HOLDER) - bld = ((df::general_ref_building_holderst *)(j->general_refs[r]))->building_id; - } - - if (bld != -1) - { - df::building* b = binsearch_in_vector(world->buildings.all, bld); - int fjid = -1; - for (int jn = 0; jn < b->jobs.size(); jn++) - { - if (b->jobs[jn]->flags.bits.suspend) - continue; - fjid = b->jobs[jn]->id; - break; - } - // check if this job is the first nonsuspended job on this building; if not, ignore it - // (except for farms) - if (fjid != j->id && b->getType() != df::building_type::FarmPlot) { - continue; - } - - } - - df::unit_labor labor = labor_mapper->find_job_labor (j); - - if (labor != df::unit_labor::NONE) - { - labor_needed[labor]++; - if (worker == -1) - { - if (j->pos.isValid()) - { - df::tile_designation* d = Maps::getTileDesignation(j->pos); - if (d->bits.outside) - labor_outside[labor] = true; - } - } else { - labor_infos[labor].mark_assigned(); - labor_in_use[labor]++; - } - - } + process_job((*jp)->job); } } @@ -1909,6 +1929,32 @@ private: } } + // check if dwarf has an axe, pick, or crossbow + + for (int j = 0; j < dwarf->dwarf->inventory.size(); j++) + { + df::unit_inventory_item* ui = dwarf->dwarf->inventory[j]; + if (ui->mode == df::unit_inventory_item::Weapon && ui->item->isWeapon()) + { + dwarf->armed = true; + df::itemdef_weaponst* weapondef = ((df::item_weaponst*)(ui->item))->subtype; + df::job_skill weaponsk = (df::job_skill) weapondef->skill_melee; + df::job_skill rangesk = (df::job_skill) weapondef->skill_ranged; + if (weaponsk == df::job_skill::AXE) + { + dwarf->has_tool[TOOL_AXE] = true; + } + else if (weaponsk == df::job_skill::MINING) + { + dwarf->has_tool[TOOL_PICK] = true; + } + else if (rangesk == df::job_skill::CROSSBOW) + { + dwarf->has_tool[TOOL_CROSSBOW] = true; + } + } + } + // Find the activity state for each dwarf bool is_on_break = false; @@ -1972,12 +2018,9 @@ private: if (labor != df::unit_labor::NONE) { labor_infos[labor].busy_dwarfs++; - - if (!dwarf->dwarf->status.labors[labor]) + if (default_labor_infos[labor].tool != TOOL_NONE) { - out.print("LABORMANAGER: dwarf %s (id %d) is doing job %s(%d) but is not enabled for labor %s(%d).\n", - dwarf->dwarf->name.first_name.c_str(), dwarf->dwarf->id, - ENUM_KEY_STR(job_type, job).c_str(), job, ENUM_KEY_STR(unit_labor, labor).c_str(), labor); + tool_in_use[default_labor_infos[labor].tool]++; } } } @@ -2044,37 +2087,7 @@ private: } dwarf->high_skill = high_skill; - // check if dwarf has an axe, pick, or crossbow - for (int j = 0; j < dwarf->dwarf->inventory.size(); j++) - { - df::unit_inventory_item* ui = dwarf->dwarf->inventory[j]; - if (ui->mode == df::unit_inventory_item::Weapon && ui->item->isWeapon()) - { - dwarf->armed = true; - df::itemdef_weaponst* weapondef = ((df::item_weaponst*)(ui->item))->subtype; - df::job_skill weaponsk = (df::job_skill) weapondef->skill_melee; - df::job_skill rangesk = (df::job_skill) weapondef->skill_ranged; - if (weaponsk == df::job_skill::AXE) - { - dwarf->has_tool[TOOL_AXE] = true; - if (state != IDLE) - tool_count[TOOL_AXE]--; - } - else if (weaponsk == df::job_skill::MINING) - { - dwarf->has_tool[TOOL_PICK] = 1; - if (state != IDLE) - tool_count[TOOL_PICK]--; - } - else if (rangesk == df::job_skill::CROSSBOW) - { - dwarf->has_tool[TOOL_CROSSBOW] = 1; - if (state != IDLE) - tool_count[TOOL_CROSSBOW]--; - } - } - } // clear labors of dwarfs with clear_all set @@ -2136,7 +2149,10 @@ private: score += 1000; if (default_labor_infos[labor].tool != TOOL_NONE && d->has_tool[default_labor_infos[labor].tool]) - score += 5000; + score += 30000; + if (default_labor_infos[labor].tool != TOOL_NONE && + !d->has_tool[default_labor_infos[labor].tool]) + score -= 30000; if (d->has_children && labor_outside[labor]) score -= 15000; if (d->armed && labor_outside[labor]) @@ -2159,6 +2175,8 @@ public: cnt_setting = cnt_traction = cnt_crutch = 0; need_food_water = 0; + labor_needed.clear(); + for (int e = 0; e < TOOLS_MAX; e++) tool_count[e] = 0; @@ -2194,8 +2212,8 @@ public: // add job entries for designation-related jobs - labor_needed[df::unit_labor::MINE] += std::min(tool_count[TOOL_PICK], dig_count); - labor_needed[df::unit_labor::CUTWOOD] += std::min(tool_count[TOOL_AXE], tree_count); + labor_needed[df::unit_labor::MINE] += dig_count; + labor_needed[df::unit_labor::CUTWOOD] += tree_count; labor_needed[df::unit_labor::DETAIL] += detail_count; labor_needed[df::unit_labor::HERBALIST] += plant_count; @@ -2269,10 +2287,19 @@ public: if (l == df::unit_labor::NONE) continue; + int before = labor_needed[l]; + if (labor_infos[l].idle_dwarfs > 0) labor_needed[l] = 0; else labor_needed[l] = max(0, labor_needed[l] - labor_in_use[l]); + + if (default_labor_infos[l].tool != TOOL_NONE) + labor_needed[l] = std::min(labor_needed[l], tool_count[default_labor_infos[l].tool] - tool_in_use[default_labor_infos[l].tool]); + + if (print_debug && before != labor_needed[l]) + out.print ("labor %s reduced from %d to %d\n", ENUM_KEY_STR(unit_labor, l).c_str(), before, labor_needed[l]); + } /* assign food haulers for rotting food items */ @@ -2427,19 +2454,30 @@ public: if (l == df::unit_labor::NONE) continue; - if (l == best_labor) + tools_enum t = default_labor_infos[l].tool; + + if (l == best_labor && ( t == TOOL_NONE || tool_in_use[t] < tool_count[t]) ) { set_labor(*bestdwarf, l, true); - tools_enum t = default_labor_infos[l].tool; - if (t != TOOL_NONE) + if (t != TOOL_NONE && (*bestdwarf)->has_tool[t]) { - tool_count[t]--; - if (!(*bestdwarf)->has_tool[t]) - (*bestdwarf)->dwarf->military.pickup_flags.bits.update = true; + df::job_type j; + j = df::job_type::NONE; + + if ((*bestdwarf)->dwarf->job.current_job) + j = (*bestdwarf)->dwarf->job.current_job->job_type; + + if (print_debug) + out.print("LABORMANAGER: asking %s to pick up tools, current job %s\n", (*bestdwarf)->dwarf->name.first_name.c_str(), ENUM_KEY_STR(job_type, j).c_str()); + + (*bestdwarf)->dwarf->military.pickup_flags.bits.update = true; + labors_changed = true; } } else if ((*bestdwarf)->state == IDLE) + { set_labor(*bestdwarf, l, false); + } } if (best_labor == df::unit_labor::HAUL_FOOD && priority_food > 0) @@ -2450,7 +2488,6 @@ public: if (best_labor != df::unit_labor::NONE) { - busy_dwarfs.push_back(*bestdwarf); labor_infos[best_labor].active_dwarfs++; to_assign[best_labor]--; } @@ -2489,6 +2526,8 @@ public: ENUM_KEY_STR(unit_labor, l).c_str(), score, ENUM_KEY_STR(unit_labor, (*d)->using_labor).c_str(), current_score); } + if ((*d)->using_labor != df::unit_labor::NONE && score > current_score + 5000) + set_labor(*d, (*d)->using_labor, false); } } } @@ -2535,7 +2574,7 @@ public: if (l >= df::unit_labor::HAUL_STONE && l <= df::unit_labor::HAUL_ANIMALS && canary & (1 << l)) set_labor(*d, l, true); - else if (l == df::unit_labor::CLEAN || l == df::unit_labor::REMOVE_CONSTRUCTION) + else if (l == df::unit_labor::CLEAN || l == df::unit_labor::REMOVE_CONSTRUCTION || l == df::unit_labor::PULL_LEVER) set_labor(*d, l, true); else set_labor(*d, l, false); @@ -2546,16 +2585,42 @@ public: for (auto d = dwarf_info.begin(); d != dwarf_info.end(); d++) { + if ((*d)->dwarf->job.current_job && (*d)->dwarf->job.current_job->job_type == df::job_type::PickupEquipment) + continue; + + if ((*d)->dwarf->military.pickup_flags.bits.update) + continue; + FOR_ENUM_ITEMS (unit_labor, l) { if (l == df::unit_labor::NONE) continue; tools_enum t = default_labor_infos[l].tool; - if (t != TOOL_NONE && tool_count[t] < 0 && (*d)->has_tool[t] && !(*d)->dwarf->status.labors[l]) + if (t == TOOL_NONE) + continue; + + bool has_tool = (*d)->has_tool[t]; + bool needs_tool = (*d)->dwarf->status.labors[l]; + + if (has_tool != needs_tool) { - tool_count[t]++; - (*d)->dwarf->military.pickup_flags.bits.update = 1; + if (has_tool && tool_count[t] > tool_in_use[t]) + continue; + + df::job_type j = df::job_type::NONE; + + if ((*d)->dwarf->job.current_job) + j = (*d)->dwarf->job.current_job->job_type; + + if (print_debug) + out.print("LABORMANAGER: asking %s to %s tools, current job %s, %d %d \n", (*d)->dwarf->name.first_name.c_str(), (has_tool) ? "drop" : "pick up", ENUM_KEY_STR(job_type, j).c_str(), has_tool, needs_tool); + + (*d)->dwarf->military.pickup_flags.bits.update = true; + labors_changed = true; + + if (needs_tool) + tool_in_use[t]++; } } } From a386228f0ede21bdd4a72cea9c9b5d9b3008c0b3 Mon Sep 17 00:00:00 2001 From: Kelly Kinkade Date: Tue, 5 Jul 2016 13:16:34 -0500 Subject: [PATCH 08/22] labormanager: add StoreItemInLocation labor, reduce tool churn Note: this commit requires updated df-structures (77968973b28d0e828f880d119a700abb079f3521 or later) --- library/xml | 2 +- plugins/devel/labormanager.cpp | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/library/xml b/library/xml index b9178de68..77968973b 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit b9178de68bd67442ff720f18b04d222302ce9f8c +Subproject commit 77968973b28d0e828f880d119a700abb079f3521 diff --git a/plugins/devel/labormanager.cpp b/plugins/devel/labormanager.cpp index 545ca7f1c..98b7f0f6f 100644 --- a/plugins/devel/labormanager.cpp +++ b/plugins/devel/labormanager.cpp @@ -1359,6 +1359,8 @@ public: job_to_labor_table[df::job_type::MakeEarring] = jlf_make_object; job_to_labor_table[df::job_type::MakeBracelet] = jlf_make_object; job_to_labor_table[df::job_type::MakeGem] = jlf_make_object; + + job_to_labor_table[df::job_type::StoreItemInLocation] = jlf_no_labor; // StoreItemInLocation }; df::unit_labor find_job_labor(df::job* j) @@ -1380,7 +1382,7 @@ public: df::unit_labor labor; if (job_to_labor_table.count(j->job_type) == 0) { - debug("LABORMANAGER: job has no job to labor table entry: %s\n", ENUM_KEY_STR(job_type, j->job_type).c_str()); + debug("LABORMANAGER: job has no job to labor table entry: %s (%d)\n", ENUM_KEY_STR(job_type, j->job_type).c_str(), j->job_type); debug_pause(); labor = df::unit_labor::NONE; } else { @@ -2526,7 +2528,7 @@ public: ENUM_KEY_STR(unit_labor, l).c_str(), score, ENUM_KEY_STR(unit_labor, (*d)->using_labor).c_str(), current_score); } - if ((*d)->using_labor != df::unit_labor::NONE && score > current_score + 5000) + if ((*d)->using_labor != df::unit_labor::NONE && score > current_score + 5000 && default_labor_infos[(*d)->using_labor].tool == TOOL_NONE) set_labor(*d, (*d)->using_labor, false); } } From 7f1d4506d4c59d6bfc9b6e6c1bda61099acf5a60 Mon Sep 17 00:00:00 2001 From: Kelly Kinkade Date: Thu, 7 Jul 2016 02:10:43 -0500 Subject: [PATCH 09/22] labormanager: fix several job-to-labor mappings --- plugins/devel/labormanager.cpp | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/plugins/devel/labormanager.cpp b/plugins/devel/labormanager.cpp index 98b7f0f6f..46322a90c 100644 --- a/plugins/devel/labormanager.cpp +++ b/plugins/devel/labormanager.cpp @@ -1135,7 +1135,7 @@ public: job_to_labor_table[df::job_type::DigChannel] = jlf_const(df::unit_labor::MINE); job_to_labor_table[df::job_type::FellTree] = jlf_const(df::unit_labor::CUTWOOD); job_to_labor_table[df::job_type::GatherPlants] = jlf_const(df::unit_labor::HERBALIST); - job_to_labor_table[df::job_type::RemoveConstruction] = jlf_no_labor; + job_to_labor_table[df::job_type::RemoveConstruction] = jlf_const(df::unit_labor::REMOVE_CONSTRUCTION); job_to_labor_table[df::job_type::CollectWebs] = jlf_const(df::unit_labor::WEAVER); job_to_labor_table[df::job_type::BringItemToDepot] = jlf_const(df::unit_labor::HAUL_TRADE); job_to_labor_table[df::job_type::BringItemToShop] = jlf_no_labor; @@ -1269,13 +1269,13 @@ public: job_to_labor_table[df::job_type::CleanTrap] = jlf_const(df::unit_labor::MECHANIC) ; job_to_labor_table[df::job_type::CastSpell] = jlf_no_labor; job_to_labor_table[df::job_type::LinkBuildingToTrigger] = jlf_const(df::unit_labor::MECHANIC) ; - job_to_labor_table[df::job_type::PullLever] = jlf_no_labor; + job_to_labor_table[df::job_type::PullLever] = jlf_const(df::unit_labor::PULL_LEVER); job_to_labor_table[df::job_type::ExtractFromPlants] = jlf_const(df::unit_labor::HERBALIST) ; job_to_labor_table[df::job_type::ExtractFromRawFish] = jlf_const(df::unit_labor::DISSECT_FISH) ; job_to_labor_table[df::job_type::ExtractFromLandAnimal] = jlf_const(df::unit_labor::DISSECT_VERMIN) ; job_to_labor_table[df::job_type::TameVermin] = jlf_const(df::unit_labor::ANIMALTRAIN) ; job_to_labor_table[df::job_type::TameAnimal] = jlf_const(df::unit_labor::ANIMALTRAIN) ; - job_to_labor_table[df::job_type::ChainAnimal] = jlf_no_labor; + job_to_labor_table[df::job_type::ChainAnimal] = jlf_const(df::unit_labor::HAUL_ANIMALS); job_to_labor_table[df::job_type::UnchainAnimal] = jlf_const(df::unit_labor::HAUL_ANIMALS); job_to_labor_table[df::job_type::UnchainPet] = jlf_no_labor; job_to_labor_table[df::job_type::ReleaseLargeCreature] = jlf_const(df::unit_labor::HAUL_ANIMALS); @@ -1335,7 +1335,7 @@ public: job_to_labor_table[df::job_type::EngraveSlab] = jlf_const(df::unit_labor::DETAIL); job_to_labor_table[df::job_type::ShearCreature] = jlf_const(df::unit_labor::SHEARER); job_to_labor_table[df::job_type::SpinThread] = jlf_const(df::unit_labor::SPINNER); - job_to_labor_table[df::job_type::PenLargeAnimal] = jlf_no_labor; + job_to_labor_table[df::job_type::PenLargeAnimal] = jlf_const(df::unit_labor::HAUL_ANIMALS); job_to_labor_table[df::job_type::PenSmallAnimal] = jlf_no_labor; job_to_labor_table[df::job_type::MakeTool] = jlf_make_furniture; job_to_labor_table[df::job_type::CollectClay] = jlf_const(df::unit_labor::POTTERY); @@ -2291,10 +2291,7 @@ public: int before = labor_needed[l]; - if (labor_infos[l].idle_dwarfs > 0) - labor_needed[l] = 0; - else - labor_needed[l] = max(0, labor_needed[l] - labor_in_use[l]); + labor_needed[l] = max(0, labor_needed[l] - labor_in_use[l]); if (default_labor_infos[l].tool != TOOL_NONE) labor_needed[l] = std::min(labor_needed[l], tool_count[default_labor_infos[l].tool] - tool_in_use[default_labor_infos[l].tool]); From bcffc53d5a2e589a7cdf5a129e99c75b46d53c95 Mon Sep 17 00:00:00 2001 From: Kelly Kinkade Date: Mon, 11 Jul 2016 22:29:38 -0500 Subject: [PATCH 10/22] labormanager: add labors for bookcase (de)construct --- plugins/devel/labormanager.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugins/devel/labormanager.cpp b/plugins/devel/labormanager.cpp index 46322a90c..cb5f11dfc 100644 --- a/plugins/devel/labormanager.cpp +++ b/plugins/devel/labormanager.cpp @@ -840,6 +840,7 @@ private: case df::building_type::BarsFloor: case df::building_type::BarsVertical: case df::building_type::GrateWall: + case df::building_type::Bookcase: return df::unit_labor::HAUL_FURNITURE; case df::building_type::Trap: case df::building_type::GearAssembly: @@ -942,6 +943,7 @@ private: case df::building_type::BarsVertical: case df::building_type::GrateFloor: case df::building_type::GrateWall: + case df::building_type::Bookcase: return df::unit_labor::HAUL_FURNITURE; case df::building_type::AnimalTrap: return df::unit_labor::TRAPPER; From 824275b23b6ca6c1b155c3c0fac3625def90aa98 Mon Sep 17 00:00:00 2001 From: Kelly Kinkade Date: Mon, 27 Jun 2016 12:04:51 -0500 Subject: [PATCH 11/22] Rename autolabor2 to labormanager and bring up to date with current --- plugins/devel/CMakeLists.txt | 2 +- .../{autolabor2.cpp => labormanager.cpp} | 487 +++++++++++++----- 2 files changed, 354 insertions(+), 135 deletions(-) rename plugins/devel/{autolabor2.cpp => labormanager.cpp} (86%) diff --git a/plugins/devel/CMakeLists.txt b/plugins/devel/CMakeLists.txt index 1f3ff6f75..a1e5b7f14 100644 --- a/plugins/devel/CMakeLists.txt +++ b/plugins/devel/CMakeLists.txt @@ -3,13 +3,13 @@ DFHACK_PLUGIN(vectors vectors.cpp) endif() ADD_DEFINITIONS(-DDEV_PLUGIN) -#DFHACK_PLUGIN(autolabor2 autolabor2.cpp) DFHACK_PLUGIN(buildprobe buildprobe.cpp) DFHACK_PLUGIN(color-dfhack-text color-dfhack-text.cpp) DFHACK_PLUGIN(counters counters.cpp) DFHACK_PLUGIN(dumpmats dumpmats.cpp) DFHACK_PLUGIN(eventExample eventExample.cpp) DFHACK_PLUGIN(frozen frozen.cpp) +DFHACK_PLUGIN(labormanager labormanager.cpp) DFHACK_PLUGIN(kittens kittens.cpp) DFHACK_PLUGIN(memview memview.cpp) DFHACK_PLUGIN(nestboxes nestboxes.cpp) diff --git a/plugins/devel/autolabor2.cpp b/plugins/devel/labormanager.cpp similarity index 86% rename from plugins/devel/autolabor2.cpp rename to plugins/devel/labormanager.cpp index 4897832d4..12e4e8ca1 100644 --- a/plugins/devel/autolabor2.cpp +++ b/plugins/devel/labormanager.cpp @@ -1,5 +1,5 @@ /* -* Autolabor 2.0 module for dfhack +* Labor manager (formerly Autolabor 2) module for dfhack * * */ @@ -77,7 +77,7 @@ using df::global::world; #define ARRAY_COUNT(array) (sizeof(array)/sizeof((array)[0])) -DFHACK_PLUGIN_IS_ENABLED(enable_autolabor); +DFHACK_PLUGIN_IS_ENABLED(enable_labormanager); static bool print_debug = 0; @@ -94,11 +94,11 @@ enum ConfigFlags { // Here go all the command declarations... // mostly to allow having the mandatory stuff on top of the file and commands on the bottom -command_result autolabor (color_ostream &out, std::vector & parameters); +command_result labormanager (color_ostream &out, std::vector & parameters); // A plugin must be able to return its name and version. -// The name string provided must correspond to the filename - autolabor2.plug.so or autolabor2.plug.dll in this case -DFHACK_PLUGIN("autolabor2"); +// The name string provided must correspond to the filename - labormanager.plug.so or labormanager.plug.dll in this case +DFHACK_PLUGIN("labormanager"); static void generate_labor_to_skill_map(); @@ -486,9 +486,20 @@ static const struct labor_default default_labor_infos[] = { /* PRESSING */ {200, 0, TOOL_NONE}, /* BEEKEEPING */ {200, 0, TOOL_NONE}, /* WAX_WORKING */ {200, 0, TOOL_NONE}, - /* PUSH_HAUL_VEHICLES */ {200, 0, TOOL_NONE} + /* PUSH_HAUL_VEHICLES */ {200, 0, TOOL_NONE}, + /* HAUL_TRADE */ {200, 0, TOOL_NONE}, + /* PULL_LEVER */ {200, 0, TOOL_NONE}, + /* REMOVE_CONSTRUCTION */ {200, 0, TOOL_NONE}, + /* HAUL_WATER */ {200, 0, TOOL_NONE}, + /* GELD */ {200, 0, TOOL_NONE}, + /* BUILD_ROAD */ {200, 0, TOOL_NONE}, + /* BUILD_CONSTRUCTION */ {200, 0, TOOL_NONE}, + /* PAPERMAKING */ {200, 0, TOOL_NONE}, + /* BOOKBINDING */ {200, 0, TOOL_NONE} }; +void debug (char* fmt, ...); + struct dwarf_info_t { df::unit* dwarf; @@ -506,12 +517,19 @@ struct dwarf_info_t df::unit_labor using_labor; dwarf_info_t(df::unit* dw) : dwarf(dw), clear_all(false), - state(OTHER), high_skill(0), has_children(false), armed(false) + state(OTHER), high_skill(0), has_children(false), armed(false), using_labor(df::unit_labor::NONE) { for (int e = TOOL_NONE; e < TOOLS_MAX; e++) has_tool[e] = false; } + ~dwarf_info_t() + { + if (print_debug) + debug("LABORMANAGER: destroying dwarf %p\n", (void*) this); + } + + void set_labor(df::unit_labor labor) { if (labor >= 0 && labor <= ENUM_LAST_ITEM(unit_labor)) @@ -554,10 +572,10 @@ static df::unit_labor hauling_labor_map[] = df::unit_labor::HAUL_ITEM, /* INSTRUMENT */ df::unit_labor::HAUL_ITEM, /* TOY */ df::unit_labor::HAUL_FURNITURE, /* WINDOW */ - df::unit_labor::HAUL_ANIMAL, /* CAGE */ + df::unit_labor::HAUL_ANIMALS, /* CAGE */ df::unit_labor::HAUL_ITEM, /* BARREL */ df::unit_labor::HAUL_ITEM, /* BUCKET */ - df::unit_labor::HAUL_ANIMAL, /* ANIMALTRAP */ + df::unit_labor::HAUL_ANIMALS, /* ANIMALTRAP */ df::unit_labor::HAUL_FURNITURE, /* TABLE */ df::unit_labor::HAUL_FURNITURE, /* COFFIN */ df::unit_labor::HAUL_FURNITURE, /* STATUE */ @@ -850,7 +868,7 @@ private: return df::unit_labor::SIEGECRAFT; } - debug ("AUTOLABOR: Cannot deduce labor for construct building job of type %s\n", + debug ("LABORMANAGER: Cannot deduce labor for construct building job of type %s\n", ENUM_KEY_STR(building_type, bld->getType()).c_str()); return df::unit_labor::NONE; @@ -944,7 +962,7 @@ private: return df::unit_labor::SIEGECRAFT; } - debug ("AUTOLABOR: Cannot deduce labor for destroy building job of type %s\n", + debug ("LABORMANAGER: Cannot deduce labor for destroy building job of type %s\n", ENUM_KEY_STR(building_type, bld->getType()).c_str()); return df::unit_labor::NONE; @@ -979,13 +997,13 @@ private: return df::unit_labor::BONE_CARVE; else { - debug ("AUTOLABOR: Cannot deduce labor for make crafts job (not bone)\n"); + debug ("LABORMANAGER: Cannot deduce labor for make crafts job (not bone)\n"); return df::unit_labor::NONE; } case df::item_type::WOOD: return df::unit_labor::WOOD_CRAFT; default: - debug ("AUTOLABOR: Cannot deduce labor for make crafts job, item type %s\n", + debug ("LABORMANAGER: Cannot deduce labor for make crafts job, item type %s\n", ENUM_KEY_STR(item_type, jobitem).c_str()); return df::unit_labor::NONE; } @@ -1002,7 +1020,7 @@ private: case df::workshop_type::MetalsmithsForge: return metaltype; default: - debug ("AUTOLABOR: Cannot deduce labor for make job, workshop type %s\n", + debug ("LABORMANAGER: Cannot deduce labor for make job, workshop type %s\n", ENUM_KEY_STR(workshop_type, type).c_str()); return df::unit_labor::NONE; } @@ -1016,13 +1034,13 @@ private: case df::furnace_type::GlassFurnace: return df::unit_labor::GLASSMAKER; default: - debug ("AUTOLABOR: Cannot deduce labor for make job, furnace type %s\n", + debug ("LABORMANAGER: Cannot deduce labor for make job, furnace type %s\n", ENUM_KEY_STR(furnace_type, type).c_str()); return df::unit_labor::NONE; } } - debug ("AUTOLABOR: Cannot deduce labor for make job, building type %s\n", + debug ("LABORMANAGER: Cannot deduce labor for make job, building type %s\n", ENUM_KEY_STR(building_type, bld->getType()).c_str()); return df::unit_labor::NONE; @@ -1147,8 +1165,6 @@ public: job_to_labor_table[df::job_type::StoreItemInStockpile] = jlf_hauling; job_to_labor_table[df::job_type::StoreItemInBag] = jlf_hauling; job_to_labor_table[df::job_type::StoreItemInHospital] = jlf_hauling; - job_to_labor_table[df::job_type::StoreItemInChest] = jlf_hauling; - job_to_labor_table[df::job_type::StoreItemInCabinet] = jlf_hauling; job_to_labor_table[df::job_type::StoreWeapon] = jlf_hauling; job_to_labor_table[df::job_type::StoreArmor] = jlf_hauling; job_to_labor_table[df::job_type::StoreItemInBarrel] = jlf_hauling; @@ -1219,7 +1235,6 @@ public: job_to_labor_table[df::job_type::MilkCreature] = jlf_const(df::unit_labor::MILK); job_to_labor_table[df::job_type::MakeCheese] = jlf_const(df::unit_labor::MAKE_CHEESE); job_to_labor_table[df::job_type::ProcessPlants] = jlf_const(df::unit_labor::PROCESS_PLANT); - job_to_labor_table[df::job_type::ProcessPlantsBag] = jlf_const(df::unit_labor::PROCESS_PLANT); job_to_labor_table[df::job_type::ProcessPlantsVial] = jlf_const(df::unit_labor::PROCESS_PLANT); job_to_labor_table[df::job_type::ProcessPlantsBarrel] = jlf_const(df::unit_labor::PROCESS_PLANT); job_to_labor_table[df::job_type::PrepareMeal] = jlf_const(df::unit_labor::COOK); @@ -1256,7 +1271,6 @@ public: job_to_labor_table[df::job_type::CastSpell] = jlf_no_labor; job_to_labor_table[df::job_type::LinkBuildingToTrigger] = jlf_const(df::unit_labor::MECHANIC) ; job_to_labor_table[df::job_type::PullLever] = jlf_no_labor; - job_to_labor_table[df::job_type::BrewDrink] = jlf_const(df::unit_labor::BREWER) ; job_to_labor_table[df::job_type::ExtractFromPlants] = jlf_const(df::unit_labor::HERBALIST) ; job_to_labor_table[df::job_type::ExtractFromRawFish] = jlf_const(df::unit_labor::DISSECT_FISH) ; job_to_labor_table[df::job_type::ExtractFromLandAnimal] = jlf_const(df::unit_labor::DISSECT_VERMIN) ; @@ -1334,9 +1348,9 @@ public: job_to_labor_table[df::job_type::ExecuteCriminal] = jlf_no_labor; job_to_labor_table[df::job_type::TrainAnimal] = jlf_const(df::unit_labor::ANIMALTRAIN); job_to_labor_table[df::job_type::CarveTrack] = jlf_const(df::unit_labor::DETAIL); - job_to_labor_table[df::job_type::PushTrackVehicle] = jlf_const(df::unit_labor::PUSH_HAUL_VEHICLE); - job_to_labor_table[df::job_type::PlaceTrackVehicle] = jlf_const(df::unit_labor::PUSH_HAUL_VEHICLE); - job_to_labor_table[df::job_type::StoreItemInVehicle] = jlf_const(df::unit_labor::PUSH_HAUL_VEHICLE); + job_to_labor_table[df::job_type::PushTrackVehicle] = jlf_const(df::unit_labor::HANDLE_VEHICLES); + job_to_labor_table[df::job_type::PlaceTrackVehicle] = jlf_const(df::unit_labor::HANDLE_VEHICLES); + job_to_labor_table[df::job_type::StoreItemInVehicle] = jlf_const(df::unit_labor::HANDLE_VEHICLES); job_to_labor_table[df::job_type::GeldAnimal] = jlf_const(df::unit_labor::GELD); job_to_labor_table[df::job_type::MakeFigurine] = jlf_make_object; job_to_labor_table[df::job_type::MakeAmulet] = jlf_make_object; @@ -1365,7 +1379,14 @@ public: df::unit_labor labor; - labor = job_to_labor_table[j->job_type]->get_labor(j); + if (job_to_labor_table.count(j->job_type) == 0) + { + debug("LABORMANAGER: job has no job to labor table entry: %s\n", ENUM_KEY_STR(job_type, j->job_type).c_str()); + labor = df::unit_labor::NONE; + } else { + + labor = job_to_labor_table[j->job_type]->get_labor(j); + } return labor; } @@ -1375,6 +1396,8 @@ public: static JobLaborMapper* labor_mapper = 0; +static bool initialized = false; + static bool isOptionEnabled(unsigned flag) { return config.isValid() && (config.ival(0) & flag) != 0; @@ -1393,8 +1416,9 @@ static void setOptionEnabled(ConfigFlags flag, bool on) static void cleanup_state() { - enable_autolabor = false; + enable_labormanager = false; labor_infos.clear(); + initialized = false; } static void reset_labor(df::unit_labor labor) @@ -1405,25 +1429,25 @@ static void reset_labor(df::unit_labor labor) static void init_state() { - config = World::GetPersistentData("autolabor/2.0/config"); + config = World::GetPersistentData("labormanager/2.0/config"); if (config.isValid() && config.ival(0) == -1) config.ival(0) = 0; - enable_autolabor = isOptionEnabled(CF_ENABLED); + enable_labormanager = isOptionEnabled(CF_ENABLED); - if (!enable_autolabor) + if (!enable_labormanager) return; // Load labors from save labor_infos.resize(ARRAY_COUNT(default_labor_infos)); std::vector items; - World::GetPersistentData(&items, "autolabor/2.0/labors/", true); + World::GetPersistentData(&items, "labormanager/2.0/labors/", true); for (auto p = items.begin(); p != items.end(); p++) { string key = p->key(); - df::unit_labor labor = (df::unit_labor) atoi(key.substr(strlen("autolabor/2.0/labors/")).c_str()); + df::unit_labor labor = (df::unit_labor) atoi(key.substr(strlen("labormanager/2.0/labors/")).c_str()); if (labor >= 0 && labor <= labor_infos.size()) { labor_infos[labor].config = *p; @@ -1437,7 +1461,7 @@ static void init_state() continue; std::stringstream name; - name << "autolabor/2.0/labors/" << i; + name << "labormanager/2.0/labors/" << i; labor_infos[i].config = World::AddPersistentData(name.str()); labor_infos[i].mark_assigned(); @@ -1445,6 +1469,8 @@ static void init_state() reset_labor((df::unit_labor) i); } + initialized = true; + } static df::job_skill labor_to_skill[ENUM_LAST_ITEM(unit_labor) + 1]; @@ -1477,12 +1503,12 @@ static void enable_plugin(color_ostream &out) { if (!config.isValid()) { - config = World::AddPersistentData("autolabor/2.0/config"); + config = World::AddPersistentData("labormanager/2.0/config"); config.ival(0) = 0; } setOptionEnabled(CF_ENABLED, true); - enable_autolabor = true; + enable_labormanager = true; out << "Enabling the plugin." << endl; cleanup_state(); @@ -1497,32 +1523,32 @@ DFhackCExport command_result plugin_init ( color_ostream &out, std::vector \n" + " labormanager max \n" " Set max number of dwarves assigned to a labor.\n" - " autolabor2 max none\n" + " labormanager max none\n" " Unrestrict the number of dwarves assigned to a labor.\n" - " autolabor2 priority \n" + " labormanager priority \n" " Change the assignment priority of a labor (default is 100)\n" - " autolabor2 reset \n" + " labormanager reset \n" " Return a labor to the default handling.\n" - " autolabor2 reset-all\n" + " labormanager reset-all\n" " Return all labors to the default handling.\n" - " autolabor2 list\n" + " labormanager list\n" " List current status of all labors.\n" - " autolabor2 status\n" + " labormanager status\n" " Show basic status information.\n" "Function:\n" - " When enabled, autolabor periodically checks your dwarves and enables or\n" + " When enabled, labormanager periodically checks your dwarves and enables or\n" " disables labors. Generally, each dwarf will be assigned exactly one labor.\n" - " Warning: autolabor will override any manual changes you make to labors\n" - " while it is enabled. Do not try to run both autolabor and autolabor2 at\n" - " the same time." + " Warning: labormanager will override any manual changes you make to labors\n" + " while it is enabled. Do not try to run both labormanager and labormanager at\n" + " the same time.\n" )); generate_labor_to_skill_map(); @@ -1594,14 +1620,21 @@ private: int need_food_water; + int priority_food; + std::map labor_needed; std::map labor_outside; std::vector dwarf_info; std::list available_dwarfs; + std::list busy_dwarfs; private: void scan_buildings() { + has_butchers = false; + has_fishery = false; + trader_requested = false; + for (auto b = world->buildings.all.begin(); b != world->buildings.all.end(); b++) { df::building *build = *b; @@ -1658,16 +1691,14 @@ private: if (dig != df::enums::tile_dig_designation::No) { df::tiletype tt = bl->tiletype[x][y]; + df::tiletype_material ttm = ENUM_ATTR(tiletype, material, tt); df::tiletype_shape tts = ENUM_ATTR(tiletype, shape, tt); - switch (tts) - { - case df::enums::tiletype_shape::TREE: - tree_count++; break; - case df::enums::tiletype_shape::SHRUB: - plant_count++; break; - default: - dig_count++; break; - } + if (ttm == df::enums::tiletype_material::TREE) + tree_count++; + else if (tts == df::enums::tiletype_shape::SHRUB) + plant_count++; + else + dig_count++; } if (bl->designation[x][y].bits.smooth != 0) detail_count++; @@ -1684,13 +1715,15 @@ private: for (int e = TOOL_NONE; e < TOOLS_MAX; e++) tool_count[e] = 0; + priority_food = 0; + df::item_flags bad_flags; bad_flags.whole = 0; #define F(x) bad_flags.bits.x = true; F(dump); F(forbid); F(garbage_collect); F(hostile); F(on_fire); F(rotten); F(trader); - F(in_building); F(construction); F(artifact); + F(in_building); F(construction); #undef F auto& v = world->items.all; @@ -1704,6 +1737,11 @@ private: if (item->flags.whole & bad_flags.whole) continue; + df::item_type t = item->getType(); + + if (item->materialRots() && t != df::item_type::CORPSEPIECE && t != df::item_type::CORPSE && item->getRotTimer() > 1) + priority_food++; + if (!item->isWeapon()) continue; @@ -1869,14 +1907,20 @@ private: { state = CHILD; } + else if (ENUM_ATTR(profession, military, dwarf->dwarf->profession)) state = MILITARY; + + else if (dwarf->dwarf->burrows.size() > 0) + state = OTHER; // dwarfs assigned to burrows are treated as if permanently busy + else if (dwarf->dwarf->job.current_job == NULL) { - if (is_on_break) + if (is_on_break || dwarf->dwarf->flags1.bits.chained || dwarf->dwarf->flags1.bits.caged) + { state = OTHER; - else if (dwarf->dwarf->burrows.size() > 0) - state = OTHER; // dwarfs assigned to burrows are treated as if permanently busy + dwarf->clear_all = true; + } else if (dwarf->dwarf->status2.limbs_grasp_count == 0) { state = OTHER; // dwarfs unable to grasp are incapable of nearly all labors @@ -1902,18 +1946,23 @@ private: if (state == BUSY) { df::unit_labor labor = labor_mapper->find_job_labor(dwarf->dwarf->job.current_job); + + dwarf->using_labor = labor; + if (labor != df::unit_labor::NONE) { - dwarf->using_labor = labor; + labor_infos[labor].busy_dwarfs++; - if (!dwarf->dwarf->status.labors[labor] && print_debug) + if (!dwarf->dwarf->status.labors[labor]) { - out.print("AUTOLABOR: dwarf %s (id %d) is doing job %s(%d) but is not enabled for labor %s(%d).\n", + out.print("LABORMANAGER: dwarf %s (id %d) is doing job %s(%d) but is not enabled for labor %s(%d).\n", dwarf->dwarf->name.first_name.c_str(), dwarf->dwarf->id, ENUM_KEY_STR(job_type, job).c_str(), job, ENUM_KEY_STR(unit_labor, labor).c_str(), labor); } } } + if (state == OTHER) + dwarf->clear_all = true; } dwarf->state = state; @@ -1925,8 +1974,6 @@ private: if (dwarf->dwarf->status.labors[l]) if (state == IDLE) labor_infos[l].idle_dwarfs++; - else if (state == BUSY) - labor_infos[l].busy_dwarfs++; } @@ -2022,19 +2069,69 @@ private: dwarf->clear_labor(labor); } + } else { + if (state == IDLE) + available_dwarfs.push_back(dwarf); + + if (state == BUSY) + busy_dwarfs.push_back(dwarf); } - else if (state == IDLE || state == BUSY) - available_dwarfs.push_back(dwarf); + } + + } + } + void release_dwarf_list() + { + while (!dwarf_info.empty()) { + auto d = dwarf_info.begin(); + delete *d; + dwarf_info.erase(d); + } + available_dwarfs.clear(); + busy_dwarfs.clear(); + } + + int score_labor (dwarf_info_t* d, df::unit_labor labor) + { + int skill_level = 0; + int xp = 0; + + if (labor != df::unit_labor::NONE) + { + df::job_skill skill = labor_to_skill[labor]; + if (skill != df::job_skill::NONE) + { + skill_level = Units::getEffectiveSkill(d->dwarf, skill); + xp = Units::getExperience(d->dwarf, skill, false); } + } + int score = skill_level * 1000 - (d->high_skill - skill_level) * 2000 + (xp / (skill_level + 5) * 10); + + if (labor != df::unit_labor::NONE) + { + if (d->dwarf->status.labors[labor]) + if (labor == df::unit_labor::OPERATE_PUMP) + score += 50000; + else + score += 1000; + if (default_labor_infos[labor].tool != TOOL_NONE && + d->has_tool[default_labor_infos[labor].tool]) + score += 5000; + if (d->has_children && labor_outside[labor]) + score -= 15000; + if (d->armed && labor_outside[labor]) + score += 5000; } + + return score; } public: void process() { - dwarf_info.clear(); + release_dwarf_list(); dig_count = tree_count = plant_count = detail_count = 0; cnt_recover_wounded = cnt_diagnosis = cnt_immobilize = cnt_dressing = cnt_cleaning = cnt_surgery = cnt_suture = @@ -2106,13 +2203,27 @@ public: labor_needed[df::unit_labor::HAUL_FOOD] += world->stockpile.num_jobs[6]; labor_needed[df::unit_labor::HAUL_REFUSE] += world->stockpile.num_jobs[7]; labor_needed[df::unit_labor::HAUL_FURNITURE] += world->stockpile.num_jobs[8]; - labor_needed[df::unit_labor::HAUL_ANIMAL] += world->stockpile.num_jobs[9]; + labor_needed[df::unit_labor::HAUL_ANIMALS] += world->stockpile.num_jobs[9]; + + labor_needed[df::unit_labor::HAUL_STONE] += (world->stockpile.num_jobs[1] >= world->stockpile.num_haulers[1]) ? 1 : 0; + labor_needed[df::unit_labor::HAUL_WOOD] += (world->stockpile.num_jobs[2] >= world->stockpile.num_haulers[2]) ? 1 : 0; + labor_needed[df::unit_labor::HAUL_ITEM] += (world->stockpile.num_jobs[3] >= world->stockpile.num_haulers[3]) ? 1 : 0; + labor_needed[df::unit_labor::HAUL_BODY] += (world->stockpile.num_jobs[5] >= world->stockpile.num_haulers[5]) ? 1 : 0; + labor_needed[df::unit_labor::HAUL_FOOD] += (world->stockpile.num_jobs[6] >= world->stockpile.num_haulers[6]) ? 1 : 0; + labor_needed[df::unit_labor::HAUL_REFUSE] += (world->stockpile.num_jobs[7] >= world->stockpile.num_haulers[7]) ? 1 : 0; + labor_needed[df::unit_labor::HAUL_FURNITURE] += (world->stockpile.num_jobs[8] >= world->stockpile.num_haulers[8]) ? 1 : 0; + labor_needed[df::unit_labor::HAUL_ANIMALS] += (world->stockpile.num_jobs[9] >= world->stockpile.num_haulers[9]) ? 1 : 0; + + int binjobs = world->stockpile.num_jobs[4] + ((world->stockpile.num_jobs[4] >= world->stockpile.num_haulers[4]) ? 1 : 0); + + labor_needed[df::unit_labor::HAUL_ITEM] += binjobs; + labor_needed[df::unit_labor::HAUL_FOOD] += priority_food; // add entries for vehicle hauling for (auto v = world->vehicles.all.begin(); v != world->vehicles.all.end(); v++) if ((*v)->route_id != -1) - labor_needed[df::unit_labor::PUSH_HAUL_VEHICLE]++; + labor_needed[df::unit_labor::HANDLE_VEHICLES]++; // add fishing & hunting @@ -2131,33 +2242,101 @@ public: // note: this doesn't test to see if the trainer is actually needed, and thus will overallocate trainers. bleah. } + /* move idle dwarfs ready to be assigned to busy list */ + for (auto d = available_dwarfs.begin(); d != available_dwarfs.end(); ) + { + bool busy = false; + + FOR_ENUM_ITEMS(unit_labor, l) + { + if (l == df::unit_labor::NONE) + continue; + + + if (labor_needed[l] > 0 && (*d)->dwarf->status.labors[l]) + { + busy = true; + labor_needed[l] = max(labor_needed[l]-1, 0); + } + } + + if (busy) + { + busy_dwarfs.push_back(*d); + d = available_dwarfs.erase(d); + } else { + d++; + } + } + /* adjust for over/under */ FOR_ENUM_ITEMS(unit_labor, l) { if (l == df::unit_labor::NONE) continue; - if (l >= df::unit_labor::HAUL_STONE && l <= df::unit_labor::HAUL_ANIMAL) - continue; - if (labor_infos[l].idle_dwarfs > 0 && labor_needed[l] > labor_infos[l].busy_dwarfs) + if (labor_infos[l].idle_dwarfs > 0) + labor_needed[l] = 0; + else + labor_needed[l] = std::max(labor_needed[l] - labor_infos[l].busy_dwarfs, 0); + } + + /* assign food haulers for rotting food items */ + + if (priority_food > 0 && labor_infos[df::unit_labor::HAUL_FOOD].idle_dwarfs > 0) + priority_food = 1; + + if (print_debug) + out.print ("priority food count = %d\n", priority_food); + + while (!available_dwarfs.empty() && priority_food > 0) + { + std::list::iterator bestdwarf = available_dwarfs.begin(); + + int best_score = INT_MIN; + + for (std::list::iterator k = available_dwarfs.begin(); k != available_dwarfs.end(); k++) { - int clawback = labor_infos[l].busy_dwarfs; - if (clawback == 0 && labor_needed[l] > 0) - clawback = 1; + dwarf_info_t* d = (*k); + int score = score_labor(d, df::unit_labor::HAUL_FOOD); + + if (score > best_score) + { + bestdwarf = k; + best_score = score; + } + } + + if (best_score > INT_MIN) + { if (print_debug) - out.print("reducing labor %s to %d (%d needed, %d busy, %d idle)\n", ENUM_KEY_STR(unit_labor, l).c_str(), - clawback, - labor_needed[l], labor_infos[l].busy_dwarfs, labor_infos[l].idle_dwarfs); - labor_needed[l] = clawback; + out.print("LABORMANAGER: assign \"%s\" labor %s score=%d (priority food)\n", (*bestdwarf)->dwarf->name.first_name.c_str(), ENUM_KEY_STR(unit_labor, df::unit_labor::HAUL_FOOD).c_str(), best_score); + + FOR_ENUM_ITEMS(unit_labor, l) + { + if (l == df::unit_labor::NONE) + continue; + + if (l == df::unit_labor::HAUL_FOOD) + (*bestdwarf)->set_labor(l); + else + (*bestdwarf)->clear_labor(l); + } + + available_dwarfs.erase(bestdwarf); + priority_food--; } + else + break; + } if (print_debug) { for (auto i = labor_needed.begin(); i != labor_needed.end(); i++) { - out.print ("labor_needed [%s] = %d, outside = %d, idle = %d\n", ENUM_KEY_STR(unit_labor, i->first).c_str(), i->second, - labor_outside[i->first], labor_infos[i->first].idle_dwarfs); + out.print ("labor_needed [%s] = %d, busy = %d, outside = %d, idle = %d\n", ENUM_KEY_STR(unit_labor, i->first).c_str(), i->second, + labor_infos[i->first].busy_dwarfs, labor_outside[i->first], labor_infos[i->first].idle_dwarfs); } } @@ -2173,6 +2352,8 @@ public: { int priority = labor_infos[l].priority(); priority += labor_infos[l].time_since_last_assigned()/12; + for (int n = 0; n < labor_infos[l].busy_dwarfs; n++) + priority /= 2; pq.push(make_pair(priority, l)); } } @@ -2212,7 +2393,7 @@ public: (1 << df::unit_labor::HAUL_REFUSE) | (1 << df::unit_labor::HAUL_ITEM) | (1 << df::unit_labor::HAUL_FURNITURE) | - (1 << df::unit_labor::HAUL_ANIMAL); + (1 << df::unit_labor::HAUL_ANIMALS); while (!available_dwarfs.empty()) { @@ -2227,36 +2408,11 @@ public: continue; df::unit_labor labor = j->first; - df::job_skill skill = labor_to_skill[labor]; for (std::list::iterator k = available_dwarfs.begin(); k != available_dwarfs.end(); k++) { dwarf_info_t* d = (*k); - int skill_level = 0; - int xp = 0; - if (skill != df::job_skill::NONE) - { - skill_level = Units::getEffectiveSkill(d->dwarf, skill); - xp = Units::getExperience(d->dwarf, skill, false); - } - int score = skill_level * 1000 - (d->high_skill - skill_level) * 2000 + (xp / (skill_level + 5) * 10); - if (d->dwarf->status.labors[labor]) - if (labor == df::unit_labor::OPERATE_PUMP) - score += 50000; - else - score += 1000; - if (default_labor_infos[labor].tool != TOOL_NONE && - d->has_tool[default_labor_infos[labor].tool]) - score += 5000; - if (d->has_children && labor_outside[labor]) - score -= 15000; - if (d->armed && labor_outside[labor]) - score += 5000; - if (d->state == BUSY) - if (d->using_labor == labor) - score += 7500; - else - score -= 7500; + int score = score_labor(d, labor); if (score > best_score) { bestdwarf = k; @@ -2289,11 +2445,15 @@ public: (*bestdwarf)->clear_labor(l); } - if (best_labor >= df::unit_labor::HAUL_STONE && best_labor <= df::unit_labor::HAUL_ANIMAL) + if (best_labor == df::unit_labor::HAUL_FOOD && priority_food > 0) + priority_food--; + + if (best_labor >= df::unit_labor::HAUL_STONE && best_labor <= df::unit_labor::HAUL_ANIMALS) canary &= ~(1 << best_labor); if (best_labor != df::unit_labor::NONE) { + busy_dwarfs.push_back(*bestdwarf); labor_infos[best_labor].active_dwarfs++; to_assign[best_labor]--; } @@ -2301,17 +2461,71 @@ public: available_dwarfs.erase(bestdwarf); } - if (canary != 0) + for (auto d = busy_dwarfs.begin(); d != busy_dwarfs.end(); d++) { - dwarf_info_t* d = dwarf_info.front(); + int current_score = score_labor (*d, (*d)->using_labor); + FOR_ENUM_ITEMS (unit_labor, l) { - if (l >= df::unit_labor::HAUL_STONE && l <= df::unit_labor::HAUL_ANIMAL && - canary & (1 << l)) - d->set_labor(l); + if (l == df::unit_labor::NONE) + continue; + if (l == (*d)->using_labor) + continue; + if (labor_needed[l] <= 0) + continue; + + int score = score_labor (*d, l); + score += labor_infos[l].time_since_last_assigned()/12; + if (l == df::unit_labor::HAUL_FOOD && priority_food > 0) + score += 1000000; + + if (score > current_score) + { + tools_enum t = default_labor_infos[l].tool; + if (t == TOOL_NONE || (*d)->has_tool[t]) + { + (*d)->set_labor (l); + if (print_debug) + out.print("assign \"%s\" extra labor %s score=%d current %s score=%d\n", + (*d)->dwarf->name.first_name.c_str(), + ENUM_KEY_STR(unit_labor, l).c_str(), score, + ENUM_KEY_STR(unit_labor, (*d)->using_labor).c_str(), current_score); + } + } + else + (*d)->clear_labor (l); + } + } + + + if (canary != 0) + { + dwarf_info_t* d = 0; + + for (auto di = busy_dwarfs.begin(); di != busy_dwarfs.end(); di++) + if (!(*di)->clear_all) + { + d = *di; + break; + } + + if (d) + { + + FOR_ENUM_ITEMS (unit_labor, l) + { + if (l >= df::unit_labor::HAUL_STONE && l <= df::unit_labor::HAUL_ANIMALS && + canary & (1 << l)) + d->set_labor(l); + } + if (print_debug) + out.print ("Setting %s as the hauling canary\n", d->dwarf->name.first_name.c_str()); + } + else + { + if (print_debug) + out.print ("No dwarf available to set as the hauling canary!\n"); } - if (print_debug) - out.print ("Setting %s as the hauling canary\n", d->dwarf->name.first_name.c_str()); } /* set reequip on any dwarfs who are carrying tools needed by others */ @@ -2320,6 +2534,9 @@ public: { FOR_ENUM_ITEMS (unit_labor, l) { + if (l == df::unit_labor::NONE) + continue; + tools_enum t = default_labor_infos[l].tool; if (t != TOOL_NONE && tool_count[t] < 0 && (*d)->has_tool[t] && !(*d)->dwarf->status.labors[l]) { @@ -2329,6 +2546,8 @@ public: } } + release_dwarf_list(); + *df::global::process_jobs = true; print_debug = 0; @@ -2359,7 +2578,7 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out ) { static int step_count = 0; // check run conditions - if(!world || !world->map.block_index || !enable_autolabor) + if(!initialized || !world || !world->map.block_index || !enable_labormanager) { // give up if we shouldn't be running' return CR_OK; @@ -2406,26 +2625,26 @@ df::unit_labor lookup_labor_by_name (std::string& name) DFhackCExport command_result plugin_enable ( color_ostream &out, bool enable ) { if (!Core::getInstance().isWorldLoaded()) { - out.printerr("World is not loaded: please load a game first.\n"); + out.printerr("World is not loaded: please load a fort first.\n"); return CR_FAILURE; } - if (enable && !enable_autolabor) + if (enable && !enable_labormanager) { enable_plugin(out); } - else if(!enable && enable_autolabor) + else if(!enable && enable_labormanager) { - enable_autolabor = false; + enable_labormanager = false; setOptionEnabled(CF_ENABLED, false); - out << "Autolabor is disabled." << endl; + out << "LaborManager is disabled." << endl; } return CR_OK; } -command_result autolabor (color_ostream &out, std::vector & parameters) +command_result labormanager (color_ostream &out, std::vector & parameters) { CoreSuspender suspend; @@ -2443,7 +2662,7 @@ command_result autolabor (color_ostream &out, std::vector & parame else if (parameters.size() == 3 && (parameters[0] == "max" || parameters[0] == "priority")) { - if (!enable_autolabor) + if (!enable_labormanager) { out << "Error: The plugin is not enabled." << endl; return CR_FAILURE; @@ -2474,7 +2693,7 @@ command_result autolabor (color_ostream &out, std::vector & parame } else if (parameters.size() == 2 && parameters[0] == "reset") { - if (!enable_autolabor) + if (!enable_labormanager) { out << "Error: The plugin is not enabled." << endl; return CR_FAILURE; @@ -2493,7 +2712,7 @@ command_result autolabor (color_ostream &out, std::vector & parame } else if (parameters.size() == 1 && (parameters[0] == "allow-fishing" || parameters[0] == "forbid-fishing")) { - if (!enable_autolabor) + if (!enable_labormanager) { out << "Error: The plugin is not enabled." << endl; return CR_FAILURE; @@ -2504,7 +2723,7 @@ command_result autolabor (color_ostream &out, std::vector & parame } else if (parameters.size() == 1 && (parameters[0] == "allow-hunting" || parameters[0] == "forbid-hunting")) { - if (!enable_autolabor) + if (!enable_labormanager) { out << "Error: The plugin is not enabled." << endl; return CR_FAILURE; @@ -2515,7 +2734,7 @@ command_result autolabor (color_ostream &out, std::vector & parame } else if (parameters.size() == 1 && parameters[0] == "reset-all") { - if (!enable_autolabor) + if (!enable_labormanager) { out << "Error: The plugin is not enabled." << endl; return CR_FAILURE; @@ -2530,7 +2749,7 @@ command_result autolabor (color_ostream &out, std::vector & parame } else if (parameters.size() == 1 && parameters[0] == "list" || parameters[0] == "status") { - if (!enable_autolabor) + if (!enable_labormanager) { out << "Error: The plugin is not enabled." << endl; return CR_FAILURE; @@ -2563,7 +2782,7 @@ command_result autolabor (color_ostream &out, std::vector & parame } else if (parameters.size() == 1 && parameters[0] == "debug") { - if (!enable_autolabor) + if (!enable_labormanager) { out << "Error: The plugin is not enabled." << endl; return CR_FAILURE; @@ -2576,8 +2795,8 @@ command_result autolabor (color_ostream &out, std::vector & parame else { out.print("Automatically assigns labors to dwarves.\n" - "Activate with 'autolabor enable', deactivate with 'autolabor disable'.\n" - "Current state: %s.\n", enable_autolabor ? "enabled" : "disabled"); + "Activate with 'labormanager enable', deactivate with 'labormanager disable'.\n" + "Current state: %s.\n", enable_labormanager ? "enabled" : "disabled"); return CR_OK; } From 07e1c819691622b450bbc4bb9dd0fb141008b38e Mon Sep 17 00:00:00 2001 From: Kelly Kinkade Date: Mon, 27 Jun 2016 20:58:38 -0500 Subject: [PATCH 12/22] labormanager improvements Add some debugging facilities. Change some hauling, construction, and deconstruction labors to reflect changes in DF since 34.11. --- plugins/devel/labormanager.cpp | 40 +++++++++++++++++++++++++++++----- 1 file changed, 35 insertions(+), 5 deletions(-) diff --git a/plugins/devel/labormanager.cpp b/plugins/devel/labormanager.cpp index 12e4e8ca1..ffabde36a 100644 --- a/plugins/devel/labormanager.cpp +++ b/plugins/devel/labormanager.cpp @@ -80,6 +80,7 @@ using df::global::world; DFHACK_PLUGIN_IS_ENABLED(enable_labormanager); static bool print_debug = 0; +static bool pause_on_error = 1; static std::vector state_count(5); @@ -525,8 +526,6 @@ struct dwarf_info_t ~dwarf_info_t() { - if (print_debug) - debug("LABORMANAGER: destroying dwarf %p\n", (void*) this); } @@ -608,7 +607,7 @@ static df::unit_labor hauling_labor_map[] = df::unit_labor::HAUL_FOOD, /* FISH_RAW */ df::unit_labor::HAUL_REFUSE, /* VERMIN */ df::unit_labor::HAUL_ITEM, /* PET */ - df::unit_labor::HAUL_FOOD, /* SEEDS */ + df::unit_labor::HAUL_ITEM, /* SEEDS */ df::unit_labor::HAUL_FOOD, /* PLANT */ df::unit_labor::HAUL_ITEM, /* SKIN_TANNED */ df::unit_labor::HAUL_FOOD, /* LEAVES */ @@ -716,6 +715,14 @@ void debug (char* fmt, ...) } } +void debug_pause () +{ + if (pause_on_error) + { + debug("LABORMANAGER: Game paused so you can investigate the above message.\nUse 'labormanager pause-on-error no' to disable autopausing.\n"); + *df::global::pause_state = true; + } +} class JobLaborMapper { @@ -804,9 +811,10 @@ private: return workshop_build_labor[ws->type]; } break; + case df::building_type::Construction: + return df::unit_labor::BUILD_CONSTRUCTION; case df::building_type::Furnace: case df::building_type::TradeDepot: - case df::building_type::Construction: case df::building_type::Bridge: case df::building_type::ArcheryTarget: case df::building_type::WaterWheel: @@ -870,6 +878,7 @@ private: debug ("LABORMANAGER: Cannot deduce labor for construct building job of type %s\n", ENUM_KEY_STR(building_type, bld->getType()).c_str()); + debug_pause(); return df::unit_labor::NONE; } @@ -900,9 +909,10 @@ private: return workshop_build_labor[ws->type]; } break; + case df::building_type::Construction: + return df::unit_labor::REMOVE_CONSTRUCTION; case df::building_type::Furnace: case df::building_type::TradeDepot: - case df::building_type::Construction: case df::building_type::Wagon: case df::building_type::Bridge: case df::building_type::ScrewPump: @@ -964,6 +974,7 @@ private: debug ("LABORMANAGER: Cannot deduce labor for destroy building job of type %s\n", ENUM_KEY_STR(building_type, bld->getType()).c_str()); + debug_pause(); return df::unit_labor::NONE; } @@ -998,6 +1009,7 @@ private: else { debug ("LABORMANAGER: Cannot deduce labor for make crafts job (not bone)\n"); + debug_pause(); return df::unit_labor::NONE; } case df::item_type::WOOD: @@ -1005,6 +1017,7 @@ private: default: debug ("LABORMANAGER: Cannot deduce labor for make crafts job, item type %s\n", ENUM_KEY_STR(item_type, jobitem).c_str()); + debug_pause(); return df::unit_labor::NONE; } } @@ -1022,6 +1035,7 @@ private: default: debug ("LABORMANAGER: Cannot deduce labor for make job, workshop type %s\n", ENUM_KEY_STR(workshop_type, type).c_str()); + debug_pause(); return df::unit_labor::NONE; } } @@ -1036,12 +1050,14 @@ private: default: debug ("LABORMANAGER: Cannot deduce labor for make job, furnace type %s\n", ENUM_KEY_STR(furnace_type, type).c_str()); + debug_pause(); return df::unit_labor::NONE; } } debug ("LABORMANAGER: Cannot deduce labor for make job, building type %s\n", ENUM_KEY_STR(building_type, bld->getType()).c_str()); + debug_pause(); return df::unit_labor::NONE; } @@ -1382,6 +1398,7 @@ public: if (job_to_labor_table.count(j->job_type) == 0) { debug("LABORMANAGER: job has no job to labor table entry: %s\n", ENUM_KEY_STR(job_type, j->job_type).c_str()); + debug_pause(); labor = df::unit_labor::NONE; } else { @@ -1941,6 +1958,7 @@ private: else { out.print("Dwarf \"%s\" has unknown job %i\n", dwarf->dwarf->name.first_name.c_str(), job); + debug_pause(); state = OTHER; } if (state == BUSY) @@ -2780,6 +2798,18 @@ command_result labormanager (color_ostream &out, std::vector & par return CR_OK; } + else if (parameters.size() == 2 && parameters[0] == "pause-on-error") + { + if (!enable_labormanager) + { + out << "Error: The plugin is not enabled." << endl; + return CR_FAILURE; + } + + pause_on_error = parameters[1] == "yes" || parameters[1] == "true"; + + return CR_OK; + } else if (parameters.size() == 1 && parameters[0] == "debug") { if (!enable_labormanager) From f095e139aaafbe7207439a2bd3eadc4e90683352 Mon Sep 17 00:00:00 2001 From: Kelly Kinkade Date: Tue, 28 Jun 2016 22:36:45 -0500 Subject: [PATCH 13/22] labormanager: more tweaks to bring up to date This update fixes some labors and attempts to address changes in the way DF maintains the job list. --- plugins/devel/labormanager.cpp | 155 +++++++++++++++------------------ 1 file changed, 72 insertions(+), 83 deletions(-) diff --git a/plugins/devel/labormanager.cpp b/plugins/devel/labormanager.cpp index ffabde36a..256841cb7 100644 --- a/plugins/devel/labormanager.cpp +++ b/plugins/devel/labormanager.cpp @@ -1154,7 +1154,7 @@ public: job_to_labor_table[df::job_type::GatherPlants] = jlf_const(df::unit_labor::HERBALIST); job_to_labor_table[df::job_type::RemoveConstruction] = jlf_no_labor; job_to_labor_table[df::job_type::CollectWebs] = jlf_const(df::unit_labor::WEAVER); - job_to_labor_table[df::job_type::BringItemToDepot] = jlf_no_labor; + job_to_labor_table[df::job_type::BringItemToDepot] = jlf_const(df::unit_labor::HAUL_TRADE); job_to_labor_table[df::job_type::BringItemToShop] = jlf_no_labor; job_to_labor_table[df::job_type::Eat] = jlf_no_labor; job_to_labor_table[df::job_type::GetProvisions] = jlf_no_labor; @@ -1178,13 +1178,13 @@ public: job_to_labor_table[df::job_type::CheckChest] = jlf_no_labor; job_to_labor_table[df::job_type::StoreOwnedItem] = jlf_no_labor; job_to_labor_table[df::job_type::PlaceItemInTomb] = jlf_const(df::unit_labor::HAUL_BODY); - job_to_labor_table[df::job_type::StoreItemInStockpile] = jlf_hauling; - job_to_labor_table[df::job_type::StoreItemInBag] = jlf_hauling; + job_to_labor_table[df::job_type::StoreItemInStockpile] = jlf_no_labor; // Can arise from many different labors, but will never appear in a pending job list + job_to_labor_table[df::job_type::StoreItemInBag] = jlf_no_labor; // Can arise from many different labors, but will never appear in a pending job list job_to_labor_table[df::job_type::StoreItemInHospital] = jlf_hauling; job_to_labor_table[df::job_type::StoreWeapon] = jlf_hauling; job_to_labor_table[df::job_type::StoreArmor] = jlf_hauling; - job_to_labor_table[df::job_type::StoreItemInBarrel] = jlf_hauling; - job_to_labor_table[df::job_type::StoreItemInBin] = jlf_const(df::unit_labor::HAUL_ITEM); + job_to_labor_table[df::job_type::StoreItemInBarrel] = jlf_no_labor; // Can arise from many different labors, but will never appear in a pending job list + job_to_labor_table[df::job_type::StoreItemInBin] = jlf_no_labor; // Can arise from many different labors, but will never appear in a pending job list job_to_labor_table[df::job_type::SeekArtifact] = jlf_no_labor; job_to_labor_table[df::job_type::SeekInfant] = jlf_no_labor; job_to_labor_table[df::job_type::AttendParty] = jlf_no_labor; @@ -1293,14 +1293,14 @@ public: job_to_labor_table[df::job_type::TameVermin] = jlf_const(df::unit_labor::ANIMALTRAIN) ; job_to_labor_table[df::job_type::TameAnimal] = jlf_const(df::unit_labor::ANIMALTRAIN) ; job_to_labor_table[df::job_type::ChainAnimal] = jlf_no_labor; - job_to_labor_table[df::job_type::UnchainAnimal] = jlf_no_labor; + job_to_labor_table[df::job_type::UnchainAnimal] = jlf_const(df::unit_labor::HAUL_ANIMALS); job_to_labor_table[df::job_type::UnchainPet] = jlf_no_labor; - job_to_labor_table[df::job_type::ReleaseLargeCreature] = jlf_no_labor; + job_to_labor_table[df::job_type::ReleaseLargeCreature] = jlf_const(df::unit_labor::HAUL_ANIMALS); job_to_labor_table[df::job_type::ReleasePet] = jlf_no_labor; job_to_labor_table[df::job_type::ReleaseSmallCreature] = jlf_no_labor; job_to_labor_table[df::job_type::HandleSmallCreature] = jlf_no_labor; - job_to_labor_table[df::job_type::HandleLargeCreature] = jlf_no_labor; - job_to_labor_table[df::job_type::CageLargeCreature] = jlf_no_labor; + job_to_labor_table[df::job_type::HandleLargeCreature] = jlf_const(df::unit_labor::HAUL_ANIMALS); + job_to_labor_table[df::job_type::CageLargeCreature] = jlf_const(df::unit_labor::HAUL_ANIMALS); job_to_labor_table[df::job_type::CageSmallCreature] = jlf_no_labor; job_to_labor_table[df::job_type::RecoverWounded] = jlf_const(df::unit_labor::RECOVER_WOUNDED); job_to_labor_table[df::job_type::DiagnosePatient] = jlf_const(df::unit_labor::DIAGNOSE) ; @@ -1640,6 +1640,7 @@ private: int priority_food; std::map labor_needed; + std::map labor_in_use; std::map labor_outside; std::vector dwarf_info; std::list available_dwarfs; @@ -1668,6 +1669,7 @@ private: { df::building_tradedepotst* depot = (df::building_tradedepotst*) build; trader_requested = depot->trade_flags.bits.trader_requested; + if (print_debug) { if (trader_requested) @@ -1819,20 +1821,23 @@ private: } df::unit_labor labor = labor_mapper->find_job_labor (j); + labor_needed[labor]++; if (labor != df::unit_labor::NONE) { - labor_needed[labor]++; - - if (worker != -1) + if (worker == -1) + { + if (j->pos.isValid()) + { + df::tile_designation* d = Maps::getTileDesignation(j->pos); + if (d->bits.outside) + labor_outside[labor] = true; + } + } else { labor_infos[labor].mark_assigned(); + labor_in_use[labor]++; + } - if (j->pos.isValid()) - { - df::tile_designation* d = Maps::getTileDesignation(j->pos); - if (d->bits.outside) - labor_outside[labor] = true; - } } } @@ -1979,8 +1984,6 @@ private: } } } - if (state == OTHER) - dwarf->clear_all = true; } dwarf->state = state; @@ -2199,7 +2202,7 @@ public: // add job entries for health care labor_needed[df::unit_labor::RECOVER_WOUNDED] += cnt_recover_wounded; - labor_needed[df::unit_labor::DIAGNOSE] += cnt_diagnosis; + labor_needed[df::unit_labor::DIAGNOSE] += cnt_diagnosis; labor_needed[df::unit_labor::BONE_SETTING] += cnt_immobilize; labor_needed[df::unit_labor::DRESSING_WOUNDS] += cnt_dressing; labor_needed[df::unit_labor::DRESSING_WOUNDS] += cnt_cleaning; @@ -2260,43 +2263,14 @@ public: // note: this doesn't test to see if the trainer is actually needed, and thus will overallocate trainers. bleah. } - /* move idle dwarfs ready to be assigned to busy list */ - for (auto d = available_dwarfs.begin(); d != available_dwarfs.end(); ) - { - bool busy = false; + /* set requirements to zero for labors with currently idle dwarfs, and remove from requirement dwarfs actually working */ - FOR_ENUM_ITEMS(unit_labor, l) - { - if (l == df::unit_labor::NONE) - continue; - - - if (labor_needed[l] > 0 && (*d)->dwarf->status.labors[l]) - { - busy = true; - labor_needed[l] = max(labor_needed[l]-1, 0); - } - } - - if (busy) - { - busy_dwarfs.push_back(*d); - d = available_dwarfs.erase(d); - } else { - d++; - } - } - - /* adjust for over/under */ - FOR_ENUM_ITEMS(unit_labor, l) - { - if (l == df::unit_labor::NONE) - continue; - if (labor_infos[l].idle_dwarfs > 0) - labor_needed[l] = 0; - else - labor_needed[l] = std::max(labor_needed[l] - labor_infos[l].busy_dwarfs, 0); - } + FOR_ENUM_ITEMS(unit_labor, l) { + if (labor_infos[l].idle_dwarfs > 0) + labor_needed[l] = 0; + else + labor_needed[l] = max(0, labor_needed[l] - labor_in_use[l]); + } /* assign food haulers for rotting food items */ @@ -2401,7 +2375,6 @@ public: priority /= 2; pq.push(make_pair(priority, labor)); } - } int canary = (1 << df::unit_labor::HAUL_STONE) | @@ -2476,6 +2449,7 @@ public: to_assign[best_labor]--; } + busy_dwarfs.push_back(*bestdwarf); available_dwarfs.erase(bestdwarf); } @@ -2510,42 +2484,57 @@ public: ENUM_KEY_STR(unit_labor, (*d)->using_labor).c_str(), current_score); } } - else - (*d)->clear_labor (l); } } - if (canary != 0) - { - dwarf_info_t* d = 0; - - for (auto di = busy_dwarfs.begin(); di != busy_dwarfs.end(); di++) - if (!(*di)->clear_all) - { - d = *di; - break; - } + dwarf_info_t* canary_dwarf = 0; - if (d) + for (auto di = busy_dwarfs.begin(); di != busy_dwarfs.end(); di++) + if (!(*di)->clear_all) { - - FOR_ENUM_ITEMS (unit_labor, l) - { - if (l >= df::unit_labor::HAUL_STONE && l <= df::unit_labor::HAUL_ANIMALS && - canary & (1 << l)) - d->set_labor(l); - } - if (print_debug) - out.print ("Setting %s as the hauling canary\n", d->dwarf->name.first_name.c_str()); + canary_dwarf = *di; + break; } - else + + if (canary_dwarf) + { + + FOR_ENUM_ITEMS (unit_labor, l) { - if (print_debug) - out.print ("No dwarf available to set as the hauling canary!\n"); + if (l >= df::unit_labor::HAUL_STONE && l <= df::unit_labor::HAUL_ANIMALS && + canary & (1 << l)) + canary_dwarf->set_labor(l); } + + /* Also set the canary to remove constructions, because we have no way yet to tell if there are constructions needing removal */ + + canary_dwarf->set_labor(df::unit_labor::REMOVE_CONSTRUCTION); + + if (print_debug) + out.print ("Setting %s as the hauling canary\n", canary_dwarf->dwarf->name.first_name.c_str()); + } + else + { + if (print_debug) + out.print ("No dwarf available to set as the hauling canary!\n"); } + /* Assign any leftover dwarfs to "standard" labors */ + + for (auto d = available_dwarfs.begin(); d != available_dwarfs.end(); d++) + { + FOR_ENUM_ITEMS (unit_labor, l) + { + if (l >= df::unit_labor::HAUL_STONE && l <= df::unit_labor::HAUL_ANIMALS && + canary & (1 << l)) + (*d)->set_labor(l); + } + + (*d)->set_labor(df::unit_labor::CLEAN); + (*d)->set_labor(df::unit_labor::REMOVE_CONSTRUCTION); + } + /* set reequip on any dwarfs who are carrying tools needed by others */ for (auto d = dwarf_info.begin(); d != dwarf_info.end(); d++) From 705134975d492a00f76e33c6549924f7c7c738f3 Mon Sep 17 00:00:00 2001 From: Kelly Kinkade Date: Tue, 28 Jun 2016 22:57:12 -0500 Subject: [PATCH 14/22] labormanager: whitespace MSVC is evil. --- plugins/devel/labormanager.cpp | 60 +++++++++++++++++----------------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/plugins/devel/labormanager.cpp b/plugins/devel/labormanager.cpp index 256841cb7..8acae7524 100644 --- a/plugins/devel/labormanager.cpp +++ b/plugins/devel/labormanager.cpp @@ -1299,7 +1299,7 @@ public: job_to_labor_table[df::job_type::ReleasePet] = jlf_no_labor; job_to_labor_table[df::job_type::ReleaseSmallCreature] = jlf_no_labor; job_to_labor_table[df::job_type::HandleSmallCreature] = jlf_no_labor; - job_to_labor_table[df::job_type::HandleLargeCreature] = jlf_const(df::unit_labor::HAUL_ANIMALS); + job_to_labor_table[df::job_type::HandleLargeCreature] = jlf_const(df::unit_labor::HAUL_ANIMALS); job_to_labor_table[df::job_type::CageLargeCreature] = jlf_const(df::unit_labor::HAUL_ANIMALS); job_to_labor_table[df::job_type::CageSmallCreature] = jlf_no_labor; job_to_labor_table[df::job_type::RecoverWounded] = jlf_const(df::unit_labor::RECOVER_WOUNDED); @@ -1640,7 +1640,7 @@ private: int priority_food; std::map labor_needed; - std::map labor_in_use; + std::map labor_in_use; std::map labor_outside; std::vector dwarf_info; std::list available_dwarfs; @@ -1821,22 +1821,22 @@ private: } df::unit_labor labor = labor_mapper->find_job_labor (j); - labor_needed[labor]++; + labor_needed[labor]++; if (labor != df::unit_labor::NONE) { - if (worker == -1) - { - if (j->pos.isValid()) - { - df::tile_designation* d = Maps::getTileDesignation(j->pos); - if (d->bits.outside) - labor_outside[labor] = true; - } - } else { + if (worker == -1) + { + if (j->pos.isValid()) + { + df::tile_designation* d = Maps::getTileDesignation(j->pos); + if (d->bits.outside) + labor_outside[labor] = true; + } + } else { labor_infos[labor].mark_assigned(); - labor_in_use[labor]++; - } + labor_in_use[labor]++; + } } } @@ -2263,14 +2263,14 @@ public: // note: this doesn't test to see if the trainer is actually needed, and thus will overallocate trainers. bleah. } - /* set requirements to zero for labors with currently idle dwarfs, and remove from requirement dwarfs actually working */ + /* set requirements to zero for labors with currently idle dwarfs, and remove from requirement dwarfs actually working */ - FOR_ENUM_ITEMS(unit_labor, l) { - if (labor_infos[l].idle_dwarfs > 0) - labor_needed[l] = 0; - else - labor_needed[l] = max(0, labor_needed[l] - labor_in_use[l]); - } + FOR_ENUM_ITEMS(unit_labor, l) { + if (labor_infos[l].idle_dwarfs > 0) + labor_needed[l] = 0; + else + labor_needed[l] = max(0, labor_needed[l] - labor_in_use[l]); + } /* assign food haulers for rotting food items */ @@ -2449,7 +2449,7 @@ public: to_assign[best_labor]--; } - busy_dwarfs.push_back(*bestdwarf); + busy_dwarfs.push_back(*bestdwarf); available_dwarfs.erase(bestdwarf); } @@ -2507,9 +2507,9 @@ public: canary_dwarf->set_labor(l); } - /* Also set the canary to remove constructions, because we have no way yet to tell if there are constructions needing removal */ + /* Also set the canary to remove constructions, because we have no way yet to tell if there are constructions needing removal */ - canary_dwarf->set_labor(df::unit_labor::REMOVE_CONSTRUCTION); + canary_dwarf->set_labor(df::unit_labor::REMOVE_CONSTRUCTION); if (print_debug) out.print ("Setting %s as the hauling canary\n", canary_dwarf->dwarf->name.first_name.c_str()); @@ -2520,10 +2520,10 @@ public: out.print ("No dwarf available to set as the hauling canary!\n"); } - /* Assign any leftover dwarfs to "standard" labors */ + /* Assign any leftover dwarfs to "standard" labors */ - for (auto d = available_dwarfs.begin(); d != available_dwarfs.end(); d++) - { + for (auto d = available_dwarfs.begin(); d != available_dwarfs.end(); d++) + { FOR_ENUM_ITEMS (unit_labor, l) { if (l >= df::unit_labor::HAUL_STONE && l <= df::unit_labor::HAUL_ANIMALS && @@ -2531,9 +2531,9 @@ public: (*d)->set_labor(l); } - (*d)->set_labor(df::unit_labor::CLEAN); - (*d)->set_labor(df::unit_labor::REMOVE_CONSTRUCTION); - } + (*d)->set_labor(df::unit_labor::CLEAN); + (*d)->set_labor(df::unit_labor::REMOVE_CONSTRUCTION); + } /* set reequip on any dwarfs who are carrying tools needed by others */ From dbc46c510ff83c877ca36e9b0e4a976001eff733 Mon Sep 17 00:00:00 2001 From: Kelly Kinkade Date: Tue, 28 Jun 2016 23:28:44 -0500 Subject: [PATCH 15/22] labormanager: fix stupid --- plugins/devel/labormanager.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/plugins/devel/labormanager.cpp b/plugins/devel/labormanager.cpp index 8acae7524..d78d06b97 100644 --- a/plugins/devel/labormanager.cpp +++ b/plugins/devel/labormanager.cpp @@ -2337,6 +2337,9 @@ public: for (auto i = labor_needed.begin(); i != labor_needed.end(); i++) { df::unit_labor l = i->first; + if (l == df::unit_labor::NONE) + continue; + if (labor_infos[l].maximum_dwarfs() > 0 && i->second > labor_infos[l].maximum_dwarfs()) i->second = labor_infos[l].maximum_dwarfs(); From 3a0ba332d9764dfbaa45c1bc0db4dca0abeead43 Mon Sep 17 00:00:00 2001 From: Kelly Kinkade Date: Wed, 29 Jun 2016 14:54:03 -0500 Subject: [PATCH 16/22] labormanager: rework for better behavior with 43.03 The main thing here is that the process loop exits if the DF process_job or process_dig flags are set since if these are set the job list is going to change soon anyway. The plugin also sets these flags when it changes any labors, which has the side effect of effectively disabling the process loop while DF is paused, which prevents flapping while editing job preferences in-game, and also allows changing job preferences in game (although such changes may not last when the clock starts up again). --- plugins/devel/labormanager.cpp | 78 +++++++++++++++++++--------------- 1 file changed, 43 insertions(+), 35 deletions(-) diff --git a/plugins/devel/labormanager.cpp b/plugins/devel/labormanager.cpp index d78d06b97..8d614ed36 100644 --- a/plugins/devel/labormanager.cpp +++ b/plugins/devel/labormanager.cpp @@ -528,23 +528,6 @@ struct dwarf_info_t { } - - void set_labor(df::unit_labor labor) - { - if (labor >= 0 && labor <= ENUM_LAST_ITEM(unit_labor)) - { - dwarf->status.labors[labor] = true; - } - } - - void clear_labor(df::unit_labor labor) - { - if (labor >= 0 && labor <= ENUM_LAST_ITEM(unit_labor)) - { - dwarf->status.labors[labor] = false; - } - } - }; /* @@ -1621,6 +1604,8 @@ private: int plant_count; int detail_count; + bool labors_changed; + int tool_count[TOOLS_MAX]; bool reequip_needed[TOOLS_MAX]; @@ -1647,11 +1632,23 @@ private: std::list busy_dwarfs; private: + void set_labor (dwarf_info_t* dwarf, df::unit_labor labor, bool value) + { + if (labor >= 0 && labor <= ENUM_LAST_ITEM(unit_labor)) + { + bool old = dwarf->dwarf->status.labors[labor]; + dwarf->dwarf->status.labors[labor] = value; + if (old != value) + labors_changed = true; + } + } + void scan_buildings() { has_butchers = false; has_fishery = false; trader_requested = false; + labors_changed = false; for (auto b = world->buildings.all.begin(); b != world->buildings.all.end(); b++) { @@ -1821,10 +1818,10 @@ private: } df::unit_labor labor = labor_mapper->find_job_labor (j); - labor_needed[labor]++; if (labor != df::unit_labor::NONE) { + labor_needed[labor]++; if (worker == -1) { if (j->pos.isValid()) @@ -2088,7 +2085,7 @@ private: if (labor == unit_labor::NONE) continue; - dwarf->clear_labor(labor); + set_labor(dwarf, labor, false); } } else { if (state == IDLE) @@ -2152,6 +2149,9 @@ private: public: void process() { + if (*df::global::process_dig || *df::global::process_jobs) + return; + release_dwarf_list(); dig_count = tree_count = plant_count = detail_count = 0; @@ -2266,6 +2266,9 @@ public: /* set requirements to zero for labors with currently idle dwarfs, and remove from requirement dwarfs actually working */ FOR_ENUM_ITEMS(unit_labor, l) { + if (l == df::unit_labor::NONE) + continue; + if (labor_infos[l].idle_dwarfs > 0) labor_needed[l] = 0; else @@ -2309,10 +2312,7 @@ public: if (l == df::unit_labor::NONE) continue; - if (l == df::unit_labor::HAUL_FOOD) - (*bestdwarf)->set_labor(l); - else - (*bestdwarf)->clear_labor(l); + set_labor (*bestdwarf, l, l == df::unit_labor::HAUL_FOOD); } available_dwarfs.erase(bestdwarf); @@ -2337,7 +2337,7 @@ public: for (auto i = labor_needed.begin(); i != labor_needed.end(); i++) { df::unit_labor l = i->first; - if (l == df::unit_labor::NONE) + if (l == df::unit_labor::NONE) continue; if (labor_infos[l].maximum_dwarfs() > 0 && @@ -2394,7 +2394,7 @@ public: std::list::iterator bestdwarf = available_dwarfs.begin(); int best_score = INT_MIN; - df::unit_labor best_labor = df::unit_labor::CLEAN; + df::unit_labor best_labor = df::unit_labor::NONE; for (auto j = to_assign.begin(); j != to_assign.end(); j++) { @@ -2416,6 +2416,9 @@ public: } } + if (best_labor == df::unit_labor::NONE) + break; + if (print_debug) out.print("assign \"%s\" labor %s score=%d\n", (*bestdwarf)->dwarf->name.first_name.c_str(), ENUM_KEY_STR(unit_labor, best_labor).c_str(), best_score); @@ -2426,7 +2429,7 @@ public: if (l == best_labor) { - (*bestdwarf)->set_labor(l); + set_labor(*bestdwarf, l, true); tools_enum t = default_labor_infos[l].tool; if (t != TOOL_NONE) { @@ -2436,7 +2439,7 @@ public: } } else if ((*bestdwarf)->state == IDLE) - (*bestdwarf)->clear_labor(l); + set_labor(*bestdwarf, l, false); } if (best_labor == df::unit_labor::HAUL_FOOD && priority_food > 0) @@ -2479,7 +2482,7 @@ public: tools_enum t = default_labor_infos[l].tool; if (t == TOOL_NONE || (*d)->has_tool[t]) { - (*d)->set_labor (l); + set_labor(*d, l, true); if (print_debug) out.print("assign \"%s\" extra labor %s score=%d current %s score=%d\n", (*d)->dwarf->name.first_name.c_str(), @@ -2507,12 +2510,12 @@ public: { if (l >= df::unit_labor::HAUL_STONE && l <= df::unit_labor::HAUL_ANIMALS && canary & (1 << l)) - canary_dwarf->set_labor(l); + set_labor(canary_dwarf, l, true); } /* Also set the canary to remove constructions, because we have no way yet to tell if there are constructions needing removal */ - canary_dwarf->set_labor(df::unit_labor::REMOVE_CONSTRUCTION); + set_labor(canary_dwarf, df::unit_labor::REMOVE_CONSTRUCTION, true); if (print_debug) out.print ("Setting %s as the hauling canary\n", canary_dwarf->dwarf->name.first_name.c_str()); @@ -2531,11 +2534,12 @@ public: { if (l >= df::unit_labor::HAUL_STONE && l <= df::unit_labor::HAUL_ANIMALS && canary & (1 << l)) - (*d)->set_labor(l); + set_labor(*d, l, true); + else if (l == df::unit_labor::CLEAN || l == df::unit_labor::REMOVE_CONSTRUCTION) + set_labor(*d, l, true); + else + set_labor(*d, l, false); } - - (*d)->set_labor(df::unit_labor::CLEAN); - (*d)->set_labor(df::unit_labor::REMOVE_CONSTRUCTION); } /* set reequip on any dwarfs who are carrying tools needed by others */ @@ -2558,7 +2562,11 @@ public: release_dwarf_list(); - *df::global::process_jobs = true; + if (labors_changed) + { + *df::global::process_dig = true; + *df::global::process_jobs = true; + } print_debug = 0; From 0509c455ddd2bb3fae6fbea4bd77a642a590e57d Mon Sep 17 00:00:00 2001 From: Kelly Kinkade Date: Thu, 30 Jun 2016 23:58:56 -0500 Subject: [PATCH 17/22] labormanager: significant restructuring to use job posting list Updated here to get potential jobs off the job posting lists, which is apparently where certain map-designated live after being designated but before they move to the actual job list. Also changes to how tools are handled, and lever pulling is assigned by default to all idle dwarfs. --- plugins/devel/labormanager.cpp | 273 ++++++++++++++++++++------------- 1 file changed, 169 insertions(+), 104 deletions(-) diff --git a/plugins/devel/labormanager.cpp b/plugins/devel/labormanager.cpp index 8d614ed36..545ca7f1c 100644 --- a/plugins/devel/labormanager.cpp +++ b/plugins/devel/labormanager.cpp @@ -184,7 +184,7 @@ static const dwarf_state dwarf_states[] = { OTHER /* GoShopping2 */, BUSY /* Clean */, OTHER /* Rest */, - BUSY /* PickupEquipment */, + OTHER /* PickupEquipment */, BUSY /* DumpItem */, OTHER /* StrangeMoodCrafter */, OTHER /* StrangeMoodJeweller */, @@ -414,7 +414,7 @@ struct labor_default static std::vector labor_infos; static const struct labor_default default_labor_infos[] = { - /* MINE */ {200, 0, TOOL_PICK}, + /* MINEa */ {200, 0, TOOL_PICK}, /* HAUL_STONE */ {100, 0, TOOL_NONE}, /* HAUL_WOOD */ {100, 0, TOOL_NONE}, /* HAUL_BODY */ {200, 0, TOOL_NONE}, @@ -1607,7 +1607,7 @@ private: bool labors_changed; int tool_count[TOOLS_MAX]; - bool reequip_needed[TOOLS_MAX]; + int tool_in_use[TOOLS_MAX]; int cnt_recover_wounded; int cnt_diagnosis; @@ -1639,7 +1639,69 @@ private: bool old = dwarf->dwarf->status.labors[labor]; dwarf->dwarf->status.labors[labor] = value; if (old != value) + { labors_changed = true; + + tools_enum tool = default_labor_infos[labor].tool; + if (tool != TOOL_NONE) + tool_in_use[tool] += value ? 1 : -1; + } + } + } + + void process_job (df::job* j) + { + if (j->flags.bits.suspend || j->flags.bits.item_lost) + return; + + int worker = -1; + int bld = -1; + + for (int r = 0; r < j->general_refs.size(); ++r) + { + if (j->general_refs[r]->getType() == df::general_ref_type::UNIT_WORKER) + worker = ((df::general_ref_unit_workerst *)(j->general_refs[r]))->unit_id; + if (j->general_refs[r]->getType() == df::general_ref_type::BUILDING_HOLDER) + bld = ((df::general_ref_building_holderst *)(j->general_refs[r]))->building_id; + } + + if (bld != -1) + { + df::building* b = binsearch_in_vector(world->buildings.all, bld); + int fjid = -1; + for (int jn = 0; jn < b->jobs.size(); jn++) + { + if (b->jobs[jn]->flags.bits.suspend) + continue; + fjid = b->jobs[jn]->id; + break; + } + // check if this job is the first nonsuspended job on this building; if not, ignore it + // (except for farms and trade depots) + if (fjid != j->id && + b->getType() != df::building_type::FarmPlot && + b->getType() != df::building_type::TradeDepot) + return; + } + + df::unit_labor labor = labor_mapper->find_job_labor (j); + + if (labor != df::unit_labor::NONE) + { + labor_needed[labor]++; + if (worker == -1) + { + if (j->pos.isValid()) + { + df::tile_designation* d = Maps::getTileDesignation(j->pos); + if (d->bits.outside) + labor_outside[labor] = true; + } + } else { + labor_infos[labor].mark_assigned(); + labor_in_use[labor]++; + } + } } @@ -1674,6 +1736,7 @@ private: else out.print("Trade depot found but trader is not requested.\n"); } + } } } @@ -1729,7 +1792,10 @@ private: void count_tools() { for (int e = TOOL_NONE; e < TOOLS_MAX; e++) + { tool_count[e] = 0; + tool_in_use[e] = 0; + } priority_food = 0; @@ -1776,66 +1842,20 @@ private: void collect_job_list() { - labor_needed.clear(); - for (df::job_list_link* jll = world->job_list.next; jll; jll = jll->next) { df::job* j = jll->item; if (!j) continue; + process_job(j); + } - if (j->flags.bits.suspend || j->flags.bits.item_lost) + for (auto jp = world->job_postings.begin(); jp != world->job_postings.end(); jp++) + { + if ((*jp)->flags.bits.dead) continue; - int worker = -1; - int bld = -1; - - for (int r = 0; r < j->general_refs.size(); ++r) - { - if (j->general_refs[r]->getType() == df::general_ref_type::UNIT_WORKER) - worker = ((df::general_ref_unit_workerst *)(j->general_refs[r]))->unit_id; - if (j->general_refs[r]->getType() == df::general_ref_type::BUILDING_HOLDER) - bld = ((df::general_ref_building_holderst *)(j->general_refs[r]))->building_id; - } - - if (bld != -1) - { - df::building* b = binsearch_in_vector(world->buildings.all, bld); - int fjid = -1; - for (int jn = 0; jn < b->jobs.size(); jn++) - { - if (b->jobs[jn]->flags.bits.suspend) - continue; - fjid = b->jobs[jn]->id; - break; - } - // check if this job is the first nonsuspended job on this building; if not, ignore it - // (except for farms) - if (fjid != j->id && b->getType() != df::building_type::FarmPlot) { - continue; - } - - } - - df::unit_labor labor = labor_mapper->find_job_labor (j); - - if (labor != df::unit_labor::NONE) - { - labor_needed[labor]++; - if (worker == -1) - { - if (j->pos.isValid()) - { - df::tile_designation* d = Maps::getTileDesignation(j->pos); - if (d->bits.outside) - labor_outside[labor] = true; - } - } else { - labor_infos[labor].mark_assigned(); - labor_in_use[labor]++; - } - - } + process_job((*jp)->job); } } @@ -1909,6 +1929,32 @@ private: } } + // check if dwarf has an axe, pick, or crossbow + + for (int j = 0; j < dwarf->dwarf->inventory.size(); j++) + { + df::unit_inventory_item* ui = dwarf->dwarf->inventory[j]; + if (ui->mode == df::unit_inventory_item::Weapon && ui->item->isWeapon()) + { + dwarf->armed = true; + df::itemdef_weaponst* weapondef = ((df::item_weaponst*)(ui->item))->subtype; + df::job_skill weaponsk = (df::job_skill) weapondef->skill_melee; + df::job_skill rangesk = (df::job_skill) weapondef->skill_ranged; + if (weaponsk == df::job_skill::AXE) + { + dwarf->has_tool[TOOL_AXE] = true; + } + else if (weaponsk == df::job_skill::MINING) + { + dwarf->has_tool[TOOL_PICK] = true; + } + else if (rangesk == df::job_skill::CROSSBOW) + { + dwarf->has_tool[TOOL_CROSSBOW] = true; + } + } + } + // Find the activity state for each dwarf bool is_on_break = false; @@ -1972,12 +2018,9 @@ private: if (labor != df::unit_labor::NONE) { labor_infos[labor].busy_dwarfs++; - - if (!dwarf->dwarf->status.labors[labor]) + if (default_labor_infos[labor].tool != TOOL_NONE) { - out.print("LABORMANAGER: dwarf %s (id %d) is doing job %s(%d) but is not enabled for labor %s(%d).\n", - dwarf->dwarf->name.first_name.c_str(), dwarf->dwarf->id, - ENUM_KEY_STR(job_type, job).c_str(), job, ENUM_KEY_STR(unit_labor, labor).c_str(), labor); + tool_in_use[default_labor_infos[labor].tool]++; } } } @@ -2044,37 +2087,7 @@ private: } dwarf->high_skill = high_skill; - // check if dwarf has an axe, pick, or crossbow - for (int j = 0; j < dwarf->dwarf->inventory.size(); j++) - { - df::unit_inventory_item* ui = dwarf->dwarf->inventory[j]; - if (ui->mode == df::unit_inventory_item::Weapon && ui->item->isWeapon()) - { - dwarf->armed = true; - df::itemdef_weaponst* weapondef = ((df::item_weaponst*)(ui->item))->subtype; - df::job_skill weaponsk = (df::job_skill) weapondef->skill_melee; - df::job_skill rangesk = (df::job_skill) weapondef->skill_ranged; - if (weaponsk == df::job_skill::AXE) - { - dwarf->has_tool[TOOL_AXE] = true; - if (state != IDLE) - tool_count[TOOL_AXE]--; - } - else if (weaponsk == df::job_skill::MINING) - { - dwarf->has_tool[TOOL_PICK] = 1; - if (state != IDLE) - tool_count[TOOL_PICK]--; - } - else if (rangesk == df::job_skill::CROSSBOW) - { - dwarf->has_tool[TOOL_CROSSBOW] = 1; - if (state != IDLE) - tool_count[TOOL_CROSSBOW]--; - } - } - } // clear labors of dwarfs with clear_all set @@ -2136,7 +2149,10 @@ private: score += 1000; if (default_labor_infos[labor].tool != TOOL_NONE && d->has_tool[default_labor_infos[labor].tool]) - score += 5000; + score += 30000; + if (default_labor_infos[labor].tool != TOOL_NONE && + !d->has_tool[default_labor_infos[labor].tool]) + score -= 30000; if (d->has_children && labor_outside[labor]) score -= 15000; if (d->armed && labor_outside[labor]) @@ -2159,6 +2175,8 @@ public: cnt_setting = cnt_traction = cnt_crutch = 0; need_food_water = 0; + labor_needed.clear(); + for (int e = 0; e < TOOLS_MAX; e++) tool_count[e] = 0; @@ -2194,8 +2212,8 @@ public: // add job entries for designation-related jobs - labor_needed[df::unit_labor::MINE] += std::min(tool_count[TOOL_PICK], dig_count); - labor_needed[df::unit_labor::CUTWOOD] += std::min(tool_count[TOOL_AXE], tree_count); + labor_needed[df::unit_labor::MINE] += dig_count; + labor_needed[df::unit_labor::CUTWOOD] += tree_count; labor_needed[df::unit_labor::DETAIL] += detail_count; labor_needed[df::unit_labor::HERBALIST] += plant_count; @@ -2269,10 +2287,19 @@ public: if (l == df::unit_labor::NONE) continue; + int before = labor_needed[l]; + if (labor_infos[l].idle_dwarfs > 0) labor_needed[l] = 0; else labor_needed[l] = max(0, labor_needed[l] - labor_in_use[l]); + + if (default_labor_infos[l].tool != TOOL_NONE) + labor_needed[l] = std::min(labor_needed[l], tool_count[default_labor_infos[l].tool] - tool_in_use[default_labor_infos[l].tool]); + + if (print_debug && before != labor_needed[l]) + out.print ("labor %s reduced from %d to %d\n", ENUM_KEY_STR(unit_labor, l).c_str(), before, labor_needed[l]); + } /* assign food haulers for rotting food items */ @@ -2427,19 +2454,30 @@ public: if (l == df::unit_labor::NONE) continue; - if (l == best_labor) + tools_enum t = default_labor_infos[l].tool; + + if (l == best_labor && ( t == TOOL_NONE || tool_in_use[t] < tool_count[t]) ) { set_labor(*bestdwarf, l, true); - tools_enum t = default_labor_infos[l].tool; - if (t != TOOL_NONE) + if (t != TOOL_NONE && (*bestdwarf)->has_tool[t]) { - tool_count[t]--; - if (!(*bestdwarf)->has_tool[t]) - (*bestdwarf)->dwarf->military.pickup_flags.bits.update = true; + df::job_type j; + j = df::job_type::NONE; + + if ((*bestdwarf)->dwarf->job.current_job) + j = (*bestdwarf)->dwarf->job.current_job->job_type; + + if (print_debug) + out.print("LABORMANAGER: asking %s to pick up tools, current job %s\n", (*bestdwarf)->dwarf->name.first_name.c_str(), ENUM_KEY_STR(job_type, j).c_str()); + + (*bestdwarf)->dwarf->military.pickup_flags.bits.update = true; + labors_changed = true; } } else if ((*bestdwarf)->state == IDLE) + { set_labor(*bestdwarf, l, false); + } } if (best_labor == df::unit_labor::HAUL_FOOD && priority_food > 0) @@ -2450,7 +2488,6 @@ public: if (best_labor != df::unit_labor::NONE) { - busy_dwarfs.push_back(*bestdwarf); labor_infos[best_labor].active_dwarfs++; to_assign[best_labor]--; } @@ -2489,6 +2526,8 @@ public: ENUM_KEY_STR(unit_labor, l).c_str(), score, ENUM_KEY_STR(unit_labor, (*d)->using_labor).c_str(), current_score); } + if ((*d)->using_labor != df::unit_labor::NONE && score > current_score + 5000) + set_labor(*d, (*d)->using_labor, false); } } } @@ -2535,7 +2574,7 @@ public: if (l >= df::unit_labor::HAUL_STONE && l <= df::unit_labor::HAUL_ANIMALS && canary & (1 << l)) set_labor(*d, l, true); - else if (l == df::unit_labor::CLEAN || l == df::unit_labor::REMOVE_CONSTRUCTION) + else if (l == df::unit_labor::CLEAN || l == df::unit_labor::REMOVE_CONSTRUCTION || l == df::unit_labor::PULL_LEVER) set_labor(*d, l, true); else set_labor(*d, l, false); @@ -2546,16 +2585,42 @@ public: for (auto d = dwarf_info.begin(); d != dwarf_info.end(); d++) { + if ((*d)->dwarf->job.current_job && (*d)->dwarf->job.current_job->job_type == df::job_type::PickupEquipment) + continue; + + if ((*d)->dwarf->military.pickup_flags.bits.update) + continue; + FOR_ENUM_ITEMS (unit_labor, l) { if (l == df::unit_labor::NONE) continue; tools_enum t = default_labor_infos[l].tool; - if (t != TOOL_NONE && tool_count[t] < 0 && (*d)->has_tool[t] && !(*d)->dwarf->status.labors[l]) + if (t == TOOL_NONE) + continue; + + bool has_tool = (*d)->has_tool[t]; + bool needs_tool = (*d)->dwarf->status.labors[l]; + + if (has_tool != needs_tool) { - tool_count[t]++; - (*d)->dwarf->military.pickup_flags.bits.update = 1; + if (has_tool && tool_count[t] > tool_in_use[t]) + continue; + + df::job_type j = df::job_type::NONE; + + if ((*d)->dwarf->job.current_job) + j = (*d)->dwarf->job.current_job->job_type; + + if (print_debug) + out.print("LABORMANAGER: asking %s to %s tools, current job %s, %d %d \n", (*d)->dwarf->name.first_name.c_str(), (has_tool) ? "drop" : "pick up", ENUM_KEY_STR(job_type, j).c_str(), has_tool, needs_tool); + + (*d)->dwarf->military.pickup_flags.bits.update = true; + labors_changed = true; + + if (needs_tool) + tool_in_use[t]++; } } } From 808afca9f01553888e82cec1ec69b25534a36fd8 Mon Sep 17 00:00:00 2001 From: Kelly Kinkade Date: Tue, 5 Jul 2016 13:16:34 -0500 Subject: [PATCH 18/22] labormanager: add StoreItemInLocation labor, reduce tool churn Note: this commit requires updated df-structures (77968973b28d0e828f880d119a700abb079f3521 or later) --- plugins/devel/labormanager.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/plugins/devel/labormanager.cpp b/plugins/devel/labormanager.cpp index 545ca7f1c..98b7f0f6f 100644 --- a/plugins/devel/labormanager.cpp +++ b/plugins/devel/labormanager.cpp @@ -1359,6 +1359,8 @@ public: job_to_labor_table[df::job_type::MakeEarring] = jlf_make_object; job_to_labor_table[df::job_type::MakeBracelet] = jlf_make_object; job_to_labor_table[df::job_type::MakeGem] = jlf_make_object; + + job_to_labor_table[df::job_type::StoreItemInLocation] = jlf_no_labor; // StoreItemInLocation }; df::unit_labor find_job_labor(df::job* j) @@ -1380,7 +1382,7 @@ public: df::unit_labor labor; if (job_to_labor_table.count(j->job_type) == 0) { - debug("LABORMANAGER: job has no job to labor table entry: %s\n", ENUM_KEY_STR(job_type, j->job_type).c_str()); + debug("LABORMANAGER: job has no job to labor table entry: %s (%d)\n", ENUM_KEY_STR(job_type, j->job_type).c_str(), j->job_type); debug_pause(); labor = df::unit_labor::NONE; } else { @@ -2526,7 +2528,7 @@ public: ENUM_KEY_STR(unit_labor, l).c_str(), score, ENUM_KEY_STR(unit_labor, (*d)->using_labor).c_str(), current_score); } - if ((*d)->using_labor != df::unit_labor::NONE && score > current_score + 5000) + if ((*d)->using_labor != df::unit_labor::NONE && score > current_score + 5000 && default_labor_infos[(*d)->using_labor].tool == TOOL_NONE) set_labor(*d, (*d)->using_labor, false); } } From 874a97ed9f8daf33c98c88d166c09fd183b6191b Mon Sep 17 00:00:00 2001 From: Kelly Kinkade Date: Thu, 7 Jul 2016 02:10:43 -0500 Subject: [PATCH 19/22] labormanager: fix several job-to-labor mappings --- plugins/devel/labormanager.cpp | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/plugins/devel/labormanager.cpp b/plugins/devel/labormanager.cpp index 98b7f0f6f..46322a90c 100644 --- a/plugins/devel/labormanager.cpp +++ b/plugins/devel/labormanager.cpp @@ -1135,7 +1135,7 @@ public: job_to_labor_table[df::job_type::DigChannel] = jlf_const(df::unit_labor::MINE); job_to_labor_table[df::job_type::FellTree] = jlf_const(df::unit_labor::CUTWOOD); job_to_labor_table[df::job_type::GatherPlants] = jlf_const(df::unit_labor::HERBALIST); - job_to_labor_table[df::job_type::RemoveConstruction] = jlf_no_labor; + job_to_labor_table[df::job_type::RemoveConstruction] = jlf_const(df::unit_labor::REMOVE_CONSTRUCTION); job_to_labor_table[df::job_type::CollectWebs] = jlf_const(df::unit_labor::WEAVER); job_to_labor_table[df::job_type::BringItemToDepot] = jlf_const(df::unit_labor::HAUL_TRADE); job_to_labor_table[df::job_type::BringItemToShop] = jlf_no_labor; @@ -1269,13 +1269,13 @@ public: job_to_labor_table[df::job_type::CleanTrap] = jlf_const(df::unit_labor::MECHANIC) ; job_to_labor_table[df::job_type::CastSpell] = jlf_no_labor; job_to_labor_table[df::job_type::LinkBuildingToTrigger] = jlf_const(df::unit_labor::MECHANIC) ; - job_to_labor_table[df::job_type::PullLever] = jlf_no_labor; + job_to_labor_table[df::job_type::PullLever] = jlf_const(df::unit_labor::PULL_LEVER); job_to_labor_table[df::job_type::ExtractFromPlants] = jlf_const(df::unit_labor::HERBALIST) ; job_to_labor_table[df::job_type::ExtractFromRawFish] = jlf_const(df::unit_labor::DISSECT_FISH) ; job_to_labor_table[df::job_type::ExtractFromLandAnimal] = jlf_const(df::unit_labor::DISSECT_VERMIN) ; job_to_labor_table[df::job_type::TameVermin] = jlf_const(df::unit_labor::ANIMALTRAIN) ; job_to_labor_table[df::job_type::TameAnimal] = jlf_const(df::unit_labor::ANIMALTRAIN) ; - job_to_labor_table[df::job_type::ChainAnimal] = jlf_no_labor; + job_to_labor_table[df::job_type::ChainAnimal] = jlf_const(df::unit_labor::HAUL_ANIMALS); job_to_labor_table[df::job_type::UnchainAnimal] = jlf_const(df::unit_labor::HAUL_ANIMALS); job_to_labor_table[df::job_type::UnchainPet] = jlf_no_labor; job_to_labor_table[df::job_type::ReleaseLargeCreature] = jlf_const(df::unit_labor::HAUL_ANIMALS); @@ -1335,7 +1335,7 @@ public: job_to_labor_table[df::job_type::EngraveSlab] = jlf_const(df::unit_labor::DETAIL); job_to_labor_table[df::job_type::ShearCreature] = jlf_const(df::unit_labor::SHEARER); job_to_labor_table[df::job_type::SpinThread] = jlf_const(df::unit_labor::SPINNER); - job_to_labor_table[df::job_type::PenLargeAnimal] = jlf_no_labor; + job_to_labor_table[df::job_type::PenLargeAnimal] = jlf_const(df::unit_labor::HAUL_ANIMALS); job_to_labor_table[df::job_type::PenSmallAnimal] = jlf_no_labor; job_to_labor_table[df::job_type::MakeTool] = jlf_make_furniture; job_to_labor_table[df::job_type::CollectClay] = jlf_const(df::unit_labor::POTTERY); @@ -2291,10 +2291,7 @@ public: int before = labor_needed[l]; - if (labor_infos[l].idle_dwarfs > 0) - labor_needed[l] = 0; - else - labor_needed[l] = max(0, labor_needed[l] - labor_in_use[l]); + labor_needed[l] = max(0, labor_needed[l] - labor_in_use[l]); if (default_labor_infos[l].tool != TOOL_NONE) labor_needed[l] = std::min(labor_needed[l], tool_count[default_labor_infos[l].tool] - tool_in_use[default_labor_infos[l].tool]); From 74f6f3d416bc785fa4d961fb6ce9cd244051d0cd Mon Sep 17 00:00:00 2001 From: Kelly Kinkade Date: Mon, 11 Jul 2016 22:29:38 -0500 Subject: [PATCH 20/22] labormanager: add labors for bookcase (de)construct --- plugins/devel/labormanager.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugins/devel/labormanager.cpp b/plugins/devel/labormanager.cpp index 46322a90c..cb5f11dfc 100644 --- a/plugins/devel/labormanager.cpp +++ b/plugins/devel/labormanager.cpp @@ -840,6 +840,7 @@ private: case df::building_type::BarsFloor: case df::building_type::BarsVertical: case df::building_type::GrateWall: + case df::building_type::Bookcase: return df::unit_labor::HAUL_FURNITURE; case df::building_type::Trap: case df::building_type::GearAssembly: @@ -942,6 +943,7 @@ private: case df::building_type::BarsVertical: case df::building_type::GrateFloor: case df::building_type::GrateWall: + case df::building_type::Bookcase: return df::unit_labor::HAUL_FURNITURE; case df::building_type::AnimalTrap: return df::unit_labor::TRAPPER; From 5ab930ba4dc7b6614aea5ad6c9c417b55a47d2e0 Mon Sep 17 00:00:00 2001 From: Kelly Kinkade Date: Thu, 11 Aug 2016 16:56:34 -0500 Subject: [PATCH 21/22] labormanager: small tweaks Handle cloth crafts correctly; handle pit jobs correctly; handle bookcase construction correctly; deal with new break behavior better; change assignment of clean labor; tweak hauling assignments slightly to avoid overallocation; assign pull lever to everyone and clean to all nonbusy dwarfs --- plugins/devel/labormanager.cpp | 73 ++++++++++++++++++++++++++-------- 1 file changed, 56 insertions(+), 17 deletions(-) diff --git a/plugins/devel/labormanager.cpp b/plugins/devel/labormanager.cpp index cb5f11dfc..edae051dc 100644 --- a/plugins/devel/labormanager.cpp +++ b/plugins/devel/labormanager.cpp @@ -999,6 +999,8 @@ private: } case df::item_type::WOOD: return df::unit_labor::WOOD_CRAFT; + case df::item_type::CLOTH: + return df::unit_labor::CLOTHESMAKER; default: debug ("LABORMANAGER: Cannot deduce labor for make crafts job, item type %s\n", ENUM_KEY_STR(item_type, jobitem).c_str()); @@ -1304,7 +1306,7 @@ public: job_to_labor_table[df::job_type::GiveWater2] = jlf_no_labor; job_to_labor_table[df::job_type::GiveFood2] = jlf_no_labor; job_to_labor_table[df::job_type::RecoverPet] = jlf_no_labor; - job_to_labor_table[df::job_type::PitLargeAnimal] = jlf_no_labor; + job_to_labor_table[df::job_type::PitLargeAnimal] = jlf_const(df::unit_labor::HAUL_ANIMALS); job_to_labor_table[df::job_type::PitSmallAnimal] = jlf_no_labor; job_to_labor_table[df::job_type::SlaughterAnimal] = jlf_const(df::unit_labor::BUTCHER); job_to_labor_table[df::job_type::MakeCharcoal] = jlf_const(df::unit_labor::BURN_WOOD); @@ -1339,7 +1341,7 @@ public: job_to_labor_table[df::job_type::SpinThread] = jlf_const(df::unit_labor::SPINNER); job_to_labor_table[df::job_type::PenLargeAnimal] = jlf_const(df::unit_labor::HAUL_ANIMALS); job_to_labor_table[df::job_type::PenSmallAnimal] = jlf_no_labor; - job_to_labor_table[df::job_type::MakeTool] = jlf_make_furniture; + job_to_labor_table[df::job_type::MakeTool] = jlf_make_object; job_to_labor_table[df::job_type::CollectClay] = jlf_const(df::unit_labor::POTTERY); job_to_labor_table[df::job_type::InstallColonyInHive] = jlf_const(df::unit_labor::BEEKEEPING); job_to_labor_table[df::job_type::CollectHiveProducts] = jlf_const(df::unit_labor::BEEKEEPING); @@ -1551,7 +1553,7 @@ DFhackCExport command_result plugin_init ( color_ostream &out, std::vector dwarf->status.misc_traits.begin(); p < dwarf->dwarf->status.misc_traits.end(); p++) { - if ((*p)->id == misc_trait_type::Migrant || (*p)->id == misc_trait_type::OnBreak) - is_on_break = true; + if ((*p)->id == misc_trait_type::Migrant) + is_migrant = true; + } + + if (dwarf->dwarf->social_activities.size() > 0) + { + if (print_debug) + out.print ("Dwarf %s is engaged in a social activity. Info only.\n", dwarf->dwarf->name.first_name.c_str()); } if (dwarf->dwarf->profession == profession::BABY || @@ -1985,7 +1993,7 @@ private: else if (dwarf->dwarf->job.current_job == NULL) { - if (is_on_break || dwarf->dwarf->flags1.bits.chained || dwarf->dwarf->flags1.bits.caged) + if (is_migrant || dwarf->dwarf->flags1.bits.chained || dwarf->dwarf->flags1.bits.caged) { state = OTHER; dwarf->clear_all = true; @@ -2351,6 +2359,8 @@ public: } + labor_needed[df::unit_labor::CLEAN] = 1; + if (print_debug) { for (auto i = labor_needed.begin(); i != labor_needed.end(); i++) @@ -2374,7 +2384,10 @@ public: if (i->second > 0) { int priority = labor_infos[l].priority(); - priority += labor_infos[l].time_since_last_assigned()/12; + + if (l < df::unit_labor::HAUL_STONE || l > df::unit_labor::HAUL_ANIMALS) + priority += labor_infos[l].time_since_last_assigned()/12; + for (int n = 0; n < labor_infos[l].busy_dwarfs; n++) priority /= 2; pq.push(make_pair(priority, l)); @@ -2511,7 +2524,8 @@ public: continue; int score = score_labor (*d, l); - score += labor_infos[l].time_since_last_assigned()/12; + if (l < df::unit_labor::HAUL_STONE || l > df::unit_labor::HAUL_ANIMALS) + score += labor_infos[l].time_since_last_assigned()/12; if (l == df::unit_labor::HAUL_FOOD && priority_food > 0) score += 1000000; @@ -2521,11 +2535,6 @@ public: if (t == TOOL_NONE || (*d)->has_tool[t]) { set_labor(*d, l, true); - if (print_debug) - out.print("assign \"%s\" extra labor %s score=%d current %s score=%d\n", - (*d)->dwarf->name.first_name.c_str(), - ENUM_KEY_STR(unit_labor, l).c_str(), score, - ENUM_KEY_STR(unit_labor, (*d)->using_labor).c_str(), current_score); } if ((*d)->using_labor != df::unit_labor::NONE && score > current_score + 5000 && default_labor_infos[(*d)->using_labor].tool == TOOL_NONE) set_labor(*d, (*d)->using_labor, false); @@ -2582,6 +2591,39 @@ public: } } + /* check for dwarfs assigned no labors and assign them the bucket list if there are */ + for (auto d = dwarf_info.begin(); d != dwarf_info.end(); d++) + { + if ((*d)->state == CHILD) + continue; + + bool any = false; + FOR_ENUM_ITEMS (unit_labor, l) + { + if (l == df::unit_labor::NONE) + continue; + if ((*d)->dwarf->status.labors[l]) + { + any = true; + break; + } + } + + set_labor (*d, df::unit_labor::PULL_LEVER, true); + + if (any) continue; + + FOR_ENUM_ITEMS (unit_labor, l) + { + if (l == df::unit_labor::NONE) + continue; + + if (to_assign[l] > 0 || l == df::unit_labor::CLEAN) + set_labor(*d, l, true); + } + } + + /* set reequip on any dwarfs who are carrying tools needed by others */ for (auto d = dwarf_info.begin(); d != dwarf_info.end(); d++) @@ -2606,9 +2648,6 @@ public: if (has_tool != needs_tool) { - if (has_tool && tool_count[t] > tool_in_use[t]) - continue; - df::job_type j = df::job_type::NONE; if ((*d)->dwarf->job.current_job) From a55ce5f1d91486982148b9e62baaedb7c8631d01 Mon Sep 17 00:00:00 2001 From: Kelly Kinkade Date: Thu, 11 Aug 2016 16:58:35 -0500 Subject: [PATCH 22/22] labormanager: whitespace --- plugins/devel/labormanager.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/devel/labormanager.cpp b/plugins/devel/labormanager.cpp index edae051dc..7d41c7814 100644 --- a/plugins/devel/labormanager.cpp +++ b/plugins/devel/labormanager.cpp @@ -2594,13 +2594,13 @@ public: /* check for dwarfs assigned no labors and assign them the bucket list if there are */ for (auto d = dwarf_info.begin(); d != dwarf_info.end(); d++) { - if ((*d)->state == CHILD) + if ((*d)->state == CHILD) continue; bool any = false; FOR_ENUM_ITEMS (unit_labor, l) { - if (l == df::unit_labor::NONE) + if (l == df::unit_labor::NONE) continue; if ((*d)->dwarf->status.labors[l]) {