Skip to content

Commit c9a8cdb

Browse files
authored
Merge branch 'master' into myk_keys
2 parents 663471c + f15dd35 commit c9a8cdb

File tree

4 files changed

+194
-24
lines changed

4 files changed

+194
-24
lines changed

changelog.txt

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,23 @@ Template for new versions:
2626

2727
# Future
2828

29+
## New Tools
30+
31+
## New Features
32+
33+
## Fixes
34+
35+
## Misc Improvements
36+
37+
## Removed
38+
39+
# 50.13-r5
40+
2941
## New Tools
3042
- `embark-anyone`: allows you to embark as any civilization, including dead and non-dwarven civs
3143
- `idle-crafting`: allow dwarves to independently satisfy their need to craft objects
3244
- `gui/family-affairs`: (reinstated) inspect or meddle with pregnancies, marriages, or lover relationships
33-
- `notes`: manage map-specific notes
45+
- `notes`: attach notes to locations on a fort map
3446

3547
## New Features
3648
- `caravan`: DFHack dialogs for trade screens (both ``Bring goods to depot`` and the ``Trade`` barter screen) can now filter by item origins (foreign vs. fort-made) and can filter bins by whether they have a mix of ethically acceptable and unacceptable items in them
@@ -64,8 +76,6 @@ Template for new versions:
6476
## Documentation
6577
- `gui/embark-anywhere`: add information about how the game determines world tile pathability and instructions for bridging two landmasses
6678

67-
## Removed
68-
6979
# 50.13-r4
7080

7181
## New Features

docs/idle-crafting.rst

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,16 @@ needs to craft objects. Workshops that have a master assigned cannot be used in
4545
this way.
4646

4747
When a workshop is designated for idle crafting, this tool will create crafting
48-
jobs and assign them to idle dwarves who have a need for crafting
49-
objects. Currently, bone carving and stonecrafting are supported, with
50-
stonecrafting being the default option. This script respects the setting for
51-
permitted general work orders from the "Workers" tab. Thus, to designate a
48+
jobs and assign them to idle dwarves who have a need for crafting objects. This
49+
script respects the setting for permitted general work order labors from the "Workers"
50+
tab.
51+
52+
For workshops without input stockpile links, bone carving and stonecrafting are
53+
supported, with stonecrafting being the default option. Thus, to designate a
5254
workshop for bone carving, disable the stonecrafting labor while keeping the
5355
bone carving labor enabled.
56+
57+
For workshops with input stockpile links, the creation of totems and horn crafts
58+
are supported as well. In this case, the choice of job is made randomly based on
59+
the resources available in the input stockpiles (respecting the permitted
60+
labors from the workshop profile).

idle-crafting.lua

Lines changed: 168 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,55 @@ local widgets = require('gui.widgets')
66
local repeatutil = require("repeat-util")
77
local orders = require('plugins.orders')
88

9+
---iterate over input materials of workshop with stockpile links
10+
---@param workshop df.building_workshopst
11+
---@param action fun(item:df.item):any
12+
local function for_inputs(workshop, action)
13+
if #workshop.profile.links.take_from_pile == 0 then
14+
dfhack.error('workshop has no links')
15+
else
16+
for _, stockpile in ipairs(workshop.profile.links.take_from_pile) do
17+
for _, item in ipairs(dfhack.buildings.getStockpileContents(stockpile)) do
18+
if item:isAssignedToThisStockpile(stockpile.id) then
19+
for _, contained_item in ipairs(dfhack.items.getContainedItems(item)) do
20+
action(contained_item)
21+
end
22+
else
23+
action(item)
24+
end
25+
end
26+
end
27+
for _, contained_item in ipairs(workshop.contained_items) do
28+
if contained_item.use_mode == df.building_item_role_type.TEMP then
29+
action(contained_item.item)
30+
end
31+
end
32+
end
33+
end
34+
35+
---choose random value based on positive integer weights
36+
---@generic T
37+
---@param choices table<T,integer>
38+
---@return T
39+
function weightedChoice(choices)
40+
local sum = 0
41+
for _, weight in pairs(choices) do
42+
sum = sum + weight
43+
end
44+
if sum <= 0 then
45+
return nil
46+
end
47+
local random = math.random(sum)
48+
for choice, weight in pairs(choices) do
49+
if random > weight then
50+
random = random - weight
51+
else
52+
return choice
53+
end
54+
end
55+
return nil --never reached on well-formed input
56+
end
57+
958
---create a new linked job
1059
---@return df.job
1160
function make_job()
@@ -14,6 +63,61 @@ function make_job()
1463
return job
1564
end
1665

