Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion docs/changelog.txt
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,10 @@ 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
- `getplants`: new ``--dry-run`` option counts affected plants without designating, and new ``--brewable``/``--edible``/``--oil``/``--cloth``/``--dye`` trait filters restrict the selection by plant use
- ``quickfix``: repair ``plotinfo.site_id`` on load when DF leaves it unassigned on reclaimed fortresses
- `stocks`: the overlay's ``collapse all`` hotkey now toggles, expanding all categories again when everything is collapsed
- `getplants`: new ``--dry-run`` option counts affected plants without designating, and new ``--brewable``/``--edible``/``--oil``/``--cloth``/``--dye`` trait filters restrict the selection by plant use

## Fixes
- Fix broken weather lookup in ``World::ReadCurrentWeather``
Expand Down
9 changes: 9 additions & 0 deletions docs/plugins/buildingplan.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
++++++++++++++++++++++++++++++++++++

Expand Down Expand Up @@ -227,6 +233,9 @@ 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.

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
item first.
Expand Down
13 changes: 13 additions & 0 deletions plugins/lua/buildingplan.lua
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
18 changes: 18 additions & 0 deletions plugins/lua/buildingplan/planneroverlay.lua
Original file line number Diff line number Diff line change
Expand Up @@ -818,6 +818,19 @@ function PlannerOverlay:init()
buildingplan.setSpecial(uibs.building_type, uibs.building_subtype, uibs.custom_type, 'empty', val)
end,
},
widgets.ToggleHotkeyLabel{
view_id='do_now',
-- 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,
on_change=function(val)
self.state.do_now = val
config:write()
end,
},
widgets.Panel{
visible=function() return #get_cur_filters() > 0 end,
subviews={
Expand Down Expand Up @@ -1512,6 +1525,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()
Expand Down
70 changes: 70 additions & 0 deletions test/plugins/buildingplan.lua
Original file line number Diff line number Diff line change
@@ -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
Loading