Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[quickfort] implement burrow mode #882

Merged
merged 3 commits into from
Nov 3, 2023
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
1 change: 1 addition & 0 deletions changelog.txt
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ Template for new versions:

## New Features
- `gui/design`: show selected dimensions next to the mouse cursor when designating with vanilla tools, for example when painting a burrow or designating digging
- `quickfort`: new blueprint mode for designating burrows

## Fixes
- `gui/unit-syndromes`: show the syndrome names properly in the UI
Expand Down
15 changes: 15 additions & 0 deletions gui/civ-alert.lua
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,21 @@ local function clear_alarm()
df.global.plotinfo.alerts.civ_alert_idx = 0
end

function set_civalert_burrow_if_unset(burrow)
local burrows = get_civ_alert().burrows
if #burrows == 0 then
burrows:insert('#', burrow.id)
end
end

function unset_civalert_burrow_if_set(burrow)
local burrows = get_civ_alert().burrows
if #burrows > 0 and burrows[0] == burrow.id then
burrows:resize(0)
clear_alarm()
end
end

local function toggle_civalert_burrow(id)
local burrows = get_civ_alert().burrows
if #burrows == 0 then
Expand Down
201 changes: 201 additions & 0 deletions internal/quickfort/burrow.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
-- burrow-related data and logic for the quickfort script
--@ module = true

if not dfhack_flags.module then
qerror('this script cannot be called directly')
end

local civalert = reqscript('gui/civ-alert')
local quickfort_common = reqscript('internal/quickfort/common')
local quickfort_map = reqscript('internal/quickfort/map')
local quickfort_parse = reqscript('internal/quickfort/parse')
local quickfort_preview = reqscript('internal/quickfort/preview')
local utils = require('utils')

local log = quickfort_common.log
local logfn = quickfort_common.logfn

local burrow_db = {
a={label='Add', add=true},
e={label='Erase', add=false},
}

local function custom_burrow(_, keys)
local token_and_label, props_start_pos = quickfort_parse.parse_token_and_label(keys, 1, '%w')
if not token_and_label or not rawget(burrow_db, token_and_label.token) then return nil end
local db_entry = copyall(burrow_db[token_and_label.token])
local props = quickfort_parse.parse_properties(keys, props_start_pos)
if props.name then
db_entry.name = props.name
props.name = nil
end
if db_entry.add and props.create == 'true' then
db_entry.create = true
props.create = nil
end
if db_entry.add and props.civalert == 'true' then
db_entry.civalert = true
props.civalert = nil
end
if db_entry.add and props.autochop_clear == 'true' then
db_entry.autochop_clear = true
props.autochop_clear = nil
end
if db_entry.add and props.autochop_chop == 'true' then
db_entry.autochop_chop = true
props.autochop_chop = nil
end

for k,v in pairs(props) do
dfhack.printerr(('unhandled property for symbol "%s": "%s"="%s"'):format(
token_and_label.token, k, v))
end

return db_entry
end

setmetatable(burrow_db, {__index=custom_burrow})

local burrows = df.global.plotinfo.burrows

local function create_burrow(name)
local b = df.burrow:new()
b.id = burrows.next_id
burrows.next_id = burrows.next_id + 1
if name then
b.name = name
end
b.symbol_index = math.random(0, 22)
b.texture_r = math.random(0, 255)
b.texture_g = math.random(0, 255)
b.texture_b = math.random(0, 255)
b.texture_br = 255 - b.texture_r
b.texture_bg = 255 - b.texture_g
b.texture_bb = 255 - b.texture_b
burrows.list:insert('#', b)
return b
end