66+
function assignToWorkshop(job, workshop)
67+
job.pos = xyz2pos(workshop.centerx, workshop.centery, workshop.z)
68+
dfhack.job.addGeneralRef(job, df.general_ref_type.BUILDING_HOLDER, workshop.id)
69+
workshop.jobs:insert("#", job)
70+
end
71+
72+
---make totem at specified workshop
73+
---@param unit df.unit
74+
---@param workshop df.building_workshopst
75+
---@return boolean
76+
function makeTotem(unit, workshop)
77+
local job = make_job()
78+
job.job_type = df.job_type.MakeTotem
79+
job.mat_type = -1
80+
81+
local jitem = df.job_item:new()
82+
jitem.item_type = df.item_type.NONE --the game seems to leave this uninitialized
83+
jitem.mat_type = -1
84+
jitem.mat_index = -1
85+
jitem.quantity = 1
86+
jitem.vector_id = df.job_item_vector_id.ANY_REFUSE
87+
jitem.flags1.unrotten = true
88+
jitem.flags2.totemable = true
89+
jitem.flags2.body_part = true
90+
job.job_items.elements:insert('#', jitem)
91+
92+
assignToWorkshop(job, workshop)
93+
return dfhack.job.addWorker(job, unit)
94+
end
95+
96+
---make totem at specified workshop
97+
---@param unit df.unit
98+
---@param workshop df.building_workshopst
99+
---@return boolean
100+
function makeHornCrafts(unit, workshop)
101+
local job = make_job()
102+
job.job_type = df.job_type.MakeCrafts
103+
job.mat_type = -1
104+
job.material_category.horn = true
105+
106+
local jitem = df.job_item:new()
107+
jitem.item_type = df.item_type.NONE --the game seems to leave this uninitialized
108+
jitem.mat_type = -1
109+
jitem.mat_index = -1
110+
jitem.quantity = 1
111+
jitem.vector_id = df.job_item_vector_id.ANY_REFUSE
112+
jitem.flags1.unrotten = true
113+
jitem.flags2.horn = true
114+
jitem.flags2.body_part = true
115+
job.job_items.elements:insert('#', jitem)
116+
117+
assignToWorkshop(job, workshop)
118+
return dfhack.job.addWorker(job, unit)
119+
end
120+
17121
---make bone crafts at specified workshop
18122
---@param unit df.unit
19123
---@param workshop df.building_workshopst
@@ -23,7 +127,6 @@ function makeBoneCraft(unit, workshop)
23127
job.job_type = df.job_type.MakeCrafts
24128
job.mat_type = -1
25129
job.material_category.bone = true
26-
job.pos = xyz2pos(workshop.centerx, workshop.centery, workshop.z)
27130

28131
local jitem = df.job_item:new()
29132
jitem.item_type = df.item_type.NONE
@@ -36,8 +139,7 @@ function makeBoneCraft(unit, workshop)
36139
jitem.flags2.body_part = true
37140
job.job_items.elements:insert('#', jitem)
38141

39-
dfhack.job.addGeneralRef(job, df.general_ref_type.BUILDING_HOLDER, workshop.id)
40-
workshop.jobs:insert("#", job)
142+
assignToWorkshop(job, workshop)
41143
return dfhack.job.addWorker(job, unit)
42144
end
43145

@@ -49,7 +151,6 @@ function makeRockCraft(unit, workshop)
49151
local job = make_job()
50152
job.job_type = df.job_type.MakeCrafts
51153
job.mat_type = 0
52-
job.pos = xyz2pos(workshop.centerx, workshop.centery, workshop.z)
53154

54155
local jitem = df.job_item:new()
55156
jitem.item_type = df.item_type.BOULDER
@@ -61,12 +162,27 @@ function makeRockCraft(unit, workshop)
61162
jitem.flags3.hard = true
62163
job.job_items.elements:insert('#', jitem)
63164

64-
dfhack.job.addGeneralRef(job, df.general_ref_type.BUILDING_HOLDER, workshop.id)
65-
workshop.jobs:insert("#", job)
66-
165+
assignToWorkshop(job, workshop)
67166
return dfhack.job.addWorker(job, unit)
68167
end
69168

