From 42dd60a24c39c80ce0eb12918adc71f02dc4f8cd Mon Sep 17 00:00:00 2001 From: Patrick Dawson Date: Mon, 24 Nov 2014 02:32:22 +0100 Subject: [PATCH 1/4] Add autolabor-artisans.lua --- scripts/autolabor-artisans.lua | 69 ++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 scripts/autolabor-artisans.lua diff --git a/scripts/autolabor-artisans.lua b/scripts/autolabor-artisans.lua new file mode 100644 index 000000000..9998c03ac --- /dev/null +++ b/scripts/autolabor-artisans.lua @@ -0,0 +1,69 @@ +-- Executes an autolabor command for each labor where skill level influences output quality. + +local artisan_labors = { + "CARPENTER", + "DETAIL", + "MASON", + "ARCHITECT", + "ANIMALTRAIN", + "LEATHER", + "BREWER", + "WEAVER", + "CLOTHESMAKER", + "COOK", + "FORGE_WEAPON", + "FORGE_ARMOR", + "FORGE_FURNITURE", + "METAL_CRAFT", + "CUT_GEM", + "ENCRUST_GEM", + "WOOD_CRAFT", + "STONE_CRAFT", + "BONE_CARVE", + "GLASSMAKER", + "SIEGECRAFT", + "BOWYER", + "MECHANIC", + "DYER", + "POTTERY", + "WAX_WORKING", +} + +local args = {...} + +function make_cmd(labor) + local cmd = string.format("autolabor %s", labor) + for i, arg in ipairs(args) do + cmd = cmd .. " " .. arg + end + return cmd +end + +function run() + if #args == 0 or args[1] == "help" then + print('Applies an autolabor command to all labors with quality-based output.') + print('') + print('Examples:') + print(' autolabor-artisans 0 2') + print(' autolabor-artisans disable') + return false + end + + dfhack.run_command("autolabor enable") + + -- Test with one to make sure the arguments are valid. + local cmd = make_cmd(artisan_labors[1]) + local output, status = dfhack.run_command_silent(cmd) + if status ~= CR_OK then + qerror("Invalid arguments.", status) + return false + end + + for i, labor in ipairs(artisan_labors) do + dfhack.run_command(make_cmd(labor)) + end + + return true +end + +run() From 8b1b32a4991d5680e9c48ed89264435f8c2005fb Mon Sep 17 00:00:00 2001 From: Patrick Dawson Date: Thu, 27 Nov 2014 15:56:14 +0100 Subject: [PATCH 2/4] autolabor: Add a talent pool parameter. --- plugins/autolabor.cpp | 36 +++++++++++++++++++++++++++++----- scripts/autolabor-artisans.lua | 2 +- 2 files changed, 32 insertions(+), 6 deletions(-) diff --git a/plugins/autolabor.cpp b/plugins/autolabor.cpp index ad7992038..fedc1992f 100644 --- a/plugins/autolabor.cpp +++ b/plugins/autolabor.cpp @@ -380,6 +380,8 @@ struct labor_info int maximum_dwarfs() { return config.ival(2); } void set_maximum_dwarfs(int maximum_dwarfs) { config.ival(2) = maximum_dwarfs; } + int talent_pool() { return config.ival(3); } + void set_talent_pool(int talent_pool) { config.ival(3) = talent_pool; } }; struct labor_default @@ -545,6 +547,7 @@ static void reset_labor(df::unit_labor labor) { labor_infos[labor].set_minimum_dwarfs(default_labor_infos[labor].minimum_dwarfs); labor_infos[labor].set_maximum_dwarfs(default_labor_infos[labor].maximum_dwarfs); + labor_infos[labor].set_talent_pool(200); labor_infos[labor].set_mode(default_labor_infos[labor].mode); } @@ -662,7 +665,7 @@ DFhackCExport command_result plugin_init ( color_ostream &out, std::vector []\n" + " autolabor [] []\n" " Set number of dwarves assigned to a labor.\n" " autolabor haulers\n" " Set a labor to be handled by hauler dwarves.\n" @@ -760,6 +763,7 @@ static void assign_labor(unit_labor::unit_labor labor, std::vector values(n_dwarfs); std::vector candidates; std::map dwarf_skill; + std::map dwarf_skillxp; std::vector previously_enabled(n_dwarfs); auto mode = labor_infos[labor].mode(); @@ -797,6 +801,7 @@ static void assign_labor(unit_labor::unit_labor labor, } dwarf_skill[dwarf] = skill_level; + dwarf_skillxp[dwarf] = skill_experience; value += skill_level * 100; value += skill_experience / 20; @@ -827,6 +832,21 @@ static void assign_labor(unit_labor::unit_labor labor, } + int pool = labor_infos[labor].talent_pool(); + if (pool < 200 && candidates.size() > 1) + { + // Sort in descending order + std::sort(candidates.begin(), candidates.end(), [&](const int lhs, const int rhs) { + if (dwarf_skill[lhs] == dwarf_skill[rhs]) + return dwarf_skillxp[lhs] > dwarf_skillxp[rhs]; + else + return dwarf_skill[lhs] > dwarf_skill[rhs]; + }); + + // Trim down to our top talents + candidates.resize(pool); + } + // Sort candidates by preference value values_sorter ivs(values); std::sort(candidates.begin(), candidates.end(), ivs); @@ -1296,7 +1316,8 @@ void print_labor (df::unit_labor labor, color_ostream &out) if (labor_infos[labor].mode() == HAULERS) out << "haulers"; else - out << "minimum " << labor_infos[labor].minimum_dwarfs() << ", maximum " << labor_infos[labor].maximum_dwarfs(); + out << "minimum " << labor_infos[labor].minimum_dwarfs() << ", maximum " << labor_infos[labor].maximum_dwarfs() + << ", pool " << labor_infos[labor].talent_pool(); out << ", currently " << labor_infos[labor].active_dwarfs << " dwarfs" << endl; } } @@ -1352,7 +1373,7 @@ command_result autolabor (color_ostream &out, std::vector & parame hauler_pct = pct; return CR_OK; } - else if (parameters.size() == 2 || parameters.size() == 3) + else if (parameters.size() >= 2 && parameters.size() <= 4) { if (!enable_autolabor) { @@ -1395,17 +1416,22 @@ command_result autolabor (color_ostream &out, std::vector & parame int minimum = atoi (parameters[1].c_str()); int maximum = 200; - if (parameters.size() == 3) + int pool = 200; + + if (parameters.size() >= 3) maximum = atoi (parameters[2].c_str()); + if (parameters.size() == 4) + pool = std::stoi(parameters[3]); if (maximum < minimum || maximum < 0 || minimum < 0) { - out.printerr("Syntax: autolabor []\n", maximum, minimum); + out.printerr("Syntax: autolabor [] []\n", maximum, minimum); return CR_WRONG_USAGE; } labor_infos[labor].set_minimum_dwarfs(minimum); labor_infos[labor].set_maximum_dwarfs(maximum); + labor_infos[labor].set_talent_pool(pool); labor_infos[labor].set_mode(AUTOMATIC); print_labor(labor, out); diff --git a/scripts/autolabor-artisans.lua b/scripts/autolabor-artisans.lua index 9998c03ac..61aa96335 100644 --- a/scripts/autolabor-artisans.lua +++ b/scripts/autolabor-artisans.lua @@ -44,7 +44,7 @@ function run() print('Applies an autolabor command to all labors with quality-based output.') print('') print('Examples:') - print(' autolabor-artisans 0 2') + print(' autolabor-artisans 0 2 3') print(' autolabor-artisans disable') return false end From 794e1ee6f0d64ee4531509e71bcb13329508cccc Mon Sep 17 00:00:00 2001 From: Patrick Dawson Date: Thu, 27 Nov 2014 21:13:20 +0100 Subject: [PATCH 3/4] autolabor: Document and improve the talent pool parameter. --- NEWS | 4 +- Readme.rst | 91 ++++++++++++++++++++++--------------------- plugins/autolabor.cpp | 23 +++++++++-- 3 files changed, 68 insertions(+), 50 deletions(-) diff --git a/NEWS b/NEWS index 5bd968839..9d78d71aa 100644 --- a/NEWS +++ b/NEWS @@ -4,6 +4,8 @@ DFHack Future New plugins: New scripts: Misc improvements: + Autolabor plugin: + - add an optional talent pool parameter DFHack 0.40.16-r1 Internals: @@ -265,7 +267,7 @@ DFHack v0.34.11-r5 - rendermax: replace the renderer with something else. Most interesting is "rendermax light"- a lighting engine for df. - automelt: allows marking stockpiles for automelt (i.e. any items placed in stocpile will be designated for melting) - embark-tools: implementations of Embark Anywhere, Nano Embark, and a few other embark-related utilities - - building-hacks: Allows to add custom functionality and/or animations to buildings. + - building-hacks: Allows to add custom functionality and/or animations to buildings. - petcapRemover: triggers pregnancies in creatures so that you can effectively raise the default pet population cap from the default 50 Misc improvements: diff --git a/Readme.rst b/Readme.rst index cd0b677b1..fad9907a4 100644 --- a/Readme.rst +++ b/Readme.rst @@ -336,8 +336,8 @@ Options: command-prompt -------------- -A one line command prompt in df. Same as entering command into dfhack console. Best -used as a keybinding. Can be called with optional "entry" that will start prompt with +A one line command prompt in df. Same as entering command into dfhack console. Best +used as a keybinding. Can be called with optional "entry" that will start prompt with that pre-filled. .. image:: images/command-prompt.png @@ -598,7 +598,7 @@ dfhack command line and can't be used from a hotkey. Settings will be remembered as long as dfhack runs. Intended for use in combination with the command liquids-here (which can be bound to a hotkey). -For more information, refer to the command's internal help. +For more information, refer to the command's internal help. .. note:: @@ -786,7 +786,7 @@ out if you have any of them running around in your fort. Dead and passive creatures (ghosts who were put to rest, killed vampires, ...) are ignored. Undead skeletons, corpses, bodyparts and the like are all thrown into the curse category "zombie". Anonymous zombies and resurrected body parts will show -as "unnamed creature". +as "unnamed creature". Options: @@ -1377,12 +1377,12 @@ produce undesirable results. There are a few good ones though. You are in fort game mode, managing your fortress and paused. You switch to the arena game mode, *assume control of a creature* and then - switch to adventure game mode(1). + switch to adventure game mode(1). You just lost a fortress and gained an adventurer. You could also do this. You are in fort game mode, managing your fortress and paused at the esc menu. You switch to the adventure game mode, then use Dfusion to *assume control of a creature* and then - save or retire. + save or retire. You just created a returnable mountain home and gained an adventurer. @@ -1420,7 +1420,7 @@ Export dwarves to RuneSmith-compatible XML. exportlegends ------------- -Controls legends mode to export data - especially useful to set-and-forget large +Controls legends mode to export data - especially useful to set-and-forget large worlds, or when you want a map of every site when there are several hundred. Options: @@ -1581,17 +1581,17 @@ Constraint examples ................... Keep metal bolts within 900-1000, and wood/bone within 150-200:: - + workflow amount AMMO:ITEM_AMMO_BOLTS/METAL 1000 100 workflow amount AMMO:ITEM_AMMO_BOLTS/WOOD,BONE 200 50 Keep the number of prepared food & drink stacks between 90 and 120:: - + workflow count FOOD 120 30 workflow count DRINK 120 30 Make sure there are always 25-30 empty bins/barrels/bags:: - + workflow count BIN 30 workflow count BARREL 30 workflow count BOX/CLOTH,SILK,YARN 30 @@ -1606,12 +1606,12 @@ Produce 15-20 gold crafts:: workflow count CRAFTS//GOLD 20 Collect 15-20 sand bags and clay boulders:: - + workflow count POWDER_MISC/SAND 20 workflow count BOULDER/CLAY 20 Make sure there are always 80-100 units of dimple dye:: - + workflow amount POWDER_MISC//MUSHROOM_CUP_DIMPLE:MILL 100 20 .. note:: @@ -1749,7 +1749,7 @@ Mass-renaming Using the 'nick' command you can set the same nickname for multiple units. If used without 'assign', 'all' or 'count' it will rename all units in the current default target zone. Combined with 'assign', 'all' or 'count' (and -further optional filters) it will rename units matching the filter conditions. +further optional filters) it will rename units matching the filter conditions. Cage zones .......... @@ -1786,7 +1786,7 @@ Examples ``zone tocages count 50 own tame male not grazer`` Stuff up to 50 owned tame male animals who are not grazers into cages built on the current default zone. - + autonestbox ----------- Assigns unpastured female egg-layers to nestbox zones. Requires that you create @@ -1867,17 +1867,17 @@ You want to keep max 7 kids (4 female, 3 male) and max 3 adults (2 female, slaughtered. Excess kids will get slaughtered starting with the youngest to allow that the older ones grow into adults. Any unnamed cats will be slaughtered as soon as possible. -:: +:: autobutcher target 4 3 2 1 ALPACA BIRD_TURKEY autobutcher target 0 0 0 0 CAT autobutcher watch ALPACA BIRD_TURKEY CAT autobutcher start - + Automatically put all new races onto the watchlist and mark unnamed tame units for slaughter as soon as they arrive in your fort. Settings already made for specific races will be left untouched. -:: +:: autobutcher target 0 0 0 0 new autobutcher autowatch @@ -1889,16 +1889,16 @@ values again. Note: 'autobutcher unwatch all' works, but only makes sense if you want to keep the plugin running with the 'autowatch' feature or manually add some new races with 'watch'. If you simply want to stop it completely use 'autobutcher stop' instead. -:: +:: autobutcher unwatch ALPACA CAT - + **Note:** Settings and watchlist are stored in the savegame, so that you can have different settings for each world. If you want to copy your watchlist to another savegame you can use the command list_export: -:: +:: Load savegame where you made the settings. Start a CMD shell and navigate to the df directory. Type the following into the shell: @@ -1924,21 +1924,21 @@ If you add :: enable getplants - + to your dfhack.init there will be a hotkey to open the dashboard from the chop designation menu. autolabor --------- -Automatically manage dwarf labors to efficiently complete jobs. +Automatically manage dwarf labors to efficiently complete jobs. Autolabor tries to keep as many dwarves as possible busy but also tries to have dwarves specialize in specific skills. -The key is that, for almost all labors, once a dwarf begins a job it will finish that -job even if the associated labor is removed. Autolabor therefore frequently checks -which dwarf or dwarves should take new jobs for that labor, and sets labors accordingly. -Labors with equiptment (mining, hunting, and woodcutting), which are abandoned +The key is that, for almost all labors, once a dwarf begins a job it will finish that +job even if the associated labor is removed. Autolabor therefore frequently checks +which dwarf or dwarves should take new jobs for that labor, and sets labors accordingly. +Labors with equiptment (mining, hunting, and woodcutting), which are abandoned if labors change mid-job, are handled slightly differently to minimise churn. *Warning: autolabor will override any manual changes you make to labors* @@ -1965,22 +1965,23 @@ Advanced usage: :`autolabor MINE 5`: Keep at least 5 dwarves with mining enabled. :`autolabor CUT_GEM 1 1`: Keep exactly 1 dwarf with gemcutting enabled. +:`autolabor COOK 1 1 3`: Keep 1 dwarf with cooking enabled, selected only from the top 3. :`autolabor FEED_WATER_CIVILIANS haulers`: Have haulers feed and water wounded dwarves. :`autolabor CUTWOOD disable`: Turn off autolabor for wood cutting. -By default, each labor is assigned to between 1 and 200 dwarves (2-200 for mining). +By default, each labor is assigned to between 1 and 200 dwarves (2-200 for mining). By default 33% of the workforce become haulers, who handle all hauling jobs as well -as cleaning, pulling levers, recovering wounded, removing constructions, and filling ponds. +as cleaning, pulling levers, recovering wounded, removing constructions, and filling ponds. Other jobs are automatically assigned as described above. Each of these settings can be adjusted. -Jobs are rarely assigned to nobles with responsibilities for meeting diplomats or merchants, +Jobs are rarely assigned to nobles with responsibilities for meeting diplomats or merchants, never to the chief medical dwarf, and less often to the bookeeper and manager. Hunting is never assigned without a butchery, and fishing is nver assigned without a fishery. -For each labor a preference order is calculated based on skill, biased against masters of other +For each labor a preference order is calculated based on skill, biased against masters of other trades and excluding those who can't do the job. The labor is then added to the best -dwarves for that labor. We assign at least the minimum number of dwarfs, in order of preference, +dwarves for that labor. We assign at least the minimum number of dwarfs, in order of preference, and then assign additional dwarfs that meet any of these conditions: * The dwarf is idle and there are no idle dwarves assigned to this labor @@ -2070,7 +2071,7 @@ Known limitations: if the selected unit is currently performing a job, the mood log-region ---------- -When enabled in dfhack.init, each time a fort is loaded identifying information will be written to the gamelog. Assists in parsing the file if you switch between forts, and adds information for story-building. +When enabled in dfhack.init, each time a fort is loaded identifying information will be written to the gamelog. Assists in parsing the file if you switch between forts, and adds information for story-building. ======= @@ -2197,7 +2198,7 @@ A script to designate an area for digging according to a plan in csv format. This script, inspired from quickfort, can designate an area for digging. Your plan should be stored in a .csv file like this:: - # this is a comment + # this is a comment d;d;u;d;d;skip this tile;d d;d;d;i @@ -2228,7 +2229,7 @@ Also works when selecting units from the 'u'nitlist viewscreen. dfstatus ======== -Show a quick overview of critical stock quantities, including food, dirnks, wood, and various bars. +Show a quick overview of critical stock quantities, including food, dirnks, wood, and various bars. embark ====== @@ -2586,8 +2587,8 @@ To activate, open the unit screen and press 'l'. This tool implements a Dwarf Therapist-like interface within the game UI. The far left column displays the unit's Happiness (color-coded based on its -value), Name, Profession/Squad, and the right half of the screen displays each -dwarf's labor settings and skill levels (0-9 for Dabbling thru Professional, A-E for +value), Name, Profession/Squad, and the right half of the screen displays each +dwarf's labor settings and skill levels (0-9 for Dabbling thru Professional, A-E for Great thru Grand Master, and U-Z for Legendary thru Legendary+5). Cells with teal backgrounds denote skills not controlled by labors, e.g. @@ -2610,7 +2611,7 @@ Press Enter to toggle the selected labor for the selected unit, or Shift+Enter to toggle all labors within the selected category. Press the ``+-`` keys to sort the unit list according to the currently selected -skill/labor, and press the ``*/`` keys to sort the unit list by Name, Profession/Squad, +skill/labor, and press the ``*/`` keys to sort the unit list by Name, Profession/Squad, Happiness, or Arrival order (using Tab to select which sort method to use here). With a unit selected, you can press the "v" key to view its properties (and @@ -2739,7 +2740,7 @@ gui/advfort =========== This script allows to perform jobs in adventure mode. For more complete help -press '?' while script is running. It's most confortable to use this as a +press '?' while script is running. It's most confortable to use this as a keybinding. (e.g. keybinding set Ctrl-T gui/advfort). Possible arguments: * -a or --nodfassign - uses different method to assign items. @@ -2748,7 +2749,7 @@ keybinding. (e.g. keybinding set Ctrl-T gui/advfort). Possible arguments: * -c or --cheat - relaxes item requirements for buildings (e.g. walls from bones). implies -a - + * job - selects that job (e.g. Dig or FellTree) An example of player digging in adventure mode: @@ -2814,7 +2815,7 @@ and selects the newly created copy. gui/companion-order =================== -A script to issue orders for companions. Select companions with lower case chars, issue orders with upper +A script to issue orders for companions. Select companions with lower case chars, issue orders with upper case. Must be in look or talk mode to issue command on tile. .. image:: images/companion-order.png @@ -2838,13 +2839,13 @@ There are three ways to open this editor: * using gui/gm-editor - executes lua command and opens editor on its results (e.g. gui/gm-editor "df.global.world.items.all" shows all items) - + * using gui/gm-editor dialog - shows an in game dialog to input lua command. Works the same as version above. - + .. image:: images/gm-editor.png -This editor allows to change and modify almost anything in df. Press '?' for an +This editor allows to change and modify almost anything in df. Press '?' for an in-game help. Hotkeys @@ -2870,13 +2871,13 @@ Type ``hotkeys`` into the DFHack console to open the screen, or bind the command globally active hotkey in dfhack.init, e.g.: ``keybinding add Ctrl-F1 hotkeys`` - + Stockpile Automation ==================== Enable the autodump plugin in your dfhack.init with ``enable autodump`` - + When querying a stockpile an option will appear to toggle autodump for this stockpile. Any items placed in this stockpile will be designated to be dumped. diff --git a/plugins/autolabor.cpp b/plugins/autolabor.cpp index fedc1992f..9b7f5835d 100644 --- a/plugins/autolabor.cpp +++ b/plugins/autolabor.cpp @@ -687,11 +687,15 @@ DFhackCExport command_result plugin_init ( color_ostream &out, std::vector most skilled\n" + " dwarves, add a talent pool number .\n" "Examples:\n" " autolabor MINE 2\n" " Keep at least 2 dwarves with mining enabled.\n" " autolabor CUT_GEM 1 1\n" " Keep exactly 1 dwarf with gemcutting enabled.\n" + " autolabor COOK 1 1 3\n" + " Keep 1 dwarf with cooking enabled, selected only from the top 3.\n" " autolabor FEED_WATER_CIVILIANS haulers\n" " Have haulers feed and water wounded dwarves.\n" " autolabor CUTWOOD disable\n" @@ -833,7 +837,7 @@ static void assign_labor(unit_labor::unit_labor labor, } int pool = labor_infos[labor].talent_pool(); - if (pool < 200 && candidates.size() > 1) + if (pool < 200 && candidates.size() > 1 && pool < candidates.size()) { // Sort in descending order std::sort(candidates.begin(), candidates.end(), [&](const int lhs, const int rhs) { @@ -843,8 +847,19 @@ static void assign_labor(unit_labor::unit_labor labor, return dwarf_skill[lhs] > dwarf_skill[rhs]; }); - // Trim down to our top talents - candidates.resize(pool); + // Check if all dwarves have equivalent skills, usually zero + int first_dwarf = candidates[0]; + int last_dwarf = candidates[candidates.size() - 1]; + if (dwarf_skill[first_dwarf] == dwarf_skill[last_dwarf] && + dwarf_skillxp[first_dwarf] == dwarf_skillxp[last_dwarf]) + { + // There's no difference in skill, so change nothing + } + else + { + // Trim down to our top talents + candidates.resize(pool); + } } // Sort candidates by preference value @@ -1423,7 +1438,7 @@ command_result autolabor (color_ostream &out, std::vector & parame if (parameters.size() == 4) pool = std::stoi(parameters[3]); - if (maximum < minimum || maximum < 0 || minimum < 0) + if (maximum < minimum || maximum < 0 || minimum < 0 || pool < 0) { out.printerr("Syntax: autolabor [] []\n", maximum, minimum); return CR_WRONG_USAGE; From a6ae6d4c28240f8af8594124bf906b7d56f14c9b Mon Sep 17 00:00:00 2001 From: Patrick Dawson Date: Fri, 28 Nov 2014 00:55:26 +0100 Subject: [PATCH 4/4] autolabor: Fix compile on Windows. VC10 wants an explicit return type for lambdas. --- plugins/autolabor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/autolabor.cpp b/plugins/autolabor.cpp index 9b7f5835d..28ac0d433 100644 --- a/plugins/autolabor.cpp +++ b/plugins/autolabor.cpp @@ -840,7 +840,7 @@ static void assign_labor(unit_labor::unit_labor labor, if (pool < 200 && candidates.size() > 1 && pool < candidates.size()) { // Sort in descending order - std::sort(candidates.begin(), candidates.end(), [&](const int lhs, const int rhs) { + std::sort(candidates.begin(), candidates.end(), [&](const int lhs, const int rhs) -> bool { if (dwarf_skill[lhs] == dwarf_skill[rhs]) return dwarf_skillxp[lhs] > dwarf_skillxp[rhs]; else