Skip to content

Commit a2895a8

Browse files
committed
new tool: autocheese
1 parent 6b9daef commit a2895a8

File tree

4 files changed

+196
-0
lines changed

4 files changed

+196
-0
lines changed

autocheese.lua

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
--@module = true
2+
3+
local ic = reqscript('idle-crafting')
4+
5+
---make cheese using a specific barrel and workshop
6+
---@param barrel df.item
7+
---@param workshop df.building_workshopst
8+
---@return df.job
9+
function makeCheese(barrel, workshop)
10+
---@type df.job
11+
local job = ic.make_job()
12+
job.job_type = df.job_type.MakeCheese
13+
14+
local jitem = df.job_item:new()
15+
jitem.quantity = 0
16+
jitem.vector_id = df.job_item_vector_id.ANY_COOKABLE
17+
jitem.flags1.unrotten = true
18+
jitem.flags1.milk = true
19+
job.job_items.elements:insert('#', jitem)
20+
21+
if not dfhack.job.attachJobItem(job, barrel, df.job_item_ref.T_role.Reagent, 0, -1) then
22+
dfhack.error('could not attach item')
23+
end
24+
25+
ic.assignToWorkshop(job, workshop)
26+
return job
27+
end
28+
29+
30+
31+
---unit is ready to take jobs
32+
---@param unit df.unit
33+
---@return boolean
34+
function unitIsAvailable(unit)
35+
if unit.job.current_job then
36+
return false
37+
elseif #unit.individual_drills > 0 then
38+
return false
39+
elseif unit.flags1.caged or unit.flags1.chained then
40+
return false
41+
elseif unit.military.squad_id ~= -1 then
42+
local squad = df.squad.find(unit.military.squad_id)
43+
-- this lookup should never fail
44+
---@diagnostic disable-next-line: need-check-nil
45+
return #squad.orders == 0 and squad.activity == -1
46+
end
47+
return true
48+
end
49+
50+
---find unit with a particular labor enabled
51+
---@param unit_labor df.unit_labor
52+
---@param job_skill df.job_skill
53+
---@param workshop df.building
54+
---@return df.unit|nil
55+
---@return integer|nil
56+
function findAvailableLaborer(unit_labor, job_skill, workshop)
57+
local max_unit = nil
58+
local max_skill = -1
59+
for _, unit in ipairs(dfhack.units.getCitizens(true, false)) do
60+
if
61+
unit.status.labors[unit_labor] and
62+
unitIsAvailable(unit) and
63+
ic.canAccessWorkshop(unit, workshop)
64+
then
65+
local unit_skill = dfhack.units.getNominalSkill(unit, job_skill, true)
66+
if unit_skill > max_skill then
67+
max_unit = unit
68+
max_skill = unit_skill
69+
end
70+
end
71+
end
72+
return max_unit, max_skill
73+
end
74+
75+
local function findMilkBarrel(min_liquids)
76+
for _, container in ipairs(df.global.world.items.other.FOOD_STORAGE) do
77+
if
78+
not (container.flags.in_job or container.flags.forbid) and
79+
container.flags.container and #container.general_refs >= min_liquids
80+
then
81+
local content_reference = dfhack.items.getGeneralRef(container, df.general_ref_type.CONTAINS_ITEM)
82+
local contained_item = df.item.find(content_reference and content_reference.item_id or -1)
83+
if contained_item then
84+
local mat_info = dfhack.matinfo.decode(contained_item)
85+
if mat_info:matches { milk = true } then
86+
return container
87+
end
88+
end
89+
end
90+
end
91+
end
92+
93+
function findWorkshop()
94+
for _,workshop in ipairs(df.global.world.buildings.other.WORKSHOP_FARMER) do
95+
if
96+
not workshop.profile.blocked_labors[df.unit_labor.MAKE_CHEESE] and
97+
#workshop.jobs == 0 and #workshop.profile.permitted_workers == 0
98+
then
99+
return workshop
100+
end
101+
end
102+
end
103+
104+
if dfhack_flags.module then
105+
return
106+
end
107+
108+
-- actual script action
109+
110+
local argparse = require('argparse')
111+
112+
local min_number = 50
113+
114+
local _ = argparse.processArgsGetopt({...},
115+
{
116+
{ 'm', 'min-milk', hasArg = true,
117+
handler = function(min)
118+
min_number = argparse.nonnegativeInt(min, 'min-milk')
119+
end }
120+
})
121+
122+
123+
local reagent = findMilkBarrel(min_number)
124+
125+
if not reagent then
126+
-- print('autocheese: no sufficiently full barrel found')
127+
return
128+
end
129+
130+
local workshop = findWorkshop()
131+
132+
if not workshop then
133+
print('autocheese: no workshop available')
134+
return
135+
end
136+
137+
local worker, skill = findAvailableLaborer(df.unit_labor.MAKE_CHEESE, df.job_skill.CHEESEMAKING, workshop)
138+
if not worker then
139+
print('autocheese: no cheesemaker available')
140+
return
141+
end
142+
local job = makeCheese(reagent, workshop)
143+
144+
print(('autocheese: dispatching cheesemaking job for %s (%d milk) to %s'):format(
145+
dfhack.items.getDescription(reagent, 0),
146+
#reagent.general_refs,
147+
dfhack.df2console(dfhack.units.getReadableName(worker))
148+
))
149+
150+
151+
-- assign a worker and send it to fetch the barrel
152+
dfhack.job.addWorker(job, worker)
153+
dfhack.units.setPathGoal(worker, reagent.pos, df.unit_path_goal.GrabJobResources)
154+
job.items[0].flags.is_fetching = true
155+
job.flags.fetching = true