169+
---categorize and count crafting materials (for Craftsdwarf's workshop)
170+
---@param tab table<string,integer>
171+
---@param item df.item
172+
local function categorize_craft(tab,item)
173+
if df.item_corpsepiecest:is_instance(item) then
174+
if item.corpse_flags.bone then
175+
tab['bone'] = (tab['bone'] or 0) + item.material_amount.Bone
176+
elseif item.corpse_flags.skull then
177+
tab['skull'] = (tab['skull'] or 0) + 1
178+
elseif item.corpse_flags.horn then
179+
tab['horn'] = (tab['horn'] or 0) + item.material_amount.Horn
180+
end
181+
elseif df.item_boulderst:is_instance(item) then
182+
tab['boulder'] = (tab['boulder'] or 0) + 1
183+
end
184+
end
185+
70186
-- script logic
71187

72188
local GLOBAL_KEY = 'idle-crafting'
@@ -180,6 +296,32 @@ function unitIsAvailable(unit)
180296
return true
181297
end
182298

299+
---select crafting job based on available resources
300+
---@param workshop df.building_workshopst
301+
---@return (fun(unit:df.unit, workshop:df.building_workshopst):boolean)?
302+
function select_crafting_job(workshop)
303+
local tab = {}
304+
for_inputs(workshop, curry(categorize_craft,tab))
305+
local blocked_labors = workshop.profile.blocked_labors
306+
if blocked_labors[STONE_CRAFT] then
307+
tab['boulder'] = nil
308+
end
309+
if blocked_labors[BONE_CARVE] then
310+
tab['bone'] = nil
311+
tab['skull'] = nil
312+
tab['horn'] = nil
313+
end
314+
local material = weightedChoice(tab)
315+
if material == 'bone' then return makeBoneCraft
316+
elseif material == 'skull' then return makeTotem
317+
elseif material == 'horn' then return makeHornCrafts
318+
elseif material == 'boulder' then return makeRockCraft
319+
else
320+
return nil
321+
end
322+
end
323+
324+
183325
---check if unit is ready and try to create a crafting job for it
184326
---@param workshop df.building_workshopst
185327
---@param idx integer "index of the unit's group"
@@ -200,19 +342,31 @@ local function processUnit(workshop, idx, unit_id)
200342
end
201343
-- We have an available unit
202344
local success = false
203-
if workshop.profile.blocked_labors[STONE_CRAFT] == false then
204-
success = makeRockCraft(unit, workshop)
205-
end
206-
if not success and workshop.profile.blocked_labors[BONE_CARVE] == false then
207-
success = makeBoneCraft(unit, workshop)
345+
if #workshop.profile.links.take_from_pile == 0 then
346+
-- can we do something smarter here?
347+
if workshop.profile.blocked_labors[STONE_CRAFT] == false then
348+
success = makeRockCraft(unit, workshop)
349+
end
350+
if not success and workshop.profile.blocked_labors[BONE_CARVE] == false then
351+
success = makeBoneCraft(unit, workshop)
352+
end
353+
if not success then
354+
dfhack.printerr('idle-crafting: profile allows neither bone carving nor stonecrafting')
355+
end
356+
else
357+
local craftItem = select_crafting_job(workshop)
358+
if craftItem then
359+
success = craftItem(unit, workshop)
360+
else
361+
print('idle-crafting: workshop has no usable materials in linked stockpiles')
362+
failing[workshop.id] = true
363+
end
208364
end
209365
if success then
210366
-- Why is the encoding still wrong, even when using df2console?
211367
print('idle-crafting: assigned crafting job to ' .. dfhack.df2console(dfhack.units.getReadableName(unit)))
212368
watched[idx][unit_id] = nil
213369
allowed[workshop.id] = df.global.world.frame_counter
214-
else
215-
dfhack.printerr('idle-crafting: profile allows neither bone carving nor stonecrafting, disabling workshop')
216370
end
217371
return true
218372
end

necronomicon.lua

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
-- lists books that contain secrets of life and death.
2-
-- Author: Ajhaa
32

43
local argparse = require("argparse")
54

@@ -14,7 +13,7 @@ function get_book_interactions(item)
1413

1514
for _, ref in ipairs (written_content.refs) do
1615
if ref._type == df.general_ref_interactionst then
17-
local interaction = df.global.world.raws.interactions[ref.interaction_id]
16+
local interaction = df.interaction.find(ref.interaction_id)
1817
table.insert(book_interactions, interaction)
1918
end
2019
end
@@ -34,7 +33,7 @@ end
3433
function get_item_artifact(item)
3534
for _, ref in ipairs(item.general_refs) do
3635
if ref._type == df.general_ref_is_artifactst then
37-
return df.global.world.artifacts.all[ref.artifact_id]
36+
return df.artifact_record.find(ref.artifact_id)
3837
end
3938
end
4039
end

0 commit comments

Comments
 (0)