263 lines
9.8 KiB
Lua
263 lines
9.8 KiB
Lua
local _ENV = mkmodule('plugins.buildingplan')
|
|
|
|
--[[
|
|
|
|
Native functions:
|
|
|
|
* void setSetting(string name, boolean value)
|
|
* bool isPlanModeEnabled(df::building_type type, int16_t subtype, int32_t custom)
|
|
* bool isPlannableBuilding(df::building_type type, int16_t subtype, int32_t custom)
|
|
* bool isPlannedBuilding(df::building *bld)
|
|
* void addPlannedBuilding(df::building *bld)
|
|
* void doCycle()
|
|
* void scheduleCycle()
|
|
|
|
--]]
|
|
|
|
local dialogs = require('gui.dialogs')
|
|
local guidm = require('gui.dwarfmode')
|
|
require('dfhack.buildings')
|
|
|
|
-- does not need the core suspended
|
|
function get_num_filters(btype, subtype, custom)
|
|
local filters = dfhack.buildings.getFiltersByType(
|
|
{}, btype, subtype, custom)
|
|
if filters then return #filters end
|
|
return 0
|
|
end
|
|
|
|
local function to_title_case(str)
|
|
str = str:gsub('(%a)([%w_]*)',
|
|
function (first, rest) return first:upper()..rest:lower() end)
|
|
str = str:gsub('_', ' ')
|
|
return str
|
|
end
|
|
|
|
local function get_filter(btype, subtype, custom, reverse_idx)
|
|
local filters = dfhack.buildings.getFiltersByType(
|
|
{}, btype, subtype, custom)
|
|
if not filters or reverse_idx < 0 or reverse_idx >= #filters then
|
|
error(string.format('invalid index: %d', reverse_idx))
|
|
end
|
|
return filters[#filters-reverse_idx]
|
|
end
|
|
|
|
-- returns a reasonable label for the item based on the qualities of the filter
|
|
-- does not need the core suspended
|
|
-- reverse_idx is 0-based and is expected to be counted from the *last* filter
|
|
function get_item_label(btype, subtype, custom, reverse_idx)
|
|
local filter = get_filter(btype, subtype, custom, reverse_idx)
|
|
if filter.has_tool_use then
|
|
return to_title_case(df.tool_uses[filter.has_tool_use])
|
|
end
|
|
if filter.item_type then
|
|
return to_title_case(df.item_type[filter.item_type])
|
|
end
|
|
if filter.flags2 and filter.flags2.building_material then
|
|
if filter.flags2.fire_safe then
|
|
return "Fire-safe building material";
|
|
end
|
|
if filter.flags2.magma_safe then
|
|
return "Magma-safe building material";
|
|
end
|
|
return "Generic building material";
|
|
end
|
|
if filter.vector_id then
|
|
return to_title_case(df.job_item_vector_id[filter.vector_id])
|
|
end
|
|
return "Unknown";
|
|
end
|
|
|
|
-- returns whether the items matched by the specified filter can have a quality
|
|
-- rating. This also conveniently indicates whether an item can be decorated.
|
|
-- does not need the core suspended
|
|
-- reverse_idx is 0-based and is expected to be counted from the *last* filter
|
|
function item_can_be_improved(btype, subtype, custom, reverse_idx)
|
|
local filter = get_filter(btype, subtype, custom, reverse_idx)
|
|
if filter.flags2 and filter.flags2.building_material then
|
|
return false;
|
|
end
|
|
return filter.item_type ~= df.item_type.WOOD and
|
|
filter.item_type ~= df.item_type.BLOCKS and
|
|
filter.item_type ~= df.item_type.BAR and
|
|
filter.item_type ~= df.item_type.BOULDER
|
|
end
|
|
|
|
-- needs the core suspended
|
|
-- returns a vector of constructed buildings (usually of size 1, but potentially
|
|
-- more for constructions)
|
|
function construct_buildings_from_ui_state()
|
|
local uibs = df.global.ui_build_selector
|
|
local world = df.global.world
|
|
local direction = world.selected_direction
|
|
local _, width, height = dfhack.buildings.getCorrectSize(
|
|
world.building_width, world.building_height, uibs.building_type,
|
|
uibs.building_subtype, uibs.custom_type, direction)
|
|
-- the cursor is at the center of the building; we need the upper-left
|
|
-- corner of the building
|
|
local pos = guidm.getCursorPos()
|
|
pos.x = pos.x - math.floor(width/2)
|
|
pos.y = pos.y - math.floor(height/2)
|
|
local min_x, max_x = pos.x, pos.x
|
|
local min_y, max_y = pos.y, pos.y
|
|
if width == 1 and height == 1 and
|
|
(world.building_width > 1 or world.building_height > 1) then
|
|
min_x = math.ceil(pos.x - world.building_width/2)
|
|
max_x = math.floor(pos.x + world.building_width/2)
|
|
min_y = math.ceil(pos.y - world.building_height/2)
|
|
max_y = math.floor(pos.y + world.building_height/2)
|
|
end
|
|
local blds = {}
|
|
for y=min_y,max_y do for x=min_x,max_x do
|
|
local bld, err = dfhack.buildings.constructBuilding{
|
|
type=uibs.building_type, subtype=uibs.building_subtype,
|
|
custom=uibs.custom_type, pos=xyz2pos(x, y, pos.z),
|
|
width=width, height=height, direction=direction}
|
|
if err then
|
|
for _,b in ipairs(blds) do
|
|
dfhack.buildings.deconstruct(b)
|
|
end
|
|
error(err)
|
|
end
|
|
-- assign fields for the types that need them. we can't pass them all in
|
|
-- to the call to constructBuilding since attempting to assign unrelated
|
|
-- fields to building types that don't support them causes errors.
|
|
for k,v in pairs(bld) do
|
|
if k == 'friction' then bld.friction = uibs.friction end
|
|
if k == 'use_dump' then bld.use_dump = uibs.use_dump end
|
|
if k == 'dump_x_shift' then bld.dump_x_shift = uibs.dump_x_shift end
|
|
if k == 'dump_y_shift' then bld.dump_y_shift = uibs.dump_y_shift end
|
|
if k == 'speed' then bld.speed = uibs.speed end
|
|
end
|
|
table.insert(blds, bld)
|
|
end end
|
|
return blds
|
|
end
|
|
|
|
--
|
|
-- GlobalSettings dialog
|
|
--
|
|
|
|
local GlobalSettings = defclass(GlobalSettings, dialogs.MessageBox)
|
|
GlobalSettings.focus_path = 'buildingplan_globalsettings'
|
|
|
|
GlobalSettings.ATTRS{
|
|
settings = {}
|
|
}
|
|
|
|
function GlobalSettings:onDismiss()
|
|
for k,v in pairs(self.settings) do
|
|
-- call back into C++ to save changes
|
|
setSetting(k, v)
|
|
end
|
|
end
|
|
|
|
-- does not need the core suspended.
|
|
function show_global_settings_dialog(settings)
|
|
GlobalSettings{
|
|
frame_title="Buildingplan Global Settings",
|
|
settings=settings,
|
|
}:show()
|
|
end
|
|
|
|
function GlobalSettings:toggle_setting(name)
|
|
self.settings[name] = not self.settings[name]
|
|
end
|
|
|
|
function GlobalSettings:get_setting_string(name)
|
|
if self.settings[name] then return 'On' end
|
|
return 'Off'
|
|
end
|
|
|
|
function GlobalSettings:get_setting_pen(name)
|
|
if self.settings[name] then return COLOR_LIGHTGREEN end
|
|
return COLOR_LIGHTRED
|
|
end
|
|
|
|
function GlobalSettings:is_setting_enabled(name)
|
|
return self.settings[name]
|
|
end
|
|
|
|
function GlobalSettings:make_setting_label_token(text, key, name, width)
|
|
return {text=text, key=key, key_sep=': ', key_pen=COLOR_LIGHTGREEN,
|
|
on_activate=self:callback('toggle_setting', name), width=width}
|
|
end
|
|
|
|
function GlobalSettings:make_setting_value_token(name)
|
|
return {text=self:callback('get_setting_string', name),
|
|
enabled=self:callback('is_setting_enabled', name),
|
|
pen=self:callback('get_setting_pen', name),
|
|
dpen=COLOR_GRAY}
|
|
end
|
|
|
|
-- mockup:
|
|
--[[
|
|
Buildingplan Global Settings
|
|
|
|
e: Enable all: Off
|
|
Enables buildingplan for all building types. Use this to avoid having to
|
|
manually enable buildingplan for each building type that you want to plan.
|
|
Note that DFHack quickfort will use buildingplan to manage buildings
|
|
regardless of whether buildingplan is "enabled" for the building type.
|
|
|
|
Allowed types for generic, fire-safe, and magma-safe building material:
|
|
b: Blocks: On
|
|
s: Boulders: On
|
|
w: Wood: On
|
|
r: Bars: Off
|
|
Changes to these settings will be applied to newly-planned buildings.
|
|
|
|
A: Apply building material filter settings to existing planned buildings
|
|
Use this if your planned buildings can't be completed because the settings
|
|
above were too restrictive when the buildings were originally planned.
|
|
|
|
M: Edit list of materials to avoid
|
|
potash
|
|
pearlash
|
|
ash
|
|
coal
|
|
Buildingplan will avoid using these material types when a planned building's
|
|
material filter is set to 'any'. They can stil be matched when they are
|
|
explicitly allowed by a planned building's material filter. Changes to this
|
|
list take effect for existing buildings immediately.
|
|
|
|
g: Allow bags: Off
|
|
This allows bags to be placed where a 'coffer' is planned.
|
|
|
|
f: Legacy Quickfort Mode: Off
|
|
Compatibility mode for the legacy Python-based Quickfort application. This
|
|
setting is not needed for DFHack quickfort.
|
|
--]]
|
|
function GlobalSettings:init()
|
|
|
|
self.subviews.label:setText{
|
|
self:make_setting_label_token('Enable all', 'CUSTOM_E', 'all_enabled', 12),
|
|
self:make_setting_value_token('all_enabled'), '\n',
|
|
' Enables buildingplan for all building types. Use this to avoid having\n',
|
|
' to manually enable buildingplan for each building type that you want\n',
|
|
' to plan. Note that DFHack quickfort will use buildingplan to manage\n',
|
|
' buildings regardless of whether buildingplan is "enabled" for the\n',
|
|
' building type.\n',
|
|
'\n',
|
|
'Allowed types for generic, fire-safe, and magma-safe building material:\n',
|
|
self:make_setting_label_token('Blocks', 'CUSTOM_B', 'blocks', 10),
|
|
self:make_setting_value_token('blocks'), '\n',
|
|
self:make_setting_label_token('Boulders', 'CUSTOM_S', 'boulders', 10),
|
|
self:make_setting_value_token('boulders'), '\n',
|
|
self:make_setting_label_token('Wood', 'CUSTOM_W', 'logs', 10),
|
|
self:make_setting_value_token('logs'), '\n',
|
|
self:make_setting_label_token('Bars', 'CUSTOM_R', 'bars', 10),
|
|
self:make_setting_value_token('bars'), '\n',
|
|
' Changes to these settings will be applied to newly-planned buildings.\n',
|
|
' If no types are enabled above, then any building material is allowed.\n',
|
|
'\n',
|
|
self:make_setting_label_token('Legacy Quickfort Mode', 'CUSTOM_F',
|
|
'quickfort_mode', 23),
|
|
self:make_setting_value_token('quickfort_mode'), '\n',
|
|
' Compatibility mode for the legacy Python-based Quickfort application.\n',
|
|
' This setting is not needed for DFHack quickfort.'
|
|
}
|
|
end
|
|
|
|
return _ENV
|