Merge branch 'develop' into ea_refactoring

develop
bseiller 2021-01-30 17:39:41 +01:00
commit b1cf65860c
19 changed files with 418 additions and 131 deletions

@ -34,6 +34,7 @@ tinythread_ Zlib \(c\) 2010, Marcus Geelnard
tinyxml_ Zlib \(c\) 2000-2006, Lee Thomason tinyxml_ Zlib \(c\) 2000-2006, Lee Thomason
UTF-8-decoder_ MIT \(c\) 2008-2010, Bjoern Hoehrmann UTF-8-decoder_ MIT \(c\) 2008-2010, Bjoern Hoehrmann
xlsxio_ MIT \(c\) 2016-2020, Brecht Sanders xlsxio_ MIT \(c\) 2016-2020, Brecht Sanders
alt-getopt_ MIT \(c\) 2009 Aleksey Cheusov
=============== ============= ================================================= =============== ============= =================================================
.. _DFHack: https://github.com/DFHack/dfhack .. _DFHack: https://github.com/DFHack/dfhack
@ -52,6 +53,7 @@ xlsxio_ MIT \(c\) 2016-2020, Brecht Sanders
.. _tinyxml: http://www.sourceforge.net/projects/tinyxml .. _tinyxml: http://www.sourceforge.net/projects/tinyxml
.. _UTF-8-decoder: http://bjoern.hoehrmann.de/utf-8/decoder/dfa .. _UTF-8-decoder: http://bjoern.hoehrmann.de/utf-8/decoder/dfa
.. _xlsxio: https://github.com/brechtsanders/xlsxio .. _xlsxio: https://github.com/brechtsanders/xlsxio
.. _alt-getopt: https://github.com/LuaDist/alt-getopt
.. _CC-BY-SA: http://creativecommons.org/licenses/by/3.0/deed.en_US .. _CC-BY-SA: http://creativecommons.org/licenses/by/3.0/deed.en_US

@ -833,6 +833,9 @@ can be omitted.
* ``dfhack.getCompiledDFVersion()`` * ``dfhack.getCompiledDFVersion()``
* ``dfhack.getGitDescription()`` * ``dfhack.getGitDescription()``
* ``dfhack.getGitCommit()`` * ``dfhack.getGitCommit()``
* ``dfhack.getGitXmlCommit()``
* ``dfhack.getGitXmlExpectedCommit()``
* ``dfhack.gitXmlMatch()``
* ``dfhack.isRelease()`` * ``dfhack.isRelease()``
Return information about the DFHack build in use. Return information about the DFHack build in use.
@ -899,6 +902,28 @@ can be omitted.
Note that the returned string may be longer than the input string. For Note that the returned string may be longer than the input string. For
example, ``ä`` is replaced with ``a``, and ``æ`` is replaced with ``ae``. example, ``ä`` is replaced with ``a``, and ``æ`` is replaced with ``ae``.
* ``dfhack.run_command(command[, ...])``
Run an arbitrary DFHack command, with the core suspended, and send output to
the DFHack console. The command can be passed as a table, multiple string
arguments, or a single string argument (not recommended - in this case, the
usual DFHack console tokenization is used).
A ``command_result`` constant starting with ``CR_`` is returned, where ``CR_OK``
indicates success.
The following examples are equivalent::
dfhack.run_command({'ls', '-a'})
dfhack.run_command('ls', '-a')
dfhack.run_command('ls -a') -- not recommended
* ``dfhack.run_command_silent(command[, ...])``
Similar to ``run_command()``, but instead of printing to the console,
returns an ``output, command_result`` pair. ``output`` is a single string -
see ``dfhack.internal.runCommand()`` to obtain colors as well.
Gui module Gui module
---------- ----------
@ -2235,11 +2260,6 @@ Internal API
These functions are intended for the use by dfhack developers, These functions are intended for the use by dfhack developers,
and are only documented here for completeness: and are only documented here for completeness:
* ``dfhack.internal.scripts``
The table used by ``dfhack.run_script()`` to give every script its own
global environment, persistent between calls to the script.
* ``dfhack.internal.getPE()`` * ``dfhack.internal.getPE()``
Returns the PE timestamp of the DF executable (only on Windows) Returns the PE timestamp of the DF executable (only on Windows)
@ -2354,6 +2374,21 @@ and are only documented here for completeness:
This requires an extension to be specified (``.lua`` or ``.rb``) - use This requires an extension to be specified (``.lua`` or ``.rb``) - use
``dfhack.findScript()`` to include the ``.lua`` extension automatically. ``dfhack.findScript()`` to include the ``.lua`` extension automatically.
* ``dfhack.internal.runCommand(command[, use_console])``
Runs a DFHack command with the core suspended. Used internally by the
``dfhack.run_command()`` family of functions.
- ``command``: either a table of strings or a single string which is parsed by
the default console tokenization strategy (not recommended)
- ``use_console``: if true, output is sent directly to the DFHack console
Returns a table with a ``status`` key set to a ``command_result`` constant
(``status = CR_OK`` indicates success). Additionally, if ``use_console`` is
not true, enumerated table entries of the form ``{color, text}`` are included,
e.g. ``result[1][0]`` is the color of the first piece of text printed (a
``COLOR_`` constant). These entries can be iterated over with ``ipairs()``.
* ``dfhack.internal.md5(string)`` * ``dfhack.internal.md5(string)``
Returns the MD5 hash of the given string. Returns the MD5 hash of the given string.
@ -2513,6 +2548,12 @@ environment by the mandatory init file dfhack.lua:
SC_WORLD_LOADED, SC_WORLD_UNLOADED, SC_MAP_LOADED, SC_WORLD_LOADED, SC_WORLD_UNLOADED, SC_MAP_LOADED,
SC_MAP_UNLOADED, SC_VIEWSCREEN_CHANGED, SC_CORE_INITIALIZED SC_MAP_UNLOADED, SC_VIEWSCREEN_CHANGED, SC_CORE_INITIALIZED
* Command result constants (equivalent to ``command_result`` in C++), used by
``dfhack.run_command()`` and related functions:
CR_OK, CR_LINK_FAILURE, CR_NEEDS_CONSOLE, CR_NOT_IMPLEMENTED, CR_FAILURE,
CR_WRONG_USAGE, CR_NOT_FOUND
* Functions already described above * Functions already described above
safecall, qerror, mkmodule, reload safecall, qerror, mkmodule, reload

