Skip to content
Open
26 changes: 26 additions & 0 deletions docs/zSelectLockLabors.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
Select Lock Overlay
===================

This is a DFHack overlay plugin for Dwarf Fortress that simulates the selection and locking of multiple units within the **Work Details** screen. It provides a simple UI interface to batch-toggle units' labor assignments, either by selecting, locking, or both.

Features
--------

- **Overlay Interface**: Integrated directly into the `LABOR/WORK_DETAILS` viewscreen.
- **Action Modes**: Choose between `Select only`, `Lock only`, or `Select + Lock`.
- **Batch Processing**: Specify how many entries to affect and apply actions with one click.
- **Non-Intrusive**: Uses DFHack GUI input simulation to trigger existing functionality.

Usage
-----

Once the plugin is loaded and you're in the `Work Details` screen (e.g., `y` -> `Work Details`), the overlay will automatically appear.

1. Use the **Mode** dropdown to select what action(s) to simulate:
- `Select only`: Just toggles unit selection.
- `Lock only`: Just toggles the lock status.
- `Select + Lock`: Toggles both selection and lock.

2. Adjust the number of entries to apply actions to (default is 7).

3. Press the **RUN** button (or the hotkey defined for it) to execute the actions.
98 changes: 98 additions & 0 deletions zSelectLockLabors.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
--@module=true
local gui = require('gui')
local widgets = require('gui.widgets')
local overlay = require('plugins.overlay')

local SelectLockOverlay = defclass(nil, overlay.OverlayWidget)
SelectLockOverlay.ATTRS {
desc = 'Simulate selection and locking of multiple units.',
viewscreens = {'dwarfmode/Info/LABOR/WORK_DETAILS/Default'},
default_enabled = true,
default_pos = {x = -70, y = 10},
frame = {w = 25, h = 6, r = 1, t = 1, transparent = false},
}

local function simulate_actions(self, count)
gui.simulateInput(dfhack.gui.getCurViewscreen(), 'STANDARDSCROLL_RIGHT')

local function step(i)
if i > count then
for _ = 1, count do
gui.simulateInput(dfhack.gui.getCurViewscreen(), 'STANDARDSCROLL_UP')
gui.simulateInput(dfhack.gui.getCurViewscreen(), 'CONTEXT_SCROLL_UP')
end
self.is_running = false
return
end

if self.action_mode ~= 'lock' then
gui.simulateInput(dfhack.gui.getCurViewscreen(), 'SELECT')
end
if self.action_mode ~= 'select' then
gui.simulateInput(dfhack.gui.getCurViewscreen(), 'UNITLIST_SPECIALIZE')
end
--This line is keyboard arrow down
gui.simulateInput(dfhack.gui.getCurViewscreen(), 'STANDARDSCROLL_DOWN')
--CONTEXT_SCROLL_DOWN helps with consistency. Otherwise the program will miss some units. Line below is scroll wheel down
gui.simulateInput(dfhack.gui.getCurViewscreen(), 'CONTEXT_SCROLL_DOWN')

dfhack.timeout(3, 'frames', function() step(i + 1) end)
end

step(1)
end

function SelectLockOverlay:init()
self.action_mode = 'both'
self.entry_count = 7
self.is_running = false
self:addviews{
widgets.Panel{
frame_style = gui.MEDIUM_FRAME,
frame_background = gui.CLEAR_PEN,
subviews = {
widgets.CycleHotkeyLabel{
view_id = 'action_mode',
frame = {l = 1, t = 1},
label = 'Mode',
option_gap = 2,
options = {
{label = 'Select only', value = 'select'},
{label = 'Lock only', value = 'lock'},
{label = 'Select + Lock', value = 'both'},
},
initial_option = 'both',
on_change = function(val) self.action_mode = val end,
},
widgets.EditField{
numeric = true,
frame = {l = 1, t = 2},
key = 'CUSTOM_CTRL_N',
auto_focus = false,
text = '7',
on_change = function(val)
local num = tonumber(val)
self.entry_count = (num and num > 0 and math.floor(num)) or 7
end,
},
widgets.HotkeyLabel{
view_id = 'run_button',
frame = {l = 1, t = 3},
label = 'RUN',
on_activate = function()
if self.is_running then return end
self.is_running = true
simulate_actions(self, self.entry_count)
end,
enabled = function() return not self.is_running end,
},
},
},
}
end

OVERLAY_WIDGETS = {
select_lock_overlay = SelectLockOverlay,
}

return {}