changelog.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ Template for new versions:
2929
## New Tools
3030
- `fix/stuck-squad`: allow squads and messengers returning from missions to rescue squads that have gotten stuck on the world map
3131
- `gui/rename`: (reinstated) give new in-game language-based names to anything that can be named (e.g. units, governments, fortresses, or the world)
32+
- `autocheese`: automatically make cheese using barrels that have accumulated sufficient milk
3233

3334
## New Features
3435
- `gui/settings-manager`: new overlay on the Labor -> Standing Orders tab for configuring the number of barrels to reserve for job use (so you can brew alcohol and not have all your barrels claimed by stockpiles for container storage)

docs/autocheese.rst

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
autocheese
2+
==========
3+
4+
.. dfhack-tool::
5+
:summary: Automatically make cheese using barrels that have accumulated sufficient milk.
6+
:tags: fort auto
7+
8+
Cheese making is difficult to automate using work orders, because a single job
9+
can consume anything from a bucket was a single unit of milk to barrel
10+
containing up to 100 units of milk.
11+
12+
The script will scan your fort for barrels with a certain minimum amount of milk
13+
(default: 50), create a cheese making job specifically for that barrel, and
14+
assign this job to one of your idle dwarves (giving preference to skilled cheese
15+
makers).
16+
17+
When enabled using `gui/control-panel`, the script will run automatically, with
18+
default options, twice a month.
19+
20+
Usage
21+
-----
22+
23+
::
24+
25+
autocheese [<options>]
26+
27+
Examples
28+
--------
29+
30+
``autocheese -m 100``
31+
Only create a job if there is a barrel that is filled to the maximum.
32+
33+
Options
34+
-------
35+
36+
``-m``, ``--min-milk``
37+
Set the minimum number of milk items in a barrel for the barrel to be
38+
considered for cheese making.

internal/control-panel/registry.lua

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ COMMANDS_BY_IDX = {
1818
{command='autobutcher target 10 10 14 2 BIRD_PEAFOWL_BLUE', group='automation', mode='run',
1919
desc='Enable if you usually want to raise peafowl.'},
2020
{command='autochop', group='automation', mode='enable'},
21+
{command='autocheese', group='automation', mode='repeat',
22+
params={'--time', '14', '--timeUnits', 'days', '--command', '[', 'autocheese', ']'}},
2123
{command='autoclothing', group='automation', mode='enable'},
2224
{command='autofarm', group='automation', mode='enable'},
2325
{command='autofarm threshold 150 grass_tail_pig', group='automation', mode='run',

0 commit comments

Comments
 (0)