-
Notifications
You must be signed in to change notification settings - Fork 200
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1374 from chdoc/autocheese
new tool: autocheese
- Loading branch information
Showing
4 changed files
with
230 additions
and
0 deletions.
There are no files selected for viewing
This file contains 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) | ||
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 | ||
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 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
This file contains 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 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