@ -765,29 +765,32 @@ and displayed with::
The available settings are: The available settings are:
+----------------+---------+---------------------------------------+ +----------------+---------+-----------+---------------------------------------+
| Setting | Default | Description | | Setting | Default | Persisted | Description |
+================+=========+=======================================+ +================+=========+===========+=======================================+
| blocks | true | Allow blocks, boulders, logs, or bars | | all_enabled | false | no | Enable planning mode for all building |
+----------------+---------+ to be matched for generic "building | | | | | types. |
| boulders | true | material" items | +----------------+---------+-----------+---------------------------------------+
+----------------+---------+ | | blocks | true | yes | Allow blocks, boulders, logs, or bars |
| logs | true | | +----------------+---------+ | to be matched for generic "building |
+----------------+---------+ | | boulders | true | | material" items |
| bars | false | | +----------------+---------+ | |
+----------------+---------+---------------------------------------+ | logs | true | | |
| quickfort_mode | false | Enable compatibility mode for the | +----------------+---------+ | |
| | | legacy Python Quickfort (not required | | bars | false | | |
| | | for DFHack quickfort) | +----------------+---------+-----------+---------------------------------------+
+----------------+---------+---------------------------------------+ | quickfort_mode | false | no | Enable compatibility mode for the |
| | | | legacy Python Quickfort (not required |
| | | | for DFHack quickfort) |
+----------------+---------+-----------+---------------------------------------+
For example, to ensure you only use blocks when a "building material" item is required, you For example, to ensure you only use blocks when a "building material" item is required, you
could add this to your ``onMapLoad.init`` file:: could add this to your ``onMapLoad.init`` file::
on-new-fortress buildingplan set boulders false; buildingplan set logs false on-new-fortress buildingplan set boulders false; buildingplan set logs false
You only need to set the settings for new fortresses since your current filter settings Persisted settings (i.e. ``blocks``, ``boulders``, ``logs``, and ``bars``) are saved with
are saved with your game. your game, so you only need to set them to the values you want once.
.. _confirm: .. _confirm:

