Skip to content

Commit 6549e57

Browse files
authored
Merge pull request DFHack#997 from myk002/myk_stuck_worship
[fix/stuck-worship] new tool: prevent units getting stuck worshipping
2 parents ea7cc80 + 192b969 commit 6549e57

File tree

4 files changed

+127
-0
lines changed

4 files changed

+127
-0
lines changed

changelog.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ Template for new versions:
2727
# Future
2828

2929
## New Tools
30+
- `fix/stuck-worship`: fix prayer so units don't get stuck in uninterruptible "Worship!" states
3031

3132
## New Features
3233
- `gui/settings-manager`: add import, export, and autoload for work details

docs/fix/stuck-worship.rst

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
fix/stuck-worship
2+
=================
3+
4+
.. dfhack-tool::
5+
:summary: Prevent dwarves from getting stuck in Worship! states.
6+
:tags: fort bugfix units
7+
8+
Dwarves that need to pray to multiple deities sometimes get stuck in a state
9+
where they repeatedly fulfill the need to pray/worship one deity but ignore the
10+
others. The intense need to pray to the other deities causes the dwarf to start
11+
a purple (uninterruptible) ``Worship!`` activity, but since those needs are
12+
never satisfied, the dwarf becomes stuck and effectively useless. More info on
13+
this problem at :bug:`10918`.
14+
15+
This fix analyzes all units that are actively praying/worshipping and detects
16+
when they have become stuck (or are on the path to becoming stuck). It then
17+
adjusts the distribution of need so when the dwarf finishes their current
18+
prayer, a different prayer need will be fulfilled.
19+
20+
This fix will run automatically if it is enabled in the ``Bugfixes`` tab in
21+
`gui/control-panel`. If it is disabled there, you can still run it as needed.
22+
You'll know it worked if your units go from ``Worship!`` to a prayer to some
23+
specific deity or if the unit just stops praying altogether and picks up
24+
another task.
25+
26+
Usage
27+
-----
28+
29+
::
30+
31+
fix/stuck-worship

fix/stuck-worship.lua

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
local function for_pray_need(needs, fn)
2+
for idx, need in ipairs(needs) do
3+
if need.id == df.need_type.PrayOrMeditate then
4+
fn(idx, need)
5+
end
6+
end
7+
end
8+
9+
local function shuffle_prayer_needs(needs, prayer_targets)
10+
local idx_of_prayer_target, max_focus_level
11+
local idx_of_min_focus_level, min_focus_level
12+
for_pray_need(needs, function(idx, need)
13+
-- only shuffle if the need for one of the current prayer targets
14+
-- is already met
15+
if prayer_targets[need.deity_id] and need.focus_level > -1000 and
16+
(not max_focus_level or need.focus_level > max_focus_level)
17+
then
18+
idx_of_prayer_target = idx
19+
max_focus_level = need.focus_level
20+
21+
-- find a need that hasn't been met outside of the current prayer targets
22+
elseif not prayer_targets[need.deity_id] and
23+
need.focus_level <= -1000 and
24+
(not min_focus_level or need.focus_level < min_focus_level)
25+
then
26+
idx_of_min_focus_level = idx
27+
min_focus_level = need.focus_level
28+
end
29+
end)
30+
31+
-- if a need inside the prayer group is met and a need outside of the
32+
-- prayer group is not met, transfer the credit outside of the prayer group
33+
if idx_of_prayer_target and idx_of_min_focus_level then
34+
needs[idx_of_min_focus_level].focus_level = needs[idx_of_prayer_target].focus_level
35+
needs[idx_of_prayer_target].focus_level = min_focus_level
36+
return true
37+
end
38+
39+
if not idx_of_prayer_target then return end
40+
41+
-- otherwise, if the only unmet needs are inside the prayer group,
42+
-- set the credit inside the prayer group to the level of the met need
43+
-- we found earlier
44+
local modified = false
45+
for_pray_need(needs, function(_, need)
46+
if prayer_targets[need.deity_id] and need.focus_level <= -1000 then
47+
need.focus_level = needs[idx_of_prayer_target].focus_level
48+
modified = true
49+
end
50+
end)
51+
return modified
52+
end
53+
54+
local function get_prayer_targets(unit)
55+
for _, sa in ipairs(unit.social_activities) do
56+
local ae = df.activity_entry.find(sa)
57+
if not ae or ae.type ~= df.activity_entry_type.Prayer then
58+
goto next_activity
59+
end
60+
for _, ev in ipairs(ae.events) do
61+
if not df.activity_event_worshipst:is_instance(ev) then
62+
goto next_event
63+
end
64+
for _, hfid in ipairs(ev.participants.histfigs) do
65+
local hf = df.historical_figure.find(hfid)
66+
if not hf then goto next_hf end
67+
local deity_set = {}
68+
for _, hf_link in ipairs(hf.histfig_links) do
69+
if df.histfig_hf_link_deityst:is_instance(hf_link) then
70+
deity_set[hf_link.target_hf] = true
71+
end
72+
end
73+
if next(deity_set) then return deity_set end
74+
::next_hf::
75+
end
76+
::next_event::
77+
end
78+
::next_activity::
79+
end
80+
end
81+
82+
for _,unit in ipairs(dfhack.units.getCitizens(true)) do
83+
local prayer_targets = get_prayer_targets(unit)
84+
if not unit.status.current_soul or not prayer_targets then
85+
goto next_unit
86+
end
87+
local needs = unit.status.current_soul.personality.needs
88+
if shuffle_prayer_needs(needs, prayer_targets) then
89+
print('rebalanced prayer needs for ' ..
90+
dfhack.df2console(dfhack.TranslateName(dfhack.units.getVisibleName(unit))))
91+
end
92+
::next_unit::
93+
end

internal/control-panel/registry.lua

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,8 @@ COMMANDS_BY_IDX = {
7070
{command='fix/stuck-instruments', group='bugfix', mode='repeat', default=true,
7171
desc='Fix activity references on stuck instruments to make them usable again.',
7272
params={'--time', '1', '--timeUnits', 'days', '--command', '[', 'fix/stuck-instruments', ']'}},
73+
{command='fix/stuck-worship', group='bugfix', mode='repeat', default=true,
74+
params={'--time', '1', '--timeUnits', 'days', '--command', '[', 'fix/stuck-worship', ']'}},
7375
{command='flask-contents', help_command='tweak', group='bugfix', mode='tweak', default=true,
7476
desc='Displays flask contents in the item name, similar to barrels and bins.'},
7577
{command='preserve-tombs', group='bugfix', mode='enable', default=true},

0 commit comments

Comments
 (0)