local function do_burrow(ctx, db_entry, pos)
local stats = ctx.stats
local b
if db_entry.name then
b = dfhack.burrows.findByName(db_entry.name, true)
end
if not b and db_entry.add then
if db_entry.create then
b = create_burrow(db_entry.name)
stats.burrow_created.value = stats.burrow_created.value + 1
else
log('could not find burrow to add to')
return
end
end
if b then
dfhack.burrows.setAssignedTile(b, pos, db_entry.add)
stats['burrow_tiles_'..(db_entry.add and 'added' or 'removed')].value =
stats['burrow_tiles_'..(db_entry.add and 'added' or 'removed')].value + 1
if db_entry.civalert then
if db_entry.add then
civalert.set_civalert_burrow_if_unset(b)
else
civalert.unset_civalert_burrow_if_set(b)
end
end
if db_entry.autochop_clear or db_entry.autochop_chop then
if db_entry.autochop_chop then
dfhack.run_command('autochop', (db_entry.add and '' or 'no')..'chop', tostring(b.id))
end
if db_entry.autochop_clear then
dfhack.run_command('autochop', (db_entry.add and '' or 'no')..'clear', tostring(b.id))
end
end
if not db_entry.add and db_entry.create and #dfhack.burrows.listBlocks(b) == 0 then
dfhack.burrows.clearTiles(b)
local _, _, idx = utils.binsearch(burrows.list, b.id, 'id')
if idx then
burrows.list:erase(idx)
b:delete()
stats.burrow_destroyed.value = stats.burrow_destroyed.value + 1
end
end
elseif not db_entry.add then
for _,burrow in ipairs(burrows.list) do
dfhack.burrows.setAssignedTile(burrow, pos, false)
end
stats.burrow_tiles_removed.value = stats.burrow_tiles_removed.value + 1
end
end

function do_run_impl(zlevel, grid, ctx, invert)
local stats = ctx.stats
stats.burrow_created = stats.burrow_created or
{label='Burrows created', value=0}
stats.burrow_destroyed = stats.burrow_destroyed or
{label='Burrows destroyed', value=0}
stats.burrow_tiles_added = stats.burrow_tiles_added or
{label='Burrow tiles added', value=0}
stats.burrow_tiles_removed = stats.burrow_tiles_removed or
{label='Burrow tiles removed', value=0}

ctx.bounds = ctx.bounds or quickfort_map.MapBoundsChecker{}
for y, row in pairs(grid) do
for x, cell_and_text in pairs(row) do
local cell, text = cell_and_text.cell, cell_and_text.text
local pos = xyz2pos(x, y, zlevel)
log('applying spreadsheet cell %s with text "%s" to map' ..
' coordinates (%d, %d, %d)', cell, text, pos.x, pos.y, pos.z)
local db_entry = nil
local keys, extent = quickfort_parse.parse_cell(ctx, text)
if keys then db_entry = burrow_db[keys] end
if not db_entry then
dfhack.printerr(('invalid key sequence: "%s" in cell %s')
:format(text, cell))
stats.invalid_keys.value = stats.invalid_keys.value + 1
goto continue
end
if invert then
db_entry = copyall(db_entry)
db_entry.add = not db_entry.add
end
if extent.specified then
-- shift pos to the upper left corner of the extent and convert
-- the extent dimensions to positive, simplifying the logic below
pos.x = math.min(pos.x, pos.x + extent.width + 1)
pos.y = math.min(pos.y, pos.y + extent.height + 1)
end
for extent_x=1,math.abs(extent.width) do
for extent_y=1,math.abs(extent.height) do
local extent_pos = xyz2pos(
pos.x+extent_x-1,
pos.y+extent_y-1,
pos.z)
if not ctx.bounds:is_on_map(extent_pos) then
log('coordinates out of bounds; skipping (%d, %d, %d)',
extent_pos.x, extent_pos.y, extent_pos.z)
stats.out_of_bounds.value =
stats.out_of_bounds.value + 1
else
quickfort_preview.set_preview_tile(ctx, extent_pos, true)
if not ctx.dry_run then
do_burrow(ctx, db_entry, extent_pos)
end
end
end
end
::continue::
end
end
end

function do_run(zlevel, grid, ctx)
do_run_impl(zlevel, grid, ctx, false)
end

function do_orders()
log('nothing to do for blueprints in mode: burrow')
end

function do_undo(zlevel, grid, ctx)
do_run_impl(zlevel, grid, ctx, true)
end
1 change: 1 addition & 0 deletions internal/quickfort/parse.lua
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ valid_modes = utils.invert({
'build',
'place',
'zone',
'burrow',
'meta',
'notes',
'ignore',
Expand Down
1 change: 1 addition & 0 deletions quickfort.lua
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ function refresh_scripts()
reqscript('internal/quickfort/api')
reqscript('internal/quickfort/build')
reqscript('internal/quickfort/building')
reqscript('internal/quickfort/burrow')
reqscript('internal/quickfort/command')
reqscript('internal/quickfort/common')
reqscript('internal/quickfort/dig')
Expand Down