@ -36,13 +36,23 @@ changelog.txt uses a syntax similar to RST, with a few special sequences:
## Fixes ## Fixes
- `embark-assistant`: fixed order of factors when calculating min temperature - `embark-assistant`: fixed order of factors when calculating min temperature
- `embark-assistant`: improved performance of surveying - `embark-assistant`: improved performance of surveying
- `quickfort`: creating zones no longer causes eventual crashes
- `search`: fixed crash when searching the ``k`` sidebar and navigating to another tile with certain keys, like ``<`` or ``>``
- `stockflow`: fixed ``j`` character being intercepted when naming stockpiles
## Misc Improvements ## Misc Improvements
- `buildingplan`: set global settings from the ``DFHack#`` prompt: e.g. ``buildingplan set boulders false`` - `buildingplan`: set global settings from the ``DFHack#`` prompt: e.g. ``buildingplan set boulders false``
- `buildingplan`: add 'enable all' option for buildingplan (so you don't have to enable all building types individually). this setting is not persisted (just like quickfort_mode is not persisted), but it can be set from onMapLoad.init
## Misc Improvements - `buildingplan`: modified ``Planning Mode`` status in the UI to show whether we're in quickfort mode, enable all mode, or whether just the building type is enabled.
- `quickfort`: Dreamfort blueprint set improvements: add a streamlined checklist for all required dreamfort commands and give names to stockpiles, levers, bridges, and zones - `quickfort`: Dreamfort blueprint set improvements: add a streamlined checklist for all required dreamfort commands and give names to stockpiles, levers, bridges, and zones
## Lua
- ``dfhack.run_command()``: changed to interface directly with the console when possible, which allows interactive commands and commands that detect the console encoding to work properly
- ``processArgsGetopt()`` added to utils.lua, providing a callback interface for parameter parsing and getopt-like flexibility for parameter ordering and combination (see docs in ``library/lua/utils.lua`` and ``library/lua/3rdparty/alt_getopt.lua`` for details).
## Documentation
- Added documentation for Lua's ``dfhack.run_command()`` and variants
# 0.47.04-r4 # 0.47.04-r4
## Fixes ## Fixes
@ -1175,4 +1185,3 @@ changelog.txt uses a syntax similar to RST, with a few special sequences:
- The ``ui_menu_width`` global is now a 2-byte array; the second item is the former ``ui_area_map_width`` global, which is now removed - The ``ui_menu_width`` global is now a 2-byte array; the second item is the former ``ui_area_map_width`` global, which is now removed
- The former ``announcements`` global is now a field in ``d_init`` - The former ``announcements`` global is now a field in ``d_init``
- ``world`` fields formerly beginning with ``job_`` are now fields of ``world.jobs``, e.g. ``world.job_list`` is now ``world.jobs.list`` - ``world`` fields formerly beginning with ``job_`` are now fields of ``world.jobs``, e.g. ``world.job_list`` is now ``world.jobs.list``

@ -405,6 +405,8 @@ namespace DFHack
/// A simple line edit (raw mode) /// A simple line edit (raw mode)
int lineedit(const std::string& prompt, std::string& output, recursive_mutex * lock, CommandHistory & ch) int lineedit(const std::string& prompt, std::string& output, recursive_mutex * lock, CommandHistory & ch)
{ {
if(state == con_lineedit)
return Console::FAILURE;
output.clear(); output.clear();
reset_color(); reset_color();
this->prompt = prompt; this->prompt = prompt;
@ -414,7 +416,9 @@ namespace DFHack
fflush(dfout_C); fflush(dfout_C);
// FIXME: what do we do here??? // FIXME: what do we do here???
//SDL_recursive_mutexV(lock); //SDL_recursive_mutexV(lock);
state = con_lineedit;
std::getline(std::cin, output); std::getline(std::cin, output);
state = con_unclaimed;
//SDL_recursive_mutexP(lock); //SDL_recursive_mutexP(lock);
return output.size(); return output.size();
} }
@ -422,8 +426,6 @@ namespace DFHack
{ {
int count; int count;
if (enable_raw() == -1) return 0; if (enable_raw() == -1) return 0;
if(state == con_lineedit)
return Console::FAILURE;
state = con_lineedit; state = con_lineedit;
count = prompt_loop(lock,ch); count = prompt_loop(lock,ch);
state = con_unclaimed; state = con_unclaimed;

@ -376,6 +376,8 @@ namespace DFHack
} }
int lineedit(const std::string & prompt, std::string & output, recursive_mutex * lock, CommandHistory & ch) int lineedit(const std::string & prompt, std::string & output, recursive_mutex * lock, CommandHistory & ch)
{ {
if(state == con_lineedit)
return Console::FAILURE;
output.clear(); output.clear();
reset_color(); reset_color();
int count; int count;

@ -2822,13 +2822,29 @@ static int internal_diffscan(lua_State *L)
static int internal_runCommand(lua_State *L) static int internal_runCommand(lua_State *L)
{ {
buffered_color_ostream out; color_ostream *out = NULL;
std::unique_ptr<buffered_color_ostream> out_buffer;
command_result res; command_result res;
if (lua_gettop(L) == 0) if (lua_gettop(L) == 0)
{ {
lua_pushstring(L, ""); lua_pushstring(L, "");
} }
int type_1 = lua_type(L, 1); int type_1 = lua_type(L, 1);
bool use_console = lua_toboolean(L, 2);
if (use_console)
{
out = Lua::GetOutput(L);
if (!out)
{
out = &Core::getInstance().getConsole();
}
}
else
{
out_buffer.reset(new buffered_color_ostream());
out = out_buffer.get();
}
if (type_1 == LUA_TTABLE) if (type_1 == LUA_TTABLE)
{ {
std::string command = ""; std::string command = "";
@ -2843,13 +2859,13 @@ static int internal_runCommand(lua_State *L)
lua_pop(L, 1); // remove value, leave key lua_pop(L, 1); // remove value, leave key
} }
CoreSuspender suspend; CoreSuspender suspend;
res = Core::getInstance().runCommand(out, command, args); res = Core::getInstance().runCommand(*out, command, args);
} }
else if (type_1 == LUA_TSTRING) else if (type_1 == LUA_TSTRING)
{ {
std::string command = lua_tostring(L, 1); std::string command = lua_tostring(L, 1);
CoreSuspender suspend; CoreSuspender suspend;
res = Core::getInstance().runCommand(out, command); res = Core::getInstance().runCommand(*out, command);
} }
else else
{ {
@ -2857,10 +2873,14 @@ static int internal_runCommand(lua_State *L)
lua_pushfstring(L, "Expected table, got %s", lua_typename(L, type_1)); lua_pushfstring(L, "Expected table, got %s", lua_typename(L, type_1));
return 2; return 2;
} }
auto fragments = out.fragments();
lua_newtable(L); lua_newtable(L);
lua_pushinteger(L, (int)res); lua_pushinteger(L, (int)res);
lua_setfield(L, -2, "status"); lua_setfield(L, -2, "status");
if (out_buffer)
{
auto fragments = out_buffer->fragments();
int i = 1; int i = 1;
for (auto iter = fragments.begin(); iter != fragments.end(); iter++, i++) for (auto iter = fragments.begin(); iter != fragments.end(); iter++, i++)
{ {
@ -2873,6 +2893,8 @@ static int internal_runCommand(lua_State *L)
lua_rawseti(L, -2, 2); lua_rawseti(L, -2, 2);
lua_rawseti(L, -2, i); lua_rawseti(L, -2, i);
} }
}
lua_pushvalue(L, -1); lua_pushvalue(L, -1);
return 1; return 1;
} }

@ -0,0 +1,169 @@
-- Copyright (c) 2009 Aleksey Cheusov <vle@gmx.net>
--
-- Permission is hereby granted, free of charge, to any person obtaining
-- a copy of this software and associated documentation files (the
-- "Software"), to deal in the Software without restriction, including
-- without limitation the rights to use, copy, modify, merge, publish,
-- distribute, sublicense, and/or sell copies of the Software, and to
-- permit persons to whom the Software is furnished to do so, subject to
-- the following conditions:
--
-- The above copyright notice and this permission notice shall be
-- included in all copies or substantial portions of the Software.
--
-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-- LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-- OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-- WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-- based on https://github.com/LuaDist/alt-getopt/blob/master/alt_getopt.lua
-- MIT licence
-- modified to support aggregation of non-options and to call qerror instead of
-- os.exit() on error. can be used directly or via the utils.processArgs2()
-- wrapper.
-- sh_opts should be in standard getopt format: a string of letters that
-- represent options, each followed by a colon if that option takes an argument.
-- e.g.: 'ak:hv' has three flags (options with no arguments): 'a', 'h', and 'v'
-- and one option that takes an argument: 'k'.
--
-- Options passed to the module to parse can be in any of the following formats:
-- -kVALUE, -k VALUE, --key=VALUE, --key VALUE
-- -abcd is equivalent to -a -b -c -d if none of them accept arguments.
-- -abckVALUE and -abck VALUE are also acceptable (where k is the only option
-- in the string that takes a value).
local _ENV = mkmodule('3rdparty.alt_getopt')
local function get_opt_map(opts)
local i = 1
local len = #opts
local options = {}
for short_opt, accept_arg in opts:gmatch('(%w)(:?)') do
options[short_opt] = #accept_arg
end
return options
end
local function err_unknown_opt(opt)
qerror(string.format('Unknown option "-%s%s"', #opt > 1 and '-' or '', opt))
end
-- resolve aliases into their canonical forms
local function canonicalize(options, opt)
if not options[opt] then
err_unknown_opt(opt)
end
while type(options[opt]) == 'string' do
opt = options[opt]
if not options[opt] then
err_unknown_opt(opt)
end
end
if type(options[opt]) ~= 'number' then
qerror(string.format(
'Option "%s" resolves to non-number for has_arg flag', opt))
end
return opt
end
local function has_arg(options, opt)
return options[canonicalize(options, opt)] == 1
end
-- returns vectors for opts, optargs, and nonoptions
function get_ordered_opts(args, sh_opts, long_opts)
local optind, count, opts, optargs, nonoptions = 1, 1, {}, {}, {}
local options = get_opt_map(sh_opts)
for k,v in pairs(long_opts) do
options[k] = v
end
while optind <= #args do
local a = args[optind]
if a == '--' then
optind = optind + 1
elseif a:sub(1, 2) == '--' then
local pos = a:find('=', 1, true)
if pos then
local opt = a:sub(3, pos-1)
if not has_arg(options, opt) then
qerror(string.format('Bad usage of option "%s"', a))
end
opts[count] = opt
optargs[count] = a:sub(pos+1)
else
local opt = a:sub(3)
opts[count] = opt
if has_arg(options, opt) then
if i == #args then
qerror(string.format(
'Missing value for option "%s"', a))
end
optargs[count] = args[optind+1]
optind = optind + 1
end
end
count = count + 1
elseif a:sub(1, 1) == '-' then
local j
for j=2,#a do
local opt = canonicalize(options, a:sub(j, j))
if not has_arg(options, opt) then
opts[count] = opt
count = count + 1
elseif j == #a then
if optind == #args then
qerror(string.format(
'Missing value for option "-%s"', opt))
end
opts[count] = opt
optargs[count] = args[optind+1]
optind = optind + 1
count = count + 1
else
opts[count] = opt
optargs[count] = a:sub(j+1)
count = count + 1
break
end
end
else
table.insert(nonoptions, args[optind])
end
optind = optind + 1
end
for i=optind,#args do
table.insert(nonoptions, args[i])
end
return opts, optargs, nonoptions
end
-- returns a map of options to their optargs (or 1 if the option doesn't take an
-- argument), and a vector for nonoptions
function get_opts(args, sh_opts, long_opts)
local ret = {}
local opts,optargs,nonoptions = get_ordered_opts(args, sh_opts, long_opts)
for i,v in ipairs(opts) do
if optarg[i] then
ret[v] = optarg[i]
else
ret[v] = 1
end
end
return ret, nonoptions
end
return _ENV

@ -735,8 +735,7 @@ function dfhack.script_help(script_name, extension)
return help return help
end end
local function _run_command(...) local function _run_command(args, use_console)
args = {...}
if type(args[1]) == 'table' then if type(args[1]) == 'table' then
command = args[1] command = args[1]
elseif #args > 1 and type(args[2]) == 'table' then elseif #args > 1 and type(args[2]) == 'table' then
@ -750,11 +749,11 @@ local function _run_command(...)
else else
error('Invalid arguments') error('Invalid arguments')
end end
return internal.runCommand(command) return internal.runCommand(command, use_console)
end end
function dfhack.run_command_silent(...) function dfhack.run_command_silent(...)
local result = _run_command(...) local result = _run_command({...})
local output = "" local output = ""
for i, f in pairs(result) do for i, f in pairs(result) do
if type(f) == 'table' then if type(f) == 'table' then
@ -765,14 +764,7 @@ function dfhack.run_command_silent(...)
end end
function dfhack.run_command(...) function dfhack.run_command(...)
local result = _run_command(...) local result = _run_command({...}, true)
for i, f in pairs(result) do
if type(f) == 'table' then
dfhack.color(f[1])
dfhack.print(f[2])
end
end
dfhack.color(COLOR_RESET)
return result.status return result.status
end end

@ -1,6 +1,7 @@
local _ENV = mkmodule('utils') local _ENV = mkmodule('utils')
local df = df local df = df
local getopt = require('3rdparty.alt_getopt')
-- Comparator function -- Comparator function
function compare(a,b) function compare(a,b)
@ -613,6 +614,60 @@ function processArgs(args, validArgs)
return result return result
end end
-- processes commandline options according to optionActions and returns all
-- argument strings that are not options. Options and non-option strings can
-- appear in any order, and single-letter options that do not take arguments
-- can be combined into a single option string (e.g. '-abc' is the same as
-- '-a -b -c' if options 'a' and 'b' do not take arguments.
--
-- optionActions is a vector with elements in the following format:
-- {shortOptionName, longOptionAlias, hasArg=boolean, handler=fn}
-- shortOptionName and handler are required. If the option takes an argument,
-- it will be passed to the handler function.
-- longOptionAlias is optional.
-- hasArgument defaults to false.
--
-- example usage:
--
-- local filename = nil
-- local open_readonly = false
-- local nonoptions = processArgsGetopt(args, {
-- {'r', handler=function() open_readonly = true end},
-- {'f', 'filename', hasArg=true,
-- handler=function(optarg) filename = optarg end}
-- })
--
-- when args is {'first', '-f', 'fname', 'second'} or, equivalently,
-- {'first', '--filename', 'fname', 'second'} (note the double dash in front of
-- the long option alias), then filename will be fname and nonoptions will
-- contain {'first', 'second'}.
function processArgsGetopt(args, optionActions)
local sh_opts, long_opts = '', {}
local handlers = {}
for _,optionAction in ipairs(optionActions) do
local sh_opt,long_opt = optionAction[1], optionAction[2]
if not sh_opt or type(sh_opt) ~= 'string' or #sh_opt ~= 1 then
error('optionAction missing option letter at index 1')
end
if not optionAction.handler then
error(string.format('handler missing for option "%s"', sh_opt))
end
sh_opts = sh_opts .. sh_opt
if optionAction.hasArg then sh_opts = sh_opts .. ':' end
handlers[sh_opt] = optionAction.handler
if long_opt then
long_opts[long_opt] = sh_opt
handlers[long_opt] = optionAction.handler
end
end
local opts, optargs, nonoptions =
getopt.get_ordered_opts(args, sh_opts, long_opts)
for i,v in ipairs(opts) do
handlers[v](optargs[i])
end
return nonoptions
end
function fillTable(table1,table2) function fillTable(table1,table2)
for k,v in pairs(table2) do for k,v in pairs(table2) do
table1[k] = v table1[k] = v

@ -881,7 +881,7 @@ static void linkRooms(df::building *bld)
for (size_t i = 0; i < vec.size(); i++) for (size_t i = 0; i < vec.size(); i++)
{ {
auto room = vec[i]; auto room = vec[i];
if (!room->is_room || room->z != bld->z) if (!room->is_room || room->z != bld->z || room == bld)
continue; continue;
df::building_extents_type *pext = getExtentTile(room->room, df::coord2d(bld->x1, bld->y1)); df::building_extents_type *pext = getExtentTile(room->room, df::coord2d(bld->x1, bld->y1));

@ -27,6 +27,7 @@ REQUIRE_GLOBAL(world); // used in buildingplan library
bool show_help = false; bool show_help = false;
bool quickfort_mode = false; bool quickfort_mode = false;
bool all_enabled = false;
bool in_dummy_screen = false; bool in_dummy_screen = false;
std::unordered_map<BuildingTypeKey, bool, BuildingTypeKeyHash> planmode_enabled; std::unordered_map<BuildingTypeKey, bool, BuildingTypeKeyHash> planmode_enabled;
@ -284,7 +285,7 @@ void ViewscreenChooseMaterial::render()
//START Viewscreen Hook //START Viewscreen Hook
static bool is_planmode_enabled(BuildingTypeKey key) static bool is_planmode_enabled(BuildingTypeKey key)
{ {
return planmode_enabled[key] || quickfort_mode; return planmode_enabled[key] || quickfort_mode || all_enabled;
} }
static std::string get_item_label(const BuildingTypeKey &key, int item_idx) static std::string get_item_label(const BuildingTypeKey &key, int item_idx)
@ -387,6 +388,7 @@ static void show_global_settings_dialog()
lua_newtable(L); lua_newtable(L);
int ctable = lua_gettop(L); int ctable = lua_gettop(L);
Lua::SetField(L, quickfort_mode, ctable, "quickfort_mode"); Lua::SetField(L, quickfort_mode, ctable, "quickfort_mode");
Lua::SetField(L, all_enabled, ctable, "all_enabled");
for (auto & setting : planner.getGlobalSettings()) for (auto & setting : planner.getGlobalSettings())
{ {
@ -629,7 +631,8 @@ struct buildingplan_place_hook : public df::viewscreen_dwarfmodest
show_help = true; show_help = true;
} }
if (input->count(interface_key::CUSTOM_SHIFT_P)) if (!quickfort_mode && !all_enabled
&& input->count(interface_key::CUSTOM_SHIFT_P))
{ {
planmode_enabled[key] = !planmode_enabled[key]; planmode_enabled[key] = !planmode_enabled[key];
if (!is_planmode_enabled(key)) if (!is_planmode_enabled(key))
@ -765,8 +768,16 @@ struct buildingplan_place_hook : public df::viewscreen_dwarfmodest
OutputString(COLOR_WHITE, x, y, "Use Shift-Keys here", true, left_margin); OutputString(COLOR_WHITE, x, y, "Use Shift-Keys here", true, left_margin);
} }
OutputToggleString(x, y, "Planning Mode", interface_key::CUSTOM_SHIFT_P, OutputHotkeyString(x, y, "Planning Mode", interface_key::CUSTOM_SHIFT_P);
planmode_enabled[key], true, left_margin, COLOR_WHITE, COLOR_LIGHTRED); OutputString(COLOR_WHITE, x, y, ": ");
if (quickfort_mode)
OutputString(COLOR_YELLOW, x, y, "Quickfort", true, left_margin);
else if (all_enabled)
OutputString(COLOR_YELLOW, x, y, "All", true, left_margin);
else if (planmode_enabled[key])
OutputString(COLOR_GREEN, x, y, "On", true, left_margin);
else
OutputString(COLOR_GREY, x, y, "Off", true, left_margin);
OutputHotkeyString(x, y, "Global Settings", interface_key::CUSTOM_SHIFT_G, OutputHotkeyString(x, y, "Global Settings", interface_key::CUSTOM_SHIFT_G,
true, left_margin, COLOR_WHITE, COLOR_LIGHTRED); true, left_margin, COLOR_WHITE, COLOR_LIGHTRED);
@ -963,6 +974,7 @@ static command_result buildingplan_cmd(color_ostream &out, vector <string> & par
// display current settings // display current settings
out.print("active settings:\n"); out.print("active settings:\n");
out.print(" all_enabled = %s\n", all_enabled ? "true" : "false");
for (auto & setting : planner.getGlobalSettings()) for (auto & setting : planner.getGlobalSettings())
{ {
out.print(" %s = %s\n", setting.first.c_str(), out.print(" %s = %s\n", setting.first.c_str(),
@ -1108,6 +1120,12 @@ static bool setSetting(std::string name, bool value) {
quickfort_mode = value; quickfort_mode = value;
return true; return true;
} }
if (name == "all_enabled")
{
debug("setting all_enabled %d -> %d", all_enabled, value);
all_enabled = value;
return true;
}
return planner.setGlobalSetting(name, value); return planner.setGlobalSetting(name, value);
} }

@ -69,23 +69,28 @@ namespace embark_assist {
Heavily_Forested Heavily_Forested
}; };
struct mid_level_tile { // only contains those attributes that are being handled during incursion processing
struct mid_level_tile_incursion_base {
uint8_t aquifer = Clear_Aquifer_Bits; uint8_t aquifer = Clear_Aquifer_Bits;
bool clay = false; bool clay = false;
bool sand = false; bool sand = false;
int8_t soil_depth;
int16_t elevation;
int8_t biome_offset;
tree_levels trees;
uint8_t savagery_level; // 0 - 2
uint8_t evilness_level; // 0 - 2
};
// contains all attributes (some by inheritance), used for regular survey/matching
struct mid_level_tile : public mid_level_tile_incursion_base {
bool flux = false; bool flux = false;
bool coal = false; bool coal = false;
int8_t soil_depth;
int8_t offset; int8_t offset;
int16_t elevation;
river_sizes river_size = river_sizes::None; river_sizes river_size = river_sizes::None;
int16_t river_elevation = 100; int16_t river_elevation = 100;
int8_t adamantine_level; // -1 = none, 0 .. 3 = cavern 1 .. magma sea. Currently not used beyond present/absent. int8_t adamantine_level; // -1 = none, 0 .. 3 = cavern 1 .. magma sea. Currently not used beyond present/absent.
int8_t magma_level; // -1 = none, 0 .. 3 = cavern 3 .. surface/volcano int8_t magma_level; // -1 = none, 0 .. 3 = cavern 3 .. surface/volcano
int8_t biome_offset;
tree_levels trees;
uint8_t savagery_level; // 0 - 2
uint8_t evilness_level; // 0 - 2
std::vector<bool> metals; std::vector<bool> metals;
std::vector<bool> economics; std::vector<bool> economics;
std::vector<bool> minerals; std::vector<bool> minerals;
@ -141,10 +146,10 @@ namespace embark_assist {
std::vector<bool> minerals; std::vector<bool> minerals;
std::vector<int16_t> neighbors; // entity_raw indices std::vector<int16_t> neighbors; // entity_raw indices
uint8_t necro_neighbors; uint8_t necro_neighbors;
mid_level_tile north_row[16]; mid_level_tile_incursion_base north_row[16];
mid_level_tile south_row[16]; mid_level_tile_incursion_base south_row[16];
mid_level_tile west_column[16]; mid_level_tile_incursion_base west_column[16];
mid_level_tile east_column[16]; mid_level_tile_incursion_base east_column[16];
uint8_t north_corner_selection[16]; // 0 - 3. For some reason DF stores everything needed for incursion uint8_t north_corner_selection[16]; // 0 - 3. For some reason DF stores everything needed for incursion
uint8_t west_corner_selection[16]; // detection in 17:th row/colum data in the region details except uint8_t west_corner_selection[16]; // detection in 17:th row/colum data in the region details except
// this info, so we have to go to neighboring world tiles to fetch it. // this info, so we have to go to neighboring world tiles to fetch it.

@ -68,7 +68,7 @@ namespace embark_assist {
void process_embark_incursion(matcher_info *result, void process_embark_incursion(matcher_info *result,
embark_assist::defs::world_tile_data *survey_results, embark_assist::defs::world_tile_data *survey_results,
embark_assist::defs::mid_level_tile *mlt, // Note this is a single tile, as opposed to most usages of this variable name. embark_assist::defs::mid_level_tile_incursion_base *mlt, // Note this is a single tile, as opposed to most usages of this variable name.
embark_assist::defs::finders *finder, embark_assist::defs::finders *finder,
int16_t elevation, int16_t elevation,
uint16_t x, uint16_t x,
@ -2699,7 +2699,7 @@ namespace embark_assist {
void merge_incursion_into_world_tile(embark_assist::defs::region_tile_datum* current, void merge_incursion_into_world_tile(embark_assist::defs::region_tile_datum* current,
embark_assist::defs::region_tile_datum* target_tile, embark_assist::defs::region_tile_datum* target_tile,
embark_assist::defs::mid_level_tile* target_mlt) { embark_assist::defs::mid_level_tile_incursion_base* target_mlt) {
df::world_data* world_data = world->world_data; df::world_data* world_data = world->world_data;
current->aquifer |= target_mlt->aquifer; current->aquifer |= target_mlt->aquifer;

@ -455,7 +455,7 @@ namespace embark_assist {
void process_embark_incursion(embark_assist::defs::site_infos *site_info, void process_embark_incursion(embark_assist::defs::site_infos *site_info,
embark_assist::defs::world_tile_data *survey_results, embark_assist::defs::world_tile_data *survey_results,
embark_assist::defs::mid_level_tile *mlt, // Note this is a single tile, as opposed to most usages of this variable name. embark_assist::defs::mid_level_tile_incursion_base *mlt, // Note this is a single tile, as opposed to most usages of this variable name.
int16_t elevation, int16_t elevation,
uint16_t x, uint16_t x,
uint16_t y) { uint16_t y) {
@ -1380,51 +1380,16 @@ void embark_assist::survey::survey_mid_level_tile(embark_assist::defs::geo_data
tile.west_column[i].sand = mlt->at(0).at(i).sand; tile.west_column[i].sand = mlt->at(0).at(i).sand;
tile.east_column[i].sand = mlt->at(15).at(i).sand; tile.east_column[i].sand = mlt->at(15).at(i).sand;
tile.north_row[i].flux = mlt->at(i).at(0).flux; // Not used
tile.south_row[i].flux = mlt->at(i).at(15).flux;
tile.west_column[i].flux = mlt->at(0).at(i).flux;
tile.east_column[i].flux = mlt->at(15).at(i).flux;
tile.north_row[i].coal = mlt->at(i).at(0).coal; // Not used
tile.south_row[i].coal = mlt->at(i).at(15).coal;
tile.west_column[i].coal = mlt->at(0).at(i).coal;
tile.east_column[i].coal = mlt->at(15).at(i).coal;
tile.north_row[i].soil_depth = mlt->at(i).at(0).soil_depth; tile.north_row[i].soil_depth = mlt->at(i).at(0).soil_depth;
tile.south_row[i].soil_depth = mlt->at(i).at(15).soil_depth; tile.south_row[i].soil_depth = mlt->at(i).at(15).soil_depth;
tile.west_column[i].soil_depth = mlt->at(0).at(i).soil_depth; tile.west_column[i].soil_depth = mlt->at(0).at(i).soil_depth;
tile.east_column[i].soil_depth = mlt->at(15).at(i).soil_depth; tile.east_column[i].soil_depth = mlt->at(15).at(i).soil_depth;
tile.north_row[i].offset = mlt->at(i).at(0).offset; // Not used
tile.south_row[i].offset = mlt->at(i).at(15).offset;
tile.west_column[i].offset = mlt->at(0).at(i).offset;
tile.east_column[i].offset = mlt->at(15).at(i).offset;
tile.north_row[i].elevation = mlt->at(i).at(0).elevation; tile.north_row[i].elevation = mlt->at(i).at(0).elevation;
tile.south_row[i].elevation = mlt->at(i).at(15).elevation; tile.south_row[i].elevation = mlt->at(i).at(15).elevation;
tile.west_column[i].elevation = mlt->at(0).at(i).elevation; tile.west_column[i].elevation = mlt->at(0).at(i).elevation;
tile.east_column[i].elevation = mlt->at(15).at(i).elevation; tile.east_column[i].elevation = mlt->at(15).at(i).elevation;
tile.north_row[i].river_size = mlt->at(i).at(0).river_size; // Not used
tile.south_row[i].river_size = mlt->at(i).at(15).river_size;
tile.west_column[i].river_size = mlt->at(0).at(i).river_size;
tile.east_column[i].river_size = mlt->at(15).at(i).river_size;
tile.north_row[i].river_elevation = mlt->at(i).at(0).river_elevation; // Not used
tile.south_row[i].river_elevation = mlt->at(i).at(15).river_elevation;
tile.west_column[i].river_elevation = mlt->at(0).at(i).river_elevation;
tile.east_column[i].river_elevation = mlt->at(15).at(i).river_elevation;
tile.north_row[i].adamantine_level = mlt->at(i).at(0).adamantine_level; // Not used
tile.south_row[i].adamantine_level = mlt->at(i).at(15).adamantine_level;
tile.west_column[i].adamantine_level = mlt->at(0).at(i).adamantine_level;
tile.east_column[i].adamantine_level = mlt->at(15).at(i).adamantine_level;
tile.north_row[i].magma_level = mlt->at(i).at(0).magma_level; // Not used
tile.south_row[i].magma_level = mlt->at(i).at(15).magma_level;
tile.west_column[i].magma_level = mlt->at(0).at(i).magma_level;
tile.east_column[i].magma_level = mlt->at(15).at(i).magma_level;
tile.north_row[i].biome_offset = mlt->at(i).at(0).biome_offset; tile.north_row[i].biome_offset = mlt->at(i).at(0).biome_offset;
tile.south_row[i].biome_offset = mlt->at(i).at(15).biome_offset; tile.south_row[i].biome_offset = mlt->at(i).at(15).biome_offset;
tile.west_column[i].biome_offset = mlt->at(0).at(i).biome_offset; tile.west_column[i].biome_offset = mlt->at(0).at(i).biome_offset;
@ -1445,21 +1410,6 @@ void embark_assist::survey::survey_mid_level_tile(embark_assist::defs::geo_data
tile.west_column[i].evilness_level = mlt->at(0).at(i).evilness_level; tile.west_column[i].evilness_level = mlt->at(0).at(i).evilness_level;
tile.east_column[i].evilness_level = mlt->at(15).at(i).evilness_level; tile.east_column[i].evilness_level = mlt->at(15).at(i).evilness_level;
tile.north_row[i].metals.resize(0); // Not used
tile.south_row[i].metals.resize(0);
tile.west_column[i].metals.resize(0);
tile.east_column[i].metals.resize(0);
tile.north_row[i].economics.resize(0); // Not used
tile.south_row[i].economics.resize(0);
tile.west_column[i].economics.resize(0);
tile.east_column[i].economics.resize(0);
tile.north_row[i].minerals.resize(0); // Not used
tile.south_row[i].minerals.resize(0);
tile.west_column[i].minerals.resize(0);
tile.east_column[i].minerals.resize(0);
tile.north_corner_selection[i] = world_data->region_details[0]->edges.biome_corner[i][0]; tile.north_corner_selection[i] = world_data->region_details[0]->edges.biome_corner[i][0];
tile.west_corner_selection[i] = world_data->region_details[0]->edges.biome_corner[0][i]; tile.west_corner_selection[i] = world_data->region_details[0]->edges.biome_corner[0][i];
tile.north_row_biome_x[i] = world_data->region_details[0]->edges.biome_x[i][0]; tile.north_row_biome_x[i] = world_data->region_details[0]->edges.biome_x[i][0];

@ -229,7 +229,16 @@ end
setting is not needed for DFHack quickfort. setting is not needed for DFHack quickfort.
--]] --]]
function GlobalSettings:init() function GlobalSettings:init()
self.subviews.label:setText{ 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', '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_label_token('Blocks', 'CUSTOM_B', 'blocks', 10),
self:make_setting_value_token('blocks'), '\n', self:make_setting_value_token('blocks'), '\n',

@ -115,14 +115,17 @@ static string get_unit_description(df::unit *unit)
return desc; return desc;
} }
static bool cursor_key_pressed (std::set<df::interface_key> *input) static bool cursor_key_pressed (std::set<df::interface_key> *input, bool in_entry_mode)
{ {
if (in_entry_mode)
{
// give text input (e.g. "2") priority over cursor keys // give text input (e.g. "2") priority over cursor keys
for (auto it = input->begin(); it != input->end(); ++it) for (auto it = input->begin(); it != input->end(); ++it)
{ {
if (Screen::keyToChar(*it) != -1) if (Screen::keyToChar(*it) != -1)
return false; return false;
} }
}
return return
input->count(df::interface_key::CURSOR_UP) || input->count(df::interface_key::CURSOR_UP) ||
input->count(df::interface_key::CURSOR_DOWN) || input->count(df::interface_key::CURSOR_DOWN) ||
@ -249,7 +252,7 @@ public:
// ENTER or ESC: leave typing mode // ENTER or ESC: leave typing mode
end_entry_mode(); end_entry_mode();
} }
else if (cursor_key_pressed(input)) else if (cursor_key_pressed(input, entry_mode))
{ {
// Arrow key pressed. Leave entry mode and allow screen to process key // Arrow key pressed. Leave entry mode and allow screen to process key
end_entry_mode(); end_entry_mode();
@ -1953,7 +1956,9 @@ public:
end_entry_mode(); end_entry_mode();
return false; return false;
} }
if (cursor_key_pressed(input)) bool hotkey_pressed =
input->lower_bound(interface_key::D_HOTKEY1) != input->upper_bound(interface_key::D_HOTKEY16);
if (cursor_key_pressed(input, in_entry_mode()) || hotkey_pressed)
{ {
end_entry_mode(); end_entry_mode();
clear_search(); clear_search();

@ -266,6 +266,9 @@ struct stockflow_hook : public df::viewscreen_dwarfmodest {
typedef df::viewscreen_dwarfmodest interpose_base; typedef df::viewscreen_dwarfmodest interpose_base;
bool handleInput(set<df::interface_key> *input) { bool handleInput(set<df::interface_key> *input) {
if (Gui::inRenameBuilding())
return false;
building_stockpilest *sp = get_selected_stockpile(); building_stockpilest *sp = get_selected_stockpile();
if (!sp) if (!sp)
return false; return false;

@ -1 +1 @@
Subproject commit ebe6f5d599bfafb614a791b637413e976c8870a5 Subproject commit e20fa9f5ca7707377477b3d9c032ec1f3a2e6414