Skip to content

Commit 58badec

Browse files
authored
Merge pull request #1419 from chdoc/idle-crafting-happy
`idle-crafting`: ignore happy units until their needs are strong
2 parents 9043155 + 1ea20e8 commit 58badec

3 files changed

Lines changed: 60 additions & 12 deletions

File tree

changelog.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,12 @@ Template for new versions:
3030

3131
## New Features
3232

33+
- `idle-crafting`: default to only considering happy and ecstatic units for the highest need threshold
34+
3335
## Fixes
3436

37+
- `idle-crafting`: check that units still have crafting needs before creating a job for them
38+
3539
## Misc Improvements
3640

3741
## Removed

docs/idle-crafting.rst

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,12 @@ Usage
2626
given unit. Units meeting higher thresholds will be prioritized. Defaults
2727
to ``500,1000,10000``.
2828

29+
``idle-crafting happy [yes|no]``
30+
If set to ``no``, "happy" and "ecstatic" dwarves not suffering from
31+
long-term stress will only satisfy their crafting needs when meeting the
32+
highest configured threshold (eg. ``10000`` at default settings). Defaults
33+
to ``no``.
34+
2935
``disable idle-crafting``
3036
Disallow idle crafting at all workshops. You can re-enable idle crafting
3137
at individual Craftsdwarf's workshops.
@@ -34,7 +40,10 @@ Examples
3440
--------
3541

3642
``idle-crafting thresholds 500,1000,10000``
37-
Reset thresholds to defaults.
43+
Reset thresholds to defaults.
44+
45+
``idle-crafting happy yes``
46+
Treat happy and ecstatic dwarves the same as everyone else.
3847

3948
Overlay
4049
-------

idle-crafting.lua

Lines changed: 46 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,9 @@ watched = watched or {}
235235
---@type integer[]
236236
thresholds = thresholds or { 10000, 1000, 500 }
237237

238+
---@type boolean
239+
ignore_happy = ignore_happy == nil and true or ignore_happy
240+
238241
-- persisting a table with numeric keys results in a json array with a huge number of null entries
239242
-- therefore, we convert the keys to strings for persistence
240243
-- also, we clear the frame counter values since the frame counter gets reset on load
@@ -262,7 +265,8 @@ local function persist_state()
262265
dfhack.persistent.saveSiteData(GLOBAL_KEY, {
263266
enabled=enabled,
264267
allowed=to_persist_allowed(),
265-
thresholds=thresholds
268+
thresholds=thresholds,
269+
ignore_happy=ignore_happy
266270
})
267271
end
268272

@@ -273,6 +277,7 @@ local function load_state()
273277
enabled = persisted_data.enabled or false
274278
allowed = from_persist_allowed(persisted_data.allowed) or {}
275279
thresholds = persisted_data.thresholds or { 10000, 1000, 500 }
280+
ignore_happy = persisted_data.ignore_happy == nil and true or ignore_happy
276281
end
277282

278283
--frequently accessed values
@@ -281,16 +286,18 @@ local BONE_CARVE = df.unit_labor['BONE_CARVE']
281286
local STONE_CRAFT = df.unit_labor['STONE_CRAFT']
282287

283288
---negative crafting focus penalty
289+
---@generic T
284290
---@param unit df.unit
285-
---@return number
286-
function getCraftingNeed(unit)
291+
---@param value_if_absent T
292+
---@return number|T
293+
function getCraftingNeed(unit, value_if_absent)
287294
local needs = unit.status.current_soul.personality.needs
288295
for _, need in ipairs(needs) do
289296
if need.id == CraftObject then
290297
return -need.focus_level
291298
end
292299
end
293-
return 0
300+
return value_if_absent
294301
end
295302

296303
local function stop()
@@ -383,8 +390,8 @@ end
383390
---@return boolean "proceed to next workshop"
384391
local function processUnit(workshop, idx, unit_id)
385392
local unit = df.unit.find(unit_id)
386-
-- check that unit is still there and not caged or chained
387-
if not unit or unit.flags1.caged or unit.flags1.chained then
393+
-- check that unit is still there, not caged or chained, and still has crafting needs
394+
if not unit or unit.flags1.caged or unit.flags1.chained or getCraftingNeed(unit, -1) < 0 then
388395
watched[idx][unit_id] = nil
389396
return false
390397
elseif not canAccessWorkshop(unit, workshop) then
@@ -511,16 +518,33 @@ local function main_loop()
511518
num_watched[idx] = 0
512519
end
513520

521+
local max_threshold = thresholds[1]
514522
for _, unit in ipairs(dfhack.units.getCitizens(true, false)) do
523+
524+
local crafting_need = getCraftingNeed(unit, nil)
525+
526+
if not crafting_need then
527+
goto next_unit
528+
end
529+
530+
local is_happy = unit.status.current_soul.personality.stress < -25000 and
531+
unit.status.current_soul.personality.longterm_stress < 0
532+
515533
for idx, threshold in ipairs(thresholds) do
516-
if getCraftingNeed(unit) > threshold then
534+
if ignore_happy and is_happy and threshold < max_threshold then
535+
-- ignore happy and ecstatic units for any threshold but the highest
536+
print(dfhack.df2console(("idle-crafting: skipping happy unit %s"):format(dfhack.units.getReadableName(unit))))
537+
goto next_unit
538+
end
539+
540+
if crafting_need > threshold then
517541
watched[idx][unit.id] = true
518542
num_watched[idx] = num_watched[idx] + 1
519543
watching = true
520-
goto continue
544+
goto next_unit
521545
end
522546
end
523-
::continue::
547+
::next_unit::
524548
end
525549
-- print(('watching %s dwarfs with crafting needs'):format(
526550
-- table.concat(num_watched, '/')
@@ -665,9 +689,12 @@ if not positionals[1] or positionals[1] == 'status' then
665689
---@type integer[]
666690
stats = {}
667691
for _, unit in ipairs(dfhack.units.getCitizens(true, false)) do
668-
local fulfillment = -getCraftingNeed(unit)
692+
local crafting_need = getCraftingNeed(unit, nil)
693+
if not crafting_need then
694+
goto continue
695+
end
669696
for i = 1, 7 do
670-
if fulfillment >= fulfillment_threshold[i] then
697+
if -crafting_need >= fulfillment_threshold[i] then
671698
stats[i] = stats[i] and stats[i] + 1 or 1
672699
goto continue
673700
end
@@ -686,11 +713,19 @@ if not positionals[1] or positionals[1] == 'status' then
686713
format(enabled and 'enabled' or 'disabled', num_workshops))
687714
print(('The thresholds for "craft item" needs are %s'):
688715
format(table.concat(thresholds, ',')))
716+
if ignore_happy then
717+
print('Will only assign crafting jobs to happy dwarves with strong needs')
718+
else
719+
print('Will treat happy units like all other units')
720+
end
721+
689722
elseif positionals[1] == 'thresholds' then
690723
thresholds = argparse.numberList(positionals[2], 'thresholds')
691724
table.sort(thresholds, function (a, b) return a > b end)
692725
print(('Thresholds for "craft item" needs set to %s'):
693726
format(table.concat(thresholds, ',')))
727+
elseif positionals[1] == 'happy' then
728+
ignore_happy = not argparse.boolean(positionals[2], 'happy')
694729
elseif positionals[1] == 'disable' then
695730
allowed = {}
696731
stop()

0 commit comments

Comments
 (0)