From c3ff140e3f9f1a194c2c78d6f2a18ec2eb6f3992 Mon Sep 17 00:00:00 2001 From: Alistair-Afton Date: Fri, 18 Sep 2026 09:30:19 +0200 Subject: [PATCH 1/5] buildingplan: add "Do now" option for planned buildings Adds a toggle to the planner overlay that flags the construction job of each subsequently placed building with do_now, and a toggle on the planned-building inspector overlay to flag or unflag individual buildings. The flag lives on the job, which exists as soon as the building is designated, so it takes effect whenever the job is unsuspended and persists with the save. Closes #2353. --- docs/changelog.txt | 1 + docs/plugins/buildingplan.rst | 11 +++ plugins/lua/buildingplan.lua | 13 ++++ plugins/lua/buildingplan/inspectoroverlay.lua | 17 +++++ plugins/lua/buildingplan/planneroverlay.lua | 16 +++++ test/plugins/buildingplan.lua | 70 +++++++++++++++++++ 6 files changed, 128 insertions(+) create mode 100644 test/plugins/buildingplan.lua diff --git a/docs/changelog.txt b/docs/changelog.txt index be0b51c69e..056cbb1096 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -60,6 +60,7 @@ Template for new versions: ## New Features - `autodump`: new ``undestroy`` option reverts pending item destruction while the game is still paused +- `buildingplan`: new "Do now" option marks the jobs of subsequently placed buildings as high priority; placed buildings can also be flagged or unflagged from the building's info sheet - `stocks`: the overlay's ``collapse all`` hotkey now toggles, expanding all categories again when everything is collapsed ## Fixes diff --git a/docs/plugins/buildingplan.rst b/docs/plugins/buildingplan.rst index 01c864f7cc..42ff4cda5e 100644 --- a/docs/plugins/buildingplan.rst +++ b/docs/plugins/buildingplan.rst @@ -146,6 +146,12 @@ Some building types will have other options available as well, such as a selector for how many weapons you want in weapon traps or whether you want to only build engraved slabs. +If you want placed buildings to be built as soon as possible, you can toggle +the "Do now" option (or hit :kbd:`n`). While it is on, the job for each placed +building is flagged as high priority, so dwarves will prefer it over their +regular work once the building is unsuspended. The option stays on until you +toggle it off again, and it is saved with your configuration. + Setting quality and material filters ++++++++++++++++++++++++++++++++++++ @@ -227,6 +233,11 @@ to the building. If there is a particular building that you need built ASAP, you can click on the "make top priority" button (or hit :kbd:`Ctrl`:kbd:`T`) to bump the items for this building to the front of their respective queues. +You can also click on the "do now" button (or hit :kbd:`Ctrl`:kbd:`N`) to flag +the building's job as high priority, so dwarves will prefer it over their +regular work once the building is unsuspended. Click again (or hit the key +again) to remove the flag. + Note that each item type and filter configuration has its own queue, so even if an item is in queue position 1, there may be other queues that snag the needed item first. diff --git a/plugins/lua/buildingplan.lua b/plugins/lua/buildingplan.lua index d6eb5fb9ae..d094f59c9d 100644 --- a/plugins/lua/buildingplan.lua +++ b/plugins/lua/buildingplan.lua @@ -59,6 +59,19 @@ function is_suspendmanager_enabled() return require('plugins.suspendmanager').isEnabled() end +-- The flag lives on the job, which exists as soon as the building is +-- designated, so it can be set before the job is unsuspended and it persists +-- with the save like any other job flag. +function setDoNow(bld, val) + if not bld or #bld.jobs == 0 then return false end + bld.jobs[0].flags.do_now = val + return true +end + +function getDoNow(bld) + return bld ~= nil and #bld.jobs > 0 and bld.jobs[0].flags.do_now +end + function get_num_filters(btype, subtype, custom) local filters = dfhack.buildings.getFiltersByType({}, btype, subtype, custom) return filters and #filters or 0 diff --git a/plugins/lua/buildingplan/inspectoroverlay.lua b/plugins/lua/buildingplan/inspectoroverlay.lua index 7c2858adcf..2c64a12869 100644 --- a/plugins/lua/buildingplan/inspectoroverlay.lua +++ b/plugins/lua/buildingplan/inspectoroverlay.lua @@ -12,6 +12,11 @@ local function get_building_filters() bld:getType(), bld:getSubtype(), bld:getCustomType()) end +local function get_do_now() + local bld = dfhack.gui.getSelectedBuilding(true) + return require('plugins.buildingplan').getDoNow(bld) +end + -------------------------------- -- InspectorLine -- @@ -94,6 +99,18 @@ function InspectorOverlay:init() key='CUSTOM_CTRL_T', on_activate=self:callback('make_top_priority'), }, + widgets.HotkeyLabel{ + frame={t=13, l=0}, + label=function() + return ('do now: %s'):format(get_do_now() and 'on' or 'off') + end, + key='CUSTOM_CTRL_N', + on_activate=function() + local buildingplan = require('plugins.buildingplan') + local bld = dfhack.gui.getSelectedBuilding(true) + buildingplan.setDoNow(bld, not get_do_now()) + end, + }, } end diff --git a/plugins/lua/buildingplan/planneroverlay.lua b/plugins/lua/buildingplan/planneroverlay.lua index d7163bcd91..e30b4017c4 100644 --- a/plugins/lua/buildingplan/planneroverlay.lua +++ b/plugins/lua/buildingplan/planneroverlay.lua @@ -818,6 +818,17 @@ function PlannerOverlay:init() buildingplan.setSpecial(uibs.building_type, uibs.building_subtype, uibs.custom_type, 'empty', val) end, }, + widgets.ToggleHotkeyLabel{ + view_id='do_now', + frame={b=4, l=1, w=22}, + key='CUSTOM_N', + label='Do now:', + initial_option=self.state.do_now or false, + on_change=function(val) + self.state.do_now = val + config:write() + end, + }, widgets.Panel{ visible=function() return #get_cur_filters() > 0 end, subviews={ @@ -1512,6 +1523,11 @@ function PlannerOverlay:place_building(placement_data, chosen_items) end end buildingplan.addPlannedBuilding(bld) + -- the job already exists at designation time; flagging it now means it + -- is posted with do_now whenever it gets unsuspended + if self.state.do_now then + buildingplan.setDoNow(bld, true) + end end buildingplan.scheduleCycle() uibs.selection_pos:clear() diff --git a/test/plugins/buildingplan.lua b/test/plugins/buildingplan.lua new file mode 100644 index 0000000000..f2f48e92f2 --- /dev/null +++ b/test/plugins/buildingplan.lua @@ -0,0 +1,70 @@ +config.mode = 'fortress' +config.target = 'buildingplan' + +local buildingplan = require('plugins.buildingplan') + +local function find_floor_pos() + for _, unit in ipairs(df.global.world.units.active) do + for dx = -4, 4 do + for dy = -4, 4 do + local pos = xyz2pos(unit.pos.x + dx, unit.pos.y + dy, unit.pos.z) + local tt = dfhack.maps.getTileType(pos) + local block = dfhack.maps.getTileBlock(pos) + if tt and block + and df.tiletype.attrs[tt].shape == df.tiletype_shape.FLOOR + and block.occupancy[pos.x % 16][pos.y % 16].building == 0 then + return pos + end + end + end + end +end + +local function place_box(pos) + return dfhack.buildings.constructBuilding{ + pos=pos, + type=df.building_type.Box, subtype=-1, custom=-1, + width=1, height=1, + filters=dfhack.buildings.getFiltersByType({}, df.building_type.Box, -1, -1), + } +end + +function test.set_get_do_now() + local pos = find_floor_pos() + expect.ne(nil, pos, 'test needs a free floor tile') + local bld = place_box(pos) + expect.ne(nil, bld, 'failed to place test building') + dfhack.with_finalize( + function() dfhack.buildings.deconstruct(bld) end, + function() + expect.false_(buildingplan.getDoNow(bld)) + expect.true_(buildingplan.setDoNow(bld, true)) + expect.true_(buildingplan.getDoNow(bld)) + expect.true_(bld.jobs[0].flags.do_now) + expect.true_(buildingplan.setDoNow(bld, false)) + expect.false_(buildingplan.getDoNow(bld)) + end) +end + +function test.do_now_invalid_input() + expect.false_(buildingplan.setDoNow(nil, true)) + expect.false_(buildingplan.getDoNow(nil)) +end + +function test.do_now_flag_survives_registration() + local pos = find_floor_pos() + expect.ne(nil, pos, 'test needs a free floor tile') + local bld = place_box(pos) + expect.ne(nil, bld, 'failed to place test building') + dfhack.with_finalize( + function() dfhack.buildings.deconstruct(bld) end, + function() + buildingplan.addPlannedBuilding(bld) + expect.true_(buildingplan.isPlannedBuilding(bld)) + expect.true_(buildingplan.setDoNow(bld, true)) + -- the flag is set on the job directly, so buildingplan's own + -- suspend/cycle handling must not clear it + buildingplan.doCycle() + expect.true_(buildingplan.getDoNow(bld)) + end) +end From 507df207b93d0964ab2573ed122f5287ed83c488 Mon Sep 17 00:00:00 2001 From: Alistair-Afton Date: Fri, 18 Sep 2026 09:45:06 +0200 Subject: [PATCH 2/5] buildingplan: move "Do now" planner toggle off the divider row The b=4 slot in the planner panel's left column is the same row the favorites divider paints across, which erased the label entirely. Relocate to the top of the options block's right column (b=3, l=24), which is always free and always visible. --- plugins/lua/buildingplan/planneroverlay.lua | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/plugins/lua/buildingplan/planneroverlay.lua b/plugins/lua/buildingplan/planneroverlay.lua index e30b4017c4..352286f626 100644 --- a/plugins/lua/buildingplan/planneroverlay.lua +++ b/plugins/lua/buildingplan/planneroverlay.lua @@ -820,7 +820,9 @@ function PlannerOverlay:init() }, widgets.ToggleHotkeyLabel{ view_id='do_now', - frame={b=4, l=1, w=22}, + -- b=4 in the left column is the favorites divider row; the first + -- free row of the options block is b=3 in the right column + frame={b=3, l=24, w=25}, key='CUSTOM_N', label='Do now:', initial_option=self.state.do_now or false, From bdb45f93475ed6dd185e25f47aca0f0f63ba778a Mon Sep 17 00:00:00 2001 From: Alistair-Afton Date: Fri, 18 Sep 2026 09:49:34 +0200 Subject: [PATCH 3/5] buildingplan: hide inspector "do now" toggle when prioritize covers it The prioritize overlay already exposes job.flags.do_now as "Make top priority" for active construction jobs. Gate the inspector toggle to suspended jobs (or a disabled prioritize overlay) so the same flag is never controlled from two places at once. --- docs/plugins/buildingplan.rst | 4 +++- plugins/lua/buildingplan/inspectoroverlay.lua | 16 ++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/docs/plugins/buildingplan.rst b/docs/plugins/buildingplan.rst index 42ff4cda5e..0663153630 100644 --- a/docs/plugins/buildingplan.rst +++ b/docs/plugins/buildingplan.rst @@ -236,7 +236,9 @@ bump the items for this building to the front of their respective queues. You can also click on the "do now" button (or hit :kbd:`Ctrl`:kbd:`N`) to flag the building's job as high priority, so dwarves will prefer it over their regular work once the building is unsuspended. Click again (or hit the key -again) to remove the flag. +again) to remove the flag. This option is only shown while the job is +suspended; once the job becomes active, the `prioritize` overlay provides the +same toggle as "Make top priority". Note that each item type and filter configuration has its own queue, so even if an item is in queue position 1, there may be other queues that snag the needed diff --git a/plugins/lua/buildingplan/inspectoroverlay.lua b/plugins/lua/buildingplan/inspectoroverlay.lua index 2c64a12869..6dc1a4588e 100644 --- a/plugins/lua/buildingplan/inspectoroverlay.lua +++ b/plugins/lua/buildingplan/inspectoroverlay.lua @@ -17,6 +17,21 @@ local function get_do_now() return require('plugins.buildingplan').getDoNow(bld) end +-- while the construction job is active, the prioritize overlay provides the +-- do_now toggle; only offer ours while the job is suspended (or when the +-- prioritize overlay isn't in play at all) +local function should_show_do_now() + local bld = dfhack.gui.getSelectedBuilding(true) + if not bld or #bld.jobs == 0 or bld.jobs[0].flags.suspend then + return true + end + local state = overlay.get_state() + local name = 'prioritize.enroute' + if not state.db[name] then return true end + local cfg = state.config[name] + return cfg ~= nil and cfg.enabled == false +end + -------------------------------- -- InspectorLine -- @@ -105,6 +120,7 @@ function InspectorOverlay:init() return ('do now: %s'):format(get_do_now() and 'on' or 'off') end, key='CUSTOM_CTRL_N', + visible=should_show_do_now, on_activate=function() local buildingplan = require('plugins.buildingplan') local bld = dfhack.gui.getSelectedBuilding(true) From 39caf45f812dea4b9d6ad752103ffb4552116ec2 Mon Sep 17 00:00:00 2001 From: Alistair-Afton Date: Fri, 18 Sep 2026 09:53:48 +0200 Subject: [PATCH 4/5] buildingplan: drop inspector "do now" toggle The prioritize overlay already exposes job.flags.do_now as "Make top priority" for construction jobs, so a second control in the building status panel would be redundant. --- docs/plugins/buildingplan.rst | 8 ++--- plugins/lua/buildingplan/inspectoroverlay.lua | 33 ------------------- 2 files changed, 2 insertions(+), 39 deletions(-) diff --git a/docs/plugins/buildingplan.rst b/docs/plugins/buildingplan.rst index 0663153630..e06b3586a8 100644 --- a/docs/plugins/buildingplan.rst +++ b/docs/plugins/buildingplan.rst @@ -233,12 +233,8 @@ to the building. If there is a particular building that you need built ASAP, you can click on the "make top priority" button (or hit :kbd:`Ctrl`:kbd:`T`) to bump the items for this building to the front of their respective queues. -You can also click on the "do now" button (or hit :kbd:`Ctrl`:kbd:`N`) to flag -the building's job as high priority, so dwarves will prefer it over their -regular work once the building is unsuspended. Click again (or hit the key -again) to remove the flag. This option is only shown while the job is -suspended; once the job becomes active, the `prioritize` overlay provides the -same toggle as "Make top priority". +To rush the construction job itself once the building is underway, use the +"Make top priority" toggle in the `prioritize` overlay. Note that each item type and filter configuration has its own queue, so even if an item is in queue position 1, there may be other queues that snag the needed diff --git a/plugins/lua/buildingplan/inspectoroverlay.lua b/plugins/lua/buildingplan/inspectoroverlay.lua index 6dc1a4588e..7c2858adcf 100644 --- a/plugins/lua/buildingplan/inspectoroverlay.lua +++ b/plugins/lua/buildingplan/inspectoroverlay.lua @@ -12,26 +12,6 @@ local function get_building_filters() bld:getType(), bld:getSubtype(), bld:getCustomType()) end -local function get_do_now() - local bld = dfhack.gui.getSelectedBuilding(true) - return require('plugins.buildingplan').getDoNow(bld) -end - --- while the construction job is active, the prioritize overlay provides the --- do_now toggle; only offer ours while the job is suspended (or when the --- prioritize overlay isn't in play at all) -local function should_show_do_now() - local bld = dfhack.gui.getSelectedBuilding(true) - if not bld or #bld.jobs == 0 or bld.jobs[0].flags.suspend then - return true - end - local state = overlay.get_state() - local name = 'prioritize.enroute' - if not state.db[name] then return true end - local cfg = state.config[name] - return cfg ~= nil and cfg.enabled == false -end - -------------------------------- -- InspectorLine -- @@ -114,19 +94,6 @@ function InspectorOverlay:init() key='CUSTOM_CTRL_T', on_activate=self:callback('make_top_priority'), }, - widgets.HotkeyLabel{ - frame={t=13, l=0}, - label=function() - return ('do now: %s'):format(get_do_now() and 'on' or 'off') - end, - key='CUSTOM_CTRL_N', - visible=should_show_do_now, - on_activate=function() - local buildingplan = require('plugins.buildingplan') - local bld = dfhack.gui.getSelectedBuilding(true) - buildingplan.setDoNow(bld, not get_do_now()) - end, - }, } end From f497857b117447b9d3d2fcba027e8f5d610f2801 Mon Sep 17 00:00:00 2001 From: Alistair-Afton Date: Fri, 18 Sep 2026 09:58:09 +0200 Subject: [PATCH 5/5] changelog: drop removed buildingplan info-sheet toggle mention --- docs/changelog.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index 056cbb1096..c059c09941 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -60,7 +60,7 @@ Template for new versions: ## New Features - `autodump`: new ``undestroy`` option reverts pending item destruction while the game is still paused -- `buildingplan`: new "Do now" option marks the jobs of subsequently placed buildings as high priority; placed buildings can also be flagged or unflagged from the building's info sheet +- `buildingplan`: new "Do now" option marks the jobs of subsequently placed buildings as high priority - `stocks`: the overlay's ``collapse all`` hotkey now toggles, expanding all categories again when everything is collapsed ## Fixes