Skip to content

Commit

Permalink
Merge pull request #4357 from myk002/myk_skill_restrictions
Browse files Browse the repository at this point in the history
[orders] implement skill level restriction overlay
  • Loading branch information
myk002 authored Mar 11, 2024
2 parents 24f3c89 + be17239 commit c81f0ef
Show file tree
Hide file tree
Showing 3 changed files with 176 additions and 18 deletions.
2 changes: 1 addition & 1 deletion docs/changelog.txt
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ Template for new versions:
- `stocks`: add button/hotkey for removing empty categories from the stocks list
- `sort`: updated and reinstated military status/squad membership/burrow membership filter for work animal assignment screen
- `logistics`: ``autoretrain`` will automatically assign trainers to your partially-trained (but not yet domesticated) livestock. this prevents children of partially-trained parents from reverting to wild if you don't notice they were born
- `orders`: add overlay for configuring labor restrictions for workshops
- `orders`: add overlay for configuring labor and skill level restrictions for workshops

## Fixes
- `autochop`: fix underestimation of log yield for cavern mushrooms
Expand Down
53 changes: 37 additions & 16 deletions docs/plugins/orders.rst
Original file line number Diff line number Diff line change
Expand Up @@ -51,37 +51,58 @@ Examples
Overlay
-------

Fort-wide work orders screen
~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Orders plugin functionality is directly available via an `overlay` widget when
the manager orders screen is open. There are hotkeys assigned to export, import,
sort, clear, and recheck conditions. You can also click on the hotkey hints as
if they were buttons. Clearing will ask for confirmation before acting.
the fort-wide work orders screen is open. There are hotkeys assigned to export,
import, sort, clear, and recheck conditions. You can also click on the hotkey
hints as if they were buttons. Clearing will ask for confirmation before acting.

When you open the conditions screen for a manager order, there is also a small
overlay that allows you to recheck conditions for just that order. This is
useful for when the conditions were true when the order started, but they have
become false and now you're just getting repeated cancellation spam as the
order cannot be fulfilled.

Finally, an overlay is added to the "Workers" tab for workshop and furnace
buildings that support orders that can be categorized by labor type. On the
overlay, you can configure the workshop to only accept general work orders that
pertain to specific labors (the list of allowed labors is different for every
workshop).
Workshop Workers tab
~~~~~~~~~~~~~~~~~~~~

For workshops that do *not* have a workshop master assigned, there is a slider
you can use to restrict the units that perform jobs at that workshop by their
skill level.

Due to space constraints, some skill levels are combined with the adjacent
higher rank on the slider:

- "Competent" includes "Adequate" workers
- "Proficient" includes "Skilled" workers
- "Expert" includes "Adept" workers
- "Accomplished" includes "Professional" workers
- "Master" includes "Great" workers
- "Grand Master" includes "High Master" workers

Finally, a list is shown for workshops that service manager orders of multiple
labor types. You can toggle the listed labors so the workshop only accepts
general work orders that match the enabled labors (the list of allowed labors
is different for every workshop).

For example, by default, all weapon, armor, and blacksmithing general manager
orders get sent to all forges. With labor restrictions, you can designate
specific forges to handle just weapons, just armor, or just metalsmithing.
Then, you can assign appropriate legendary masters to each forge, and they will
only receive orders for appropriate products.
orders get sent to all forges that can take general work orders. With labor
restrictions, you can designate specific forges to handle just weapons, just
armor, or just metalsmithing. Then, you can assign appropriate legendary
masters to each forge, and they will only receive orders for appropriate
products.

Simiarly, you can set up Craftsdwarf's workshops to specialize in stone, wood,
or bone.

Regardless of these settings, you can manually assign any task to the workshop
and it will still be completed. The labor restrictions only apply to general
manager work orders scheduled from the fort-wide work orders screen.
Regardless of the labor restriction settings, you can manually assign any task
to the workshop and it will still be completed. The labor restrictions only
apply to general manager work orders scheduled from the fort-wide work orders
screen.

Veteran players may remember this as a vanilla feature in pre-v50 Dwarf
Veteran players may remember these overlays as vanilla features in pre-v50 Dwarf
Fortress. This is actually still the case. The DFHack overlay simply provides a
UI for the vanilla feature hiding beneath the surface.

Expand Down
139 changes: 138 additions & 1 deletion plugins/lua/orders.lua
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ local dialogs = require('gui.dialogs')
local gui = require('gui')
local overlay = require('plugins.overlay')
local textures = require('gui.textures')
local utils = require('utils')
local widgets = require('gui.widgets')

--
Expand Down Expand Up @@ -256,14 +257,149 @@ function RecheckOverlay:onRenderBody(dc)
RecheckOverlay.super.onRenderBody(self, dc)
end

--
-- SkillRestrictionOverlay
--

