-
Notifications
You must be signed in to change notification settings - Fork 221
new tool: autocheese #1374
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
new tool: autocheese #1374
Changes from all commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
a2895a8
new tool: autocheese
chdoc 70240fe
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] 67e94aa
Stylistic improvements suggested by Myk
chdoc 75f3521
Merge remote-tracking branch 'origin/master' into autocheese
chdoc 44b5547
Implement feedback from code review
chdoc 6d5340f
Merge remote-tracking branch 'origin/master' into autocheese
chdoc e07775a
remove duplicate changelog line
myk002 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,187 @@ | ||
| --@module = true | ||
|
|
||
| local ic = reqscript('idle-crafting') | ||
|
|
||
| ---make cheese using a specific barrel and workshop | ||
| ---@param barrel df.item | ||
| ---@param workshop df.building_workshopst | ||
| ---@return df.job | ||
| function makeCheese(barrel, workshop) | ||
| ---@type df.job | ||
| local job = ic.make_job() | ||
| job.job_type = df.job_type.MakeCheese | ||
|
|
||
| local jitem = df.job_item:new() | ||
| jitem.quantity = 0 | ||
| jitem.vector_id = df.job_item_vector_id.ANY_COOKABLE | ||
| jitem.flags1.unrotten = true | ||
| jitem.flags1.milk = true | ||
| job.job_items.elements:insert('#', jitem) | ||
|
|
||
| if not dfhack.job.attachJobItem(job, barrel, df.job_item_ref.T_role.Reagent, 0, -1) then | ||
| dfhack.error('could not attach item') | ||
| end | ||
|
|
||
| ic.assignToWorkshop(job, workshop) | ||
| return job | ||
| end | ||
|
|
||
|
|
||
|
|
||
| ---unit is ready to take jobs | ||
| ---@param unit df.unit | ||
| ---@return boolean | ||
| function unitIsAvailable(unit) | ||
| if unit.job.current_job then | ||
| return false | ||
| elseif #unit.individual_drills > 0 then | ||
| return false | ||
| elseif unit.flags1.caged or unit.flags1.chained then | ||
| return false | ||
| elseif unit.military.squad_id ~= -1 then | ||
| local squad = df.squad.find(unit.military.squad_id) | ||
| -- this lookup should never fail | ||
| ---@diagnostic disable-next-line: need-check-nil | ||
| return #squad.orders == 0 and squad.activity == -1 | ||
| end | ||
| return true | ||
| end | ||
|
|
||
| ---check if unit can perform labor at workshop | ||
| ---@param unit df.unit | ||
| ---@param unit_labor df.unit_labor | ||
| ---@param workshop df.building | ||
| ---@return boolean | ||
| function availableLaborer(unit, unit_labor, workshop) | ||
| return unit.status.labors[unit_labor] | ||
| and unitIsAvailable(unit) | ||
| and ic.canAccessWorkshop(unit, workshop) | ||
| end | ||
|
|
||
| ---find unit with a particular labor enabled | ||
| ---@param unit_labor df.unit_labor | ||
| ---@param job_skill df.job_skill | ||
| ---@param workshop df.building | ||
| ---@return df.unit|nil | ||
| ---@return integer|nil | ||
| function findAvailableLaborer(unit_labor, job_skill, workshop) | ||
| local max_unit = nil | ||
| local max_skill = -1 | ||
| for _, unit in ipairs(dfhack.units.getCitizens(true, false)) do | ||
| if | ||
| availableLaborer(unit, unit_labor, workshop) | ||
| then | ||
| local unit_skill = dfhack.units.getNominalSkill(unit, job_skill, true) | ||
| if unit_skill > max_skill then | ||
| max_unit = unit | ||
| max_skill = unit_skill | ||
| end | ||
| end | ||
| end | ||
| return max_unit, max_skill | ||
| end | ||
|
|
||
| local function findMilkBarrel(min_liquids) | ||
myk002 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| for _, container in ipairs(df.global.world.items.other.FOOD_STORAGE) do | ||
| if | ||
| not (container.flags.in_job or container.flags.forbid) and | ||
| container.flags.container and #container.general_refs >= min_liquids | ||
myk002 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| then | ||
| local content_reference = dfhack.items.getGeneralRef(container, df.general_ref_type.CONTAINS_ITEM) | ||
| local contained_item = df.item.find(content_reference and content_reference.item_id or -1) | ||
| if contained_item then | ||
| local mat_info = dfhack.matinfo.decode(contained_item) | ||
| if mat_info:matches { milk = true } then | ||
| return container | ||
| end | ||
| end | ||
| end | ||
| end | ||
| end | ||
|
|
||
| ---find a workshop to which the barrel can be brought | ||
| ---if the workshop has a master, only return workshop and master if the master is available | ||
| ---@param pos df.coord | ||
| ---@return df.building_workshopst? | ||
| ---@return df.unit? | ||
| function findWorkshop(pos) | ||
| for _,workshop in ipairs(df.global.world.buildings.other.WORKSHOP_FARMER) do | ||
| if | ||
| dfhack.maps.canWalkBetween(pos, xyz2pos(workshop.centerx, workshop.centery, workshop.z)) and | ||
| not workshop.profile.blocked_labors[df.unit_labor.MAKE_CHEESE] and | ||
| #workshop.jobs == 0 | ||
| then | ||
| if #workshop.profile.permitted_workers == 0 then | ||
| -- immediately return workshop without master | ||
| return workshop, nil | ||
| else | ||
| unit = df.unit.find(workshop.profile.permitted_workers[0]) | ||
| if | ||
| unit and availableLaborer(unit, df.unit_labor.MAKE_CHEESE, workshop) | ||
| then | ||
| -- return workshop and master, if master is available | ||
| return workshop, unit | ||
| else | ||
| print("autocheese: Skipping farmer's workshop with unavailable master") | ||
| end | ||
| end | ||
| end | ||
| end | ||
| end | ||
|
|
||
| if dfhack_flags.module then | ||
| return | ||
| end | ||
|
|
||
| -- actual script action | ||
|
|
||
| local argparse = require('argparse') | ||
|
|
||
| local min_number = 50 | ||
|
|
||
| local _ = argparse.processArgsGetopt({...}, | ||
| { | ||
| { 'm', 'min-milk', hasArg = true, | ||
| handler = function(min) | ||
| min_number = argparse.nonnegativeInt(min, 'min-milk') | ||
| end } | ||
| }) | ||
|
|
||
|
|
||
| local reagent = findMilkBarrel(min_number) | ||
|
|
||
| if not reagent then | ||
| -- print('autocheese: no sufficiently full barrel found') | ||
| return | ||
| end | ||
|
|
||
| local workshop, worker = findWorkshop(xyz2pos(dfhack.items.getPosition(reagent))) | ||
|
|
||
| if not workshop then | ||
| print("autocheese: no Farmer's Workshop available") | ||
| return | ||
| end | ||
|
|
||
| -- try to find laborer for workshop without master | ||
| if not worker then | ||
| worker, _ = findAvailableLaborer(df.unit_labor.MAKE_CHEESE, df.job_skill.CHEESEMAKING, workshop) | ||
| end | ||
|
|
||
| if not worker then | ||
| print('autocheese: no cheesemaker available') | ||
| return | ||
| end | ||
| local job = makeCheese(reagent, workshop) | ||
|
|
||
| print(('autocheese: dispatching cheesemaking job for %s (%d milk) to %s'):format( | ||
| dfhack.df2console(dfhack.items.getReadableDescription(reagent)), | ||
| #reagent.general_refs, | ||
| dfhack.df2console(dfhack.units.getReadableName(worker)) | ||
| )) | ||
|
|
||
|
|
||
| -- assign a worker and send it to fetch the barrel | ||
| dfhack.job.addWorker(job, worker) | ||
| dfhack.units.setPathGoal(worker, reagent.pos, df.unit_path_goal.GrabJobResources) | ||
| job.items[0].flags.is_fetching = true | ||
| job.flags.fetching = true | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -28,6 +28,8 @@ Template for new versions: | |
|
|
||
| ## New Tools | ||
|
|
||
| - `autocheese`: automatically make cheese using barrels that have accumulated sufficient milk | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. needs to be moved up to the current version |
||
|
|
||
| ## New Features | ||
|
|
||
| ## Fixes | ||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,39 @@ | ||
| autocheese | ||
| ========== | ||
|
|
||
| .. dfhack-tool:: | ||
| :summary: Schedule cheese making jobs based on milk reserves. | ||
| :tags: fort auto | ||
|
|
||
| Cheese making is difficult to automate using work orders. A single job | ||
| can consume anything from a bucket with a single unit of milk to a barrel | ||
| with 100 units of milk. This makes it hard to predict how much cheese will | ||
| actually be produced by an automated order. | ||
|
|
||
| The script will scan your fort for barrels with a certain minimum amount of milk | ||
| (default: 50), create a cheese making job specifically for that barrel, and | ||
| assign this job to one of your idle dwarves (giving preference to skilled cheese | ||
| makers). | ||
|
|
||
| When enabled using `gui/control-panel`, the script will run automatically, with | ||
| default options, twice a month. | ||
|
|
||
| Usage | ||
| ----- | ||
|
|
||
| :: | ||
|
|
||
| autocheese [<options>] | ||
|
|
||
| Examples | ||
| -------- | ||
|
|
||
| ``autocheese -m 100`` | ||
| Only create a job if there is a barrel that is filled to the maximum. | ||
|
|
||
| Options | ||
| ------- | ||
|
|
||
| ``-m``, ``--min-milk`` | ||
| Set the minimum number of milk items in a barrel for the barrel to be | ||
| considered for cheese making. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.