SkillRestrictionOverlay = defclass(SkillRestrictionOverlay, overlay.OverlayWidget)
SkillRestrictionOverlay.ATTRS{
desc='Adds a UI to the Workers tab for vanilla workshop labor restrictions.',
default_pos={x=-40, y=16},
default_enabled=true,
viewscreens={
'dwarfmode/ViewSheets/BUILDING/Furnace',
'dwarfmode/ViewSheets/BUILDING/Workshop',
},
frame={w=54, h=7},
}

local function can_set_skill_level()
for _,fs in ipairs(dfhack.gui.getFocusStrings(dfhack.gui.getDFViewscreen(true))) do
if fs:endswith('/Workers') then
local bld = dfhack.gui.getSelectedBuilding(true)
if not bld then return false end
return #bld.profile.permitted_workers == 0
end
end
return false
end

local function set_skill_level(which, val, bld)
bld = bld or dfhack.gui.getSelectedBuilding(true)
if not bld then return end
bld.profile[which] = val
end

-- the UI isn't wide enough to accomodate all the skills. select a few to combine
-- into the adjacent tier.
local SKIP_RATINGS = utils.invert{
df.skill_rating.Adequate,
df.skill_rating.Skilled,
df.skill_rating.Adept,
df.skill_rating.Professional,
df.skill_rating.Great,
df.skill_rating.HighMaster,
df.skill_rating.Legendary1,
df.skill_rating.Legendary2,
df.skill_rating.Legendary3,
df.skill_rating.Legendary4,
df.skill_rating.Legendary5,
}

function SkillRestrictionOverlay:init()
local options = {}
local min_rating, max_rating = {}, {}
for ridx in ipairs(df.skill_rating) do
if SKIP_RATINGS[ridx] then goto continue end
local idx = #options + 1
table.insert(options, {label=df.skill_rating.attrs[ridx].caption, value=idx})
min_rating[idx] = max_rating[idx-1] and (max_rating[idx-1]+1) or 0
max_rating[idx] = ridx
::continue::
end
max_rating[#max_rating] = 3000 -- DF value for upper cap

local panel = widgets.Panel{
frame_style=gui.FRAME_MEDIUM,
frame_background=gui.CLEAR_PEN,
}
panel:addviews{
widgets.CycleHotkeyLabel{
view_id='min_skill',
frame={l=0, t=0, w=16},
label='Min skill:',
label_below=true,
key_back='CUSTOM_SHIFT_C',
key='CUSTOM_SHIFT_V',
options=options,
initial_option=options[1].value,
on_change=function(val)
local bld = dfhack.gui.getSelectedBuilding(true)
if self.subviews.max_skill:getOptionValue() < val then
self.subviews.max_skill:setOption(val)
set_skill_level('max_level', max_rating[val], bld)
end
set_skill_level('min_level', min_rating[val], bld)
end,
},
widgets.CycleHotkeyLabel{
view_id='max_skill',
frame={r=1, t=0, w=16},
label='Max skill:',
label_below=true,
key_back='CUSTOM_SHIFT_E',
key='CUSTOM_SHIFT_R',
options=options,
initial_option=options[#options].value,
on_change=function(val)
local bld = dfhack.gui.getSelectedBuilding(true)
if self.subviews.min_skill:getOptionValue() > val then
self.subviews.min_skill:setOption(val)
set_skill_level('min_level', min_rating[val], bld)
end
set_skill_level('max_level', max_rating[val], bld)
end,
},
widgets.RangeSlider{
frame={l=0, t=3},
num_stops=#options,
get_left_idx_fn=function()
return self.subviews.min_skill:getOptionValue()
end,
get_right_idx_fn=function()
return self.subviews.max_skill:getOptionValue()
end,
on_left_change=function(idx) self.subviews.min_skill:setOption(idx, true) end,
on_right_change=function(idx) self.subviews.max_skill:setOption(idx, true) end,
},
}

self:addviews{
panel,
widgets.HelpButton{command='orders'},
}
end

function SkillRestrictionOverlay:render(dc)
if can_set_skill_level() then
SkillRestrictionOverlay.super.render(self, dc)
end
end

function SkillRestrictionOverlay:onInput(keys)
if can_set_skill_level() then
return SkillRestrictionOverlay.super.onInput(self, keys)
end
end

--
-- LaborRestrictionsOverlay
--

LaborRestrictionsOverlay = defclass(LaborRestrictionsOverlay, overlay.OverlayWidget)
LaborRestrictionsOverlay.ATTRS{
desc='Adds a UI to the Workers tab for vanilla workshop labor restrictions.',
default_pos={x=-40,y=24},
default_pos={x=-40, y=24},
default_enabled=true,
viewscreens={
'dwarfmode/ViewSheets/BUILDING/Furnace/Kiln/Workers',
Expand Down Expand Up @@ -431,6 +567,7 @@ end
OVERLAY_WIDGETS = {
recheck=RecheckOverlay,
importexport=OrdersOverlay,
skillrestrictions=SkillRestrictionOverlay,
laborrestrictions=LaborRestrictionsOverlay,
}

Expand Down

0 comments on commit c81f0ef

Please sign in to comment.